mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-18 18:08:15 +02:00
There are cases like zink where we have a file descriptors we are searching for devices for, so we don't need device selecting reordering, we just want the fastest path to get the devices so we can match them. This also helps avoid some cases of deadlock inside compositors where zink/vulkan initialises later and tries to connect to the compositor. This uses a VK_EXT_layer_setting to add a bypass setting. Cc: mesa-stable Reviewed-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/38252>
463 lines
18 KiB
C
463 lines
18 KiB
C
/*
|
|
* Copyright © 2017 Google
|
|
* Copyright © 2019 Red Hat
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
/* Rules for device selection.
|
|
* Is there an X or wayland connection open (or DISPLAY set).
|
|
* If no - try and find which device was the boot_vga device.
|
|
* If yes - try and work out which device is the connection primary,
|
|
* DRI_PRIME tagged overrides only work if bus info, =1 will just pick an alternate.
|
|
*/
|
|
|
|
#include <vulkan/vk_layer.h>
|
|
#include <vulkan/vulkan.h>
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "util/hash_table.h"
|
|
#include "util/simple_mtx.h"
|
|
#include "util/u_debug.h"
|
|
#include "device_select.h"
|
|
#include "vk_util.h"
|
|
|
|
static struct hash_table *device_select_instance_ht = NULL;
|
|
static simple_mtx_t device_select_mutex = SIMPLE_MTX_INITIALIZER;
|
|
|
|
static void
|
|
device_select_layer_add_instance(VkInstance instance, struct instance_info *info)
|
|
{
|
|
simple_mtx_lock(&device_select_mutex);
|
|
|
|
if (!device_select_instance_ht)
|
|
device_select_instance_ht = _mesa_pointer_hash_table_create(NULL);
|
|
_mesa_hash_table_insert(device_select_instance_ht, instance, info);
|
|
|
|
simple_mtx_unlock(&device_select_mutex);
|
|
}
|
|
|
|
static struct instance_info *
|
|
device_select_layer_get_instance(VkInstance instance)
|
|
{
|
|
struct hash_entry *entry;
|
|
struct instance_info *info = NULL;
|
|
simple_mtx_lock(&device_select_mutex);
|
|
entry = _mesa_hash_table_search(device_select_instance_ht, (void *)instance);
|
|
if (entry)
|
|
info = (struct instance_info *)entry->data;
|
|
simple_mtx_unlock(&device_select_mutex);
|
|
return info;
|
|
}
|
|
|
|
static void
|
|
device_select_layer_remove_instance(VkInstance instance)
|
|
{
|
|
simple_mtx_lock(&device_select_mutex);
|
|
|
|
_mesa_hash_table_remove_key(device_select_instance_ht, instance);
|
|
|
|
if (_mesa_hash_table_num_entries(device_select_instance_ht) == 0) {
|
|
_mesa_hash_table_destroy(device_select_instance_ht, NULL);
|
|
device_select_instance_ht = NULL;
|
|
}
|
|
|
|
simple_mtx_unlock(&device_select_mutex);
|
|
}
|
|
|
|
static VkResult
|
|
device_select_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
|
|
const VkAllocationCallbacks *pAllocator, VkInstance *pInstance)
|
|
{
|
|
VkLayerInstanceCreateInfo *chain_info = NULL;
|
|
bool bypass_device_select = false;
|
|
vk_foreach_struct_const(s, pCreateInfo->pNext) {
|
|
switch (s->sType) {
|
|
case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO: {
|
|
const VkLayerInstanceCreateInfo *this_info = (const void *)s;
|
|
if (this_info->function == VK_LAYER_LINK_INFO)
|
|
chain_info = (VkLayerInstanceCreateInfo *)this_info; /* loses const */
|
|
break;
|
|
}
|
|
case VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT: {
|
|
const VkLayerSettingsCreateInfoEXT *lsci = (const void *)s;
|
|
for (unsigned i = 0; i < lsci->settingCount; i++) {
|
|
const VkLayerSettingEXT *ls = &lsci->pSettings[i];
|
|
if (!strcmp(ls->pLayerName, "MESA_device_select")) {
|
|
if (!strcmp(ls->pSettingName, "no_device_select")) {
|
|
assert(ls->type == VK_LAYER_SETTING_TYPE_BOOL32_EXT);
|
|
uint32_t *values = (uint32_t *)ls->pValues;
|
|
bypass_device_select = values[0];
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert(chain_info->u.pLayerInfo);
|
|
|
|
PFN_vkGetInstanceProcAddr GetInstanceProcAddr =
|
|
chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
|
|
PFN_vkCreateInstance fpCreateInstance =
|
|
(PFN_vkCreateInstance)GetInstanceProcAddr(NULL, "vkCreateInstance");
|
|
if (fpCreateInstance == NULL) {
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
}
|
|
|
|
chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
|
|
|
|
const char *engineName =
|
|
pCreateInfo->pApplicationInfo && pCreateInfo->pApplicationInfo->pEngineName
|
|
? pCreateInfo->pApplicationInfo->pEngineName
|
|
: "";
|
|
const char *applicationName =
|
|
pCreateInfo->pApplicationInfo && pCreateInfo->pApplicationInfo->pApplicationName
|
|
? pCreateInfo->pApplicationInfo->pApplicationName
|
|
: "";
|
|
VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
|
|
if (result != VK_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
bool zink = !strcmp(engineName, "mesa zink");
|
|
bool xwayland = !strcmp(applicationName, "Xwayland");
|
|
struct instance_info *info = (struct instance_info *)calloc(1, sizeof(struct instance_info));
|
|
info->GetInstanceProcAddr = GetInstanceProcAddr;
|
|
info->xserver = !strcmp(applicationName, "Xorg") || !strcmp(applicationName, "Xephyr");
|
|
|
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
|
bool has_wayland = os_get_option("WAYLAND_DISPLAY") || os_get_option("WAYLAND_SOCKET");
|
|
#endif
|
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
|
bool has_xcb = !!os_get_option("DISPLAY");
|
|
#endif
|
|
|
|
for (unsigned i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
|
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
|
if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME) &&
|
|
has_wayland)
|
|
info->has_wayland = true;
|
|
#endif
|
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
|
if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_XCB_SURFACE_EXTENSION_NAME) &&
|
|
has_xcb)
|
|
info->has_xcb = !info->xserver || !zink;
|
|
#endif
|
|
}
|
|
|
|
if (zink && xwayland)
|
|
bypass_device_select = true;
|
|
/*
|
|
* The loader is currently not able to handle GetPhysicalDeviceProperties2KHR calls in
|
|
* EnumeratePhysicalDevices when there are other layers present. To avoid mysterious crashes
|
|
* for users just use only the vulkan version for now.
|
|
*/
|
|
info->bypass_device_select = bypass_device_select;
|
|
info->has_vulkan11 = pCreateInfo->pApplicationInfo &&
|
|
pCreateInfo->pApplicationInfo->apiVersion >= VK_MAKE_VERSION(1, 1, 0);
|
|
|
|
info->debug = debug_get_bool_option("MESA_VK_DEVICE_SELECT_DEBUG", false) ||
|
|
debug_get_bool_option("DRI_PRIME_DEBUG", false);
|
|
info->selection = os_get_option_dup("MESA_VK_DEVICE_SELECT");
|
|
info->dri_prime = os_get_option_dup("DRI_PRIME");
|
|
info->force_default_device =
|
|
debug_get_bool_option("MESA_VK_DEVICE_SELECT_FORCE_DEFAULT_DEVICE", false);
|
|
|
|
#define DEVSEL_GET_CB(func) \
|
|
info->func = (PFN_vk##func)info->GetInstanceProcAddr(*pInstance, "vk" #func)
|
|
DEVSEL_GET_CB(DestroyInstance);
|
|
DEVSEL_GET_CB(EnumeratePhysicalDevices);
|
|
DEVSEL_GET_CB(EnumeratePhysicalDeviceGroups);
|
|
DEVSEL_GET_CB(GetPhysicalDeviceProperties);
|
|
DEVSEL_GET_CB(EnumerateDeviceExtensionProperties);
|
|
if (info->has_vulkan11)
|
|
DEVSEL_GET_CB(GetPhysicalDeviceProperties2);
|
|
#undef DEVSEL_GET_CB
|
|
|
|
device_select_layer_add_instance(*pInstance, info);
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
device_select_DestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator)
|
|
{
|
|
struct instance_info *info = device_select_layer_get_instance(instance);
|
|
|
|
device_select_layer_remove_instance(instance);
|
|
info->DestroyInstance(instance, pAllocator);
|
|
free(info->dri_prime);
|
|
free(info->selection);
|
|
free(info);
|
|
}
|
|
|
|
void
|
|
device_select_get_properties(const struct instance_info *info, VkPhysicalDevice device,
|
|
VkPhysicalDeviceProperties2 *properties)
|
|
{
|
|
if (info->GetPhysicalDeviceProperties2 && info->has_vulkan11)
|
|
info->GetPhysicalDeviceProperties2(device, properties);
|
|
else
|
|
info->GetPhysicalDeviceProperties(device, &properties->properties);
|
|
}
|
|
|
|
static void
|
|
print_gpu(const struct instance_info *info, unsigned index, VkPhysicalDevice device)
|
|
{
|
|
const char *type = "";
|
|
VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties =
|
|
(VkPhysicalDevicePCIBusInfoPropertiesEXT){
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT,
|
|
};
|
|
VkPhysicalDeviceProperties2 properties = (VkPhysicalDeviceProperties2){
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
|
};
|
|
if (info->has_vulkan11 && info->has_pci_bus)
|
|
properties.pNext = &ext_pci_properties;
|
|
device_select_get_properties(info, device, &properties);
|
|
|
|
switch (properties.properties.deviceType) {
|
|
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
|
|
default:
|
|
type = "other";
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
|
type = "integrated GPU";
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
|
type = "discrete GPU";
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
|
type = "virtual GPU";
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
|
type = "CPU";
|
|
break;
|
|
}
|
|
fprintf(stderr, " GPU %d: %x:%x \"%s\" %s", index, properties.properties.vendorID,
|
|
properties.properties.deviceID, properties.properties.deviceName, type);
|
|
if (info->has_vulkan11 && info->has_pci_bus)
|
|
fprintf(stderr, " %04x:%02x:%02x.%x", ext_pci_properties.pciDomain, ext_pci_properties.pciBus,
|
|
ext_pci_properties.pciDevice, ext_pci_properties.pciFunction);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
static VkResult
|
|
device_select_EnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount,
|
|
VkPhysicalDevice *pPhysicalDevices)
|
|
{
|
|
struct instance_info *info = device_select_layer_get_instance(instance);
|
|
uint32_t physical_device_count = 0;
|
|
uint32_t selected_physical_device_count = 0;
|
|
bool expose_only_one_dev = false;
|
|
if (info->bypass_device_select)
|
|
return info->EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
|
|
VkResult result = info->EnumeratePhysicalDevices(instance, &physical_device_count, NULL);
|
|
VK_OUTARRAY_MAKE_TYPED(VkPhysicalDevice, out, pPhysicalDevices, pPhysicalDeviceCount);
|
|
if (result != VK_SUCCESS)
|
|
return result;
|
|
|
|
VkPhysicalDevice *physical_devices =
|
|
(VkPhysicalDevice *)calloc(sizeof(VkPhysicalDevice), physical_device_count);
|
|
VkPhysicalDevice *selected_physical_devices =
|
|
(VkPhysicalDevice *)calloc(sizeof(VkPhysicalDevice), physical_device_count);
|
|
|
|
if (!physical_devices || !selected_physical_devices) {
|
|
result = VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
result = info->EnumeratePhysicalDevices(instance, &physical_device_count, physical_devices);
|
|
if (result != VK_SUCCESS)
|
|
goto out;
|
|
|
|
for (unsigned i = 0; i < physical_device_count; i++) {
|
|
uint32_t count;
|
|
info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count, NULL);
|
|
if (count > 0) {
|
|
VkExtensionProperties *extensions = calloc(count, sizeof(VkExtensionProperties));
|
|
if (info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count,
|
|
extensions) == VK_SUCCESS) {
|
|
for (unsigned j = 0; j < count; j++) {
|
|
if (!strcmp(extensions[j].extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME))
|
|
info->has_pci_bus = true;
|
|
}
|
|
}
|
|
free(extensions);
|
|
}
|
|
}
|
|
if (info->debug || (info->selection && strcmp(info->selection, "list") == 0)) {
|
|
fprintf(stderr, "selectable devices:\n");
|
|
for (unsigned i = 0; i < physical_device_count; ++i)
|
|
print_gpu(info, i, physical_devices[i]);
|
|
|
|
if (info->selection && strcmp(info->selection, "list") == 0)
|
|
exit(0);
|
|
}
|
|
|
|
unsigned selected_index =
|
|
device_select_get_first(info, physical_device_count, physical_devices, &expose_only_one_dev);
|
|
selected_physical_device_count = physical_device_count;
|
|
selected_physical_devices[0] = physical_devices[selected_index];
|
|
for (unsigned i = 0; i < physical_device_count - 1; ++i) {
|
|
unsigned this_idx = i < selected_index ? i : i + 1;
|
|
selected_physical_devices[i + 1] = physical_devices[this_idx];
|
|
}
|
|
|
|
if (selected_physical_device_count == 0) {
|
|
fprintf(stderr, "WARNING: selected no devices with MESA_VK_DEVICE_SELECT\n");
|
|
}
|
|
|
|
assert(result == VK_SUCCESS);
|
|
|
|
/* do not give multiple device option to app if force default device */
|
|
if (info->force_default_device && selected_physical_device_count != 0)
|
|
expose_only_one_dev = true;
|
|
|
|
if (expose_only_one_dev)
|
|
selected_physical_device_count = 1;
|
|
|
|
for (unsigned i = 0; i < selected_physical_device_count; i++) {
|
|
vk_outarray_append_typed(VkPhysicalDevice, &out, ent)
|
|
{
|
|
*ent = selected_physical_devices[i];
|
|
}
|
|
}
|
|
result = vk_outarray_status(&out);
|
|
out:
|
|
free(physical_devices);
|
|
free(selected_physical_devices);
|
|
return result;
|
|
}
|
|
|
|
static VkResult
|
|
device_select_EnumeratePhysicalDeviceGroups(VkInstance instance,
|
|
uint32_t *pPhysicalDeviceGroupCount,
|
|
VkPhysicalDeviceGroupProperties *pPhysicalDeviceGroups)
|
|
{
|
|
struct instance_info *info = device_select_layer_get_instance(instance);
|
|
uint32_t physical_device_group_count = 0;
|
|
uint32_t selected_physical_device_group_count = 0;
|
|
if (info->bypass_device_select)
|
|
return info->EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount,
|
|
pPhysicalDeviceGroups);
|
|
VkResult result =
|
|
info->EnumeratePhysicalDeviceGroups(instance, &physical_device_group_count, NULL);
|
|
VK_OUTARRAY_MAKE_TYPED(VkPhysicalDeviceGroupProperties, out, pPhysicalDeviceGroups,
|
|
pPhysicalDeviceGroupCount);
|
|
|
|
if (result != VK_SUCCESS)
|
|
return result;
|
|
|
|
VkPhysicalDeviceGroupProperties *physical_device_groups =
|
|
(VkPhysicalDeviceGroupProperties *)calloc(sizeof(VkPhysicalDeviceGroupProperties),
|
|
physical_device_group_count);
|
|
VkPhysicalDeviceGroupProperties *selected_physical_device_groups =
|
|
(VkPhysicalDeviceGroupProperties *)calloc(sizeof(VkPhysicalDeviceGroupProperties),
|
|
physical_device_group_count);
|
|
|
|
if (!physical_device_groups || !selected_physical_device_groups) {
|
|
result = VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
for (unsigned i = 0; i < physical_device_group_count; i++)
|
|
physical_device_groups[i].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
|
|
|
|
result = info->EnumeratePhysicalDeviceGroups(instance, &physical_device_group_count,
|
|
physical_device_groups);
|
|
if (result != VK_SUCCESS)
|
|
goto out;
|
|
|
|
/* just sort groups with CPU devices to the end? - assume nobody will mix these */
|
|
int num_gpu_groups = 0;
|
|
int num_cpu_groups = 0;
|
|
selected_physical_device_group_count = physical_device_group_count;
|
|
for (unsigned i = 0; i < physical_device_group_count; i++) {
|
|
bool group_has_cpu_device = false;
|
|
for (unsigned j = 0; j < physical_device_groups[i].physicalDeviceCount; j++) {
|
|
VkPhysicalDevice physical_device = physical_device_groups[i].physicalDevices[j];
|
|
VkPhysicalDeviceProperties2 properties = (VkPhysicalDeviceProperties2){
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
|
};
|
|
info->GetPhysicalDeviceProperties(physical_device, &properties.properties);
|
|
group_has_cpu_device = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
|
|
}
|
|
|
|
if (group_has_cpu_device) {
|
|
selected_physical_device_groups[physical_device_group_count - num_cpu_groups - 1] =
|
|
physical_device_groups[i];
|
|
num_cpu_groups++;
|
|
} else {
|
|
selected_physical_device_groups[num_gpu_groups] = physical_device_groups[i];
|
|
num_gpu_groups++;
|
|
}
|
|
}
|
|
|
|
assert(result == VK_SUCCESS);
|
|
|
|
for (unsigned i = 0; i < selected_physical_device_group_count; i++) {
|
|
vk_outarray_append_typed(VkPhysicalDeviceGroupProperties, &out, ent)
|
|
{
|
|
*ent = selected_physical_device_groups[i];
|
|
}
|
|
}
|
|
result = vk_outarray_status(&out);
|
|
out:
|
|
free(physical_device_groups);
|
|
free(selected_physical_device_groups);
|
|
return result;
|
|
}
|
|
|
|
static void (*get_instance_proc_addr(VkInstance instance, const char *name))()
|
|
{
|
|
if (strcmp(name, "vkGetInstanceProcAddr") == 0)
|
|
return (void (*)())get_instance_proc_addr;
|
|
if (strcmp(name, "vkCreateInstance") == 0)
|
|
return (void (*)())device_select_CreateInstance;
|
|
if (strcmp(name, "vkDestroyInstance") == 0)
|
|
return (void (*)())device_select_DestroyInstance;
|
|
if (strcmp(name, "vkEnumeratePhysicalDevices") == 0)
|
|
return (void (*)())device_select_EnumeratePhysicalDevices;
|
|
if (strcmp(name, "vkEnumeratePhysicalDeviceGroups") == 0)
|
|
return (void (*)())device_select_EnumeratePhysicalDeviceGroups;
|
|
|
|
struct instance_info *info = device_select_layer_get_instance(instance);
|
|
return info->GetInstanceProcAddr(instance, name);
|
|
}
|
|
|
|
PUBLIC VkResult
|
|
vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct)
|
|
{
|
|
if (pVersionStruct->loaderLayerInterfaceVersion < 2)
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
pVersionStruct->loaderLayerInterfaceVersion = 2;
|
|
|
|
pVersionStruct->pfnGetInstanceProcAddr = get_instance_proc_addr;
|
|
|
|
return VK_SUCCESS;
|
|
}
|