Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 106 additions & 75 deletions src/gpu/vulkan/SDL_gpu_vulkan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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
}

Expand All @@ -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);
}

Expand All @@ -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
}
Expand Down Expand Up @@ -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!
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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;
}
}

Expand Down
Loading