2 * Copyright © 2017 Google
3 * Copyright © 2019 Red Hat
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 /* Rules for device selection.
26 * Is there an X or wayland connection open (or DISPLAY set).
27 * If no - try and find which device was the boot_vga device.
28 * If yes - try and work out which device is the connection primary,
29 * DRI_PRIME tagged overrides only work if bus info, =1 will just pick an alternate.
32 #include <vulkan/vk_layer.h>
40 #include "device_select.h"
41 #include "c99_compat.h"
42 #include "hash_table.h"
44 #include "c11/threads.h"
46 struct instance_info
{
47 PFN_vkDestroyInstance DestroyInstance
;
48 PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices
;
49 PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups
;
50 PFN_vkGetInstanceProcAddr GetInstanceProcAddr
;
51 PFN_GetPhysicalDeviceProcAddr GetPhysicalDeviceProcAddr
;
52 PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties
;
53 PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties
;
54 PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR
;
55 bool has_props2
, has_pci_bus
;
56 bool has_wayland
, has_xcb
;
59 static struct hash_table
*device_select_instance_ht
= NULL
;
60 static mtx_t device_select_mutex
;
62 static once_flag device_select_is_init
= ONCE_FLAG_INIT
;
64 static void device_select_once_init(void) {
65 mtx_init(&device_select_mutex
, mtx_plain
);
69 device_select_init_instances(void)
71 call_once(&device_select_is_init
, device_select_once_init
);
73 mtx_lock(&device_select_mutex
);
74 if (!device_select_instance_ht
)
75 device_select_instance_ht
= _mesa_hash_table_create(NULL
, _mesa_hash_pointer
,
76 _mesa_key_pointer_equal
);
77 mtx_unlock(&device_select_mutex
);
81 device_select_try_free_ht(void)
83 mtx_lock(&device_select_mutex
);
84 if (device_select_instance_ht
) {
85 if (_mesa_hash_table_num_entries(device_select_instance_ht
) == 0) {
86 _mesa_hash_table_destroy(device_select_instance_ht
, NULL
);
87 device_select_instance_ht
= NULL
;
90 mtx_unlock(&device_select_mutex
);
94 device_select_layer_add_instance(VkInstance instance
, struct instance_info
*info
)
96 device_select_init_instances();
97 mtx_lock(&device_select_mutex
);
98 _mesa_hash_table_insert(device_select_instance_ht
, instance
, info
);
99 mtx_unlock(&device_select_mutex
);
102 static struct instance_info
*
103 device_select_layer_get_instance(VkInstance instance
)
105 struct hash_entry
*entry
;
106 struct instance_info
*info
= NULL
;
107 mtx_lock(&device_select_mutex
);
108 entry
= _mesa_hash_table_search(device_select_instance_ht
, (void *)instance
);
110 info
= (struct instance_info
*)entry
->data
;
111 mtx_unlock(&device_select_mutex
);
116 device_select_layer_remove_instance(VkInstance instance
)
118 mtx_lock(&device_select_mutex
);
119 _mesa_hash_table_remove_key(device_select_instance_ht
, instance
);
120 mtx_unlock(&device_select_mutex
);
121 device_select_try_free_ht();
124 static VkResult
device_select_CreateInstance(const VkInstanceCreateInfo
*pCreateInfo
,
125 const VkAllocationCallbacks
*pAllocator
,
126 VkInstance
*pInstance
)
128 VkLayerInstanceCreateInfo
*chain_info
;
129 for(chain_info
= (VkLayerInstanceCreateInfo
*)pCreateInfo
->pNext
; chain_info
; chain_info
= (VkLayerInstanceCreateInfo
*)chain_info
->pNext
)
130 if(chain_info
->sType
== VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
&& chain_info
->function
== VK_LAYER_LINK_INFO
)
133 assert(chain_info
->u
.pLayerInfo
);
134 struct instance_info
*info
= (struct instance_info
*)calloc(1, sizeof(struct instance_info
));
136 info
->GetInstanceProcAddr
= chain_info
->u
.pLayerInfo
->pfnNextGetInstanceProcAddr
;
137 PFN_vkCreateInstance fpCreateInstance
=
138 (PFN_vkCreateInstance
)info
->GetInstanceProcAddr(NULL
, "vkCreateInstance");
139 if (fpCreateInstance
== NULL
) {
141 return VK_ERROR_INITIALIZATION_FAILED
;
144 chain_info
->u
.pLayerInfo
= chain_info
->u
.pLayerInfo
->pNext
;
146 VkResult result
= fpCreateInstance(pCreateInfo
, pAllocator
, pInstance
);
147 if (result
!= VK_SUCCESS
) {
152 for (unsigned i
= 0; i
< pCreateInfo
->enabledExtensionCount
; i
++) {
153 if (!strcmp(pCreateInfo
->ppEnabledExtensionNames
[i
], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME
))
154 info
->has_props2
= true;
155 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
156 if (!strcmp(pCreateInfo
->ppEnabledExtensionNames
[i
], VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
))
157 info
->has_wayland
= true;
159 #ifdef VK_USE_PLATFORM_XCB_KHR
160 if (!strcmp(pCreateInfo
->ppEnabledExtensionNames
[i
], VK_KHR_XCB_SURFACE_EXTENSION_NAME
))
161 info
->has_xcb
= true;
165 info
->GetPhysicalDeviceProcAddr
= (PFN_GetPhysicalDeviceProcAddr
)info
->GetInstanceProcAddr(*pInstance
, "vk_layerGetPhysicalDeviceProcAddr");
166 #define DEVSEL_GET_CB(func) info->func = (PFN_vk##func)info->GetInstanceProcAddr(*pInstance, "vk" #func)
167 DEVSEL_GET_CB(DestroyInstance
);
168 DEVSEL_GET_CB(EnumeratePhysicalDevices
);
169 DEVSEL_GET_CB(EnumeratePhysicalDeviceGroups
);
170 DEVSEL_GET_CB(GetPhysicalDeviceProperties
);
171 DEVSEL_GET_CB(EnumerateDeviceExtensionProperties
);
172 if (info
->has_props2
)
173 DEVSEL_GET_CB(GetPhysicalDeviceProperties2KHR
);
176 device_select_layer_add_instance(*pInstance
, info
);
181 static void device_select_DestroyInstance(VkInstance instance
, const VkAllocationCallbacks
* pAllocator
)
183 struct instance_info
*info
= device_select_layer_get_instance(instance
);
185 device_select_layer_remove_instance(instance
);
186 info
->DestroyInstance(instance
, pAllocator
);
191 static void print_gpu(const struct instance_info
*info
, unsigned index
, VkPhysicalDevice device
)
193 const char *type
= "";
194 VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties
= (VkPhysicalDevicePCIBusInfoPropertiesEXT
) {
195 .sType
= VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
197 VkPhysicalDeviceProperties2KHR properties
= (VkPhysicalDeviceProperties2KHR
){
198 .sType
= VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR
200 if (info
->has_props2
&& info
->has_pci_bus
)
201 properties
.pNext
= &ext_pci_properties
;
202 if (info
->GetPhysicalDeviceProperties2KHR
)
203 info
->GetPhysicalDeviceProperties2KHR(device
, &properties
);
205 info
->GetPhysicalDeviceProperties(device
, &properties
.properties
);
207 switch(properties
.properties
.deviceType
) {
208 case VK_PHYSICAL_DEVICE_TYPE_OTHER
:
212 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
:
213 type
= "integrated GPU";
215 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
:
216 type
= "discrete GPU";
218 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU
:
219 type
= "virtual GPU";
221 case VK_PHYSICAL_DEVICE_TYPE_CPU
:
225 fprintf(stderr
, " GPU %d: %x:%x \"%s\" %s", index
, properties
.properties
.vendorID
,
226 properties
.properties
.deviceID
, properties
.properties
.deviceName
, type
);
227 if (info
->has_pci_bus
)
228 fprintf(stderr
, " %04x:%02x:%02x.%x", ext_pci_properties
.pciDomain
,
229 ext_pci_properties
.pciBus
, ext_pci_properties
.pciDevice
,
230 ext_pci_properties
.pciFunction
);
231 fprintf(stderr
, "\n");
234 static bool fill_drm_device_info(const struct instance_info
*info
,
235 struct device_pci_info
*drm_device
,
236 VkPhysicalDevice device
)
238 VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties
= (VkPhysicalDevicePCIBusInfoPropertiesEXT
) {
239 .sType
= VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
242 VkPhysicalDeviceProperties2KHR properties
= (VkPhysicalDeviceProperties2KHR
){
243 .sType
= VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR
246 if (info
->has_props2
&& info
->has_pci_bus
)
247 properties
.pNext
= &ext_pci_properties
;
248 if (info
->GetPhysicalDeviceProperties2KHR
)
249 info
->GetPhysicalDeviceProperties2KHR(device
, &properties
);
251 info
->GetPhysicalDeviceProperties(device
, &properties
.properties
);
253 drm_device
->cpu_device
= properties
.properties
.deviceType
== VK_PHYSICAL_DEVICE_TYPE_CPU
;
254 drm_device
->dev_info
.vendor_id
= properties
.properties
.vendorID
;
255 drm_device
->dev_info
.device_id
= properties
.properties
.deviceID
;
256 if (info
->has_pci_bus
) {
257 drm_device
->has_bus_info
= true;
258 drm_device
->bus_info
.domain
= ext_pci_properties
.pciDomain
;
259 drm_device
->bus_info
.bus
= ext_pci_properties
.pciBus
;
260 drm_device
->bus_info
.dev
= ext_pci_properties
.pciDevice
;
261 drm_device
->bus_info
.func
= ext_pci_properties
.pciFunction
;
263 return drm_device
->cpu_device
;
266 static int device_select_find_explicit_default(struct device_pci_info
*pci_infos
,
267 uint32_t device_count
,
268 const char *selection
)
270 int default_idx
= -1;
271 unsigned vendor_id
, device_id
;
272 int matched
= sscanf(selection
, "%x:%x", &vendor_id
, &device_id
);
276 for (unsigned i
= 0; i
< device_count
; ++i
) {
277 if (pci_infos
[i
].dev_info
.vendor_id
== vendor_id
&&
278 pci_infos
[i
].dev_info
.device_id
== device_id
)
284 static int device_select_find_dri_prime_tag_default(struct device_pci_info
*pci_infos
,
285 uint32_t device_count
,
286 const char *dri_prime
)
288 int default_idx
= -1;
289 for (unsigned i
= 0; i
< device_count
; ++i
) {
291 if (asprintf(&tag
, "pci-%04x_%02x_%02x_%1u",
292 pci_infos
[i
].bus_info
.domain
,
293 pci_infos
[i
].bus_info
.bus
,
294 pci_infos
[i
].bus_info
.dev
,
295 pci_infos
[i
].bus_info
.func
) >= 0) {
296 if (strcmp(dri_prime
, tag
))
304 static int device_select_find_boot_vga_default(struct device_pci_info
*pci_infos
,
305 uint32_t device_count
)
307 char boot_vga_path
[1024];
308 int default_idx
= -1;
309 for (unsigned i
= 0; i
< device_count
; ++i
) {
310 /* fallback to probing the pci bus boot_vga device. */
311 snprintf(boot_vga_path
, 1023, "/sys/bus/pci/devices/%04x:%02x:%02x.%x/boot_vga", pci_infos
[i
].bus_info
.domain
,
312 pci_infos
[i
].bus_info
.bus
, pci_infos
[i
].bus_info
.dev
, pci_infos
[i
].bus_info
.func
);
313 int fd
= open(boot_vga_path
, O_RDONLY
);
316 if (read(fd
, &val
, 1) == 1) {
322 if (default_idx
!= -1)
328 static int device_select_find_non_cpu(struct device_pci_info
*pci_infos
,
329 uint32_t device_count
)
331 int default_idx
= -1;
333 /* pick first GPU device */
334 for (unsigned i
= 0; i
< device_count
; ++i
) {
335 if (!pci_infos
[i
].cpu_device
){
343 static int find_non_cpu_skip(struct device_pci_info
*pci_infos
,
344 uint32_t device_count
,
347 for (unsigned i
= 0; i
< device_count
; ++i
) {
350 if (pci_infos
[i
].cpu_device
)
357 static uint32_t get_default_device(const struct instance_info
*info
,
358 const char *selection
,
359 uint32_t physical_device_count
,
360 VkPhysicalDevice
*pPhysicalDevices
)
362 int default_idx
= -1;
363 const char *dri_prime
= getenv("DRI_PRIME");
364 bool dri_prime_is_one
= false;
366 if (dri_prime
&& !strcmp(dri_prime
, "1"))
367 dri_prime_is_one
= true;
369 if (dri_prime
&& !dri_prime_is_one
&& !info
->has_pci_bus
) {
370 fprintf(stderr
, "device-select: cannot correctly use DRI_PRIME tag\n");
373 struct device_pci_info
*pci_infos
= (struct device_pci_info
*)calloc(physical_device_count
, sizeof(struct device_pci_info
));
377 for (unsigned i
= 0; i
< physical_device_count
; ++i
) {
378 cpu_count
+= fill_drm_device_info(info
, &pci_infos
[i
], pPhysicalDevices
[i
]) ? 1 : 0;
382 default_idx
= device_select_find_explicit_default(pci_infos
, physical_device_count
, selection
);
383 if (default_idx
== -1 && info
->has_pci_bus
&& dri_prime
&& !dri_prime_is_one
)
384 default_idx
= device_select_find_dri_prime_tag_default(pci_infos
, physical_device_count
, dri_prime
);
385 if (default_idx
== -1 && info
->has_wayland
)
386 default_idx
= device_select_find_wayland_pci_default(pci_infos
, physical_device_count
);
387 if (default_idx
== -1 && info
->has_xcb
)
388 default_idx
= device_select_find_xcb_pci_default(pci_infos
, physical_device_count
);
389 if (default_idx
== -1 && info
->has_pci_bus
)
390 default_idx
= device_select_find_boot_vga_default(pci_infos
, physical_device_count
);
391 if (default_idx
== -1 && cpu_count
)
392 default_idx
= device_select_find_non_cpu(pci_infos
, physical_device_count
);
394 /* DRI_PRIME=1 handling - pick any other device than default. */
395 if (default_idx
!= -1 && dri_prime_is_one
&& physical_device_count
> (cpu_count
+ 1)) {
396 if (default_idx
== 0 || default_idx
== 1)
397 default_idx
= find_non_cpu_skip(pci_infos
, physical_device_count
, default_idx
);
400 return default_idx
== -1 ? 0 : default_idx
;
403 static VkResult
device_select_EnumeratePhysicalDevices(VkInstance instance
,
404 uint32_t* pPhysicalDeviceCount
,
405 VkPhysicalDevice
*pPhysicalDevices
)
407 struct instance_info
*info
= device_select_layer_get_instance(instance
);
408 uint32_t physical_device_count
= 0;
409 uint32_t selected_physical_device_count
= 0;
410 const char* selection
= getenv("MESA_VK_DEVICE_SELECT");
411 VkResult result
= info
->EnumeratePhysicalDevices(instance
, &physical_device_count
, NULL
);
412 VK_OUTARRAY_MAKE(out
, pPhysicalDevices
, pPhysicalDeviceCount
);
413 if (result
!= VK_SUCCESS
)
416 VkPhysicalDevice
*physical_devices
= (VkPhysicalDevice
*)calloc(sizeof(VkPhysicalDevice
), physical_device_count
);
417 VkPhysicalDevice
*selected_physical_devices
= (VkPhysicalDevice
*)calloc(sizeof(VkPhysicalDevice
),
418 physical_device_count
);
420 if (!physical_devices
|| !selected_physical_devices
) {
421 result
= VK_ERROR_OUT_OF_HOST_MEMORY
;
425 result
= info
->EnumeratePhysicalDevices(instance
, &physical_device_count
, physical_devices
);
426 if (result
!= VK_SUCCESS
)
429 for (unsigned i
= 0; i
< physical_device_count
; i
++) {
431 info
->EnumerateDeviceExtensionProperties(physical_devices
[i
], NULL
, &count
, NULL
);
433 VkExtensionProperties
*extensions
= calloc(count
, sizeof(VkExtensionProperties
));
434 if (info
->EnumerateDeviceExtensionProperties(physical_devices
[i
], NULL
, &count
, extensions
) == VK_SUCCESS
) {
435 for (unsigned j
= 0; j
< count
; j
++) {
436 if (!strcmp(extensions
[j
].extensionName
, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME
))
437 info
->has_pci_bus
= true;
443 if (selection
&& strcmp(selection
, "list") == 0) {
444 fprintf(stderr
, "selectable devices:\n");
445 for (unsigned i
= 0; i
< physical_device_count
; ++i
)
446 print_gpu(info
, i
, physical_devices
[i
]);
449 unsigned selected_index
= get_default_device(info
, selection
, physical_device_count
, physical_devices
);
450 selected_physical_device_count
= physical_device_count
;
451 selected_physical_devices
[0] = physical_devices
[selected_index
];
452 for (unsigned i
= 0; i
< physical_device_count
- 1; ++i
) {
453 unsigned this_idx
= i
< selected_index
? i
: i
+ 1;
454 selected_physical_devices
[i
+ 1] = physical_devices
[this_idx
];
458 if (selected_physical_device_count
== 0) {
459 fprintf(stderr
, "WARNING: selected no devices with MESA_VK_DEVICE_SELECT\n");
462 assert(result
== VK_SUCCESS
);
464 for (unsigned i
= 0; i
< selected_physical_device_count
; i
++) {
465 vk_outarray_append(&out
, ent
) {
466 *ent
= selected_physical_devices
[i
];
469 result
= vk_outarray_status(&out
);
471 free(physical_devices
);
472 free(selected_physical_devices
);
476 static VkResult
device_select_EnumeratePhysicalDeviceGroups(VkInstance instance
,
477 uint32_t* pPhysicalDeviceGroupCount
,
478 VkPhysicalDeviceGroupProperties
*pPhysicalDeviceGroups
)
480 struct instance_info
*info
= device_select_layer_get_instance(instance
);
481 VkResult result
= info
->EnumeratePhysicalDeviceGroups(instance
, pPhysicalDeviceGroupCount
, pPhysicalDeviceGroups
);
485 static void (*get_pdevice_proc_addr(VkInstance instance
, const char* name
))()
487 struct instance_info
*info
= device_select_layer_get_instance(instance
);
488 return info
->GetPhysicalDeviceProcAddr(instance
, name
);
491 static void (*get_instance_proc_addr(VkInstance instance
, const char* name
))()
493 if (strcmp(name
, "vkCreateInstance") == 0)
494 return (void(*)())device_select_CreateInstance
;
495 if (strcmp(name
, "vkDestroyInstance") == 0)
496 return (void(*)())device_select_DestroyInstance
;
497 if (strcmp(name
, "vkEnumeratePhysicalDevices") == 0)
498 return (void(*)())device_select_EnumeratePhysicalDevices
;
499 if (strcmp(name
, "vkEnumeratePhysicalDeviceGroups") == 0)
500 return (void(*)())device_select_EnumeratePhysicalDeviceGroups
;
502 struct instance_info
*info
= device_select_layer_get_instance(instance
);
503 return info
->GetInstanceProcAddr(instance
, name
);
506 VK_LAYER_EXPORT VkResult
vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface
*pVersionStruct
)
508 if (pVersionStruct
->loaderLayerInterfaceVersion
< 2)
509 return VK_ERROR_INITIALIZATION_FAILED
;
510 pVersionStruct
->loaderLayerInterfaceVersion
= 2;
512 pVersionStruct
->pfnGetInstanceProcAddr
= get_instance_proc_addr
;
513 pVersionStruct
->pfnGetPhysicalDeviceProcAddr
= get_pdevice_proc_addr
;