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
21 #include "vulkan_loader.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(vulkan
);
30 /* For now default to 4 as it felt like a reasonable version feature wise to support.
31 * Version 5 adds more extensive version checks. Something to tackle later.
33 #define WINE_VULKAN_ICD_VERSION 4
35 DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID
, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
36 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_GPU_VULKAN_UUID
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5c, 2);
38 NTSTATUS (WINAPI
*p_vk_direct_unix_call
)(unixlib_handle_t handle
, unsigned int code
, void *args
) = NULL
;
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 NTSTATUS status
= __wine_init_unix_call();
239 p_vk_direct_unix_call
= __wine_unix_call_ptr
;
240 if (status
) return FALSE
;
241 return !vk_unix_call(unix_init
, &p_vk_direct_unix_call
);
244 static BOOL
wine_vk_init_once(void)
246 static INIT_ONCE init_once
= INIT_ONCE_STATIC_INIT
;
248 return InitOnceExecuteOnce(&init_once
, wine_vk_init
, NULL
, NULL
);
251 VkResult WINAPI
vkCreateInstance(const VkInstanceCreateInfo
*create_info
,
252 const VkAllocationCallbacks
*allocator
, VkInstance
*ret
)
254 struct vkCreateInstance_params params
;
255 struct VkInstance_T
*instance
;
256 uint32_t phys_dev_count
= 8, i
;
259 TRACE("create_info %p, allocator %p, instance %p\n", create_info
, allocator
, ret
);
261 if (!wine_vk_init_once())
262 return VK_ERROR_INITIALIZATION_FAILED
;
266 if (!(instance
= alloc_vk_object(FIELD_OFFSET(struct VkInstance_T
, phys_devs
[phys_dev_count
]))))
267 return VK_ERROR_OUT_OF_HOST_MEMORY
;
268 instance
->phys_dev_count
= phys_dev_count
;
269 for (i
= 0; i
< phys_dev_count
; i
++)
270 instance
->phys_devs
[i
].base
.loader_magic
= VULKAN_ICD_MAGIC_VALUE
;
272 params
.pCreateInfo
= create_info
;
273 params
.pAllocator
= allocator
;
274 params
.pInstance
= ret
;
275 params
.client_ptr
= instance
;
276 status
= vk_unix_call(unix_vkCreateInstance
, ¶ms
);
278 if (instance
->phys_dev_count
<= phys_dev_count
)
280 phys_dev_count
= instance
->phys_dev_count
;
284 if (!instance
->base
.unix_handle
)
286 return params
.result
;
289 void WINAPI
vkDestroyInstance(VkInstance instance
, const VkAllocationCallbacks
*pAllocator
)
291 struct vkDestroyInstance_params params
;
294 params
.instance
= instance
;
295 params
.pAllocator
= pAllocator
;
296 status
= vk_unix_call(unix_vkDestroyInstance
, ¶ms
);
301 VkResult WINAPI
vkEnumerateInstanceExtensionProperties(const char *layer_name
,
302 uint32_t *count
, VkExtensionProperties
*properties
)
304 struct vkEnumerateInstanceExtensionProperties_params params
;
307 TRACE("%p, %p, %p\n", layer_name
, count
, properties
);
311 WARN("Layer enumeration not supported from ICD.\n");
312 return VK_ERROR_LAYER_NOT_PRESENT
;
315 if (!wine_vk_init_once())
321 params
.pLayerName
= layer_name
;
322 params
.pPropertyCount
= count
;
323 params
.pProperties
= properties
;
324 status
= vk_unix_call(unix_vkEnumerateInstanceExtensionProperties
, ¶ms
);
326 return params
.result
;
329 VkResult WINAPI
vkEnumerateInstanceVersion(uint32_t *version
)
331 struct vkEnumerateInstanceVersion_params params
;
334 TRACE("%p\n", version
);
336 if (!wine_vk_init_once())
338 *version
= VK_API_VERSION_1_0
;
342 params
.pApiVersion
= version
;
343 status
= vk_unix_call(unix_vkEnumerateInstanceVersion
, ¶ms
);
345 return params
.result
;
348 static HANDLE
get_display_device_init_mutex(void)
350 HANDLE mutex
= CreateMutexW(NULL
, FALSE
, L
"display_device_init");
352 WaitForSingleObject(mutex
, INFINITE
);
356 static void release_display_device_init_mutex(HANDLE mutex
)
362 /* Wait until graphics driver is loaded by explorer */
363 static void wait_graphics_driver_ready(void)
365 static BOOL ready
= FALSE
;
369 SendMessageW(GetDesktopWindow(), WM_NULL
, 0, 0);
374 static void fill_luid_property(VkPhysicalDeviceProperties2
*properties2
)
376 VkPhysicalDeviceVulkan11Properties
*vk11
;
377 VkBool32 device_luid_valid
= VK_FALSE
;
378 VkPhysicalDeviceIDProperties
*id
;
379 uint32_t device_node_mask
= 0;
380 SP_DEVINFO_DATA device_data
;
381 const uint8_t* device_uuid
;
382 DWORD type
, device_idx
= 0;
388 vk11
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES
);
389 id
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_ID_PROPERTIES
);
394 wait_graphics_driver_ready();
395 mutex
= get_display_device_init_mutex();
396 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, L
"PCI", NULL
, 0);
397 device_data
.cbSize
= sizeof(device_data
);
398 while (SetupDiEnumDeviceInfo(devinfo
, device_idx
++, &device_data
))
400 if (!SetupDiGetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_GPU_VULKAN_UUID
,
401 &type
, (BYTE
*)&uuid
, sizeof(uuid
), NULL
, 0))
404 device_uuid
= id
? id
->deviceUUID
: vk11
->deviceUUID
;
406 if (!IsEqualGUID(&uuid
, device_uuid
))
409 if (SetupDiGetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_GPU_LUID
, &type
,
410 (BYTE
*)&luid
, sizeof(luid
), NULL
, 0))
412 device_luid_valid
= VK_TRUE
;
413 device_node_mask
= 1;
417 SetupDiDestroyDeviceInfoList(devinfo
);
418 release_display_device_init_mutex(mutex
);
422 if (device_luid_valid
) memcpy(&id
->deviceLUID
, &luid
, sizeof(id
->deviceLUID
));
423 id
->deviceLUIDValid
= device_luid_valid
;
424 id
->deviceNodeMask
= device_node_mask
;
429 if (device_luid_valid
) memcpy(&vk11
->deviceLUID
, &luid
, sizeof(vk11
->deviceLUID
));
430 vk11
->deviceLUIDValid
= device_luid_valid
;
431 vk11
->deviceNodeMask
= device_node_mask
;
434 TRACE("deviceName:%s deviceLUIDValid:%d LUID:%08lx:%08lx deviceNodeMask:%#x.\n",
435 properties2
->properties
.deviceName
, device_luid_valid
, luid
.HighPart
, luid
.LowPart
,
439 void WINAPI
vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev
,
440 VkPhysicalDeviceProperties2
*properties2
)
442 struct vkGetPhysicalDeviceProperties2_params params
;
445 TRACE("%p, %p\n", phys_dev
, properties2
);
447 params
.physicalDevice
= phys_dev
;
448 params
.pProperties
= properties2
;
449 status
= vk_unix_call(unix_vkGetPhysicalDeviceProperties2
, ¶ms
);
451 fill_luid_property(properties2
);
454 void WINAPI
vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev
,
455 VkPhysicalDeviceProperties2
*properties2
)
457 struct vkGetPhysicalDeviceProperties2KHR_params params
;
460 TRACE("%p, %p\n", phys_dev
, properties2
);
462 params
.physicalDevice
= phys_dev
;
463 params
.pProperties
= properties2
;
464 status
= vk_unix_call(unix_vkGetPhysicalDeviceProperties2KHR
, ¶ms
);
466 fill_luid_property(properties2
);
469 VkResult WINAPI
vkCreateDevice(VkPhysicalDevice phys_dev
, const VkDeviceCreateInfo
*create_info
,
470 const VkAllocationCallbacks
*allocator
, VkDevice
*ret
)
472 struct vkCreateDevice_params params
;
473 uint32_t queue_count
= 0, i
;
477 for (i
= 0; i
< create_info
->queueCreateInfoCount
; i
++)
478 queue_count
+= create_info
->pQueueCreateInfos
[i
].queueCount
;
479 if (!(device
= alloc_vk_object(FIELD_OFFSET(struct VkDevice_T
, queues
[queue_count
]))))
480 return VK_ERROR_OUT_OF_HOST_MEMORY
;
481 for (i
= 0; i
< queue_count
; i
++)
482 device
->queues
[i
].base
.loader_magic
= VULKAN_ICD_MAGIC_VALUE
;
484 params
.physicalDevice
= phys_dev
;
485 params
.pCreateInfo
= create_info
;
486 params
.pAllocator
= allocator
;
487 params
.pDevice
= ret
;
488 params
.client_ptr
= device
;
489 status
= vk_unix_call(unix_vkCreateDevice
, ¶ms
);
491 if (!device
->base
.unix_handle
)
493 return params
.result
;
496 void WINAPI
vkDestroyDevice(VkDevice device
, const VkAllocationCallbacks
*allocator
)
498 struct vkDestroyDevice_params params
;
501 params
.device
= device
;
502 params
.pAllocator
= allocator
;
503 status
= vk_unix_call(unix_vkDestroyDevice
, ¶ms
);
508 VkResult WINAPI
vkCreateCommandPool(VkDevice device
, const VkCommandPoolCreateInfo
*create_info
,
509 const VkAllocationCallbacks
*allocator
, VkCommandPool
*ret
)
511 struct vkCreateCommandPool_params params
;
512 struct vk_command_pool
*cmd_pool
;
515 if (!(cmd_pool
= malloc(sizeof(*cmd_pool
))))
516 return VK_ERROR_OUT_OF_HOST_MEMORY
;
517 cmd_pool
->unix_handle
= 0;
518 list_init(&cmd_pool
->command_buffers
);
520 params
.device
= device
;
521 params
.pCreateInfo
= create_info
;
522 params
.pAllocator
= allocator
;
523 params
.pCommandPool
= ret
;
524 params
.client_ptr
= cmd_pool
;
525 status
= vk_unix_call(unix_vkCreateCommandPool
, ¶ms
);
527 if (!cmd_pool
->unix_handle
)
529 return params
.result
;
532 void WINAPI
vkDestroyCommandPool(VkDevice device
, VkCommandPool handle
, const VkAllocationCallbacks
*allocator
)
534 struct vk_command_pool
*cmd_pool
= command_pool_from_handle(handle
);
535 struct vkDestroyCommandPool_params params
;
536 VkCommandBuffer buffer
, cursor
;
542 /* The Vulkan spec says:
544 * "When a pool is destroyed, all command buffers allocated from the pool are freed."
546 LIST_FOR_EACH_ENTRY_SAFE(buffer
, cursor
, &cmd_pool
->command_buffers
, struct VkCommandBuffer_T
, pool_link
)
548 vkFreeCommandBuffers(device
, handle
, 1, &buffer
);
551 params
.device
= device
;
552 params
.commandPool
= handle
;
553 params
.pAllocator
= allocator
;
554 status
= vk_unix_call(unix_vkDestroyCommandPool
, ¶ms
);
559 VkResult WINAPI
vkAllocateCommandBuffers(VkDevice device
, const VkCommandBufferAllocateInfo
*allocate_info
,
560 VkCommandBuffer
*buffers
)
562 struct vk_command_pool
*pool
= command_pool_from_handle(allocate_info
->commandPool
);
563 struct vkAllocateCommandBuffers_params params
;
567 for (i
= 0; i
< allocate_info
->commandBufferCount
; i
++)
568 buffers
[i
] = alloc_vk_object(sizeof(*buffers
[i
]));
570 params
.device
= device
;
571 params
.pAllocateInfo
= allocate_info
;
572 params
.pCommandBuffers
= buffers
;
573 status
= vk_unix_call(unix_vkAllocateCommandBuffers
, ¶ms
);
575 if (params
.result
== VK_SUCCESS
)
577 for (i
= 0; i
< allocate_info
->commandBufferCount
; i
++)
578 list_add_tail(&pool
->command_buffers
, &buffers
[i
]->pool_link
);
582 for (i
= 0; i
< allocate_info
->commandBufferCount
; i
++)
588 return params
.result
;
591 void WINAPI
vkFreeCommandBuffers(VkDevice device
, VkCommandPool cmd_pool
, uint32_t count
,
592 const VkCommandBuffer
*buffers
)
594 struct vkFreeCommandBuffers_params params
;
598 params
.device
= device
;
599 params
.commandPool
= cmd_pool
;
600 params
.commandBufferCount
= count
;
601 params
.pCommandBuffers
= buffers
;
602 status
= vk_unix_call(unix_vkFreeCommandBuffers
, ¶ms
);
604 for (i
= 0; i
< count
; i
++)
606 list_remove(&buffers
[i
]->pool_link
);
611 static BOOL WINAPI
call_vulkan_debug_report_callback( struct wine_vk_debug_report_params
*params
, ULONG size
)
613 return params
->user_callback(params
->flags
, params
->object_type
, params
->object_handle
, params
->location
,
614 params
->code
, params
->layer_prefix
, params
->message
, params
->user_data
);
617 static BOOL WINAPI
call_vulkan_debug_utils_callback( struct wine_vk_debug_utils_params
*params
, ULONG size
)
619 return params
->user_callback(params
->severity
, params
->message_types
, ¶ms
->data
, params
->user_data
);
622 BOOL WINAPI
DllMain(HINSTANCE hinst
, DWORD reason
, void *reserved
)
624 void **kernel_callback_table
;
626 TRACE("%p, %lu, %p\n", hinst
, reason
, reserved
);
630 case DLL_PROCESS_ATTACH
:
632 DisableThreadLibraryCalls(hinst
);
634 kernel_callback_table
= NtCurrentTeb()->Peb
->KernelCallbackTable
;
635 kernel_callback_table
[NtUserCallVulkanDebugReportCallback
] = call_vulkan_debug_report_callback
;
636 kernel_callback_table
[NtUserCallVulkanDebugUtilsCallback
] = call_vulkan_debug_utils_callback
;
642 static const WCHAR winevulkan_json_pathW
[] = L
"\\winevulkan.json";
643 static const WCHAR vulkan_driversW
[] = L
"Software\\Khronos\\Vulkan\\Drivers";
645 HRESULT WINAPI
DllRegisterServer(void)
647 WCHAR json_path
[MAX_PATH
];
650 DWORD datalen
, written
, zero
= 0;
654 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
656 rsrc
= FindResourceW(hinstance
, L
"winevulkan_json", (const WCHAR
*)RT_RCDATA
);
657 data
= LockResource(LoadResource(hinstance
, rsrc
));
658 datalen
= SizeofResource(hinstance
, rsrc
);
660 GetSystemDirectoryW(json_path
, ARRAY_SIZE(json_path
));
661 lstrcatW(json_path
, winevulkan_json_pathW
);
662 file
= CreateFileW(json_path
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
663 if (file
== INVALID_HANDLE_VALUE
)
665 ERR("Unable to create JSON manifest.\n");
668 WriteFile(file
, data
, datalen
, &written
, NULL
);
671 if (!RegCreateKeyExW(HKEY_LOCAL_MACHINE
, vulkan_driversW
, 0, NULL
, 0, KEY_SET_VALUE
, NULL
, &key
, NULL
))
673 RegSetValueExW(key
, json_path
, 0, REG_DWORD
, (const BYTE
*)&zero
, sizeof(zero
));
679 HRESULT WINAPI
DllUnregisterServer(void)
681 WCHAR json_path
[MAX_PATH
];
684 /* Remove the JSON manifest and registry key */
686 GetSystemDirectoryW(json_path
, ARRAY_SIZE(json_path
));
687 lstrcatW(json_path
, winevulkan_json_pathW
);
688 DeleteFileW(json_path
);
690 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, vulkan_driversW
, 0, KEY_SET_VALUE
, &key
) == ERROR_SUCCESS
)
692 RegDeleteValueW(key
, json_path
);