From 521c5edcf56ad2b6d689d8f2ce187b661240a287 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Thu, 6 Nov 2025 13:14:57 -0500 Subject: [PATCH 1/5] gpu: Check for MSFT_layered_driver, skip layered drivers during enumeration --- src/gpu/vulkan/SDL_gpu_vulkan.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 440a70d503288..4dd794e9388a4 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; @@ -11024,7 +11026,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 +11041,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 +11058,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 } @@ -11369,6 +11373,28 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } + // Ignore Dozen, for now + if (renderer->supports.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( + renderer->physicalDevice, + &physicalDeviceProperties); + + if (physicalDeviceLayeredDriverProperties.underlyingAPI != VK_LAYERED_DRIVER_UNDERLYING_API_NONE_MSFT) { + return 0; + } + } + renderer->vkGetPhysicalDeviceQueueFamilyProperties( physicalDevice, &queueFamilyCount, From 82a3737a3078ea003867831ba9a732f639769fd9 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Thu, 6 Nov 2025 20:57:26 -0500 Subject: [PATCH 2/5] gpu: Move device ranking to a standalone function. This allows us to treat Dozen as an "other" driver, rather than blacklisting it outright. --- src/gpu/vulkan/SDL_gpu_vulkan.c | 173 +++++++++++++++++--------------- 1 file changed, 94 insertions(+), 79 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 4dd794e9388a4..5f749de3b3434 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -81,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, @@ -11307,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! @@ -11349,9 +11360,50 @@ 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, + Uint64 *deviceRank) +{ + Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest; + VkQueueFamilyProperties *queueProps; + bool supportsPresent; + VkPhysicalDeviceFeatures deviceFeatures; + Uint32 i; + renderer->vkGetPhysicalDeviceFeatures( physicalDevice, &deviceFeatures); @@ -11373,26 +11425,13 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } - // Ignore Dozen, for now - if (renderer->supports.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( - renderer->physicalDevice, - &physicalDeviceProperties); - - if (physicalDeviceLayeredDriverProperties.underlyingAPI != VK_LAYERED_DRIVER_UNDERLYING_API_NONE_MSFT) { - return 0; - } + // Device rank depends on extension support, do NOT move this block any higher! + if (!VULKAN_INTERNAL_GetDeviceRank( + renderer, + physicalDevice, + physicalDeviceExtensions, + deviceRank)) { + return 0; } renderer->vkGetPhysicalDeviceQueueFamilyProperties( @@ -11469,30 +11508,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; } From 550e9a6a0086d2af4280007dc6e90840dde95548 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 7 Nov 2025 12:16:57 -0500 Subject: [PATCH 3/5] gpu: Move device ranking to the bottom of IsDeviceSuitable. This prevents devices without presentation/graphics support from getting ranked. --- src/gpu/vulkan/SDL_gpu_vulkan.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 5f749de3b3434..dc0d1ef4550bb 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -11425,15 +11425,6 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } - // Device rank depends on extension support, do NOT move this block any higher! - if (!VULKAN_INTERNAL_GetDeviceRank( - renderer, - physicalDevice, - physicalDeviceExtensions, - deviceRank)) { - return 0; - } - renderer->vkGetPhysicalDeviceQueueFamilyProperties( physicalDevice, &queueFamilyCount, @@ -11508,6 +11499,15 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } + // Now that we know this device supports what we need, rank it against any other devices + if (!VULKAN_INTERNAL_GetDeviceRank( + renderer, + physicalDevice, + physicalDeviceExtensions, + deviceRank)) { + return 0; + } + // FIXME: Need better structure for checking vs storing swapchain support details return 1; } From e871fa4317ee2b69e1b9c3093f36af742bcbc4b7 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 7 Nov 2025 12:33:04 -0500 Subject: [PATCH 4/5] gpu: Separate device suitability from device rank when enumerating Vulkan devices. This makes it a bit clearer what the "minimum" is vs. the "best", which should be two separate queries. --- src/gpu/vulkan/SDL_gpu_vulkan.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index dc0d1ef4550bb..72784b5559e4e 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -11395,8 +11395,7 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( VulkanRenderer *renderer, VkPhysicalDevice physicalDevice, VulkanExtensions *physicalDeviceExtensions, - Uint32 *queueFamilyIndex, - Uint64 *deviceRank) + Uint32 *queueFamilyIndex) { Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest; VkQueueFamilyProperties *queueProps; @@ -11499,15 +11498,6 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } - // Now that we know this device supports what we need, rank it against any other devices - if (!VULKAN_INTERNAL_GetDeviceRank( - renderer, - physicalDevice, - physicalDeviceExtensions, - deviceRank)) { - return 0; - } - // FIXME: Need better structure for checking vs storing swapchain support details return 1; } @@ -11519,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, @@ -11566,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 From b8a9a37d83e4051bc4ac3cd951be1f438cd620fa Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 7 Nov 2025 13:12:36 -0500 Subject: [PATCH 5/5] gpu: Remove dead code in the Vulkan device ranking system. This block was a sloppy way of trying to avoid prioritizing lavapipe over another device, but truthfully the only way to guarantee avoiding CPU drivers is to add a property to allow apps to require hardware acceleration. --- src/gpu/vulkan/SDL_gpu_vulkan.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 72784b5559e4e..6dfb9c4624000 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -11581,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; } }