Consistent loader device ordering

The loader ICD ordering could be random on Linux based on using readdir
to find ICD manifest files.  This can result in random behaviors as
applications that select only the first device can switch which device is
used.  To resolve this, we now sort based on device type and then
internally to the types based on PCI bus information.

This also introduces a VK_LOADER_DEFAULT_DEVICE environment variable
that can be used to force a specific PCI device.  This environment variable
is actually a duplicate of the MESA_VK_DEVICE_SELECT variable, which is
also looked for if the loader environment variable is not found.

Note, that at least one ICD must support it for the extension to be used at all.
So we only do the sorting if one ICD supports it.

Fixes part of #657
This commit is contained in:
Mark Young 2021-11-15 10:49:37 -07:00
parent 1fb56971c5
commit cbe28ac790
12 changed files with 1191 additions and 74 deletions

View File

@ -181,6 +181,12 @@ if (!is_android) {
}
libs = [ "Cfgmgr32.lib" ]
}
if (is_linux) {
sources += [
"loader/loader_linux.c",
"loader/loader_linux.h",
]
}
if (is_mac) {
frameworks = [ "CoreFoundation.framework" ]
}

View File

@ -43,6 +43,7 @@
* [Instance and Device Extensions](#instance-and-device-extensions)
* [WSI Extensions](#wsi-extensions)
* [Unknown Extensions](#unknown-extensions)
* [Physical Device Ordering](#physical-device-ordering)
## Overview
@ -888,5 +889,38 @@ variable to a non-zero number.
This will effectively disable the loader's filtering of instance extension
names.
## Physical Device Ordering
Prior to the 1.2.204 loader, physical devices on Linux could be returned in an
inconsistent order.
To remedy this, the Vulkan loader will now sort devices once they have been
received from the drivers (before returning the information to any enabled
layers) in the following fashion:
* Sort based on device type (Discrete, Integrated, Virtual, all others)
* Sort internal to the types based on PCI information (Domain, Bus, Device, and
Function).
This allows for a consistent physical device order from run to run on the same
system, unless the actual underlying hardware changes.
A new environment variable is defined to give users the ability to force a
specific device, `VK_LOADER_DEVICE_SELECT`.
This environment variable should be set to the desired devices hex value for
Vendor Id and Device Id (as returned from `vkGetPhysicalDeviceProperties` in
the `VkPhysicalDeviceProperties` structure).
It should look like the following:
```
set VK_LOADER_DEVICE_SELECT=0x10de:0x1f91
```
This will force on the device with a vendor ID of "0x10de" and a device ID
of "0x1f91".
If that device is not found, this is simply ignored.
All device selection work done in the loader can be disabled by setting the
environment variable `VK_LOADER_DISABLE_SELECT` to a non-zero value.
This is intended for debug purposes to narrow down any issues with the loader
device selection mechanism, but can be used by others.
[Return to the top-level LoaderInterfaceArchitecture.md file.](LoaderInterfaceArchitecture.md)

View File

@ -537,6 +537,27 @@ discovery.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;path_a&gt;;&lt;path_b&gt;</small>
</td>
</tr>
<tr>
<td><small><i>VK_LOADER_DEVICE_SELECT</i></small></td>
<td><b>Linux Only</b><br/>
Allows the user to force a particular device to be prioritized above all
other devices in the return order of <i>vkGetPhysicalDevices<i> and
<i>vkGetPhysicalDeviceGroups<i> functions.<br/>
The value should be "<hex vendor id>:<hex device id>".<br/>
<b>NOTE:</b> This not remove devices.
</td>
<td><small>set VK_LOADER_DEVICE_SELECT=0x10de:0x1f91</small>
</td>
</tr>
<tr>
<td><small><i>VK_LOADER_DISABLE_SELECT</i></small></td>
<td><b>Linux Only</b><br/>
Allows the user to disable the consistent sorting algorithm run in the
loader before returning the set of physical devices to layers.<br/>
</td>
<td><small>set VK_LOADER_DISABLE_SELECT=1</small>
</td>
</tr>
<tr>
<td><small><i>VK_LOADER_DISABLE_INST_EXT_FILTER</i></small></td>
<td>Disable the filtering out of instance extensions that the loader doesn't

View File

@ -160,6 +160,9 @@ set(NORMAL_LOADER_SRCS
if(WIN32)
set(NORMAL_LOADER_SRCS ${NORMAL_LOADER_SRCS} loader_windows.c)
else(UNIX AND NOT APPLE) # i.e.: Linux
set(NORMAL_LOADER_SRCS ${NORMAL_LOADER_SRCS} loader_linux.c)
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS LOADER_ENABLE_LINUX_SORT)
endif()
set(OPT_LOADER_SRCS dev_ext_trampoline.c phys_dev_ext.c)

View File

@ -1,8 +1,8 @@
/*
*
* Copyright (c) 2014-2021 The Khronos Group Inc.
* Copyright (c) 2014-2021 Valve Corporation
* Copyright (c) 2014-2021 LunarG, Inc.
* Copyright (c) 2014-2022 The Khronos Group Inc.
* Copyright (c) 2014-2022 Valve Corporation
* Copyright (c) 2014-2022 LunarG, Inc.
* Copyright (C) 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -67,6 +67,10 @@
#if defined(WIN32)
#include "loader_windows.h"
#endif
#ifdef LOADER_ENABLE_LINUX_SORT
// This header is currently only used when sorting Linux devices, so don't include it otherwise.
#include "loader_linux.h"
#endif // LOADER_ENABLE_LINUX_SORT
// Generated file containing all the extension data
#include "vk_loader_extensions.c"
@ -5437,16 +5441,31 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateI
// No ICD will advertise support for layers. An ICD library could
// support a layer, but it would be independent of the actual ICD,
// just in the same library.
filtered_extension_names = loader_stack_alloc(pCreateInfo->enabledExtensionCount * sizeof(char *));
uint32_t extension_count = pCreateInfo->enabledExtensionCount;
#ifdef LOADER_ENABLE_LINUX_SORT
extension_count += 1;
#endif // LOADER_ENABLE_LINUX_SORT
filtered_extension_names = loader_stack_alloc(extension_count * sizeof(char *));
if (!filtered_extension_names) {
loader_log(ptr_instance, VULKAN_LOADER_ERROR_BIT, 0,
"terminator_CreateInstance: Failed create extension name array for %d extensions",
pCreateInfo->enabledExtensionCount);
"terminator_CreateInstance: Failed create extension name array for %d extensions", extension_count);
res = VK_ERROR_OUT_OF_HOST_MEMORY;
goto out;
}
icd_create_info.ppEnabledExtensionNames = (const char *const *)filtered_extension_names;
// Determine if Get Physical Device Properties 2 is available to this Instance
if (pCreateInfo->pApplicationInfo && pCreateInfo->pApplicationInfo->apiVersion >= VK_API_VERSION_1_1) {
ptr_instance->supports_get_dev_prop_2 = true;
} else {
for (uint32_t j = 0; j < pCreateInfo->enabledExtensionCount; j++) {
if (!strcmp(pCreateInfo->ppEnabledExtensionNames[j], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
ptr_instance->supports_get_dev_prop_2 = true;
break;
}
}
}
for (uint32_t i = 0; i < ptr_instance->icd_tramp_list.count; i++) {
icd_term = loader_icd_add(ptr_instance, &ptr_instance->icd_tramp_list.scanned_list[i]);
if (NULL == icd_term) {
@ -5502,6 +5521,29 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateI
icd_create_info.enabledExtensionCount++;
}
}
#ifdef LOADER_ENABLE_LINUX_SORT
// Force on "VK_KHR_get_physical_device_properties2" for Linux as we use it for GPU sorting.
if (icd_term->scanned_icd->api_version < VK_API_VERSION_1_1) {
prop = get_extension_property(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, &icd_exts);
if (prop) {
filtered_extension_names[pCreateInfo->enabledExtensionCount] =
(char *)VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
icd_create_info.enabledExtensionCount++;
}
}
#endif // LOADER_ENABLE_LINUX_SORT
// Determine if vkGetPhysicalDeviceProperties2 is available to this Instance
if (icd_term->scanned_icd->api_version >= VK_API_VERSION_1_1) {
icd_term->supports_get_dev_prop_2 = true;
} else {
for (uint32_t j = 0; j < icd_create_info.enabledExtensionCount; j++) {
if (!strcmp(filtered_extension_names[j], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
icd_term->supports_get_dev_prop_2 = true;
break;
}
}
}
loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&icd_exts);
@ -5592,6 +5634,23 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateI
one_icd_successful = true;
}
// For vkGetPhysicalDeviceProperties2, at least one ICD needs to support the extension for the
// instance to have it
if (ptr_instance->supports_get_dev_prop_2) {
bool at_least_one_supports = false;
icd_term = ptr_instance->icd_terms;
while (icd_term != NULL) {
if (icd_term->supports_get_dev_prop_2) {
at_least_one_supports = true;
break;
}
icd_term = icd_term->next;
}
if (!at_least_one_supports) {
ptr_instance->supports_get_dev_prop_2 = false;
}
}
// If no ICDs were added to instance list and res is unchanged from it's initial value, the loader was unable to
// find a suitable ICD.
if (VK_SUCCESS == res && (ptr_instance->icd_terms == NULL || !one_icd_successful)) {
@ -6057,12 +6116,28 @@ out:
return res;
}
#ifdef LOADER_ENABLE_LINUX_SORT
bool is_linux_sort_enabled(struct loader_instance *inst) {
bool sort_items = inst->supports_get_dev_prop_2;
char *env_value = loader_getenv("VK_LOADER_DISABLE_SELECT", inst);
if (NULL != env_value) {
int32_t int_env_val = atoi(env_value);
loader_free_getenv(env_value, inst);
if (int_env_val != 0) {
sort_items = false;
}
}
return sort_items;
}
#endif // LOADER_ENABLE_LINUX_SORT
VkResult setup_loader_term_phys_devs(struct loader_instance *inst) {
VkResult res = VK_SUCCESS;
struct loader_icd_term *icd_term;
struct loader_phys_dev_per_icd *icd_phys_dev_array = NULL;
struct loader_physical_device_term **new_phys_devs = NULL;
struct LoaderSortedPhysicalDevice *sorted_phys_dev_array = NULL;
uint32_t icd_idx = 0;
uint32_t sorted_count = 0;
inst->total_gpu_count = 0;
@ -6091,15 +6166,12 @@ VkResult setup_loader_term_phys_devs(struct loader_instance *inst) {
// For each ICD, query the number of physical devices, and then get an
// internal value for those physical devices.
icd_term = inst->icd_terms;
for (uint32_t icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) {
icd_phys_dev_array[icd_idx].count = 0;
icd_phys_dev_array[icd_idx].phys_devs = NULL;
icd_phys_dev_array[icd_idx].this_icd_term = NULL;
while (NULL != icd_term) {
// This is the legacy behavior which should be skipped if EnumerateAdapterPhysicalDevices is available
// and we successfully enumerated sorted adapters using windows_read_sorted_physical_devices.
#if defined(VK_USE_PLATFORM_WIN32_KHR)
if (sorted_count && icd_term->scanned_icd->EnumerateAdapterPhysicalDevices != NULL) {
icd_term = icd_term->next;
continue;
}
#endif
@ -6129,6 +6201,8 @@ VkResult setup_loader_term_phys_devs(struct loader_instance *inst) {
}
inst->total_gpu_count += icd_phys_dev_array[icd_idx].count;
icd_phys_dev_array[icd_idx].this_icd_term = icd_term;
icd_term = icd_term->next;
++icd_idx;
}
if (0 == inst->total_gpu_count) {
@ -6148,6 +6222,26 @@ VkResult setup_loader_term_phys_devs(struct loader_instance *inst) {
}
memset(new_phys_devs, 0, sizeof(struct loader_physical_device_term *) * inst->total_gpu_count);
#ifdef LOADER_ENABLE_LINUX_SORT
if (is_linux_sort_enabled(inst)) {
for (uint32_t dev = 0; dev < inst->total_gpu_count; ++dev) {
new_phys_devs[dev] =
loader_instance_heap_alloc(inst, sizeof(struct loader_physical_device_term), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (NULL == new_phys_devs[dev]) {
loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
"setup_loader_term_phys_devs: Failed to allocate physical device terminator object %d", dev);
inst->total_gpu_count = dev;
res = VK_ERROR_OUT_OF_HOST_MEMORY;
goto out;
}
}
// Get the physical devices supported by platform sorting mechanism into a separate list
res = linux_read_sorted_physical_devices(inst, icd_idx, icd_phys_dev_array, new_phys_devs);
goto out;
}
#endif // LOADER_ENABLE_LINUX_SORT
// Copy or create everything to fill the new array of physical devices
uint32_t idx = 0;
@ -6190,7 +6284,7 @@ VkResult setup_loader_term_phys_devs(struct loader_instance *inst) {
#endif
// Copy over everything found through EnumeratePhysicalDevices
for (uint32_t icd_idx = 0; icd_idx < inst->total_icd_count; icd_idx++) {
for (icd_idx = 0; icd_idx < inst->total_icd_count; icd_idx++) {
for (uint32_t pd_idx = 0; pd_idx < icd_phys_dev_array[icd_idx].count; pd_idx++) {
// Check if this physical device is already in the old buffer
if (NULL != inst->phys_devs_term) {
@ -6651,11 +6745,12 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
uint32_t total_count = 0;
uint32_t cur_icd_group_count = 0;
VkPhysicalDeviceGroupPropertiesKHR **new_phys_dev_groups = NULL;
VkPhysicalDeviceGroupPropertiesKHR *local_phys_dev_groups = NULL;
struct loader_physical_device_group_term *local_phys_dev_groups = NULL;
bool *local_phys_dev_group_sorted = NULL;
PFN_vkEnumeratePhysicalDeviceGroups fpEnumeratePhysicalDeviceGroups = NULL;
struct LoaderSortedPhysicalDevice *sorted_phys_dev_array = NULL;
uint32_t sorted_count = 0;
uint32_t icd_idx = 0;
if (0 == inst->phys_dev_count_term) {
loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
@ -6669,7 +6764,7 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
// For each ICD, query the number of physical device groups, and then get an
// internal value for those physical devices.
icd_term = inst->icd_terms;
for (uint32_t icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) {
for (icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) {
// Get the function pointer to use to call into the ICD. This could be the core or KHR version
if (inst->enabled_known_extensions.khr_device_group_creation) {
fpEnumeratePhysicalDeviceGroups = icd_term->dispatch.EnumeratePhysicalDeviceGroupsKHR;
@ -6724,7 +6819,7 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
// Create a temporary array (on the stack) to keep track of the
// returned VkPhysicalDevice values.
local_phys_dev_groups = loader_stack_alloc(sizeof(VkPhysicalDeviceGroupProperties) * total_count);
local_phys_dev_groups = loader_stack_alloc(sizeof(struct loader_physical_device_group_term) * total_count);
local_phys_dev_group_sorted = loader_stack_alloc(sizeof(bool) * total_count);
if (NULL == local_phys_dev_groups || NULL == local_phys_dev_group_sorted) {
loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
@ -6734,12 +6829,12 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
goto out;
}
// Initialize the memory to something valid
memset(local_phys_dev_groups, 0, sizeof(VkPhysicalDeviceGroupProperties) * total_count);
memset(local_phys_dev_groups, 0, sizeof(struct loader_physical_device_group_term) * total_count);
memset(local_phys_dev_group_sorted, 0, sizeof(bool) * total_count);
for (uint32_t group = 0; group < total_count; group++) {
local_phys_dev_groups[group].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR;
local_phys_dev_groups[group].pNext = NULL;
local_phys_dev_groups[group].subsetAllocation = false;
local_phys_dev_groups[group].group_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR;
local_phys_dev_groups[group].group_props.pNext = NULL;
local_phys_dev_groups[group].group_props.subsetAllocation = false;
}
#if defined(_WIN32)
@ -6752,7 +6847,7 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
cur_icd_group_count = 0;
icd_term = inst->icd_terms;
for (uint32_t icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) {
for (icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) {
uint32_t count_this_time = total_count - cur_icd_group_count;
// Check if this group can be sorted
@ -6791,16 +6886,20 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
// Add each GPU as it's own group
for (uint32_t indiv_gpu = 0; indiv_gpu < count_this_time; indiv_gpu++) {
local_phys_dev_groups[indiv_gpu + cur_icd_group_count].physicalDeviceCount = 1;
local_phys_dev_groups[indiv_gpu + cur_icd_group_count].physicalDevices[0] = phys_dev_array[indiv_gpu];
local_phys_dev_groups[indiv_gpu + cur_icd_group_count].this_icd_term = icd_term;
local_phys_dev_groups[indiv_gpu + cur_icd_group_count].icd_index = icd_idx;
local_phys_dev_groups[indiv_gpu + cur_icd_group_count].group_props.physicalDeviceCount = 1;
local_phys_dev_groups[indiv_gpu + cur_icd_group_count].group_props.physicalDevices[0] = phys_dev_array[indiv_gpu];
local_phys_dev_group_sorted[indiv_gpu + cur_icd_group_count] = icd_sorted;
}
} else {
res =
fpEnumeratePhysicalDeviceGroups(icd_term->instance, &count_this_time, &local_phys_dev_groups[cur_icd_group_count]);
res = fpEnumeratePhysicalDeviceGroups(icd_term->instance, &count_this_time,
&local_phys_dev_groups[cur_icd_group_count].group_props);
for (uint32_t group = 0; group < count_this_time; ++group) {
local_phys_dev_group_sorted[group + cur_icd_group_count] = icd_sorted;
local_phys_dev_groups[group].this_icd_term = icd_term;
local_phys_dev_groups[group].icd_index = icd_idx;
}
if (VK_SUCCESS != res) {
loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
@ -6814,13 +6913,22 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
cur_icd_group_count += count_this_time;
}
#ifdef LOADER_ENABLE_LINUX_SORT
if (is_linux_sort_enabled(inst)) {
// Get the physical devices supported by platform sorting mechanism into a separate list
res = linux_read_sorted_physical_device_groups(inst, total_count, local_phys_dev_groups);
}
#endif // LOADER_ENABLE_LINUX_SORT
// Replace all the physical device IDs with the proper loader values
for (uint32_t group = 0; group < total_count; group++) {
for (uint32_t group_gpu = 0; group_gpu < local_phys_dev_groups[group].physicalDeviceCount; group_gpu++) {
for (uint32_t group_gpu = 0; group_gpu < local_phys_dev_groups[group].group_props.physicalDeviceCount; group_gpu++) {
bool found = false;
for (uint32_t term_gpu = 0; term_gpu < inst->phys_dev_count_term; term_gpu++) {
if (local_phys_dev_groups[group].physicalDevices[group_gpu] == inst->phys_devs_term[term_gpu]->phys_dev) {
local_phys_dev_groups[group].physicalDevices[group_gpu] = (VkPhysicalDevice)inst->phys_devs_term[term_gpu];
if (local_phys_dev_groups[group].group_props.physicalDevices[group_gpu] ==
inst->phys_devs_term[term_gpu]->phys_dev) {
local_phys_dev_groups[group].group_props.physicalDevices[group_gpu] =
(VkPhysicalDevice)inst->phys_devs_term[term_gpu];
found = true;
break;
}
@ -6844,21 +6952,22 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
// Find the VkPhysicalDeviceGroupProperties object in local_phys_dev_groups
VkPhysicalDeviceGroupProperties *group_properties = NULL;
for (uint32_t group = 0; group < total_count; group++) {
if (sorted_phys_dev_array[i].device_count != local_phys_dev_groups[group].physicalDeviceCount) {
if (sorted_phys_dev_array[i].device_count != local_phys_dev_groups[group].group_props.physicalDeviceCount) {
continue;
}
bool match = true;
for (uint32_t group_gpu = 0; group_gpu < local_phys_dev_groups[group].physicalDeviceCount; group_gpu++) {
for (uint32_t group_gpu = 0; group_gpu < local_phys_dev_groups[group].group_props.physicalDeviceCount; group_gpu++) {
if (sorted_phys_dev_array[i].physical_devices[group_gpu] !=
((struct loader_physical_device_term *)local_phys_dev_groups[group].physicalDevices[group_gpu])->phys_dev) {
((struct loader_physical_device_term *)local_phys_dev_groups[group].group_props.physicalDevices[group_gpu])
->phys_dev) {
match = false;
break;
}
}
if (match) {
group_properties = &local_phys_dev_groups[group];
group_properties = &local_phys_dev_groups[group].group_props;
}
}
@ -6913,18 +7022,20 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
// Copy or create everything to fill the new array of physical device groups
for (uint32_t new_idx = 0; new_idx < total_count; new_idx++) {
// Skip groups which have been included through sorting
if (local_phys_dev_group_sorted[new_idx] || local_phys_dev_groups[new_idx].physicalDeviceCount == 0) {
if (local_phys_dev_group_sorted[new_idx] || local_phys_dev_groups[new_idx].group_props.physicalDeviceCount == 0) {
continue;
}
// Check if this physical device group with the same contents is already in the old buffer
for (uint32_t old_idx = 0; old_idx < inst->phys_dev_group_count_term; old_idx++) {
if (local_phys_dev_groups[new_idx].physicalDeviceCount == inst->phys_dev_groups_term[old_idx]->physicalDeviceCount) {
if (local_phys_dev_groups[new_idx].group_props.physicalDeviceCount ==
inst->phys_dev_groups_term[old_idx]->physicalDeviceCount) {
bool found_all_gpus = true;
for (uint32_t old_gpu = 0; old_gpu < inst->phys_dev_groups_term[old_idx]->physicalDeviceCount; old_gpu++) {
bool found_gpu = false;
for (uint32_t new_gpu = 0; new_gpu < local_phys_dev_groups[new_idx].physicalDeviceCount; new_gpu++) {
if (local_phys_dev_groups[new_idx].physicalDevices[new_gpu] ==
for (uint32_t new_gpu = 0; new_gpu < local_phys_dev_groups[new_idx].group_props.physicalDeviceCount;
new_gpu++) {
if (local_phys_dev_groups[new_idx].group_props.physicalDevices[new_gpu] ==
inst->phys_dev_groups_term[old_idx]->physicalDevices[old_gpu]) {
found_gpu = true;
break;
@ -6957,7 +7068,8 @@ VkResult setup_loader_term_phys_dev_groups(struct loader_instance *inst) {
res = VK_ERROR_OUT_OF_HOST_MEMORY;
goto out;
}
memcpy(new_phys_dev_groups[idx], &local_phys_dev_groups[new_idx], sizeof(VkPhysicalDeviceGroupPropertiesKHR));
memcpy(new_phys_dev_groups[idx], &local_phys_dev_groups[new_idx].group_props,
sizeof(VkPhysicalDeviceGroupPropertiesKHR));
}
++idx;

View File

@ -1,8 +1,8 @@
/*
*
* Copyright (c) 2014-2021 The Khronos Group Inc.
* Copyright (c) 2014-2021 Valve Corporation
* Copyright (c) 2014-2021 LunarG, Inc.
* Copyright (c) 2014-2022 The Khronos Group Inc.
* Copyright (c) 2014-2022 Valve Corporation
* Copyright (c) 2014-2022 LunarG, Inc.
* Copyright (C) 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -210,6 +210,7 @@ struct loader_icd_term {
struct loader_icd_term *next;
PFN_PhysDevExt phys_dev_ext[MAX_NUM_UNKNOWN_EXTS];
bool supports_get_dev_prop_2;
};
// Per ICD library structure
@ -333,6 +334,7 @@ struct loader_instance {
bool wsi_display_enabled;
bool wsi_display_props2_enabled;
bool create_terminator_invalid_extension;
bool supports_get_dev_prop_2;
};
// VkPhysicalDevice requires special treatment by loader. Firstly, terminator
@ -367,6 +369,44 @@ struct loader_physical_device_term {
VkPhysicalDevice phys_dev; // object from ICD
};
#ifdef LOADER_ENABLE_LINUX_SORT
// Structure for storing the relevent device information for selecting a device.
// NOTE: Needs to be defined here so we can store this content in the term structrue
// for quicker sorting.
struct LinuxSortedDeviceInfo {
// Associated Vulkan Physical Device
VkPhysicalDevice physical_device;
bool default_device;
// Loader specific items about the driver providing support for this physical device
uint32_t icd_index;
struct loader_icd_term *icd_term;
// Some generic device properties
VkPhysicalDeviceType device_type;
char device_name[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
uint32_t vendor_id;
uint32_t device_id;
// PCI information on this device
bool has_pci_bus_info;
uint32_t pci_domain;
uint32_t pci_bus;
uint32_t pci_device;
uint32_t pci_function;
};
#endif // LOADER_ENABLE_LINUX_SORT
// Per enumerated PhysicalDeviceGroup structure, used to wrap in terminator code
struct loader_physical_device_group_term {
struct loader_icd_term *this_icd_term;
uint8_t icd_index;
VkPhysicalDeviceGroupProperties group_props;
#ifdef LOADER_ENABLE_LINUX_SORT
struct LinuxSortedDeviceInfo internal_device_info[VK_MAX_DEVICE_GROUP_SIZE];
#endif // LOADER_ENABLE_LINUX_SORT
};
struct loader_struct {
struct loader_instance *instances;
};

450
loader/loader_linux.c Normal file
View File

@ -0,0 +1,450 @@
/*
*
* Copyright (c) 2021-2022 The Khronos Group Inc.
* Copyright (c) 2021-2022 Valve Corporation
* Copyright (c) 2021-2022 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Mark Young <marky@lunarg.com>
*
*/
// Non-windows and non-apple only header file, guard it so that accidental
// inclusion doesn't cause unknown header include errors
#ifdef LOADER_ENABLE_LINUX_SORT
#include <stdio.h>
#include <stdlib.h>
#include "loader_linux.h"
#include "allocation.h"
#include "get_environment.h"
#include "loader.h"
#include "log.h"
// Determine a priority based on device type with the higher value being higher priority.
static uint32_t determine_priority_type_value(VkPhysicalDeviceType type) {
switch (type) {
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
return 10;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
return 5;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
return 3;
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
return 2;
case VK_PHYSICAL_DEVICE_TYPE_CPU:
return 1;
case VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM: // Not really an enum, but throws warning if it's not here
break;
}
return 0;
}
// Compare the two device types.
// This behaves similar to a qsort compare.
static int32_t device_type_compare(VkPhysicalDeviceType a, VkPhysicalDeviceType b) {
uint32_t a_value = determine_priority_type_value(a);
uint32_t b_value = determine_priority_type_value(b);
if (a_value > b_value) {
return -1;
} else if (b_value > a_value) {
return 1;
}
return 0;
}
// Used to compare two devices and determine which one should have priority. The criteria is
// simple:
// 1) Default device ALWAYS wins
// 2) Sort by type
// 3) Sort by PCI bus ID
// 4) Ties broken by device_ID XOR vendor_ID comparison
int32_t compare_devices(const void *a, const void *b) {
struct LinuxSortedDeviceInfo *left = (struct LinuxSortedDeviceInfo *)a;
struct LinuxSortedDeviceInfo *right = (struct LinuxSortedDeviceInfo *)b;
// Default device always gets priority
if (left->default_device) {
return -1;
} else if (right->default_device) {
return 1;
}
// Order by device type next
int32_t dev_type_comp = device_type_compare(left->device_type, right->device_type);
if (0 != dev_type_comp) {
return dev_type_comp;
}
// Sort by PCI info (prioritize devices that have info over those that don't)
if (left->has_pci_bus_info && !right->has_pci_bus_info) {
return -1;
} else if (!left->has_pci_bus_info && right->has_pci_bus_info) {
return 1;
} else if (left->has_pci_bus_info && right->has_pci_bus_info) {
// Sort low to high PCI domain
if (left->pci_domain < right->pci_domain) {
return -1;
} else if (left->pci_domain > right->pci_domain) {
return 1;
}
// Sort low to high PCI bus
if (left->pci_bus < right->pci_bus) {
return -1;
} else if (left->pci_bus > right->pci_bus) {
return 1;
}
// Sort low to high PCI device
if (left->pci_device < right->pci_device) {
return -1;
} else if (left->pci_device > right->pci_device) {
return 1;
}
// Sort low to high PCI function
if (left->pci_function < right->pci_function) {
return -1;
} else if (left->pci_function > right->pci_function) {
return 1;
}
}
// Somehow we have a tie above, so XOR vendorID and deviceID and compare
uint32_t left_xord_dev_vend = left->device_id ^ left->vendor_id;
uint32_t right_xord_dev_vend = right->device_id ^ right->vendor_id;
if (left_xord_dev_vend < right_xord_dev_vend) {
return -1;
} else if (right_xord_dev_vend < left_xord_dev_vend) {
return 1;
}
return 0;
}
// Used to compare two device groups and determine which one should have priority.
// NOTE: This assumes that devices in each group have already been sorted.
// The group sort criteria is simple:
// 1) Group with the default device ALWAYS wins
// 2) Group with the best device type for device 0 wins
// 3) Group with best PCI bus ID for device 0 wins
// 4) Ties broken by group device 0 device_ID XOR vendor_ID comparison
int32_t compare_device_groups(const void *a, const void *b) {
struct loader_physical_device_group_term *grp_a = (struct loader_physical_device_group_term *)a;
struct loader_physical_device_group_term *grp_b = (struct loader_physical_device_group_term *)b;
// Use the first GPU's info from each group to sort the groups by
struct LinuxSortedDeviceInfo *left = &grp_a->internal_device_info[0];
struct LinuxSortedDeviceInfo *right = &grp_b->internal_device_info[0];
// Default device always gets priority
if (left->default_device) {
return -1;
} else if (right->default_device) {
return 1;
}
// Order by device type next
int32_t dev_type_comp = device_type_compare(left->device_type, right->device_type);
if (0 != dev_type_comp) {
return dev_type_comp;
}
// Sort by PCI info (prioritize devices that have info over those that don't)
if (left->has_pci_bus_info && !right->has_pci_bus_info) {
return -1;
} else if (!left->has_pci_bus_info && right->has_pci_bus_info) {
return 1;
} else if (left->has_pci_bus_info && right->has_pci_bus_info) {
// Sort low to high PCI domain
if (left->pci_domain < right->pci_domain) {
return -1;
} else if (left->pci_domain > right->pci_domain) {
return 1;
}
// Sort low to high PCI bus
if (left->pci_bus < right->pci_bus) {
return -1;
} else if (left->pci_bus > right->pci_bus) {
return 1;
}
// Sort low to high PCI device
if (left->pci_device < right->pci_device) {
return -1;
} else if (left->pci_device > right->pci_device) {
return 1;
}
// Sort low to high PCI function
if (left->pci_function < right->pci_function) {
return -1;
} else if (left->pci_function > right->pci_function) {
return 1;
}
}
// Somehow we have a tie above, so XOR vendorID and deviceID and compare
uint32_t left_xord_dev_vend = left->device_id ^ left->vendor_id;
uint32_t right_xord_dev_vend = right->device_id ^ right->vendor_id;
if (left_xord_dev_vend < right_xord_dev_vend) {
return -1;
} else if (right_xord_dev_vend < left_xord_dev_vend) {
return 1;
}
return 0;
}
// Search for the default device using the loader environment variable.
static void linux_env_var_default_device(struct loader_instance *inst, uint32_t device_count,
struct LinuxSortedDeviceInfo *sorted_device_info) {
char *selection = loader_getenv("VK_LOADER_DEVICE_SELECT", inst);
if (NULL != selection) {
loader_log(inst, VULKAN_LOADER_DEBUG_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
"linux_env_var_default_device: Found VK_LOADER_DEVICE_SELECT set to %s", selection);
// The environment variable exists, so grab the vendor ID and device ID of the
// selected default device
unsigned vendor_id, device_id;
int32_t matched = sscanf(selection, "%x:%x", &vendor_id, &device_id);
if (matched == 2) {
for (int32_t i = 0; i < (int32_t)device_count; ++i) {
if (sorted_device_info[i].vendor_id == vendor_id && sorted_device_info[i].device_id == device_id) {
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
"linux_env_var_default_device: Found default at index %u \'%s\'", i, sorted_device_info[i].device_name);
sorted_device_info[i].default_device = true;
break;
}
}
}
loader_free_getenv(selection, inst);
}
}
// This function allocates an array in sorted_devices which must be freed by the caller if not null
VkResult linux_read_sorted_physical_devices(struct loader_instance *inst, uint32_t icd_count,
struct loader_phys_dev_per_icd *icd_devices,
struct loader_physical_device_term **sorted_device_term) {
VkResult res = VK_SUCCESS;
bool is_vulkan_1_1 = false;
if (inst->app_api_major_version >= 1 && inst->app_api_minor_version >= 1) {
is_vulkan_1_1 = true;
}
struct LinuxSortedDeviceInfo *sorted_device_info = loader_instance_heap_alloc(
inst, inst->total_gpu_count * sizeof(struct LinuxSortedDeviceInfo), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (NULL == sorted_device_info) {
res = VK_ERROR_OUT_OF_HOST_MEMORY;
goto out;
}
memset(sorted_device_info, 0, inst->total_gpu_count * sizeof(struct LinuxSortedDeviceInfo));
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "linux_read_sorted_physical_devices: Original order:");
// Grab all the necessary info we can about each device
uint32_t index = 0;
for (uint32_t icd_idx = 0; icd_idx < icd_count; ++icd_idx) {
for (uint32_t phys_dev = 0; phys_dev < icd_devices[icd_idx].count; ++phys_dev) {
struct loader_icd_term *icd_term = icd_devices[icd_idx].this_icd_term;
VkPhysicalDeviceProperties dev_props = {};
sorted_device_info[index].physical_device = icd_devices[icd_idx].phys_devs[phys_dev];
sorted_device_info[index].icd_index = icd_idx;
sorted_device_info[index].icd_term = icd_term;
sorted_device_info[index].has_pci_bus_info = false;
icd_term->dispatch.GetPhysicalDeviceProperties(sorted_device_info[index].physical_device, &dev_props);
sorted_device_info[index].device_type = dev_props.deviceType;
strncpy(sorted_device_info[index].device_name, dev_props.deviceName, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE);
sorted_device_info[index].vendor_id = dev_props.vendorID;
sorted_device_info[index].device_id = dev_props.deviceID;
bool device_is_1_1_capable =
VK_API_VERSION_MAJOR(dev_props.apiVersion) == 1 && VK_API_VERSION_MINOR(dev_props.apiVersion) >= 1;
sorted_device_info[index].has_pci_bus_info = device_is_1_1_capable;
if (!sorted_device_info[index].has_pci_bus_info) {
uint32_t ext_count;
icd_term->dispatch.EnumerateDeviceExtensionProperties(sorted_device_info[index].physical_device, NULL, &ext_count,
NULL);
if (ext_count > 0) {
VkExtensionProperties *ext_props =
(VkExtensionProperties *)loader_stack_alloc(sizeof(VkExtensionProperties) * ext_count);
if (NULL == ext_props) {
res = VK_ERROR_OUT_OF_HOST_MEMORY;
goto out;
}
icd_term->dispatch.EnumerateDeviceExtensionProperties(sorted_device_info[index].physical_device, NULL,
&ext_count, ext_props);
for (uint32_t ext = 0; ext < ext_count; ++ext) {
if (!strcmp(ext_props[ext].extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME)) {
sorted_device_info[index].has_pci_bus_info = true;
break;
}
}
}
}
if (sorted_device_info[index].has_pci_bus_info) {
VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = (VkPhysicalDevicePCIBusInfoPropertiesEXT){
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT};
VkPhysicalDeviceProperties2 dev_props2 = (VkPhysicalDeviceProperties2){
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = (VkBaseInStructure *)&pci_props};
if (is_vulkan_1_1 && device_is_1_1_capable) {
icd_term->dispatch.GetPhysicalDeviceProperties2(sorted_device_info[index].physical_device, &dev_props2);
} else {
icd_term->dispatch.GetPhysicalDeviceProperties2KHR(sorted_device_info[index].physical_device, &dev_props2);
}
sorted_device_info[index].pci_domain = pci_props.pciDomain;
sorted_device_info[index].pci_bus = pci_props.pciBus;
sorted_device_info[index].pci_device = pci_props.pciDevice;
sorted_device_info[index].pci_function = pci_props.pciFunction;
}
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s", index,
sorted_device_info[index].device_name);
index++;
}
}
// Select default device if set in the environment variable
linux_env_var_default_device(inst, inst->total_gpu_count, sorted_device_info);
// Sort devices by PCI info
qsort(sorted_device_info, inst->total_gpu_count, sizeof(struct LinuxSortedDeviceInfo), compare_devices);
// If we have a selected index, add that first.
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "linux_read_sorted_physical_devices: Order set to:");
// Add all others after (they've already been sorted)
for (uint32_t dev = 0; dev < inst->total_gpu_count; ++dev) {
sorted_device_term[dev]->this_icd_term = sorted_device_info[dev].icd_term;
sorted_device_term[dev]->icd_index = sorted_device_info[dev].icd_index;
sorted_device_term[dev]->phys_dev = sorted_device_info[dev].physical_device;
loader_set_dispatch((void *)sorted_device_term[dev], inst->disp);
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s %s", dev,
sorted_device_info[dev].device_name, (sorted_device_info[dev].default_device ? "[default]" : ""));
}
out:
if (NULL != sorted_device_info) {
loader_instance_heap_free(inst, sorted_device_info);
}
return res;
}
// This function allocates an array in sorted_devices which must be freed by the caller if not null
VkResult linux_read_sorted_physical_device_groups(struct loader_instance *inst, uint32_t group_count,
struct loader_physical_device_group_term *sorted_group_term) {
VkResult res = VK_SUCCESS;
bool is_vulkan_1_1 = false;
if (inst->app_api_major_version >= 1 && inst->app_api_minor_version >= 1) {
is_vulkan_1_1 = true;
}
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
"linux_read_sorted_physical_device_groups: Original order:");
for (uint32_t group = 0; group < group_count; ++group) {
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " Group %u", group);
struct loader_icd_term *icd_term = sorted_group_term[group].this_icd_term;
for (uint32_t gpu = 0; gpu < sorted_group_term[group].group_props.physicalDeviceCount; ++gpu) {
VkPhysicalDeviceProperties dev_props = {};
sorted_group_term[group].internal_device_info[gpu].physical_device =
sorted_group_term[group].group_props.physicalDevices[gpu];
sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info = false;
icd_term->dispatch.GetPhysicalDeviceProperties(sorted_group_term[group].internal_device_info[gpu].physical_device,
&dev_props);
sorted_group_term[group].internal_device_info[gpu].device_type = dev_props.deviceType;
strncpy(sorted_group_term[group].internal_device_info[gpu].device_name, dev_props.deviceName,
VK_MAX_PHYSICAL_DEVICE_NAME_SIZE);
sorted_group_term[group].internal_device_info[gpu].vendor_id = dev_props.vendorID;
sorted_group_term[group].internal_device_info[gpu].device_id = dev_props.deviceID;
bool device_is_1_1_capable =
VK_API_VERSION_MAJOR(dev_props.apiVersion) == 1 && VK_API_VERSION_MINOR(dev_props.apiVersion) >= 1;
sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info = device_is_1_1_capable;
if (!sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info) {
uint32_t ext_count;
icd_term->dispatch.EnumerateDeviceExtensionProperties(
sorted_group_term[group].internal_device_info[gpu].physical_device, NULL, &ext_count, NULL);
if (ext_count > 0) {
VkExtensionProperties *ext_props =
(VkExtensionProperties *)loader_stack_alloc(sizeof(VkExtensionProperties) * ext_count);
if (NULL == ext_props) {
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
icd_term->dispatch.EnumerateDeviceExtensionProperties(
sorted_group_term[group].internal_device_info[gpu].physical_device, NULL, &ext_count, ext_props);
for (uint32_t ext = 0; ext < ext_count; ++ext) {
if (!strcmp(ext_props[ext].extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME)) {
sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info = true;
break;
}
}
}
}
if (sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info) {
VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = (VkPhysicalDevicePCIBusInfoPropertiesEXT){
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT};
VkPhysicalDeviceProperties2 dev_props2 = (VkPhysicalDeviceProperties2){
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = (VkBaseInStructure *)&pci_props};
if (is_vulkan_1_1 && device_is_1_1_capable) {
icd_term->dispatch.GetPhysicalDeviceProperties2(
sorted_group_term[group].internal_device_info[gpu].physical_device, &dev_props2);
} else {
icd_term->dispatch.GetPhysicalDeviceProperties2KHR(
sorted_group_term[group].internal_device_info[gpu].physical_device, &dev_props2);
}
sorted_group_term[group].internal_device_info[gpu].pci_domain = pci_props.pciDomain;
sorted_group_term[group].internal_device_info[gpu].pci_bus = pci_props.pciBus;
sorted_group_term[group].internal_device_info[gpu].pci_device = pci_props.pciDevice;
sorted_group_term[group].internal_device_info[gpu].pci_function = pci_props.pciFunction;
}
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s", gpu,
sorted_group_term[group].internal_device_info[gpu].device_name);
}
// Select default device if set in the environment variable
linux_env_var_default_device(inst, sorted_group_term[group].group_props.physicalDeviceCount,
sorted_group_term[group].internal_device_info);
// Sort GPUs in each group
qsort(sorted_group_term[group].internal_device_info, sorted_group_term[group].group_props.physicalDeviceCount,
sizeof(struct LinuxSortedDeviceInfo), compare_devices);
}
// Sort device groups by PCI info
qsort(sorted_group_term, group_count, sizeof(struct loader_physical_device_group_term), compare_device_groups);
if (loader_get_debug_level() & (VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT)) {
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
"linux_read_sorted_physical_device_groups: Sorted order:");
for (uint32_t group = 0; group < group_count; ++group) {
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " Group %u", group);
for (uint32_t gpu = 0; gpu < sorted_group_term[group].group_props.physicalDeviceCount; ++gpu) {
loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s %s", gpu,
sorted_group_term[group].internal_device_info[gpu].device_name,
(sorted_group_term[group].internal_device_info[gpu].default_device ? "[default]" : ""));
}
}
}
return res;
}
#endif // LOADER_ENABLE_LINUX_SORT

