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