meson: use gnu_symbol_visibility argument
[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 return VK_ERROR_INITIALIZATION_FAILED;
141 }
142
143 chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
144
145 VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
146 if (result != VK_SUCCESS)
147 return result;
148
149 for (unsigned i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
150 if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
151 info->has_props2 = true;
152 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
153 if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME))
154 info->has_wayland = true;
155 #endif
156 #ifdef VK_USE_PLATFORM_XCB_KHR
157 if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_XCB_SURFACE_EXTENSION_NAME))
158 info->has_xcb = true;
159 #endif
160 }
161
162 info->GetPhysicalDeviceProcAddr = (PFN_GetPhysicalDeviceProcAddr)info->GetInstanceProcAddr(*pInstance, "vk_layerGetPhysicalDeviceProcAddr");
163 #define DEVSEL_GET_CB(func) info->func = (PFN_vk##func)info->GetInstanceProcAddr(*pInstance, "vk" #func)
164 DEVSEL_GET_CB(DestroyInstance);
165 DEVSEL_GET_CB(EnumeratePhysicalDevices);
166 DEVSEL_GET_CB(EnumeratePhysicalDeviceGroups);
167 DEVSEL_GET_CB(GetPhysicalDeviceProperties);
168 DEVSEL_GET_CB(EnumerateDeviceExtensionProperties);
169 if (info->has_props2)
170 DEVSEL_GET_CB(GetPhysicalDeviceProperties2KHR);
171 #undef DEVSEL_GET_CB
172
173 device_select_layer_add_instance(*pInstance, info);
174
175 return VK_SUCCESS;
176 }
177
178 static void device_select_DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator)
179 {
180 struct instance_info *info = device_select_layer_get_instance(instance);
181
182 device_select_layer_remove_instance(instance);
183 info->DestroyInstance(instance, pAllocator);
184 free(info);
185 }
186
187
188 static void print_gpu(const struct instance_info *info, unsigned index, VkPhysicalDevice device)
189 {
190 const char *type = "";
191 VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties = (VkPhysicalDevicePCIBusInfoPropertiesEXT) {
192 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
193 };
194 VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){
195 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR
196 };
197 if (info->has_props2 && info->has_pci_bus)
198 properties.pNext = &ext_pci_properties;
199 if (info->GetPhysicalDeviceProperties2KHR)
200 info->GetPhysicalDeviceProperties2KHR(device, &properties);
201 else
202 info->GetPhysicalDeviceProperties(device, &properties.properties);
203
204 switch(properties.properties.deviceType) {
205 case VK_PHYSICAL_DEVICE_TYPE_OTHER:
206 default:
207 type = "other";
208 break;
209 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
210 type = "integrated GPU";
211 break;
212 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
213 type = "discrete GPU";
214 break;
215 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
216 type = "virtual GPU";
217 break;
218 case VK_PHYSICAL_DEVICE_TYPE_CPU:
219 type = "CPU";
220 break;
221 }
222 fprintf(stderr, " GPU %d: %x:%x \"%s\" %s", index, properties.properties.vendorID,
223 properties.properties.deviceID, properties.properties.deviceName, type);
224 if (info->has_pci_bus)
225 fprintf(stderr, " %04x:%02x:%02x.%x", ext_pci_properties.pciDomain,
226 ext_pci_properties.pciBus, ext_pci_properties.pciDevice,
227 ext_pci_properties.pciFunction);
228 fprintf(stderr, "\n");
229 }
230
231 static void fill_drm_device_info(const struct instance_info *info,
232 struct device_pci_info *drm_device,
233 VkPhysicalDevice device)
234 {
235 VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties = (VkPhysicalDevicePCIBusInfoPropertiesEXT) {
236 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
237 };
238
239 VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){
240 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR
241 };
242
243 if (info->has_props2 && info->has_pci_bus)
244 properties.pNext = &ext_pci_properties;
245 if (info->GetPhysicalDeviceProperties2KHR)
246 info->GetPhysicalDeviceProperties2KHR(device, &properties);
247 else
248 info->GetPhysicalDeviceProperties(device, &properties.properties);
249
250 drm_device->dev_info.vendor_id = properties.properties.vendorID;
251 drm_device->dev_info.device_id = properties.properties.deviceID;
252 if (info->has_pci_bus) {
253 drm_device->has_bus_info = true;
254 drm_device->bus_info.domain = ext_pci_properties.pciDomain;
255 drm_device->bus_info.bus = ext_pci_properties.pciBus;
256 drm_device->bus_info.dev = ext_pci_properties.pciDevice;
257 drm_device->bus_info.func = ext_pci_properties.pciFunction;
258 }
259 }
260
261 static int device_select_find_explicit_default(struct device_pci_info *pci_infos,
262 uint32_t device_count,
263 const char *selection)
264 {
265 int default_idx = -1;
266 unsigned vendor_id, device_id;
267 int matched = sscanf(selection, "%x:%x", &vendor_id, &device_id);
268 if (matched != 2)
269 return default_idx;
270
271 for (unsigned i = 0; i < device_count; ++i) {
272 if (pci_infos[i].dev_info.vendor_id == vendor_id &&
273 pci_infos[i].dev_info.device_id == device_id)
274 default_idx = i;
275 }
276 return default_idx;
277 }
278
279 static int device_select_find_dri_prime_tag_default(struct device_pci_info *pci_infos,
280 uint32_t device_count,
281 const char *dri_prime)
282 {
283 int default_idx = -1;
284 for (unsigned i = 0; i < device_count; ++i) {
285 char *tag = NULL;
286 if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u",
287 pci_infos[i].bus_info.domain,
288 pci_infos[i].bus_info.bus,
289 pci_infos[i].bus_info.dev,
290 pci_infos[i].bus_info.func) >= 0) {
291 if (strcmp(dri_prime, tag))
292 default_idx = i;
293 }
294 free(tag);
295 }
296 return default_idx;
297 }
298
299 static int device_select_find_boot_vga_default(struct device_pci_info *pci_infos,
300 uint32_t device_count)
301 {
302 char boot_vga_path[1024];
303 int default_idx = -1;
304 for (unsigned i = 0; i < device_count; ++i) {
305 /* fallback to probing the pci bus boot_vga device. */
306 snprintf(boot_vga_path, 1023, "/sys/bus/pci/devices/%04x:%02x:%02x.%x/boot_vga", pci_infos[i].bus_info.domain,
307 pci_infos[i].bus_info.bus, pci_infos[i].bus_info.dev, pci_infos[i].bus_info.func);
308 int fd = open(boot_vga_path, O_RDONLY);
309 if (fd != -1) {
310 uint8_t val;
311 if (read(fd, &val, 1) == 1) {
312 if (val == '1')
313 default_idx = i;
314 }
315 close(fd);
316 }
317 if (default_idx != -1)
318 break;
319 }
320 return default_idx;
321 }
322
323 static uint32_t get_default_device(const struct instance_info *info,
324 const char *selection,
325 uint32_t physical_device_count,
326 VkPhysicalDevice *pPhysicalDevices)
327 {
328 int default_idx = -1;
329 const char *dri_prime = getenv("DRI_PRIME");
330 bool dri_prime_is_one = false;
331
332 if (dri_prime && !strcmp(dri_prime, "1"))
333 dri_prime_is_one = true;
334
335 if (dri_prime && !dri_prime_is_one && !info->has_pci_bus) {
336 fprintf(stderr, "device-select: cannot correctly use DRI_PRIME tag\n");
337 }
338
339 struct device_pci_info *pci_infos = (struct device_pci_info *)calloc(physical_device_count, sizeof(struct device_pci_info));
340 if (!pci_infos)
341 return 0;
342
343 for (unsigned i = 0; i < physical_device_count; ++i) {
344 fill_drm_device_info(info, &pci_infos[i], pPhysicalDevices[i]);
345 }
346
347 if (selection)
348 default_idx = device_select_find_explicit_default(pci_infos, physical_device_count, selection);
349 if (default_idx == -1 && info->has_pci_bus && dri_prime && !dri_prime_is_one)
350 default_idx = device_select_find_dri_prime_tag_default(pci_infos, physical_device_count, dri_prime);
351 if (default_idx == -1 && info->has_wayland)
352 default_idx = device_select_find_wayland_pci_default(pci_infos, physical_device_count);
353 if (default_idx == -1 && info->has_xcb)
354 default_idx = device_select_find_xcb_pci_default(pci_infos, physical_device_count);
355 if (info->has_pci_bus && default_idx == -1) {
356 default_idx = device_select_find_boot_vga_default(pci_infos, physical_device_count);
357 }
358
359 /* DRI_PRIME=1 handling - pick any other device than default. */
360 if (default_idx != -1 && dri_prime_is_one && physical_device_count > 1) {
361 if (default_idx == 0)
362 default_idx = 1;
363 else if (default_idx == 1)
364 default_idx = 0;
365 }
366 free(pci_infos);
367 return default_idx == -1 ? 0 : default_idx;
368 }
369
370 static VkResult device_select_EnumeratePhysicalDevices(VkInstance instance,
371 uint32_t* pPhysicalDeviceCount,
372 VkPhysicalDevice *pPhysicalDevices)
373 {
374 struct instance_info *info = device_select_layer_get_instance(instance);
375 uint32_t physical_device_count = 0;
376 uint32_t selected_physical_device_count = 0;
377 const char* selection = getenv("MESA_VK_DEVICE_SELECT");
378 VkResult result = info->EnumeratePhysicalDevices(instance, &physical_device_count, NULL);
379 VK_OUTARRAY_MAKE(out, pPhysicalDevices, pPhysicalDeviceCount);
380 if (result != VK_SUCCESS)
381 return result;
382
383 VkPhysicalDevice *physical_devices = (VkPhysicalDevice*)calloc(sizeof(VkPhysicalDevice), physical_device_count);
384 VkPhysicalDevice *selected_physical_devices = (VkPhysicalDevice*)calloc(sizeof(VkPhysicalDevice),
385 physical_device_count);
386
387 if (!physical_devices || !selected_physical_devices) {
388 result = VK_ERROR_OUT_OF_HOST_MEMORY;
389 goto out;
390 }
391
392 result = info->EnumeratePhysicalDevices(instance, &physical_device_count, physical_devices);
393 if (result != VK_SUCCESS)
394 goto out;
395
396 for (unsigned i = 0; i < physical_device_count; i++) {
397 uint32_t count;
398 info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count, NULL);
399 if (count > 0) {
400 VkExtensionProperties *extensions = calloc(count, sizeof(VkExtensionProperties));
401 if (info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count, extensions) == VK_SUCCESS) {
402 for (unsigned j = 0; j < count; j++) {
403 if (!strcmp(extensions[j].extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME))
404 info->has_pci_bus = true;
405 }
406 }
407 free(extensions);
408 }
409 }
410 if (selection && strcmp(selection, "list") == 0) {
411 fprintf(stderr, "selectable devices:\n");
412 for (unsigned i = 0; i < physical_device_count; ++i)
413 print_gpu(info, i, physical_devices[i]);
414 exit(0);
415 } else {
416 unsigned selected_index = get_default_device(info, selection, physical_device_count, physical_devices);
417 selected_physical_device_count = physical_device_count;
418 selected_physical_devices[0] = physical_devices[selected_index];
419 for (unsigned i = 0; i < physical_device_count - 1; ++i) {
420 unsigned this_idx = i < selected_index ? i : i + 1;
421 selected_physical_devices[i + 1] = physical_devices[this_idx];
422 }
423 }
424
425 if (selected_physical_device_count == 0) {
426 fprintf(stderr, "WARNING: selected no devices with MESA_VK_DEVICE_SELECT\n");
427 }
428
429 assert(result == VK_SUCCESS);
430
431 for (unsigned i = 0; i < selected_physical_device_count; i++) {
432 vk_outarray_append(&out, ent) {
433 *ent = selected_physical_devices[i];
434 }
435 }
436 result = vk_outarray_status(&out);
437 out:
438 free(physical_devices);
439 free(selected_physical_devices);
440 return result;
441 }
442
443 static VkResult device_select_EnumeratePhysicalDeviceGroups(VkInstance instance,
444 uint32_t* pPhysicalDeviceGroupCount,
445 VkPhysicalDeviceGroupProperties *pPhysicalDeviceGroups)
446 {
447 struct instance_info *info = device_select_layer_get_instance(instance);
448 VkResult result = info->EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroups);
449 return result;
450 }
451
452 static void (*get_pdevice_proc_addr(VkInstance instance, const char* name))()
453 {
454 struct instance_info *info = device_select_layer_get_instance(instance);
455 return info->GetPhysicalDeviceProcAddr(instance, name);
456 }
457
458 static void (*get_instance_proc_addr(VkInstance instance, const char* name))()
459 {
460 if (strcmp(name, "vkCreateInstance") == 0)
461 return (void(*)())device_select_CreateInstance;
462 if (strcmp(name, "vkDestroyInstance") == 0)
463 return (void(*)())device_select_DestroyInstance;
464 if (strcmp(name, "vkEnumeratePhysicalDevices") == 0)
465 return (void(*)())device_select_EnumeratePhysicalDevices;
466 if (strcmp(name, "vkEnumeratePhysicalDeviceGroups") == 0)
467 return (void(*)())device_select_EnumeratePhysicalDeviceGroups;
468
469 struct instance_info *info = device_select_layer_get_instance(instance);
470 return info->GetInstanceProcAddr(instance, name);
471 }
472
473 VK_LAYER_EXPORT VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct)
474 {
475 if (pVersionStruct->loaderLayerInterfaceVersion < 2)
476 return VK_ERROR_INITIALIZATION_FAILED;
477 pVersionStruct->loaderLayerInterfaceVersion = 2;
478
479 pVersionStruct->pfnGetInstanceProcAddr = get_instance_proc_addr;
480 pVersionStruct->pfnGetPhysicalDeviceProcAddr = get_pdevice_proc_addr;
481
482 return VK_SUCCESS;
483 }