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 static HINSTANCE hinstance
;
40 static void *wine_vk_get_global_proc_addr(const char *name
);
42 #define wine_vk_find_struct(s, t) wine_vk_find_struct_((void *)s, VK_STRUCTURE_TYPE_##t)
43 static void *wine_vk_find_struct_(void *s
, VkStructureType t
)
45 VkBaseOutStructure
*header
;
47 for (header
= s
; header
; header
= header
->pNext
)
49 if (header
->sType
== t
)
56 VkResult WINAPI
vkEnumerateInstanceLayerProperties(uint32_t *count
, VkLayerProperties
*properties
)
58 TRACE("%p, %p\n", count
, properties
);
64 static const struct vulkan_func vk_global_dispatch_table
[] =
66 /* These functions must call wine_vk_init_once() before accessing vk_funcs. */
67 {"vkCreateInstance", &vkCreateInstance
},
68 {"vkEnumerateInstanceExtensionProperties", &vkEnumerateInstanceExtensionProperties
},
69 {"vkEnumerateInstanceLayerProperties", &vkEnumerateInstanceLayerProperties
},
70 {"vkEnumerateInstanceVersion", &vkEnumerateInstanceVersion
},
71 {"vkGetInstanceProcAddr", &vkGetInstanceProcAddr
},
74 static void *wine_vk_get_global_proc_addr(const char *name
)
78 for (i
= 0; i
< ARRAY_SIZE(vk_global_dispatch_table
); i
++)
80 if (strcmp(name
, vk_global_dispatch_table
[i
].name
) == 0)
82 TRACE("Found name=%s in global table\n", debugstr_a(name
));
83 return vk_global_dispatch_table
[i
].func
;
89 static BOOL
is_available_instance_function(VkInstance instance
, const char *name
)
91 struct is_available_instance_function_params params
= { .instance
= instance
, .name
= name
};
92 return UNIX_CALL(is_available_instance_function
, ¶ms
);
95 static BOOL
is_available_device_function(VkDevice device
, const char *name
)
97 struct is_available_device_function_params params
= { .device
= device
, .name
= name
};
98 return UNIX_CALL(is_available_device_function
, ¶ms
);
101 static void *alloc_vk_object(size_t size
)
103 struct wine_vk_base
*object
= calloc(1, size
);
104 object
->loader_magic
= VULKAN_ICD_MAGIC_VALUE
;
108 PFN_vkVoidFunction WINAPI
vkGetInstanceProcAddr(VkInstance instance
, const char *name
)
112 TRACE("%p, %s\n", instance
, debugstr_a(name
));
117 /* vkGetInstanceProcAddr can load most Vulkan functions when an instance is passed in, however
118 * for a NULL instance it can only load global functions.
120 func
= wine_vk_get_global_proc_addr(name
);
127 WARN("Global function %s not found.\n", debugstr_a(name
));
131 if (!is_available_instance_function(instance
, name
))
134 func
= wine_vk_get_instance_proc_addr(name
);
135 if (func
) return func
;
137 func
= wine_vk_get_phys_dev_proc_addr(name
);
138 if (func
) return func
;
140 /* vkGetInstanceProcAddr also loads any children of instance, so device functions as well. */
141 func
= wine_vk_get_device_proc_addr(name
);
142 if (func
) return func
;
144 WARN("Unsupported device or instance function: %s.\n", debugstr_a(name
));
148 PFN_vkVoidFunction WINAPI
vkGetDeviceProcAddr(VkDevice device
, const char *name
)
151 TRACE("%p, %s\n", device
, debugstr_a(name
));
153 /* The spec leaves return value undefined for a NULL device, let's just return NULL. */
154 if (!device
|| !name
)
157 /* Per the spec, we are only supposed to return device functions as in functions
158 * for which the first parameter is vkDevice or a child of vkDevice like a
159 * vkCommandBuffer or vkQueue.
160 * Loader takes care of filtering of extensions which are enabled or not.
162 if (is_available_device_function(device
, name
))
164 func
= wine_vk_get_device_proc_addr(name
);
169 /* vkGetDeviceProcAddr was intended for loading device and subdevice functions.
170 * idTech 6 titles such as Doom and Wolfenstein II, however use it also for
171 * loading of instance functions. This is undefined behavior as the specification
172 * disallows using any of the returned function pointers outside of device /
173 * subdevice objects. The games don't actually use the function pointers and if they
174 * did, they would crash as VkInstance / VkPhysicalDevice parameters need unwrapping.
175 * Khronos clarified behavior in the Vulkan spec and expects drivers to get updated,
176 * however it would require both driver and game fixes.
177 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/2323
178 * https://github.com/KhronosGroup/Vulkan-Docs/issues/655
180 if ((device
->quirks
& WINEVULKAN_QUIRK_GET_DEVICE_PROC_ADDR
)
181 && ((func
= wine_vk_get_instance_proc_addr(name
))
182 || (func
= wine_vk_get_phys_dev_proc_addr(name
))))
184 WARN("Returning instance function %s.\n", debugstr_a(name
));
188 WARN("Unsupported device function: %s.\n", debugstr_a(name
));
192 void * WINAPI
vk_icdGetPhysicalDeviceProcAddr(VkInstance instance
, const char *name
)
194 TRACE("%p, %s\n", instance
, debugstr_a(name
));
196 if (!is_available_instance_function(instance
, name
))
199 return wine_vk_get_phys_dev_proc_addr(name
);
202 void * WINAPI
vk_icdGetInstanceProcAddr(VkInstance instance
, const char *name
)
204 TRACE("%p, %s\n", instance
, debugstr_a(name
));
206 /* Initial version of the Vulkan ICD spec required vkGetInstanceProcAddr to be
207 * exported. vk_icdGetInstanceProcAddr was added later to separate ICD calls from
208 * Vulkan API. One of them in our case should forward to the other, so just forward
209 * to the older vkGetInstanceProcAddr.
211 return vkGetInstanceProcAddr(instance
, name
);
214 VkResult WINAPI
vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t *supported_version
)
216 uint32_t req_version
;
218 TRACE("%p\n", supported_version
);
220 /* The spec is not clear how to handle this. Mesa drivers don't check, but it
221 * is probably best to not explode. VK_INCOMPLETE seems to be the closest value.
223 if (!supported_version
)
224 return VK_INCOMPLETE
;
226 req_version
= *supported_version
;
227 *supported_version
= min(req_version
, WINE_VULKAN_ICD_VERSION
);
228 TRACE("Loader requested ICD version %u, returning %u\n", req_version
, *supported_version
);
233 static BOOL WINAPI
wine_vk_init(INIT_ONCE
*once
, void *param
, void **context
)
235 return !__wine_init_unix_call() && !UNIX_CALL(init
, NULL
);
238 static BOOL
wine_vk_init_once(void)
240 static INIT_ONCE init_once
= INIT_ONCE_STATIC_INIT
;
242 return InitOnceExecuteOnce(&init_once
, wine_vk_init
, NULL
, NULL
);
245 VkResult WINAPI
vkCreateInstance(const VkInstanceCreateInfo
*create_info
,
246 const VkAllocationCallbacks
*allocator
, VkInstance
*ret
)
248 struct vkCreateInstance_params params
;
249 struct VkInstance_T
*instance
;
250 uint32_t phys_dev_count
= 8, i
;
253 TRACE("create_info %p, allocator %p, instance %p\n", create_info
, allocator
, ret
);
255 if (!wine_vk_init_once())
256 return VK_ERROR_INITIALIZATION_FAILED
;
260 if (!(instance
= alloc_vk_object(FIELD_OFFSET(struct VkInstance_T
, phys_devs
[phys_dev_count
]))))
261 return VK_ERROR_OUT_OF_HOST_MEMORY
;
262 instance
->phys_dev_count
= phys_dev_count
;
263 for (i
= 0; i
< phys_dev_count
; i
++)
264 instance
->phys_devs
[i
].base
.loader_magic
= VULKAN_ICD_MAGIC_VALUE
;
266 params
.pCreateInfo
= create_info
;
267 params
.pAllocator
= allocator
;
268 params
.pInstance
= ret
;
269 params
.client_ptr
= instance
;
270 status
= UNIX_CALL(vkCreateInstance
, ¶ms
);
272 if (instance
->phys_dev_count
<= phys_dev_count
)
274 phys_dev_count
= instance
->phys_dev_count
;
278 if (!instance
->base
.unix_handle
)
280 return params
.result
;
283 void WINAPI
vkDestroyInstance(VkInstance instance
, const VkAllocationCallbacks
*pAllocator
)
285 struct vkDestroyInstance_params params
;
288 params
.instance
= instance
;
289 params
.pAllocator
= pAllocator
;
290 status
= UNIX_CALL(vkDestroyInstance
, ¶ms
);
295 VkResult WINAPI
vkEnumerateInstanceExtensionProperties(const char *layer_name
,
296 uint32_t *count
, VkExtensionProperties
*properties
)
298 struct vkEnumerateInstanceExtensionProperties_params params
;
301 TRACE("%p, %p, %p\n", layer_name
, count
, properties
);
305 WARN("Layer enumeration not supported from ICD.\n");
306 return VK_ERROR_LAYER_NOT_PRESENT
;
309 if (!wine_vk_init_once())
315 params
.pLayerName
= layer_name
;
316 params
.pPropertyCount
= count
;
317 params
.pProperties
= properties
;
318 status
= UNIX_CALL(vkEnumerateInstanceExtensionProperties
, ¶ms
);
320 return params
.result
;
323 VkResult WINAPI
vkEnumerateInstanceVersion(uint32_t *version
)
325 struct vkEnumerateInstanceVersion_params params
;
328 TRACE("%p\n", version
);
330 if (!wine_vk_init_once())
332 *version
= VK_API_VERSION_1_0
;
336 params
.pApiVersion
= version
;
337 status
= UNIX_CALL(vkEnumerateInstanceVersion
, ¶ms
);
339 return params
.result
;
342 static HANDLE
get_display_device_init_mutex(void)
344 HANDLE mutex
= CreateMutexW(NULL
, FALSE
, L
"display_device_init");
346 WaitForSingleObject(mutex
, INFINITE
);
350 static void release_display_device_init_mutex(HANDLE mutex
)
356 /* Wait until graphics driver is loaded by explorer */
357 static void wait_graphics_driver_ready(void)
359 static BOOL ready
= FALSE
;
363 SendMessageW(GetDesktopWindow(), WM_NULL
, 0, 0);
368 static void fill_luid_property(VkPhysicalDeviceProperties2
*properties2
)
370 VkPhysicalDeviceVulkan11Properties
*vk11
;
371 VkBool32 device_luid_valid
= VK_FALSE
;
372 VkPhysicalDeviceIDProperties
*id
;
373 uint32_t device_node_mask
= 0;
374 SP_DEVINFO_DATA device_data
;
375 const uint8_t* device_uuid
;
376 DWORD type
, device_idx
= 0;
382 vk11
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES
);
383 id
= wine_vk_find_struct(properties2
, PHYSICAL_DEVICE_ID_PROPERTIES
);
388 wait_graphics_driver_ready();
389 mutex
= get_display_device_init_mutex();
390 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, L
"PCI", NULL
, 0);
391 device_data
.cbSize
= sizeof(device_data
);
392 while (SetupDiEnumDeviceInfo(devinfo
, device_idx
++, &device_data
))
394 if (!SetupDiGetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_GPU_VULKAN_UUID
,
395 &type
, (BYTE
*)&uuid
, sizeof(uuid
), NULL
, 0))
398 device_uuid
= id
? id
->deviceUUID
: vk11
->deviceUUID
;
400 if (!IsEqualGUID(&uuid
, device_uuid
))
403 if (SetupDiGetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_GPU_LUID
, &type
,
404 (BYTE
*)&luid
, sizeof(luid
), NULL
, 0))
406 device_luid_valid
= VK_TRUE
;
407 device_node_mask
= 1;
411 SetupDiDestroyDeviceInfoList(devinfo
);
412 release_display_device_init_mutex(mutex
);
416 if (device_luid_valid
) memcpy(&id
->deviceLUID
, &luid
, sizeof(id
->deviceLUID
));
417 id
->deviceLUIDValid
= device_luid_valid
;
418 id
->deviceNodeMask
= device_node_mask
;
423 if (device_luid_valid
) memcpy(&vk11
->deviceLUID
, &luid
, sizeof(vk11
->deviceLUID
));
424 vk11
->deviceLUIDValid
= device_luid_valid
;
425 vk11
->deviceNodeMask
= device_node_mask
;
428 TRACE("deviceName:%s deviceLUIDValid:%d LUID:%08lx:%08lx deviceNodeMask:%#x.\n",
429 properties2
->properties
.deviceName
, device_luid_valid
, luid
.HighPart
, luid
.LowPart
,
433 void WINAPI
vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev
,
434 VkPhysicalDeviceProperties2
*properties2
)
436 struct vkGetPhysicalDeviceProperties2_params params
;
439 TRACE("%p, %p\n", phys_dev
, properties2
);
441 params
.physicalDevice
= phys_dev
;
442 params
.pProperties
= properties2
;
443 status
= UNIX_CALL(vkGetPhysicalDeviceProperties2
, ¶ms
);
445 fill_luid_property(properties2
);
448 void WINAPI
vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev
,
449 VkPhysicalDeviceProperties2
*properties2
)
451 struct vkGetPhysicalDeviceProperties2KHR_params params
;
454 TRACE("%p, %p\n", phys_dev
, properties2
);
456 params
.physicalDevice
= phys_dev
;
457 params
.pProperties
= properties2
;
458 status
= UNIX_CALL(vkGetPhysicalDeviceProperties2KHR
, ¶ms
);
460 fill_luid_property(properties2
);
463 VkResult WINAPI
vkCreateDevice(VkPhysicalDevice phys_dev
, const VkDeviceCreateInfo
*create_info
,
464 const VkAllocationCallbacks
*allocator
, VkDevice
*ret
)
466 struct vkCreateDevice_params params
;
467 uint32_t queue_count
= 0, i
;
471 for (i
= 0; i
< create_info
->queueCreateInfoCount
; i
++)
472 queue_count
+= create_info
->pQueueCreateInfos
[i
].queueCount
;
473 if (!(device
= alloc_vk_object(FIELD_OFFSET(struct VkDevice_T
, queues
[queue_count
]))))
474 return VK_ERROR_OUT_OF_HOST_MEMORY
;
475 for (i
= 0; i
< queue_count
; i
++)
476 device
->queues
[i
].base
.loader_magic
= VULKAN_ICD_MAGIC_VALUE
;
478 params
.physicalDevice
= phys_dev
;
479 params
.pCreateInfo
= create_info
;
480 params
.pAllocator
= allocator
;
481 params
.pDevice
= ret
;
482 params
.client_ptr
= device
;
483 status
= UNIX_CALL(vkCreateDevice
, ¶ms
);
485 if (!device
->base
.unix_handle
)
487 return params
.result
;
490 void WINAPI
vkDestroyDevice(VkDevice device
, const VkAllocationCallbacks
*allocator
)
492 struct vkDestroyDevice_params params
;
495 params
.device
= device
;
496 params
.pAllocator
= allocator
;
497 status
= UNIX_CALL(vkDestroyDevice
, ¶ms
);
502 VkResult WINAPI
vkCreateCommandPool(VkDevice device
, const VkCommandPoolCreateInfo
*create_info
,
503 const VkAllocationCallbacks
*allocator
, VkCommandPool
*ret
)
505 struct vkCreateCommandPool_params params
;
506 struct vk_command_pool
*cmd_pool
;
509 if (!(cmd_pool
= malloc(sizeof(*cmd_pool
))))
510 return VK_ERROR_OUT_OF_HOST_MEMORY
;
511 cmd_pool
->unix_handle
= 0;
512 list_init(&cmd_pool
->command_buffers
);
514 params
.device
= device
;
515 params
.pCreateInfo
= create_info
;
516 params
.pAllocator
= allocator
;
517 params
.pCommandPool
= ret
;
518 params
.client_ptr
= cmd_pool
;
519 status
= UNIX_CALL(vkCreateCommandPool
, ¶ms
);
521 if (!cmd_pool
->unix_handle
)
523 return params
.result
;
526 void WINAPI
vkDestroyCommandPool(VkDevice device
, VkCommandPool handle
, const VkAllocationCallbacks
*allocator
)
528 struct vk_command_pool
*cmd_pool
= command_pool_from_handle(handle
);
529 struct vkDestroyCommandPool_params params
;
530 VkCommandBuffer buffer
, cursor
;
536 /* The Vulkan spec says:
538 * "When a pool is destroyed, all command buffers allocated from the pool are freed."
540 LIST_FOR_EACH_ENTRY_SAFE(buffer
, cursor
, &cmd_pool
->command_buffers
, struct VkCommandBuffer_T
, pool_link
)
542 vkFreeCommandBuffers(device
, handle
, 1, &buffer
);
545 params
.device
= device
;
546 params
.commandPool
= handle
;
547 params
.pAllocator
= allocator
;
548 status
= UNIX_CALL(vkDestroyCommandPool
, ¶ms
);
553 VkResult WINAPI
vkAllocateCommandBuffers(VkDevice device
, const VkCommandBufferAllocateInfo
*allocate_info
,
554 VkCommandBuffer
*buffers
)
556 struct vk_command_pool
*pool
= command_pool_from_handle(allocate_info
->commandPool
);
557 struct vkAllocateCommandBuffers_params params
;
561 for (i
= 0; i
< allocate_info
->commandBufferCount
; i
++)
562 buffers
[i
] = alloc_vk_object(sizeof(*buffers
[i
]));
564 params
.device
= device
;
565 params
.pAllocateInfo
= allocate_info
;
566 params
.pCommandBuffers
= buffers
;
567 status
= UNIX_CALL(vkAllocateCommandBuffers
, ¶ms
);
569 if (params
.result
== VK_SUCCESS
)
571 for (i
= 0; i
< allocate_info
->commandBufferCount
; i
++)
572 list_add_tail(&pool
->command_buffers
, &buffers
[i
]->pool_link
);
576 for (i
= 0; i
< allocate_info
->commandBufferCount
; i
++)
582 return params
.result
;
585 void WINAPI
vkFreeCommandBuffers(VkDevice device
, VkCommandPool cmd_pool
, uint32_t count
,
586 const VkCommandBuffer
*buffers
)
588 struct vkFreeCommandBuffers_params params
;
592 params
.device
= device
;
593 params
.commandPool
= cmd_pool
;
594 params
.commandBufferCount
= count
;
595 params
.pCommandBuffers
= buffers
;
596 status
= UNIX_CALL(vkFreeCommandBuffers
, ¶ms
);
598 for (i
= 0; i
< count
; i
++)
600 list_remove(&buffers
[i
]->pool_link
);
605 static BOOL WINAPI
call_vulkan_debug_report_callback( struct wine_vk_debug_report_params
*params
, ULONG size
)
607 return params
->user_callback(params
->flags
, params
->object_type
, params
->object_handle
, params
->location
,
608 params
->code
, params
->layer_prefix
, params
->message
, params
->user_data
);
611 static BOOL WINAPI
call_vulkan_debug_utils_callback( struct wine_vk_debug_utils_params
*params
, ULONG size
)
613 return params
->user_callback(params
->severity
, params
->message_types
, ¶ms
->data
, params
->user_data
);
616 BOOL WINAPI
DllMain(HINSTANCE hinst
, DWORD reason
, void *reserved
)
618 void **kernel_callback_table
;
620 TRACE("%p, %lu, %p\n", hinst
, reason
, reserved
);
624 case DLL_PROCESS_ATTACH
:
626 DisableThreadLibraryCalls(hinst
);
628 kernel_callback_table
= NtCurrentTeb()->Peb
->KernelCallbackTable
;
629 kernel_callback_table
[NtUserCallVulkanDebugReportCallback
] = call_vulkan_debug_report_callback
;
630 kernel_callback_table
[NtUserCallVulkanDebugUtilsCallback
] = call_vulkan_debug_utils_callback
;
636 static const WCHAR winevulkan_json_pathW
[] = L
"\\winevulkan.json";
637 static const WCHAR vulkan_driversW
[] = L
"Software\\Khronos\\Vulkan\\Drivers";
639 HRESULT WINAPI
DllRegisterServer(void)
641 WCHAR json_path
[MAX_PATH
];
644 DWORD datalen
, written
, zero
= 0;
648 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
650 rsrc
= FindResourceW(hinstance
, L
"winevulkan_json", (const WCHAR
*)RT_RCDATA
);
651 data
= LockResource(LoadResource(hinstance
, rsrc
));
652 datalen
= SizeofResource(hinstance
, rsrc
);
654 GetSystemDirectoryW(json_path
, ARRAY_SIZE(json_path
));
655 lstrcatW(json_path
, winevulkan_json_pathW
);
656 file
= CreateFileW(json_path
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
657 if (file
== INVALID_HANDLE_VALUE
)
659 ERR("Unable to create JSON manifest.\n");
662 WriteFile(file
, data
, datalen
, &written
, NULL
);
665 if (!RegCreateKeyExW(HKEY_LOCAL_MACHINE
, vulkan_driversW
, 0, NULL
, 0, KEY_SET_VALUE
, NULL
, &key
, NULL
))
667 RegSetValueExW(key
, json_path
, 0, REG_DWORD
, (const BYTE
*)&zero
, sizeof(zero
));
673 HRESULT WINAPI
DllUnregisterServer(void)
675 WCHAR json_path
[MAX_PATH
];
678 /* Remove the JSON manifest and registry key */
680 GetSystemDirectoryW(json_path
, ARRAY_SIZE(json_path
));
681 lstrcatW(json_path
, winevulkan_json_pathW
);
682 DeleteFileW(json_path
);
684 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, vulkan_driversW
, 0, KEY_SET_VALUE
, &key
) == ERROR_SUCCESS
)
686 RegDeleteValueW(key
, json_path
);