15db71995Sopenharmony_ci/* 25db71995Sopenharmony_ci * 35db71995Sopenharmony_ci * Copyright (c) 2021-2022 The Khronos Group Inc. 45db71995Sopenharmony_ci * Copyright (c) 2021-2022 Valve Corporation 55db71995Sopenharmony_ci * Copyright (c) 2021-2022 LunarG, Inc. 65db71995Sopenharmony_ci * 75db71995Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 85db71995Sopenharmony_ci * you may not use this file except in compliance with the License. 95db71995Sopenharmony_ci * You may obtain a copy of the License at 105db71995Sopenharmony_ci * 115db71995Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 125db71995Sopenharmony_ci * 135db71995Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 145db71995Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 155db71995Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 165db71995Sopenharmony_ci * See the License for the specific language governing permissions and 175db71995Sopenharmony_ci * limitations under the License. 185db71995Sopenharmony_ci * 195db71995Sopenharmony_ci * Author: Mark Young <marky@lunarg.com> 205db71995Sopenharmony_ci * 215db71995Sopenharmony_ci */ 225db71995Sopenharmony_ci 235db71995Sopenharmony_ci// Non-windows and non-apple only header file, guard it so that accidental 245db71995Sopenharmony_ci// inclusion doesn't cause unknown header include errors 255db71995Sopenharmony_ci#if defined(LOADER_ENABLE_LINUX_SORT) 265db71995Sopenharmony_ci 275db71995Sopenharmony_ci#include <stdio.h> 285db71995Sopenharmony_ci#include <stdlib.h> 295db71995Sopenharmony_ci 305db71995Sopenharmony_ci#include "loader_linux.h" 315db71995Sopenharmony_ci 325db71995Sopenharmony_ci#include "allocation.h" 335db71995Sopenharmony_ci#include "loader_environment.h" 345db71995Sopenharmony_ci#include "loader.h" 355db71995Sopenharmony_ci#include "log.h" 365db71995Sopenharmony_ci 375db71995Sopenharmony_ci// Determine a priority based on device type with the higher value being higher priority. 385db71995Sopenharmony_ciuint32_t determine_priority_type_value(VkPhysicalDeviceType type) { 395db71995Sopenharmony_ci switch (type) { 405db71995Sopenharmony_ci case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: 415db71995Sopenharmony_ci return 10; 425db71995Sopenharmony_ci case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: 435db71995Sopenharmony_ci return 5; 445db71995Sopenharmony_ci case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: 455db71995Sopenharmony_ci return 3; 465db71995Sopenharmony_ci case VK_PHYSICAL_DEVICE_TYPE_OTHER: 475db71995Sopenharmony_ci return 2; 485db71995Sopenharmony_ci case VK_PHYSICAL_DEVICE_TYPE_CPU: 495db71995Sopenharmony_ci return 1; 505db71995Sopenharmony_ci case VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM: // Not really an enum, but throws warning if it's not here 515db71995Sopenharmony_ci break; 525db71995Sopenharmony_ci } 535db71995Sopenharmony_ci return 0; 545db71995Sopenharmony_ci} 555db71995Sopenharmony_ci 565db71995Sopenharmony_ci// Compare the two device types. 575db71995Sopenharmony_ci// This behaves similar to a qsort compare. 585db71995Sopenharmony_ciint32_t device_type_compare(VkPhysicalDeviceType a, VkPhysicalDeviceType b) { 595db71995Sopenharmony_ci uint32_t a_value = determine_priority_type_value(a); 605db71995Sopenharmony_ci uint32_t b_value = determine_priority_type_value(b); 615db71995Sopenharmony_ci if (a_value > b_value) { 625db71995Sopenharmony_ci return -1; 635db71995Sopenharmony_ci } else if (b_value > a_value) { 645db71995Sopenharmony_ci return 1; 655db71995Sopenharmony_ci } 665db71995Sopenharmony_ci return 0; 675db71995Sopenharmony_ci} 685db71995Sopenharmony_ci 695db71995Sopenharmony_ci// Used to compare two devices and determine which one should have priority. The criteria is 705db71995Sopenharmony_ci// simple: 715db71995Sopenharmony_ci// 1) Default device ALWAYS wins 725db71995Sopenharmony_ci// 2) Sort by type 735db71995Sopenharmony_ci// 3) Sort by PCI bus ID 745db71995Sopenharmony_ci// 4) Ties broken by device_ID XOR vendor_ID comparison 755db71995Sopenharmony_ciint32_t compare_devices(const void *a, const void *b) { 765db71995Sopenharmony_ci struct LinuxSortedDeviceInfo *left = (struct LinuxSortedDeviceInfo *)a; 775db71995Sopenharmony_ci struct LinuxSortedDeviceInfo *right = (struct LinuxSortedDeviceInfo *)b; 785db71995Sopenharmony_ci 795db71995Sopenharmony_ci // Default device always gets priority 805db71995Sopenharmony_ci if (left->default_device) { 815db71995Sopenharmony_ci return -1; 825db71995Sopenharmony_ci } else if (right->default_device) { 835db71995Sopenharmony_ci return 1; 845db71995Sopenharmony_ci } 855db71995Sopenharmony_ci 865db71995Sopenharmony_ci // Order by device type next 875db71995Sopenharmony_ci int32_t dev_type_comp = device_type_compare(left->device_type, right->device_type); 885db71995Sopenharmony_ci if (0 != dev_type_comp) { 895db71995Sopenharmony_ci return dev_type_comp; 905db71995Sopenharmony_ci } 915db71995Sopenharmony_ci 925db71995Sopenharmony_ci // Sort by PCI info (prioritize devices that have info over those that don't) 935db71995Sopenharmony_ci if (left->has_pci_bus_info && !right->has_pci_bus_info) { 945db71995Sopenharmony_ci return -1; 955db71995Sopenharmony_ci } else if (!left->has_pci_bus_info && right->has_pci_bus_info) { 965db71995Sopenharmony_ci return 1; 975db71995Sopenharmony_ci } else if (left->has_pci_bus_info && right->has_pci_bus_info) { 985db71995Sopenharmony_ci // Sort low to high PCI domain 995db71995Sopenharmony_ci if (left->pci_domain < right->pci_domain) { 1005db71995Sopenharmony_ci return -1; 1015db71995Sopenharmony_ci } else if (left->pci_domain > right->pci_domain) { 1025db71995Sopenharmony_ci return 1; 1035db71995Sopenharmony_ci } 1045db71995Sopenharmony_ci // Sort low to high PCI bus 1055db71995Sopenharmony_ci if (left->pci_bus < right->pci_bus) { 1065db71995Sopenharmony_ci return -1; 1075db71995Sopenharmony_ci } else if (left->pci_bus > right->pci_bus) { 1085db71995Sopenharmony_ci return 1; 1095db71995Sopenharmony_ci } 1105db71995Sopenharmony_ci // Sort low to high PCI device 1115db71995Sopenharmony_ci if (left->pci_device < right->pci_device) { 1125db71995Sopenharmony_ci return -1; 1135db71995Sopenharmony_ci } else if (left->pci_device > right->pci_device) { 1145db71995Sopenharmony_ci return 1; 1155db71995Sopenharmony_ci } 1165db71995Sopenharmony_ci // Sort low to high PCI function 1175db71995Sopenharmony_ci if (left->pci_function < right->pci_function) { 1185db71995Sopenharmony_ci return -1; 1195db71995Sopenharmony_ci } else if (left->pci_function > right->pci_function) { 1205db71995Sopenharmony_ci return 1; 1215db71995Sopenharmony_ci } 1225db71995Sopenharmony_ci } 1235db71995Sopenharmony_ci 1245db71995Sopenharmony_ci // Somehow we have a tie above, so XOR vendorID and deviceID and compare 1255db71995Sopenharmony_ci uint32_t left_xord_dev_vend = left->device_id ^ left->vendor_id; 1265db71995Sopenharmony_ci uint32_t right_xord_dev_vend = right->device_id ^ right->vendor_id; 1275db71995Sopenharmony_ci if (left_xord_dev_vend < right_xord_dev_vend) { 1285db71995Sopenharmony_ci return -1; 1295db71995Sopenharmony_ci } else if (right_xord_dev_vend < left_xord_dev_vend) { 1305db71995Sopenharmony_ci return 1; 1315db71995Sopenharmony_ci } 1325db71995Sopenharmony_ci return 0; 1335db71995Sopenharmony_ci} 1345db71995Sopenharmony_ci 1355db71995Sopenharmony_ci// Used to compare two device groups and determine which one should have priority. 1365db71995Sopenharmony_ci// NOTE: This assumes that devices in each group have already been sorted. 1375db71995Sopenharmony_ci// The group sort criteria is simple: 1385db71995Sopenharmony_ci// 1) Group with the default device ALWAYS wins 1395db71995Sopenharmony_ci// 2) Group with the best device type for device 0 wins 1405db71995Sopenharmony_ci// 3) Group with best PCI bus ID for device 0 wins 1415db71995Sopenharmony_ci// 4) Ties broken by group device 0 device_ID XOR vendor_ID comparison 1425db71995Sopenharmony_ciint32_t compare_device_groups(const void *a, const void *b) { 1435db71995Sopenharmony_ci struct loader_physical_device_group_term *grp_a = (struct loader_physical_device_group_term *)a; 1445db71995Sopenharmony_ci struct loader_physical_device_group_term *grp_b = (struct loader_physical_device_group_term *)b; 1455db71995Sopenharmony_ci 1465db71995Sopenharmony_ci // Use the first GPU's info from each group to sort the groups by 1475db71995Sopenharmony_ci struct LinuxSortedDeviceInfo *left = &grp_a->internal_device_info[0]; 1485db71995Sopenharmony_ci struct LinuxSortedDeviceInfo *right = &grp_b->internal_device_info[0]; 1495db71995Sopenharmony_ci 1505db71995Sopenharmony_ci // Default device always gets priority 1515db71995Sopenharmony_ci if (left->default_device) { 1525db71995Sopenharmony_ci return -1; 1535db71995Sopenharmony_ci } else if (right->default_device) { 1545db71995Sopenharmony_ci return 1; 1555db71995Sopenharmony_ci } 1565db71995Sopenharmony_ci 1575db71995Sopenharmony_ci // Order by device type next 1585db71995Sopenharmony_ci int32_t dev_type_comp = device_type_compare(left->device_type, right->device_type); 1595db71995Sopenharmony_ci if (0 != dev_type_comp) { 1605db71995Sopenharmony_ci return dev_type_comp; 1615db71995Sopenharmony_ci } 1625db71995Sopenharmony_ci 1635db71995Sopenharmony_ci // Sort by PCI info (prioritize devices that have info over those that don't) 1645db71995Sopenharmony_ci if (left->has_pci_bus_info && !right->has_pci_bus_info) { 1655db71995Sopenharmony_ci return -1; 1665db71995Sopenharmony_ci } else if (!left->has_pci_bus_info && right->has_pci_bus_info) { 1675db71995Sopenharmony_ci return 1; 1685db71995Sopenharmony_ci } else if (left->has_pci_bus_info && right->has_pci_bus_info) { 1695db71995Sopenharmony_ci // Sort low to high PCI domain 1705db71995Sopenharmony_ci if (left->pci_domain < right->pci_domain) { 1715db71995Sopenharmony_ci return -1; 1725db71995Sopenharmony_ci } else if (left->pci_domain > right->pci_domain) { 1735db71995Sopenharmony_ci return 1; 1745db71995Sopenharmony_ci } 1755db71995Sopenharmony_ci // Sort low to high PCI bus 1765db71995Sopenharmony_ci if (left->pci_bus < right->pci_bus) { 1775db71995Sopenharmony_ci return -1; 1785db71995Sopenharmony_ci } else if (left->pci_bus > right->pci_bus) { 1795db71995Sopenharmony_ci return 1; 1805db71995Sopenharmony_ci } 1815db71995Sopenharmony_ci // Sort low to high PCI device 1825db71995Sopenharmony_ci if (left->pci_device < right->pci_device) { 1835db71995Sopenharmony_ci return -1; 1845db71995Sopenharmony_ci } else if (left->pci_device > right->pci_device) { 1855db71995Sopenharmony_ci return 1; 1865db71995Sopenharmony_ci } 1875db71995Sopenharmony_ci // Sort low to high PCI function 1885db71995Sopenharmony_ci if (left->pci_function < right->pci_function) { 1895db71995Sopenharmony_ci return -1; 1905db71995Sopenharmony_ci } else if (left->pci_function > right->pci_function) { 1915db71995Sopenharmony_ci return 1; 1925db71995Sopenharmony_ci } 1935db71995Sopenharmony_ci } 1945db71995Sopenharmony_ci 1955db71995Sopenharmony_ci // Somehow we have a tie above, so XOR vendorID and deviceID and compare 1965db71995Sopenharmony_ci uint32_t left_xord_dev_vend = left->device_id ^ left->vendor_id; 1975db71995Sopenharmony_ci uint32_t right_xord_dev_vend = right->device_id ^ right->vendor_id; 1985db71995Sopenharmony_ci if (left_xord_dev_vend < right_xord_dev_vend) { 1995db71995Sopenharmony_ci return -1; 2005db71995Sopenharmony_ci } else if (right_xord_dev_vend < left_xord_dev_vend) { 2015db71995Sopenharmony_ci return 1; 2025db71995Sopenharmony_ci } 2035db71995Sopenharmony_ci return 0; 2045db71995Sopenharmony_ci} 2055db71995Sopenharmony_ci 2065db71995Sopenharmony_ci// Search for the default device using the loader environment variable. 2075db71995Sopenharmony_civoid linux_env_var_default_device(struct loader_instance *inst, uint32_t device_count, 2085db71995Sopenharmony_ci struct LinuxSortedDeviceInfo *sorted_device_info) { 2095db71995Sopenharmony_ci char *selection = loader_getenv("VK_LOADER_DEVICE_SELECT", inst); 2105db71995Sopenharmony_ci if (NULL != selection) { 2115db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_DEBUG_BIT | VULKAN_LOADER_DRIVER_BIT, 0, 2125db71995Sopenharmony_ci "linux_env_var_default_device: Found \'VK_LOADER_DEVICE_SELECT\' set to %s", selection); 2135db71995Sopenharmony_ci 2145db71995Sopenharmony_ci // The environment variable exists, so grab the vendor ID and device ID of the 2155db71995Sopenharmony_ci // selected default device 2165db71995Sopenharmony_ci unsigned vendor_id, device_id; 2175db71995Sopenharmony_ci int32_t matched = sscanf(selection, "%x:%x", &vendor_id, &device_id); 2185db71995Sopenharmony_ci if (matched == 2) { 2195db71995Sopenharmony_ci for (int32_t i = 0; i < (int32_t)device_count; ++i) { 2205db71995Sopenharmony_ci if (sorted_device_info[i].vendor_id == vendor_id && sorted_device_info[i].device_id == device_id) { 2215db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, 2225db71995Sopenharmony_ci "linux_env_var_default_device: Found default at index %u \'%s\'", i, 2235db71995Sopenharmony_ci sorted_device_info[i].device_name); 2245db71995Sopenharmony_ci sorted_device_info[i].default_device = true; 2255db71995Sopenharmony_ci break; 2265db71995Sopenharmony_ci } 2275db71995Sopenharmony_ci } 2285db71995Sopenharmony_ci } 2295db71995Sopenharmony_ci 2305db71995Sopenharmony_ci loader_free_getenv(selection, inst); 2315db71995Sopenharmony_ci } 2325db71995Sopenharmony_ci} 2335db71995Sopenharmony_ci 2345db71995Sopenharmony_ci// This function allocates an array in sorted_devices which must be freed by the caller if not null 2355db71995Sopenharmony_ciVkResult linux_read_sorted_physical_devices(struct loader_instance *inst, uint32_t icd_count, 2365db71995Sopenharmony_ci struct loader_icd_physical_devices *icd_devices, uint32_t phys_dev_count, 2375db71995Sopenharmony_ci struct loader_physical_device_term **sorted_device_term) { 2385db71995Sopenharmony_ci VkResult res = VK_SUCCESS; 2395db71995Sopenharmony_ci bool app_is_vulkan_1_1 = loader_check_version_meets_required(LOADER_VERSION_1_1_0, inst->app_api_version); 2405db71995Sopenharmony_ci 2415db71995Sopenharmony_ci struct LinuxSortedDeviceInfo *sorted_device_info = loader_instance_heap_calloc( 2425db71995Sopenharmony_ci inst, phys_dev_count * sizeof(struct LinuxSortedDeviceInfo), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); 2435db71995Sopenharmony_ci if (NULL == sorted_device_info) { 2445db71995Sopenharmony_ci res = VK_ERROR_OUT_OF_HOST_MEMORY; 2455db71995Sopenharmony_ci goto out; 2465db71995Sopenharmony_ci } 2475db71995Sopenharmony_ci 2485db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "linux_read_sorted_physical_devices:"); 2495db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " Original order:"); 2505db71995Sopenharmony_ci 2515db71995Sopenharmony_ci // Grab all the necessary info we can about each device 2525db71995Sopenharmony_ci uint32_t index = 0; 2535db71995Sopenharmony_ci for (uint32_t icd_idx = 0; icd_idx < icd_count; ++icd_idx) { 2545db71995Sopenharmony_ci for (uint32_t phys_dev = 0; phys_dev < icd_devices[icd_idx].device_count; ++phys_dev) { 2555db71995Sopenharmony_ci struct loader_icd_term *icd_term = icd_devices[icd_idx].icd_term; 2565db71995Sopenharmony_ci VkPhysicalDeviceProperties dev_props = {}; 2575db71995Sopenharmony_ci 2585db71995Sopenharmony_ci sorted_device_info[index].physical_device = icd_devices[icd_idx].physical_devices[phys_dev]; 2595db71995Sopenharmony_ci sorted_device_info[index].icd_index = icd_idx; 2605db71995Sopenharmony_ci sorted_device_info[index].icd_term = icd_term; 2615db71995Sopenharmony_ci sorted_device_info[index].has_pci_bus_info = false; 2625db71995Sopenharmony_ci 2635db71995Sopenharmony_ci icd_term->dispatch.GetPhysicalDeviceProperties(sorted_device_info[index].physical_device, &dev_props); 2645db71995Sopenharmony_ci sorted_device_info[index].device_type = dev_props.deviceType; 2655db71995Sopenharmony_ci strncpy(sorted_device_info[index].device_name, dev_props.deviceName, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE); 2665db71995Sopenharmony_ci sorted_device_info[index].vendor_id = dev_props.vendorID; 2675db71995Sopenharmony_ci sorted_device_info[index].device_id = dev_props.deviceID; 2685db71995Sopenharmony_ci 2695db71995Sopenharmony_ci bool device_is_1_1_capable = 2705db71995Sopenharmony_ci loader_check_version_meets_required(LOADER_VERSION_1_1_0, loader_make_version(dev_props.apiVersion)); 2715db71995Sopenharmony_ci if (!sorted_device_info[index].has_pci_bus_info) { 2725db71995Sopenharmony_ci uint32_t ext_count = 0; 2735db71995Sopenharmony_ci icd_term->dispatch.EnumerateDeviceExtensionProperties(sorted_device_info[index].physical_device, NULL, &ext_count, 2745db71995Sopenharmony_ci NULL); 2755db71995Sopenharmony_ci if (ext_count > 0) { 2765db71995Sopenharmony_ci VkExtensionProperties *ext_props = 2775db71995Sopenharmony_ci (VkExtensionProperties *)loader_stack_alloc(sizeof(VkExtensionProperties) * ext_count); 2785db71995Sopenharmony_ci if (NULL == ext_props) { 2795db71995Sopenharmony_ci res = VK_ERROR_OUT_OF_HOST_MEMORY; 2805db71995Sopenharmony_ci goto out; 2815db71995Sopenharmony_ci } 2825db71995Sopenharmony_ci icd_term->dispatch.EnumerateDeviceExtensionProperties(sorted_device_info[index].physical_device, NULL, 2835db71995Sopenharmony_ci &ext_count, ext_props); 2845db71995Sopenharmony_ci for (uint32_t ext = 0; ext < ext_count; ++ext) { 2855db71995Sopenharmony_ci if (!strcmp(ext_props[ext].extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME)) { 2865db71995Sopenharmony_ci sorted_device_info[index].has_pci_bus_info = true; 2875db71995Sopenharmony_ci break; 2885db71995Sopenharmony_ci } 2895db71995Sopenharmony_ci } 2905db71995Sopenharmony_ci } 2915db71995Sopenharmony_ci } 2925db71995Sopenharmony_ci 2935db71995Sopenharmony_ci if (sorted_device_info[index].has_pci_bus_info) { 2945db71995Sopenharmony_ci VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = (VkPhysicalDevicePCIBusInfoPropertiesEXT){ 2955db71995Sopenharmony_ci .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT}; 2965db71995Sopenharmony_ci VkPhysicalDeviceProperties2 dev_props2 = (VkPhysicalDeviceProperties2){ 2975db71995Sopenharmony_ci .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = (VkBaseInStructure *)&pci_props}; 2985db71995Sopenharmony_ci 2995db71995Sopenharmony_ci PFN_vkGetPhysicalDeviceProperties2 GetPhysDevProps2 = NULL; 3005db71995Sopenharmony_ci if (app_is_vulkan_1_1 && device_is_1_1_capable) { 3015db71995Sopenharmony_ci GetPhysDevProps2 = icd_term->dispatch.GetPhysicalDeviceProperties2; 3025db71995Sopenharmony_ci } else { 3035db71995Sopenharmony_ci GetPhysDevProps2 = (PFN_vkGetPhysicalDeviceProperties2)icd_term->dispatch.GetPhysicalDeviceProperties2KHR; 3045db71995Sopenharmony_ci } 3055db71995Sopenharmony_ci if (NULL != GetPhysDevProps2) { 3065db71995Sopenharmony_ci GetPhysDevProps2(sorted_device_info[index].physical_device, &dev_props2); 3075db71995Sopenharmony_ci sorted_device_info[index].pci_domain = pci_props.pciDomain; 3085db71995Sopenharmony_ci sorted_device_info[index].pci_bus = pci_props.pciBus; 3095db71995Sopenharmony_ci sorted_device_info[index].pci_device = pci_props.pciDevice; 3105db71995Sopenharmony_ci sorted_device_info[index].pci_function = pci_props.pciFunction; 3115db71995Sopenharmony_ci } else { 3125db71995Sopenharmony_ci sorted_device_info[index].has_pci_bus_info = false; 3135db71995Sopenharmony_ci } 3145db71995Sopenharmony_ci } 3155db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s", index, 3165db71995Sopenharmony_ci sorted_device_info[index].device_name); 3175db71995Sopenharmony_ci index++; 3185db71995Sopenharmony_ci } 3195db71995Sopenharmony_ci } 3205db71995Sopenharmony_ci 3215db71995Sopenharmony_ci // Select default device if set in the environment variable 3225db71995Sopenharmony_ci linux_env_var_default_device(inst, phys_dev_count, sorted_device_info); 3235db71995Sopenharmony_ci 3245db71995Sopenharmony_ci // Sort devices by PCI info 3255db71995Sopenharmony_ci qsort(sorted_device_info, phys_dev_count, sizeof(struct LinuxSortedDeviceInfo), compare_devices); 3265db71995Sopenharmony_ci 3275db71995Sopenharmony_ci // If we have a selected index, add that first. 3285db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " Sorted order:"); 3295db71995Sopenharmony_ci 3305db71995Sopenharmony_ci // Add all others after (they've already been sorted) 3315db71995Sopenharmony_ci for (uint32_t dev = 0; dev < phys_dev_count; ++dev) { 3325db71995Sopenharmony_ci sorted_device_term[dev]->this_icd_term = sorted_device_info[dev].icd_term; 3335db71995Sopenharmony_ci sorted_device_term[dev]->icd_index = sorted_device_info[dev].icd_index; 3345db71995Sopenharmony_ci sorted_device_term[dev]->phys_dev = sorted_device_info[dev].physical_device; 3355db71995Sopenharmony_ci loader_set_dispatch((void *)sorted_device_term[dev], inst->disp); 3365db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s %s", dev, 3375db71995Sopenharmony_ci sorted_device_info[dev].device_name, (sorted_device_info[dev].default_device ? "[default]" : "")); 3385db71995Sopenharmony_ci } 3395db71995Sopenharmony_ci 3405db71995Sopenharmony_ciout: 3415db71995Sopenharmony_ci loader_instance_heap_free(inst, sorted_device_info); 3425db71995Sopenharmony_ci 3435db71995Sopenharmony_ci return res; 3445db71995Sopenharmony_ci} 3455db71995Sopenharmony_ci 3465db71995Sopenharmony_ci// This function sorts an array of physical device groups 3475db71995Sopenharmony_ciVkResult linux_sort_physical_device_groups(struct loader_instance *inst, uint32_t group_count, 3485db71995Sopenharmony_ci struct loader_physical_device_group_term *sorted_group_term) { 3495db71995Sopenharmony_ci VkResult res = VK_SUCCESS; 3505db71995Sopenharmony_ci bool app_is_vulkan_1_1 = loader_check_version_meets_required(LOADER_VERSION_1_1_0, inst->app_api_version); 3515db71995Sopenharmony_ci 3525db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "linux_sort_physical_device_groups: Original order:"); 3535db71995Sopenharmony_ci 3545db71995Sopenharmony_ci for (uint32_t group = 0; group < group_count; ++group) { 3555db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " Group %u", group); 3565db71995Sopenharmony_ci 3575db71995Sopenharmony_ci struct loader_icd_term *icd_term = sorted_group_term[group].this_icd_term; 3585db71995Sopenharmony_ci for (uint32_t gpu = 0; gpu < sorted_group_term[group].group_props.physicalDeviceCount; ++gpu) { 3595db71995Sopenharmony_ci VkPhysicalDeviceProperties dev_props = {}; 3605db71995Sopenharmony_ci 3615db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].physical_device = 3625db71995Sopenharmony_ci sorted_group_term[group].group_props.physicalDevices[gpu]; 3635db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info = false; 3645db71995Sopenharmony_ci 3655db71995Sopenharmony_ci icd_term->dispatch.GetPhysicalDeviceProperties(sorted_group_term[group].internal_device_info[gpu].physical_device, 3665db71995Sopenharmony_ci &dev_props); 3675db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].device_type = dev_props.deviceType; 3685db71995Sopenharmony_ci strncpy(sorted_group_term[group].internal_device_info[gpu].device_name, dev_props.deviceName, 3695db71995Sopenharmony_ci VK_MAX_PHYSICAL_DEVICE_NAME_SIZE); 3705db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].vendor_id = dev_props.vendorID; 3715db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].device_id = dev_props.deviceID; 3725db71995Sopenharmony_ci 3735db71995Sopenharmony_ci bool device_is_1_1_capable = 3745db71995Sopenharmony_ci loader_check_version_meets_required(LOADER_VERSION_1_1_0, loader_make_version(dev_props.apiVersion)); 3755db71995Sopenharmony_ci if (!sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info) { 3765db71995Sopenharmony_ci uint32_t ext_count; 3775db71995Sopenharmony_ci icd_term->dispatch.EnumerateDeviceExtensionProperties( 3785db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].physical_device, NULL, &ext_count, NULL); 3795db71995Sopenharmony_ci if (ext_count > 0) { 3805db71995Sopenharmony_ci VkExtensionProperties *ext_props = 3815db71995Sopenharmony_ci (VkExtensionProperties *)loader_stack_alloc(sizeof(VkExtensionProperties) * ext_count); 3825db71995Sopenharmony_ci if (NULL == ext_props) { 3835db71995Sopenharmony_ci return VK_ERROR_OUT_OF_HOST_MEMORY; 3845db71995Sopenharmony_ci } 3855db71995Sopenharmony_ci icd_term->dispatch.EnumerateDeviceExtensionProperties( 3865db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].physical_device, NULL, &ext_count, ext_props); 3875db71995Sopenharmony_ci for (uint32_t ext = 0; ext < ext_count; ++ext) { 3885db71995Sopenharmony_ci if (!strcmp(ext_props[ext].extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME)) { 3895db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info = true; 3905db71995Sopenharmony_ci break; 3915db71995Sopenharmony_ci } 3925db71995Sopenharmony_ci } 3935db71995Sopenharmony_ci } 3945db71995Sopenharmony_ci } 3955db71995Sopenharmony_ci 3965db71995Sopenharmony_ci if (sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info) { 3975db71995Sopenharmony_ci VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = (VkPhysicalDevicePCIBusInfoPropertiesEXT){ 3985db71995Sopenharmony_ci .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT}; 3995db71995Sopenharmony_ci VkPhysicalDeviceProperties2 dev_props2 = (VkPhysicalDeviceProperties2){ 4005db71995Sopenharmony_ci .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = (VkBaseInStructure *)&pci_props}; 4015db71995Sopenharmony_ci 4025db71995Sopenharmony_ci PFN_vkGetPhysicalDeviceProperties2 GetPhysDevProps2 = NULL; 4035db71995Sopenharmony_ci if (app_is_vulkan_1_1 && device_is_1_1_capable) { 4045db71995Sopenharmony_ci GetPhysDevProps2 = icd_term->dispatch.GetPhysicalDeviceProperties2; 4055db71995Sopenharmony_ci } else { 4065db71995Sopenharmony_ci GetPhysDevProps2 = (PFN_vkGetPhysicalDeviceProperties2)icd_term->dispatch.GetPhysicalDeviceProperties2KHR; 4075db71995Sopenharmony_ci } 4085db71995Sopenharmony_ci if (NULL != GetPhysDevProps2) { 4095db71995Sopenharmony_ci GetPhysDevProps2(sorted_group_term[group].internal_device_info[gpu].physical_device, &dev_props2); 4105db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].pci_domain = pci_props.pciDomain; 4115db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].pci_bus = pci_props.pciBus; 4125db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].pci_device = pci_props.pciDevice; 4135db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].pci_function = pci_props.pciFunction; 4145db71995Sopenharmony_ci } else { 4155db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info = false; 4165db71995Sopenharmony_ci } 4175db71995Sopenharmony_ci } 4185db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s", gpu, 4195db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].device_name); 4205db71995Sopenharmony_ci } 4215db71995Sopenharmony_ci 4225db71995Sopenharmony_ci // Select default device if set in the environment variable 4235db71995Sopenharmony_ci linux_env_var_default_device(inst, sorted_group_term[group].group_props.physicalDeviceCount, 4245db71995Sopenharmony_ci sorted_group_term[group].internal_device_info); 4255db71995Sopenharmony_ci 4265db71995Sopenharmony_ci // Sort GPUs in each group 4275db71995Sopenharmony_ci qsort(sorted_group_term[group].internal_device_info, sorted_group_term[group].group_props.physicalDeviceCount, 4285db71995Sopenharmony_ci sizeof(struct LinuxSortedDeviceInfo), compare_devices); 4295db71995Sopenharmony_ci 4305db71995Sopenharmony_ci // Match the externally used physical device list with the sorted physical device list for this group. 4315db71995Sopenharmony_ci for (uint32_t dev = 0; dev < sorted_group_term[group].group_props.physicalDeviceCount; ++dev) { 4325db71995Sopenharmony_ci sorted_group_term[group].group_props.physicalDevices[dev] = 4335db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[dev].physical_device; 4345db71995Sopenharmony_ci } 4355db71995Sopenharmony_ci } 4365db71995Sopenharmony_ci 4375db71995Sopenharmony_ci // Sort device groups by PCI info 4385db71995Sopenharmony_ci qsort(sorted_group_term, group_count, sizeof(struct loader_physical_device_group_term), compare_device_groups); 4395db71995Sopenharmony_ci 4405db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "linux_sort_physical_device_groups: Sorted order:"); 4415db71995Sopenharmony_ci for (uint32_t group = 0; group < group_count; ++group) { 4425db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " Group %u", group); 4435db71995Sopenharmony_ci for (uint32_t gpu = 0; gpu < sorted_group_term[group].group_props.physicalDeviceCount; ++gpu) { 4445db71995Sopenharmony_ci loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s %p %s", gpu, 4455db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].device_name, 4465db71995Sopenharmony_ci sorted_group_term[group].internal_device_info[gpu].physical_device, 4475db71995Sopenharmony_ci (sorted_group_term[group].internal_device_info[gpu].default_device ? "[default]" : "")); 4485db71995Sopenharmony_ci } 4495db71995Sopenharmony_ci } 4505db71995Sopenharmony_ci 4515db71995Sopenharmony_ci return res; 4525db71995Sopenharmony_ci} 4535db71995Sopenharmony_ci 4545db71995Sopenharmony_ci#endif // LOADER_ENABLE_LINUX_SORT 455