conhost: Fix ctrl-c handling.
[wine.git] / dlls / winevulkan / loader.c
blob19fd240f0cb783619c90bfc00a52cd598ab4c73f
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 <stdlib.h>
21 #include "vulkan_loader.h"
22 #include "winreg.h"
23 #include "ntuser.h"
24 #include "initguid.h"
25 #include "devguid.h"
26 #include "setupapi.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)
50 return header;
53 return NULL;
56 VkResult WINAPI vkEnumerateInstanceLayerProperties(uint32_t *count, VkLayerProperties *properties)
58 TRACE("%p, %p\n", count, properties);
60 *count = 0;
61 return VK_SUCCESS;
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)
76 unsigned int i;
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;
86 return NULL;
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, &params);
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, &params);
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;
105 return object;
108 PFN_vkVoidFunction WINAPI vkGetInstanceProcAddr(VkInstance instance, const char *name)
110 void *func;
112 TRACE("%p, %s\n", instance, debugstr_a(name));
114 if (!name)
115 return NULL;
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);
121 if (func)
123 return func;
125 if (!instance)
127 WARN("Global function %s not found.\n", debugstr_a(name));
128 return NULL;
131 if (!is_available_instance_function(instance, name))
132 return NULL;
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));
145 return NULL;
148 PFN_vkVoidFunction WINAPI vkGetDeviceProcAddr(VkDevice device, const char *name)
150 void *func;
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)
155 return NULL;
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);
165 if (func)
166 return func;
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));
185 return func;
188 WARN("Unsupported device function: %s.\n", debugstr_a(name));
189 return NULL;
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))
197 return NULL;
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);
230 return VK_SUCCESS;
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;
251 NTSTATUS status;
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;
258 for (;;)
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, &params);
271 assert(!status);
272 if (instance->phys_dev_count <= phys_dev_count)
273 break;
274 phys_dev_count = instance->phys_dev_count;
275 free(instance);
278 if (!instance->base.unix_handle)
279 free(instance);
280 return params.result;
283 void WINAPI vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator)
285 struct vkDestroyInstance_params params;
286 NTSTATUS status;
288 params.instance = instance;
289 params.pAllocator = pAllocator;
290 status = UNIX_CALL(vkDestroyInstance, &params);
291 assert(!status);
292 free(instance);
295 VkResult WINAPI vkEnumerateInstanceExtensionProperties(const char *layer_name,
296 uint32_t *count, VkExtensionProperties *properties)
298 struct vkEnumerateInstanceExtensionProperties_params params;
299 NTSTATUS status;
301 TRACE("%p, %p, %p\n", layer_name, count, properties);
303 if (layer_name)
305 WARN("Layer enumeration not supported from ICD.\n");
306 return VK_ERROR_LAYER_NOT_PRESENT;
309 if (!wine_vk_init_once())
311 *count = 0;
312 return VK_SUCCESS;
315 params.pLayerName = layer_name;
316 params.pPropertyCount = count;
317 params.pProperties = properties;
318 status = UNIX_CALL(vkEnumerateInstanceExtensionProperties, &params);
319 assert(!status);
320 return params.result;
323 VkResult WINAPI vkEnumerateInstanceVersion(uint32_t *version)
325 struct vkEnumerateInstanceVersion_params params;
326 NTSTATUS status;
328 TRACE("%p\n", version);
330 if (!wine_vk_init_once())
332 *version = VK_API_VERSION_1_0;
333 return VK_SUCCESS;
336 params.pApiVersion = version;
337 status = UNIX_CALL(vkEnumerateInstanceVersion, &params);
338 assert(!status);
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);
347 return mutex;
350 static void release_display_device_init_mutex(HANDLE mutex)
352 ReleaseMutex(mutex);
353 CloseHandle(mutex);
356 /* Wait until graphics driver is loaded by explorer */
357 static void wait_graphics_driver_ready(void)
359 static BOOL ready = FALSE;
361 if (!ready)
363 SendMessageW(GetDesktopWindow(), WM_NULL, 0, 0);
364 ready = TRUE;
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;
377 HDEVINFO devinfo;
378 HANDLE mutex;
379 GUID uuid;
380 LUID luid;
382 vk11 = wine_vk_find_struct(properties2, PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES);
383 id = wine_vk_find_struct(properties2, PHYSICAL_DEVICE_ID_PROPERTIES);
385 if (!vk11 && !id)
386 return;
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))
396 continue;
398 device_uuid = id ? id->deviceUUID : vk11->deviceUUID;
400 if (!IsEqualGUID(&uuid, device_uuid))
401 continue;
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;
408 break;
411 SetupDiDestroyDeviceInfoList(devinfo);
412 release_display_device_init_mutex(mutex);
414 if (id)
416 if (device_luid_valid) memcpy(&id->deviceLUID, &luid, sizeof(id->deviceLUID));
417 id->deviceLUIDValid = device_luid_valid;
418 id->deviceNodeMask = device_node_mask;
421 if (vk11)
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,
430 device_node_mask);
433 void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev,
434 VkPhysicalDeviceProperties2 *properties2)
436 struct vkGetPhysicalDeviceProperties2_params params;
437 NTSTATUS status;
439 TRACE("%p, %p\n", phys_dev, properties2);
441 params.physicalDevice = phys_dev;
442 params.pProperties = properties2;
443 status = UNIX_CALL(vkGetPhysicalDeviceProperties2, &params);
444 assert(!status);
445 fill_luid_property(properties2);
448 void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev,
449 VkPhysicalDeviceProperties2 *properties2)
451 struct vkGetPhysicalDeviceProperties2KHR_params params;
452 NTSTATUS status;
454 TRACE("%p, %p\n", phys_dev, properties2);
456 params.physicalDevice = phys_dev;
457 params.pProperties = properties2;
458 status = UNIX_CALL(vkGetPhysicalDeviceProperties2KHR, &params);
459 assert(!status);
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;
468 VkDevice device;
469 NTSTATUS status;
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, &params);
484 assert(!status);
485 if (!device->base.unix_handle)
486 free(device);
487 return params.result;
490 void WINAPI vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *allocator)
492 struct vkDestroyDevice_params params;
493 NTSTATUS status;
495 params.device = device;
496 params.pAllocator = allocator;
497 status = UNIX_CALL(vkDestroyDevice, &params);
498 assert(!status);
499 free(device);
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;
507 NTSTATUS status;
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, &params);
520 assert(!status);
521 if (!cmd_pool->unix_handle)
522 free(cmd_pool);
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;
531 NTSTATUS status;
533 if (!cmd_pool)
534 return;
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, &params);
549 assert(!status);
550 free(cmd_pool);
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;
558 NTSTATUS status;
559 uint32_t i;
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, &params);
568 assert(!status);
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);
574 else
576 for (i = 0; i < allocate_info->commandBufferCount; i++)
578 free(buffers[i]);
579 buffers[i] = NULL;
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;
589 NTSTATUS status;
590 uint32_t i;
592 params.device = device;
593 params.commandPool = cmd_pool;
594 params.commandBufferCount = count;
595 params.pCommandBuffers = buffers;
596 status = UNIX_CALL(vkFreeCommandBuffers, &params);
597 assert(!status);
598 for (i = 0; i < count; i++)
600 list_remove(&buffers[i]->pool_link);
601 free(buffers[i]);
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, &params->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);
622 switch (reason)
624 case DLL_PROCESS_ATTACH:
625 hinstance = hinst;
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;
631 break;
633 return TRUE;
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];
642 HRSRC rsrc;
643 const char *data;
644 DWORD datalen, written, zero = 0;
645 HANDLE file;
646 HKEY key;
648 /* Create the JSON manifest and registry key to register this ICD with the official Vulkan loader. */
649 TRACE("\n");
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");
660 return E_UNEXPECTED;
662 WriteFile(file, data, datalen, &written, NULL);
663 CloseHandle(file);
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));
668 RegCloseKey(key);
670 return S_OK;
673 HRESULT WINAPI DllUnregisterServer(void)
675 WCHAR json_path[MAX_PATH];
676 HKEY key;
678 /* Remove the JSON manifest and registry key */
679 TRACE("\n");
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);
687 RegCloseKey(key);
690 return S_OK;