From adaf65d1b8a1f07afaef45d762f2830ea929fdbb Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Sat, 16 Aug 2025 23:10:22 -0500 Subject: [PATCH 1/5] VideoBackends:Vulkan: Use vkGetPhysicalDeviceFeatures2 --- .../VideoBackends/Vulkan/VulkanContext.cpp | 61 +++++++++++++------ .../Core/VideoBackends/Vulkan/VulkanContext.h | 7 ++- .../Vulkan/VulkanEntryPoints.inl | 1 + 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 93cd5a969ba..7f85e4515e5 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -20,6 +20,8 @@ static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validatio std::unique_ptr g_vulkan_context; +/// Inserts an element into the front of a pNext chain +/// Element must not be a chain itself template static void InsertIntoChain(Chain* chain, Element* element) { @@ -27,9 +29,22 @@ static void InsertIntoChain(Chain* chain, Element* element) chain->pNext = element; } +/// Appends one pNext chain to another +template +static void ConcatenateChains(Chain1* chain1, Chain2* chain2) +{ + (void)chain1->pNext; // Make sure Chain1 has a pNext + (void)chain2->pNext; // Make sure Chain2 has a pNext + VkBaseOutStructure* next = reinterpret_cast(chain1); + while (next->pNext) + next = next->pNext; + next->pNext = reinterpret_cast(chain2); +} + VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) { - VkPhysicalDeviceFeatures features; + VkPhysicalDeviceFeatures2 features2; + VkPhysicalDeviceFeatures& features = features2.features; VkPhysicalDeviceProperties2 properties2; VkPhysicalDeviceProperties& properties = properties2.properties; @@ -41,6 +56,8 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) { VkPhysicalDeviceSubgroupProperties properties_subgroup = {}; VkPhysicalDeviceVulkan12Properties properties_vk12 = {}; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + features2.pNext = nullptr; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = nullptr; properties_subgroup.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; @@ -53,6 +70,7 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) } vkGetPhysicalDeviceProperties2(device, &properties2); + vkGetPhysicalDeviceFeatures2(device, &features2); if (apiVersion >= VK_API_VERSION_1_2) { @@ -103,25 +121,25 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) textureCompressionBC = features.textureCompressionBC != VK_FALSE; } -VkPhysicalDeviceFeatures VulkanContext::PhysicalDeviceInfo::features() const +VulkanContext::DeviceFeatures::DeviceFeatures(const PhysicalDeviceInfo& info) { - VkPhysicalDeviceFeatures features; - memset(&features, 0, sizeof(features)); - features.dualSrcBlend = dualSrcBlend ? VK_TRUE : VK_FALSE; - features.geometryShader = geometryShader ? VK_TRUE : VK_FALSE; - features.samplerAnisotropy = samplerAnisotropy ? VK_TRUE : VK_FALSE; - features.logicOp = logicOp ? VK_TRUE : VK_FALSE; - features.fragmentStoresAndAtomics = fragmentStoresAndAtomics ? VK_TRUE : VK_FALSE; - features.sampleRateShading = sampleRateShading ? VK_TRUE : VK_FALSE; - features.largePoints = largePoints ? VK_TRUE : VK_FALSE; - features.shaderStorageImageMultisample = shaderStorageImageMultisample ? VK_TRUE : VK_FALSE; + memset(this, 0, sizeof(*this)); + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + VkPhysicalDeviceFeatures& features = features2.features; + features.dualSrcBlend = info.dualSrcBlend ? VK_TRUE : VK_FALSE; + features.geometryShader = info.geometryShader ? VK_TRUE : VK_FALSE; + features.samplerAnisotropy = info.samplerAnisotropy ? VK_TRUE : VK_FALSE; + features.logicOp = info.logicOp ? VK_TRUE : VK_FALSE; + features.fragmentStoresAndAtomics = info.fragmentStoresAndAtomics ? VK_TRUE : VK_FALSE; + features.sampleRateShading = info.sampleRateShading ? VK_TRUE : VK_FALSE; + features.largePoints = info.largePoints ? VK_TRUE : VK_FALSE; + features.shaderStorageImageMultisample = info.shaderStorageImageMultisample ? VK_TRUE : VK_FALSE; features.shaderTessellationAndGeometryPointSize = - shaderTessellationAndGeometryPointSize ? VK_TRUE : VK_FALSE; - features.occlusionQueryPrecise = occlusionQueryPrecise ? VK_TRUE : VK_FALSE; - features.shaderClipDistance = shaderClipDistance ? VK_TRUE : VK_FALSE; - features.depthClamp = depthClamp ? VK_TRUE : VK_FALSE; - features.textureCompressionBC = textureCompressionBC ? VK_TRUE : VK_FALSE; - return features; + info.shaderTessellationAndGeometryPointSize ? VK_TRUE : VK_FALSE; + features.occlusionQueryPrecise = info.occlusionQueryPrecise ? VK_TRUE : VK_FALSE; + features.shaderClipDistance = info.shaderClipDistance ? VK_TRUE : VK_FALSE; + features.depthClamp = info.depthClamp ? VK_TRUE : VK_FALSE; + features.textureCompressionBC = info.textureCompressionBC ? VK_TRUE : VK_FALSE; } VulkanContext::VulkanContext(VkInstance instance, VkPhysicalDevice physical_device) @@ -803,8 +821,11 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la WarnMissingDeviceFeatures(); - VkPhysicalDeviceFeatures device_features = m_device_info.features(); - device_info.pEnabledFeatures = &device_features; + DeviceFeatures device_features(m_device_info); + if (m_device_info.apiVersion >= VK_API_VERSION_1_1) + ConcatenateChains(&device_info, &device_features.features2); + else + device_info.pEnabledFeatures = &device_features.features2.features; // Enable debug layer on debug builds if (enable_validation_layer) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index f59c7421f4f..ebef1a336ee 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -22,7 +22,6 @@ public: { PhysicalDeviceInfo(const PhysicalDeviceInfo&) = default; explicit PhysicalDeviceInfo(VkPhysicalDevice device); - VkPhysicalDeviceFeatures features() const; char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]; u8 pipelineCacheUUID[VK_UUID_SIZE]; @@ -55,6 +54,12 @@ public: bool textureCompressionBC; bool shaderSubgroupOperations = false; }; + struct DeviceFeatures + { + DeviceFeatures(const PhysicalDeviceInfo& info); + DeviceFeatures(DeviceFeatures&&) = delete; // Contains internal pointers + VkPhysicalDeviceFeatures2 features2; + }; VulkanContext(VkInstance instance, VkPhysicalDevice physical_device); ~VulkanContext(); diff --git a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl index d716ce49d28..a364ef518b4 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl +++ b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl @@ -67,6 +67,7 @@ VULKAN_INSTANCE_ENTRY_POINT(vkQueueEndDebugUtilsLabelEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkQueueInsertDebugUtilsLabelEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkSetDebugUtilsObjectTagEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkSubmitDebugUtilsMessageEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceFeatures2, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfaceCapabilities2KHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkSetDebugUtilsObjectNameEXT, false) From 3a70e602e9c0483be308005c97f5a795f6678387 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Sun, 17 Aug 2025 01:30:42 -0500 Subject: [PATCH 2/5] VideoBackends:Vulkan: Move extension checking to PhysicalDeviceInfo --- .../VideoBackends/Vulkan/VulkanContext.cpp | 182 ++++++++++++------ .../Core/VideoBackends/Vulkan/VulkanContext.h | 18 +- 2 files changed, 135 insertions(+), 65 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 7f85e4515e5..d701d72c07b 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -41,6 +41,51 @@ static void ConcatenateChains(Chain1* chain1, Chain2* chain2) next->pNext = reinterpret_cast(chain2); } +static const char* ExtensionName(VulkanContext::Extension ext) +{ + using Ext = VulkanContext::Extension; + // clang-format off + switch (ext) + { + case Ext::KHR_swapchain: return VK_KHR_SWAPCHAIN_EXTENSION_NAME; + case Ext::KHR_get_physical_device_properties2: return VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; +#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN + case Ext::EXT_full_screen_exclusive: return VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME; +#endif + case Ext::EXT_memory_budget: return VK_EXT_MEMORY_BUDGET_EXTENSION_NAME; + case Ext::EXT_depth_clamp_control: return VK_EXT_DEPTH_CLAMP_CONTROL_EXTENSION_NAME; + case Ext::EXT_depth_range_unrestricted: return VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME; + } + // clang-format on + return "UNKNOWN_VULKAN_EXTENSION"; +} + +static std::vector GetExtensionProperties(VkPhysicalDevice device) +{ + uint32_t count = 0; + VkResult res = vkEnumerateDeviceExtensionProperties(device, nullptr, &count, nullptr); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: "); + count = 0; + } + std::vector extensions(count); + if (count) + { + res = vkEnumerateDeviceExtensionProperties(device, nullptr, &count, extensions.data()); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: "); + extensions.clear(); + } + else if (count < extensions.size()) + { + extensions.resize(count); + } + } + return extensions; +} + VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) { VkPhysicalDeviceFeatures2 features2; @@ -52,6 +97,18 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) vkGetPhysicalDeviceFeatures(device, &features); apiVersion = vkGetPhysicalDeviceProperties2 ? properties.apiVersion : VK_API_VERSION_1_0; + DEBUG_LOG_FMT(VIDEO, "Vulkan: Dumping extensions for {}...", properties.deviceName); + for (const VkExtensionProperties& ext : GetExtensionProperties(device)) + { + DEBUG_LOG_FMT(VIDEO, " Supports {}", ext.extensionName); + for (uint32_t i = 0; i < extensions.size(); i++) + { + auto check = static_cast(i); + if (0 == strcmp(ext.extensionName, ExtensionName(check))) + extensions[check] = true; + } + } + if (apiVersion >= VK_API_VERSION_1_1) { VkPhysicalDeviceSubgroupProperties properties_subgroup = {}; @@ -503,6 +560,9 @@ void VulkanContext::PopulateBackendInfoFeatures(BackendInfo* backend_info, VkPhy info.fragmentStoresAndAtomics; backend_info->bSupportsSSAA = info.sampleRateShading; backend_info->bSupportsLogicOp = info.logicOp; + backend_info->bSupportsUnrestrictedDepthRange = + info.extensions[Extension::EXT_depth_clamp_control] && + info.extensions[Extension::EXT_depth_range_unrestricted]; // Metal doesn't support this. backend_info->bSupportsLodBiasInSampler = info.driverID != VK_DRIVER_ID_MOLTENVK; @@ -552,6 +612,9 @@ void VulkanContext::PopulateBackendInfoFeatures(BackendInfo* backend_info, VkPhy // Dynamic sampler indexing locks up Intel GPUs on MoltenVK/Metal if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DYNAMIC_SAMPLER_INDEXING)) backend_info->bSupportsDynamicSamplerIndexing = false; + + if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DEPTH_CLAMP_CONTROL)) + backend_info->bSupportsUnrestrictedDepthRange = false; } void VulkanContext::PopulateBackendInfoMultisampleModes(BackendInfo* backend_info, @@ -630,68 +693,72 @@ std::unique_ptr VulkanContext::Create(VkInstance instance, VkPhys return context; } +static bool LogExtension(const VulkanContext::PhysicalDeviceInfo& info, + VulkanContext::Extension extension, bool required, bool ignored) +{ + using namespace Common::Log; + const char* name = ExtensionName(extension); + const char* type = required ? "required" : "optional"; + const char* action = ignored ? "Ignoring" : "Enabling"; + LogLevel level = LogLevel::LINFO; + bool enabled = info.extensions[extension]; + if (!enabled) + { + action = "Missing"; + if (required) + level = LogLevel::LERROR; + } + GENERIC_LOG_FMT(LogType::VIDEO, level, "Vulkan: {} {} extension {}.", action, type, name); + return enabled; +} + +static bool RequiredExtension(const VulkanContext::PhysicalDeviceInfo& info, + VulkanContext::Extension extension) +{ + return LogExtension(info, extension, true, false); +} + +static bool OptionalExtension(const VulkanContext::PhysicalDeviceInfo& info, + VulkanContext::Extension extension) +{ + return LogExtension(info, extension, false, false); +} + +static void IgnoreExtension(VulkanContext::PhysicalDeviceInfo* info, + VulkanContext::Extension extension) +{ + LogExtension(*info, extension, false, true); + info->extensions[extension] = false; +} + bool VulkanContext::SelectDeviceExtensions(bool enable_surface) { - u32 extension_count = 0; - VkResult res = - vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count, nullptr); - if (res != VK_SUCCESS) + if (enable_surface) { - LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: "); - return false; + if (!RequiredExtension(m_device_info, Extension::KHR_swapchain)) + return false; } - - if (extension_count == 0) + else { - ERROR_LOG_FMT(VIDEO, "Vulkan: No extensions supported by device."); - return false; + m_device_info.extensions[Extension::KHR_swapchain] = false; } - - std::vector available_extension_list(extension_count); - res = vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count, - available_extension_list.data()); - ASSERT(res == VK_SUCCESS); - - for (const auto& extension_properties : available_extension_list) - INFO_LOG_FMT(VIDEO, "Available extension: {}", extension_properties.extensionName); - - auto AddExtension = [&](const char* name, bool required) { - if (Common::Contains(available_extension_list, std::string_view{name}, - &VkExtensionProperties::extensionName)) - { - INFO_LOG_FMT(VIDEO, "Enabling extension: {}", name); - m_device_extensions.push_back(name); - return true; - } - - if (required) - ERROR_LOG_FMT(VIDEO, "Vulkan: Missing required extension {}.", name); - - return false; - }; - - if (enable_surface && !AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true)) - return false; - #ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN // VK_EXT_full_screen_exclusive - if (AddExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, true)) + if (OptionalExtension(m_device_info, Extension::EXT_full_screen_exclusive)) INFO_LOG_FMT(VIDEO, "Using VK_EXT_full_screen_exclusive for exclusive fullscreen."); #endif - - AddExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false); - AddExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, false); - - if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DEPTH_CLAMP_CONTROL)) + OptionalExtension(m_device_info, Extension::KHR_get_physical_device_properties2); + OptionalExtension(m_device_info, Extension::EXT_memory_budget); + if (g_backend_info.bSupportsUnrestrictedDepthRange) { - // Unrestricted depth range is one of the few extensions that changes the behavior - // of Vulkan just by being enabled, so we rely on lazy evaluation to ensure it is - // not enabled unless depth clamp control is supported. - g_backend_info.bSupportsUnrestrictedDepthRange = - AddExtension(VK_EXT_DEPTH_CLAMP_CONTROL_EXTENSION_NAME, false) && - AddExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, false); + OptionalExtension(m_device_info, Extension::EXT_depth_clamp_control); + OptionalExtension(m_device_info, Extension::EXT_depth_range_unrestricted); + } + else + { + IgnoreExtension(&m_device_info, Extension::EXT_depth_clamp_control); + IgnoreExtension(&m_device_info, Extension::EXT_depth_range_unrestricted); } - return true; } @@ -811,8 +878,9 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la // convert std::string list to a char pointer list which we can feed in std::vector extension_name_pointers; - for (const std::string& name : m_device_extensions) - extension_name_pointers.push_back(name.c_str()); + for (size_t i = 0; i < m_device_info.extensions.size(); i++) + if (m_device_info.extensions[static_cast(i)]) + extension_name_pointers.push_back(ExtensionName(static_cast(i))); device_info.enabledLayerCount = 0; device_info.ppEnabledLayerNames = nullptr; @@ -869,7 +937,7 @@ bool VulkanContext::CreateAllocator(u32 vk_api_version) allocator_info.vulkanApiVersion = vk_api_version; allocator_info.pTypeExternalMemoryHandleTypes = nullptr; - if (SupportsDeviceExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) + if (m_device_info.extensions[Extension::EXT_memory_budget]) allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; VkResult res = vmaCreateAllocator(&allocator_info, &m_allocator); @@ -946,12 +1014,6 @@ void VulkanContext::DisableDebugUtils() } } -bool VulkanContext::SupportsDeviceExtension(const char* name) const -{ - return std::ranges::any_of(m_device_extensions, - [name](const std::string& extension) { return extension == name; }); -} - static bool DriverIsMesa(VkDriverId driver_id) { switch (driver_id) @@ -1088,7 +1150,7 @@ bool VulkanContext::SupportsExclusiveFullscreen(const WindowSystemInfo& wsi, VkS { #ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN if (!surface || !vkGetPhysicalDeviceSurfaceCapabilities2KHR || - !SupportsDeviceExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME)) + !m_device_info.extensions[Extension::EXT_full_screen_exclusive]) { return false; } diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index ebef1a336ee..8cd3b1a6082 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -18,6 +18,18 @@ namespace Vulkan class VulkanContext { public: + enum class Extension : uint32_t + { + KHR_get_physical_device_properties2, + KHR_swapchain, +#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN + EXT_full_screen_exclusive, +#endif + EXT_memory_budget, + EXT_depth_clamp_control, + EXT_depth_range_unrestricted, + Last = EXT_depth_range_unrestricted + }; struct PhysicalDeviceInfo { PhysicalDeviceInfo(const PhysicalDeviceInfo&) = default; @@ -53,6 +65,7 @@ public: bool depthClamp; bool textureCompressionBC; bool shaderSubgroupOperations = false; + Common::EnumMap extensions = {}; }; struct DeviceFeatures { @@ -122,9 +135,6 @@ public: VkDeviceSize GetBufferImageGranularity() const { return m_device_info.bufferImageGranularity; } float GetMaxSamplerAnisotropy() const { return m_device_info.maxSamplerAnisotropy; } - // Returns true if the specified extension is supported and enabled. - bool SupportsDeviceExtension(const char* name) const; - // Returns true if exclusive fullscreen is supported for the given surface. bool SupportsExclusiveFullscreen(const WindowSystemInfo& wsi, VkSurfaceKHR surface); @@ -160,8 +170,6 @@ private: VkDebugUtilsMessengerEXT m_debug_utils_messenger = VK_NULL_HANDLE; PhysicalDeviceInfo m_device_info; - - std::vector m_device_extensions; }; extern std::unique_ptr g_vulkan_context; From 94acd94274aafe2971e6d5391a200de0d1630ce3 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Sun, 17 Aug 2025 23:06:59 -0500 Subject: [PATCH 3/5] VideoBackends:Vulkan: Allow use of GetPhysicalDevicePropertiesKHR if VK1.1 isn't supported --- .../VideoBackends/Vulkan/VulkanContext.cpp | 19 +++++++++++++------ .../Vulkan/VulkanEntryPoints.inl | 2 ++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index d701d72c07b..8c1eb8beb95 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -109,7 +109,8 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) } } - if (apiVersion >= VK_API_VERSION_1_1) + const bool vk11 = apiVersion >= VK_API_VERSION_1_1; + if (vk11 || vkGetPhysicalDeviceProperties2KHR) { VkPhysicalDeviceSubgroupProperties properties_subgroup = {}; VkPhysicalDeviceVulkan12Properties properties_vk12 = {}; @@ -117,8 +118,12 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) features2.pNext = nullptr; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = nullptr; - properties_subgroup.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - InsertIntoChain(&properties2, &properties_subgroup); + + if (apiVersion >= VK_API_VERSION_1_1) + { + properties_subgroup.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + InsertIntoChain(&properties2, &properties_subgroup); + } if (apiVersion >= VK_API_VERSION_1_2) { @@ -126,8 +131,10 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) InsertIntoChain(&properties2, &properties_vk12); } - vkGetPhysicalDeviceProperties2(device, &properties2); - vkGetPhysicalDeviceFeatures2(device, &features2); + auto getProps = vk11 ? vkGetPhysicalDeviceProperties2 : vkGetPhysicalDeviceProperties2KHR; + getProps(device, &properties2); + auto getFeatures = vk11 ? vkGetPhysicalDeviceFeatures2 : vkGetPhysicalDeviceFeatures2KHR; + getFeatures(device, &features2); if (apiVersion >= VK_API_VERSION_1_2) { @@ -890,7 +897,7 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la WarnMissingDeviceFeatures(); DeviceFeatures device_features(m_device_info); - if (m_device_info.apiVersion >= VK_API_VERSION_1_1) + if (m_device_info.apiVersion >= VK_API_VERSION_1_1 || vkGetPhysicalDeviceProperties2KHR) ConcatenateChains(&device_info, &device_features.features2); else device_info.pEnabledFeatures = &device_features.features2.features; diff --git a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl index a364ef518b4..d55e1c7071c 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl +++ b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl @@ -67,7 +67,9 @@ VULKAN_INSTANCE_ENTRY_POINT(vkQueueEndDebugUtilsLabelEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkQueueInsertDebugUtilsLabelEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkSetDebugUtilsObjectTagEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkSubmitDebugUtilsMessageEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceFeatures2KHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceFeatures2, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2KHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfaceCapabilities2KHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkSetDebugUtilsObjectNameEXT, false) From 7b0870cdc8b63b8f6843b467ad5f5bbb59038c17 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Mon, 18 Aug 2025 21:45:16 -0500 Subject: [PATCH 4/5] Android: Add Vulkan-Headers as include directory jni/GpuDriver.cpp uses Vulkan headers --- Source/Android/jni/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index be200affa9b..357686964d5 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -57,6 +57,7 @@ PRIVATE target_include_directories(main PRIVATE + ${CMAKE_SOURCE_DIR}/Externals/Vulkan-Headers/include ${CMAKE_SOURCE_DIR}/Externals/libadrenotools/include ${CMAKE_SOURCE_DIR}/Externals/VulkanMemoryAllocator/include ) From eaf34b5078849c5e712df9c79d1fd30408649f35 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Sun, 17 Aug 2025 02:00:03 -0500 Subject: [PATCH 5/5] VideoBackends:Vulkan: Actually enable depthClampControl --- .../VideoBackends/Vulkan/VulkanContext.cpp | 19 +++++++++++++++++++ .../Core/VideoBackends/Vulkan/VulkanContext.h | 1 + 2 files changed, 20 insertions(+) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 8c1eb8beb95..02827ceb505 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -114,6 +114,7 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) { VkPhysicalDeviceSubgroupProperties properties_subgroup = {}; VkPhysicalDeviceVulkan12Properties properties_vk12 = {}; + VkPhysicalDeviceDepthClampControlFeaturesEXT features_depth_clamp = {}; features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features2.pNext = nullptr; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; @@ -131,6 +132,13 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) InsertIntoChain(&properties2, &properties_vk12); } + if (extensions[Extension::EXT_depth_clamp_control]) + { + features_depth_clamp.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLAMP_CONTROL_FEATURES_EXT; + InsertIntoChain(&features2, &features_depth_clamp); + } + auto getProps = vk11 ? vkGetPhysicalDeviceProperties2 : vkGetPhysicalDeviceProperties2KHR; getProps(device, &properties2); auto getFeatures = vk11 ? vkGetPhysicalDeviceFeatures2 : vkGetPhysicalDeviceFeatures2KHR; @@ -141,6 +149,10 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device) driverID = properties_vk12.driverID; } + // These extensions are useless to us without their associated feature, so use the extension bit + // to indicate that both the extension and the features we need from it are supported. + extensions[Extension::EXT_depth_clamp_control] = features_depth_clamp.depthClampControl; + subgroupSize = properties_subgroup.subgroupSize; // We require basic ops (for gl_SubgroupInvocationID), ballot (for subgroupBallot, @@ -204,6 +216,13 @@ VulkanContext::DeviceFeatures::DeviceFeatures(const PhysicalDeviceInfo& info) features.shaderClipDistance = info.shaderClipDistance ? VK_TRUE : VK_FALSE; features.depthClamp = info.depthClamp ? VK_TRUE : VK_FALSE; features.textureCompressionBC = info.textureCompressionBC ? VK_TRUE : VK_FALSE; + + if (info.extensions[Extension::EXT_depth_clamp_control]) + { + features_depth_clamp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLAMP_CONTROL_FEATURES_EXT; + features_depth_clamp.depthClampControl = VK_TRUE; + InsertIntoChain(&features2, &features_depth_clamp); + } } VulkanContext::VulkanContext(VkInstance instance, VkPhysicalDevice physical_device) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index 8cd3b1a6082..4edb55e4051 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -72,6 +72,7 @@ public: DeviceFeatures(const PhysicalDeviceInfo& info); DeviceFeatures(DeviceFeatures&&) = delete; // Contains internal pointers VkPhysicalDeviceFeatures2 features2; + VkPhysicalDeviceDepthClampControlFeaturesEXT features_depth_clamp; }; VulkanContext(VkInstance instance, VkPhysicalDevice physical_device);