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