winevulkan: Use __wine_unix_call for checking Vulkan functions availability.
[wine.git] / dlls / winevulkan / loader.c
blobd3f20e0d544ec6644ea424086e51169c5b474713
1 /* Wine Vulkan ICD implementation
3 * Copyright 2017 Roderick Colenbrander
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include "vulkan_loader.h"
21 #include "winreg.h"
22 #include "ntuser.h"
23 #include "initguid.h"
24 #include "devguid.h"
25 #include "setupapi.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(vulkan);
29 /* For now default to 4 as it felt like a reasonable version feature wise to support.
30 * Version 5 adds more extensive version checks. Something to tackle later.
32 #define WINE_VULKAN_ICD_VERSION 4
34 DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
35 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_GPU_VULKAN_UUID, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5c, 2);
37 const struct unix_funcs *unix_funcs;
38 unixlib_handle_t unix_handle;
40 static HINSTANCE hinstance;
42 static void *wine_vk_get_global_proc_addr(const char *name);
44 #define wine_vk_find_struct(s, t) wine_vk_find_struct_((void *)s, VK_STRUCTURE_TYPE_##t)
45 static void *wine_vk_find_struct_(void *s, VkStructureType t)
47 VkBaseOutStructure *header;
49 for (header = s; header; header = header->pNext)
51 if (header->sType == t)
52 return header;
55 return NULL;
58 VkResult WINAPI vkEnumerateInstanceLayerProperties(uint32_t *count, VkLayerProperties *properties)
60 TRACE("%p, %p\n", count, properties);
62 *count = 0;
63 return VK_SUCCESS;
66 static const struct vulkan_func vk_global_dispatch_table[] =
68 /* These functions must call wine_vk_init_once() before accessing vk_funcs. */
69 {"vkCreateInstance", &vkCreateInstance},
70 {"vkEnumerateInstanceExtensionProperties", &vkEnumerateInstanceExtensionProperties},
71 {"vkEnumerateInstanceLayerProperties", &vkEnumerateInstanceLayerProperties},
72 {"vkEnumerateInstanceVersion", &vkEnumerateInstanceVersion},
73 {"vkGetInstanceProcAddr", &vkGetInstanceProcAddr},
76 static void *wine_vk_get_global_proc_addr(const char *name)
78 unsigned int i;
80 for (i = 0; i < ARRAY_SIZE(vk_global_dispatch_table); i++)
82 if (strcmp(name, vk_global_dispatch_table[i].name) == 0)
84 TRACE("Found name=%s in global table\n", debugstr_a(name));
85 return vk_global_dispatch_table[i].func;
88 return NULL;
91 static BOOL is_available_instance_function(VkInstance instance, const char *name)
93 struct is_available_instance_function_params params = { .instance = instance, .name = name };
94 return vk_unix_call(unix_is_available_instance_function, &params);
97 static BOOL is_available_device_function(VkDevice device, const char *name)
99 struct is_available_device_function_params params = { .device = device, .name = name };
100 return vk_unix_call(unix_is_available_device_function, &params);
103 PFN_vkVoidFunction WINAPI vkGetInstanceProcAddr(VkInstance instance, const char *name)
105 void *func;
107 TRACE("%p, %s\n", instance, debugstr_a(name));
109 if (!name)
110 return NULL;
112 /* vkGetInstanceProcAddr can load most Vulkan functions when an instance is passed in, however
113 * for a NULL instance it can only load global functions.
115 func = wine_vk_get_global_proc_addr(name);
116 if (func)
118 return func;
120 if (!instance)
122 WARN("Global function %s not found.\n", debugstr_a(name));
123 return NULL;
126 if (!is_available_instance_function(instance, name))
127 return NULL;
129 func = wine_vk_get_instance_proc_addr(name);
130 if (func) return func;
132 func = wine_vk_get_phys_dev_proc_addr(name);
133 if (func) return func;
135 /* vkGetInstanceProcAddr also loads any children of instance, so device functions as well. */
136 func = wine_vk_get_device_proc_addr(name);
137 if (func) return func;
139 WARN("Unsupported device or instance function: %s.\n", debugstr_a(name));
140 return NULL;
143 PFN_vkVoidFunction WINAPI vkGetDeviceProcAddr(VkDevice device, const char *name)
145 void *func;
146 TRACE("%p, %s\n", device, debugstr_a(name));
148 /* The spec leaves return value undefined for a NULL device, let's just return NULL. */
149 if (!device || !name)
150 return NULL;
152 /* Per the spec, we are only supposed to return device functions as in functions
153 * for which the first parameter is vkDevice or a child of vkDevice like a
154 * vkCommandBuffer or vkQueue.
155 * Loader takes care of filtering of extensions which are enabled or not.
157 if (is_available_device_function(device, name))
159 func = wine_vk_get_device_proc_addr(name);
160 if (func)
161 return func;
164 /* vkGetDeviceProcAddr was intended for loading device and subdevice functions.
165 * idTech 6 titles such as Doom and Wolfenstein II, however use it also for
166 * loading of instance functions. This is undefined behavior as the specification
167 * disallows using any of the returned function pointers outside of device /
168 * subdevice objects. The games don't actually use the function pointers and if they
169 * did, they would crash as VkInstance / VkPhysicalDevice parameters need unwrapping.
170 * Khronos clarified behavior in the Vulkan spec and expects drivers to get updated,
171 * however it would require both driver and game fixes.
172 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/2323
173 * https://github.com/KhronosGroup/Vulkan-Docs/issues/655
175 if (((struct wine_vk_device_base *)device)->quirks & WINEVULKAN_QUIRK_GET_DEVICE_PROC_ADDR
176 && ((func = wine_vk_get_instance_proc_addr(name))
177 || (func = wine_vk_get_phys_dev_proc_addr(name))))
179 WARN("Returning instance function %s.\n", debugstr_a(name));
180 return func;
183 WARN("Unsupported device function: %s.\n", debugstr_a(name));
184 return NULL;
187 void * WINAPI vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char *name)
189 TRACE("%p, %s\n", instance, debugstr_a(name));
191 if (!is_available_instance_function(instance, name))
192 return NULL;
194 return wine_vk_get_phys_dev_proc_addr(name);
197 void * WINAPI vk_icdGetInstanceProcAddr(VkInstance instance, const char *name)
199 TRACE("%p, %s\n", instance, debugstr_a(name));
201 /* Initial version of the Vulkan ICD spec required vkGetInstanceProcAddr to be
202 * exported. vk_icdGetInstanceProcAddr was added later to separate ICD calls from
203 * Vulkan API. One of them in our case should forward to the other, so just forward
204 * to the older vkGetInstanceProcAddr.
206 return vkGetInstanceProcAddr(instance, name);
209 VkResult WINAPI vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t *supported_version)
211 uint32_t req_version;
213 TRACE("%p\n", supported_version);
215 /* The spec is not clear how to handle this. Mesa drivers don't check, but it
216 * is probably best to not explode. VK_INCOMPLETE seems to be the closest value.
218 if (!supported_version)
219 return VK_INCOMPLETE;
221 req_version = *supported_version;
222 *supported_version = min(req_version, WINE_VULKAN_ICD_VERSION);
223 TRACE("Loader requested ICD version %u, returning %u\n", req_version, *supported_version);
225 return VK_SUCCESS;
228 static BOOL WINAPI wine_vk_init(INIT_ONCE *once, void *param, void **context)
230 if (NtQueryVirtualMemory(GetCurrentProcess(), hinstance, MemoryWineUnixFuncs,
231 &unix_handle, sizeof(unix_handle), NULL))
232 return FALSE;
234 return !vk_unix_call(unix_init, &unix_funcs);
237 static BOOL wine_vk_init_once(void)
239 static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
241 return InitOnceExecuteOnce(&init_once, wine_vk_init, NULL, NULL);
244 VkResult WINAPI vkCreateInstance(const VkInstanceCreateInfo *create_info,
245 const VkAllocationCallbacks *allocator, VkInstance *instance)
247 struct vkCreateInstance_params params;
249 TRACE("create_info %p, allocator %p, instance %p\n", create_info, allocator, instance);
251 if(!wine_vk_init_once())
252 return VK_ERROR_INITIALIZATION_FAILED;
254 params.pCreateInfo = create_info;
255 params.pAllocator = allocator;
256 params.pInstance = instance;
257 return vk_unix_call(unix_vkCreateInstance, &params);
260 VkResult WINAPI vkEnumerateInstanceExtensionProperties(const char *layer_name,
261 uint32_t *count, VkExtensionProperties *properties)
263 struct vkEnumerateInstanceExtensionProperties_params params;
265 TRACE("%p, %p, %p\n", layer_name, count, properties);
267 if (layer_name)
269 WARN("Layer enumeration not supported from ICD.\n");
270 return VK_ERROR_LAYER_NOT_PRESENT;
273 if (!wine_vk_init_once())
275 *count = 0;
276 return VK_SUCCESS;
279 params.pLayerName = layer_name;
280 params.pPropertyCount = count;
281 params.pProperties = properties;
282 return vk_unix_call(unix_vkEnumerateInstanceExtensionProperties, &params);
285 VkResult WINAPI vkEnumerateInstanceVersion(uint32_t *version)
287 struct vkEnumerateInstanceVersion_params params;
289 TRACE("%p\n", version);
291 if (!wine_vk_init_once())
293 *version = VK_API_VERSION_1_0;
294 return VK_SUCCESS;
297 params.pApiVersion = version;
298 return vk_unix_call(unix_vkEnumerateInstanceVersion, &params);
301 static HANDLE get_display_device_init_mutex(void)
303 HANDLE mutex = CreateMutexW(NULL, FALSE, L"display_device_init");
305 WaitForSingleObject(mutex, INFINITE);
306 return mutex;
309 static void release_display_device_init_mutex(HANDLE mutex)
311 ReleaseMutex(mutex);
312 CloseHandle(mutex);
315 /* Wait until graphics driver is loaded by explorer */
316 static void wait_graphics_driver_ready(void)
318 static BOOL ready = FALSE;
320 if (!ready)
322 SendMessageW(GetDesktopWindow(), WM_NULL, 0, 0);
323 ready = TRUE;
327 static void fill_luid_property(VkPhysicalDeviceProperties2 *properties2)
329 VkPhysicalDeviceVulkan11Properties *vk11;
330 VkBool32 device_luid_valid = VK_FALSE;
331 VkPhysicalDeviceIDProperties *id;
332 uint32_t device_node_mask = 0;
333 SP_DEVINFO_DATA device_data;
334 const uint8_t* device_uuid;
335 DWORD type, device_idx = 0;
336 HDEVINFO devinfo;
337 HANDLE mutex;
338 GUID uuid;
339 LUID luid;
341 vk11 = wine_vk_find_struct(properties2, PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES);
342 id = wine_vk_find_struct(properties2, PHYSICAL_DEVICE_ID_PROPERTIES);
344 if (!vk11 && !id)
345 return;
347 wait_graphics_driver_ready();
348 mutex = get_display_device_init_mutex();
349 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, L"PCI", NULL, 0);
350 device_data.cbSize = sizeof(device_data);
351 while (SetupDiEnumDeviceInfo(devinfo, device_idx++, &device_data))
353 if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_GPU_VULKAN_UUID,
354 &type, (BYTE *)&uuid, sizeof(uuid), NULL, 0))
355 continue;
357 device_uuid = id ? id->deviceUUID : vk11->deviceUUID;
359 if (!IsEqualGUID(&uuid, device_uuid))
360 continue;
362 if (SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_GPU_LUID, &type,
363 (BYTE *)&luid, sizeof(luid), NULL, 0))
365 device_luid_valid = VK_TRUE;
366 device_node_mask = 1;
368 if (id)
370 memcpy(&id->deviceLUID, &luid, sizeof(id->deviceLUID));
371 id->deviceLUIDValid = device_luid_valid;
372 id->deviceNodeMask = device_node_mask;
375 if (vk11)
377 memcpy(&vk11->deviceLUID, &luid, sizeof(vk11->deviceLUID));
378 vk11->deviceLUIDValid = device_luid_valid;
379 vk11->deviceNodeMask = device_node_mask;
381 break;
384 SetupDiDestroyDeviceInfoList(devinfo);
385 release_display_device_init_mutex(mutex);
387 TRACE("deviceName:%s deviceLUIDValid:%d LUID:%08lx:%08lx deviceNodeMask:%#x.\n",
388 properties2->properties.deviceName, device_luid_valid, luid.HighPart, luid.LowPart,
389 device_node_mask);
392 void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev,
393 VkPhysicalDeviceProperties2 *properties2)
395 struct vkGetPhysicalDeviceProperties2_params params;
397 TRACE("%p, %p\n", phys_dev, properties2);
399 params.physicalDevice = phys_dev;
400 params.pProperties = properties2;
401 vk_unix_call(unix_vkGetPhysicalDeviceProperties2, &params);
402 fill_luid_property(properties2);
405 void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev,
406 VkPhysicalDeviceProperties2 *properties2)
408 struct vkGetPhysicalDeviceProperties2KHR_params params;
410 TRACE("%p, %p\n", phys_dev, properties2);
412 params.physicalDevice = phys_dev;
413 params.pProperties = properties2;
414 vk_unix_call(unix_vkGetPhysicalDeviceProperties2KHR, &params);
415 fill_luid_property(properties2);
418 static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size )
420 return params->user_callback(params->flags, params->object_type, params->object_handle, params->location,
421 params->code, params->layer_prefix, params->message, params->user_data);
424 static BOOL WINAPI call_vulkan_debug_utils_callback( struct wine_vk_debug_utils_params *params, ULONG size )
426 return params->user_callback(params->severity, params->message_types, &params->data, params->user_data);
429 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved)
431 void **kernel_callback_table;
433 TRACE("%p, %lu, %p\n", hinst, reason, reserved);
435 switch (reason)
437 case DLL_PROCESS_ATTACH:
438 hinstance = hinst;
439 DisableThreadLibraryCalls(hinst);
441 kernel_callback_table = NtCurrentTeb()->Peb->KernelCallbackTable;
442 kernel_callback_table[NtUserCallVulkanDebugReportCallback] = call_vulkan_debug_report_callback;
443 kernel_callback_table[NtUserCallVulkanDebugUtilsCallback] = call_vulkan_debug_utils_callback;
444 break;
446 return TRUE;
449 static const WCHAR winevulkan_json_pathW[] = L"\\winevulkan.json";
450 static const WCHAR vulkan_driversW[] = L"Software\\Khronos\\Vulkan\\Drivers";
452 HRESULT WINAPI DllRegisterServer(void)
454 WCHAR json_path[MAX_PATH];
455 HRSRC rsrc;
456 const char *data;
457 DWORD datalen, written, zero = 0;
458 HANDLE file;
459 HKEY key;
461 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
462 TRACE("\n");
463 rsrc = FindResourceW(hinstance, L"winevulkan_json", (const WCHAR *)RT_RCDATA);
464 data = LockResource(LoadResource(hinstance, rsrc));
465 datalen = SizeofResource(hinstance, rsrc);
467 GetSystemDirectoryW(json_path, ARRAY_SIZE(json_path));
468 lstrcatW(json_path, winevulkan_json_pathW);
469 file = CreateFileW(json_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
470 if (file == INVALID_HANDLE_VALUE)
472 ERR("Unable to create JSON manifest.\n");
473 return E_UNEXPECTED;
475 WriteFile(file, data, datalen, &written, NULL);
476 CloseHandle(file);
478 if (!RegCreateKeyExW(HKEY_LOCAL_MACHINE, vulkan_driversW, 0, NULL, 0, KEY_SET_VALUE, NULL, &key, NULL))
480 RegSetValueExW(key, json_path, 0, REG_DWORD, (const BYTE *)&zero, sizeof(zero));
481 RegCloseKey(key);
483 return S_OK;
486 HRESULT WINAPI DllUnregisterServer(void)
488 WCHAR json_path[MAX_PATH];
489 HKEY key;
491 /* Remove the JSON manifest and registry key */
492 TRACE("\n");
493 GetSystemDirectoryW(json_path, ARRAY_SIZE(json_path));
494 lstrcatW(json_path, winevulkan_json_pathW);
495 DeleteFileW(json_path);
497 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, vulkan_driversW, 0, KEY_SET_VALUE, &key) == ERROR_SUCCESS)
499 RegDeleteValueW(key, json_path);
500 RegCloseKey(key);
503 return S_OK;