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.
This commit is contained in:
Wunk 2024-10-08 08:07:09 -07:00 committed by GitHub
parent ff6a3119b0
commit 556e2ead56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 36 additions and 11 deletions

View File

@ -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)