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 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
)
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 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
;
110 PFN_vkVoidFunction WINAPI
vkGetInstanceProcAddr(VkInstance instance
, const char *name
)
114 TRACE("%p, %s\n", instance
, debugstr_a(name
));
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
);
129 WARN("Global function %s not found.\n", debugstr_a(name
));
133 if (!is_available_instance_function(instance
, name
))
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
));
150 PFN_vkVoidFunction WINAPI
vkGetDeviceProcAddr(VkDevice device
, const char *name
)
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
)
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
);
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
));
190 WARN("Unsupported device function: %s.\n", debugstr_a(name
));
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
))
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
);
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
))
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
;
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
, ¶ms
);
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
);
278 WARN("Layer enumeration not supported from ICD.\n");
279 return VK_ERROR_LAYER_NOT_PRESENT
;
282 if (!wine_vk_init_once())
288 params
.pLayerName
= layer_name
;
289 params
.pPropertyCount
= count
;
290 params
.pProperties
= properties
;
291 return vk_unix_call(unix_vkEnumerateInstanceExtensionProperties
, ¶ms
);
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
;
306 params
.pApiVersion
= version
;
307 return vk_unix_call(unix_vkEnumerateInstanceVersion
, ¶ms
);
310 static HANDLE
get_display_device_init_mutex(void)
312 HANDLE mutex
= CreateMutexW(NULL
, FALSE
, L
"display_device_init");
314 WaitForSingleObject(mutex
, INFINITE
);
318 static void release_display_device_init_mutex(HANDLE mutex
)
324 /* Wait until graphics driver is loaded by explorer */
325 static void wait_graphics_driver_ready(void)
327 static BOOL ready
= FALSE
;
331 SendMessageW(GetDesktopWindow(), WM_NULL
, 0, 0);
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;
350 vk11
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES
);
351 id
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_ID_PROPERTIES
);
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))
366 device_uuid
= id
? id
->deviceUUID
: vk11
->deviceUUID
;
368 if (!IsEqualGUID(&uuid
, device_uuid
))
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;
379 memcpy(&id
->deviceLUID
, &luid
, sizeof(id
->deviceLUID
));
380 id
->deviceLUIDValid
= device_luid_valid
;
381 id
->deviceNodeMask
= device_node_mask
;
386 memcpy(&vk11
->deviceLUID
, &luid
, sizeof(vk11
->deviceLUID
));
387 vk11
->deviceLUIDValid
= device_luid_valid
;
388 vk11
->deviceNodeMask
= device_node_mask
;
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
,
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
, ¶ms
);
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
, ¶ms
);
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
;
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
, ¶ms
);
448 if (!device
->base
.unix_handle
)
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
, ¶ms
);
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
, ¶ms
->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
);
482 case DLL_PROCESS_ATTACH
:
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
;
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
];
502 DWORD datalen
, written
, zero
= 0;
506 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
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");
520 WriteFile(file
, data
, datalen
, &written
, NULL
);
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
));
531 HRESULT WINAPI
DllUnregisterServer(void)
533 WCHAR json_path
[MAX_PATH
];
536 /* Remove the JSON manifest and registry key */
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
);