With [`VK_EXT_device_address_binding_report`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_dev...) we can get [debug_util](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_deb...) callbacks used to track memory bindings. Since it's the host's implementation that starts the callback we have to be sure that we have a way of converting it to the client side's variant before it's added to the handle map - i.e. we don't know the host handle at that time yet.
This is [used by vkd3d-proton](https://github.com/HansKristian-Work/vkd3d-proton/pull/1962). Requires Mesa with https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/28649 (24.3 is modern enough to have it).
before (note the missing BIND for VkDevice which actually means `VkDeviceMemory`): ``` vkd3d-proton % WINEDLLOVERRIDES="d3d12=n;d3d12core=n" \ VKD3D_TEST_FILTER=create_placed_resource_size \ VKD3D_CONFIG=fault VKD3D_DEBUG=trace \ ~/build/wine/wine ./tests/d3d12.exe 2>&1 | grep vkd3d_address_binding_callback 6669.627:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: BIND || VkBuffer || VA ffff800101600000 || size 0000000001000000. 6669.627:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: BIND || VkImage || VA ffff800101600000 || size 000000000019a000. 6669.627:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkImage || VA ffff800101600000 || size 000000000019a000. 6669.627:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkBuffer || VA ffff800101600000 || size 0000000001000000. 6669.627:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkDevice || VA ffff800101600000 || size 0000000001000000. 6669.627:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: BIND || VkBuffer || VA ffff800101600000 || size 0000000001000000. 6669.627:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkBuffer || VA ffff800101600000 || size 0000000001000000. 6669.627:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkDevice || VA ffff800101600000 || size 0000000001000000 ```
after: ``` vkd3d-proton % WINEDLLOVERRIDES="d3d12=n;d3d12core=n" \ VKD3D_TEST_FILTER=create_placed_resource_size \ VKD3D_CONFIG=fault VKD3D_DEBUG=trace \ ~/build/wine/wine ./tests/d3d12.exe 2>&1 | grep vkd3d_address_binding_callback 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: BIND || VkDevice || VA ffff800101600000 || size 0000000001000000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: BIND || VkBuffer || VA ffff800101600000 || size 0000000001000000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: BIND || VkImage || VA ffff800101600000 || size 000000000019a000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkImage || VA ffff800101600000 || size 000000000019a000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkBuffer || VA ffff800101600000 || size 0000000001000000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkDevice || VA ffff800101600000 || size 0000000001000000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: BIND || VkDevice || VA ffff800101600000 || size 0000000001000000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: BIND || VkBuffer || VA ffff800101600000 || size 0000000001000000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkBuffer || VA ffff800101600000 || size 0000000001000000. 4015.597:0020:0024:trace:vkd3d-proton:vkd3d_address_binding_callback: UNBIND || VkDevice || VA ffff800101600000 || size 0000000001000000. ```
[The spec guarantees](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#de...) the following:
An application can receive multiple callbacks if multiple VkDebugUtilsMessengerEXT objects are created. A callback will always be executed in the same thread as the originating Vulkan call.
-- v5: winevulkan: Make device memory wrapper available in callbacks.
From: Arkadiusz Hiler ahiler@codeweavers.com
With VK_EXT_device_address_binding_report we can get debug_util callbacks used to track memory bindings. Since it's the host's implementation that starts the callback we have to be sure that we have a way of converting it to the client side's variant before it's added to the handle map - i.e. we don't know the host handle at that time yet. --- dlls/winevulkan/vulkan.c | 32 +++++++++++++++++++++++++++++++- dlls/winevulkan/vulkan_private.h | 2 ++ 2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 61b3a42d63f..6bed1f32833 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -164,6 +164,23 @@ static void append_debug_utils_object(const VkDebugUtilsObjectNameInfoEXT *objec dst->object_name_len = append_string(object->pObjectName, strings, strings_len); }
+static void set_transient_client_handle(struct wine_instance *instance, uint64_t client_handle) +{ + uint64_t *handle = pthread_getspecific(instance->transient_object_handle); + if (!handle) + { + handle = malloc(sizeof(uint64_t)); + pthread_setspecific(instance->transient_object_handle, handle); + } + *handle = client_handle; +} + +static uint64_t get_transient_handle(struct wine_instance *instance) +{ + uint64_t *handle = pthread_getspecific(instance->transient_object_handle); + return handle && *handle; +} + static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT message_types, const VkDebugUtilsMessengerCallbackDataEXT *callback_data, @@ -174,6 +191,8 @@ static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagB struct wine_debug_utils_messenger *object; struct debug_utils_object dummy_object, *objects; struct debug_utils_label dummy_label, *labels; + VkInstance instance; + struct wine_instance *wine_instance; UINT size, strings_len; char *ptr, *strings; ULONG ret_len; @@ -183,13 +202,16 @@ static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagB TRACE("%i, %u, %p, %p\n", severity, message_types, callback_data, user_data);
object = user_data; + instance = object->instance->host.instance;
- if (!object->instance->host.instance) + if (!instance) { /* instance wasn't yet created, this is a message from the host loader */ return VK_FALSE; }
+ wine_instance = CONTAINING_RECORD(object->instance, struct wine_instance, obj); + if ((address = callback_data->pNext)) { if (address->sType != VK_STRUCTURE_TYPE_DEVICE_ADDRESS_BINDING_CALLBACK_DATA_EXT) address = NULL; @@ -246,6 +268,8 @@ static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagB if (wine_vk_is_type_wrapped(objects[i].object_type)) { objects[i].object_handle = client_handle_from_host(object->instance, objects[i].object_handle); + if (!objects[i].object_handle) + objects[i].object_handle = get_transient_handle(wine_instance); if (!objects[i].object_handle) { WARN("handle conversion failed 0x%s\n", wine_dbgstr_longlong(callback_data->pObjects[i].objectHandle)); @@ -1004,6 +1028,8 @@ VkResult wine_vkCreateInstance(const VkInstanceCreateInfo *create_info, instance->quirks |= WINEVULKAN_QUIRK_GET_DEVICE_PROC_ADDR; }
+ pthread_key_create(&instance->transient_object_handle, free); + TRACE("Created instance %p, host_instance %p.\n", instance, instance->obj.host.instance);
for (i = 0; i < instance->phys_dev_count; i++) @@ -1054,6 +1080,8 @@ void wine_vkDestroyInstance(VkInstance client_instance, const VkAllocationCallba } vulkan_instance_remove_object(&instance->obj, &instance->obj.obj);
+ pthread_key_delete(instance->transient_object_handle); + if (instance->objects.compare) pthread_rwlock_destroy(&instance->objects_lock); free(instance->utils_messengers); free(instance); @@ -1645,6 +1673,7 @@ VkResult wine_vkAllocateMemory(VkDevice client_device, const VkMemoryAllocateInf struct vulkan_device *device = vulkan_device_from_handle(client_device); struct wine_phys_dev *physical_device = CONTAINING_RECORD(device->physical_device, struct wine_phys_dev, obj); struct vulkan_instance *instance = device->physical_device->instance; + struct wine_instance *wine_instance = CONTAINING_RECORD(instance, struct wine_instance, obj); struct wine_device_memory *memory; VkMemoryAllocateInfo info = *alloc_info; VkImportMemoryHostPointerInfoEXT host_pointer_info; @@ -1724,6 +1753,7 @@ VkResult wine_vkAllocateMemory(VkDevice client_device, const VkMemoryAllocateInf if (!(memory = malloc(sizeof(*memory)))) return VK_ERROR_OUT_OF_HOST_MEMORY;
+ set_transient_client_handle(wine_instance, (uintptr_t)memory); result = device->p_vkAllocateMemory(device->host.device, &info, NULL, &host_device_memory); if (result != VK_SUCCESS) { diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 59b900930b3..8e0b48af169 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -100,6 +100,8 @@ struct wine_instance struct rb_tree objects; pthread_rwlock_t objects_lock;
+ pthread_key_t transient_object_handle; + /* We cache devices as we need to wrap them as they are dispatchable objects. */ uint32_t phys_dev_count; struct wine_phys_dev phys_devs[];