winevulkan: Separate PE and Unix VkQueue structs.
[wine.git] / dlls / winevulkan / loader.c
blob3e92ba0396fcc27398d719ec44d321ab48b029e7
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 NTSTATUS (WINAPI *p_vk_direct_unix_call)(unixlib_handle_t handle, unsigned int code, void *args);
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 static void *alloc_vk_object(size_t size)
105 struct wine_vk_base *object = calloc(1, size);
106 object->loader_magic = VULKAN_ICD_MAGIC_VALUE;
107 return object;
110 PFN_vkVoidFunction WINAPI vkGetInstanceProcAddr(VkInstance instance, const char *name)
112 void *func;
114 TRACE("%p, %s\n", instance, debugstr_a(name));
116 if (!name)
117 return NULL;
119 /* vkGetInstanceProcAddr can load most Vulkan functions when an instance is passed in, however
120 * for a NULL instance it can only load global functions.
122 func = wine_vk_get_global_proc_addr(name);
123 if (func)
125 return func;
127 if (!instance)
129 WARN("Global function %s not found.\n", debugstr_a(name));
130 return NULL;
133 if (!is_available_instance_function(instance, name))
134 return NULL;
136 func = wine_vk_get_instance_proc_addr(name);
137 if (func) return func;
139 func = wine_vk_get_phys_dev_proc_addr(name);
140 if (func) return func;
142 /* vkGetInstanceProcAddr also loads any children of instance, so device functions as well. */
143 func = wine_vk_get_device_proc_addr(name);
144 if (func) return func;
146 WARN("Unsupported device or instance function: %s.\n", debugstr_a(name));
147 return NULL;
150 PFN_vkVoidFunction WINAPI vkGetDeviceProcAddr(VkDevice device, const char *name)
152 void *func;
153 TRACE("%p, %s\n", device, debugstr_a(name));
155 /* The spec leaves return value undefined for a NULL device, let's just return NULL. */
156 if (!device || !name)
157 return NULL;
159 /* Per the spec, we are only supposed to return device functions as in functions
160 * for which the first parameter is vkDevice or a child of vkDevice like a
161 * vkCommandBuffer or vkQueue.
162 * Loader takes care of filtering of extensions which are enabled or not.
164 if (is_available_device_function(device, name))
166 func = wine_vk_get_device_proc_addr(name);
167 if (func)
168 return func;
171 /* vkGetDeviceProcAddr was intended for loading device and subdevice functions.
172 * idTech 6 titles such as Doom and Wolfenstein II, however use it also for
173 * loading of instance functions. This is undefined behavior as the specification
174 * disallows using any of the returned function pointers outside of device /
175 * subdevice objects. The games don't actually use the function pointers and if they
176 * did, they would crash as VkInstance / VkPhysicalDevice parameters need unwrapping.
177 * Khronos clarified behavior in the Vulkan spec and expects drivers to get updated,
178 * however it would require both driver and game fixes.
179 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/2323
180 * https://github.com/KhronosGroup/Vulkan-Docs/issues/655
182 if ((device->quirks & WINEVULKAN_QUIRK_GET_DEVICE_PROC_ADDR)
183 && ((func = wine_vk_get_instance_proc_addr(name))
184 || (func = wine_vk_get_phys_dev_proc_addr(name))))
186 WARN("Returning instance function %s.\n", debugstr_a(name));
187 return func;
190 WARN("Unsupported device function: %s.\n", debugstr_a(name));
191 return NULL;
194 void * WINAPI vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char *name)
196 TRACE("%p, %s\n", instance, debugstr_a(name));
198 if (!is_available_instance_function(instance, name))
199 return NULL;
201 return wine_vk_get_phys_dev_proc_addr(name);
204 void * WINAPI vk_icdGetInstanceProcAddr(VkInstance instance, const char *name)
206 TRACE("%p, %s\n", instance, debugstr_a(name));
208 /* Initial version of the Vulkan ICD spec required vkGetInstanceProcAddr to be
209 * exported. vk_icdGetInstanceProcAddr was added later to separate ICD calls from
210 * Vulkan API. One of them in our case should forward to the other, so just forward
211 * to the older vkGetInstanceProcAddr.
213 return vkGetInstanceProcAddr(instance, name);
216 VkResult WINAPI vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t *supported_version)
218 uint32_t req_version;
220 TRACE("%p\n", supported_version);
222 /* The spec is not clear how to handle this. Mesa drivers don't check, but it
223 * is probably best to not explode. VK_INCOMPLETE seems to be the closest value.
225 if (!supported_version)
226 return VK_INCOMPLETE;
228 req_version = *supported_version;
229 *supported_version = min(req_version, WINE_VULKAN_ICD_VERSION);
230 TRACE("Loader requested ICD version %u, returning %u\n", req_version, *supported_version);
232 return VK_SUCCESS;
235 static BOOL WINAPI wine_vk_init(INIT_ONCE *once, void *param, void **context)
237 if (NtQueryVirtualMemory(GetCurrentProcess(), hinstance, MemoryWineUnixFuncs,
238 &unix_handle, sizeof(unix_handle), NULL))
239 return FALSE;
241 if (vk_unix_call(unix_init, &p_vk_direct_unix_call)) return FALSE;
242 if (!p_vk_direct_unix_call) p_vk_direct_unix_call = __wine_unix_call;
243 return TRUE;
246 static BOOL wine_vk_init_once(void)
248 static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
250 return InitOnceExecuteOnce(&init_once, wine_vk_init, NULL, NULL);
253 VkResult WINAPI vkCreateInstance(const VkInstanceCreateInfo *create_info,
254 const VkAllocationCallbacks *allocator, VkInstance *instance)
256 struct vkCreateInstance_params params;
258 TRACE("create_info %p, allocator %p, instance %p\n", create_info, allocator, instance);
260 if(!wine_vk_init_once())
261 return VK_ERROR_INITIALIZATION_FAILED;
263 params.pCreateInfo = create_info;
264 params.pAllocator = allocator;
265 params.pInstance = instance;
266 return vk_unix_call(unix_vkCreateInstance, &params);
269 VkResult WINAPI vkEnumerateInstanceExtensionProperties(const char *layer_name,
270 uint32_t *count, VkExtensionProperties *properties)
272 struct vkEnumerateInstanceExtensionProperties_params params;
274 TRACE("%p, %p, %p\n", layer_name, count, properties);
276 if (layer_name)
278 WARN("Layer enumeration not supported from ICD.\n");
279 return VK_ERROR_LAYER_NOT_PRESENT;
282 if (!wine_vk_init_once())
284 *count = 0;
285 return VK_SUCCESS;
288 params.pLayerName = layer_name;
289 params.pPropertyCount = count;
290 params.pProperties = properties;
291 return vk_unix_call(unix_vkEnumerateInstanceExtensionProperties, &params);
294 VkResult WINAPI vkEnumerateInstanceVersion(uint32_t *version)
296 struct vkEnumerateInstanceVersion_params params;
298 TRACE("%p\n", version);
300 if (!wine_vk_init_once())
302 *version = VK_API_VERSION_1_0;
303 return VK_SUCCESS;
306 params.pApiVersion = version;
307 return vk_unix_call(unix_vkEnumerateInstanceVersion, &params);
310 static HANDLE get_display_device_init_mutex(void)
312 HANDLE mutex = CreateMutexW(NULL, FALSE, L"display_device_init");
314 WaitForSingleObject(mutex, INFINITE);
315 return mutex;
318 static void release_display_device_init_mutex(HANDLE mutex)
320 ReleaseMutex(mutex);
321 CloseHandle(mutex);
324 /* Wait until graphics driver is loaded by explorer */
325 static void wait_graphics_driver_ready(void)
327 static BOOL ready = FALSE;
329 if (!ready)
331 SendMessageW(GetDesktopWindow(), WM_NULL, 0, 0);
332 ready = TRUE;
336 static void fill_luid_property(VkPhysicalDeviceProperties2 *properties2)
338 VkPhysicalDeviceVulkan11Properties *vk11;
339 VkBool32 device_luid_valid = VK_FALSE;
340 VkPhysicalDeviceIDProperties *id;
341 uint32_t device_node_mask = 0;
342 SP_DEVINFO_DATA device_data;
343 const uint8_t* device_uuid;
344 DWORD type, device_idx = 0;
345 HDEVINFO devinfo;
346 HANDLE mutex;
347 GUID uuid;
348 LUID luid;
350 vk11 = wine_vk_find_struct(properties2, PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES);
351 id = wine_vk_find_struct(properties2, PHYSICAL_DEVICE_ID_PROPERTIES);
353 if (!vk11 && !id)
354 return;
356 wait_graphics_driver_ready();
357 mutex = get_display_device_init_mutex();
358 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, L"PCI", NULL, 0);
359 device_data.cbSize = sizeof(device_data);
360 while (SetupDiEnumDeviceInfo(devinfo, device_idx++, &device_data))
362 if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_GPU_VULKAN_UUID,
363 &type, (BYTE *)&uuid, sizeof(uuid), NULL, 0))
364 continue;
366 device_uuid = id ? id->deviceUUID : vk11->deviceUUID;
368 if (!IsEqualGUID(&uuid, device_uuid))
369 continue;
371 if (SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_GPU_LUID, &type,
372 (BYTE *)&luid, sizeof(luid), NULL, 0))
374 device_luid_valid = VK_TRUE;
375 device_node_mask = 1;
377 if (id)
379 memcpy(&id->deviceLUID, &luid, sizeof(id->deviceLUID));
380 id->deviceLUIDValid = device_luid_valid;
381 id->deviceNodeMask = device_node_mask;
384 if (vk11)
386 memcpy(&vk11->deviceLUID, &luid, sizeof(vk11->deviceLUID));
387 vk11->deviceLUIDValid = device_luid_valid;
388 vk11->deviceNodeMask = device_node_mask;
390 break;
393 SetupDiDestroyDeviceInfoList(devinfo);
394 release_display_device_init_mutex(mutex);
396 TRACE("deviceName:%s deviceLUIDValid:%d LUID:%08lx:%08lx deviceNodeMask:%#x.\n",
397 properties2->properties.deviceName, device_luid_valid, luid.HighPart, luid.LowPart,
398 device_node_mask);
401 void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev,
402 VkPhysicalDeviceProperties2 *properties2)
404 struct vkGetPhysicalDeviceProperties2_params params;
406 TRACE("%p, %p\n", phys_dev, properties2);
408 params.physicalDevice = phys_dev;
409 params.pProperties = properties2;
410 vk_unix_call(unix_vkGetPhysicalDeviceProperties2, &params);
411 fill_luid_property(properties2);
414 void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev,
415 VkPhysicalDeviceProperties2 *properties2)
417 struct vkGetPhysicalDeviceProperties2KHR_params params;
419 TRACE("%p, %p\n", phys_dev, properties2);
421 params.physicalDevice = phys_dev;
422 params.pProperties = properties2;
423 vk_unix_call(unix_vkGetPhysicalDeviceProperties2KHR, &params);
424 fill_luid_property(properties2);
427 VkResult WINAPI vkCreateDevice(VkPhysicalDevice phys_dev, const VkDeviceCreateInfo *create_info,
428 const VkAllocationCallbacks *allocator, VkDevice *ret)
430 struct vkCreateDevice_params params;
431 uint32_t queue_count = 0, i;
432 VkDevice device;
433 VkResult result;
435 for (i = 0; i < create_info->queueCreateInfoCount; i++)
436 queue_count += create_info->pQueueCreateInfos[i].queueCount;
437 if (!(device = alloc_vk_object(FIELD_OFFSET(struct VkDevice_T, queues[queue_count]))))
438 return VK_ERROR_OUT_OF_HOST_MEMORY;
439 for (i = 0; i < queue_count; i++)
440 device->queues[i].base.loader_magic = VULKAN_ICD_MAGIC_VALUE;
442 params.physicalDevice = phys_dev;
443 params.pCreateInfo = create_info;
444 params.pAllocator = allocator;
445 params.pDevice = ret;
446 params.client_ptr = device;
447 result = vk_unix_call(unix_vkCreateDevice, &params);
448 if (!device->base.unix_handle)
449 free(device);
450 return result;
453 void WINAPI vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *allocator)
455 struct vkDestroyDevice_params params;
457 params.device = device;
458 params.pAllocator = allocator;
459 vk_unix_call(unix_vkDestroyDevice, &params);
460 free(device);
463 static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size )
465 return params->user_callback(params->flags, params->object_type, params->object_handle, params->location,
466 params->code, params->layer_prefix, params->message, params->user_data);
469 static BOOL WINAPI call_vulkan_debug_utils_callback( struct wine_vk_debug_utils_params *params, ULONG size )
471 return params->user_callback(params->severity, params->message_types, &params->data, params->user_data);
474 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved)
476 void **kernel_callback_table;
478 TRACE("%p, %lu, %p\n", hinst, reason, reserved);
480 switch (reason)
482 case DLL_PROCESS_ATTACH:
483 hinstance = hinst;
484 DisableThreadLibraryCalls(hinst);
486 kernel_callback_table = NtCurrentTeb()->Peb->KernelCallbackTable;
487 kernel_callback_table[NtUserCallVulkanDebugReportCallback] = call_vulkan_debug_report_callback;
488 kernel_callback_table[NtUserCallVulkanDebugUtilsCallback] = call_vulkan_debug_utils_callback;
489 break;
491 return TRUE;
494 static const WCHAR winevulkan_json_pathW[] = L"\\winevulkan.json";
495 static const WCHAR vulkan_driversW[] = L"Software\\Khronos\\Vulkan\\Drivers";
497 HRESULT WINAPI DllRegisterServer(void)
499 WCHAR json_path[MAX_PATH];
500 HRSRC rsrc;
501 const char *data;
502 DWORD datalen, written, zero = 0;
503 HANDLE file;
504 HKEY key;
506 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
507 TRACE("\n");
508 rsrc = FindResourceW(hinstance, L"winevulkan_json", (const WCHAR *)RT_RCDATA);
509 data = LockResource(LoadResource(hinstance, rsrc));
510 datalen = SizeofResource(hinstance, rsrc);
512 GetSystemDirectoryW(json_path, ARRAY_SIZE(json_path));
513 lstrcatW(json_path, winevulkan_json_pathW);
514 file = CreateFileW(json_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
515 if (file == INVALID_HANDLE_VALUE)
517 ERR("Unable to create JSON manifest.\n");
518 return E_UNEXPECTED;
520 WriteFile(file, data, datalen, &written, NULL);
521 CloseHandle(file);
523 if (!RegCreateKeyExW(HKEY_LOCAL_MACHINE, vulkan_driversW, 0, NULL, 0, KEY_SET_VALUE, NULL, &key, NULL))
525 RegSetValueExW(key, json_path, 0, REG_DWORD, (const BYTE *)&zero, sizeof(zero));
526 RegCloseKey(key);
528 return S_OK;
531 HRESULT WINAPI DllUnregisterServer(void)
533 WCHAR json_path[MAX_PATH];
534 HKEY key;
536 /* Remove the JSON manifest and registry key */
537 TRACE("\n");
538 GetSystemDirectoryW(json_path, ARRAY_SIZE(json_path));
539 lstrcatW(json_path, winevulkan_json_pathW);
540 DeleteFileW(json_path);
542 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, vulkan_driversW, 0, KEY_SET_VALUE, &key) == ERROR_SUCCESS)
544 RegDeleteValueW(key, json_path);
545 RegCloseKey(key);
548 return S_OK;