winevulkan: Move Vulkan calls to Unix lib.
[wine.git] / dlls / winevulkan / loader.c
blob769724fe20ddb369dce544d54787b623ed565c8a
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 <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winternl.h"
25 #include "winreg.h"
26 #include "winuser.h"
27 #include "initguid.h"
28 #include "devguid.h"
29 #include "setupapi.h"
31 #include "vulkan_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(vulkan);
35 /* For now default to 4 as it felt like a reasonable version feature wise to support.
36 * Version 5 adds more extensive version checks. Something to tackle later.
38 #define WINE_VULKAN_ICD_VERSION 4
40 DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
41 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_GPU_VULKAN_UUID, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5c, 2);
43 const struct unix_funcs *unix_funcs;
45 static HINSTANCE hinstance;
47 static void *wine_vk_get_global_proc_addr(const char *name);
49 #define wine_vk_find_struct(s, t) wine_vk_find_struct_((void *)s, VK_STRUCTURE_TYPE_##t)
50 static void *wine_vk_find_struct_(void *s, VkStructureType t)
52 VkBaseOutStructure *header;
54 for (header = s; header; header = header->pNext)
56 if (header->sType == t)
57 return header;
60 return NULL;
63 VkResult WINAPI wine_vkEnumerateInstanceLayerProperties(uint32_t *count, VkLayerProperties *properties)
65 TRACE("%p, %p\n", count, properties);
67 *count = 0;
68 return VK_SUCCESS;
71 static const struct vulkan_func vk_global_dispatch_table[] =
73 /* These functions must call wine_vk_init_once() before accessing vk_funcs. */
74 {"vkCreateInstance", &wine_vkCreateInstance},
75 {"vkEnumerateInstanceExtensionProperties", &wine_vkEnumerateInstanceExtensionProperties},
76 {"vkEnumerateInstanceLayerProperties", &wine_vkEnumerateInstanceLayerProperties},
77 {"vkEnumerateInstanceVersion", &wine_vkEnumerateInstanceVersion},
78 {"vkGetInstanceProcAddr", &wine_vkGetInstanceProcAddr},
81 static void *wine_vk_get_global_proc_addr(const char *name)
83 unsigned int i;
85 for (i = 0; i < ARRAY_SIZE(vk_global_dispatch_table); i++)
87 if (strcmp(name, vk_global_dispatch_table[i].name) == 0)
89 TRACE("Found name=%s in global table\n", debugstr_a(name));
90 return vk_global_dispatch_table[i].func;
93 return NULL;
96 PFN_vkVoidFunction WINAPI wine_vkGetInstanceProcAddr(VkInstance instance, const char *name)
98 void *func;
100 TRACE("%p, %s\n", instance, debugstr_a(name));
102 if (!name)
103 return NULL;
105 /* vkGetInstanceProcAddr can load most Vulkan functions when an instance is passed in, however
106 * for a NULL instance it can only load global functions.
108 func = wine_vk_get_global_proc_addr(name);
109 if (func)
111 return func;
113 if (!instance)
115 WARN("Global function %s not found.\n", debugstr_a(name));
116 return NULL;
119 func = wine_vk_get_instance_proc_addr(name);
120 if (func) return func;
122 func = wine_vk_get_phys_dev_proc_addr(name);
123 if (func) return func;
125 /* vkGetInstanceProcAddr also loads any children of instance, so device functions as well. */
126 func = wine_vk_get_device_proc_addr(name);
127 if (func) return func;
129 WARN("Unsupported device or instance function: %s.\n", debugstr_a(name));
130 return NULL;
133 PFN_vkVoidFunction WINAPI wine_vkGetDeviceProcAddr(VkDevice device, const char *name)
135 void *func;
136 TRACE("%p, %s\n", device, debugstr_a(name));
138 /* The spec leaves return value undefined for a NULL device, let's just return NULL. */
139 if (!device || !name)
140 return NULL;
142 /* Per the spec, we are only supposed to return device functions as in functions
143 * for which the first parameter is vkDevice or a child of vkDevice like a
144 * vkCommandBuffer or vkQueue.
145 * Loader takes care of filtering of extensions which are enabled or not.
147 func = wine_vk_get_device_proc_addr(name);
148 if (func)
149 return func;
151 /* vkGetDeviceProcAddr was intended for loading device and subdevice functions.
152 * idTech 6 titles such as Doom and Wolfenstein II, however use it also for
153 * loading of instance functions. This is undefined behavior as the specification
154 * disallows using any of the returned function pointers outside of device /
155 * subdevice objects. The games don't actually use the function pointers and if they
156 * did, they would crash as VkInstance / VkPhysicalDevice parameters need unwrapping.
157 * Khronos clarified behavior in the Vulkan spec and expects drivers to get updated,
158 * however it would require both driver and game fixes.
159 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/2323
160 * https://github.com/KhronosGroup/Vulkan-Docs/issues/655
162 if (device->quirks & WINEVULKAN_QUIRK_GET_DEVICE_PROC_ADDR
163 && ((func = wine_vk_get_instance_proc_addr(name))
164 || (func = wine_vk_get_phys_dev_proc_addr(name))))
166 WARN("Returning instance function %s.\n", debugstr_a(name));
167 return func;
170 WARN("Unsupported device function: %s.\n", debugstr_a(name));
171 return NULL;
174 void * WINAPI wine_vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char *name)
176 TRACE("%p, %s\n", instance, debugstr_a(name));
178 return wine_vk_get_phys_dev_proc_addr(name);
181 void * WINAPI wine_vk_icdGetInstanceProcAddr(VkInstance instance, const char *name)
183 TRACE("%p, %s\n", instance, debugstr_a(name));
185 /* Initial version of the Vulkan ICD spec required vkGetInstanceProcAddr to be
186 * exported. vk_icdGetInstanceProcAddr was added later to separate ICD calls from
187 * Vulkan API. One of them in our case should forward to the other, so just forward
188 * to the older vkGetInstanceProcAddr.
190 return wine_vkGetInstanceProcAddr(instance, name);
193 VkResult WINAPI wine_vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t *supported_version)
195 uint32_t req_version;
197 TRACE("%p\n", supported_version);
199 /* The spec is not clear how to handle this. Mesa drivers don't check, but it
200 * is probably best to not explode. VK_INCOMPLETE seems to be the closest value.
202 if (!supported_version)
203 return VK_INCOMPLETE;
205 req_version = *supported_version;
206 *supported_version = min(req_version, WINE_VULKAN_ICD_VERSION);
207 TRACE("Loader requested ICD version %u, returning %u\n", req_version, *supported_version);
209 return VK_SUCCESS;
212 static BOOL WINAPI wine_vk_init(INIT_ONCE *once, void *param, void **context)
214 const struct vulkan_funcs *driver;
215 HDC hdc;
217 hdc = GetDC(0);
218 driver = __wine_get_vulkan_driver(hdc, WINE_VULKAN_DRIVER_VERSION);
219 ReleaseDC(0, hdc);
220 if (!driver)
221 ERR("Failed to load Wine graphics driver supporting Vulkan.\n");
223 return driver && !__wine_init_unix_lib(hinstance, DLL_PROCESS_ATTACH, driver, &unix_funcs);
226 static BOOL wine_vk_init_once(void)
228 static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
230 return InitOnceExecuteOnce(&init_once, wine_vk_init, NULL, NULL);
233 VkResult WINAPI wine_vkCreateInstance(const VkInstanceCreateInfo *create_info,
234 const VkAllocationCallbacks *allocator, VkInstance *instance)
236 TRACE("create_info %p, allocator %p, instance %p\n", create_info, allocator, instance);
238 if(!wine_vk_init_once())
239 return VK_ERROR_INITIALIZATION_FAILED;
241 return unix_funcs->p_vkCreateInstance(create_info, allocator, instance);
244 VkResult WINAPI wine_vkEnumerateInstanceExtensionProperties(const char *layer_name,
245 uint32_t *count, VkExtensionProperties *properties)
247 TRACE("%p, %p, %p\n", layer_name, count, properties);
249 if (layer_name)
251 WARN("Layer enumeration not supported from ICD.\n");
252 return VK_ERROR_LAYER_NOT_PRESENT;
255 if (!wine_vk_init_once())
257 *count = 0;
258 return VK_SUCCESS;
261 return unix_funcs->p_vkEnumerateInstanceExtensionProperties(layer_name, count, properties);
264 VkResult WINAPI wine_vkEnumerateInstanceVersion(uint32_t *version)
266 TRACE("%p\n", version);
268 if (!wine_vk_init_once())
270 *version = VK_API_VERSION_1_0;
271 return VK_SUCCESS;
274 return unix_funcs->p_vkEnumerateInstanceVersion(version);
277 static HANDLE get_display_device_init_mutex(void)
279 static const WCHAR init_mutexW[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0};
280 HANDLE mutex = CreateMutexW(NULL, FALSE, init_mutexW);
282 WaitForSingleObject(mutex, INFINITE);
283 return mutex;
286 static void release_display_device_init_mutex(HANDLE mutex)
288 ReleaseMutex(mutex);
289 CloseHandle(mutex);
292 /* Wait until graphics driver is loaded by explorer */
293 static void wait_graphics_driver_ready(void)
295 static BOOL ready = FALSE;
297 if (!ready)
299 SendMessageW(GetDesktopWindow(), WM_NULL, 0, 0);
300 ready = TRUE;
304 static void fill_luid_property(VkPhysicalDeviceProperties2 *properties2)
306 static const WCHAR pci[] = {'P','C','I',0};
307 VkPhysicalDeviceIDProperties *id;
308 SP_DEVINFO_DATA device_data;
309 DWORD type, device_idx = 0;
310 HDEVINFO devinfo;
311 HANDLE mutex;
312 GUID uuid;
313 LUID luid;
315 if (!(id = wine_vk_find_struct(properties2, PHYSICAL_DEVICE_ID_PROPERTIES)))
316 return;
318 wait_graphics_driver_ready();
319 mutex = get_display_device_init_mutex();
320 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, pci, NULL, 0);
321 device_data.cbSize = sizeof(device_data);
322 while (SetupDiEnumDeviceInfo(devinfo, device_idx++, &device_data))
324 if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_GPU_VULKAN_UUID,
325 &type, (BYTE *)&uuid, sizeof(uuid), NULL, 0))
326 continue;
328 if (!IsEqualGUID(&uuid, id->deviceUUID))
329 continue;
331 if (SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_GPU_LUID, &type,
332 (BYTE *)&luid, sizeof(luid), NULL, 0))
334 memcpy(&id->deviceLUID, &luid, sizeof(id->deviceLUID));
335 id->deviceLUIDValid = VK_TRUE;
336 id->deviceNodeMask = 1;
337 break;
340 SetupDiDestroyDeviceInfoList(devinfo);
341 release_display_device_init_mutex(mutex);
343 TRACE("deviceName:%s deviceLUIDValid:%d LUID:%08x:%08x deviceNodeMask:%#x.\n",
344 properties2->properties.deviceName, id->deviceLUIDValid, luid.HighPart, luid.LowPart,
345 id->deviceNodeMask);
348 void WINAPI wine_vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev,
349 VkPhysicalDeviceProperties2 *properties2)
351 TRACE("%p, %p\n", phys_dev, properties2);
353 unix_funcs->p_vkGetPhysicalDeviceProperties2(phys_dev, properties2);
354 fill_luid_property(properties2);
357 void WINAPI wine_vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev,
358 VkPhysicalDeviceProperties2 *properties2)
360 TRACE("%p, %p\n", phys_dev, properties2);
362 unix_funcs->p_vkGetPhysicalDeviceProperties2KHR(phys_dev, properties2);
363 fill_luid_property(properties2);
366 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved)
368 TRACE("%p, %u, %p\n", hinst, reason, reserved);
370 switch (reason)
372 case DLL_PROCESS_ATTACH:
373 hinstance = hinst;
374 DisableThreadLibraryCalls(hinst);
375 break;
377 return TRUE;
380 static const WCHAR winevulkan_json_resW[] = {'w','i','n','e','v','u','l','k','a','n','_','j','s','o','n',0};
381 static const WCHAR winevulkan_json_pathW[] = {'\\','w','i','n','e','v','u','l','k','a','n','.','j','s','o','n',0};
382 static const WCHAR vulkan_driversW[] = {'S','o','f','t','w','a','r','e','\\','K','h','r','o','n','o','s','\\',
383 'V','u','l','k','a','n','\\','D','r','i','v','e','r','s',0};
385 HRESULT WINAPI DllRegisterServer(void)
387 WCHAR json_path[MAX_PATH];
388 HRSRC rsrc;
389 const char *data;
390 DWORD datalen, written, zero = 0;
391 HANDLE file;
392 HKEY key;
394 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
395 TRACE("\n");
396 rsrc = FindResourceW(hinstance, winevulkan_json_resW, (const WCHAR *)RT_RCDATA);
397 data = LockResource(LoadResource(hinstance, rsrc));
398 datalen = SizeofResource(hinstance, rsrc);
400 GetSystemDirectoryW(json_path, ARRAY_SIZE(json_path));
401 lstrcatW(json_path, winevulkan_json_pathW);
402 file = CreateFileW(json_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
403 if (file == INVALID_HANDLE_VALUE)
405 ERR("Unable to create JSON manifest.\n");
406 return E_UNEXPECTED;
408 WriteFile(file, data, datalen, &written, NULL);
409 CloseHandle(file);
411 if (!RegCreateKeyExW(HKEY_LOCAL_MACHINE, vulkan_driversW, 0, NULL, 0, KEY_SET_VALUE, NULL, &key, NULL))
413 RegSetValueExW(key, json_path, 0, REG_DWORD, (const BYTE *)&zero, sizeof(zero));
414 RegCloseKey(key);
416 return S_OK;
419 HRESULT WINAPI DllUnregisterServer(void)
421 WCHAR json_path[MAX_PATH];
422 HKEY key;
424 /* Remove the JSON manifest and registry key */
425 TRACE("\n");
426 GetSystemDirectoryW(json_path, ARRAY_SIZE(json_path));
427 lstrcatW(json_path, winevulkan_json_pathW);
428 DeleteFileW(json_path);
430 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, vulkan_driversW, 0, KEY_SET_VALUE, &key) == ERROR_SUCCESS)
432 RegDeleteValueW(key, json_path);
433 RegCloseKey(key);
436 return S_OK;