38
loader/loader_linux.h Normal file
View File

@ -0,0 +1,38 @@
/*
*
* Copyright (c) 2021-2022 The Khronos Group Inc.
* Copyright (c) 2021-2022 Valve Corporation
* Copyright (c) 2021-2022 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Mark Young <marky@lunarg.com>
*
*/
#pragma once
#ifdef LOADER_ENABLE_LINUX_SORT
#include "loader_common.h"
// This function allocates an array in sorted_devices which must be freed by the caller if not null
VkResult linux_read_sorted_physical_devices(struct loader_instance *inst, uint32_t icd_count,
struct loader_phys_dev_per_icd *icd_devices,
struct loader_physical_device_term **sorted_device_term);
// This function allocates an array in sorted_devices which must be freed by the caller if not null
VkResult linux_read_sorted_physical_device_groups(struct loader_instance *inst, uint32_t group_count,
struct loader_physical_device_group_term *sorted_group_term);
#endif // LOADER_ENABLE_LINUX_SORT

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2021 The Khronos Group Inc.
* Copyright (c) 2021 Valve Corporation
* Copyright (c) 2021 LunarG, Inc.
* Copyright (c) 2021-2022 The Khronos Group Inc.
* Copyright (c) 2021-2022 Valve Corporation
* Copyright (c) 2021-2022 LunarG, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and/or associated documentation files (the "Materials"), to
@ -34,7 +34,8 @@ struct PhysicalDevice {
PhysicalDevice() {}
PhysicalDevice(std::string name) : deviceName(name) {}
PhysicalDevice(const char* name) : deviceName(name) {}
PhysicalDevice(std::string name, uint32_t bus) : deviceName(name), pci_bus(bus) {}
PhysicalDevice(const char* name, uint32_t bus) : deviceName(name), pci_bus(bus) {}
DispatchableHandle<VkPhysicalDevice> vk_physical_device;
BUILDER_VALUE(PhysicalDevice, std::string, deviceName, "")
BUILDER_VALUE(PhysicalDevice, VkPhysicalDeviceProperties, properties, {})
@ -44,6 +45,7 @@ struct PhysicalDevice {
BUILDER_VALUE(PhysicalDevice, VkExternalMemoryProperties, external_memory_properties, {})
BUILDER_VALUE(PhysicalDevice, VkExternalSemaphoreProperties, external_semaphore_properties, {})
BUILDER_VALUE(PhysicalDevice, VkExternalFenceProperties, external_fence_properties, {})
BUILDER_VALUE(PhysicalDevice, uint32_t, pci_bus, {})
BUILDER_VECTOR(PhysicalDevice, MockQueueFamilyProperties, queue_family_properties, queue_family_properties)
BUILDER_VECTOR(PhysicalDevice, VkFormatProperties, format_properties, format_properties)

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2021 The Khronos Group Inc.
* Copyright (c) 2021 Valve Corporation
* Copyright (c) 2021 LunarG, Inc.
* Copyright (c) 2021-2022 The Khronos Group Inc.
* Copyright (c) 2021-2022 Valve Corporation
* Copyright (c) 2021-2022 LunarG, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and/or associated documentation files (the "Materials"), to
@ -723,7 +723,12 @@ VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceFeatures(VkPhysicalDevice phy
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceProperties* pProperties) {
if (nullptr != pProperties) {
memcpy(pProperties, &icd.GetPhysDevice(physicalDevice).properties, sizeof(VkPhysicalDeviceProperties));
auto& phys_dev = icd.GetPhysDevice(physicalDevice);
memcpy(pProperties, &phys_dev.properties, sizeof(VkPhysicalDeviceProperties));
uint32_t max_len = (phys_dev.deviceName.length() > VK_MAX_PHYSICAL_DEVICE_NAME_SIZE) ? VK_MAX_PHYSICAL_DEVICE_NAME_SIZE
: phys_dev.deviceName.length();
std::copy(phys_dev.deviceName.c_str(), phys_dev.deviceName.c_str() + max_len, pProperties->deviceName);
pProperties->deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE - 1] = '\0';
}
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice,
@ -766,7 +771,17 @@ VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceFeatures2(VkPhysicalDevice ph
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceProperties2* pProperties) {
if (nullptr != pProperties) {
auto& phys_dev = icd.GetPhysDevice(physicalDevice);
test_vkGetPhysicalDeviceProperties(physicalDevice, &pProperties->properties);
VkBaseInStructure* pNext = reinterpret_cast<VkBaseInStructure*>(pProperties->pNext);
while (pNext) {
if (pNext->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT) {
VkPhysicalDevicePCIBusInfoPropertiesEXT* bus_info =
reinterpret_cast<VkPhysicalDevicePCIBusInfoPropertiesEXT*>(pNext);
bus_info->pciBus = phys_dev.pci_bus;
}
pNext = reinterpret_cast<VkBaseInStructure*>(const_cast<VkBaseInStructure*>(pNext->pNext));
}
}
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice,
@ -1037,6 +1052,23 @@ PFN_vkVoidFunction get_physical_device_func(VkInstance instance, const char* pNa
if (string_eq(pName, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR"))
return TO_VOID_PFN(test_vkGetPhysicalDeviceSparseImageFormatProperties2);
if (string_eq(pName, "vkGetPhysicalDeviceImageFormatProperties2KHR")) {
return TO_VOID_PFN(test_vkGetPhysicalDeviceImageFormatProperties2);
}
} else if (IsInstanceExtensionEnabled("VK_KHR_get_physical_device_properties2")) {
if (string_eq(pName, "vkGetPhysicalDeviceFeatures2KHR")) return TO_VOID_PFN(test_vkGetPhysicalDeviceFeatures2);
if (string_eq(pName, "vkGetPhysicalDeviceProperties2KHR")) return TO_VOID_PFN(test_vkGetPhysicalDeviceProperties2);
if (string_eq(pName, "vkGetPhysicalDeviceFormatProperties2KHR"))
return TO_VOID_PFN(test_vkGetPhysicalDeviceFormatProperties2);
if (string_eq(pName, "vkGetPhysicalDeviceMemoryProperties2KHR"))
return TO_VOID_PFN(test_vkGetPhysicalDeviceMemoryProperties2);
if (string_eq(pName, "vkGetPhysicalDeviceQueueFamilyProperties2KHR"))
return TO_VOID_PFN(test_vkGetPhysicalDeviceQueueFamilyProperties2);
if (string_eq(pName, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR"))
return TO_VOID_PFN(test_vkGetPhysicalDeviceSparseImageFormatProperties2);
if (string_eq(pName, "vkGetPhysicalDeviceImageFormatProperties2KHR")) {
return TO_VOID_PFN(test_vkGetPhysicalDeviceImageFormatProperties2);
}

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2021 The Khronos Group Inc.
* Copyright (c) 2021 Valve Corporation
* Copyright (c) 2021 LunarG, Inc.
* Copyright (c) 2021-2022 The Khronos Group Inc.
* Copyright (c) 2021-2022 Valve Corporation
* Copyright (c) 2021-2022 LunarG, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and/or associated documentation files (the "Materials"), to

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2021 The Khronos Group Inc.
* Copyright (c) 2021 Valve Corporation
* Copyright (c) 2021 LunarG, Inc.
* Copyright (c) 2021-2022 The Khronos Group Inc.
* Copyright (c) 2021-2022 Valve Corporation
* Copyright (c) 2021-2022 LunarG, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and/or associated documentation files (the "Materials"), to
@ -53,6 +53,7 @@ class EnumerateInstanceExtensionProperties : public RegressionTests {};
class EnumerateDeviceLayerProperties : public RegressionTests {};
class EnumerateDeviceExtensionProperties : public RegressionTests {};
class EnumeratePhysicalDevices : public RegressionTests {};
class SortedPhysicalDevices : public RegressionTests {};
class CreateDevice : public RegressionTests {};
class EnumeratePhysicalDeviceGroups : public RegressionTests {};
class WrapObjects : public RegressionTests {};
@ -349,10 +350,10 @@ TEST_F(EnumerateDeviceExtensionProperties, PropertyCountLessThanAvailable) {
TEST_F(EnumeratePhysicalDevices, OneCall) {
auto& driver = env->get_test_icd().set_min_icd_interface_version(5);
driver.physical_devices.emplace_back("physical_device_0");
driver.physical_devices.emplace_back("physical_device_1");
driver.physical_devices.emplace_back("physical_device_2");
driver.physical_devices.emplace_back("physical_device_3");
driver.physical_devices.emplace_back("physical_device_0", 1);
driver.physical_devices.emplace_back("physical_device_1", 2);
driver.physical_devices.emplace_back("physical_device_2", 3);
driver.physical_devices.emplace_back("physical_device_3", 4);
InstWrapper inst{env->vulkan_functions};
inst.CheckCreate();
@ -366,10 +367,13 @@ TEST_F(EnumeratePhysicalDevices, OneCall) {
TEST_F(EnumeratePhysicalDevices, TwoCall) {
auto& driver = env->get_test_icd().set_min_icd_interface_version(5);
Extension first_ext{VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME}; // known instance extensions
env->reset_icd().add_instance_extension(first_ext);
const uint32_t real_device_count = 2;
for (size_t i = 0; i < real_device_count; i++) {
driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(i));
driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(i), i + 1);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
}
InstWrapper inst{env->vulkan_functions};
@ -389,10 +393,13 @@ TEST_F(EnumeratePhysicalDevices, TwoCall) {
TEST_F(EnumeratePhysicalDevices, MatchOneAndTwoCallNumbers) {
auto& driver = env->get_test_icd();
driver.set_min_icd_interface_version(5);
Extension first_ext{VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME}; // known instance extensions
env->reset_icd().add_instance_extension(first_ext);
const uint32_t real_device_count = 3;
for (size_t i = 0; i < real_device_count; i++) {
driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(i));
driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(i), i + 1);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
}
InstWrapper inst1{env->vulkan_functions};
@ -421,10 +428,13 @@ TEST_F(EnumeratePhysicalDevices, MatchOneAndTwoCallNumbers) {
TEST_F(EnumeratePhysicalDevices, TwoCallIncomplete) {
auto& driver = env->get_test_icd().set_min_icd_interface_version(5);
Extension first_ext{VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME}; // known instance extensions
env->reset_icd().add_instance_extension(first_ext);
const uint32_t real_device_count = 2;
for (size_t i = 0; i < real_device_count; i++) {
driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(i));
driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(i), i + 1);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
}
InstWrapper inst{env->vulkan_functions};
@ -596,9 +606,14 @@ TEST(TryLoadWrongBinaries, WrongExplicitAndImplicit) {
TEST_F(EnumeratePhysicalDeviceGroups, OneCall) {
auto& driver = env->get_test_icd().set_min_icd_interface_version(5);
Extension first_ext{VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME}; // known instance extensions
env->reset_icd().add_instance_extension(first_ext);
// ICD contains 2 devices
driver.physical_devices.emplace_back("PhysicalDevice0");
driver.physical_devices.emplace_back("PhysicalDevice1");
driver.physical_devices.emplace_back("PhysicalDevice0", 12);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
driver.physical_devices.emplace_back("PhysicalDevice1", 24);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
// ICD contains 1 group, which contains both devices
driver.physical_device_groups.push_back({});
driver.physical_device_groups.back()
@ -623,8 +638,6 @@ TEST_F(EnumeratePhysicalDeviceGroups, OneCall) {
group_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDeviceGroups(inst, &returned_group_count, &group_props));
ASSERT_EQ(group_count, returned_group_count);
handle_assert_equal(group_props.physicalDevices[0], physical_devices[0]);
handle_assert_equal(group_props.physicalDevices[1], physical_devices[1]);
}
driver.add_instance_extension({"VK_KHR_device_group_creation"});
// Extension
@ -647,16 +660,19 @@ TEST_F(EnumeratePhysicalDeviceGroups, OneCall) {
group_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR;
ASSERT_EQ(VK_SUCCESS, vkEnumeratePhysicalDeviceGroupsKHR(inst, &returned_group_count, &group_props));
ASSERT_EQ(group_count, returned_group_count);
handle_assert_equal(group_props.physicalDevices[0], physical_devices[0]);
handle_assert_equal(group_props.physicalDevices[1], physical_devices[1]);
}
}
TEST_F(EnumeratePhysicalDeviceGroups, TwoCall) {
auto& driver = env->get_test_icd().set_min_icd_interface_version(5);
Extension first_ext{VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME}; // known instance extensions
env->reset_icd().add_instance_extension(first_ext);
// ICD contains 2 devices
driver.physical_devices.emplace_back("PhysicalDevice0");
driver.physical_devices.emplace_back("PhysicalDevice1");
driver.physical_devices.emplace_back("PhysicalDevice0", 12);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
driver.physical_devices.emplace_back("PhysicalDevice1", 24);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
// ICD contains 1 group, which contains both devices
driver.physical_device_groups.push_back({});
driver.physical_device_groups.back()
@ -684,8 +700,6 @@ TEST_F(EnumeratePhysicalDeviceGroups, TwoCall) {
group_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDeviceGroups(inst, &returned_group_count, &group_props));
ASSERT_EQ(group_count, returned_group_count);
handle_assert_equal(group_props.physicalDevices[0], physical_devices[0]);
handle_assert_equal(group_props.physicalDevices[1], physical_devices[1]);
}
driver.add_instance_extension({"VK_KHR_device_group_creation"});
// Extension
@ -711,16 +725,19 @@ TEST_F(EnumeratePhysicalDeviceGroups, TwoCall) {
group_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR;
ASSERT_EQ(VK_SUCCESS, vkEnumeratePhysicalDeviceGroupsKHR(inst, &returned_group_count, &group_props));
ASSERT_EQ(group_count, returned_group_count);
handle_assert_equal(group_props.physicalDevices[0], physical_devices[0]);
handle_assert_equal(group_props.physicalDevices[1], physical_devices[1]);
}
}
TEST_F(EnumeratePhysicalDeviceGroups, TwoCallIncomplete) {
auto& driver = env->get_test_icd().set_min_icd_interface_version(5);
Extension first_ext{VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME}; // known instance extensions
env->reset_icd().add_instance_extension(first_ext);
// ICD contains 2 devices
driver.physical_devices.emplace_back("PhysicalDevice0");
driver.physical_devices.emplace_back("PhysicalDevice1");
driver.physical_devices.emplace_back("PhysicalDevice0", 12);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
driver.physical_devices.emplace_back("PhysicalDevice1", 24);
driver.physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
// ICD contains 1 group, which contains both devices
driver.physical_device_groups.push_back({});
driver.physical_device_groups.back()
@ -921,3 +938,365 @@ TEST_F(CreateInstance, InstanceNullExtensionPtr) {
ASSERT_EQ(env->vulkan_functions.vkCreateInstance(&info, VK_NULL_HANDLE, &inst), VK_ERROR_EXTENSION_NOT_PRESENT);
}
// Fill in random but valid data into the device properties struct for the current physical device
static void FillInRandomDeviceProps(VkPhysicalDeviceProperties& props, VkPhysicalDeviceType dev_type, uint32_t api_vers,
uint32_t vendor, uint32_t device) {
props.apiVersion = api_vers;
props.vendorID = vendor;
props.deviceID = device;
props.deviceType = dev_type;
for (uint8_t idx = 0; idx < VK_UUID_SIZE; ++idx) {
props.pipelineCacheUUID[idx] = static_cast<uint8_t>(rand() % 255);
}
}
#if defined(__linux__) || defined(__FreeBSD__)
// NOTE: Sort order only affects Linux
TEST(SortedPhysicalDevices, DevicesSortEnabled) {
FrameworkEnvironment env{};
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
env.get_test_icd(0).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
env.get_test_icd(0).physical_devices.push_back({"pd0", 7});
FillInRandomDeviceProps(env.get_test_icd(0).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
VK_API_VERSION_1_0, 888, 0xAAA001);
env.get_test_icd(0).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.get_test_icd(0).physical_devices.push_back({"pd1", 3});
FillInRandomDeviceProps(env.get_test_icd(0).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,
VK_API_VERSION_1_0, 888, 0xAAA002);
env.get_test_icd(0).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
env.get_test_icd(1).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
env.get_test_icd(1).physical_devices.push_back({"pd2", 0});
FillInRandomDeviceProps(env.get_test_icd(1).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_CPU, VK_API_VERSION_1_0,
1, 0xBBBB001);
env.get_test_icd(1).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
env.get_test_icd(2).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
env.get_test_icd(2).physical_devices.push_back({"pd3", 1});
FillInRandomDeviceProps(env.get_test_icd(2).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
VK_API_VERSION_1_0, 75, 0xCCCC001);
env.get_test_icd(2).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.get_test_icd(2).physical_devices.push_back({"pd4", 4});
FillInRandomDeviceProps(env.get_test_icd(2).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
VK_API_VERSION_1_0, 75, 0xCCCC002);
env.get_test_icd(2).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
env.get_test_icd(3).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
env.get_test_icd(3).physical_devices.push_back({"pd5", 0});
FillInRandomDeviceProps(env.get_test_icd(3).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU,
VK_API_VERSION_1_0, 6940, 0xDDDD001);
env.get_test_icd(3).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
InstWrapper instance(env.vulkan_functions);
instance.create_info.add_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
instance.CheckCreate();
auto GetPhysDevProps2 = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(
instance.functions->vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR"));
ASSERT_NE(GetPhysDevProps2, nullptr);
const uint32_t max_phys_devs = 6;
uint32_t device_count = max_phys_devs;
std::array<VkPhysicalDevice, max_phys_devs> physical_devices;
ASSERT_EQ(VK_SUCCESS, instance->vkEnumeratePhysicalDevices(instance, &device_count, physical_devices.data()));
ASSERT_EQ(device_count, max_phys_devs);
for (uint32_t dev = 0; dev < device_count; ++dev) {
VkPhysicalDeviceProperties props{};
instance->vkGetPhysicalDeviceProperties(physical_devices[dev], &props);
VkPhysicalDeviceProperties2KHR props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
VkPhysicalDevicePCIBusInfoPropertiesEXT pci_bus_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT};
props2.pNext = &pci_bus_info;
GetPhysDevProps2(physical_devices[dev], &props2);
switch (dev) {
case 0:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
ASSERT_EQ(true, !strcmp("pd3", props.deviceName));
ASSERT_EQ(props.vendorID, 75);
ASSERT_EQ(props.deviceID, 0xCCCC001);
ASSERT_EQ(pci_bus_info.pciBus, 1);
break;
case 1:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
ASSERT_EQ(true, !strcmp("pd4", props.deviceName));
ASSERT_EQ(props.vendorID, 75);
ASSERT_EQ(props.deviceID, 0xCCCC002);
ASSERT_EQ(pci_bus_info.pciBus, 4);
break;
case 2:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
ASSERT_EQ(true, !strcmp("pd0", props.deviceName));
ASSERT_EQ(props.vendorID, 888);
ASSERT_EQ(props.deviceID, 0xAAA001);
ASSERT_EQ(pci_bus_info.pciBus, 7);
break;
case 3:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU);
ASSERT_EQ(true, !strcmp("pd1", props.deviceName));
ASSERT_EQ(props.vendorID, 888);
ASSERT_EQ(props.deviceID, 0xAAA002);
ASSERT_EQ(pci_bus_info.pciBus, 3);
break;
case 4:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU);
ASSERT_EQ(true, !strcmp("pd5", props.deviceName));
ASSERT_EQ(props.vendorID, 6940);
ASSERT_EQ(props.deviceID, 0xDDDD001);
ASSERT_EQ(pci_bus_info.pciBus, 0);
break;
case 5:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_CPU);
ASSERT_EQ(true, !strcmp("pd2", props.deviceName));
ASSERT_EQ(props.vendorID, 1);
ASSERT_EQ(props.deviceID, 0xBBBB001);
ASSERT_EQ(pci_bus_info.pciBus, 0);
break;
default:
ASSERT_EQ(false, true);
}
}
}
TEST(SortedPhysicalDevices, DevicesSortedDisabled) {
FrameworkEnvironment env{};
set_env_var("VK_LOADER_DISABLE_SELECT", "1");
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
env.get_test_icd(0).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
env.get_test_icd(0).physical_devices.push_back({"pd0", 7});
FillInRandomDeviceProps(env.get_test_icd(0).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
VK_API_VERSION_1_0, 888, 0xAAA001);
env.get_test_icd(0).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.get_test_icd(0).physical_devices.push_back({"pd1", 3});
FillInRandomDeviceProps(env.get_test_icd(0).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,
VK_API_VERSION_1_0, 888, 0xAAA002);
env.get_test_icd(0).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
env.get_test_icd(1).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
env.get_test_icd(1).physical_devices.push_back({"pd2", 0});
FillInRandomDeviceProps(env.get_test_icd(1).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_CPU, VK_API_VERSION_1_0,
1, 0xBBBB001);
env.get_test_icd(1).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
env.get_test_icd(2).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
env.get_test_icd(2).physical_devices.push_back({"pd3", 1});
FillInRandomDeviceProps(env.get_test_icd(2).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
VK_API_VERSION_1_0, 75, 0xCCCC001);
env.get_test_icd(2).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.get_test_icd(2).physical_devices.push_back({"pd4", 4});
FillInRandomDeviceProps(env.get_test_icd(2).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
VK_API_VERSION_1_0, 75, 0xCCCC002);
env.get_test_icd(2).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
env.get_test_icd(3).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
env.get_test_icd(3).physical_devices.push_back({"pd5", 0});
FillInRandomDeviceProps(env.get_test_icd(3).physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU,
VK_API_VERSION_1_0, 6940, 0xDDDD001);
env.get_test_icd(3).physical_devices.back().extensions.push_back({VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, 0});
InstWrapper instance(env.vulkan_functions);
instance.create_info.add_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
instance.CheckCreate();
// Just make sure we have the correct number of devices
const uint32_t max_phys_devs = 6;
uint32_t device_count = max_phys_devs;
std::array<VkPhysicalDevice, max_phys_devs> physical_devices;
ASSERT_EQ(VK_SUCCESS, instance->vkEnumeratePhysicalDevices(instance, &device_count, physical_devices.data()));
ASSERT_EQ(device_count, max_phys_devs);
// Make sure the devices are not in the sorted order. The order is really undefined, but the chances of
// it being exactly the expected sorted is very low.
bool sorted = true;
for (uint32_t dev = 0; dev < device_count; ++dev) {
VkPhysicalDeviceProperties props{};
instance->vkGetPhysicalDeviceProperties(physical_devices[dev], &props);
switch (dev) {
case 0:
if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd3", props.deviceName)) {
sorted = false;
}
break;
case 1:
if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd4", props.deviceName)) {
sorted = false;
}
break;
case 2:
if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd0", props.deviceName)) {
sorted = false;
}
break;
case 3:
if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU || strcmp("pd1", props.deviceName)) {
sorted = false;
}
break;
case 4:
if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU || strcmp("pd5", props.deviceName)) {
sorted = false;
}
break;
case 5:
if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_CPU || strcmp("pd2", props.deviceName)) {
sorted = false;
}
break;
default:
ASSERT_EQ(false, true);
}
}
ASSERT_EQ(false, sorted);
remove_env_var("VK_LOADER_DISABLE_SELECT");
}
#if 0 // TODO: Enable check on physical device group sorting to make sure proper order returned.
// This is disabled because the test framework needs a little more work for this.
TEST(SortedPhysicalDevices, DeviceGroupsSortedEnabled) {
FrameworkEnvironment env{};
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_1));
auto& cur_icd_0 = env.get_test_icd(0);
cur_icd_0.set_icd_api_version(VK_API_VERSION_1_1);
cur_icd_0.physical_devices.push_back({"pd0", 7});
FillInRandomDeviceProps(cur_icd_0.physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_API_VERSION_1_1,
888, 0xAAA001);
cur_icd_0.physical_devices.push_back({"pd1", 3});
FillInRandomDeviceProps(cur_icd_0.physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,
VK_API_VERSION_1_1, 888, 0xAAA002);
cur_icd_0.physical_devices.push_back({"pd2", 6});
FillInRandomDeviceProps(cur_icd_0.physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_API_VERSION_1_1,
888, 0xAAA003);
cur_icd_0.physical_device_groups.push_back({});
cur_icd_0.physical_device_groups.back()
.use_physical_device(cur_icd_0.physical_devices[0])
.use_physical_device(cur_icd_0.physical_devices[2]);
cur_icd_0.physical_device_groups.push_back({});
cur_icd_0.physical_device_groups.back().use_physical_device(cur_icd_0.physical_devices[1]);
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_1));
auto& cur_icd_1 = env.get_test_icd(1);
cur_icd_1.set_icd_api_version(VK_API_VERSION_1_1);
cur_icd_1.physical_devices.push_back({"pd3", 0});
FillInRandomDeviceProps(cur_icd_0.physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_CPU, VK_API_VERSION_1_1, 1,
0xBBBB001);
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_1));
auto& cur_icd_2 = env.get_test_icd(2);
cur_icd_2.set_icd_api_version(VK_API_VERSION_1_1);
cur_icd_2.physical_devices.push_back({"pd4", 1});
FillInRandomDeviceProps(cur_icd_0.physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_API_VERSION_1_1,
75, 0xCCCC001);
cur_icd_2.physical_devices.push_back({"pd5", 4});
FillInRandomDeviceProps(cur_icd_0.physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_API_VERSION_1_1,
75, 0xCCCC002);
cur_icd_2.physical_devices.push_back({"pd6", 2});
FillInRandomDeviceProps(cur_icd_0.physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_API_VERSION_1_1,
75, 0xCCCC003);
cur_icd_2.physical_device_groups.push_back({});
cur_icd_2.physical_device_groups.back()
.use_physical_device(cur_icd_2.physical_devices[1])
.use_physical_device(cur_icd_2.physical_devices[2]);
cur_icd_2.physical_device_groups.push_back({});
cur_icd_2.physical_device_groups.back().use_physical_device(cur_icd_2.physical_devices[0]);
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_1));
auto& cur_icd_3 = env.get_test_icd(3);
cur_icd_3.set_icd_api_version(VK_API_VERSION_1_1);
cur_icd_3.physical_devices.push_back({"pd7", 0});
FillInRandomDeviceProps(cur_icd_0.physical_devices.back().properties, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU, VK_API_VERSION_1_1,
6940, 0xDDDD001);
InstWrapper inst(env.vulkan_functions);
inst.create_info.set_api_version(VK_API_VERSION_1_1);
inst.CheckCreate();
auto GetPhysDevProps2 = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2>(
inst.functions->vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2"));
ASSERT_NE(GetPhysDevProps2, nullptr);
const uint32_t max_phys_devs = 8;
uint32_t device_count = max_phys_devs;
std::array<VkPhysicalDevice, max_phys_devs> physical_devices;
ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst, &device_count, physical_devices.data()));
ASSERT_EQ(device_count, max_phys_devs);
const uint32_t max_phys_dev_groups = 8;
uint32_t group_count = max_phys_dev_groups;
std::array<VkPhysicalDeviceGroupProperties, max_phys_dev_groups> physical_device_groups{
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES};
ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDeviceGroups(inst, &group_count, physical_device_groups.data()));
ASSERT_EQ(group_count, max_phys_dev_groups);
uint32_t cur_dev = 0;
for (uint32_t group = 0; group < max_phys_dev_groups; ++group) {
for (uint32_t dev = 0; dev < physical_device_groups[group].physicalDeviceCount; ++dev) {
VkPhysicalDeviceProperties props{};
inst->vkGetPhysicalDeviceProperties(physical_devices[dev], &props);
VkPhysicalDeviceProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
VkPhysicalDevicePCIBusInfoPropertiesEXT pci_bus_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT};
props2.pNext = &pci_bus_info;
GetPhysDevProps2(physical_devices[dev], &props2);
/*
switch (++cur_dev) {
case 0:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
ASSERT_EQ(true, !strcmp("pd3", props.deviceName));
ASSERT_EQ(props.vendorID, 75);
ASSERT_EQ(props.deviceID, 0xCCCC001);
ASSERT_EQ(pci_bus_info.pciBus, 1);
break;
case 1:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
ASSERT_EQ(true, !strcmp("pd4", props.deviceName));
ASSERT_EQ(props.vendorID, 75);
ASSERT_EQ(props.deviceID, 0xCCCC002);
ASSERT_EQ(pci_bus_info.pciBus, 4);
break;
case 2:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
ASSERT_EQ(true, !strcmp("pd0", props.deviceName));
ASSERT_EQ(props.vendorID, 888);
ASSERT_EQ(props.deviceID, 0xAAA001);
ASSERT_EQ(pci_bus_info.pciBus, 7);
break;
case 3:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU);
ASSERT_EQ(true, !strcmp("pd1", props.deviceName));
ASSERT_EQ(props.vendorID, 888);
ASSERT_EQ(props.deviceID, 0xAAA002);
ASSERT_EQ(pci_bus_info.pciBus, 3);
break;
case 4:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU);
ASSERT_EQ(true, !strcmp("pd5", props.deviceName));
ASSERT_EQ(props.vendorID, 6940);
ASSERT_EQ(props.deviceID, 0xDDDD001);
ASSERT_EQ(pci_bus_info.pciBus, 0);
break;
case 5:
ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_CPU);
ASSERT_EQ(true, !strcmp("pd2", props.deviceName));
ASSERT_EQ(props.vendorID, 1);
ASSERT_EQ(props.deviceID, 0xBBBB001);
ASSERT_EQ(pci_bus_info.pciBus, 0);
break;
default:
ASSERT_EQ(false, true);
}
*/
}
}
}
#endif
#endif // __linux__ || __FreeBSD__