diff options
Diffstat (limited to 'drivers/vulkan/vulkan_context.cpp')
-rw-r--r-- | drivers/vulkan/vulkan_context.cpp | 977 |
1 files changed, 578 insertions, 399 deletions
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 0d8a3310fd..0bccefede8 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* vulkan_context.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* vulkan_context.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "vulkan_context.h" @@ -48,15 +48,119 @@ VulkanHooks *VulkanContext::vulkan_hooks = nullptr; -VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) { - if (fpCreateRenderPass2KHR == nullptr) { - fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetInstanceProcAddr(inst, "vkCreateRenderPass2KHR"); +Vector<VkAttachmentReference> VulkanContext::_convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs) { + Vector<VkAttachmentReference> att_refs; + + if (p_refs != nullptr) { + for (uint32_t i = 0; i < p_count; i++) { + // We lose aspectMask in this conversion but we don't use it currently. + + VkAttachmentReference ref = { + p_refs[i].attachment, /* attachment */ + p_refs[i].layout /* layout */ + }; + + att_refs.push_back(ref); + } } - if (fpCreateRenderPass2KHR == nullptr) { - return VK_ERROR_EXTENSION_NOT_PRESENT; + return att_refs; +} + +VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass) { + if (is_device_extension_enabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) { + if (fpCreateRenderPass2KHR == nullptr) { + fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetDeviceProcAddr(p_device, "vkCreateRenderPass2KHR"); + } + + if (fpCreateRenderPass2KHR == nullptr) { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } else { + return (fpCreateRenderPass2KHR)(p_device, p_create_info, p_allocator, p_render_pass); + } } else { - return (fpCreateRenderPass2KHR)(device, pCreateInfo, pAllocator, pRenderPass); + // need to fall back on vkCreateRenderPass + + const void *next = p_create_info->pNext; // ATM we only support multiview which should work if supported. + + Vector<VkAttachmentDescription> attachments; + for (uint32_t i = 0; i < p_create_info->attachmentCount; i++) { + // Basically the old layout just misses type and next. + VkAttachmentDescription att = { + p_create_info->pAttachments[i].flags, /* flags */ + p_create_info->pAttachments[i].format, /* format */ + p_create_info->pAttachments[i].samples, /* samples */ + p_create_info->pAttachments[i].loadOp, /* loadOp */ + p_create_info->pAttachments[i].storeOp, /* storeOp */ + p_create_info->pAttachments[i].stencilLoadOp, /* stencilLoadOp */ + p_create_info->pAttachments[i].stencilStoreOp, /* stencilStoreOp */ + p_create_info->pAttachments[i].initialLayout, /* initialLayout */ + p_create_info->pAttachments[i].finalLayout /* finalLayout */ + }; + + attachments.push_back(att); + } + + Vector<VkSubpassDescription> subpasses; + for (uint32_t i = 0; i < p_create_info->subpassCount; i++) { + // Here we need to do more, again it's just stripping out type and next + // but we have VkAttachmentReference2 to convert to VkAttachmentReference. + // Also viewmask is not supported but we don't use it outside of multiview. + + Vector<VkAttachmentReference> input_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].inputAttachmentCount, p_create_info->pSubpasses[i].pInputAttachments); + Vector<VkAttachmentReference> color_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pColorAttachments); + Vector<VkAttachmentReference> resolve_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pResolveAttachments); + Vector<VkAttachmentReference> depth_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pDepthStencilAttachment); + + VkSubpassDescription subpass = { + p_create_info->pSubpasses[i].flags, /* flags */ + p_create_info->pSubpasses[i].pipelineBindPoint, /* pipelineBindPoint */ + p_create_info->pSubpasses[i].inputAttachmentCount, /* inputAttachmentCount */ + input_attachments.size() == 0 ? nullptr : input_attachments.ptr(), /* pInputAttachments */ + p_create_info->pSubpasses[i].colorAttachmentCount, /* colorAttachmentCount */ + color_attachments.size() == 0 ? nullptr : color_attachments.ptr(), /* pColorAttachments */ + resolve_attachments.size() == 0 ? nullptr : resolve_attachments.ptr(), /* pResolveAttachments */ + depth_attachments.size() == 0 ? nullptr : depth_attachments.ptr(), /* pDepthStencilAttachment */ + p_create_info->pSubpasses[i].preserveAttachmentCount, /* preserveAttachmentCount */ + p_create_info->pSubpasses[i].pPreserveAttachments /* pPreserveAttachments */ + }; + + subpasses.push_back(subpass); + } + + Vector<VkSubpassDependency> dependencies; + for (uint32_t i = 0; i < p_create_info->dependencyCount; i++) { + // We lose viewOffset here but again I don't believe we use this anywhere. + VkSubpassDependency dep = { + p_create_info->pDependencies[i].srcSubpass, /* srcSubpass */ + p_create_info->pDependencies[i].dstSubpass, /* dstSubpass */ + p_create_info->pDependencies[i].srcStageMask, /* srcStageMask */ + p_create_info->pDependencies[i].dstStageMask, /* dstStageMask */ + p_create_info->pDependencies[i].srcAccessMask, /* srcAccessMask */ + p_create_info->pDependencies[i].dstAccessMask, /* dstAccessMask */ + p_create_info->pDependencies[i].dependencyFlags, /* dependencyFlags */ + }; + + dependencies.push_back(dep); + } + + // CorrelatedViewMask is not supported in vkCreateRenderPass but we + // currently only use this for multiview. + // We'll need to look into this. + + VkRenderPassCreateInfo create_info = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, /* sType */ + next, /* pNext*/ + p_create_info->flags, /* flags */ + (uint32_t)attachments.size(), /* attachmentCount */ + attachments.ptr(), /* pAttachments */ + (uint32_t)subpasses.size(), /* subpassCount */ + subpasses.ptr(), /* pSubpasses */ + (uint32_t)dependencies.size(), /* */ + dependencies.ptr(), /* */ + }; + + return vkCreateRenderPass(device, &create_info, p_allocator, p_render_pass); } } @@ -85,19 +189,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( return VK_FALSE; } - // Workaround for Vulkan-Loader usability bug: https://github.com/KhronosGroup/Vulkan-Loader/issues/262. - if (strstr(pCallbackData->pMessage, "wrong ELF class: ELFCLASS32") != nullptr) { - return VK_FALSE; - } - -#ifdef WINDOWS_ENABLED - // Some software installs Vulkan overlays in Windows registry and never cleans them up on uninstall. - // So we get spammy error level messages from the loader about those - make them verbose instead. - if (strstr(pCallbackData->pMessage, "loader_get_json: Failed to open JSON file") != nullptr) { - messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - } -#endif - if (pCallbackData->pMessageIdName && strstr(pCallbackData->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw") != nullptr) { return VK_FALSE; } @@ -237,7 +328,7 @@ Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const cha { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" } }; - // Clear out-arguments + // Clear out-arguments. *count = 0; if (names != nullptr) { *names = nullptr; @@ -287,9 +378,7 @@ Error VulkanContext::_obtain_vulkan_version() { uint32_t api_version; VkResult res = func(&api_version); if (res == VK_SUCCESS) { - vulkan_major = VK_API_VERSION_MAJOR(api_version); - vulkan_minor = VK_API_VERSION_MINOR(api_version); - vulkan_patch = VK_API_VERSION_PATCH(api_version); + instance_api_version = api_version; } 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. @@ -297,74 +386,179 @@ Error VulkanContext::_obtain_vulkan_version() { } } 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; - vulkan_patch = 0; + instance_api_version = VK_API_VERSION_1_0; } return OK; } -Error VulkanContext::_initialize_extensions() { - uint32_t instance_extension_count = 0; +bool VulkanContext::instance_extensions_initialized = false; +HashMap<CharString, bool> VulkanContext::requested_instance_extensions; + +void VulkanContext::register_requested_instance_extension(const CharString &extension_name, bool p_required) { + ERR_FAIL_COND_MSG(instance_extensions_initialized, "You can only registered extensions before the Vulkan instance is created"); + ERR_FAIL_COND(requested_instance_extensions.has(extension_name)); + + requested_instance_extensions[extension_name] = p_required; +} + +Error VulkanContext::_initialize_instance_extensions() { + enabled_instance_extension_names.clear(); + + // Make sure our core extensions are here + register_requested_instance_extension(VK_KHR_SURFACE_EXTENSION_NAME, true); + register_requested_instance_extension(_get_platform_surface_extension(), true); + + if (_use_validation_layers()) { + register_requested_instance_extension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, false); + } - enabled_extension_count = 0; - enabled_debug_utils = false; - enabled_debug_report = false; - // Look for instance extensions. - VkBool32 surfaceExtFound = 0; - VkBool32 platformSurfaceExtFound = 0; - memset(extension_names, 0, sizeof(extension_names)); + register_requested_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false); + // Only enable debug utils in verbose mode or DEV_ENABLED. + // End users would get spammed with messages of varying verbosity due to the + // mess that thirdparty layers/extensions and drivers seem to leave in their + // wake, making the Windows registry a bottomless pit of broken layer JSON. +#ifdef DEV_ENABLED + bool want_debug_utils = true; +#else + bool want_debug_utils = OS::get_singleton()->is_stdout_verbose(); +#endif + if (want_debug_utils) { + register_requested_instance_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false); + } + + // Load instance extensions that are available... + uint32_t instance_extension_count = 0; VkResult err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr); ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_INCOMPLETE, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(instance_extension_count == 0, ERR_CANT_CREATE, "No instance extensions found, is a driver installed?"); - if (instance_extension_count > 0) { - VkExtensionProperties *instance_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * instance_extension_count); - err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, instance_extensions); - if (err != VK_SUCCESS && err != VK_INCOMPLETE) { - free(instance_extensions); - ERR_FAIL_V(ERR_CANT_CREATE); + VkExtensionProperties *instance_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * instance_extension_count); + err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, instance_extensions); + if (err != VK_SUCCESS && err != VK_INCOMPLETE) { + free(instance_extensions); + ERR_FAIL_V(ERR_CANT_CREATE); + } +#ifdef DEV_ENABLED + for (uint32_t i = 0; i < instance_extension_count; i++) { + print_verbose(String("VULKAN: Found instance extension ") + String(instance_extensions[i].extensionName)); + } +#endif + + // Enable all extensions that are supported and requested + for (uint32_t i = 0; i < instance_extension_count; i++) { + CharString extension_name(instance_extensions[i].extensionName); + if (requested_instance_extensions.has(extension_name)) { + enabled_instance_extension_names.insert(extension_name); } - for (uint32_t i = 0; i < instance_extension_count; i++) { - if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) { - surfaceExtFound = 1; - extension_names[enabled_extension_count++] = VK_KHR_SURFACE_EXTENSION_NAME; - } + } - if (!strcmp(_get_platform_surface_extension(), instance_extensions[i].extensionName)) { - platformSurfaceExtFound = 1; - extension_names[enabled_extension_count++] = _get_platform_surface_extension(); - } - if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, instance_extensions[i].extensionName)) { - if (_use_validation_layers()) { - extension_names[enabled_extension_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; - enabled_debug_report = true; - } - } - if (!strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instance_extensions[i].extensionName)) { - extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - enabled_debug_utils = true; - } - if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) { - extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; - } - if (enabled_extension_count >= MAX_EXTENSIONS) { + // Now check our requested extensions + for (KeyValue<CharString, bool> &requested_extension : requested_instance_extensions) { + if (!enabled_instance_extension_names.has(requested_extension.key)) { + if (requested_extension.value) { free(instance_extensions); - ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); + ERR_FAIL_V_MSG(ERR_BUG, String("Required extension ") + String(requested_extension.key) + String(" not found, is a driver installed?")); + } else { + print_verbose(String("Optional extension ") + String(requested_extension.key) + String(" not found")); } } + } - free(instance_extensions); + free(instance_extensions); + + instance_extensions_initialized = true; + return OK; +} + +bool VulkanContext::device_extensions_initialized = false; +HashMap<CharString, bool> VulkanContext::requested_device_extensions; + +void VulkanContext::register_requested_device_extension(const CharString &extension_name, bool p_required) { + ERR_FAIL_COND_MSG(device_extensions_initialized, "You can only registered extensions before the Vulkan instance is created"); + ERR_FAIL_COND(requested_device_extensions.has(extension_name)); + + requested_device_extensions[extension_name] = p_required; +} + +Error VulkanContext::_initialize_device_extensions() { + // Look for device extensions. + enabled_device_extension_names.clear(); + + // Make sure our core extensions are here + register_requested_device_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true); + + register_requested_device_extension(VK_KHR_MULTIVIEW_EXTENSION_NAME, false); + register_requested_device_extension(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, false); + register_requested_device_extension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, false); + register_requested_device_extension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); + register_requested_device_extension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, false); + + // TODO consider the following extensions: + // - VK_KHR_spirv_1_4 + // - VK_KHR_swapchain_mutable_format + // - VK_EXT_full_screen_exclusive + // - VK_EXT_hdr_metadata + // - VK_KHR_depth_stencil_resolve + + // Even though the user "enabled" the extension via the command + // line, we must make sure that it's enumerated for use with the + // device. Therefore, disable it here, and re-enable it again if + // enumerated. + if (VK_KHR_incremental_present_enabled) { + register_requested_device_extension(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, false); + } + if (VK_GOOGLE_display_timing_enabled) { + register_requested_device_extension(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, false); } - ERR_FAIL_COND_V_MSG(!surfaceExtFound, ERR_CANT_CREATE, "No surface extension found, is a driver installed?"); - ERR_FAIL_COND_V_MSG(!platformSurfaceExtFound, ERR_CANT_CREATE, "No platform surface extension found, is a driver installed?"); + // obtain available device extensions + uint32_t device_extension_count = 0; + VkResult err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, nullptr); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(device_extension_count == 0, ERR_CANT_CREATE, + "vkEnumerateDeviceExtensionProperties failed to find any extensions\n\n" + "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" + "vkCreateInstance Failure"); + VkExtensionProperties *device_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * device_extension_count); + err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, device_extensions); + if (err) { + free(device_extensions); + ERR_FAIL_V(ERR_CANT_CREATE); + } + +#ifdef DEV_ENABLED + for (uint32_t i = 0; i < device_extension_count; i++) { + print_verbose(String("VULKAN: Found device extension ") + String(device_extensions[i].extensionName)); + } +#endif + + // Enable all extensions that are supported and requested + for (uint32_t i = 0; i < device_extension_count; i++) { + CharString extension_name(device_extensions[i].extensionName); + if (requested_device_extensions.has(extension_name)) { + enabled_device_extension_names.insert(extension_name); + } + } + + // Now check our requested extensions + for (KeyValue<CharString, bool> &requested_extension : requested_device_extensions) { + if (!enabled_device_extension_names.has(requested_extension.key)) { + if (requested_extension.value) { + free(device_extensions); + ERR_FAIL_V_MSG(ERR_BUG, + String("vkEnumerateDeviceExtensionProperties failed to find the ") + String(requested_extension.key) + String(" extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\nvkCreateInstance Failure")); + } else { + print_verbose(String("Optional extension ") + String(requested_extension.key) + String(" not found")); + } + } + } + + free(device_extensions); + + device_extensions_initialized = true; return OK; } @@ -441,7 +635,7 @@ String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { res += ", STAGE_MESH_NV"; } - return res.substr(2); // Remove first ", " + return res.substr(2); // Remove first ", ". } uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const { @@ -506,7 +700,7 @@ String VulkanContext::SubgroupCapabilities::supported_operations_desc() const { res += ", FEATURE_PARTITIONED_NV"; } - return res.substr(2); // Remove first ", " + return res.substr(2); // Remove first ", ". } Error VulkanContext::_check_capabilities() { @@ -522,6 +716,9 @@ Error VulkanContext::_check_capabilities() { vrs_capabilities.pipeline_vrs_supported = false; vrs_capabilities.primitive_vrs_supported = false; vrs_capabilities.attachment_vrs_supported = false; + vrs_capabilities.min_texel_size = Size2i(); + vrs_capabilities.max_texel_size = Size2i(); + vrs_capabilities.texel_size = Size2i(); multiview_capabilities.is_supported = false; multiview_capabilities.geometry_shader_is_supported = false; multiview_capabilities.tessellation_shader_is_supported = false; @@ -538,155 +735,176 @@ Error VulkanContext::_check_capabilities() { storage_buffer_capabilities.storage_push_constant_16_is_supported = false; storage_buffer_capabilities.storage_input_output_16 = false; - // Check for extended features. - PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); - if (vkGetPhysicalDeviceFeatures2_func == nullptr) { - // In Vulkan 1.0 might be accessible under its original extension name. - vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); - } - if (vkGetPhysicalDeviceFeatures2_func != nullptr) { - // Check our extended features. - VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, - /*pNext*/ nullptr, - /*pipelineFragmentShadingRate*/ false, - /*primitiveFragmentShadingRate*/ false, - /*attachmentFragmentShadingRate*/ false, - }; - - VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, - /*pNext*/ &vrs_features, - /*shaderFloat16*/ false, - /*shaderInt8*/ false, - }; + if (is_instance_extension_enabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { + // Check for extended features. + PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); + if (vkGetPhysicalDeviceFeatures2_func == nullptr) { + // In Vulkan 1.0 might be accessible under its original extension name. + vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); + } + if (vkGetPhysicalDeviceFeatures2_func != nullptr) { + // Check our extended features. + VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, + /*pNext*/ nullptr, + /*pipelineFragmentShadingRate*/ false, + /*primitiveFragmentShadingRate*/ false, + /*attachmentFragmentShadingRate*/ false, + }; - VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, - /*pNext*/ &shader_features, - /*storageBuffer16BitAccess*/ false, - /*uniformAndStorageBuffer16BitAccess*/ false, - /*storagePushConstant16*/ false, - /*storageInputOutput16*/ false, - }; + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, + /*pNext*/ &vrs_features, + /*shaderFloat16*/ false, + /*shaderInt8*/ false, + }; - VkPhysicalDeviceMultiviewFeatures multiview_features = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, - /*pNext*/ &storage_feature, - /*multiview*/ false, - /*multiviewGeometryShader*/ false, - /*multiviewTessellationShader*/ false, - }; + VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, + /*pNext*/ &shader_features, + /*storageBuffer16BitAccess*/ false, + /*uniformAndStorageBuffer16BitAccess*/ false, + /*storagePushConstant16*/ false, + /*storageInputOutput16*/ false, + }; - VkPhysicalDeviceFeatures2 device_features; - device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - device_features.pNext = &multiview_features; + VkPhysicalDeviceMultiviewFeatures multiview_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + /*pNext*/ &storage_feature, + /*multiview*/ false, + /*multiviewGeometryShader*/ false, + /*multiviewTessellationShader*/ false, + }; - vkGetPhysicalDeviceFeatures2_func(gpu, &device_features); + VkPhysicalDeviceFeatures2 device_features; + device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + device_features.pNext = &multiview_features; - vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate; - vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate; - vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate; + vkGetPhysicalDeviceFeatures2_func(gpu, &device_features); - multiview_capabilities.is_supported = multiview_features.multiview; - multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; - multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; + // We must check that the relative extension is present before assuming a + // feature as enabled. Actually, according to the spec we shouldn't add the + // structs in pNext at all, but this works fine. + // See also: https://github.com/godotengine/godot/issues/65409 + if (is_device_extension_enabled(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { + vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate; + vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate; + vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate; + } - shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; - shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; + if (is_device_extension_enabled(VK_KHR_MULTIVIEW_EXTENSION_NAME)) { + multiview_capabilities.is_supported = multiview_features.multiview; + multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; + multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; + } - storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess; - storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess; - storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16; - storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; - } + if (is_device_extension_enabled(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { + shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; + shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; + } - // Check extended properties. - PFN_vkGetPhysicalDeviceProperties2 device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2"); - if (device_properties_func == nullptr) { - // In Vulkan 1.0 might be accessible under its original extension name. - device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR"); - } - if (device_properties_func != nullptr) { - VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties; - VkPhysicalDeviceMultiviewProperties multiviewProperties; - VkPhysicalDeviceSubgroupProperties subgroupProperties; - VkPhysicalDeviceProperties2 physicalDeviceProperties; - void *nextptr = nullptr; + if (is_device_extension_enabled(VK_KHR_16BIT_STORAGE_EXTENSION_NAME)) { + storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess; + storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess; + storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16; + storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; + } + } - subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - subgroupProperties.pNext = nextptr; - nextptr = &subgroupProperties; + // Check extended properties. + PFN_vkGetPhysicalDeviceProperties2 device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2"); + if (device_properties_func == nullptr) { + // In Vulkan 1.0 might be accessible under its original extension name. + device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR"); + } + if (device_properties_func != nullptr) { + VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties{}; + VkPhysicalDeviceMultiviewProperties multiviewProperties{}; + VkPhysicalDeviceSubgroupProperties subgroupProperties{}; + VkPhysicalDeviceProperties2 physicalDeviceProperties{}; + void *nextptr = nullptr; + + if (device_api_version >= VK_API_VERSION_1_1) { // Vulkan 1.1 or higher + subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + subgroupProperties.pNext = nextptr; + + nextptr = &subgroupProperties; + } - if (multiview_capabilities.is_supported) { - multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; - multiviewProperties.pNext = nextptr; + if (multiview_capabilities.is_supported) { + multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; + multiviewProperties.pNext = nextptr; - nextptr = &multiviewProperties; - } + nextptr = &multiviewProperties; + } - if (vrs_capabilities.attachment_vrs_supported) { - vrsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; - vrsProperties.pNext = nextptr; + if (vrs_capabilities.attachment_vrs_supported) { + vrsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; + vrsProperties.pNext = nextptr; - nextptr = &vrsProperties; - } + nextptr = &vrsProperties; + } - physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - physicalDeviceProperties.pNext = nextptr; + physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physicalDeviceProperties.pNext = nextptr; - device_properties_func(gpu, &physicalDeviceProperties); + device_properties_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; + 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; - if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { - print_verbose("- Vulkan Variable Rate Shading supported:"); - if (vrs_capabilities.pipeline_vrs_supported) { - print_verbose(" Pipeline fragment shading rate"); - } - if (vrs_capabilities.primitive_vrs_supported) { - print_verbose(" Primitive fragment shading rate"); - } - if (vrs_capabilities.attachment_vrs_supported) { - // TODO expose these somehow to the end user - vrs_capabilities.min_texel_size.x = vrsProperties.minFragmentShadingRateAttachmentTexelSize.width; - vrs_capabilities.min_texel_size.y = vrsProperties.minFragmentShadingRateAttachmentTexelSize.height; - vrs_capabilities.max_texel_size.x = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.width; - vrs_capabilities.max_texel_size.y = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.height; + if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { + print_verbose("- Vulkan Variable Rate Shading supported:"); + if (vrs_capabilities.pipeline_vrs_supported) { + print_verbose(" Pipeline fragment shading rate"); + } + if (vrs_capabilities.primitive_vrs_supported) { + print_verbose(" Primitive fragment shading rate"); + } + if (vrs_capabilities.attachment_vrs_supported) { + // TODO expose these somehow to the end user. + vrs_capabilities.min_texel_size.x = vrsProperties.minFragmentShadingRateAttachmentTexelSize.width; + vrs_capabilities.min_texel_size.y = vrsProperties.minFragmentShadingRateAttachmentTexelSize.height; + vrs_capabilities.max_texel_size.x = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.width; + vrs_capabilities.max_texel_size.y = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.height; + + // We'll attempt to default to a texel size of 16x16 + vrs_capabilities.texel_size.x = CLAMP(16, vrs_capabilities.min_texel_size.x, vrs_capabilities.max_texel_size.x); + vrs_capabilities.texel_size.y = CLAMP(16, vrs_capabilities.min_texel_size.y, vrs_capabilities.max_texel_size.y); + + print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")")); + } - print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")")); + } else { + print_verbose("- Vulkan Variable Rate Shading not supported"); } - } else { - print_verbose("- Vulkan Variable Rate Shading not supported"); - } + if (multiview_capabilities.is_supported) { + multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount; + multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex; - if (multiview_capabilities.is_supported) { - multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount; - multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex; + print_verbose("- Vulkan multiview supported:"); + print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count)); + print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count)); + } else { + print_verbose("- Vulkan multiview not supported"); + } - print_verbose("- Vulkan multiview supported:"); - print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count)); - print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count)); + print_verbose("- Vulkan subgroup:"); + print_verbose(" size: " + itos(subgroup_capabilities.size)); + print_verbose(" stages: " + subgroup_capabilities.supported_stages_desc()); + print_verbose(" supported ops: " + subgroup_capabilities.supported_operations_desc()); + if (subgroup_capabilities.quadOperationsInAllStages) { + print_verbose(" quad operations in all stages"); + } } else { - print_verbose("- Vulkan multiview not supported"); + print_verbose("- Couldn't call vkGetPhysicalDeviceProperties2"); } - - print_verbose("- Vulkan subgroup:"); - print_verbose(" size: " + itos(subgroup_capabilities.size)); - print_verbose(" stages: " + subgroup_capabilities.supported_stages_desc()); - print_verbose(" supported ops: " + subgroup_capabilities.supported_operations_desc()); - if (subgroup_capabilities.quadOperationsInAllStages) { - print_verbose(" quad operations in all stages"); - } - } else { - print_verbose("- Couldn't call vkGetPhysicalDeviceProperties2"); } return OK; @@ -698,27 +916,40 @@ Error VulkanContext::_create_instance() { // Initialize extensions. { - Error err = _initialize_extensions(); + Error err = _initialize_instance_extensions(); if (err != OK) { return err; } } - CharString cs = ProjectSettings::get_singleton()->get("application/config/name").operator String().utf8(); + int enabled_extension_count = 0; + const char *enabled_extension_names[MAX_EXTENSIONS]; + ERR_FAIL_COND_V(enabled_instance_extension_names.size() > MAX_EXTENSIONS, ERR_CANT_CREATE); + for (const CharString &extension_name : enabled_instance_extension_names) { + enabled_extension_names[enabled_extension_count++] = extension_name.ptr(); + } + + // We'll set application version to the Vulkan version we're developing against, even if our instance is based on + // an older Vulkan version, devices can still support newer versions of Vulkan. + // The exception is when we're on Vulkan 1.0, we should not set this to anything but 1.0. + // Note that this value is only used by validation layers to warn us about version issues. + uint32_t application_api_version = instance_api_version == VK_API_VERSION_1_0 ? VK_API_VERSION_1_0 : VK_API_VERSION_1_2; + + CharString cs = GLOBAL_GET("application/config/name").operator String().utf8(); const VkApplicationInfo app = { /*sType*/ VK_STRUCTURE_TYPE_APPLICATION_INFO, /*pNext*/ nullptr, /*pApplicationName*/ cs.get_data(), - /*applicationVersion*/ 0, + /*applicationVersion*/ 0, // It would be really nice if we store a version number in project settings, say "application/config/version" /*pEngineName*/ VERSION_NAME, /*engineVersion*/ VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH), - /*apiVersion*/ VK_MAKE_VERSION(vulkan_major, vulkan_minor, 0) + /*apiVersion*/ application_api_version }; VkInstanceCreateInfo inst_info{}; inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; inst_info.pApplicationInfo = &app; inst_info.enabledExtensionCount = enabled_extension_count; - inst_info.ppEnabledExtensionNames = (const char *const *)extension_names; + inst_info.ppEnabledExtensionNames = (const char *const *)enabled_extension_names; if (_use_validation_layers()) { _get_preferred_validation_layers(&inst_info.enabledLayerCount, &inst_info.ppEnabledLayerNames); } @@ -728,10 +959,10 @@ Error VulkanContext::_create_instance() { * After the instance is created, we use the instance-based * function to register the final callback. */ - VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info; - VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info{}; - if (enabled_debug_utils) { - // VK_EXT_debug_utils style + VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info = {}; + VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info = {}; + if (is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { + // VK_EXT_debug_utils style. dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; dbg_messenger_create_info.pNext = nullptr; dbg_messenger_create_info.flags = 0; @@ -743,7 +974,7 @@ Error VulkanContext::_create_instance() { dbg_messenger_create_info.pfnUserCallback = _debug_messenger_callback; dbg_messenger_create_info.pUserData = this; inst_info.pNext = &dbg_messenger_create_info; - } else if (enabled_debug_report) { + } else if (is_instance_extension_enabled(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { dbg_report_callback_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; dbg_report_callback_create_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | @@ -783,7 +1014,7 @@ Error VulkanContext::_create_instance() { volkLoadInstance(inst); #endif - if (enabled_debug_utils) { + if (is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { // Setup VK_EXT_debug_utils function pointers always (we use them for debug labels and names). CreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT"); @@ -824,7 +1055,7 @@ Error VulkanContext::_create_instance() { ERR_FAIL_V(ERR_CANT_CREATE); break; } - } else if (enabled_debug_report) { + } else if (is_instance_extension_enabled(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT"); DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT"); DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT"); @@ -902,8 +1133,8 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { } } else { // TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances. - // The device should really be a preference, but for now choosing a discrete GPU over the - // integrated one is better than the default. + // The device should really be a preference, but for now choosing a discrete GPU over the + // integrated one is better than the default. int type_selected = -1; print_verbose("Vulkan devices:"); @@ -1005,12 +1236,6 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { free(physical_devices); - // Look for device extensions. - uint32_t device_extension_count = 0; - VkBool32 swapchainExtFound = 0; - enabled_extension_count = 0; - memset(extension_names, 0, sizeof(extension_names)); - // Get identifier properties. vkGetPhysicalDeviceProperties(gpu, &gpu_props); @@ -1030,89 +1255,19 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { } } - print_line( - "Vulkan API " + itos(vulkan_major) + "." + itos(vulkan_minor) + "." + itos(vulkan_patch) + - " - " + "Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name); - + // Get device version device_api_version = gpu_props.apiVersion; - err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, nullptr); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - if (device_extension_count > 0) { - VkExtensionProperties *device_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * device_extension_count); - err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, device_extensions); - if (err) { - free(device_extensions); - ERR_FAIL_V(ERR_CANT_CREATE); - } - - for (uint32_t i = 0; i < device_extension_count; i++) { - if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, device_extensions[i].extensionName)) { - swapchainExtFound = 1; - extension_names[enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; - } - if (!strcmp(VK_KHR_MULTIVIEW_EXTENSION_NAME, device_extensions[i].extensionName)) { - // If multiview is supported, enable it. - extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME; - } - if (!strcmp(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, device_extensions[i].extensionName)) { - // if shading rate image is supported, enable it - extension_names[enabled_extension_count++] = VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME; - } - if (!strcmp(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, device_extensions[i].extensionName)) { - extension_names[enabled_extension_count++] = VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME; - } - if (enabled_extension_count >= MAX_EXTENSIONS) { - free(device_extensions); - ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); - } - } - - if (VK_KHR_incremental_present_enabled) { - // Even though the user "enabled" the extension via the command - // line, we must make sure that it's enumerated for use with the - // device. Therefore, disable it here, and re-enable it again if - // enumerated. - VK_KHR_incremental_present_enabled = false; - for (uint32_t i = 0; i < device_extension_count; i++) { - if (!strcmp(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, device_extensions[i].extensionName)) { - extension_names[enabled_extension_count++] = VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME; - VK_KHR_incremental_present_enabled = true; - } - if (enabled_extension_count >= MAX_EXTENSIONS) { - free(device_extensions); - ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); - } - } - } + // Output our device version + print_line("Vulkan API " + get_device_api_version() + " - " + "Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name); - if (VK_GOOGLE_display_timing_enabled) { - // Even though the user "enabled" the extension via the command - // line, we must make sure that it's enumerated for use with the - // device. Therefore, disable it here, and re-enable it again if - // enumerated. - VK_GOOGLE_display_timing_enabled = false; - for (uint32_t i = 0; i < device_extension_count; i++) { - if (!strcmp(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, device_extensions[i].extensionName)) { - extension_names[enabled_extension_count++] = VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME; - VK_GOOGLE_display_timing_enabled = true; - } - if (enabled_extension_count >= MAX_EXTENSIONS) { - free(device_extensions); - ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); - } - } + { + Error _err = _initialize_device_extensions(); + if (_err != OK) { + return _err; } - - free(device_extensions); } - ERR_FAIL_COND_V_MSG(!swapchainExtFound, ERR_CANT_CREATE, - "vkEnumerateDeviceExtensionProperties failed to find the " VK_KHR_SWAPCHAIN_EXTENSION_NAME - " extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n" - "vkCreateInstance Failure"); - // Call with nullptr data to get count. vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr); ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE); @@ -1173,9 +1328,9 @@ Error VulkanContext::_create_device() { }; nextptr = &shader_features; - VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features; + VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {}; if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { - // insert into our chain to enable these features if they are available + // Insert into our chain to enable these features if they are available. vrs_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; vrs_features.pNext = nextptr; vrs_features.pipelineFragmentShadingRate = vrs_capabilities.pipeline_vrs_supported; @@ -1185,10 +1340,10 @@ Error VulkanContext::_create_device() { nextptr = &vrs_features; } - VkPhysicalDeviceVulkan11Features vulkan11features; - VkPhysicalDevice16BitStorageFeaturesKHR storage_feature; - VkPhysicalDeviceMultiviewFeatures multiview_features; - if (vulkan_major > 1 || vulkan_minor >= 2) { + VkPhysicalDeviceVulkan11Features vulkan11features = {}; + VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {}; + VkPhysicalDeviceMultiviewFeatures multiview_features = {}; + if (device_api_version >= VK_API_VERSION_1_2) { // In Vulkan 1.2 and newer we use a newer struct to enable various features. vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; @@ -1207,7 +1362,7 @@ Error VulkanContext::_create_device() { vulkan11features.shaderDrawParameters = 0; nextptr = &vulkan11features; } else { - // On Vulkan 1.0 and 1.1 we use our older structs to initialise these features. + // On Vulkan 1.0 and 1.1 we use our older structs to initialize these features. storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; storage_feature.pNext = nextptr; storage_feature.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; @@ -1216,7 +1371,7 @@ Error VulkanContext::_create_device() { storage_feature.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; nextptr = &storage_feature; - if (vulkan_major == 1 && vulkan_minor == 1) { + if (device_api_version >= VK_API_VERSION_1_1) { // any Vulkan 1.1.x version multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; multiview_features.pNext = nextptr; multiview_features.multiview = multiview_capabilities.is_supported; @@ -1226,6 +1381,13 @@ Error VulkanContext::_create_device() { } } + uint32_t enabled_extension_count = 0; + const char *enabled_extension_names[MAX_EXTENSIONS]; + ERR_FAIL_COND_V(enabled_device_extension_names.size() > MAX_EXTENSIONS, ERR_CANT_CREATE); + for (const CharString &extension_name : enabled_device_extension_names) { + enabled_extension_names[enabled_extension_count++] = extension_name.ptr(); + } + VkDeviceCreateInfo sdevice = { /*sType*/ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, /*pNext*/ nextptr, @@ -1235,7 +1397,7 @@ Error VulkanContext::_create_device() { /*enabledLayerCount*/ 0, /*ppEnabledLayerNames*/ nullptr, /*enabledExtensionCount*/ enabled_extension_count, - /*ppEnabledExtensionNames*/ (const char *const *)extension_names, + /*ppEnabledExtensionNames*/ (const char *const *)enabled_extension_names, /*pEnabledFeatures*/ &physical_device_features, // If specific features are required, pass them in here. }; if (separate_present_queue) { @@ -1323,7 +1485,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR); GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR); GET_DEVICE_PROC_ADDR(device, QueuePresentKHR); - if (VK_GOOGLE_display_timing_enabled) { + if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { GET_DEVICE_PROC_ADDR(device, GetRefreshCycleDurationGOOGLE); GET_DEVICE_PROC_ADDR(device, GetPastPresentationTimingGOOGLE); } @@ -1354,7 +1516,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { color_space = surfFormats[0].colorSpace; } else { // These should be ordered with the ones we want to use on top and fallback modes further down - // we want a 32bit RGBA unsigned normalised buffer or similar. + // we want a 32bit RGBA unsigned normalized buffer or similar. const VkFormat allowed_formats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM @@ -1436,6 +1598,24 @@ bool VulkanContext::_use_validation_layers() { return Engine::get_singleton()->is_validation_layers_enabled(); } +VkExtent2D VulkanContext::_compute_swapchain_extent(const VkSurfaceCapabilitiesKHR &p_surf_capabilities, int *p_window_width, int *p_window_height) const { + // Width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. + if (p_surf_capabilities.currentExtent.width == 0xFFFFFFFF) { + // If the surface size is undefined, the size is set to the size + // of the images requested, which must fit within the minimum and + // maximum values. + VkExtent2D extent = {}; + extent.width = CLAMP((uint32_t)(*p_window_width), p_surf_capabilities.minImageExtent.width, p_surf_capabilities.maxImageExtent.width); + extent.height = CLAMP((uint32_t)(*p_window_height), p_surf_capabilities.minImageExtent.height, p_surf_capabilities.maxImageExtent.height); + return extent; + } else { + // If the surface size is defined, the swap chain size must match. + *p_window_width = p_surf_capabilities.currentExtent.width; + *p_window_height = p_surf_capabilities.currentExtent.height; + return p_surf_capabilities.currentExtent; + } +} + Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) { ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER); @@ -1576,32 +1756,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { ERR_FAIL_V(ERR_CANT_CREATE); } - VkExtent2D swapchainExtent; - // Width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. - if (surfCapabilities.currentExtent.width == 0xFFFFFFFF) { - // If the surface size is undefined, the size is set to the size - // of the images requested, which must fit within the minimum and - // maximum values. - swapchainExtent.width = window->width; - swapchainExtent.height = window->height; - - if (swapchainExtent.width < surfCapabilities.minImageExtent.width) { - swapchainExtent.width = surfCapabilities.minImageExtent.width; - } else if (swapchainExtent.width > surfCapabilities.maxImageExtent.width) { - swapchainExtent.width = surfCapabilities.maxImageExtent.width; - } - - if (swapchainExtent.height < surfCapabilities.minImageExtent.height) { - swapchainExtent.height = surfCapabilities.minImageExtent.height; - } else if (swapchainExtent.height > surfCapabilities.maxImageExtent.height) { - swapchainExtent.height = surfCapabilities.maxImageExtent.height; - } - } else { - // If the surface size is defined, the swap chain size must match. - swapchainExtent = surfCapabilities.currentExtent; - window->width = surfCapabilities.currentExtent.width; - window->height = surfCapabilities.currentExtent.height; - } + VkExtent2D swapchainExtent = _compute_swapchain_extent(surfCapabilities, &window->width, &window->height); if (window->width == 0 || window->height == 0) { free(presentModes); @@ -1611,17 +1766,17 @@ Error VulkanContext::_update_swap_chain(Window *window) { // The FIFO present mode is guaranteed by the spec to be supported // and to have no tearing. It's a great default present mode to use. - // There are times when you may wish to use another present mode. The - // following code shows how to select them, and the comments provide some - // reasons you may wish to use them. + // There are times when you may wish to use another present mode. The + // following code shows how to select them, and the comments provide some + // reasons you may wish to use them. // // It should be noted that Vulkan 1.0 doesn't provide a method for - // synchronizing rendering with the presentation engine's display. There + // synchronizing rendering with the presentation engine's display. There // is a method provided for throttling rendering with the display, but // there are some presentation engines for which this method will not work. // If an application doesn't throttle its rendering, and if it renders much // faster than the refresh rate of the display, this can waste power on - // mobile devices. That is because power is being spent rendering images + // mobile devices. That is because power is being spent rendering images // that may never be seen. // VK_PRESENT_MODE_IMMEDIATE_KHR is for applications that don't care about @@ -1666,7 +1821,22 @@ Error VulkanContext::_update_swap_chain(Window *window) { if (present_mode_available) { window->presentMode = requested_present_mode; } else { - WARN_PRINT("Requested VSync mode is not available!"); + String present_mode_string; + switch (window->vsync_mode) { + case DisplayServer::VSYNC_MAILBOX: + present_mode_string = "Mailbox"; + break; + case DisplayServer::VSYNC_ADAPTIVE: + present_mode_string = "Adaptive"; + break; + case DisplayServer::VSYNC_ENABLED: + present_mode_string = "Enabled"; + break; + case DisplayServer::VSYNC_DISABLED: + present_mode_string = "Disabled"; + break; + } + WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled.", present_mode_string)); window->vsync_mode = DisplayServer::VSYNC_ENABLED; // Set to default. } @@ -1684,7 +1854,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { // If maxImageCount is 0, we can ask for as many images as we want; // otherwise we're limited to maxImageCount. if ((surfCapabilities.maxImageCount > 0) && (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) { - // Application must settle for fewer images than desired: + // Application must settle for fewer images than desired. desiredNumOfSwapchainImages = surfCapabilities.maxImageCount; } @@ -1695,18 +1865,22 @@ Error VulkanContext::_update_swap_chain(Window *window) { preTransform = surfCapabilities.currentTransform; } - // Find a supported composite alpha mode - one of these is guaranteed to be set. VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, - }; - for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { - if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { - compositeAlpha = compositeAlphaFlags[i]; - break; + + if (OS::get_singleton()->is_layered_allowed() || !(surfCapabilities.supportedCompositeAlpha & compositeAlpha)) { + // Find a supported composite alpha mode - one of these is guaranteed to be set. + VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + }; + + for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { + if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { + compositeAlpha = compositeAlphaFlags[i]; + break; + } } } @@ -2028,14 +2202,14 @@ Error VulkanContext::prepare_buffers() { } do { - // Get the index of the next available swapchain image: + // Get the index of the next available swapchain image. err = fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX, w->image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); if (err == VK_ERROR_OUT_OF_DATE_KHR) { // Swapchain is out of date (e.g. the window was resized) and - // must be recreated: + // must be recreated. print_verbose("Vulkan: Early out of date swapchain, recreating."); // resize_notify(); _update_swap_chain(w); @@ -2066,9 +2240,9 @@ Error VulkanContext::swap_buffers() { VkResult err; #if 0 - if (VK_GOOGLE_display_timing_enabled) { + if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { // Look at what happened to previous presents, and make appropriate - // adjustments in timing: + // adjustments in timing. DemoUpdateTargetIPD(demo); // Note: a real application would position its geometry to that it's in @@ -2187,7 +2361,7 @@ Error VulkanContext::swap_buffers() { } #if 0 - if (VK_KHR_incremental_present_enabled) { + if (is_device_extension_enabled(VK_KHR_incremental_present_enabled)) { // If using VK_KHR_incremental_present, we provide a hint of the region // that contains changed content relative to the previously-presented // image. The implementation can use this hint in order to save @@ -2218,7 +2392,7 @@ Error VulkanContext::swap_buffers() { #endif #if 0 - if (VK_GOOGLE_display_timing_enabled) { + if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { VkPresentTimeGOOGLE ptime; if (prev_desired_present_time == 0) { // This must be the first present for this swapchain. @@ -2231,7 +2405,7 @@ Error VulkanContext::swap_buffers() { uint64_t curtime = getTimeInNanoseconds(); if (curtime == 0) { // Since we didn't find out the current time, don't give a - // desiredPresentTime: + // desiredPresentTime. ptime.desiredPresentTime = 0; } else { ptime.desiredPresentTime = curtime + (target_IPD >> 1); @@ -2248,13 +2422,11 @@ Error VulkanContext::swap_buffers() { /*swapchainCount*/ present.swapchainCount, /*pTimes*/ &ptime, }; - if (VK_GOOGLE_display_timing_enabled) { + if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { present.pNext = &present_time; } } #endif - static int total_frames = 0; - total_frames++; // print_line("current buffer: " + itos(current_buffer)); err = fpQueuePresentKHR(present_queue, &present); @@ -2263,7 +2435,7 @@ Error VulkanContext::swap_buffers() { if (err == VK_ERROR_OUT_OF_DATE_KHR) { // Swapchain is out of date (e.g. the window was resized) and - // must be recreated: + // must be recreated. print_verbose("Vulkan: Swapchain is out of date, recreating."); resize_notify(); } else if (err == VK_SUBOPTIMAL_KHR) { @@ -2323,6 +2495,13 @@ RID VulkanContext::local_device_create() { queues[0].pQueuePriorities = queue_priorities; queues[0].flags = 0; + uint32_t enabled_extension_count = 0; + const char *enabled_extension_names[MAX_EXTENSIONS]; + ERR_FAIL_COND_V(enabled_device_extension_names.size() > MAX_EXTENSIONS, RID()); + for (const CharString &extension_name : enabled_device_extension_names) { + enabled_extension_names[enabled_extension_count++] = extension_name.ptr(); + } + VkDeviceCreateInfo sdevice = { /*sType =*/VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, /*pNext */ nullptr, @@ -2332,7 +2511,7 @@ RID VulkanContext::local_device_create() { /*enabledLayerCount */ 0, /*ppEnabledLayerNames */ nullptr, /*enabledExtensionCount */ enabled_extension_count, - /*ppEnabledExtensionNames */ (const char *const *)extension_names, + /*ppEnabledExtensionNames */ (const char *const *)enabled_extension_names, /*pEnabledFeatures */ &physical_device_features, // If specific features are required, pass them in here. }; err = vkCreateDevice(gpu, &sdevice, nullptr, &ld.device); @@ -2397,7 +2576,7 @@ void VulkanContext::local_device_free(RID p_local_device) { } void VulkanContext::command_begin_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color) { - if (!enabled_debug_utils) { + if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { return; } @@ -2414,7 +2593,7 @@ void VulkanContext::command_begin_label(VkCommandBuffer p_command_buffer, String } void VulkanContext::command_insert_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color) { - if (!enabled_debug_utils) { + if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { return; } CharString cs = p_label_name.utf8(); @@ -2430,14 +2609,14 @@ void VulkanContext::command_insert_label(VkCommandBuffer p_command_buffer, Strin } void VulkanContext::command_end_label(VkCommandBuffer p_command_buffer) { - if (!enabled_debug_utils) { + if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { return; } CmdEndDebugUtilsLabelEXT(p_command_buffer); } void VulkanContext::set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name) { - if (!enabled_debug_utils) { + if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { return; } CharString obj_data = p_object_name.utf8(); @@ -2463,7 +2642,7 @@ RenderingDevice::DeviceType VulkanContext::get_device_type() const { } String VulkanContext::get_device_api_version() const { - return vformat("%d.%d.%d", vulkan_major, vulkan_minor, vulkan_patch); + return vformat("%d.%d.%d", VK_API_VERSION_MAJOR(device_api_version), VK_API_VERSION_MINOR(device_api_version), VK_API_VERSION_PATCH(device_api_version)); } String VulkanContext::get_device_pipeline_cache_uuid() const { @@ -2471,12 +2650,12 @@ String VulkanContext::get_device_pipeline_cache_uuid() const { } DisplayServer::VSyncMode VulkanContext::get_vsync_mode(DisplayServer::WindowID p_window) const { - ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist."); return windows[p_window].vsync_mode; } void VulkanContext::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) { - ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist."); windows[p_window].vsync_mode = p_mode; _update_swap_chain(&windows[p_window]); } @@ -2498,7 +2677,7 @@ VulkanContext::~VulkanContext() { vkDestroySemaphore(device, image_ownership_semaphores[i], nullptr); } } - if (inst_initialized && enabled_debug_utils) { + if (inst_initialized && is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { DestroyDebugUtilsMessengerEXT(inst, dbg_messenger, nullptr); } if (inst_initialized && dbg_debug_report != VK_NULL_HANDLE) { |