diff --git a/BUILD.gn b/BUILD.gn
index 0a4194c2..29d5dead 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -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" ]
}
diff --git a/docs/LoaderApplicationInterface.md b/docs/LoaderApplicationInterface.md
index c7d6cb24..6ef33993 100644
--- a/docs/LoaderApplicationInterface.md
+++ b/docs/LoaderApplicationInterface.md
@@ -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)
diff --git a/docs/LoaderInterfaceArchitecture.md b/docs/LoaderInterfaceArchitecture.md
index f20a46ee..31f603ca 100644
--- a/docs/LoaderInterfaceArchitecture.md
+++ b/docs/LoaderInterfaceArchitecture.md
@@ -537,6 +537,27 @@ discovery.
<path_a>;<path_b>
+
VK_LOADER_DISABLE_INST_EXT_FILTER |
Disable the filtering out of instance extensions that the loader doesn't
diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt
index 0ed50e21..8c814f22 100644
--- a/loader/CMakeLists.txt
+++ b/loader/CMakeLists.txt
@@ -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)
diff --git a/loader/loader.c b/loader/loader.c
index 7d2ff2d1..4633e5b4 100644
--- a/loader/loader.c
+++ b/loader/loader.c
@@ -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;
diff --git a/loader/loader_common.h b/loader/loader_common.h
index 14921a74..bd75e106 100644
--- a/loader/loader_common.h
+++ b/loader/loader_common.h
@@ -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;
};
diff --git a/loader/loader_linux.c b/loader/loader_linux.c
new file mode 100644
index 00000000..86fcae23
--- /dev/null
+++ b/loader/loader_linux.c
@@ -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
+ *
+ */
+
+// 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
+#include
+
+#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
diff --git a/loader/loader_linux.h b/loader/loader_linux.h
new file mode 100644
index 00000000..5546cbf7
--- /dev/null
+++ b/loader/loader_linux.h
@@ -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
+ *
+ */
+
+#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
\ No newline at end of file
diff --git a/tests/framework/icd/physical_device.h b/tests/framework/icd/physical_device.h
index eeeff0e7..243674ac 100644
--- a/tests/framework/icd/physical_device.h
+++ b/tests/framework/icd/physical_device.h
@@ -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 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)
diff --git a/tests/framework/icd/test_icd.cpp b/tests/framework/icd/test_icd.cpp
index 2559f262..a9927174 100644
--- a/tests/framework/icd/test_icd.cpp
+++ b/tests/framework/icd/test_icd.cpp
@@ -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(pProperties->pNext);
+ while (pNext) {
+ if (pNext->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT) {
+ VkPhysicalDevicePCIBusInfoPropertiesEXT* bus_info =
+ reinterpret_cast(pNext);
+ bus_info->pciBus = phys_dev.pci_bus;
+ }
+ pNext = reinterpret_cast(const_cast(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);
}
diff --git a/tests/loader_handle_validation_tests.cpp b/tests/loader_handle_validation_tests.cpp
index b6aacbbc..89dd03ee 100644
--- a/tests/loader_handle_validation_tests.cpp
+++ b/tests/loader_handle_validation_tests.cpp
@@ -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
diff --git a/tests/loader_regression_tests.cpp b/tests/loader_regression_tests.cpp
index e04e4503..b5251151 100644
--- a/tests/loader_regression_tests.cpp
+++ b/tests/loader_regression_tests.cpp
@@ -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(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(
+ 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 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 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(
+ 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 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 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__
|