From a6c989bc1ba3da2cd7fdf2f51c915b838af4a2aa Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Mon, 22 Mar 2021 21:04:55 +1100 Subject: [PATCH] Obtain supported Vulkan API --- drivers/vulkan/rendering_device_vulkan.cpp | 13 ++ drivers/vulkan/vulkan_context.cpp | 232 ++++++++++++++++++++- drivers/vulkan/vulkan_context.h | 25 +++ modules/glslang/register_types.cpp | 26 ++- servers/rendering/rendering_device.cpp | 2 +- servers/rendering/rendering_device.h | 35 +++- 6 files changed, 326 insertions(+), 7 deletions(-) diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 469d80368d33..6d5809feafdd 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -7834,6 +7834,18 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { } void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_device) { + // get our device capabilities + { + device_capabilities.version_major = p_context->get_vulkan_major(); + device_capabilities.version_minor = p_context->get_vulkan_minor(); + + // get info about subgroups + VulkanContext::SubgroupCapabilities subgroup_capabilities = p_context->get_subgroup_capabilities(); + device_capabilities.subgroup_size = subgroup_capabilities.size; + device_capabilities.subgroup_in_shaders = subgroup_capabilities.supported_stages_flags_rd(); + device_capabilities.subgroup_operations = subgroup_capabilities.supported_operations_flags_rd(); + } + context = p_context; device = p_context->get_device(); if (p_local_device) { @@ -8253,6 +8265,7 @@ RenderingDevice *RenderingDeviceVulkan::create_local_device() { } RenderingDeviceVulkan::RenderingDeviceVulkan() { + device_capabilities.device_family = DEVICE_VULKAN; } RenderingDeviceVulkan::~RenderingDeviceVulkan() { diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 69517b17bacc..49882ca3ad69 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -34,6 +34,7 @@ #include "core/config/project_settings.h" #include "core/string/ustring.h" #include "core/version.h" +#include "servers/rendering/rendering_device.h" #include "vk_enum_string_helper.h" @@ -234,6 +235,39 @@ Error VulkanContext::_create_validation_layers() { return OK; } +typedef VkResult(VKAPI_PTR *_vkEnumerateInstanceVersion)(uint32_t *); + +Error VulkanContext::_obtain_vulkan_version() { + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html#_description + // for Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android. + _vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"); + if (func != nullptr) { + uint32_t api_version; + VkResult res = func(&api_version); + if (res == VK_SUCCESS) { + vulkan_major = VK_VERSION_MAJOR(api_version); + vulkan_minor = VK_VERSION_MINOR(api_version); + uint32_t vulkan_patch = VK_VERSION_PATCH(api_version); + + print_line("Vulkan API " + itos(vulkan_major) + "." + itos(vulkan_minor) + "." + itos(vulkan_patch)); + } else { + // according to the documentation this shouldn't fail with anything except a memory allocation error + // in which case we're in deep trouble anyway + ERR_FAIL_V(ERR_CANT_CREATE); + } + } else { + print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0"); + } + + // we don't go above 1.2 + if ((vulkan_major > 1) || (vulkan_major == 1 && vulkan_minor > 2)) { + vulkan_major = 1; + vulkan_minor = 2; + } + + return OK; +} + Error VulkanContext::_initialize_extensions() { uint32_t instance_extension_count = 0; @@ -289,12 +323,200 @@ Error VulkanContext::_initialize_extensions() { return OK; } +typedef void(VKAPI_PTR *_vkGetPhysicalDeviceProperties2)(VkPhysicalDevice, VkPhysicalDeviceProperties2 *); + +uint32_t VulkanContext::SubgroupCapabilities::supported_stages_flags_rd() const { + uint32_t flags = 0; + + if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_VERTEX_BIT; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_EVALUATION_BIT; + } + // if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { + // flags += RenderingDevice::ShaderStage::SHADER_STAGE_GEOMETRY_BIT; + // } + if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_FRAGMENT_BIT; + } + if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_COMPUTE_BIT; + } + + return flags; +} + +String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { + String res; + + if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { + res += ", STAGE_VERTEX"; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { + res += ", STAGE_TESSELLATION_CONTROL"; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { + res += ", STAGE_TESSELLATION_EVALUATION"; + } + if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { + res += ", STAGE_GEOMETRY"; + } + if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { + res += ", STAGE_FRAGMENT"; + } + if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { + res += ", STAGE_COMPUTE"; + } + + /* these are not defined on Android GRMBL */ + if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) { + res += ", STAGE_RAYGEN_KHR"; + } + if (supportedStages & 0x00000200 /* VK_SHADER_STAGE_ANY_HIT_BIT_KHR */) { + res += ", STAGE_ANY_HIT_KHR"; + } + if (supportedStages & 0x00000400 /* VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR */) { + res += ", STAGE_CLOSEST_HIT_KHR"; + } + if (supportedStages & 0x00000800 /* VK_SHADER_STAGE_MISS_BIT_KHR */) { + res += ", STAGE_MISS_KHR"; + } + if (supportedStages & 0x00001000 /* VK_SHADER_STAGE_INTERSECTION_BIT_KHR */) { + res += ", STAGE_INTERSECTION_KHR"; + } + if (supportedStages & 0x00002000 /* VK_SHADER_STAGE_CALLABLE_BIT_KHR */) { + res += ", STAGE_CALLABLE_KHR"; + } + if (supportedStages & 0x00000040 /* VK_SHADER_STAGE_TASK_BIT_NV */) { + res += ", STAGE_TASK_NV"; + } + if (supportedStages & 0x00000080 /* VK_SHADER_STAGE_MESH_BIT_NV */) { + res += ", STAGE_MESH_NV"; + } + + return res.substr(2); // remove first ", " +} + +uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const { + uint32_t flags = 0; + + if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_BASIC_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_VOTE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_BALLOT_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_CLUSTERED_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_QUAD_BIT; + } + + return flags; +} + +String VulkanContext::SubgroupCapabilities::supported_operations_desc() const { + String res; + + if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { + res += ", FEATURE_BASIC"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { + res += ", FEATURE_VOTE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { + res += ", FEATURE_ARITHMETIC"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { + res += ", FEATURE_BALLOT"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { + res += ", FEATURE_SHUFFLE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { + res += ", FEATURE_SHUFFLE_RELATIVE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { + res += ", FEATURE_CLUSTERED"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { + res += ", FEATURE_QUAD"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV) { + res += ", FEATURE_PARTITIONED_NV"; + } + + return res.substr(2); // remove first ", " +} + +Error VulkanContext::_check_capabilities() { + // check subgroups + // https://www.khronos.org/blog/vulkan-subgroup-tutorial + // for Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android. + _vkGetPhysicalDeviceProperties2 func = (_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2"); + if (func != nullptr) { + VkPhysicalDeviceSubgroupProperties subgroupProperties; + subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + subgroupProperties.pNext = NULL; + + VkPhysicalDeviceProperties2 physicalDeviceProperties; + physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physicalDeviceProperties.pNext = &subgroupProperties; + + func(gpu, &physicalDeviceProperties); + + subgroup_capabilities.size = subgroupProperties.subgroupSize; + subgroup_capabilities.supportedStages = subgroupProperties.supportedStages; + subgroup_capabilities.supportedOperations = subgroupProperties.supportedOperations; + // Note: quadOperationsInAllStages will be true if: + // - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT + // - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT + subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages; + + // only output this when debugging? + print_line("- Vulkan subgroup size " + itos(subgroup_capabilities.size)); + print_line("- Vulkan subgroup stages " + subgroup_capabilities.supported_stages_desc()); + print_line("- Vulkan subgroup supported ops " + subgroup_capabilities.supported_operations_desc()); + if (subgroup_capabilities.quadOperationsInAllStages) { + print_line("- Vulkan subgroup quad operations in all stages"); + } + } else { + subgroup_capabilities.size = 0; + subgroup_capabilities.supportedStages = 0; + subgroup_capabilities.supportedOperations = 0; + subgroup_capabilities.quadOperationsInAllStages = false; + } + + return OK; +} + Error VulkanContext::_create_physical_device() { + /* obtain version */ + _obtain_vulkan_version(); + /* Look for validation layers */ if (use_validation_layers) { _create_validation_layers(); } + /* initialise extensions */ { Error err = _initialize_extensions(); if (err != OK) { @@ -312,7 +534,7 @@ Error VulkanContext::_create_physical_device() { /*applicationVersion*/ 0, /*pEngineName*/ namecs.get_data(), /*engineVersion*/ 0, - /*apiVersion*/ VK_API_VERSION_1_0, + /*apiVersion*/ VK_MAKE_VERSION(vulkan_major, vulkan_minor, 0) }; VkInstanceCreateInfo inst_info = { /*sType*/ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, @@ -561,6 +783,14 @@ Error VulkanContext::_create_physical_device() { GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR); GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR); + // get info about what our vulkan driver is capable off + { + Error res = _check_capabilities(); + if (res != OK) { + return res; + } + } + return OK; } diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index dc6b0410bc28..3504cf1e10d1 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -41,6 +41,20 @@ #include class VulkanContext { +public: + struct SubgroupCapabilities { + uint32_t size; + VkShaderStageFlags supportedStages; + VkSubgroupFeatureFlags supportedOperations; + VkBool32 quadOperationsInAllStages; + + uint32_t supported_stages_flags_rd() const; + String supported_stages_desc() const; + uint32_t supported_operations_flags_rd() const; + String supported_operations_desc() const; + }; + +private: enum { MAX_EXTENSIONS = 128, MAX_LAYERS = 64, @@ -57,6 +71,11 @@ class VulkanContext { bool device_initialized = false; bool inst_initialized = false; + // Vulkan 1.0 doesn't return version info so we assume this by default until we know otherwise + uint32_t vulkan_major = 1; + uint32_t vulkan_minor = 0; + SubgroupCapabilities subgroup_capabilities; + String device_vendor; String device_name; String pipeline_cache_id; @@ -150,8 +169,10 @@ class VulkanContext { VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE; + Error _obtain_vulkan_version(); Error _create_validation_layers(); Error _initialize_extensions(); + Error _check_capabilities(); VkBool32 _check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers); static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback( @@ -186,6 +207,10 @@ protected: } public: + uint32_t get_vulkan_major() const { return vulkan_major; }; + uint32_t get_vulkan_minor() const { return vulkan_minor; }; + SubgroupCapabilities get_subgroup_capabilities() const { return subgroup_capabilities; }; + VkDevice get_device(); VkPhysicalDevice get_physical_device(); int get_swapchain_image_count() const; diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index 545aa687477f..6934d7c5037b 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -37,7 +37,7 @@ #include #include -static Vector _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error) { +static Vector _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice::Capabilities *p_capabilities) { Vector ret; ERR_FAIL_COND_V(p_language == RenderingDevice::SHADER_LANGUAGE_HLSL, ret); @@ -52,17 +52,35 @@ static Vector _compile_shader_glsl(RenderingDevice::ShaderStage p_stage int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100 - glslang::EShTargetClientVersion VulkanClientVersion = glslang::EShTargetVulkan_1_0; - glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_3; + glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2; + glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5; glslang::TShader::ForbidIncluder includer; + if (p_capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) { + if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 0) { + ClientVersion = glslang::EShTargetVulkan_1_0; + TargetVersion = glslang::EShTargetSpv_1_0; + } else if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 1) { + ClientVersion = glslang::EShTargetVulkan_1_1; + TargetVersion = glslang::EShTargetSpv_1_3; + } else { + // use defaults + } + } else { + // once we support other backends we'll need to do something here + if (r_error) { + (*r_error) = "GLSLANG - Unsupported device family"; + } + return ret; + } + glslang::TShader shader(stages[p_stage]); CharString cs = p_source_code.ascii(); const char *cs_strings = cs.get_data(); shader.setStrings(&cs_strings, 1); shader.setEnvInput(glslang::EShSourceGlsl, stages[p_stage], glslang::EShClientVulkan, ClientInputSemanticsVersion); - shader.setEnvClient(glslang::EShClientVulkan, VulkanClientVersion); + shader.setEnvClient(glslang::EShClientVulkan, ClientVersion); shader.setEnvTarget(glslang::EShTargetSpv, TargetVersion); EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 27a9353e4e91..e6ad0018076b 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -59,7 +59,7 @@ Vector RenderingDevice::shader_compile_from_source(ShaderStage p_stage, ERR_FAIL_COND_V(!compile_function, Vector()); - return compile_function(p_stage, p_source_code, p_language, r_error); + return compile_function(p_stage, p_source_code, p_language, r_error, &device_capabilities); } RID RenderingDevice::_texture_create(const Ref &p_format, const Ref &p_view, const TypedArray &p_data) { diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 9fbf58d1315f..2de0549e8da9 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -51,6 +51,13 @@ class RDPipelineColorBlendState; class RenderingDevice : public Object { GDCLASS(RenderingDevice, Object) public: + enum DeviceFamily { + DEVICE_UNKNOWN, + DEVICE_OPENGL, + DEVICE_VULKAN, + DEVICE_DIRECTX + }; + enum ShaderStage { SHADER_STAGE_VERTEX, SHADER_STAGE_FRAGMENT, @@ -70,7 +77,29 @@ public: SHADER_LANGUAGE_HLSL }; - typedef Vector (*ShaderCompileFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error); + enum SubgroupOperations { + SUBGROUP_BASIC_BIT = 1, + SUBGROUP_VOTE_BIT = 2, + SUBGROUP_ARITHMETIC_BIT = 4, + SUBGROUP_BALLOT_BIT = 8, + SUBGROUP_SHUFFLE_BIT = 16, + SUBGROUP_SHUFFLE_RELATIVE_BIT = 32, + SUBGROUP_CLUSTERED_BIT = 64, + SUBGROUP_QUAD_BIT = 128, + }; + + struct Capabilities { + // main device info + DeviceFamily device_family = DEVICE_UNKNOWN; + uint32_t version_major = 1.0; + uint32_t version_minor = 0.0; + // subgroup capabilities + uint32_t subgroup_size = 0; + uint32_t subgroup_in_shaders = 0; // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc. + uint32_t subgroup_operations = 0; // Set flags, using SubgroupOperations + }; + + typedef Vector (*ShaderCompileFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, const Capabilities *p_capabilities); typedef Vector (*ShaderCacheFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language); private: @@ -82,6 +111,8 @@ private: protected: static void _bind_methods(); + Capabilities device_capabilities; + public: //base numeric ID for all types enum { @@ -597,6 +628,8 @@ public: /**** SHADER ****/ /****************/ + const Capabilities *get_device_capabilities() const { return &device_capabilities; }; + virtual Vector shader_compile_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language = SHADER_LANGUAGE_GLSL, String *r_error = nullptr, bool p_allow_cache = true); static void shader_set_compile_function(ShaderCompileFunction p_function);