17ec681f3Smrg/* 27ec681f3Smrg * Copyright © 2017 Google 37ec681f3Smrg * Copyright © 2019 Red Hat 47ec681f3Smrg * 57ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 67ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 77ec681f3Smrg * to deal in the Software without restriction, including without limitation 87ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 97ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 107ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 117ec681f3Smrg * 127ec681f3Smrg * The above copyright notice and this permission notice (including the next 137ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 147ec681f3Smrg * Software. 157ec681f3Smrg * 167ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 177ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 187ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 197ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 207ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 217ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 227ec681f3Smrg * IN THE SOFTWARE. 237ec681f3Smrg */ 247ec681f3Smrg 257ec681f3Smrg/* Rules for device selection. 267ec681f3Smrg * Is there an X or wayland connection open (or DISPLAY set). 277ec681f3Smrg * If no - try and find which device was the boot_vga device. 287ec681f3Smrg * If yes - try and work out which device is the connection primary, 297ec681f3Smrg * DRI_PRIME tagged overrides only work if bus info, =1 will just pick an alternate. 307ec681f3Smrg */ 317ec681f3Smrg 327ec681f3Smrg#include <vulkan/vk_layer.h> 337ec681f3Smrg 347ec681f3Smrg#include <assert.h> 357ec681f3Smrg#include <stdio.h> 367ec681f3Smrg#include <string.h> 377ec681f3Smrg#include <fcntl.h> 387ec681f3Smrg#include <unistd.h> 397ec681f3Smrg 407ec681f3Smrg#include "device_select.h" 417ec681f3Smrg#include "c99_compat.h" 427ec681f3Smrg#include "hash_table.h" 437ec681f3Smrg#include "vk_util.h" 447ec681f3Smrg#include "c11/threads.h" 457ec681f3Smrg 467ec681f3Smrgstruct instance_info { 477ec681f3Smrg PFN_vkDestroyInstance DestroyInstance; 487ec681f3Smrg PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices; 497ec681f3Smrg PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups; 507ec681f3Smrg PFN_vkGetInstanceProcAddr GetInstanceProcAddr; 517ec681f3Smrg PFN_GetPhysicalDeviceProcAddr GetPhysicalDeviceProcAddr; 527ec681f3Smrg PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; 537ec681f3Smrg PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties; 547ec681f3Smrg PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2; 557ec681f3Smrg bool has_pci_bus, has_vulkan11; 567ec681f3Smrg bool has_wayland, has_xcb; 577ec681f3Smrg}; 587ec681f3Smrg 597ec681f3Smrgstatic struct hash_table *device_select_instance_ht = NULL; 607ec681f3Smrgstatic mtx_t device_select_mutex; 617ec681f3Smrg 627ec681f3Smrgstatic once_flag device_select_is_init = ONCE_FLAG_INIT; 637ec681f3Smrg 647ec681f3Smrgstatic void device_select_once_init(void) { 657ec681f3Smrg mtx_init(&device_select_mutex, mtx_plain); 667ec681f3Smrg} 677ec681f3Smrg 687ec681f3Smrgstatic void 697ec681f3Smrgdevice_select_init_instances(void) 707ec681f3Smrg{ 717ec681f3Smrg call_once(&device_select_is_init, device_select_once_init); 727ec681f3Smrg 737ec681f3Smrg mtx_lock(&device_select_mutex); 747ec681f3Smrg if (!device_select_instance_ht) 757ec681f3Smrg device_select_instance_ht = _mesa_hash_table_create(NULL, _mesa_hash_pointer, 767ec681f3Smrg _mesa_key_pointer_equal); 777ec681f3Smrg mtx_unlock(&device_select_mutex); 787ec681f3Smrg} 797ec681f3Smrg 807ec681f3Smrgstatic void 817ec681f3Smrgdevice_select_try_free_ht(void) 827ec681f3Smrg{ 837ec681f3Smrg mtx_lock(&device_select_mutex); 847ec681f3Smrg if (device_select_instance_ht) { 857ec681f3Smrg if (_mesa_hash_table_num_entries(device_select_instance_ht) == 0) { 867ec681f3Smrg _mesa_hash_table_destroy(device_select_instance_ht, NULL); 877ec681f3Smrg device_select_instance_ht = NULL; 887ec681f3Smrg } 897ec681f3Smrg } 907ec681f3Smrg mtx_unlock(&device_select_mutex); 917ec681f3Smrg} 927ec681f3Smrg 937ec681f3Smrgstatic void 947ec681f3Smrgdevice_select_layer_add_instance(VkInstance instance, struct instance_info *info) 957ec681f3Smrg{ 967ec681f3Smrg device_select_init_instances(); 977ec681f3Smrg mtx_lock(&device_select_mutex); 987ec681f3Smrg _mesa_hash_table_insert(device_select_instance_ht, instance, info); 997ec681f3Smrg mtx_unlock(&device_select_mutex); 1007ec681f3Smrg} 1017ec681f3Smrg 1027ec681f3Smrgstatic struct instance_info * 1037ec681f3Smrgdevice_select_layer_get_instance(VkInstance instance) 1047ec681f3Smrg{ 1057ec681f3Smrg struct hash_entry *entry; 1067ec681f3Smrg struct instance_info *info = NULL; 1077ec681f3Smrg mtx_lock(&device_select_mutex); 1087ec681f3Smrg entry = _mesa_hash_table_search(device_select_instance_ht, (void *)instance); 1097ec681f3Smrg if (entry) 1107ec681f3Smrg info = (struct instance_info *)entry->data; 1117ec681f3Smrg mtx_unlock(&device_select_mutex); 1127ec681f3Smrg return info; 1137ec681f3Smrg} 1147ec681f3Smrg 1157ec681f3Smrgstatic void 1167ec681f3Smrgdevice_select_layer_remove_instance(VkInstance instance) 1177ec681f3Smrg{ 1187ec681f3Smrg mtx_lock(&device_select_mutex); 1197ec681f3Smrg _mesa_hash_table_remove_key(device_select_instance_ht, instance); 1207ec681f3Smrg mtx_unlock(&device_select_mutex); 1217ec681f3Smrg device_select_try_free_ht(); 1227ec681f3Smrg} 1237ec681f3Smrg 1247ec681f3Smrgstatic VkResult device_select_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, 1257ec681f3Smrg const VkAllocationCallbacks *pAllocator, 1267ec681f3Smrg VkInstance *pInstance) 1277ec681f3Smrg{ 1287ec681f3Smrg VkLayerInstanceCreateInfo *chain_info; 1297ec681f3Smrg for(chain_info = (VkLayerInstanceCreateInfo*)pCreateInfo->pNext; chain_info; chain_info = (VkLayerInstanceCreateInfo*)chain_info->pNext) 1307ec681f3Smrg if(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && chain_info->function == VK_LAYER_LINK_INFO) 1317ec681f3Smrg break; 1327ec681f3Smrg 1337ec681f3Smrg assert(chain_info->u.pLayerInfo); 1347ec681f3Smrg struct instance_info *info = (struct instance_info *)calloc(1, sizeof(struct instance_info)); 1357ec681f3Smrg 1367ec681f3Smrg info->GetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; 1377ec681f3Smrg PFN_vkCreateInstance fpCreateInstance = 1387ec681f3Smrg (PFN_vkCreateInstance)info->GetInstanceProcAddr(NULL, "vkCreateInstance"); 1397ec681f3Smrg if (fpCreateInstance == NULL) { 1407ec681f3Smrg free(info); 1417ec681f3Smrg return VK_ERROR_INITIALIZATION_FAILED; 1427ec681f3Smrg } 1437ec681f3Smrg 1447ec681f3Smrg chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; 1457ec681f3Smrg 1467ec681f3Smrg VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance); 1477ec681f3Smrg if (result != VK_SUCCESS) { 1487ec681f3Smrg free(info); 1497ec681f3Smrg return result; 1507ec681f3Smrg } 1517ec681f3Smrg 1527ec681f3Smrg for (unsigned i = 0; i < pCreateInfo->enabledExtensionCount; i++) { 1537ec681f3Smrg#ifdef VK_USE_PLATFORM_WAYLAND_KHR 1547ec681f3Smrg if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) 1557ec681f3Smrg info->has_wayland = true; 1567ec681f3Smrg#endif 1577ec681f3Smrg#ifdef VK_USE_PLATFORM_XCB_KHR 1587ec681f3Smrg if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_XCB_SURFACE_EXTENSION_NAME)) 1597ec681f3Smrg info->has_xcb = true; 1607ec681f3Smrg#endif 1617ec681f3Smrg } 1627ec681f3Smrg 1637ec681f3Smrg /* 1647ec681f3Smrg * The loader is currently not able to handle GetPhysicalDeviceProperties2KHR calls in 1657ec681f3Smrg * EnumeratePhysicalDevices when there are other layers present. To avoid mysterious crashes 1667ec681f3Smrg * for users just use only the vulkan version for now. 1677ec681f3Smrg */ 1687ec681f3Smrg info->has_vulkan11 = pCreateInfo->pApplicationInfo && 1697ec681f3Smrg pCreateInfo->pApplicationInfo->apiVersion >= VK_MAKE_VERSION(1, 1, 0); 1707ec681f3Smrg 1717ec681f3Smrg info->GetPhysicalDeviceProcAddr = (PFN_GetPhysicalDeviceProcAddr)info->GetInstanceProcAddr(*pInstance, "vk_layerGetPhysicalDeviceProcAddr"); 1727ec681f3Smrg#define DEVSEL_GET_CB(func) info->func = (PFN_vk##func)info->GetInstanceProcAddr(*pInstance, "vk" #func) 1737ec681f3Smrg DEVSEL_GET_CB(DestroyInstance); 1747ec681f3Smrg DEVSEL_GET_CB(EnumeratePhysicalDevices); 1757ec681f3Smrg DEVSEL_GET_CB(EnumeratePhysicalDeviceGroups); 1767ec681f3Smrg DEVSEL_GET_CB(GetPhysicalDeviceProperties); 1777ec681f3Smrg DEVSEL_GET_CB(EnumerateDeviceExtensionProperties); 1787ec681f3Smrg if (info->has_vulkan11) 1797ec681f3Smrg DEVSEL_GET_CB(GetPhysicalDeviceProperties2); 1807ec681f3Smrg#undef DEVSEL_GET_CB 1817ec681f3Smrg 1827ec681f3Smrg device_select_layer_add_instance(*pInstance, info); 1837ec681f3Smrg 1847ec681f3Smrg return VK_SUCCESS; 1857ec681f3Smrg} 1867ec681f3Smrg 1877ec681f3Smrgstatic void device_select_DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) 1887ec681f3Smrg{ 1897ec681f3Smrg struct instance_info *info = device_select_layer_get_instance(instance); 1907ec681f3Smrg 1917ec681f3Smrg device_select_layer_remove_instance(instance); 1927ec681f3Smrg info->DestroyInstance(instance, pAllocator); 1937ec681f3Smrg free(info); 1947ec681f3Smrg} 1957ec681f3Smrg 1967ec681f3Smrgstatic void get_device_properties(const struct instance_info *info, VkPhysicalDevice device, VkPhysicalDeviceProperties2 *properties) 1977ec681f3Smrg{ 1987ec681f3Smrg info->GetPhysicalDeviceProperties(device, &properties->properties); 1997ec681f3Smrg 2007ec681f3Smrg if (info->GetPhysicalDeviceProperties2 && properties->properties.apiVersion >= VK_API_VERSION_1_1) 2017ec681f3Smrg info->GetPhysicalDeviceProperties2(device, properties); 2027ec681f3Smrg} 2037ec681f3Smrg 2047ec681f3Smrgstatic void print_gpu(const struct instance_info *info, unsigned index, VkPhysicalDevice device) 2057ec681f3Smrg{ 2067ec681f3Smrg const char *type = ""; 2077ec681f3Smrg VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties = (VkPhysicalDevicePCIBusInfoPropertiesEXT) { 2087ec681f3Smrg .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT 2097ec681f3Smrg }; 2107ec681f3Smrg VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){ 2117ec681f3Smrg .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR 2127ec681f3Smrg }; 2137ec681f3Smrg if (info->has_vulkan11 && info->has_pci_bus) 2147ec681f3Smrg properties.pNext = &ext_pci_properties; 2157ec681f3Smrg get_device_properties(info, device, &properties); 2167ec681f3Smrg 2177ec681f3Smrg switch(properties.properties.deviceType) { 2187ec681f3Smrg case VK_PHYSICAL_DEVICE_TYPE_OTHER: 2197ec681f3Smrg default: 2207ec681f3Smrg type = "other"; 2217ec681f3Smrg break; 2227ec681f3Smrg case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: 2237ec681f3Smrg type = "integrated GPU"; 2247ec681f3Smrg break; 2257ec681f3Smrg case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: 2267ec681f3Smrg type = "discrete GPU"; 2277ec681f3Smrg break; 2287ec681f3Smrg case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: 2297ec681f3Smrg type = "virtual GPU"; 2307ec681f3Smrg break; 2317ec681f3Smrg case VK_PHYSICAL_DEVICE_TYPE_CPU: 2327ec681f3Smrg type = "CPU"; 2337ec681f3Smrg break; 2347ec681f3Smrg } 2357ec681f3Smrg fprintf(stderr, " GPU %d: %x:%x \"%s\" %s", index, properties.properties.vendorID, 2367ec681f3Smrg properties.properties.deviceID, properties.properties.deviceName, type); 2377ec681f3Smrg if (info->has_pci_bus) 2387ec681f3Smrg fprintf(stderr, " %04x:%02x:%02x.%x", ext_pci_properties.pciDomain, 2397ec681f3Smrg ext_pci_properties.pciBus, ext_pci_properties.pciDevice, 2407ec681f3Smrg ext_pci_properties.pciFunction); 2417ec681f3Smrg fprintf(stderr, "\n"); 2427ec681f3Smrg} 2437ec681f3Smrg 2447ec681f3Smrgstatic bool fill_drm_device_info(const struct instance_info *info, 2457ec681f3Smrg struct device_pci_info *drm_device, 2467ec681f3Smrg VkPhysicalDevice device) 2477ec681f3Smrg{ 2487ec681f3Smrg VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties = (VkPhysicalDevicePCIBusInfoPropertiesEXT) { 2497ec681f3Smrg .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT 2507ec681f3Smrg }; 2517ec681f3Smrg 2527ec681f3Smrg VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){ 2537ec681f3Smrg .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR 2547ec681f3Smrg }; 2557ec681f3Smrg 2567ec681f3Smrg if (info->has_vulkan11 && info->has_pci_bus) 2577ec681f3Smrg properties.pNext = &ext_pci_properties; 2587ec681f3Smrg get_device_properties(info, device, &properties); 2597ec681f3Smrg 2607ec681f3Smrg drm_device->cpu_device = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; 2617ec681f3Smrg drm_device->dev_info.vendor_id = properties.properties.vendorID; 2627ec681f3Smrg drm_device->dev_info.device_id = properties.properties.deviceID; 2637ec681f3Smrg if (info->has_vulkan11 && info->has_pci_bus) { 2647ec681f3Smrg drm_device->has_bus_info = true; 2657ec681f3Smrg drm_device->bus_info.domain = ext_pci_properties.pciDomain; 2667ec681f3Smrg drm_device->bus_info.bus = ext_pci_properties.pciBus; 2677ec681f3Smrg drm_device->bus_info.dev = ext_pci_properties.pciDevice; 2687ec681f3Smrg drm_device->bus_info.func = ext_pci_properties.pciFunction; 2697ec681f3Smrg } 2707ec681f3Smrg return drm_device->cpu_device; 2717ec681f3Smrg} 2727ec681f3Smrg 2737ec681f3Smrgstatic int device_select_find_explicit_default(struct device_pci_info *pci_infos, 2747ec681f3Smrg uint32_t device_count, 2757ec681f3Smrg const char *selection) 2767ec681f3Smrg{ 2777ec681f3Smrg int default_idx = -1; 2787ec681f3Smrg unsigned vendor_id, device_id; 2797ec681f3Smrg int matched = sscanf(selection, "%x:%x", &vendor_id, &device_id); 2807ec681f3Smrg if (matched != 2) 2817ec681f3Smrg return default_idx; 2827ec681f3Smrg 2837ec681f3Smrg for (unsigned i = 0; i < device_count; ++i) { 2847ec681f3Smrg if (pci_infos[i].dev_info.vendor_id == vendor_id && 2857ec681f3Smrg pci_infos[i].dev_info.device_id == device_id) 2867ec681f3Smrg default_idx = i; 2877ec681f3Smrg } 2887ec681f3Smrg return default_idx; 2897ec681f3Smrg} 2907ec681f3Smrg 2917ec681f3Smrgstatic int device_select_find_dri_prime_tag_default(struct device_pci_info *pci_infos, 2927ec681f3Smrg uint32_t device_count, 2937ec681f3Smrg const char *dri_prime) 2947ec681f3Smrg{ 2957ec681f3Smrg int default_idx = -1; 2967ec681f3Smrg for (unsigned i = 0; i < device_count; ++i) { 2977ec681f3Smrg char *tag = NULL; 2987ec681f3Smrg if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u", 2997ec681f3Smrg pci_infos[i].bus_info.domain, 3007ec681f3Smrg pci_infos[i].bus_info.bus, 3017ec681f3Smrg pci_infos[i].bus_info.dev, 3027ec681f3Smrg pci_infos[i].bus_info.func) >= 0) { 3037ec681f3Smrg if (strcmp(dri_prime, tag)) 3047ec681f3Smrg default_idx = i; 3057ec681f3Smrg } 3067ec681f3Smrg free(tag); 3077ec681f3Smrg } 3087ec681f3Smrg return default_idx; 3097ec681f3Smrg} 3107ec681f3Smrg 3117ec681f3Smrgstatic int device_select_find_boot_vga_default(struct device_pci_info *pci_infos, 3127ec681f3Smrg uint32_t device_count) 3137ec681f3Smrg{ 3147ec681f3Smrg char boot_vga_path[1024]; 3157ec681f3Smrg int default_idx = -1; 3167ec681f3Smrg for (unsigned i = 0; i < device_count; ++i) { 3177ec681f3Smrg /* fallback to probing the pci bus boot_vga device. */ 3187ec681f3Smrg snprintf(boot_vga_path, 1023, "/sys/bus/pci/devices/%04x:%02x:%02x.%x/boot_vga", pci_infos[i].bus_info.domain, 3197ec681f3Smrg pci_infos[i].bus_info.bus, pci_infos[i].bus_info.dev, pci_infos[i].bus_info.func); 3207ec681f3Smrg int fd = open(boot_vga_path, O_RDONLY); 3217ec681f3Smrg if (fd != -1) { 3227ec681f3Smrg uint8_t val; 3237ec681f3Smrg if (read(fd, &val, 1) == 1) { 3247ec681f3Smrg if (val == '1') 3257ec681f3Smrg default_idx = i; 3267ec681f3Smrg } 3277ec681f3Smrg close(fd); 3287ec681f3Smrg } 3297ec681f3Smrg if (default_idx != -1) 3307ec681f3Smrg break; 3317ec681f3Smrg } 3327ec681f3Smrg return default_idx; 3337ec681f3Smrg} 3347ec681f3Smrg 3357ec681f3Smrgstatic int device_select_find_non_cpu(struct device_pci_info *pci_infos, 3367ec681f3Smrg uint32_t device_count) 3377ec681f3Smrg{ 3387ec681f3Smrg int default_idx = -1; 3397ec681f3Smrg 3407ec681f3Smrg /* pick first GPU device */ 3417ec681f3Smrg for (unsigned i = 0; i < device_count; ++i) { 3427ec681f3Smrg if (!pci_infos[i].cpu_device){ 3437ec681f3Smrg default_idx = i; 3447ec681f3Smrg break; 3457ec681f3Smrg } 3467ec681f3Smrg } 3477ec681f3Smrg return default_idx; 3487ec681f3Smrg} 3497ec681f3Smrg 3507ec681f3Smrgstatic int find_non_cpu_skip(struct device_pci_info *pci_infos, 3517ec681f3Smrg uint32_t device_count, 3527ec681f3Smrg int skip_idx) 3537ec681f3Smrg{ 3547ec681f3Smrg for (unsigned i = 0; i < device_count; ++i) { 3557ec681f3Smrg if (i == skip_idx) 3567ec681f3Smrg continue; 3577ec681f3Smrg if (pci_infos[i].cpu_device) 3587ec681f3Smrg continue; 3597ec681f3Smrg return i; 3607ec681f3Smrg } 3617ec681f3Smrg return -1; 3627ec681f3Smrg} 3637ec681f3Smrg 3647ec681f3Smrgstatic uint32_t get_default_device(const struct instance_info *info, 3657ec681f3Smrg const char *selection, 3667ec681f3Smrg uint32_t physical_device_count, 3677ec681f3Smrg VkPhysicalDevice *pPhysicalDevices) 3687ec681f3Smrg{ 3697ec681f3Smrg int default_idx = -1; 3707ec681f3Smrg const char *dri_prime = getenv("DRI_PRIME"); 3717ec681f3Smrg bool dri_prime_is_one = false; 3727ec681f3Smrg int cpu_count = 0; 3737ec681f3Smrg if (dri_prime && !strcmp(dri_prime, "1")) 3747ec681f3Smrg dri_prime_is_one = true; 3757ec681f3Smrg 3767ec681f3Smrg if (dri_prime && !dri_prime_is_one && !info->has_pci_bus) { 3777ec681f3Smrg fprintf(stderr, "device-select: cannot correctly use DRI_PRIME tag\n"); 3787ec681f3Smrg } 3797ec681f3Smrg 3807ec681f3Smrg struct device_pci_info *pci_infos = (struct device_pci_info *)calloc(physical_device_count, sizeof(struct device_pci_info)); 3817ec681f3Smrg if (!pci_infos) 3827ec681f3Smrg return 0; 3837ec681f3Smrg 3847ec681f3Smrg for (unsigned i = 0; i < physical_device_count; ++i) { 3857ec681f3Smrg cpu_count += fill_drm_device_info(info, &pci_infos[i], pPhysicalDevices[i]) ? 1 : 0; 3867ec681f3Smrg } 3877ec681f3Smrg 3887ec681f3Smrg if (selection) 3897ec681f3Smrg default_idx = device_select_find_explicit_default(pci_infos, physical_device_count, selection); 3907ec681f3Smrg if (default_idx == -1 && info->has_pci_bus && dri_prime && !dri_prime_is_one) 3917ec681f3Smrg default_idx = device_select_find_dri_prime_tag_default(pci_infos, physical_device_count, dri_prime); 3927ec681f3Smrg if (default_idx == -1 && info->has_wayland) 3937ec681f3Smrg default_idx = device_select_find_wayland_pci_default(pci_infos, physical_device_count); 3947ec681f3Smrg if (default_idx == -1 && info->has_xcb) 3957ec681f3Smrg default_idx = device_select_find_xcb_pci_default(pci_infos, physical_device_count); 3967ec681f3Smrg if (default_idx == -1 && info->has_pci_bus) 3977ec681f3Smrg default_idx = device_select_find_boot_vga_default(pci_infos, physical_device_count); 3987ec681f3Smrg if (default_idx == -1 && cpu_count) 3997ec681f3Smrg default_idx = device_select_find_non_cpu(pci_infos, physical_device_count); 4007ec681f3Smrg 4017ec681f3Smrg /* DRI_PRIME=1 handling - pick any other device than default. */ 4027ec681f3Smrg if (default_idx != -1 && dri_prime_is_one && physical_device_count > (cpu_count + 1)) { 4037ec681f3Smrg if (default_idx == 0 || default_idx == 1) 4047ec681f3Smrg default_idx = find_non_cpu_skip(pci_infos, physical_device_count, default_idx); 4057ec681f3Smrg } 4067ec681f3Smrg free(pci_infos); 4077ec681f3Smrg return default_idx == -1 ? 0 : default_idx; 4087ec681f3Smrg} 4097ec681f3Smrg 4107ec681f3Smrgstatic VkResult device_select_EnumeratePhysicalDevices(VkInstance instance, 4117ec681f3Smrg uint32_t* pPhysicalDeviceCount, 4127ec681f3Smrg VkPhysicalDevice *pPhysicalDevices) 4137ec681f3Smrg{ 4147ec681f3Smrg struct instance_info *info = device_select_layer_get_instance(instance); 4157ec681f3Smrg uint32_t physical_device_count = 0; 4167ec681f3Smrg uint32_t selected_physical_device_count = 0; 4177ec681f3Smrg const char* selection = getenv("MESA_VK_DEVICE_SELECT"); 4187ec681f3Smrg VkResult result = info->EnumeratePhysicalDevices(instance, &physical_device_count, NULL); 4197ec681f3Smrg VK_OUTARRAY_MAKE(out, pPhysicalDevices, pPhysicalDeviceCount); 4207ec681f3Smrg if (result != VK_SUCCESS) 4217ec681f3Smrg return result; 4227ec681f3Smrg 4237ec681f3Smrg VkPhysicalDevice *physical_devices = (VkPhysicalDevice*)calloc(sizeof(VkPhysicalDevice), physical_device_count); 4247ec681f3Smrg VkPhysicalDevice *selected_physical_devices = (VkPhysicalDevice*)calloc(sizeof(VkPhysicalDevice), 4257ec681f3Smrg physical_device_count); 4267ec681f3Smrg 4277ec681f3Smrg if (!physical_devices || !selected_physical_devices) { 4287ec681f3Smrg result = VK_ERROR_OUT_OF_HOST_MEMORY; 4297ec681f3Smrg goto out; 4307ec681f3Smrg } 4317ec681f3Smrg 4327ec681f3Smrg result = info->EnumeratePhysicalDevices(instance, &physical_device_count, physical_devices); 4337ec681f3Smrg if (result != VK_SUCCESS) 4347ec681f3Smrg goto out; 4357ec681f3Smrg 4367ec681f3Smrg for (unsigned i = 0; i < physical_device_count; i++) { 4377ec681f3Smrg uint32_t count; 4387ec681f3Smrg info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count, NULL); 4397ec681f3Smrg if (count > 0) { 4407ec681f3Smrg VkExtensionProperties *extensions = calloc(count, sizeof(VkExtensionProperties)); 4417ec681f3Smrg if (info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count, extensions) == VK_SUCCESS) { 4427ec681f3Smrg for (unsigned j = 0; j < count; j++) { 4437ec681f3Smrg if (!strcmp(extensions[j].extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME)) 4447ec681f3Smrg info->has_pci_bus = true; 4457ec681f3Smrg } 4467ec681f3Smrg } 4477ec681f3Smrg free(extensions); 4487ec681f3Smrg } 4497ec681f3Smrg } 4507ec681f3Smrg if (selection && strcmp(selection, "list") == 0) { 4517ec681f3Smrg fprintf(stderr, "selectable devices:\n"); 4527ec681f3Smrg for (unsigned i = 0; i < physical_device_count; ++i) 4537ec681f3Smrg print_gpu(info, i, physical_devices[i]); 4547ec681f3Smrg exit(0); 4557ec681f3Smrg } else { 4567ec681f3Smrg unsigned selected_index = get_default_device(info, selection, physical_device_count, physical_devices); 4577ec681f3Smrg selected_physical_device_count = physical_device_count; 4587ec681f3Smrg selected_physical_devices[0] = physical_devices[selected_index]; 4597ec681f3Smrg for (unsigned i = 0; i < physical_device_count - 1; ++i) { 4607ec681f3Smrg unsigned this_idx = i < selected_index ? i : i + 1; 4617ec681f3Smrg selected_physical_devices[i + 1] = physical_devices[this_idx]; 4627ec681f3Smrg } 4637ec681f3Smrg } 4647ec681f3Smrg 4657ec681f3Smrg if (selected_physical_device_count == 0) { 4667ec681f3Smrg fprintf(stderr, "WARNING: selected no devices with MESA_VK_DEVICE_SELECT\n"); 4677ec681f3Smrg } 4687ec681f3Smrg 4697ec681f3Smrg assert(result == VK_SUCCESS); 4707ec681f3Smrg 4717ec681f3Smrg for (unsigned i = 0; i < selected_physical_device_count; i++) { 4727ec681f3Smrg vk_outarray_append(&out, ent) { 4737ec681f3Smrg *ent = selected_physical_devices[i]; 4747ec681f3Smrg } 4757ec681f3Smrg } 4767ec681f3Smrg result = vk_outarray_status(&out); 4777ec681f3Smrg out: 4787ec681f3Smrg free(physical_devices); 4797ec681f3Smrg free(selected_physical_devices); 4807ec681f3Smrg return result; 4817ec681f3Smrg} 4827ec681f3Smrg 4837ec681f3Smrgstatic VkResult device_select_EnumeratePhysicalDeviceGroups(VkInstance instance, 4847ec681f3Smrg uint32_t* pPhysicalDeviceGroupCount, 4857ec681f3Smrg VkPhysicalDeviceGroupProperties *pPhysicalDeviceGroups) 4867ec681f3Smrg{ 4877ec681f3Smrg struct instance_info *info = device_select_layer_get_instance(instance); 4887ec681f3Smrg uint32_t physical_device_group_count = 0; 4897ec681f3Smrg uint32_t selected_physical_device_group_count = 0; 4907ec681f3Smrg VkResult result = info->EnumeratePhysicalDeviceGroups(instance, &physical_device_group_count, NULL); 4917ec681f3Smrg VK_OUTARRAY_MAKE(out, pPhysicalDeviceGroups, pPhysicalDeviceGroupCount); 4927ec681f3Smrg 4937ec681f3Smrg if (result != VK_SUCCESS) 4947ec681f3Smrg return result; 4957ec681f3Smrg 4967ec681f3Smrg VkPhysicalDeviceGroupProperties *physical_device_groups = (VkPhysicalDeviceGroupProperties*)calloc(sizeof(VkPhysicalDeviceGroupProperties), physical_device_group_count); 4977ec681f3Smrg VkPhysicalDeviceGroupProperties *selected_physical_device_groups = (VkPhysicalDeviceGroupProperties*)calloc(sizeof(VkPhysicalDeviceGroupProperties), physical_device_group_count); 4987ec681f3Smrg 4997ec681f3Smrg if (!physical_device_groups || !selected_physical_device_groups) { 5007ec681f3Smrg result = VK_ERROR_OUT_OF_HOST_MEMORY; 5017ec681f3Smrg goto out; 5027ec681f3Smrg } 5037ec681f3Smrg 5047ec681f3Smrg result = info->EnumeratePhysicalDeviceGroups(instance, &physical_device_group_count, physical_device_groups); 5057ec681f3Smrg if (result != VK_SUCCESS) 5067ec681f3Smrg goto out; 5077ec681f3Smrg 5087ec681f3Smrg /* just sort groups with CPU devices to the end? - assume nobody will mix these */ 5097ec681f3Smrg int num_gpu_groups = 0; 5107ec681f3Smrg int num_cpu_groups = 0; 5117ec681f3Smrg selected_physical_device_group_count = physical_device_group_count; 5127ec681f3Smrg for (unsigned i = 0; i < physical_device_group_count; i++) { 5137ec681f3Smrg bool group_has_cpu_device = false; 5147ec681f3Smrg for (unsigned j = 0; j < physical_device_groups[i].physicalDeviceCount; j++) { 5157ec681f3Smrg VkPhysicalDevice physical_device = physical_device_groups[i].physicalDevices[j]; 5167ec681f3Smrg VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){ 5177ec681f3Smrg .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR 5187ec681f3Smrg }; 5197ec681f3Smrg info->GetPhysicalDeviceProperties(physical_device, &properties.properties); 5207ec681f3Smrg group_has_cpu_device = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; 5217ec681f3Smrg } 5227ec681f3Smrg 5237ec681f3Smrg if (group_has_cpu_device) { 5247ec681f3Smrg selected_physical_device_groups[physical_device_group_count - num_cpu_groups - 1] = physical_device_groups[i]; 5257ec681f3Smrg num_cpu_groups++; 5267ec681f3Smrg } else { 5277ec681f3Smrg selected_physical_device_groups[num_gpu_groups] = physical_device_groups[i]; 5287ec681f3Smrg num_gpu_groups++; 5297ec681f3Smrg } 5307ec681f3Smrg } 5317ec681f3Smrg 5327ec681f3Smrg assert(result == VK_SUCCESS); 5337ec681f3Smrg 5347ec681f3Smrg for (unsigned i = 0; i < selected_physical_device_group_count; i++) { 5357ec681f3Smrg vk_outarray_append(&out, ent) { 5367ec681f3Smrg *ent = selected_physical_device_groups[i]; 5377ec681f3Smrg } 5387ec681f3Smrg } 5397ec681f3Smrg result = vk_outarray_status(&out); 5407ec681f3Smrgout: 5417ec681f3Smrg free(physical_device_groups); 5427ec681f3Smrg free(selected_physical_device_groups); 5437ec681f3Smrg return result; 5447ec681f3Smrg} 5457ec681f3Smrg 5467ec681f3Smrgstatic void (*get_pdevice_proc_addr(VkInstance instance, const char* name))() 5477ec681f3Smrg{ 5487ec681f3Smrg struct instance_info *info = device_select_layer_get_instance(instance); 5497ec681f3Smrg return info->GetPhysicalDeviceProcAddr(instance, name); 5507ec681f3Smrg} 5517ec681f3Smrg 5527ec681f3Smrgstatic void (*get_instance_proc_addr(VkInstance instance, const char* name))() 5537ec681f3Smrg{ 5547ec681f3Smrg if (strcmp(name, "vkGetInstanceProcAddr") == 0) 5557ec681f3Smrg return (void(*)())get_instance_proc_addr; 5567ec681f3Smrg if (strcmp(name, "vkCreateInstance") == 0) 5577ec681f3Smrg return (void(*)())device_select_CreateInstance; 5587ec681f3Smrg if (strcmp(name, "vkDestroyInstance") == 0) 5597ec681f3Smrg return (void(*)())device_select_DestroyInstance; 5607ec681f3Smrg if (strcmp(name, "vkEnumeratePhysicalDevices") == 0) 5617ec681f3Smrg return (void(*)())device_select_EnumeratePhysicalDevices; 5627ec681f3Smrg if (strcmp(name, "vkEnumeratePhysicalDeviceGroups") == 0) 5637ec681f3Smrg return (void(*)())device_select_EnumeratePhysicalDeviceGroups; 5647ec681f3Smrg 5657ec681f3Smrg struct instance_info *info = device_select_layer_get_instance(instance); 5667ec681f3Smrg return info->GetInstanceProcAddr(instance, name); 5677ec681f3Smrg} 5687ec681f3Smrg 5697ec681f3SmrgVK_LAYER_EXPORT VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct) 5707ec681f3Smrg{ 5717ec681f3Smrg if (pVersionStruct->loaderLayerInterfaceVersion < 2) 5727ec681f3Smrg return VK_ERROR_INITIALIZATION_FAILED; 5737ec681f3Smrg pVersionStruct->loaderLayerInterfaceVersion = 2; 5747ec681f3Smrg 5757ec681f3Smrg pVersionStruct->pfnGetInstanceProcAddr = get_instance_proc_addr; 5767ec681f3Smrg pVersionStruct->pfnGetPhysicalDeviceProcAddr = get_pdevice_proc_addr; 5777ec681f3Smrg 5787ec681f3Smrg return VK_SUCCESS; 5797ec681f3Smrg} 580