vulkan/device_select: don't pick a cpu driver as the default
[mesa.git] / src / vulkan / device-select-layer / device_select_layer.c
1 /*
2 * Copyright © 2017 Google
3 * Copyright © 2019 Red Hat
4 *
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:
11 *
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
14 * Software.
15 *
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
22 * IN THE SOFTWARE.
23 */
24
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.
30 */
31
32 #include <vulkan/vk_layer.h>
33
34 #include <assert.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39
40 #include "device_select.h"
41 #include "c99_compat.h"
42 #include "hash_table.h"
43 #include "vk_util.h"
44 #include "c11/threads.h"
45
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;
57 };
58
59 static struct hash_table *device_select_instance_ht = NULL;
60 static mtx_t device_select_mutex;
61
62 static once_flag device_select_is_init = ONCE_FLAG_INIT;
63
64 static void device_select_once_init(void) {
65 mtx_init(&device_select_mutex, mtx_plain);
66 }
67
68 static void
69 device_select_init_instances(void)
70 {
71 call_once(&device_select_is_init, device_select_once_init);
72
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);
78 }
79
80 static void
81 device_select_try_free_ht(void)
82 {
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;
88 }
89 }
90 mtx_unlock(&device_select_mutex);
91 }
92
93 static void
94 device_select_layer_add_instance(VkInstance instance, struct instance_info *info)
95 {
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);
100 }
101
102 static struct instance_info *
103 device_select_layer_get_instance(VkInstance instance)
104 {
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);
109 if (entry)
110 info = (struct instance_info *)entry->data;
111 mtx_unlock(&device_select_mutex);
112 return info;
113 }
114
115 static void
116 device_select_layer_remove_instance(VkInstance instance)
117 {
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();
122 }
123
124 static VkResult device_select_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
125 const VkAllocationCallbacks *pAllocator,
126 VkInstance *pInstance)
127 {
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)
131 break;
132
133 assert(chain_info->u.pLayerInfo);
134 struct instance_info *info = (struct instance_info *)calloc(1, sizeof(struct instance_info));
135
136 info->GetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
137 PFN_vkCreateInstance fpCreateInstance =
138 (PFN_vkCreateInstance)info->GetInstanceProcAddr(NULL, "vkCreateInstance");
139 if (fpCreateInstance == NULL) {
140 free(info);
141 return VK_ERROR_INITIALIZATION_FAILED;
142 }
143
144 chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
145
146 VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
147 if (result != VK_SUCCESS) {
148 free(info);
149 return result;
150 }
151
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;
158 #endif
159 #ifdef VK_USE_PLATFORM_XCB_KHR
160 if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_XCB_SURFACE_EXTENSION_NAME))
161 info->has_xcb = true;
162 #endif
163 }
164
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);
174 #undef DEVSEL_GET_CB
175
176 device_select_layer_add_instance(*pInstance, info);
177
178 return VK_SUCCESS;
179 }
180
181 static void device_select_DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator)
182 {
183 struct instance_info *info = device_select_layer_get_instance(instance);
184
185 device_select_layer_remove_instance(instance);
186 info->DestroyInstance(instance, pAllocator);
187 free(info);
188 }
189
190
191 static void print_gpu(const struct instance_info *info, unsigned index, VkPhysicalDevice device)
192 {
193 const char *type = "";
194 VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties = (VkPhysicalDevicePCIBusInfoPropertiesEXT) {
195 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
196 };
197 VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){
198 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR
199 };
200 if (info->has_props2 && info->has_pci_bus)
201 properties.pNext = &ext_pci_properties;
202 if (info->GetPhysicalDeviceProperties2KHR)
203 info->GetPhysicalDeviceProperties2KHR(device, &properties);
204 else
205 info->GetPhysicalDeviceProperties(device, &properties.properties);
206
207 switch(properties.properties.deviceType) {
208 case VK_PHYSICAL_DEVICE_TYPE_OTHER:
209 default:
210 type = "other";
211 break;
212 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
213 type = "integrated GPU";
214 break;
215 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
216 type = "discrete GPU";
217 break;
218 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
219 type = "virtual GPU";
220 break;
221 case VK_PHYSICAL_DEVICE_TYPE_CPU:
222 type = "CPU";
223 break;
224 }
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");
232 }
233
234 static bool fill_drm_device_info(const struct instance_info *info,
235 struct device_pci_info *drm_device,
236 VkPhysicalDevice device)
237 {
238 VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties = (VkPhysicalDevicePCIBusInfoPropertiesEXT) {
239 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
240 };
241
242 VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){
243 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR
244 };
245
246 if (info->has_props2 && info->has_pci_bus)
247 properties.pNext = &ext_pci_properties;
248 if (info->GetPhysicalDeviceProperties2KHR)
249 info->GetPhysicalDeviceProperties2KHR(device, &properties);
250 else
251 info->GetPhysicalDeviceProperties(device, &properties.properties);
252
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;
262 }
263 return drm_device->cpu_device;
264 }
265
266 static int device_select_find_explicit_default(struct device_pci_info *pci_infos,
267 uint32_t device_count,
268 const char *selection)
269 {
270 int default_idx = -1;
271 unsigned vendor_id, device_id;
272 int matched = sscanf(selection, "%x:%x", &vendor_id, &device_id);
273 if (matched != 2)
274 return default_idx;
275
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)
279 default_idx = i;
280 }
281 return default_idx;
282 }
283
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)
287 {
288 int default_idx = -1;
289 for (unsigned i = 0; i < device_count; ++i) {
290 char *tag = NULL;
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))
297 default_idx = i;
298 }
299 free(tag);
300 }
301 return default_idx;
302 }
303
304 static int device_select_find_boot_vga_default(struct device_pci_info *pci_infos,
305 uint32_t device_count)
306 {
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);
314 if (fd != -1) {
315 uint8_t val;
316 if (read(fd, &val, 1) == 1) {
317 if (val == '1')
318 default_idx = i;
319 }
320 close(fd);
321 }
322 if (default_idx != -1)
323 break;
324 }
325 return default_idx;
326 }
327
328 static int device_select_find_non_cpu(struct device_pci_info *pci_infos,
329 uint32_t device_count)
330 {
331 int default_idx = -1;
332
333 /* pick first GPU device */
334 for (unsigned i = 0; i < device_count; ++i) {
335 if (!pci_infos[i].cpu_device){
336 default_idx = i;
337 break;
338 }
339 }
340 return default_idx;
341 }
342
343 static int find_non_cpu_skip(struct device_pci_info *pci_infos,
344 uint32_t device_count,
345 int skip_idx)
346 {
347 for (unsigned i = 0; i < device_count; ++i) {
348 if (i == skip_idx)
349 continue;
350 if (pci_infos[i].cpu_device)
351 continue;
352 return i;
353 }
354 return -1;
355 }
356
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)
361 {
362 int default_idx = -1;
363 const char *dri_prime = getenv("DRI_PRIME");
364 bool dri_prime_is_one = false;
365 int cpu_count = 0;
366 if (dri_prime && !strcmp(dri_prime, "1"))
367 dri_prime_is_one = true;
368
369 if (dri_prime && !dri_prime_is_one && !info->has_pci_bus) {
370 fprintf(stderr, "device-select: cannot correctly use DRI_PRIME tag\n");
371 }
372
373 struct device_pci_info *pci_infos = (struct device_pci_info *)calloc(physical_device_count, sizeof(struct device_pci_info));
374 if (!pci_infos)
375 return 0;
376
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;
379 }
380
381 if (selection)
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);
393
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);
398 }
399 free(pci_infos);
400 return default_idx == -1 ? 0 : default_idx;
401 }
402
403 static VkResult device_select_EnumeratePhysicalDevices(VkInstance instance,
404 uint32_t* pPhysicalDeviceCount,
405 VkPhysicalDevice *pPhysicalDevices)
406 {
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)
414 return result;
415
416 VkPhysicalDevice *physical_devices = (VkPhysicalDevice*)calloc(sizeof(VkPhysicalDevice), physical_device_count);
417 VkPhysicalDevice *selected_physical_devices = (VkPhysicalDevice*)calloc(sizeof(VkPhysicalDevice),
418 physical_device_count);
419
420 if (!physical_devices || !selected_physical_devices) {
421 result = VK_ERROR_OUT_OF_HOST_MEMORY;
422 goto out;
423 }
424
425 result = info->EnumeratePhysicalDevices(instance, &physical_device_count, physical_devices);
426 if (result != VK_SUCCESS)
427 goto out;
428
429 for (unsigned i = 0; i < physical_device_count; i++) {
430 uint32_t count;
431 info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count, NULL);
432 if (count > 0) {
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;
438 }
439 }
440 free(extensions);
441 }
442 }
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]);
447 exit(0);
448 } else {
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];
455 }
456 }
457
458 if (selected_physical_device_count == 0) {
459 fprintf(stderr, "WARNING: selected no devices with MESA_VK_DEVICE_SELECT\n");
460 }
461
462 assert(result == VK_SUCCESS);
463
464 for (unsigned i = 0; i < selected_physical_device_count; i++) {
465 vk_outarray_append(&out, ent) {
466 *ent = selected_physical_devices[i];
467 }
468 }
469 result = vk_outarray_status(&out);
470 out:
471 free(physical_devices);
472 free(selected_physical_devices);
473 return result;
474 }
475
476 static VkResult device_select_EnumeratePhysicalDeviceGroups(VkInstance instance,
477 uint32_t* pPhysicalDeviceGroupCount,
478 VkPhysicalDeviceGroupProperties *pPhysicalDeviceGroups)
479 {
480 struct instance_info *info = device_select_layer_get_instance(instance);
481 VkResult result = info->EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroups);
482 return result;
483 }
484
485 static void (*get_pdevice_proc_addr(VkInstance instance, const char* name))()
486 {
487 struct instance_info *info = device_select_layer_get_instance(instance);
488 return info->GetPhysicalDeviceProcAddr(instance, name);
489 }
490
491 static void (*get_instance_proc_addr(VkInstance instance, const char* name))()
492 {
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;
501
502 struct instance_info *info = device_select_layer_get_instance(instance);
503 return info->GetInstanceProcAddr(instance, name);
504 }
505
506 VK_LAYER_EXPORT VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct)
507 {
508 if (pVersionStruct->loaderLayerInterfaceVersion < 2)
509 return VK_ERROR_INITIALIZATION_FAILED;
510 pVersionStruct->loaderLayerInterfaceVersion = 2;
511
512 pVersionStruct->pfnGetInstanceProcAddr = get_instance_proc_addr;
513 pVersionStruct->pfnGetPhysicalDeviceProcAddr = get_pdevice_proc_addr;
514
515 return VK_SUCCESS;
516 }