From 4d5ba3b4e69a89f0ff01ef40e71e7e5ddea950dc Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 23 May 2025 17:06:58 +1000 Subject: [PATCH] VulkanDevice: Tidy up device/extension initialization Single pass, avoids enabling extensions that will not be used. --- src/util/vulkan_device.cpp | 571 ++++++++++++++++++++----------------- src/util/vulkan_device.h | 12 +- 2 files changed, 320 insertions(+), 263 deletions(-) diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index f550f218a..00d8a6f36 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "vulkan_device.h" @@ -15,6 +15,7 @@ #include "common/bitutils.h" #include "common/error.h" #include "common/file_system.h" +#include "common/heap_array.h" #include "common/log.h" #include "common/path.h" #include "common/scoped_guard.h" @@ -426,107 +427,185 @@ GPUDevice::AdapterInfoList VulkanDevice::GetAdapterList() return ret; } -bool VulkanDevice::SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface, Error* error) +bool VulkanDevice::EnableOptionalDeviceExtensions(VkPhysicalDevice physical_device, + std::span available_extensions, + ExtensionList& enabled_extensions, + VkPhysicalDeviceFeatures& enabled_features, bool enable_surface, + Error* error) { - u32 extension_count = 0; - VkResult res = vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count, nullptr); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: "); - Vulkan::SetErrorObject(error, "vkEnumerateDeviceExtensionProperties failed: ", res); - return false; - } - - if (extension_count == 0) - { - ERROR_LOG("No extensions supported by device."); - Error::SetStringView(error, "No extensions supported by device."); - return false; - } - - std::vector available_extension_list(extension_count); - res = - vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count, available_extension_list.data()); - DebugAssert(res == VK_SUCCESS); - - auto SupportsExtension = [&](const char* name, bool required) { - if (std::find_if(available_extension_list.begin(), available_extension_list.end(), - [&](const VkExtensionProperties& properties) { - return !strcmp(name, properties.extensionName); - }) != available_extension_list.end()) - { - if (std::none_of(extension_list->begin(), extension_list->end(), - [&](const char* existing_name) { return (std::strcmp(existing_name, name) == 0); })) - { - DEV_LOG("Enabling extension: {}", name); - extension_list->push_back(name); - } - - return true; - } - - if (required) - { - ERROR_LOG("Vulkan: Missing required extension {}.", name); - Error::SetStringFmt(error, "Missing required extension {}.", name); - } - - return false; + const auto SupportsExtension = [&available_extensions](const char* name) { + return (std::find_if(available_extensions.begin(), available_extensions.end(), + [&](const VkExtensionProperties& properties) { + return (std::strcmp(name, properties.extensionName) == 0); + }) != available_extensions.end()); }; - if (enable_surface && !SupportsExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true)) + const auto AddExtension = [&enabled_extensions](const char* name) { + if (std::none_of(enabled_extensions.begin(), enabled_extensions.end(), + [&](const char* existing_name) { return (std::strcmp(existing_name, name) == 0); })) + { + DEV_LOG("Enabling extension: {}", name); + enabled_extensions.push_back(name); + } + + return true; + }; + const auto SupportsAndAddExtension = [&](const char* name) { + if (!SupportsExtension(name)) + return false; + + AddExtension(name); + return true; + }; + + if (enable_surface && !SupportsAndAddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) return false; + // get api version, and fixup any bad values from the driver + vkGetPhysicalDeviceProperties(physical_device, &m_device_properties); + m_device_properties.limits.minUniformBufferOffsetAlignment = + std::max(m_device_properties.limits.minUniformBufferOffsetAlignment, static_cast(16)); + m_device_properties.limits.minTexelBufferOffsetAlignment = + std::max(m_device_properties.limits.minTexelBufferOffsetAlignment, static_cast(1)); + m_device_properties.limits.optimalBufferCopyOffsetAlignment = + std::max(m_device_properties.limits.optimalBufferCopyOffsetAlignment, static_cast(1)); + m_device_properties.limits.optimalBufferCopyRowPitchAlignment = + std::max(m_device_properties.limits.optimalBufferCopyRowPitchAlignment, static_cast(1)); + m_device_properties.limits.bufferImageGranularity = + std::max(m_device_properties.limits.bufferImageGranularity, static_cast(1)); + + // advanced feature checks + VkPhysicalDeviceFeatures2 features2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, nullptr, {}}; + VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT, nullptr, VK_FALSE, VK_FALSE, + VK_FALSE}; + VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_feature = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, nullptr, VK_FALSE}; + VkPhysicalDeviceDynamicRenderingLocalReadFeaturesKHR dynamic_rendering_local_read_feature = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES_KHR, nullptr, VK_FALSE}; + VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_feature = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT, nullptr, VK_FALSE, VK_FALSE, VK_FALSE}; + VkPhysicalDeviceMaintenance4Features maintenance4_features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES, nullptr, VK_FALSE}; + VkPhysicalDeviceMaintenance5FeaturesKHR maintenance5_features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES_KHR, nullptr, VK_FALSE}; + + // add in optional feature structs // Gate most of the extension checks behind a Vulkan 1.1 device, so we don't have to deal with situations where // some extensions are supported but not others, and the prerequisite extensions for those extensions. if (m_device_properties.apiVersion >= VK_API_VERSION_1_1) { - m_optional_extensions.vk_ext_memory_budget = SupportsExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, false); - m_optional_extensions.vk_ext_rasterization_order_attachment_access = - SupportsExtension(VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, false) || - SupportsExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, false); - m_optional_extensions.vk_khr_driver_properties = SupportsExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, false); - m_optional_extensions.vk_khr_dynamic_rendering = - SupportsExtension(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, false) && - SupportsExtension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, false) && - SupportsExtension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, false); - m_optional_extensions.vk_khr_dynamic_rendering_local_read = - m_optional_extensions.vk_khr_dynamic_rendering && - SupportsExtension(VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME, false); - m_optional_extensions.vk_khr_push_descriptor = SupportsExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, false); - - // glslang generates debug info instructions before phi nodes at the beginning of blocks when non-semantic debug - // info is enabled, triggering errors by spirv-val. Gate it by an environment variable if you want source debugging - // until this is fixed. - if (const char* val = std::getenv("USE_NON_SEMANTIC_DEBUG_INFO"); - val && StringUtil::FromChars(val).value_or(false)) + if (SupportsExtension(VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME) || + SupportsExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME)) { - m_optional_extensions.vk_khr_shader_non_semantic_info = - SupportsExtension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, false); + m_optional_extensions.vk_ext_rasterization_order_attachment_access = true; + Vulkan::AddPointerToChain(&features2, &rasterization_order_access_feature); } + if (SupportsExtension(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME) && + SupportsExtension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME) && + SupportsExtension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) + { + m_optional_extensions.vk_khr_dynamic_rendering = true; + Vulkan::AddPointerToChain(&features2, &dynamic_rendering_feature); - m_optional_extensions.vk_ext_external_memory_host = - SupportsExtension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, false); + if (SupportsExtension(VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME)) + { + m_optional_extensions.vk_khr_dynamic_rendering_local_read = true; + Vulkan::AddPointerToChain(&features2, &dynamic_rendering_local_read_feature); + } - // Dynamic rendering isn't strictly needed for FSI, but we want it with framebufferless rendering. - m_optional_extensions.vk_ext_fragment_shader_interlock = - m_optional_extensions.vk_khr_dynamic_rendering && - SupportsExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME, false); + if (SupportsExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME)) + { + m_optional_extensions.vk_ext_fragment_shader_interlock = true; + Vulkan::AddPointerToChain(&features2, &fragment_shader_interlock_feature); + } + } + if (SupportsExtension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME)) + { + m_optional_extensions.vk_khr_maintenance4 = true; + Vulkan::AddPointerToChain(&features2, &maintenance4_features); - m_optional_extensions.vk_ext_swapchain_maintenance1 = - enable_surface && SupportsExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, false); - m_optional_extensions.vk_khr_maintenance4 = m_optional_extensions.vk_ext_swapchain_maintenance1 && - SupportsExtension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME, false); - m_optional_extensions.vk_khr_maintenance5 = - m_optional_extensions.vk_khr_maintenance4 && SupportsExtension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME, false); + if (SupportsExtension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME)) + { + m_optional_extensions.vk_khr_maintenance5 = true; + Vulkan::AddPointerToChain(&features2, &maintenance5_features); + } + } } -#ifdef _WIN32 - m_optional_extensions.vk_ext_full_screen_exclusive = - enable_surface && SupportsExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, false); - INFO_LOG("VK_EXT_full_screen_exclusive is {}", - m_optional_extensions.vk_ext_full_screen_exclusive ? "supported" : "NOT supported"); -#endif + // we might not have VK_KHR_get_physical_device_properties2... + if (!vkGetPhysicalDeviceFeatures2 || !vkGetPhysicalDeviceProperties2 || !vkGetPhysicalDeviceMemoryProperties2) + { + if (!vkGetPhysicalDeviceFeatures2KHR || !vkGetPhysicalDeviceProperties2KHR || + !vkGetPhysicalDeviceMemoryProperties2KHR) + { + ERROR_LOG("One or more functions from VK_KHR_get_physical_device_properties2 is missing, disabling extension."); + m_optional_extensions.vk_khr_get_physical_device_properties2 = false; + vkGetPhysicalDeviceFeatures2 = nullptr; + vkGetPhysicalDeviceProperties2 = nullptr; + vkGetPhysicalDeviceMemoryProperties2 = nullptr; + } + else + { + vkGetPhysicalDeviceFeatures2 = vkGetPhysicalDeviceFeatures2KHR; + vkGetPhysicalDeviceProperties2 = vkGetPhysicalDeviceProperties2KHR; + vkGetPhysicalDeviceMemoryProperties2 = vkGetPhysicalDeviceMemoryProperties2KHR; + } + } + + // don't bother querying if we're not actually looking at any features + if (vkGetPhysicalDeviceFeatures2 && features2.pNext) + vkGetPhysicalDeviceFeatures2(physical_device, &features2); + else + vkGetPhysicalDeviceFeatures(physical_device, &features2.features); + + // confirm we actually support it + m_optional_extensions.vk_ext_rasterization_order_attachment_access &= + (rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess == VK_TRUE); + m_optional_extensions.vk_khr_dynamic_rendering &= (dynamic_rendering_feature.dynamicRendering == VK_TRUE); + m_optional_extensions.vk_khr_dynamic_rendering_local_read &= + (dynamic_rendering_local_read_feature.dynamicRenderingLocalRead == VK_TRUE); + m_optional_extensions.vk_ext_fragment_shader_interlock &= + (m_optional_extensions.vk_khr_dynamic_rendering && + fragment_shader_interlock_feature.fragmentShaderPixelInterlock == VK_TRUE); + m_optional_extensions.vk_khr_maintenance4 &= (maintenance4_features.maintenance4 == VK_TRUE); + m_optional_extensions.vk_khr_maintenance5 &= (maintenance5_features.maintenance5 == VK_TRUE); + + VkPhysicalDeviceProperties2 properties2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, nullptr, {}}; + VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor_properties = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR, nullptr, 0u}; + VkPhysicalDeviceExternalMemoryHostPropertiesEXT external_memory_host_properties = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT, nullptr, 0}; + + if (SupportsExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) + { + m_optional_extensions.vk_khr_driver_properties = true; + m_device_driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + Vulkan::AddPointerToChain(&properties2, &m_device_driver_properties); + } + + if (SupportsExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)) + { + m_optional_extensions.vk_khr_push_descriptor = true; + Vulkan::AddPointerToChain(&properties2, &push_descriptor_properties); + } + + if (SupportsExtension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME)) + { + m_optional_extensions.vk_ext_external_memory_host = true; + Vulkan::AddPointerToChain(&properties2, &external_memory_host_properties); + } + + // don't bother querying if we're not actually looking at any features + if (vkGetPhysicalDeviceProperties2 && properties2.pNext) + vkGetPhysicalDeviceProperties2(physical_device, &properties2); + + // check we actually support enough + m_optional_extensions.vk_khr_push_descriptor &= (push_descriptor_properties.maxPushDescriptors >= 1); + + // vk_ext_external_memory_host is only used if the import alignment is the same as the system's page size + m_optional_extensions.vk_ext_external_memory_host &= + (external_memory_host_properties.minImportedHostPointerAlignment <= HOST_PAGE_SIZE); if (IsBrokenMobileDriver()) { @@ -557,14 +636,102 @@ bool VulkanDevice::SelectDeviceExtensions(ExtensionList* extension_list, bool en #endif } + // Actually enable the extensions. See above for VK1.1 reasoning. + if (m_device_properties.apiVersion >= VK_API_VERSION_1_1) + { + m_optional_extensions.vk_ext_memory_budget = SupportsAndAddExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME); + m_optional_extensions.vk_khr_driver_properties = SupportsAndAddExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME); + + // glslang generates debug info instructions before phi nodes at the beginning of blocks when non-semantic debug + // info is enabled, triggering errors by spirv-val. Gate it by an environment variable if you want source debugging + // until this is fixed. + if (const char* val = std::getenv("USE_NON_SEMANTIC_DEBUG_INFO"); + val && StringUtil::FromChars(val).value_or(false)) + { + m_optional_extensions.vk_khr_shader_non_semantic_info = + SupportsAndAddExtension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME); + } + + if (m_optional_extensions.vk_ext_rasterization_order_attachment_access) + { + if (!SupportsAndAddExtension(VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME)) + SupportsAndAddExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME); + } + if (m_optional_extensions.vk_khr_dynamic_rendering) + { + AddExtension(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); + AddExtension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME); + AddExtension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); + + if (m_optional_extensions.vk_khr_dynamic_rendering_local_read) + AddExtension(VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME); + } + if (m_optional_extensions.vk_khr_push_descriptor) + AddExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); + + if (m_optional_extensions.vk_ext_external_memory_host) + AddExtension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME); + + // Dynamic rendering isn't strictly needed for FSI, but we want it with framebufferless rendering. + if (m_optional_extensions.vk_ext_fragment_shader_interlock) + AddExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME); + + if (m_optional_extensions.vk_khr_maintenance4) + AddExtension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); + + if (m_optional_extensions.vk_khr_maintenance5) + AddExtension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); + + m_optional_extensions.vk_ext_swapchain_maintenance1 = + enable_surface && SupportsAndAddExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); + } + + // Enable the features we use. + enabled_features.dualSrcBlend |= features2.features.dualSrcBlend; + enabled_features.largePoints |= features2.features.largePoints; + enabled_features.wideLines |= features2.features.wideLines; + enabled_features.samplerAnisotropy |= features2.features.samplerAnisotropy; + enabled_features.sampleRateShading |= features2.features.sampleRateShading; + enabled_features.geometryShader |= features2.features.geometryShader; + enabled_features.fragmentStoresAndAtomics |= features2.features.fragmentStoresAndAtomics; + enabled_features.textureCompressionBC |= features2.features.textureCompressionBC; + +#define LOG_EXT(name, field) \ + Log::FastWrite(___LogChannel___, Log::Level::Info, \ + m_optional_extensions.field ? Log::Color::StrongGreen : Log::Color::StrongOrange, name " is {}", \ + m_optional_extensions.field ? "supported" : "NOT supported") + + LOG_EXT("VK_EXT_external_memory_host", vk_ext_external_memory_host); + LOG_EXT("VK_EXT_fragment_shader_interlock", vk_ext_fragment_shader_interlock); + LOG_EXT("VK_EXT_memory_budget", vk_ext_memory_budget); + LOG_EXT("VK_EXT_rasterization_order_attachment_access", vk_ext_rasterization_order_attachment_access); + LOG_EXT("VK_EXT_surface_maintenance1", vk_ext_surface_maintenance1); + LOG_EXT("VK_EXT_swapchain_maintenance1", vk_ext_swapchain_maintenance1); + LOG_EXT("VK_KHR_get_physical_device_properties2", vk_khr_get_physical_device_properties2); + LOG_EXT("VK_KHR_driver_properties", vk_khr_driver_properties); + LOG_EXT("VK_KHR_dynamic_rendering", vk_khr_dynamic_rendering); + LOG_EXT("VK_KHR_dynamic_rendering_local_read", vk_khr_dynamic_rendering_local_read); + LOG_EXT("VK_KHR_get_surface_capabilities2", vk_khr_get_surface_capabilities2); + LOG_EXT("VK_KHR_maintenance4", vk_khr_maintenance4); + LOG_EXT("VK_KHR_maintenance5", vk_khr_maintenance5); + LOG_EXT("VK_KHR_push_descriptor", vk_khr_push_descriptor); + +#ifdef _WIN32 + m_optional_extensions.vk_ext_full_screen_exclusive = + enable_surface && SupportsAndAddExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME); + LOG_EXT("VK_EXT_full_screen_exclusive", vk_ext_full_screen_exclusive); +#endif + +#undef LOG_EXT + return true; } -bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer, FeatureMask disabled_features, - Error* error) +bool VulkanDevice::CreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, bool enable_validation_layer, + FeatureMask disabled_features, Error* error) { u32 queue_family_count; - vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count, nullptr); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr); if (queue_family_count == 0) { ERROR_LOG("No queue families found on specified vulkan physical device."); @@ -572,8 +739,8 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay return false; } - std::vector queue_family_properties(queue_family_count); - vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count, queue_family_properties.data()); + DynamicHeapArray queue_family_properties(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_family_properties.data()); DEV_LOG("{} vulkan queue families", queue_family_count); // Find graphics and present queues. @@ -585,17 +752,16 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay if (graphics_supported) { m_graphics_queue_family_index = i; + // Quit now, no need for a present queue. if (!surface) - { break; - } } if (surface) { VkBool32 present_supported; - VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, i, surface, &present_supported); + VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &present_supported); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: "); @@ -604,15 +770,11 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay } if (present_supported) - { m_present_queue_family_index = i; - } // Prefer one queue family index that does both graphics and present. if (graphics_supported && present_supported) - { break; - } } } if (m_graphics_queue_family_index == queue_family_count) @@ -657,29 +819,40 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay device_info.pQueueCreateInfos = queue_infos.data(); - ExtensionList enabled_extensions; - if (!SelectDeviceExtensions(&enabled_extensions, surface != VK_NULL_HANDLE, error)) + u32 extension_count = 0; + VkResult res = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &extension_count, nullptr); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: "); + Vulkan::SetErrorObject(error, "vkEnumerateDeviceExtensionProperties failed: ", res); return false; + } + + if (extension_count == 0) + { + ERROR_LOG("No extensions supported by device."); + Error::SetStringView(error, "No extensions supported by device."); + return false; + } + + DynamicHeapArray available_extension_list(extension_count); + res = + vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &extension_count, available_extension_list.data()); + DebugAssert(res == VK_SUCCESS); + + VkPhysicalDeviceFeatures enabled_features = {}; + ExtensionList enabled_extensions; + if (!EnableOptionalDeviceExtensions(physical_device, available_extension_list.cspan(), enabled_extensions, + enabled_features, surface != VK_NULL_HANDLE, error)) + { + return false; + } device_info.enabledExtensionCount = static_cast(enabled_extensions.size()); device_info.ppEnabledExtensionNames = enabled_extensions.data(); - - // Check for required features before creating. - VkPhysicalDeviceFeatures available_features; - vkGetPhysicalDeviceFeatures(m_physical_device, &available_features); - - // Enable the features we use. - VkPhysicalDeviceFeatures enabled_features = {}; - enabled_features.dualSrcBlend = available_features.dualSrcBlend; - enabled_features.largePoints = available_features.largePoints; - enabled_features.wideLines = available_features.wideLines; - enabled_features.samplerAnisotropy = available_features.samplerAnisotropy; - enabled_features.sampleRateShading = available_features.sampleRateShading; - enabled_features.geometryShader = available_features.geometryShader; - enabled_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics; - enabled_features.textureCompressionBC = available_features.textureCompressionBC; device_info.pEnabledFeatures = &enabled_features; + // Optional feature structs VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT, nullptr, VK_TRUE, VK_FALSE, VK_FALSE}; @@ -691,6 +864,10 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, nullptr, VK_TRUE}; VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_feature = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT, nullptr, VK_FALSE, VK_TRUE, VK_FALSE}; + VkPhysicalDeviceMaintenance4Features maintenance4_features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES, nullptr, VK_TRUE}; + VkPhysicalDeviceMaintenance5FeaturesKHR maintenance5_features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES_KHR, nullptr, VK_TRUE}; if (m_optional_extensions.vk_ext_rasterization_order_attachment_access) Vulkan::AddPointerToChain(&device_info, &rasterization_order_access_feature); @@ -704,8 +881,14 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay if (m_optional_extensions.vk_ext_fragment_shader_interlock) Vulkan::AddPointerToChain(&device_info, &fragment_shader_interlock_feature); } + if (m_optional_extensions.vk_khr_maintenance4) + { + Vulkan::AddPointerToChain(&device_info, &maintenance4_features); + if (m_optional_extensions.vk_khr_maintenance5) + Vulkan::AddPointerToChain(&device_info, &maintenance5_features); + } - VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device); + res = vkCreateDevice(physical_device, &device_info, nullptr, &m_device); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateDevice failed: "); @@ -714,6 +897,7 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay } // With the device created, we can fill the remaining entry points. + m_physical_device = physical_device; if (!Vulkan::LoadVulkanDeviceFunctions(m_device)) return false; @@ -731,130 +915,10 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay queue_family_properties[m_graphics_queue_family_index].timestampValidBits, m_device_properties.limits.timestampPeriod); - ProcessDeviceExtensions(); - SetFeatures(disabled_features, enabled_features); + SetFeatures(disabled_features, physical_device, enabled_features); return true; } -void VulkanDevice::ProcessDeviceExtensions() -{ - // advanced feature checks - VkPhysicalDeviceFeatures2 features2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, nullptr, {}}; - VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT, nullptr, VK_FALSE, VK_FALSE, - VK_FALSE}; - VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_feature = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, nullptr, VK_FALSE}; - VkPhysicalDeviceDynamicRenderingLocalReadFeaturesKHR dynamic_rendering_local_read_feature = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES_KHR, nullptr, VK_FALSE}; - VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_feature = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, nullptr, VK_FALSE}; - VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_feature = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT, nullptr, VK_FALSE, VK_FALSE, VK_FALSE}; - VkPhysicalDeviceMaintenance4Features maintenance4_features = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES, nullptr, VK_FALSE}; - - // add in optional feature structs - if (m_optional_extensions.vk_ext_rasterization_order_attachment_access) - Vulkan::AddPointerToChain(&features2, &rasterization_order_access_feature); - if (m_optional_extensions.vk_ext_swapchain_maintenance1) - Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_feature); - if (m_optional_extensions.vk_khr_dynamic_rendering) - { - Vulkan::AddPointerToChain(&features2, &dynamic_rendering_feature); - if (m_optional_extensions.vk_khr_dynamic_rendering_local_read) - Vulkan::AddPointerToChain(&features2, &dynamic_rendering_local_read_feature); - if (m_optional_extensions.vk_ext_fragment_shader_interlock) - Vulkan::AddPointerToChain(&features2, &fragment_shader_interlock_feature); - } - if (m_optional_extensions.vk_khr_maintenance5) - Vulkan::AddPointerToChain(&features2, &maintenance4_features); - - // we might not have VK_KHR_get_physical_device_properties2... - if (!vkGetPhysicalDeviceFeatures2 || !vkGetPhysicalDeviceProperties2 || !vkGetPhysicalDeviceMemoryProperties2) - { - if (!vkGetPhysicalDeviceFeatures2KHR || !vkGetPhysicalDeviceProperties2KHR || - !vkGetPhysicalDeviceMemoryProperties2KHR) - { - ERROR_LOG("One or more functions from VK_KHR_get_physical_device_properties2 is missing, disabling extension."); - m_optional_extensions.vk_khr_get_physical_device_properties2 = false; - vkGetPhysicalDeviceFeatures2 = nullptr; - vkGetPhysicalDeviceProperties2 = nullptr; - vkGetPhysicalDeviceMemoryProperties2 = nullptr; - } - else - { - vkGetPhysicalDeviceFeatures2 = vkGetPhysicalDeviceFeatures2KHR; - vkGetPhysicalDeviceProperties2 = vkGetPhysicalDeviceProperties2KHR; - vkGetPhysicalDeviceMemoryProperties2 = vkGetPhysicalDeviceMemoryProperties2KHR; - } - } - - // don't bother querying if we're not actually looking at any features - if (vkGetPhysicalDeviceFeatures2 && features2.pNext) - vkGetPhysicalDeviceFeatures2(m_physical_device, &features2); - - // confirm we actually support it - m_optional_extensions.vk_ext_rasterization_order_attachment_access &= - (rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess == VK_TRUE); - m_optional_extensions.vk_ext_swapchain_maintenance1 &= - (swapchain_maintenance1_feature.swapchainMaintenance1 == VK_TRUE); - m_optional_extensions.vk_khr_dynamic_rendering &= (dynamic_rendering_feature.dynamicRendering == VK_TRUE); - m_optional_extensions.vk_khr_dynamic_rendering_local_read &= - (dynamic_rendering_local_read_feature.dynamicRenderingLocalRead == VK_TRUE); - m_optional_extensions.vk_ext_fragment_shader_interlock &= - (m_optional_extensions.vk_khr_dynamic_rendering && - fragment_shader_interlock_feature.fragmentShaderPixelInterlock == VK_TRUE); - m_optional_extensions.vk_khr_maintenance4 &= (maintenance4_features.maintenance4 == VK_TRUE); - m_optional_extensions.vk_khr_maintenance5 &= m_optional_extensions.vk_khr_maintenance4; - - VkPhysicalDeviceProperties2 properties2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, nullptr, {}}; - VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor_properties = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR, nullptr, 0u}; - VkPhysicalDeviceExternalMemoryHostPropertiesEXT external_memory_host_properties = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT, nullptr, 0}; - - if (m_optional_extensions.vk_khr_driver_properties) - { - m_device_driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; - Vulkan::AddPointerToChain(&properties2, &m_device_driver_properties); - } - if (m_optional_extensions.vk_khr_push_descriptor) - Vulkan::AddPointerToChain(&properties2, &push_descriptor_properties); - - if (m_optional_extensions.vk_ext_external_memory_host) - Vulkan::AddPointerToChain(&properties2, &external_memory_host_properties); - - // don't bother querying if we're not actually looking at any features - if (vkGetPhysicalDeviceProperties2 && properties2.pNext) - vkGetPhysicalDeviceProperties2(m_physical_device, &properties2); - - m_optional_extensions.vk_khr_push_descriptor &= (push_descriptor_properties.maxPushDescriptors >= 1); - - // vk_ext_external_memory_host is only used if the import alignment is the same as the system's page size - m_optional_extensions.vk_ext_external_memory_host &= - (external_memory_host_properties.minImportedHostPointerAlignment <= HOST_PAGE_SIZE); - -#define LOG_EXT(name, field) INFO_LOG(name " is {}", m_optional_extensions.field ? "supported" : "NOT supported") - - LOG_EXT("VK_EXT_external_memory_host", vk_ext_external_memory_host); - LOG_EXT("VK_EXT_fragment_shader_interlock", vk_ext_fragment_shader_interlock); - LOG_EXT("VK_EXT_memory_budget", vk_ext_memory_budget); - LOG_EXT("VK_EXT_rasterization_order_attachment_access", vk_ext_rasterization_order_attachment_access); - LOG_EXT("VK_EXT_surface_maintenance1", vk_ext_surface_maintenance1); - LOG_EXT("VK_EXT_swapchain_maintenance1", vk_ext_swapchain_maintenance1); - LOG_EXT("VK_KHR_get_physical_device_properties2", vk_khr_get_physical_device_properties2); - LOG_EXT("VK_KHR_driver_properties", vk_khr_driver_properties); - LOG_EXT("VK_KHR_dynamic_rendering", vk_khr_dynamic_rendering); - LOG_EXT("VK_KHR_dynamic_rendering_local_read", vk_khr_dynamic_rendering_local_read); - LOG_EXT("VK_KHR_get_surface_capabilities2", vk_khr_get_surface_capabilities2); - LOG_EXT("VK_KHR_maintenance4", vk_khr_maintenance4); - LOG_EXT("VK_KHR_maintenance5", vk_khr_maintenance5); - LOG_EXT("VK_KHR_push_descriptor", vk_khr_push_descriptor); - -#undef LOG_EXT -} - bool VulkanDevice::CreateAllocator() { const u32 apiVersion = std::min(m_device_properties.apiVersion, VK_API_VERSION_1_1); @@ -1978,6 +2042,7 @@ bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Featur return false; } + VkPhysicalDevice physical_device = VK_NULL_HANDLE; if (!adapter.empty()) { u32 gpu_index = 0; @@ -1986,36 +2051,23 @@ bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Featur INFO_LOG("GPU {}: {}", gpu_index, gpus[gpu_index].second.name); if (gpus[gpu_index].second.name == adapter) { - m_physical_device = gpus[gpu_index].first; + physical_device = gpus[gpu_index].first; break; } } - if (gpu_index == static_cast(gpus.size())) + if (m_physical_device == VK_NULL_HANDLE) { WARNING_LOG("Requested GPU '{}' not found, using first ({})", adapter, gpus[0].second.name); - m_physical_device = gpus[0].first; + physical_device = gpus[0].first; } } else { INFO_LOG("No GPU requested, using first ({})", gpus[0].second.name); - m_physical_device = gpus[0].first; + physical_device = gpus[0].first; } - // Read device physical memory properties, we need it for allocating buffers - vkGetPhysicalDeviceProperties(m_physical_device, &m_device_properties); - m_device_properties.limits.minUniformBufferOffsetAlignment = - std::max(m_device_properties.limits.minUniformBufferOffsetAlignment, static_cast(16)); - m_device_properties.limits.minTexelBufferOffsetAlignment = - std::max(m_device_properties.limits.minTexelBufferOffsetAlignment, static_cast(1)); - m_device_properties.limits.optimalBufferCopyOffsetAlignment = - std::max(m_device_properties.limits.optimalBufferCopyOffsetAlignment, static_cast(1)); - m_device_properties.limits.optimalBufferCopyRowPitchAlignment = - std::max(m_device_properties.limits.optimalBufferCopyRowPitchAlignment, static_cast(1)); - m_device_properties.limits.bufferImageGranularity = - std::max(m_device_properties.limits.bufferImageGranularity, static_cast(1)); - if (enable_debug_utils) EnableDebugUtils(); @@ -2024,7 +2076,7 @@ bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Featur { swap_chain = std::make_unique(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_control); - if (!swap_chain->CreateSurface(m_instance, m_physical_device, error)) + if (!swap_chain->CreateSurface(m_instance, physical_device, error)) { swap_chain->Destroy(*this, false); return false; @@ -2032,8 +2084,8 @@ bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Featur } // Attempt to create the device. - if (!CreateDevice(swap_chain ? swap_chain->GetSurface() : VK_NULL_HANDLE, enable_validation_layer, disabled_features, - error)) + if (!CreateDevice(physical_device, swap_chain ? swap_chain->GetSurface() : VK_NULL_HANDLE, enable_validation_layer, + disabled_features, error)) { return false; } @@ -2436,14 +2488,15 @@ u32 VulkanDevice::GetMaxMultisamples(VkPhysicalDevice physical_device, const VkP return 1; } -void VulkanDevice::SetFeatures(FeatureMask disabled_features, const VkPhysicalDeviceFeatures& vk_features) +void VulkanDevice::SetFeatures(FeatureMask disabled_features, VkPhysicalDevice physical_device, + const VkPhysicalDeviceFeatures& vk_features) { const u32 store_api_version = std::min(m_device_properties.apiVersion, VK_API_VERSION_1_1); m_render_api_version = (VK_API_VERSION_MAJOR(store_api_version) * 100u) + (VK_API_VERSION_MINOR(store_api_version) * 10u) + (VK_API_VERSION_PATCH(store_api_version)); m_max_texture_size = std::min(m_device_properties.limits.maxImageDimension2D, m_device_properties.limits.maxFramebufferWidth); - m_max_multisamples = GetMaxMultisamples(m_physical_device, m_device_properties); + m_max_multisamples = GetMaxMultisamples(physical_device, m_device_properties); m_features.dual_source_blend = !(disabled_features & FEATURE_MASK_DUAL_SOURCE_BLEND) && vk_features.dualSrcBlend; m_features.framebuffer_fetch = diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index e0d070966..e264e9c85 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -340,10 +340,14 @@ private: using ExtensionList = std::vector; static bool SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils); - bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface, Error* error); - bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer, FeatureMask disabled_features, Error* error); - void ProcessDeviceExtensions(); - void SetFeatures(FeatureMask disabled_features, const VkPhysicalDeviceFeatures& vk_features); + bool CreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, bool enable_validation_layer, + FeatureMask disabled_features, Error* error); + bool EnableOptionalDeviceExtensions(VkPhysicalDevice physical_device, + std::span available_extensions, + ExtensionList& enabled_extensions, VkPhysicalDeviceFeatures& enabled_features, + bool enable_surface, Error* error); + void SetFeatures(FeatureMask disabled_features, VkPhysicalDevice physical_device, + const VkPhysicalDeviceFeatures& vk_features); static u32 GetMaxMultisamples(VkPhysicalDevice physical_device, const VkPhysicalDeviceProperties& properties);