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_DEVICE_SELECT + Linux Only
+ Allows the user to force a particular device to be prioritized above all + other devices in the return order of vkGetPhysicalDevices and + vkGetPhysicalDeviceGroups functions.
+ The value should be ":".
+ NOTE: This not remove devices. + + set VK_LOADER_DEVICE_SELECT=0x10de:0x1f91 + + + + VK_LOADER_DISABLE_SELECT + Linux Only
+ Allows the user to disable the consistent sorting algorithm run in the + loader before returning the set of physical devices to layers.
+ + set VK_LOADER_DISABLE_SELECT=1 + + 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__