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"
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
)
58 VkResult WINAPI
vkEnumerateInstanceLayerProperties(uint32_t *count
, VkLayerProperties
*properties
)
60 TRACE("%p, %p\n", count
, properties
);
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
)
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
;
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
, ¶ms
);
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
, ¶ms
);
103 PFN_vkVoidFunction WINAPI
vkGetInstanceProcAddr(VkInstance instance
, const char *name
)
107 TRACE("%p, %s\n", instance
, debugstr_a(name
));
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
);
122 WARN("Global function %s not found.\n", debugstr_a(name
));
126 if (!is_available_instance_function(instance
, name
))
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
));
143 PFN_vkVoidFunction WINAPI
vkGetDeviceProcAddr(VkDevice device
, const char *name
)
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
)
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
);
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
));
183 WARN("Unsupported device function: %s.\n", debugstr_a(name
));
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
))
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
);
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
))
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
, ¶ms
);
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
);
269 WARN("Layer enumeration not supported from ICD.\n");
270 return VK_ERROR_LAYER_NOT_PRESENT
;
273 if (!wine_vk_init_once())
279 params
.pLayerName
= layer_name
;
280 params
.pPropertyCount
= count
;
281 params
.pProperties
= properties
;
282 return vk_unix_call(unix_vkEnumerateInstanceExtensionProperties
, ¶ms
);
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
;
297 params
.pApiVersion
= version
;
298 return vk_unix_call(unix_vkEnumerateInstanceVersion
, ¶ms
);
301 static HANDLE
get_display_device_init_mutex(void)
303 HANDLE mutex
= CreateMutexW(NULL
, FALSE
, L
"display_device_init");
305 WaitForSingleObject(mutex
, INFINITE
);
309 static void release_display_device_init_mutex(HANDLE mutex
)
315 /* Wait until graphics driver is loaded by explorer */
316 static void wait_graphics_driver_ready(void)
318 static BOOL ready
= FALSE
;
322 SendMessageW(GetDesktopWindow(), WM_NULL
, 0, 0);
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;
341 vk11
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES
);
342 id
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_ID_PROPERTIES
);
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))
357 device_uuid
= id
? id
->deviceUUID
: vk11
->deviceUUID
;
359 if (!IsEqualGUID(&uuid
, device_uuid
))
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;
370 memcpy(&id
->deviceLUID
, &luid
, sizeof(id
->deviceLUID
));
371 id
->deviceLUIDValid
= device_luid_valid
;
372 id
->deviceNodeMask
= device_node_mask
;
377 memcpy(&vk11
->deviceLUID
, &luid
, sizeof(vk11
->deviceLUID
));
378 vk11
->deviceLUIDValid
= device_luid_valid
;
379 vk11
->deviceNodeMask
= device_node_mask
;
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
,
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
, ¶ms
);
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
, ¶ms
);
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
, ¶ms
->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
);
437 case DLL_PROCESS_ATTACH
:
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
;
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
];
457 DWORD datalen
, written
, zero
= 0;
461 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
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");
475 WriteFile(file
, data
, datalen
, &written
, NULL
);
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
));
486 HRESULT WINAPI
DllUnregisterServer(void)
488 WCHAR json_path
[MAX_PATH
];
491 /* Remove the JSON manifest and registry key */
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
);