From 556e2ead5602605885d40b797ec259064b53a92d Mon Sep 17 00:00:00 2001 From: Wunk Date: Tue, 8 Oct 2024 08:07:09 -0700 Subject: [PATCH] vk: Refactor physical device selection (#1671) Rather than electing the first physical device it finds, and falling back on the first-listed GPU: a series of stable-partitions are done so that the "least compromising" GPU is selected based on a series of criteria. It will now maximally try to find a GPU that(in order of priority): * Is a discrete GPU * Supports `fragmentStoresAndAtomics` * Supports `R5G5B5`/`R5G6B5A1`/`R4G4B4A4` In the case that a system has two dGPUs and one of them supports optimal-formats, the optimal-format one is selected In the case that a system has an iGPU and the dGPU and they both support optimal formats, the dGPU is selected. In the case that a system has an iGPU and the dGPU and the dGPU doesn't support optimal formats, the dGPU is still selected. --- core/rend/vulkan/vulkan_context.cpp | 47 ++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/core/rend/vulkan/vulkan_context.cpp b/core/rend/vulkan/vulkan_context.cpp index e5b9951c4..676142057 100644 --- a/core/rend/vulkan/vulkan_context.cpp +++ b/core/rend/vulkan/vulkan_context.cpp @@ -207,25 +207,50 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co #endif #endif - const auto devices = instance->enumeratePhysicalDevices(); + auto devices = instance->enumeratePhysicalDevices(); if (devices.empty()) { ERROR_LOG(RENDERER, "Vulkan error: no physical devices found"); return false; } - // Choose a discrete gpu if there's one, otherwise just pick the first one - physicalDevice = nullptr; - for (const auto& phyDev : devices) - { - if (phyDev.getProperties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu) + // The order of physical-devices provided by the driver should be somewhat preserved with stable-partitions/stable-sorts + + // Prefer GPUs that support optimal R5G5B5/R5G6B5A1/R4G4B4A4 + const auto supportsOptimalFormat = [](vk::Format format) { - physicalDevice = phyDev; - break; + return [format](const vk::PhysicalDevice& physicalDevice) -> bool + { + const vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format); + return (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImage) + && (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitDst) + && (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitSrc); + }; + }; + std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR5G6B5UnormPack16)); + std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR5G5B5A1UnormPack16)); + std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR4G4B4A4UnormPack16)); + + // Prefer GPUs that support fragmentStoresAndAtomics + std::stable_partition( + devices.begin(), devices.end(), + [](const vk::PhysicalDevice& physicalDevice) -> bool + { + return !!physicalDevice.getFeatures().fragmentStoresAndAtomics; } - } - if (!physicalDevice) - physicalDevice = devices.front(); + ); + + // Finally, prefer Discrete GPUs + std::stable_partition( + devices.begin(), devices.end(), + [](const vk::PhysicalDevice& physicalDevice) -> bool + { + return physicalDevice.getProperties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu; + } + ); + + // Top of the device-list is the _most_ qualified GPU + physicalDevice = devices.front(); vk::PhysicalDeviceProperties properties = physicalDevice.getProperties(); if (vulkan11 && properties.apiVersion >= VK_API_VERSION_1_1)