diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 440a70d503288..6dfb9c4624000 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -56,6 +56,8 @@ typedef struct VulkanExtensions Uint8 KHR_driver_properties; // Only required for special implementations (i.e. MoltenVK) Uint8 KHR_portability_subset; + // Only required to detect devices using Dozen D3D12 driver + Uint8 MSFT_layered_driver; // Only required for decoding HDR ASTC textures Uint8 EXT_texture_compression_astc_hdr; } VulkanExtensions; @@ -79,22 +81,6 @@ typedef struct VulkanExtensions // Conversions -static const Uint8 DEVICE_PRIORITY_HIGHPERFORMANCE[] = { - 0, // VK_PHYSICAL_DEVICE_TYPE_OTHER - 3, // VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU - 4, // VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU - 2, // VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU - 1 // VK_PHYSICAL_DEVICE_TYPE_CPU -}; - -static const Uint8 DEVICE_PRIORITY_LOWPOWER[] = { - 0, // VK_PHYSICAL_DEVICE_TYPE_OTHER - 4, // VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU - 3, // VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU - 2, // VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU - 1 // VK_PHYSICAL_DEVICE_TYPE_CPU -}; - static VkPresentModeKHR SDLToVK_PresentMode[] = { VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, @@ -11024,7 +11010,7 @@ static inline Uint8 CheckDeviceExtensions( supports->ext = 1; \ } CHECK(KHR_swapchain) - else CHECK(KHR_maintenance1) else CHECK(KHR_driver_properties) else CHECK(KHR_portability_subset) else CHECK(EXT_texture_compression_astc_hdr) + else CHECK(KHR_maintenance1) else CHECK(KHR_driver_properties) else CHECK(KHR_portability_subset) else CHECK(MSFT_layered_driver) else CHECK(EXT_texture_compression_astc_hdr) #undef CHECK } @@ -11039,6 +11025,7 @@ static inline Uint32 GetDeviceExtensionCount(VulkanExtensions *supports) supports->KHR_maintenance1 + supports->KHR_driver_properties + supports->KHR_portability_subset + + supports->MSFT_layered_driver + supports->EXT_texture_compression_astc_hdr); } @@ -11055,6 +11042,7 @@ static inline void CreateDeviceExtensionArray( CHECK(KHR_maintenance1) CHECK(KHR_driver_properties) CHECK(KHR_portability_subset) + CHECK(MSFT_layered_driver) CHECK(EXT_texture_compression_astc_hdr) #undef CHECK } @@ -11303,35 +11291,62 @@ static Uint8 VULKAN_INTERNAL_CreateInstance(VulkanRenderer *renderer) return 1; } -static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( +static bool VULKAN_INTERNAL_GetDeviceRank( VulkanRenderer *renderer, VkPhysicalDevice physicalDevice, VulkanExtensions *physicalDeviceExtensions, - Uint32 *queueFamilyIndex, Uint64 *deviceRank) { - Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest; - VkQueueFamilyProperties *queueProps; - bool supportsPresent; - VkPhysicalDeviceProperties deviceProperties; - VkPhysicalDeviceFeatures deviceFeatures; - VkPhysicalDeviceMemoryProperties deviceMemory; - Uint32 i; - + static const Uint8 DEVICE_PRIORITY_HIGHPERFORMANCE[] = { + 0, // VK_PHYSICAL_DEVICE_TYPE_OTHER + 3, // VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU + 4, // VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU + 2, // VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU + 1 // VK_PHYSICAL_DEVICE_TYPE_CPU + }; + static const Uint8 DEVICE_PRIORITY_LOWPOWER[] = { + 0, // VK_PHYSICAL_DEVICE_TYPE_OTHER + 4, // VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU + 3, // VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU + 2, // VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU + 1 // VK_PHYSICAL_DEVICE_TYPE_CPU + }; const Uint8 *devicePriority = renderer->preferLowPower ? DEVICE_PRIORITY_LOWPOWER : DEVICE_PRIORITY_HIGHPERFORMANCE; - /* Get the device rank before doing any checks, in case one fails. - * Note: If no dedicated device exists, one that supports our features - * would be fine - */ - renderer->vkGetPhysicalDeviceProperties( - physicalDevice, - &deviceProperties); + VkPhysicalDeviceType deviceType; + if (physicalDeviceExtensions->MSFT_layered_driver) { + VkPhysicalDeviceProperties2KHR physicalDeviceProperties; + VkPhysicalDeviceLayeredDriverPropertiesMSFT physicalDeviceLayeredDriverProperties; + + physicalDeviceProperties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physicalDeviceProperties.pNext = &physicalDeviceLayeredDriverProperties; + + physicalDeviceLayeredDriverProperties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LAYERED_DRIVER_PROPERTIES_MSFT; + physicalDeviceLayeredDriverProperties.pNext = NULL; + + renderer->vkGetPhysicalDeviceProperties2KHR( + physicalDevice, + &physicalDeviceProperties); + + if (physicalDeviceLayeredDriverProperties.underlyingAPI != VK_LAYERED_DRIVER_UNDERLYING_API_NONE_MSFT) { + deviceType = VK_PHYSICAL_DEVICE_TYPE_OTHER; + } else { + deviceType = physicalDeviceProperties.properties.deviceType; + } + } else { + VkPhysicalDeviceProperties physicalDeviceProperties; + renderer->vkGetPhysicalDeviceProperties( + physicalDevice, + &physicalDeviceProperties); + deviceType = physicalDeviceProperties.deviceType; + } /* Apply a large bias on the devicePriority so that we always respect the order in the priority arrays. * We also rank by e.g. VRAM which should have less influence than the device type. */ - Uint64 devicePriorityValue = devicePriority[deviceProperties.deviceType] * 1000000; + Uint64 devicePriorityValue = devicePriority[deviceType] * 1000000; if (*deviceRank < devicePriorityValue) { /* This device outranks the best device we've found so far! @@ -11345,9 +11360,49 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( * run a query and reset the rank to avoid overwrites */ *deviceRank = 0; - return 0; + return false; } + /* If we prefer high performance, sum up all device local memory (rounded to megabytes) + * to deviceRank. In the niche case of someone having multiple dedicated GPUs in the same + * system, this theoretically picks the most powerful one (or at least the one with the + * most memory!) + * + * We do this *after* discarding all non suitable devices, which means if this computer + * has multiple dedicated GPUs that all meet our criteria, *and* the user asked for high + * performance, then we always pick the GPU with more VRAM. + */ + if (!renderer->preferLowPower) { + Uint32 i; + Uint64 videoMemory = 0; + VkPhysicalDeviceMemoryProperties deviceMemory; + renderer->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemory); + for (i = 0; i < deviceMemory.memoryHeapCount; i++) { + VkMemoryHeap heap = deviceMemory.memoryHeaps[i]; + if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { + videoMemory += heap.size; + } + } + // Round it to megabytes (as per the vulkan spec videoMemory is in bytes) + Uint64 videoMemoryRounded = videoMemory / 1024 / 1024; + *deviceRank += videoMemoryRounded; + } + + return true; +} + +static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( + VulkanRenderer *renderer, + VkPhysicalDevice physicalDevice, + VulkanExtensions *physicalDeviceExtensions, + Uint32 *queueFamilyIndex) +{ + Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest; + VkQueueFamilyProperties *queueProps; + bool supportsPresent; + VkPhysicalDeviceFeatures deviceFeatures; + Uint32 i; + renderer->vkGetPhysicalDeviceFeatures( physicalDevice, &deviceFeatures); @@ -11443,30 +11498,6 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } - /* If we prefer high performance, sum up all device local memory (rounded to megabytes) - * to deviceRank. In the niche case of someone having multiple dedicated GPUs in the same - * system, this theoretically picks the most powerful one (or at least the one with the - * most memory!) - * - * We do this *after* discarding all non suitable devices, which means if this computer - * has multiple dedicated GPUs that all meet our criteria, *and* the user asked for high - * performance, then we always pick the GPU with more VRAM. - */ - if (!renderer->preferLowPower) { - renderer->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemory); - Uint64 videoMemory = 0; - for (i = 0; i < deviceMemory.memoryHeapCount; i++) { - VkMemoryHeap heap = deviceMemory.memoryHeaps[i]; - if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { - videoMemory += heap.size; - } - } - // Round it to megabytes (as per the vulkan spec videoMemory is in bytes) - Uint64 videoMemoryRounded = videoMemory / 1024 / 1024; - *deviceRank += videoMemoryRounded; - } - - // FIXME: Need better structure for checking vs storing swapchain support details return 1; } @@ -11478,8 +11509,8 @@ static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice(VulkanRenderer *renderer) VulkanExtensions *physicalDeviceExtensions; Uint32 i, physicalDeviceCount; Sint32 suitableIndex; - Uint32 queueFamilyIndex, suitableQueueFamilyIndex; - Uint64 deviceRank, highestRank; + Uint32 suitableQueueFamilyIndex; + Uint64 highestRank; vulkanResult = renderer->vkEnumeratePhysicalDevices( renderer->instance, @@ -11525,12 +11556,23 @@ static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice(VulkanRenderer *renderer) suitableQueueFamilyIndex = 0; highestRank = 0; for (i = 0; i < physicalDeviceCount; i += 1) { + Uint32 queueFamilyIndex; + Uint64 deviceRank; + + if (!VULKAN_INTERNAL_IsDeviceSuitable( + renderer, + physicalDevices[i], + &physicalDeviceExtensions[i], + &queueFamilyIndex)) { + // Device does not meet the minimum requirements, skip it entirely + continue; + } + deviceRank = highestRank; - if (VULKAN_INTERNAL_IsDeviceSuitable( + if (VULKAN_INTERNAL_GetDeviceRank( renderer, physicalDevices[i], &physicalDeviceExtensions[i], - &queueFamilyIndex, &deviceRank)) { /* Use this for rendering. * Note that this may override a previous device that @@ -11539,17 +11581,6 @@ static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice(VulkanRenderer *renderer) suitableIndex = i; suitableQueueFamilyIndex = queueFamilyIndex; highestRank = deviceRank; - } else if (deviceRank > highestRank) { - /* In this case, we found a... "realer?" GPU, - * but it doesn't actually support our Vulkan. - * We should disqualify all devices below as a - * result, because if we don't we end up - * ignoring real hardware and risk using - * something like LLVMpipe instead! - * -flibit - */ - suitableIndex = -1; - highestRank = deviceRank; } }