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
31 #include "vulkan_loader.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
)
63 VkResult WINAPI
wine_vkEnumerateInstanceLayerProperties(uint32_t *count
, VkLayerProperties
*properties
)
65 TRACE("%p, %p\n", count
, properties
);
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
)
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
;
96 PFN_vkVoidFunction WINAPI
wine_vkGetInstanceProcAddr(VkInstance instance
, const char *name
)
100 TRACE("%p, %s\n", instance
, debugstr_a(name
));
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
);
115 WARN("Global function %s not found.\n", debugstr_a(name
));
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
));
133 PFN_vkVoidFunction WINAPI
wine_vkGetDeviceProcAddr(VkDevice device
, const char *name
)
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
)
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
);
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 (((struct wine_vk_device_base
*)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
));
170 WARN("Unsupported device function: %s.\n", debugstr_a(name
));
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
);
212 static BOOL WINAPI
wine_vk_init(INIT_ONCE
*once
, void *param
, void **context
)
214 const struct vulkan_funcs
*driver
;
218 driver
= __wine_get_vulkan_driver(hdc
, WINE_VULKAN_DRIVER_VERSION
);
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
);
251 WARN("Layer enumeration not supported from ICD.\n");
252 return VK_ERROR_LAYER_NOT_PRESENT
;
255 if (!wine_vk_init_once())
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
;
274 return unix_funcs
->p_vkEnumerateInstanceVersion(version
);
277 static HANDLE
get_display_device_init_mutex(void)
279 HANDLE mutex
= CreateMutexW(NULL
, FALSE
, L
"display_device_init");
281 WaitForSingleObject(mutex
, INFINITE
);
285 static void release_display_device_init_mutex(HANDLE mutex
)
291 /* Wait until graphics driver is loaded by explorer */
292 static void wait_graphics_driver_ready(void)
294 static BOOL ready
= FALSE
;
298 SendMessageW(GetDesktopWindow(), WM_NULL
, 0, 0);
303 static void fill_luid_property(VkPhysicalDeviceProperties2
*properties2
)
305 VkPhysicalDeviceIDProperties
*id
;
306 SP_DEVINFO_DATA device_data
;
307 DWORD type
, device_idx
= 0;
313 if (!(id
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_ID_PROPERTIES
)))
316 wait_graphics_driver_ready();
317 mutex
= get_display_device_init_mutex();
318 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, L
"PCI", NULL
, 0);
319 device_data
.cbSize
= sizeof(device_data
);
320 while (SetupDiEnumDeviceInfo(devinfo
, device_idx
++, &device_data
))
322 if (!SetupDiGetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_GPU_VULKAN_UUID
,
323 &type
, (BYTE
*)&uuid
, sizeof(uuid
), NULL
, 0))
326 if (!IsEqualGUID(&uuid
, id
->deviceUUID
))
329 if (SetupDiGetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_GPU_LUID
, &type
,
330 (BYTE
*)&luid
, sizeof(luid
), NULL
, 0))
332 memcpy(&id
->deviceLUID
, &luid
, sizeof(id
->deviceLUID
));
333 id
->deviceLUIDValid
= VK_TRUE
;
334 id
->deviceNodeMask
= 1;
338 SetupDiDestroyDeviceInfoList(devinfo
);
339 release_display_device_init_mutex(mutex
);
341 TRACE("deviceName:%s deviceLUIDValid:%d LUID:%08x:%08x deviceNodeMask:%#x.\n",
342 properties2
->properties
.deviceName
, id
->deviceLUIDValid
, luid
.HighPart
, luid
.LowPart
,
346 void WINAPI
wine_vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev
,
347 VkPhysicalDeviceProperties2
*properties2
)
349 TRACE("%p, %p\n", phys_dev
, properties2
);
351 unix_funcs
->p_vkGetPhysicalDeviceProperties2(phys_dev
, properties2
);
352 fill_luid_property(properties2
);
355 void WINAPI
wine_vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev
,
356 VkPhysicalDeviceProperties2
*properties2
)
358 TRACE("%p, %p\n", phys_dev
, properties2
);
360 unix_funcs
->p_vkGetPhysicalDeviceProperties2KHR(phys_dev
, properties2
);
361 fill_luid_property(properties2
);
364 BOOL WINAPI
DllMain(HINSTANCE hinst
, DWORD reason
, void *reserved
)
366 TRACE("%p, %u, %p\n", hinst
, reason
, reserved
);
370 case DLL_PROCESS_ATTACH
:
372 DisableThreadLibraryCalls(hinst
);
378 static const WCHAR winevulkan_json_pathW
[] = L
"\\winevulkan.json";
379 static const WCHAR vulkan_driversW
[] = L
"Software\\Khronos\\Vulkan\\Drivers";
381 HRESULT WINAPI
DllRegisterServer(void)
383 WCHAR json_path
[MAX_PATH
];
386 DWORD datalen
, written
, zero
= 0;
390 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
392 rsrc
= FindResourceW(hinstance
, L
"winevulkan_json", (const WCHAR
*)RT_RCDATA
);
393 data
= LockResource(LoadResource(hinstance
, rsrc
));
394 datalen
= SizeofResource(hinstance
, rsrc
);
396 GetSystemDirectoryW(json_path
, ARRAY_SIZE(json_path
));
397 lstrcatW(json_path
, winevulkan_json_pathW
);
398 file
= CreateFileW(json_path
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
399 if (file
== INVALID_HANDLE_VALUE
)
401 ERR("Unable to create JSON manifest.\n");
404 WriteFile(file
, data
, datalen
, &written
, NULL
);
407 if (!RegCreateKeyExW(HKEY_LOCAL_MACHINE
, vulkan_driversW
, 0, NULL
, 0, KEY_SET_VALUE
, NULL
, &key
, NULL
))
409 RegSetValueExW(key
, json_path
, 0, REG_DWORD
, (const BYTE
*)&zero
, sizeof(zero
));
415 HRESULT WINAPI
DllUnregisterServer(void)
417 WCHAR json_path
[MAX_PATH
];
420 /* Remove the JSON manifest and registry key */
422 GetSystemDirectoryW(json_path
, ARRAY_SIZE(json_path
));
423 lstrcatW(json_path
, winevulkan_json_pathW
);
424 DeleteFileW(json_path
);
426 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, vulkan_driversW
, 0, KEY_SET_VALUE
, &key
) == ERROR_SUCCESS
)
428 RegDeleteValueW(key
, json_path
);