Signed-off-by: Derek Lesho dlesho@codeweavers.com --- This commit had to be squashed in with the tests, as data structures for the extension are missing from the headers when the extension is blacklisted by make_vulkan. --- dlls/vulkan-1/tests/vulkan.c | 242 +++++++++++++++++-- dlls/winevulkan/loader_thunks.c | 12 + dlls/winevulkan/loader_thunks.h | 2 + dlls/winevulkan/make_vulkan | 63 ++++- dlls/winevulkan/vulkan.c | 385 +++++++++++++++++++++++++++++-- dlls/winevulkan/vulkan_private.h | 22 ++ 6 files changed, 679 insertions(+), 47 deletions(-)
diff --git a/dlls/vulkan-1/tests/vulkan.c b/dlls/vulkan-1/tests/vulkan.c index 9061b2b6db8..c1350da70ee 100644 --- a/dlls/vulkan-1/tests/vulkan.c +++ b/dlls/vulkan-1/tests/vulkan.c @@ -144,7 +144,7 @@ static void test_instance_version(void) VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)); }
-static void enumerate_physical_device(VkPhysicalDevice vk_physical_device) +static void enumerate_physical_device(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkPhysicalDeviceProperties properties;
@@ -224,7 +224,7 @@ static void test_enumerate_physical_device2(void) vkDestroyInstance(vk_instance, NULL); }
-static void enumerate_device_queues(VkPhysicalDevice vk_physical_device) +static void enumerate_device_queues(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkPhysicalDeviceProperties device_properties; VkQueueFamilyProperties *properties; @@ -283,7 +283,7 @@ static void test_physical_device_groups(void) trace("Group[%u] count %u, subset allocation %#x\n", i, properties[i].physicalDeviceCount, properties[i].subsetAllocation); for (j = 0; j < properties[i].physicalDeviceCount; ++j) - enumerate_physical_device(properties[i].physicalDevices[j]); + enumerate_physical_device(vk_instance, properties[i].physicalDevices[j]); }
if ((vr = create_device(properties->physicalDevices[0], 0, NULL, NULL, &vk_device)) < 0) @@ -307,7 +307,7 @@ static void test_physical_device_groups(void) vkDestroyInstance(vk_instance, NULL); }
-static void test_destroy_command_pool(VkPhysicalDevice vk_physical_device) +static void test_destroy_command_pool(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkCommandBufferAllocateInfo allocate_info; VkCommandPoolCreateInfo pool_info; @@ -366,7 +366,7 @@ static void test_unsupported_instance_extensions(void) } }
-static void test_unsupported_device_extensions(VkPhysicalDevice vk_physical_device) +static void test_unsupported_device_extensions(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkDevice vk_device; unsigned int i; @@ -387,7 +387,7 @@ static void test_unsupported_device_extensions(VkPhysicalDevice vk_physical_devi } }
-static void test_private_data(VkPhysicalDevice vk_physical_device) +static void test_private_data(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { PFN_vkDestroyPrivateDataSlotEXT pfn_vkDestroyPrivateDataSlotEXT; PFN_vkCreatePrivateDataSlotEXT pfn_vkCreatePrivateDataSlotEXT; @@ -437,7 +437,218 @@ static void test_private_data(VkPhysicalDevice vk_physical_device) vkDestroyDevice(vk_device, NULL); }
-static void for_each_device(void (*test_func)(VkPhysicalDevice)) +uint32_t find_memory_type(VkPhysicalDevice vk_physical_device, VkMemoryPropertyFlagBits flags) +{ + VkPhysicalDeviceMemoryProperties properties = {0}; + unsigned int i; + + vkGetPhysicalDeviceMemoryProperties(vk_physical_device, &properties); + + for(i = 0; i < properties.memoryTypeCount; i++) + { + if (properties.memoryTypes[i].propertyFlags & flags) + return i; + } + return -1; +} + +static void test_external_memory(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) +{ + PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR; + PFN_vkGetMemoryWin32HandleKHR pfn_vkGetMemoryWin32HandleKHR; + VkPhysicalDeviceExternalBufferInfoKHR external_buffer_info; + VkExternalBufferPropertiesKHR external_buffer_properties; + VkMemoryDedicatedAllocateInfoKHR dedicated_alloc_info; + VkExportMemoryWin32HandleInfoKHR export_handle_info; + VkImportMemoryWin32HandleInfoKHR import_handle_info; + VkExportMemoryAllocateInfoKHR export_memory_info; + VkMemoryGetWin32HandleInfoKHR get_handle_info; + VkBufferCreateInfo buffer_create_info; + VkMemoryAllocateInfo alloc_info; + uint32_t queue_family_index; + HANDLE nt_handle, kmt_handle; + VkDeviceMemory vk_memory, vk_memory_import; + VkBuffer vk_buffer; + VkDevice vk_device; + VkResult vr; + + static const char *extensions[] = + { + "VK_KHR_dedicated_allocation", + "VK_KHR_external_memory", + "VK_KHR_external_memory_win32", + }; + + pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR = + (void*) vkGetInstanceProcAddr(vk_instance, "vkGetPhysicalDeviceExternalBufferPropertiesKHR"); + + if ((vr = create_device(vk_physical_device, ARRAY_SIZE(extensions), extensions, NULL, &vk_device))) + { + skip("Failed to create device with external memory extensions, VkResult %d.\n", vr); + return; + } + + pfn_vkGetMemoryWin32HandleKHR = (void *) vkGetDeviceProcAddr(vk_device, "vkGetMemoryWin32HandleKHR"); + + find_queue_family(vk_physical_device, VK_QUEUE_GRAPHICS_BIT, &queue_family_index); + + /* Most implementations only support exporting dedicated allocations */ + + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.pNext = NULL; + buffer_create_info.flags = 0; + buffer_create_info.size = 1; + buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + buffer_create_info.sharingMode = VK_SHARING_MODE_CONCURRENT; + buffer_create_info.queueFamilyIndexCount = 1; + buffer_create_info.pQueueFamilyIndices = &queue_family_index; + if ((vr = vkCreateBuffer(vk_device, &buffer_create_info, NULL, &vk_buffer))) + { + skip("Failed to create generic buffer, VkResult %d.\n", vr); + vkDestroyDevice(vk_device, NULL); + return; + } + + dedicated_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + dedicated_alloc_info.pNext = NULL; + dedicated_alloc_info.image = VK_NULL_HANDLE; + dedicated_alloc_info.buffer = vk_buffer; + + external_buffer_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR; + external_buffer_info.pNext = NULL; + external_buffer_info.flags = 0; + external_buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + external_buffer_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR; + + memset(&external_buffer_properties, 0, sizeof(external_buffer_properties)); + external_buffer_properties.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR; + + pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR(vk_physical_device, &external_buffer_info, &external_buffer_properties); + + if (!(external_buffer_properties.externalMemoryProperties.externalMemoryFeatures & + (VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR|VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR))) + skip("With desired parameters, buffers are not exportable to and importable from an NT handle.\n"); + else + { + ok(external_buffer_properties.externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR, + "Unexpected compatibleHandleTypes %#x.\n", external_buffer_properties.externalMemoryProperties.compatibleHandleTypes); + + export_memory_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + export_memory_info.pNext = &dedicated_alloc_info; + export_memory_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR; + + export_handle_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + export_handle_info.pNext = &export_memory_info; + export_handle_info.name = L"wine_test_buffer_export_name"; + export_handle_info.dwAccess = GENERIC_ALL; + export_handle_info.pAttributes = NULL; + + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = &export_handle_info; + alloc_info.allocationSize = 1; + alloc_info.memoryTypeIndex = find_memory_type(vk_physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + ok(alloc_info.memoryTypeIndex != -1, "Device local memory type index was not found.\n"); + + vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + + get_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR; + get_handle_info.pNext = NULL; + get_handle_info.memory = vk_memory; + get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR; + + vr = pfn_vkGetMemoryWin32HandleKHR(vk_device, &get_handle_info, &nt_handle); + ok(vr == VK_SUCCESS, "vkGetMemoryWin32HandleKHR failed, VkResult %d.\n", vr); + + import_handle_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + import_handle_info.pNext = &dedicated_alloc_info; + import_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR; + import_handle_info.handle = nt_handle; + import_handle_info.name = NULL; + + alloc_info.pNext = &import_handle_info; + + vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + ok(vk_memory_import != vk_memory, "Expected new memory object.\n"); + + vkFreeMemory(vk_device, vk_memory_import, NULL); + + import_handle_info.handle = NULL; + import_handle_info.name = L"wine_test_buffer_export_name"; + + vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + todo_wine + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + if (vr == VK_SUCCESS) + { + ok(vk_memory_import != vk_memory, "Expected new memory object.\n"); + vkFreeMemory(vk_device, vk_memory_import, NULL); + } + vkFreeMemory(vk_device, vk_memory, NULL); + CloseHandle(nt_handle); + } + + external_buffer_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR; + + memset(&external_buffer_properties, 0, sizeof(external_buffer_properties)); + external_buffer_properties.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR; + + pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR(vk_physical_device, &external_buffer_info, &external_buffer_properties); + + if (!(external_buffer_properties.externalMemoryProperties.externalMemoryFeatures & + (VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR|VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR))) + skip("With desired parameters, buffers are not exportable to and importable from a KMT handle.\n"); + else + { + ok(external_buffer_properties.externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR, + "Unexpected compatibleHandleTypes %#x.\n", external_buffer_properties.externalMemoryProperties.compatibleHandleTypes); + + export_memory_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + export_memory_info.pNext = &dedicated_alloc_info; + export_memory_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR; + + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = &export_memory_info; + alloc_info.allocationSize = 1; + alloc_info.memoryTypeIndex = find_memory_type(vk_physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + ok(alloc_info.memoryTypeIndex != -1, "Device local memory type index was not found.\n"); + + vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + + get_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR; + get_handle_info.pNext = NULL; + get_handle_info.memory = vk_memory; + get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR; + + vr = pfn_vkGetMemoryWin32HandleKHR(vk_device, &get_handle_info, &kmt_handle); + ok(vr == VK_SUCCESS, "vkGetMemoryWin32HandleKHR failed, VkResult %d.\n", vr); + + import_handle_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + import_handle_info.pNext = &dedicated_alloc_info; + import_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR; + import_handle_info.handle = kmt_handle; + import_handle_info.name = NULL; + + alloc_info.pNext = &import_handle_info; + + vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + ok(vk_memory_import != vk_memory, "Expected new memory object.\n"); + + vkFreeMemory(vk_device, vk_memory_import, NULL); + vkFreeMemory(vk_device, vk_memory, NULL); + } + + vkDestroyBuffer(vk_device, vk_buffer, NULL); + vkDestroyDevice(vk_device, NULL); +} + +static void for_each_device(void (*test_func)(VkInstance,VkPhysicalDevice), uint32_t extension_count, + const char * const *enabled_extensions) { VkPhysicalDevice *vk_physical_devices; VkInstance vk_instance; @@ -445,7 +656,7 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) uint32_t count; VkResult vr;
- if ((vr = create_instance_skip(0, NULL, &vk_instance)) < 0) + if ((vr = create_instance_skip(extension_count, enabled_extensions, &vk_instance)) < 0) return; ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr);
@@ -463,7 +674,7 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr);
for (i = 0; i < count; ++i) - test_func(vk_physical_devices[i]); + test_func(vk_instance, vk_physical_devices[i]);
heap_free(vk_physical_devices);
@@ -472,13 +683,16 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice))
START_TEST(vulkan) { + static const char *external_memory_capabilities = "VK_KHR_external_memory_capabilities"; + test_instance_version(); - for_each_device(enumerate_physical_device); + for_each_device(enumerate_physical_device, 0, NULL); test_enumerate_physical_device2(); - for_each_device(enumerate_device_queues); + for_each_device(enumerate_device_queues, 0, NULL); test_physical_device_groups(); - for_each_device(test_destroy_command_pool); + for_each_device(test_destroy_command_pool, 0, NULL); test_unsupported_instance_extensions(); - for_each_device(test_unsupported_device_extensions); - for_each_device(test_private_data); + for_each_device(test_unsupported_device_extensions, 0, NULL); + for_each_device(test_private_data, 0, NULL); + for_each_device(test_external_memory, 1, &external_memory_capabilities); } diff --git a/dlls/winevulkan/loader_thunks.c b/dlls/winevulkan/loader_thunks.c index 69fcd44160e..471999a3821 100644 --- a/dlls/winevulkan/loader_thunks.c +++ b/dlls/winevulkan/loader_thunks.c @@ -1448,6 +1448,16 @@ VkResult WINAPI vkGetMemoryHostPointerPropertiesEXT(VkDevice device, VkExternalM return unix_funcs->p_vkGetMemoryHostPointerPropertiesEXT(device, handleType, pHostPointer, pMemoryHostPointerProperties); }
+VkResult WINAPI vkGetMemoryWin32HandleKHR(VkDevice device, const VkMemoryGetWin32HandleInfoKHR *pGetWin32HandleInfo, HANDLE *pHandle) +{ + return unix_funcs->p_vkGetMemoryWin32HandleKHR(device, pGetWin32HandleInfo, pHandle); +} + +VkResult WINAPI vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *pMemoryWin32HandleProperties) +{ + return unix_funcs->p_vkGetMemoryWin32HandlePropertiesKHR(device, handleType, handle, pMemoryWin32HandleProperties); +} + VkResult WINAPI vkGetPerformanceParameterINTEL(VkDevice device, VkPerformanceParameterTypeINTEL parameter, VkPerformanceValueINTEL *pValue) { return unix_funcs->p_vkGetPerformanceParameterINTEL(device, parameter, pValue); @@ -2223,6 +2233,8 @@ static const struct vulkan_func vk_device_dispatch_table[] = {"vkGetImageSparseMemoryRequirements2KHR", &vkGetImageSparseMemoryRequirements2KHR}, {"vkGetImageSubresourceLayout", &vkGetImageSubresourceLayout}, {"vkGetMemoryHostPointerPropertiesEXT", &vkGetMemoryHostPointerPropertiesEXT}, + {"vkGetMemoryWin32HandleKHR", &vkGetMemoryWin32HandleKHR}, + {"vkGetMemoryWin32HandlePropertiesKHR", &vkGetMemoryWin32HandlePropertiesKHR}, {"vkGetPerformanceParameterINTEL", &vkGetPerformanceParameterINTEL}, {"vkGetPipelineCacheData", &vkGetPipelineCacheData}, {"vkGetPipelineExecutableInternalRepresentationsKHR", &vkGetPipelineExecutableInternalRepresentationsKHR}, diff --git a/dlls/winevulkan/loader_thunks.h b/dlls/winevulkan/loader_thunks.h index e7257cef508..031b7f5652b 100644 --- a/dlls/winevulkan/loader_thunks.h +++ b/dlls/winevulkan/loader_thunks.h @@ -304,6 +304,8 @@ struct unix_funcs void (WINAPI *p_vkGetImageSparseMemoryRequirements2KHR)(VkDevice, const VkImageSparseMemoryRequirementsInfo2 *, uint32_t *, VkSparseImageMemoryRequirements2 *); void (WINAPI *p_vkGetImageSubresourceLayout)(VkDevice, VkImage, const VkImageSubresource *, VkSubresourceLayout *); VkResult (WINAPI *p_vkGetMemoryHostPointerPropertiesEXT)(VkDevice, VkExternalMemoryHandleTypeFlagBits, const void *, VkMemoryHostPointerPropertiesEXT *); + VkResult (WINAPI *p_vkGetMemoryWin32HandleKHR)(VkDevice, const VkMemoryGetWin32HandleInfoKHR *, HANDLE *); + VkResult (WINAPI *p_vkGetMemoryWin32HandlePropertiesKHR)(VkDevice, VkExternalMemoryHandleTypeFlagBits, HANDLE, VkMemoryWin32HandlePropertiesKHR *); VkResult (WINAPI *p_vkGetPerformanceParameterINTEL)(VkDevice, VkPerformanceParameterTypeINTEL, VkPerformanceValueINTEL *); VkResult (WINAPI *p_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT)(VkPhysicalDevice, uint32_t *, VkTimeDomainEXT *); VkResult (WINAPI *p_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV)(VkPhysicalDevice, uint32_t *, VkCooperativeMatrixPropertiesNV *); diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index b1877acda17..0a0e3bac299 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -99,7 +99,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_pipeline_creation_feedback", "VK_GOOGLE_display_timing", "VK_KHR_external_fence_win32", - "VK_KHR_external_memory_win32", "VK_KHR_external_semaphore_win32", # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work. @@ -109,7 +108,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_external_memory_dma_buf", "VK_EXT_image_drm_format_modifier", "VK_KHR_external_fence_fd", - "VK_KHR_external_memory_fd", "VK_KHR_external_semaphore_fd",
# Extensions which require callback handling @@ -154,6 +152,8 @@ class ThunkType(Enum): # - PRIVATE thunks can be used in custom implementations for # struct conversion. # - loader_thunk sets whether to create a thunk for unix_funcs. +# - host_only sets whether to preclude the function from the +# win32-facing dispatch table. FUNCTION_OVERRIDES = { # Global functions "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, @@ -169,7 +169,7 @@ FUNCTION_OVERRIDES = { "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE}, "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, - "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, @@ -178,11 +178,13 @@ FUNCTION_OVERRIDES = {
# Device functions "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkAllocateMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkFreeMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE}, "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, @@ -213,7 +215,7 @@ FUNCTION_OVERRIDES = { "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
# VK_KHR_external_memory_capabilities - "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
# VK_KHR_external_semaphore_capabilities @@ -249,12 +251,22 @@ FUNCTION_OVERRIDES = { # VK_EXT_debug_marker "vkDebugMarkerSetObjectNameEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkDebugMarkerSetObjectTagEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE}, + + # VK_KHR_external_memory_win32 + "vkGetMemoryWin32HandleKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetMemoryWin32HandlePropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + + # VK_KHR_external_memory_fd + "vkGetMemoryFdKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "host_only" : True}, + "vkGetMemoryFdPropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "host_only" : True}, }
-STRUCT_CHAIN_CONVERSIONS = [ - "VkDeviceCreateInfo", - "VkInstanceCreateInfo", -] +STRUCT_CHAIN_CONVERSIONS = { + "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"], + "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"], + "VkPhysicalDeviceImageFormatInfo2": [], + "VkMemoryAllocateInfo": ["VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR", "VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR"], +}
class Direction(Enum): @@ -532,6 +544,7 @@ class VkFunction(object): self.driver = func_info["driver"] if func_info else False self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC + self.host_only = func_info["host_only"] if func_info and "host_only" in func_info else False
# Required is set while parsing which APIs and types are required # and is used by the code generation. @@ -651,6 +664,9 @@ class VkFunction(object): def needs_private_thunk(self): return self.thunk_type == ThunkType.PRIVATE
+ def needs_win_exclusion(self): + return self.host_only + def pfn(self, prefix="p", call_conv=None, conv=False): """ Create function pointer. """
@@ -1028,6 +1044,8 @@ class VkHandle(object): return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name) if self.name == "VkSurfaceKHR": return "wine_surface_from_handle({0})->surface".format(name) + if self.name == "VkDeviceMemory": + return "wine_dev_mem_from_handle({0})->dev_mem".format(name)
native_handle_name = None
@@ -2115,9 +2133,10 @@ class FreeFunction(object):
class StructChainConversionFunction(object): - def __init__(self, direction, struct): + def __init__(self, direction, struct, ignores): self.direction = direction self.struct = struct + self.ignores = ignores self.type = struct.name
self.name = "convert_{0}_struct_chain".format(self.type) @@ -2143,8 +2162,8 @@ class StructChainConversionFunction(object): body += " {\n"
# Ignore to not confuse host loader. - body += " case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO:\n" - body += " case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO:\n" + for i in self.ignores: + body += " case {0}:\n".format(i) body += " break;\n\n"
for e in self.struct.struct_extensions: @@ -2153,6 +2172,9 @@ class StructChainConversionFunction(object):
stype = next(x for x in e.members if x.name == "sType")
+ if stype.values in self.ignores: + continue + body += " case {0}:\n".format(stype.values) body += " {\n"
@@ -2250,7 +2272,7 @@ class VkGenerator(object):
for struct in self.registry.structs: if struct.name in STRUCT_CHAIN_CONVERSIONS: - self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct)) + self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct, STRUCT_CHAIN_CONVERSIONS[struct.name])) self.struct_chain_conversions.append(FreeStructChainFunction(struct))
def _generate_copyright(self, f, spec_file=False): @@ -2292,6 +2314,9 @@ class VkGenerator(object): if not vk_func.is_required(): continue
+ if vk_func.needs_win_exclusion(): + continue + if vk_func.is_global_func(): continue
@@ -2382,6 +2407,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type == ThunkType.NONE: continue
@@ -2403,6 +2430,8 @@ class VkGenerator(object): continue if vk_func.needs_thunk() and not vk_func.needs_private_thunk(): continue + if vk_func.needs_win_exclusion(): + continue
if vk_func.is_core_func(): f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix=prefix))) @@ -2510,6 +2539,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type != ThunkType.PUBLIC: continue
@@ -2519,6 +2550,8 @@ class VkGenerator(object): for vk_func in self.registry.device_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue
f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2527,6 +2560,8 @@ class VkGenerator(object): for vk_func in self.registry.phys_dev_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue
f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2535,6 +2570,8 @@ class VkGenerator(object): for vk_func in self.registry.instance_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue
f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2592,6 +2629,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type == ThunkType.NONE: continue
diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 45eda78e997..e95860cb3fa 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -25,6 +25,7 @@ #include <time.h> #include <stdarg.h> #include <stdlib.h> +#include <unistd.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -34,6 +35,8 @@ #include "winuser.h" #include "winternl.h"
+#include "wine/server.h" + #include "vulkan_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(vulkan); @@ -265,6 +268,14 @@ static struct VkPhysicalDevice_T *wine_vk_physical_device_alloc(struct VkInstanc { if (wine_vk_device_extension_supported(host_properties[i].extensionName)) { + if (!strcmp(host_properties[i].extensionName, "VK_KHR_external_memory_fd")) + { + TRACE("Substituting VK_KHR_external_memory_fd for VK_KHR_external_memory_win32\n"); + + snprintf(host_properties[i].extensionName, sizeof(host_properties[i].extensionName), + VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME); + host_properties[i].specVersion = VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION; + } TRACE("Enabling extension '%s' for physical device %p\n", host_properties[i].extensionName, object); num_properties++; } @@ -368,12 +379,16 @@ static void wine_vk_device_free_create_info(VkDeviceCreateInfo *create_info) }
free_VkDeviceCreateInfo_struct_chain(create_info); + + if (create_info->enabledExtensionCount) + free((void *)create_info->ppEnabledExtensionNames); }
static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src, VkDeviceCreateInfo *dst) { VkDeviceGroupDeviceCreateInfo *group_info; + const char **enabled_extensions; unsigned int i; VkResult res;
@@ -406,6 +421,39 @@ static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src dst->enabledLayerCount = 0; dst->ppEnabledLayerNames = NULL;
+ if (src->enabledExtensionCount > 0) + { + enabled_extensions = calloc(src->enabledExtensionCount, sizeof(*src->ppEnabledExtensionNames)); + if (!enabled_extensions) + { + if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO))) + free((void *)group_info->pPhysicalDevices); + free_VkDeviceCreateInfo_struct_chain(dst); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + for (i = 0; i < src->enabledExtensionCount; i++) + { + if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_fd")) + { + if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO))) + free((void *)group_info->pPhysicalDevices); + free_VkDeviceCreateInfo_struct_chain(dst); + free(enabled_extensions); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + else if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_win32")) + { + enabled_extensions[i] = "VK_KHR_external_memory_fd"; + } + else + { + enabled_extensions[i] = src->ppEnabledExtensionNames[i]; + } + } + dst->ppEnabledExtensionNames = enabled_extensions; + } + TRACE("Enabled %u extensions.\n", dst->enabledExtensionCount); for (i = 0; i < dst->enabledExtensionCount; i++) { @@ -1300,57 +1348,106 @@ void WINAPI wine_vkGetPhysicalDeviceExternalFencePropertiesKHR(VkPhysicalDevice void WINAPI wine_vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice phys_dev, const VkPhysicalDeviceExternalBufferInfo *buffer_info, VkExternalBufferProperties *properties) { + VkPhysicalDeviceExternalBufferInfo buffer_info_dup = *buffer_info; + TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties); - memset(&properties->externalMemoryProperties, 0, sizeof(properties->externalMemoryProperties)); + + if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + + thunk_vkGetPhysicalDeviceExternalBufferProperties(phys_dev, &buffer_info_dup, properties); + + if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + properties->externalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + properties->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; }
void WINAPI wine_vkGetPhysicalDeviceExternalBufferPropertiesKHR(VkPhysicalDevice phys_dev, const VkPhysicalDeviceExternalBufferInfo *buffer_info, VkExternalBufferProperties *properties) { + VkPhysicalDeviceExternalBufferInfo buffer_info_dup = *buffer_info; + TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties); - memset(&properties->externalMemoryProperties, 0, sizeof(properties->externalMemoryProperties)); + + if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + + thunk_vkGetPhysicalDeviceExternalBufferPropertiesKHR(phys_dev, &buffer_info_dup, properties); + + if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + properties->externalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + properties->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; }
-VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice phys_dev, +static VkResult wine_vk_get_physical_device_image_format_properties_2(VkPhysicalDevice phys_dev, + VkResult (*p_vkGetPhysicalDeviceImageFormatProperties2)(VkPhysicalDevice, const VkPhysicalDeviceImageFormatInfo2 *, VkImageFormatProperties2 *), const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) { + VkPhysicalDeviceExternalImageFormatInfo *external_image_info_host = NULL; + const VkPhysicalDeviceExternalImageFormatInfo *external_image_info; + VkPhysicalDeviceImageFormatInfo2 format_info_host = *format_info; VkExternalImageFormatProperties *external_image_properties; VkResult res;
- TRACE("%p, %p, %p\n", phys_dev, format_info, properties); + if ((external_image_info = wine_vk_find_struct(format_info, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO))) + { + if (external_image_info->handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + if ((res = convert_VkPhysicalDeviceImageFormatInfo2_struct_chain(format_info->pNext, &format_info_host)) < 0) + { + WARN("Failed to convert VkPhysicalDeviceImageFormatInfo2 pNext chain, res=%d.\n", res); + return res; + } + + external_image_info_host = wine_vk_find_struct(&format_info_host, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO); + external_image_info_host->handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + } + if (external_image_info->handleType &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + WARN("Unsupported handle type %#x.\n", external_image_info->handleType); + return VK_ERROR_FORMAT_NOT_SUPPORTED; + } + } + + res = p_vkGetPhysicalDeviceImageFormatProperties2(phys_dev, &format_info_host, properties);
- res = thunk_vkGetPhysicalDeviceImageFormatProperties2(phys_dev, format_info, properties); + if (external_image_info_host) + free_VkPhysicalDeviceImageFormatInfo2_struct_chain(&format_info_host);
if ((external_image_properties = wine_vk_find_struct(properties, EXTERNAL_IMAGE_FORMAT_PROPERTIES))) { VkExternalMemoryProperties *p = &external_image_properties->externalMemoryProperties; - p->externalMemoryFeatures = 0; - p->exportFromImportedHandleTypes = 0; - p->compatibleHandleTypes = 0; + if (p->exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + { + p->exportFromImportedHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + p->exportFromImportedHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + } + if (p->compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + { + p->compatibleHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + p->compatibleHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + } }
return res; }
-VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice phys_dev, +VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice phys_dev, const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) { - VkExternalImageFormatProperties *external_image_properties; - VkResult res; - TRACE("%p, %p, %p\n", phys_dev, format_info, properties);
- res = thunk_vkGetPhysicalDeviceImageFormatProperties2KHR(phys_dev, format_info, properties); + return wine_vk_get_physical_device_image_format_properties_2(phys_dev, thunk_vkGetPhysicalDeviceImageFormatProperties2, format_info, properties); +}
- if ((external_image_properties = wine_vk_find_struct(properties, EXTERNAL_IMAGE_FORMAT_PROPERTIES))) - { - VkExternalMemoryProperties *p = &external_image_properties->externalMemoryProperties; - p->externalMemoryFeatures = 0; - p->exportFromImportedHandleTypes = 0; - p->compatibleHandleTypes = 0; - } +VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice phys_dev, + const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) +{ + TRACE("%p, %p, %p\n", phys_dev, format_info, properties);
- return res; + return wine_vk_get_physical_device_image_format_properties_2(phys_dev, thunk_vkGetPhysicalDeviceImageFormatProperties2KHR, format_info, properties); }
/* From ntdll/unix/sync.c */ @@ -1860,3 +1957,249 @@ VkResult WINAPI wine_vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebu
return thunk_vkDebugMarkerSetObjectNameEXT(device, &wine_name_info); } + +static HANDLE create_video_resource(int fd, LPCWSTR name) +{ + HANDLE ret = INVALID_HANDLE_VALUE; + + if (name) + FIXME("Naming video resources not supported.\n"); + + wine_server_fd_to_handle(fd, GENERIC_ALL, 0, &ret); + + return ret; +} + +VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *allocate_info, + const VkAllocationCallbacks *allocator, VkDeviceMemory *memory_out) +{ + const VkImportMemoryWin32HandleInfoKHR *handle_import_info = NULL; + const VkExportMemoryWin32HandleInfoKHR *handle_export_info = NULL; + VkMemoryAllocateInfo allocate_info_dup = *allocate_info; + VkExportMemoryAllocateInfo *export_info = NULL; + VkImportMemoryFdInfoKHR fd_import_info; + const VkBaseOutStructure *header; + struct wine_dev_mem *object; + VkResult res; + int fd; + +#if defined(USE_STRUCT_CONVERSION) + VkMemoryAllocateInfo_host allocate_info_host; + VkMemoryGetFdInfoKHR_host get_fd_info; +#else + VkMemoryAllocateInfo allocate_info_host; + VkMemoryGetFdInfoKHR get_fd_info; +#endif + + TRACE("%p %p %p %p\n", device, allocate_info, allocator, memory_out); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + if ((res = convert_VkMemoryAllocateInfo_struct_chain(allocate_info->pNext, &allocate_info_dup)) < 0) + { + WARN("Failed to convert VkMemoryAllocateInfo pNext chain, res=%d.\n", res); + return res; + } + + if (!(object = calloc(1, sizeof(*object)))) + { + free_VkMemoryAllocateInfo_struct_chain(&allocate_info_dup); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + object->dev_mem = VK_NULL_HANDLE; + object->handle = INVALID_HANDLE_VALUE; + fd_import_info.fd = -1; + fd_import_info.pNext = NULL; + + /* find and process handle import/export info and grab it */ + for (header = allocate_info->pNext; header; header = header->pNext) + { + switch (header->sType) + { + case VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR: + handle_import_info = (const VkImportMemoryWin32HandleInfoKHR *)header; + break; + case VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR: + handle_export_info = (const VkExportMemoryWin32HandleInfoKHR *)header; + if (handle_export_info->pAttributes && handle_export_info->pAttributes->lpSecurityDescriptor) + FIXME("Support for custom security descriptor not implemented.\n"); + break; + default: + break; + } + } + + for (header = allocate_info_dup.pNext; header; header = header->pNext) + { + if (header->sType == VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO) + { + export_info = (VkExportMemoryAllocateInfo *)header; + object->handle_types = export_info->handleTypes; + if (object->handle_types &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto done; + } + if (object->handle_types) + export_info->handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + break; + } + } + + /* Important to note is that Vulkan does consume imported FDs, but it doesn't consume imported HANDLEs */ + if (handle_import_info) + { + fd_import_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; + fd_import_info.pNext = allocate_info_dup.pNext; + fd_import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + + switch (handle_import_info->handleType) + { + case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: + if (handle_import_info->handle) + NtDuplicateObject( NtCurrentProcess(), handle_import_info->handle, NtCurrentProcess(), &object->handle, 0, 0, DUPLICATE_SAME_ACCESS ); + else if (handle_import_info->name) + FIXME("Importing device memory by resource name not supported.\n"); + break; + default: + WARN("Invalid handle type %08x passed in.\n", handle_import_info->handleType); + res = VK_ERROR_INVALID_EXTERNAL_HANDLE; + goto done; + } + + if (object->handle != INVALID_HANDLE_VALUE) + wine_server_handle_to_fd(object->handle, FILE_READ_DATA, &fd_import_info.fd, NULL); + + if (fd_import_info.fd == -1) + { + TRACE("Couldn't access resource handle or name. type=%08x handle=%p name=%s\n", handle_import_info->handleType, handle_import_info->handle, + handle_import_info->name ? debugstr_w(handle_import_info->name) : ""); + res = VK_ERROR_INVALID_EXTERNAL_HANDLE; + goto done; + } + } + + allocate_info_host.sType = allocate_info_dup.sType; + allocate_info_host.pNext = fd_import_info.fd == -1 ? allocate_info_dup.pNext : &fd_import_info; + allocate_info_host.allocationSize = allocate_info_dup.allocationSize; + allocate_info_host.memoryTypeIndex = allocate_info_dup.memoryTypeIndex; + + if ((res = device->funcs.p_vkAllocateMemory(device->device, &allocate_info_host, NULL, &object->dev_mem)) == VK_SUCCESS) + { + if (object->handle == INVALID_HANDLE_VALUE && export_info && export_info->handleTypes) + { + get_fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + get_fd_info.pNext = NULL; + get_fd_info.memory = object->dev_mem; + get_fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + + if (device->funcs.p_vkGetMemoryFdKHR(device->device, &get_fd_info, &fd) == VK_SUCCESS) + { + object->handle = create_video_resource(fd, handle_export_info ? handle_export_info->name : NULL); + object->access = handle_export_info ? handle_export_info->dwAccess : GENERIC_ALL; + if (handle_export_info && handle_export_info->pAttributes) + object->inherit = handle_export_info->pAttributes->bInheritHandle; + else + object->inherit = FALSE; + close(fd); + } + + if (object->handle == INVALID_HANDLE_VALUE) + { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto done; + } + } + + *memory_out = wine_dev_mem_to_handle(object); + } + + done: + + if (res != VK_SUCCESS) + { + if (object->dev_mem != VK_NULL_HANDLE) + device->funcs.p_vkFreeMemory(device->device, object->dev_mem, NULL); + if (fd_import_info.fd != -1) + close(fd_import_info.fd); + if (object->handle != INVALID_HANDLE_VALUE) + NtClose(object->handle); + free(object); + } + + free_VkMemoryAllocateInfo_struct_chain(&allocate_info_dup); + + return res; +} + +VkResult WINAPI wine_vkGetMemoryWin32HandleKHR(VkDevice device, + const VkMemoryGetWin32HandleInfoKHR *handle_info, HANDLE *handle) +{ + struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle_info->memory); + const VkBaseInStructure *chain; + + TRACE("%p, %p %p\n", device, handle_info, handle); + + if (!(dev_mem->handle_types & handle_info->handleType)) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + if ((chain = handle_info->pNext)) + FIXME("Ignoring a linked structure of type %u.\n", chain->sType); + + switch(handle_info->handleType) + { + case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: + return !NtDuplicateObject( NtCurrentProcess(), dev_mem->handle, NtCurrentProcess(), handle, dev_mem->access, dev_mem->inherit ? OBJ_INHERIT : 0, 0) ? + VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + default: + return VK_ERROR_UNKNOWN; + } +} + +void WINAPI wine_vkFreeMemory(VkDevice device, VkDeviceMemory handle, const VkAllocationCallbacks *allocator) +{ + struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle); + + TRACE("%p 0x%s, %p\n", device, wine_dbgstr_longlong(handle), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + device->funcs.p_vkFreeMemory(device->device, dev_mem->dev_mem, NULL); + if (dev_mem->handle != INVALID_HANDLE_VALUE) + NtClose(dev_mem->handle); + free(dev_mem); +} + +VkResult WINAPI wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, + VkExternalMemoryHandleTypeFlagBits type, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *properties) +{ + VkMemoryFdPropertiesKHR fd_props; + VkResult res; + int fd = -1; + + TRACE("%p %u %p %p\n", device, type, handle, properties); + + if (type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + wine_server_handle_to_fd(handle, FILE_READ_DATA, &fd, NULL); + } + + if (fd == -1) + return VK_ERROR_INVALID_EXTERNAL_HANDLE; + + res = device->funcs.p_vkGetMemoryFdPropertiesKHR(device->device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, fd, &fd_props); + + close(fd); + + if (res != VK_SUCCESS) + return res; + + properties->sType = VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR; + properties->pNext = NULL; + properties->memoryTypeBits = fd_props.memoryTypeBits; + + return res; +} diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 83dc90ca15e..468ab890dab 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -204,6 +204,28 @@ static inline VkSurfaceKHR wine_surface_to_handle(struct wine_surface *surface) return (VkSurfaceKHR)(uintptr_t)surface; }
+struct wine_dev_mem +{ + VkDeviceMemory dev_mem; + + VkExternalMemoryHandleTypeFlagBits handle_types; + + BOOL inherit; + DWORD access; + + HANDLE handle; +}; + +static inline struct wine_dev_mem *wine_dev_mem_from_handle(VkDeviceMemory handle) +{ + return (struct wine_dev_mem *)(uintptr_t)handle; +} + +static inline VkDeviceMemory wine_dev_mem_to_handle(struct wine_dev_mem *dev_mem) +{ + return (VkDeviceMemory)(uintptr_t)dev_mem; +} + BOOL wine_vk_device_extension_supported(const char *name) DECLSPEC_HIDDEN; BOOL wine_vk_instance_extension_supported(const char *name) DECLSPEC_HIDDEN;
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/ntoskrnl.exe/ntoskrnl.c | 6 ++++++ dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 1 + include/ddk/wdm.h | 1 + 3 files changed, 8 insertions(+)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 0580231d8e3..5b84c626547 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -243,6 +243,12 @@ void ObReferenceObject( void *obj ) LeaveCriticalSection( &obref_cs ); }
+ULONG WINAPI ObGetObjectPointerCount( void *object ) +{ + struct object_header *header = (struct object_header *)object - 1; + return header->ref; +} + /*********************************************************************** * ObGetObjectType (NTOSKRNL.EXE.@) */ diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index 2e5e2e6e11b..6426bc3968a 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -834,6 +834,7 @@ @ stdcall ObGetFilterVersion() @ stub ObGetObjectSecurity @ stdcall ObGetObjectType(ptr) +@ stdcall ObGetObjectPointerCount(ptr) @ stub ObInsertObject @ stub ObLogSecurityDescriptor @ stub ObMakeTemporaryObject diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index 51097bfe3ab..1e7ce63f776 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1802,6 +1802,7 @@ static inline void *MmGetSystemAddressForMdlSafe(MDL *mdl, ULONG priority) void FASTCALL ObfReferenceObject(void*); void WINAPI ObDereferenceObject(void*); USHORT WINAPI ObGetFilterVersion(void); +ULONG WINAPI ObGetObjectPointerCount(void*); NTSTATUS WINAPI ObRegisterCallbacks(POB_CALLBACK_REGISTRATION, void**); NTSTATUS WINAPI ObReferenceObjectByHandle(HANDLE,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,PVOID*,POBJECT_HANDLE_INFORMATION); NTSTATUS WINAPI ObReferenceObjectByName(UNICODE_STRING*,ULONG,ACCESS_STATE*,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,void*,void**);
Beyond whether this approach is acceptable, one thing I'd appreciate feedback on is a better name for the wine driver. winevideo.sys seems too generic and winevideoresource.sys seems too verbose. --- configure.ac | 1 + dlls/ntoskrnl.exe/ntoskrnl.c | 57 +++++- dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 + dlls/vulkan-1/tests/vulkan.c | 9 +- dlls/winevideo.sys/Makefile.in | 6 + dlls/winevideo.sys/winevideo.c | 284 ++++++++++++++++++++++++++ dlls/winevideo.sys/winevideo.sys.spec | 1 + dlls/winevulkan/vulkan.c | 82 +++++++- include/ddk/wdm.h | 1 + loader/wine.inf.in | 13 ++ 10 files changed, 443 insertions(+), 13 deletions(-) create mode 100644 dlls/winevideo.sys/Makefile.in create mode 100644 dlls/winevideo.sys/winevideo.c create mode 100644 dlls/winevideo.sys/winevideo.sys.spec
diff --git a/configure.ac b/configure.ac index 46bbb8a70f8..0071eeb0dd3 100644 --- a/configure.ac +++ b/configure.ac @@ -3823,6 +3823,7 @@ WINE_CONFIG_MAKEFILE(dlls/wineps16.drv16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/winepulse.drv) WINE_CONFIG_MAKEFILE(dlls/wineqtdecoder) WINE_CONFIG_MAKEFILE(dlls/wineusb.sys) +WINE_CONFIG_MAKEFILE(dlls/winevideo.sys) WINE_CONFIG_MAKEFILE(dlls/winevulkan) WINE_CONFIG_MAKEFILE(dlls/winex11.drv) WINE_CONFIG_MAKEFILE(dlls/wing.dll16,enable_win16) diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 5b84c626547..5156f3d3ab1 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -138,6 +138,10 @@ static HANDLE get_device_manager(void) struct object_header { LONG ref; + /* only blocks object from being freed, not released */ + LONG weak_ref; + /* an additional ref used to indicate whether server is done w/ obj */ + BOOL in_server; POBJECT_TYPE type; };
@@ -166,6 +170,7 @@ void *alloc_kernel_object( POBJECT_TYPE type, HANDLE handle, SIZE_T size, LONG r } SERVER_END_REQ; if (status) FIXME( "set_object_reference failed: %#x\n", status ); + else header->in_server = TRUE; }
header->ref = ref; @@ -197,7 +202,8 @@ void WINAPI ObDereferenceObject( void *obj ) { if (header->type->release) { - header->type->release( obj ); + if (!header->weak_ref) + header->type->release( obj ); } else { @@ -227,6 +233,13 @@ void ObReferenceObject( void *obj )
EnterCriticalSection( &obref_cs );
+ if (header->ref == 0 && !header->in_server) + { + ERR("Weak references may not be turned into strong references\n"); + LeaveCriticalSection( &obref_cs ); + return; + } + ref = ++header->ref; TRACE( "(%p) ref=%u\n", obj, ref ); if (ref == 1) @@ -246,7 +259,37 @@ void ObReferenceObject( void *obj ) ULONG WINAPI ObGetObjectPointerCount( void *object ) { struct object_header *header = (struct object_header *)object - 1; - return header->ref; + return header->ref + header->in_server; +} + +void CDECL wine_ob_weak_ref( void *object ) +{ + struct object_header *header = (struct object_header *) object - 1; + + EnterCriticalSection( &obref_cs ); + + header->weak_ref++; + + LeaveCriticalSection( &obref_cs ); +} + +void CDECL wine_ob_weak_deref( void *object ) +{ + struct object_header *header = (struct object_header *) object - 1; + + EnterCriticalSection( &obref_cs ); + + header->weak_ref--; + + if (!header->weak_ref && !header->ref && !header->in_server) + { + if (header->type->release) + header->type->release(object); + else + free_kernel_object(object); + } + + LeaveCriticalSection( &obref_cs ); }
/*********************************************************************** @@ -814,8 +857,16 @@ static NTSTATUS dispatch_volume( struct dispatch_context *context ) static NTSTATUS dispatch_free( struct dispatch_context *context ) { void *obj = wine_server_get_ptr( context->params.free.obj ); + struct object_header *header = (struct object_header *)obj - 1; + TRACE( "freeing %p object\n", obj ); - free_kernel_object( obj ); + + EnterCriticalSection(&obref_cs); + header->in_server = FALSE; + if (!header->weak_ref) + free_kernel_object( obj ); + LeaveCriticalSection(&obref_cs); + return STATUS_SUCCESS; }
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index 6426bc3968a..318344fe90f 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -1688,3 +1688,5 @@
@ cdecl wine_ntoskrnl_main_loop(long) @ cdecl wine_enumerate_root_devices(wstr) +@ cdecl wine_ob_weak_ref(ptr) +@ cdecl wine_ob_weak_deref(ptr) diff --git a/dlls/vulkan-1/tests/vulkan.c b/dlls/vulkan-1/tests/vulkan.c index c1350da70ee..53df0e9a8d6 100644 --- a/dlls/vulkan-1/tests/vulkan.c +++ b/dlls/vulkan-1/tests/vulkan.c @@ -579,13 +579,10 @@ static void test_external_memory(VkInstance vk_instance, VkPhysicalDevice vk_phy import_handle_info.name = L"wine_test_buffer_export_name";
vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); - todo_wine ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); - if (vr == VK_SUCCESS) - { - ok(vk_memory_import != vk_memory, "Expected new memory object.\n"); - vkFreeMemory(vk_device, vk_memory_import, NULL); - } + ok(vk_memory_import != vk_memory, "Expected new memory object.\n"); + + vkFreeMemory(vk_device, vk_memory_import, NULL); vkFreeMemory(vk_device, vk_memory, NULL); CloseHandle(nt_handle); } diff --git a/dlls/winevideo.sys/Makefile.in b/dlls/winevideo.sys/Makefile.in new file mode 100644 index 00000000000..ed185b9f071 --- /dev/null +++ b/dlls/winevideo.sys/Makefile.in @@ -0,0 +1,6 @@ +MODULE = winevideo.sys +IMPORTS = ntoskrnl +EXTRADLLFLAGS = -Wl,--subsystem,native -mno-cygwin + +C_SRCS = \ + winevideo.c diff --git a/dlls/winevideo.sys/winevideo.c b/dlls/winevideo.sys/winevideo.c new file mode 100644 index 00000000000..04e171bea3f --- /dev/null +++ b/dlls/winevideo.sys/winevideo.c @@ -0,0 +1,284 @@ +#include <stdarg.h> + +#define NONAMELESSUNION +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h" + +#include "ddk/wdm.h" + +#include "wine/debug.h" +#include "wine/server.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winevideo); + +#define IOCTL_VIDEO_RESOURCE_CREATE CTL_CODE(FILE_DEVICE_VIDEO, 0, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_VIDEO_RESOURCE_FIND_BY_NAME CTL_CODE(FILE_DEVICE_VIDEO, 1, METHOD_BUFFERED, FILE_READ_ACCESS) + +struct video_resource +{ + void *object; + LPWSTR name; +}; + +static struct video_resource *video_resources; +static unsigned int video_resources_size; + +extern void CDECL wine_ob_weak_ref( void *object ); +extern void CDECL wine_ob_weak_deref( void *object ); + +/* TODO: If/when ntoskrnl gets support for referencing user handles directly, remove this function */ +static void *reference_client_handle(obj_handle_t handle) +{ + HANDLE client_process, kernel_handle; + OBJECT_ATTRIBUTES attr; + void *object = NULL; + CLIENT_ID cid; + + attr.Length = sizeof(OBJECT_ATTRIBUTES); + attr.RootDirectory = 0; + attr.Attributes = OBJ_KERNEL_HANDLE; + attr.ObjectName = NULL; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + cid.UniqueProcess = PsGetCurrentProcessId(); + cid.UniqueThread = 0; + + if (NtOpenProcess(&client_process, PROCESS_ALL_ACCESS, &attr, &cid) != STATUS_SUCCESS) + return NULL; + + if (NtDuplicateObject(client_process, wine_server_ptr_handle(handle), NtCurrentProcess(), &kernel_handle, + 0, OBJ_KERNEL_HANDLE, DUPLICATE_SAME_ACCESS) != STATUS_SUCCESS) + { + NtClose(client_process); + return NULL; + } + + ObReferenceObjectByHandle(kernel_handle, 0, NULL, KernelMode, &object, NULL); + + NtClose(client_process); + NtClose(kernel_handle); + + return object; +} + +struct resource_create_in +{ + obj_handle_t handle; + WCHAR name[1]; +}; + +static NTSTATUS resource_create(void *buff, SIZE_T insize, SIZE_T outsize, IO_STATUS_BLOCK *iosb) +{ + struct resource_create_in *input = buff; + struct video_resource *resource = NULL; + int free_entry = -1; + unsigned int i; + void *object; + LPWSTR name; + + if (insize < offsetof(struct resource_create_in, name)) + return STATUS_INFO_LENGTH_MISMATCH; + + if (insize < sizeof(*input)) + name = NULL; + else if (input->name[ ((insize - offsetof(struct resource_create_in, name)) / sizeof(WCHAR)) - 1 ]) + return STATUS_INVALID_PARAMETER; + else + name = &input->name[0]; + + if (!(object = reference_client_handle(input->handle))) + return STATUS_INVALID_HANDLE; + + /* Find lowest free slot while checking for duplicate names */ + for (i = video_resources_size - 1; i < video_resources_size; i--) + { + resource = &video_resources[i]; + if (resource->object) + { + if (!ObGetObjectPointerCount(resource->object)) + { + wine_ob_weak_deref(resource->object); + if (resource->name) + { + ExFreePoolWithTag(resource->name, 0); + resource->name = NULL; + } + + free_entry = i; + } + else if (resource->object == object) + { + ObDereferenceObject(object); + return STATUS_INVALID_PARAMETER; + } + else if (resource->name && name && !wcscmp(name, resource->name)) + { + ObDereferenceObject(object); + return STATUS_OBJECT_NAME_COLLISION; + } + } + else + free_entry = i; + } + + if (free_entry == -1) + { + struct video_resource *new_resources = + ExAllocatePoolWithTag(NonPagedPool, sizeof(struct video_resource) * (video_resources_size + 1024), 0); + + if (video_resources) + { + memcpy(new_resources, video_resources, video_resources_size * sizeof(struct video_resource)); + ExFreePoolWithTag(video_resources, 0); + } + + memset(&new_resources[video_resources_size], 0, 1024 * sizeof (struct video_resource)); + + video_resources = new_resources; + free_entry = video_resources_size; + video_resources_size += 1024; + } + + resource = &video_resources[free_entry]; + + resource->object = object; + if (name) + { + resource->name = ExAllocatePoolWithTag(NonPagedPool, (wcslen(name) + 1) * sizeof(WCHAR), 0); + wcscpy(resource->name, name); + } + + wine_ob_weak_ref(object); + ObDereferenceObject(object); + + iosb->Information = 0; + return STATUS_SUCCESS; +} + +/* TODO: If/when ntoskrnl gets support for opening user handles directly, remove this function */ +static obj_handle_t open_client_handle(void *object) +{ + HANDLE client_process, kernel_handle, handle = NULL; + OBJECT_ATTRIBUTES attr; + CLIENT_ID cid; + + attr.Length = sizeof(OBJECT_ATTRIBUTES); + attr.RootDirectory = 0; + attr.Attributes = OBJ_KERNEL_HANDLE; + attr.ObjectName = NULL; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + cid.UniqueProcess = PsGetCurrentProcessId(); + cid.UniqueThread = 0; + + if (NtOpenProcess(&client_process, PROCESS_ALL_ACCESS, &attr, &cid) != STATUS_SUCCESS) + return 0; + + if (ObOpenObjectByPointer(object, 0, NULL, GENERIC_ALL, NULL, KernelMode, &kernel_handle) != STATUS_SUCCESS) + { + NtClose(client_process); + return 0; + } + + NtDuplicateObject(NtCurrentProcess(), kernel_handle, client_process, &handle, + 0, 0, DUPLICATE_SAME_ACCESS); + + NtClose(client_process); + NtClose(kernel_handle); + + return wine_server_obj_handle(handle); +} + +static NTSTATUS find_resource_by_name(void *buff, SIZE_T insize, SIZE_T outsize, IO_STATUS_BLOCK *iosb) +{ + LPCWSTR name = buff; + obj_handle_t ret; + unsigned int i; + + if (insize < sizeof(WCHAR)) + return STATUS_INVALID_PARAMETER; + if (name[(insize / sizeof(WCHAR)) - 1]) + return STATUS_INVALID_PARAMETER; + + for (i = 0; i < video_resources_size; i++) + { + if (video_resources[i].name && !wcscmp(name, video_resources[i].name)) + { + if (!ObGetObjectPointerCount(video_resources[i].object)) + break; + + if (!(ret = open_client_handle(video_resources[i].object))) + return STATUS_INTERNAL_ERROR; + + if (outsize < sizeof(obj_handle_t)) + return STATUS_INFO_LENGTH_MISMATCH; + + iosb->Information = sizeof(obj_handle_t); + *(obj_handle_t *)buff = ret; + + return STATUS_SUCCESS; + } + } + + return STATUS_OBJECT_NAME_NOT_FOUND; +} + +static NTSTATUS WINAPI winevideo_ioctl(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + NTSTATUS status; + + TRACE( "ioctl %x insize %u outsize %u\n", + irpsp->Parameters.DeviceIoControl.IoControlCode, + irpsp->Parameters.DeviceIoControl.InputBufferLength, + irpsp->Parameters.DeviceIoControl.OutputBufferLength ); + + switch (irpsp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_VIDEO_RESOURCE_CREATE: + status = resource_create( irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.InputBufferLength, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, + &irp->IoStatus ); + break; + case IOCTL_VIDEO_RESOURCE_FIND_BY_NAME: + status = find_resource_by_name( irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.InputBufferLength, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, + &irp->IoStatus ); + break; + default: + FIXME( "ioctl %x not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode ); + status = STATUS_NOT_SUPPORTED; + break; + } + + irp->IoStatus.u.Status = status; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return status; +} + +NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) +{ + static const WCHAR device_nameW[] = L"\Device\WineVideoResourceManager"; + static const WCHAR link_nameW[] = L"\??\WineVideoResourceManager"; + UNICODE_STRING device_name, link_name; + DEVICE_OBJECT *device; + NTSTATUS status; + + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = winevideo_ioctl; + + RtlInitUnicodeString(&device_name, device_nameW); + RtlInitUnicodeString(&link_name, link_nameW); + + if ((status = IoCreateDevice(driver, 0, &device_name, 0, 0, FALSE, &device))) + return status; + + return IoCreateSymbolicLink(&link_name, &device_name); +} diff --git a/dlls/winevideo.sys/winevideo.sys.spec b/dlls/winevideo.sys/winevideo.sys.spec new file mode 100644 index 00000000000..76421d7e35b --- /dev/null +++ b/dlls/winevideo.sys/winevideo.sys.spec @@ -0,0 +1 @@ +# nothing to export diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index e95860cb3fa..65f53aa679e 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -31,6 +31,7 @@ #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" +#include "winioctl.h" #include "winreg.h" #include "winuser.h" #include "winternl.h" @@ -502,10 +503,33 @@ static void wine_vk_device_free(struct VkDevice_T *device) free(device); }
+static HANDLE winevideo_device; + NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *driver, void *ptr_out) { + static const WCHAR manager_nameW[] = {'\','?','?','\','W','i','n','e','V','i','d','e','o','R','e','s','o','u','r','c','e','M','a','n','a','g','e','r',0}; + UNICODE_STRING manager_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + if (reason != DLL_PROCESS_ATTACH) return STATUS_SUCCESS;
+ RtlInitUnicodeString(&manager_name, manager_nameW); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = 0; + attr.ObjectName = &manager_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + if ((status = NtCreateFile(&winevideo_device, GENERIC_READ | GENERIC_WRITE, &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0))) + { + ERR("Failed to load the wine video-resource manager, status %#x.\n", status); + winevideo_device = NULL; + } + vk_funcs = driver; p_vkEnumerateInstanceVersion = vk_funcs->p_vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion"); *(const struct unix_funcs **)ptr_out = &loader_funcs; @@ -1958,18 +1982,68 @@ VkResult WINAPI wine_vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebu return thunk_vkDebugMarkerSetObjectNameEXT(device, &wine_name_info); }
+#define IOCTL_VIDEO_RESOURCE_CREATE CTL_CODE(FILE_DEVICE_VIDEO, 0, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +struct resource_create_in +{ + obj_handle_t handle; + WCHAR name[1]; +}; + static HANDLE create_video_resource(int fd, LPCWSTR name) { HANDLE ret = INVALID_HANDLE_VALUE; - - if (name) - FIXME("Naming video resources not supported.\n"); + struct resource_create_in *inbuff; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + DWORD in_size;
wine_server_fd_to_handle(fd, GENERIC_ALL, 0, &ret);
+ if (ret != INVALID_HANDLE_VALUE && winevideo_device) + { + in_size = offsetof(struct resource_create_in, name) + (name ? (lstrlenW(name) + 1) * sizeof(WCHAR) : 0); + inbuff = malloc(in_size); + inbuff->handle = wine_server_obj_handle(ret); + if (name) + lstrcpyW(&inbuff->name[0], name); + + if ((status = NtDeviceIoControlFile(winevideo_device, NULL, NULL, NULL, &iosb, IOCTL_VIDEO_RESOURCE_CREATE, + inbuff, in_size, NULL, 0))) + ERR("Failed to create video resource, status %#x.\n", status); + + free(inbuff); + } return ret; }
+#define IOCTL_VIDEO_RESOURCE_FIND_BY_NAME CTL_CODE(FILE_DEVICE_VIDEO, 1, METHOD_BUFFERED, FILE_READ_ACCESS) + +static HANDLE open_video_resource_by_name(LPCWSTR name) +{ + obj_handle_t handle_out; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + if (!winevideo_device) + return INVALID_HANDLE_VALUE; + + if ((status = NtDeviceIoControlFile(winevideo_device, NULL, NULL, NULL, &iosb, IOCTL_VIDEO_RESOURCE_FIND_BY_NAME, + (PVOID) name, (lstrlenW(name) + 1) * sizeof(WCHAR), &handle_out, sizeof(handle_out)))) + { + ERR("Failed to open video resource by name, status %#x.\n", status); + return INVALID_HANDLE_VALUE; + } + + if (iosb.Information < sizeof(handle_out)) + { + ERR("Unexpected out size.\n"); + return INVALID_HANDLE_VALUE; + } + + return wine_server_ptr_handle(handle_out); +} + VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *allocate_info, const VkAllocationCallbacks *allocator, VkDeviceMemory *memory_out) { @@ -2061,7 +2135,7 @@ VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInf if (handle_import_info->handle) NtDuplicateObject( NtCurrentProcess(), handle_import_info->handle, NtCurrentProcess(), &object->handle, 0, 0, DUPLICATE_SAME_ACCESS ); else if (handle_import_info->name) - FIXME("Importing device memory by resource name not supported.\n"); + object->handle = open_video_resource_by_name( handle_import_info->name ); break; default: WARN("Invalid handle type %08x passed in.\n", handle_import_info->handleType); diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index 1e7ce63f776..906f70b72e9 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1803,6 +1803,7 @@ void FASTCALL ObfReferenceObject(void*); void WINAPI ObDereferenceObject(void*); USHORT WINAPI ObGetFilterVersion(void); ULONG WINAPI ObGetObjectPointerCount(void*); +NTSTATUS WINAPI ObOpenObjectByPointer(void*,ULONG,ACCESS_STATE*,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,HANDLE*); NTSTATUS WINAPI ObRegisterCallbacks(POB_CALLBACK_REGISTRATION, void**); NTSTATUS WINAPI ObReferenceObjectByHandle(HANDLE,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,PVOID*,POBJECT_HANDLE_INFORMATION); NTSTATUS WINAPI ObReferenceObjectByName(UNICODE_STRING*,ULONG,ACCESS_STATE*,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,void*,void**); diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 6ae4e96e37f..17b5db66f0d 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -165,6 +165,7 @@ AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService AddService=NDIS,0x800,NDISService +AddService=WineVideoResources,0x800,WineVideoResourcesService
[DefaultInstall.NT.Services] AddService=BITS,0,BITSService @@ -184,6 +185,7 @@ AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService AddService=NDIS,0x800,NDISService +AddService=WineVideoResources,0x800,WineVideoResourcesService
[DefaultInstall.ntamd64.Services] AddService=BITS,0,BITSService @@ -203,6 +205,7 @@ AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService AddService=NDIS,0x800,NDISService +AddService=WineVideoResources,0x800,WineVideoResourcesService
[DefaultInstall.ntarm64.Services] AddService=BITS,0,BITSService @@ -222,6 +225,7 @@ AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService AddService=NDIS,0x800,NDISService +AddService=WineVideoResources,0x800,WineVideoResourcesService
[Strings] MciExtStr="Software\Microsoft\Windows NT\CurrentVersion\MCI Extensions" @@ -3799,6 +3803,15 @@ StartType=2 ErrorControl=1 LoadOrderGroup="System Bus Extender"
+[WineVideoResourcesService] +Description="Video resource helper service" +DisplayName="Wine Video Resources" +ServiceBinary="%12%\winevideo.sys" +ServiceType=1 +StartType=2 +ErrorControl=1 +LoadOrderGroup="System Bus Extender" + [RpcSsService] Description="RPC service" DisplayName="Remote Procedure Call (RPC)"
On 5/3/21 12:46 PM, Derek Lesho wrote:
Beyond whether this approach is acceptable, one thing I'd appreciate feedback on is a better name for the wine driver. winevideo.sys seems too generic and winevideoresource.sys seems too verbose.
Email cut off the first paragraph of my message, resending it here:
This patch adds a some amount of infrastructure allowing a custom wine driver to store extra information about video resource objects. To do this without adding any custom fields to object structures in wineserver or ntoskrnl, new functionality is added to ntoskrnl which allows wine drivers to keep weak references to kernel objects: that is, references which don't block the server from destroying the object. Using this, the driver stores a table of video resource objects with the extra data the client may need, and, by using ObGetObjectPointerCount, which doesn't include weak references, knows when to free up slots in the resource table, preventing leaks and pointer aliasing issues.
configure.ac | 1 + dlls/ntoskrnl.exe/ntoskrnl.c | 57 +++++- dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 + dlls/vulkan-1/tests/vulkan.c | 9 +- dlls/winevideo.sys/Makefile.in | 6 + dlls/winevideo.sys/winevideo.c | 284 ++++++++++++++++++++++++++ dlls/winevideo.sys/winevideo.sys.spec | 1 + dlls/winevulkan/vulkan.c | 82 +++++++- include/ddk/wdm.h | 1 + loader/wine.inf.in | 13 ++ 10 files changed, 443 insertions(+), 13 deletions(-) create mode 100644 dlls/winevideo.sys/Makefile.in create mode 100644 dlls/winevideo.sys/winevideo.c create mode 100644 dlls/winevideo.sys/winevideo.sys.spec
diff --git a/configure.ac b/configure.ac index 46bbb8a70f8..0071eeb0dd3 100644 --- a/configure.ac +++ b/configure.ac @@ -3823,6 +3823,7 @@ WINE_CONFIG_MAKEFILE(dlls/wineps16.drv16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/winepulse.drv) WINE_CONFIG_MAKEFILE(dlls/wineqtdecoder) WINE_CONFIG_MAKEFILE(dlls/wineusb.sys) +WINE_CONFIG_MAKEFILE(dlls/winevideo.sys) WINE_CONFIG_MAKEFILE(dlls/winevulkan) WINE_CONFIG_MAKEFILE(dlls/winex11.drv) WINE_CONFIG_MAKEFILE(dlls/wing.dll16,enable_win16) diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 5b84c626547..5156f3d3ab1 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -138,6 +138,10 @@ static HANDLE get_device_manager(void) struct object_header { LONG ref;
- /* only blocks object from being freed, not released */
- LONG weak_ref;
- /* an additional ref used to indicate whether server is done w/ obj */
- BOOL in_server; POBJECT_TYPE type; };
@@ -166,6 +170,7 @@ void *alloc_kernel_object( POBJECT_TYPE type, HANDLE handle, SIZE_T size, LONG r } SERVER_END_REQ; if (status) FIXME( "set_object_reference failed: %#x\n", status );
else header->in_server = TRUE; } header->ref = ref;
@@ -197,7 +202,8 @@ void WINAPI ObDereferenceObject( void *obj ) { if (header->type->release) {
header->type->release( obj );
if (!header->weak_ref)
header->type->release( obj ); } else {
@@ -227,6 +233,13 @@ void ObReferenceObject( void *obj )
EnterCriticalSection( &obref_cs );
- if (header->ref == 0 && !header->in_server)
- {
ERR("Weak references may not be turned into strong references\n");
LeaveCriticalSection( &obref_cs );
return;
- }
ref = ++header->ref; TRACE( "(%p) ref=%u\n", obj, ref ); if (ref == 1)
@@ -246,7 +259,37 @@ void ObReferenceObject( void *obj ) ULONG WINAPI ObGetObjectPointerCount( void *object ) { struct object_header *header = (struct object_header *)object - 1;
- return header->ref;
- return header->ref + header->in_server;
+}
+void CDECL wine_ob_weak_ref( void *object ) +{
- struct object_header *header = (struct object_header *) object - 1;
- EnterCriticalSection( &obref_cs );
- header->weak_ref++;
- LeaveCriticalSection( &obref_cs );
+}
+void CDECL wine_ob_weak_deref( void *object ) +{
struct object_header *header = (struct object_header *) object - 1;
EnterCriticalSection( &obref_cs );
header->weak_ref--;
if (!header->weak_ref && !header->ref && !header->in_server)
{
if (header->type->release)
header->type->release(object);
else
free_kernel_object(object);
}
LeaveCriticalSection( &obref_cs ); }
/***********************************************************************
@@ -814,8 +857,16 @@ static NTSTATUS dispatch_volume( struct dispatch_context *context ) static NTSTATUS dispatch_free( struct dispatch_context *context ) { void *obj = wine_server_get_ptr( context->params.free.obj );
- struct object_header *header = (struct object_header *)obj - 1;
TRACE( "freeing %p object\n", obj );
- free_kernel_object( obj );
- EnterCriticalSection(&obref_cs);
- header->in_server = FALSE;
- if (!header->weak_ref)
free_kernel_object( obj );
- LeaveCriticalSection(&obref_cs);
}return STATUS_SUCCESS;
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index 6426bc3968a..318344fe90f 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -1688,3 +1688,5 @@
@ cdecl wine_ntoskrnl_main_loop(long) @ cdecl wine_enumerate_root_devices(wstr) +@ cdecl wine_ob_weak_ref(ptr) +@ cdecl wine_ob_weak_deref(ptr) diff --git a/dlls/vulkan-1/tests/vulkan.c b/dlls/vulkan-1/tests/vulkan.c index c1350da70ee..53df0e9a8d6 100644 --- a/dlls/vulkan-1/tests/vulkan.c +++ b/dlls/vulkan-1/tests/vulkan.c @@ -579,13 +579,10 @@ static void test_external_memory(VkInstance vk_instance, VkPhysicalDevice vk_phy import_handle_info.name = L"wine_test_buffer_export_name";
vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import);
- todo_wine ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
if (vr == VK_SUCCESS)
{
ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
vkFreeMemory(vk_device, vk_memory_import, NULL);
}
ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
vkFreeMemory(vk_device, vk_memory_import, NULL); vkFreeMemory(vk_device, vk_memory, NULL); CloseHandle(nt_handle); }
diff --git a/dlls/winevideo.sys/Makefile.in b/dlls/winevideo.sys/Makefile.in new file mode 100644 index 00000000000..ed185b9f071 --- /dev/null +++ b/dlls/winevideo.sys/Makefile.in @@ -0,0 +1,6 @@ +MODULE = winevideo.sys +IMPORTS = ntoskrnl +EXTRADLLFLAGS = -Wl,--subsystem,native -mno-cygwin
+C_SRCS = \
- winevideo.c
diff --git a/dlls/winevideo.sys/winevideo.c b/dlls/winevideo.sys/winevideo.c new file mode 100644 index 00000000000..04e171bea3f --- /dev/null +++ b/dlls/winevideo.sys/winevideo.c @@ -0,0 +1,284 @@ +#include <stdarg.h>
+#define NONAMELESSUNION +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h"
+#include "ddk/wdm.h"
+#include "wine/debug.h" +#include "wine/server.h"
+WINE_DEFAULT_DEBUG_CHANNEL(winevideo);
+#define IOCTL_VIDEO_RESOURCE_CREATE CTL_CODE(FILE_DEVICE_VIDEO, 0, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_VIDEO_RESOURCE_FIND_BY_NAME CTL_CODE(FILE_DEVICE_VIDEO, 1, METHOD_BUFFERED, FILE_READ_ACCESS)
+struct video_resource +{
- void *object;
- LPWSTR name;
+};
+static struct video_resource *video_resources; +static unsigned int video_resources_size;
+extern void CDECL wine_ob_weak_ref( void *object ); +extern void CDECL wine_ob_weak_deref( void *object );
+/* TODO: If/when ntoskrnl gets support for referencing user handles directly, remove this function */ +static void *reference_client_handle(obj_handle_t handle) +{
- HANDLE client_process, kernel_handle;
- OBJECT_ATTRIBUTES attr;
- void *object = NULL;
- CLIENT_ID cid;
- attr.Length = sizeof(OBJECT_ATTRIBUTES);
- attr.RootDirectory = 0;
- attr.Attributes = OBJ_KERNEL_HANDLE;
- attr.ObjectName = NULL;
- attr.SecurityDescriptor = NULL;
- attr.SecurityQualityOfService = NULL;
- cid.UniqueProcess = PsGetCurrentProcessId();
- cid.UniqueThread = 0;
- if (NtOpenProcess(&client_process, PROCESS_ALL_ACCESS, &attr, &cid) != STATUS_SUCCESS)
return NULL;
- if (NtDuplicateObject(client_process, wine_server_ptr_handle(handle), NtCurrentProcess(), &kernel_handle,
0, OBJ_KERNEL_HANDLE, DUPLICATE_SAME_ACCESS) != STATUS_SUCCESS)
- {
NtClose(client_process);
return NULL;
- }
- ObReferenceObjectByHandle(kernel_handle, 0, NULL, KernelMode, &object, NULL);
- NtClose(client_process);
- NtClose(kernel_handle);
- return object;
+}
+struct resource_create_in +{
- obj_handle_t handle;
- WCHAR name[1];
+};
+static NTSTATUS resource_create(void *buff, SIZE_T insize, SIZE_T outsize, IO_STATUS_BLOCK *iosb) +{
- struct resource_create_in *input = buff;
- struct video_resource *resource = NULL;
- int free_entry = -1;
- unsigned int i;
- void *object;
- LPWSTR name;
- if (insize < offsetof(struct resource_create_in, name))
return STATUS_INFO_LENGTH_MISMATCH;
- if (insize < sizeof(*input))
name = NULL;
- else if (input->name[ ((insize - offsetof(struct resource_create_in, name)) / sizeof(WCHAR)) - 1 ])
return STATUS_INVALID_PARAMETER;
- else
name = &input->name[0];
- if (!(object = reference_client_handle(input->handle)))
return STATUS_INVALID_HANDLE;
- /* Find lowest free slot while checking for duplicate names */
- for (i = video_resources_size - 1; i < video_resources_size; i--)
- {
resource = &video_resources[i];
if (resource->object)
{
if (!ObGetObjectPointerCount(resource->object))
{
wine_ob_weak_deref(resource->object);
if (resource->name)
{
ExFreePoolWithTag(resource->name, 0);
resource->name = NULL;
}
free_entry = i;
}
else if (resource->object == object)
{
ObDereferenceObject(object);
return STATUS_INVALID_PARAMETER;
}
else if (resource->name && name && !wcscmp(name, resource->name))
{
ObDereferenceObject(object);
return STATUS_OBJECT_NAME_COLLISION;
}
}
else
free_entry = i;
- }
- if (free_entry == -1)
- {
struct video_resource *new_resources =
ExAllocatePoolWithTag(NonPagedPool, sizeof(struct video_resource) * (video_resources_size + 1024), 0);
if (video_resources)
{
memcpy(new_resources, video_resources, video_resources_size * sizeof(struct video_resource));
ExFreePoolWithTag(video_resources, 0);
}
memset(&new_resources[video_resources_size], 0, 1024 * sizeof (struct video_resource));
video_resources = new_resources;
free_entry = video_resources_size;
video_resources_size += 1024;
- }
- resource = &video_resources[free_entry];
- resource->object = object;
- if (name)
- {
resource->name = ExAllocatePoolWithTag(NonPagedPool, (wcslen(name) + 1) * sizeof(WCHAR), 0);
wcscpy(resource->name, name);
- }
- wine_ob_weak_ref(object);
- ObDereferenceObject(object);
- iosb->Information = 0;
- return STATUS_SUCCESS;
+}
+/* TODO: If/when ntoskrnl gets support for opening user handles directly, remove this function */ +static obj_handle_t open_client_handle(void *object) +{
- HANDLE client_process, kernel_handle, handle = NULL;
- OBJECT_ATTRIBUTES attr;
- CLIENT_ID cid;
- attr.Length = sizeof(OBJECT_ATTRIBUTES);
- attr.RootDirectory = 0;
- attr.Attributes = OBJ_KERNEL_HANDLE;
- attr.ObjectName = NULL;
- attr.SecurityDescriptor = NULL;
- attr.SecurityQualityOfService = NULL;
- cid.UniqueProcess = PsGetCurrentProcessId();
- cid.UniqueThread = 0;
- if (NtOpenProcess(&client_process, PROCESS_ALL_ACCESS, &attr, &cid) != STATUS_SUCCESS)
return 0;
- if (ObOpenObjectByPointer(object, 0, NULL, GENERIC_ALL, NULL, KernelMode, &kernel_handle) != STATUS_SUCCESS)
- {
NtClose(client_process);
return 0;
- }
- NtDuplicateObject(NtCurrentProcess(), kernel_handle, client_process, &handle,
0, 0, DUPLICATE_SAME_ACCESS);
- NtClose(client_process);
- NtClose(kernel_handle);
- return wine_server_obj_handle(handle);
+}
+static NTSTATUS find_resource_by_name(void *buff, SIZE_T insize, SIZE_T outsize, IO_STATUS_BLOCK *iosb) +{
- LPCWSTR name = buff;
- obj_handle_t ret;
- unsigned int i;
- if (insize < sizeof(WCHAR))
return STATUS_INVALID_PARAMETER;
- if (name[(insize / sizeof(WCHAR)) - 1])
return STATUS_INVALID_PARAMETER;
- for (i = 0; i < video_resources_size; i++)
- {
if (video_resources[i].name && !wcscmp(name, video_resources[i].name))
{
if (!ObGetObjectPointerCount(video_resources[i].object))
break;
if (!(ret = open_client_handle(video_resources[i].object)))
return STATUS_INTERNAL_ERROR;
if (outsize < sizeof(obj_handle_t))
return STATUS_INFO_LENGTH_MISMATCH;
iosb->Information = sizeof(obj_handle_t);
*(obj_handle_t *)buff = ret;
return STATUS_SUCCESS;
}
- }
- return STATUS_OBJECT_NAME_NOT_FOUND;
+}
+static NTSTATUS WINAPI winevideo_ioctl(DEVICE_OBJECT *device, IRP *irp) +{
- IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
- NTSTATUS status;
- TRACE( "ioctl %x insize %u outsize %u\n",
irpsp->Parameters.DeviceIoControl.IoControlCode,
irpsp->Parameters.DeviceIoControl.InputBufferLength,
irpsp->Parameters.DeviceIoControl.OutputBufferLength );
- switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
- {
case IOCTL_VIDEO_RESOURCE_CREATE:
status = resource_create( irp->AssociatedIrp.SystemBuffer,
irpsp->Parameters.DeviceIoControl.InputBufferLength,
irpsp->Parameters.DeviceIoControl.OutputBufferLength,
&irp->IoStatus );
break;
case IOCTL_VIDEO_RESOURCE_FIND_BY_NAME:
status = find_resource_by_name( irp->AssociatedIrp.SystemBuffer,
irpsp->Parameters.DeviceIoControl.InputBufferLength,
irpsp->Parameters.DeviceIoControl.OutputBufferLength,
&irp->IoStatus );
break;
- default:
FIXME( "ioctl %x not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode );
status = STATUS_NOT_SUPPORTED;
break;
- }
- irp->IoStatus.u.Status = status;
- IoCompleteRequest( irp, IO_NO_INCREMENT );
- return status;
+}
+NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) +{
- static const WCHAR device_nameW[] = L"\Device\WineVideoResourceManager";
- static const WCHAR link_nameW[] = L"\??\WineVideoResourceManager";
- UNICODE_STRING device_name, link_name;
- DEVICE_OBJECT *device;
- NTSTATUS status;
- driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = winevideo_ioctl;
- RtlInitUnicodeString(&device_name, device_nameW);
- RtlInitUnicodeString(&link_name, link_nameW);
- if ((status = IoCreateDevice(driver, 0, &device_name, 0, 0, FALSE, &device)))
return status;
- return IoCreateSymbolicLink(&link_name, &device_name);
+} diff --git a/dlls/winevideo.sys/winevideo.sys.spec b/dlls/winevideo.sys/winevideo.sys.spec new file mode 100644 index 00000000000..76421d7e35b --- /dev/null +++ b/dlls/winevideo.sys/winevideo.sys.spec @@ -0,0 +1 @@ +# nothing to export diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index e95860cb3fa..65f53aa679e 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -31,6 +31,7 @@ #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" +#include "winioctl.h" #include "winreg.h" #include "winuser.h" #include "winternl.h" @@ -502,10 +503,33 @@ static void wine_vk_device_free(struct VkDevice_T *device) free(device); }
+static HANDLE winevideo_device;
NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *driver, void *ptr_out) {
static const WCHAR manager_nameW[] = {'\','?','?','\','W','i','n','e','V','i','d','e','o','R','e','s','o','u','r','c','e','M','a','n','a','g','e','r',0};
UNICODE_STRING manager_name;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
NTSTATUS status;
if (reason != DLL_PROCESS_ATTACH) return STATUS_SUCCESS;
RtlInitUnicodeString(&manager_name, manager_nameW);
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.Attributes = 0;
attr.ObjectName = &manager_name;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
if ((status = NtCreateFile(&winevideo_device, GENERIC_READ | GENERIC_WRITE, &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0)))
{
ERR("Failed to load the wine video-resource manager, status %#x.\n", status);
winevideo_device = NULL;
}
vk_funcs = driver; p_vkEnumerateInstanceVersion = vk_funcs->p_vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion"); *(const struct unix_funcs **)ptr_out = &loader_funcs;
@@ -1958,18 +1982,68 @@ VkResult WINAPI wine_vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebu return thunk_vkDebugMarkerSetObjectNameEXT(device, &wine_name_info); }
+#define IOCTL_VIDEO_RESOURCE_CREATE CTL_CODE(FILE_DEVICE_VIDEO, 0, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+struct resource_create_in +{
- obj_handle_t handle;
- WCHAR name[1];
+};
- static HANDLE create_video_resource(int fd, LPCWSTR name) { HANDLE ret = INVALID_HANDLE_VALUE;
- if (name)
FIXME("Naming video resources not supported.\n");
struct resource_create_in *inbuff;
IO_STATUS_BLOCK iosb;
NTSTATUS status;
DWORD in_size;
wine_server_fd_to_handle(fd, GENERIC_ALL, 0, &ret);
if (ret != INVALID_HANDLE_VALUE && winevideo_device)
{
in_size = offsetof(struct resource_create_in, name) + (name ? (lstrlenW(name) + 1) * sizeof(WCHAR) : 0);
inbuff = malloc(in_size);
inbuff->handle = wine_server_obj_handle(ret);
if (name)
lstrcpyW(&inbuff->name[0], name);
if ((status = NtDeviceIoControlFile(winevideo_device, NULL, NULL, NULL, &iosb, IOCTL_VIDEO_RESOURCE_CREATE,
inbuff, in_size, NULL, 0)))
ERR("Failed to create video resource, status %#x.\n", status);
free(inbuff);
} return ret; }
+#define IOCTL_VIDEO_RESOURCE_FIND_BY_NAME CTL_CODE(FILE_DEVICE_VIDEO, 1, METHOD_BUFFERED, FILE_READ_ACCESS)
+static HANDLE open_video_resource_by_name(LPCWSTR name) +{
- obj_handle_t handle_out;
- IO_STATUS_BLOCK iosb;
- NTSTATUS status;
- if (!winevideo_device)
return INVALID_HANDLE_VALUE;
- if ((status = NtDeviceIoControlFile(winevideo_device, NULL, NULL, NULL, &iosb, IOCTL_VIDEO_RESOURCE_FIND_BY_NAME,
(PVOID) name, (lstrlenW(name) + 1) * sizeof(WCHAR), &handle_out, sizeof(handle_out))))
- {
ERR("Failed to open video resource by name, status %#x.\n", status);
return INVALID_HANDLE_VALUE;
- }
- if (iosb.Information < sizeof(handle_out))
- {
ERR("Unexpected out size.\n");
return INVALID_HANDLE_VALUE;
- }
- return wine_server_ptr_handle(handle_out);
+}
- VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *allocate_info, const VkAllocationCallbacks *allocator, VkDeviceMemory *memory_out) {
@@ -2061,7 +2135,7 @@ VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInf if (handle_import_info->handle) NtDuplicateObject( NtCurrentProcess(), handle_import_info->handle, NtCurrentProcess(), &object->handle, 0, 0, DUPLICATE_SAME_ACCESS ); else if (handle_import_info->name)
FIXME("Importing device memory by resource name not supported.\n");
object->handle = open_video_resource_by_name( handle_import_info->name ); break; default: WARN("Invalid handle type %08x passed in.\n", handle_import_info->handleType);
diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index 1e7ce63f776..906f70b72e9 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1803,6 +1803,7 @@ void FASTCALL ObfReferenceObject(void*); void WINAPI ObDereferenceObject(void*); USHORT WINAPI ObGetFilterVersion(void); ULONG WINAPI ObGetObjectPointerCount(void*); +NTSTATUS WINAPI ObOpenObjectByPointer(void*,ULONG,ACCESS_STATE*,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,HANDLE*); NTSTATUS WINAPI ObRegisterCallbacks(POB_CALLBACK_REGISTRATION, void**); NTSTATUS WINAPI ObReferenceObjectByHandle(HANDLE,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,PVOID*,POBJECT_HANDLE_INFORMATION); NTSTATUS WINAPI ObReferenceObjectByName(UNICODE_STRING*,ULONG,ACCESS_STATE*,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,void*,void**); diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 6ae4e96e37f..17b5db66f0d 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -165,6 +165,7 @@ AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService AddService=NDIS,0x800,NDISService +AddService=WineVideoResources,0x800,WineVideoResourcesService
[DefaultInstall.NT.Services] AddService=BITS,0,BITSService @@ -184,6 +185,7 @@ AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService AddService=NDIS,0x800,NDISService +AddService=WineVideoResources,0x800,WineVideoResourcesService
[DefaultInstall.ntamd64.Services] AddService=BITS,0,BITSService @@ -203,6 +205,7 @@ AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService AddService=NDIS,0x800,NDISService +AddService=WineVideoResources,0x800,WineVideoResourcesService
[DefaultInstall.ntarm64.Services] AddService=BITS,0,BITSService @@ -222,6 +225,7 @@ AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService AddService=NDIS,0x800,NDISService +AddService=WineVideoResources,0x800,WineVideoResourcesService
[Strings] MciExtStr="Software\Microsoft\Windows NT\CurrentVersion\MCI Extensions" @@ -3799,6 +3803,15 @@ StartType=2 ErrorControl=1 LoadOrderGroup="System Bus Extender"
+[WineVideoResourcesService] +Description="Video resource helper service" +DisplayName="Wine Video Resources" +ServiceBinary="%12%\winevideo.sys" +ServiceType=1 +StartType=2 +ErrorControl=1 +LoadOrderGroup="System Bus Extender"
- [RpcSsService] Description="RPC service" DisplayName="Remote Procedure Call (RPC)"
Hello Derek, it looks like you've put a lot of thought into this problem. I assume you've already discussed this privately in detail with our Direct3D maintainers, but for the benefit of the rest of us, I have a few questions:
1. How do you plan to accomodate the following (whether in the near or distant future):
* CL_KHR_d3d11_sharing
* CL_KHR_gl_sharing
* Shared resources in d3d9-11, when using the OpenGL backend
* Shared resources in d3d12
* Shared resources between d3d9-12 devices, when using wined3d or libvkd3d on Windows
* Shared resources between d3d9-12 devices and OpenGL or Vulkan, when using wined3d or libvkd3d on Windows
2. What is the reason for the "weak" reference counting introduced in this patch?
3. Why is winevideo.sys necessary at all, if wine_server_fd_to_handle() and wine_server_handle_to_fd() provide the necessary wrapping of an external FD object?
4. What kind of objects are Vulkan or Direct3D shared handles on Windows? E.g. what does NtQueryObject(ObjectTypeInformation) return? Are named Vulkan objects implemented using the NT namespace? How is this different between e.g. D3D11_RESOURCE_MISC_SHARED and D3D11_RESOURCE_MISC_SHARED_NTHANDLE, or between VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT and VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT?
5. Are D3D11_RESOURCE_MISC_SHARED / KMT handles cross-process? If not, should they be implemented using a process-local handle table somewhere instead of using NT handles? (Could/should we use D3DKMT* APIs from gdi32?)
6. Would it make more sense to use wineserver to manage shared resources instead of a separate driver, and thereby avoid introducing hacks into ntoskrnl?
On 5/3/21 4:14 PM, Zebediah Figura (she/her) wrote:
Hello Derek, it looks like you've put a lot of thought into this problem. I assume you've already discussed this privately in detail with our Direct3D maintainers, but for the benefit of the rest of us, I have a few questions:
- How do you plan to accomodate the following (whether in the near or
distant future):
CL_KHR_d3d11_sharing
CL_KHR_gl_sharing
Shared resources in d3d9-11, when using the OpenGL backend
Shared resources in d3d12
My plan is to add an optional map inside the resource objects in which graphics API implementations (DXVK, vkd3d-proton, wined3d ...) and wine layers (openGL, openCL) can store extra data as needed. There would be an interface in the driver which takes a key (probably a GUID) and either accept or return an arbitrary blob of data. The benefit to this approach, in my view, is that it would allow some flexibility as requirements are understood and evolve for sharing between all these APIs. For example, at first, DXVK might roll its own data structure / interface for sharing between its D3D9 and D3D11 implementations, and maybe later on as the problem is better understood, an interface could come into being for sharing between DXVK's d3d11 and wined3d's d3d9.
- Shared resources between d3d9-12 devices, when using wined3d or
libvkd3d on Windows
- Shared resources between d3d9-12 devices and OpenGL or Vulkan, when
using wined3d or libvkd3d on Windows
I decided not to worry about the situation on windows, as it seems much more complex to me. There seems to be a number of sparsely documented D3DKMT APIs for tracking information about shared resources, and it seemed to me that it would be improper to try to implement these interfaces given how different the stack is on wine. As for a custom interface unrelated to the native one working on Windows, I'm really not sure how feasible that would be, and what benefits it would provide. We need to be able to associate metadata to the objects underlying the HANDLEs, as well as provide a global lookup for these objects, all in usermode. You could try implementing a global memory section that stores this metadata associated to KMT (global) handles, but you would have trouble doing the same for regular NT handles, since as far as I can see it is impossible to fetch a KMT handle from a regular NT handle on the windows implementation. If a solution does come about for this problem, I doubt it would be a good fit for wine.
- What is the reason for the "weak" reference counting introduced in
this patch?
In wine's ntoskrnl's object manager code, object structure data isn't freed until there are no more references to the object in the server. This is because a driver could reasonably expect that even though they don't hold any references to an object, they can expect for their pointer to valid if they or anyone else holds a handle to the object. The weak references allow a driver to safely store their own data alongside a weakly-referenced object, as with a weak reference, the destruction of the object structure will be deferred until there are no more weak references. The reason why the driver can't just hold a full reference is that it has no indication on when to free it, an application can request a HANDLE for a graphics resource, then completely de-initialize the graphics API, keeping the HANDLE. That is to say, there's no single point in the winevulkan code where we can make a call to the driver to free that object. On the other hand, if the driver were to keep no reference to the object, but just the pointer, it would have no way of knowing whether the object pointers are still valid when the client asks for information about an object.
- Why is winevideo.sys necessary at all, if
wine_server_fd_to_handle() and wine_server_handle_to_fd() provide the necessary wrapping of an external FD object?
Because we need more information to be stored in the object behind the HANDLE, and we need a way to lookup these objects globally. For example, with D3D11 shared resources, we must be able to lookup and object by its KMT handle and name, and we must be able to store extra information about the object such as width, height, and layers, as Vulkan doesn't do this for us. Of course, we could figure add a way to put these objects into the server's object namespace, but that still leaves the KMT handles to worry about.
- What kind of objects are Vulkan or Direct3D shared handles on
Windows? E.g. what does NtQueryObject(ObjectTypeInformation) return? Are named Vulkan objects implemented using the NT namespace? How is this different between e.g. D3D11_RESOURCE_MISC_SHARED and D3D11_RESOURCE_MISC_SHARED_NTHANDLE, or between VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT and VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT?
KMT handles are in their own namespace, completely separated from the NT object system. None of the usual object APIs work with them. The objects behind the NT handles do have a custom type, and named Vulkan objects are implemented using the NT namespace. This is not reflected in the implementation I sent. I previously sent a patchset to implement these resources in 2019, and back then, the concern was that implementing these custom object types complicated wineserver code unnecessarily since the fd<->HANDLE APIs were already present. This is the reason why I had to come up with the new approach.
- Are D3D11_RESOURCE_MISC_SHARED / KMT handles cross-process? If not,
should they be implemented using a process-local handle table somewhere instead of using NT handles? (Could/should we use D3DKMT* APIs from gdi32?)
I'd always assumed that they were, and I must admit I wrote the rest of this email under that assumption. I'll write tests tomorrow to find out. If they aren't, that would indeed make things a lot simpler...
- Would it make more sense to use wineserver to manage shared
resources instead of a separate driver, and thereby avoid introducing hacks into ntoskrnl?
I think I addressed this in my response to question 4.
On 5/3/21 4:08 PM, Derek Lesho wrote:
On 5/3/21 4:14 PM, Zebediah Figura (she/her) wrote:
Hello Derek, it looks like you've put a lot of thought into this problem. I assume you've already discussed this privately in detail with our Direct3D maintainers, but for the benefit of the rest of us, I have a few questions:
- How do you plan to accomodate the following (whether in the near or
distant future):
CL_KHR_d3d11_sharing
CL_KHR_gl_sharing
Shared resources in d3d9-11, when using the OpenGL backend
Shared resources in d3d12
My plan is to add an optional map inside the resource objects in which graphics API implementations (DXVK, vkd3d-proton, wined3d ...) and wine layers (openGL, openCL) can store extra data as needed. There would be an interface in the driver which takes a key (probably a GUID) and either accept or return an arbitrary blob of data. The benefit to this approach, in my view, is that it would allow some flexibility as requirements are understood and evolve for sharing between all these APIs. For example, at first, DXVK might roll its own data structure / interface for sharing between its D3D9 and D3D11 implementations, and maybe later on as the problem is better understood, an interface could come into being for sharing between DXVK's d3d11 and wined3d's d3d9.
I was kind of hoping for some more specific answers, i.e. rough descriptions of what the code does. Nevertheless, according to my understanding, this really doesn't account for all of the specifics of each API I've listed. Put simply, I'm worried that this patch set fits Vulkan and nothing else.
- Shared resources between d3d9-12 devices, when using wined3d or
libvkd3d on Windows
- Shared resources between d3d9-12 devices and OpenGL or Vulkan, when
using wined3d or libvkd3d on Windows
I decided not to worry about the situation on windows, as it seems much more complex to me. There seems to be a number of sparsely documented D3DKMT APIs for tracking information about shared resources, and it seemed to me that it would be improper to try to implement these interfaces given how different the stack is on wine. As for a custom interface unrelated to the native one working on Windows, I'm really not sure how feasible that would be, and what benefits it would provide. We need to be able to associate metadata to the objects underlying the HANDLEs, as well as provide a global lookup for these objects, all in usermode. You could try implementing a global memory section that stores this metadata associated to KMT (global) handles, but you would have trouble doing the same for regular NT handles, since as far as I can see it is impossible to fetch a KMT handle from a regular NT handle on the windows implementation. If a solution does come about for this problem, I doubt it would be a good fit for wine.
I would strongly advise not throwing these possibilities out the window immediately.
- What is the reason for the "weak" reference counting introduced in
this patch?
In wine's ntoskrnl's object manager code, object structure data isn't freed until there are no more references to the object in the server. This is because a driver could reasonably expect that even though they don't hold any references to an object, they can expect for their pointer to valid if they or anyone else holds a handle to the object. The weak references allow a driver to safely store their own data alongside a weakly-referenced object, as with a weak reference, the destruction of the object structure will be deferred until there are no more weak references. The reason why the driver can't just hold a full reference is that it has no indication on when to free it, an application can request a HANDLE for a graphics resource, then completely de-initialize the graphics API, keeping the HANDLE. That is to say, there's no single point in the winevulkan code where we can make a call to the driver to free that object. On the other hand, if the driver were to keep no reference to the object, but just the pointer, it would have no way of knowing whether the object pointers are still valid when the client asks for information about an object.
The code, as it stands, looks all sorts of wrong. The right approach would be to let the server notify ntoskrnl of destruction, the way it already does.
That's easier said than done, of course, and it takes some thought. One possible approach that comes to mind is to have shared resource handles be (NT) handles to device file objects. These files expose a pair of ioctls IOCTL_RESOURCE_SET_INFO and IOCTL_RESOURCE_GET_INFO, which store and retrieve a structure like:
struct resource_info { obj_handle_t fd_handle; struct wined3d_resource_desc desc; unsigned int layer_count; unsigned int level_count; };
"file" is itself a handle, which was created through wine_server_fd_to_handle(), and will be duplicated anew into the calling process through each IOCTL_RESOURCE_GET_INFO call. The remaining parameters are stored and returned transparently.
When winevulkan needs to create a shared resource handle, it does something like this:
vkGetMemoryFdKHR(..., &fd); file = CreateFile(...); resource_info.fd_handle = wine_server_fd_to_handle(fd); /* fill resource_info.desc &c. */ DeviceIoControl(file, ..., &resource_info, ...); CloseHandle(fd_handle);
and eventually returns "file" from vkGetMemoryWin32HandleKHR().
When wined3d, Vulkan, or OpenGL needs to open a shared resource handle, it uses IOCTL_RESOURCE_GET_INFO on that handle, followed by wine_server_handle_to_fd(). wined3d will subsequently use the other fields of struct resource_info; other APIs will ignore them.
Extending this to objects which aren't resources (e.g. semaphores, fences) is left as an exercise to the reader; it could be done with a separate pair of ioctls or by extending the above structure.
I will of course disclaim that I have thought this approach through completely, or that it is the best approach (in particular, I still am inclined to think that server-side management probably isn't that bad), but it seems like a reasonable way to use the existing ntoskrnl infrastructure and avoid any awful hacks.
Note that this still doesn't handle OpenGL, and that's something that should really be resolved before anything is implemented.
- Why is winevideo.sys necessary at all, if
wine_server_fd_to_handle() and wine_server_handle_to_fd() provide the necessary wrapping of an external FD object?
Because we need more information to be stored in the object behind the HANDLE, and we need a way to lookup these objects globally. For example, with D3D11 shared resources, we must be able to lookup and object by its KMT handle and name, and we must be able to store extra information about the object such as width, height, and layers, as Vulkan doesn't do this for us. Of course, we could figure add a way to put these objects into the server's object namespace, but that still leaves the KMT handles to worry about.
- What kind of objects are Vulkan or Direct3D shared handles on
Windows? E.g. what does NtQueryObject(ObjectTypeInformation) return? Are named Vulkan objects implemented using the NT namespace? How is this different between e.g. D3D11_RESOURCE_MISC_SHARED and D3D11_RESOURCE_MISC_SHARED_NTHANDLE, or between VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT and VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT?
KMT handles are in their own namespace, completely separated from the NT object system. None of the usual object APIs work with them. The objects behind the NT handles do have a custom type, and named Vulkan objects are implemented using the NT namespace. This is not reflected in the implementation I sent. I previously sent a patchset to implement these resources in 2019, and back then, the concern was that implementing these custom object types complicated wineserver code unnecessarily since the fd<->HANDLE APIs were already present. This is the reason why I had to come up with the new approach.
That is an answer to one of the questions I asked, though not really the others.
In particular, I suspect (especially given [1]) that the name parameter is really an NT name, or maps to one, which means that name assignment and lookup should be done in a way consistent with other NT objects.
- Are D3D11_RESOURCE_MISC_SHARED / KMT handles cross-process? If not,
should they be implemented using a process-local handle table somewhere instead of using NT handles? (Could/should we use D3DKMT* APIs from gdi32?)
I'd always assumed that they were, and I must admit I wrote the rest of this email under that assumption. I'll write tests tomorrow to find out. If they aren't, that would indeed make things a lot simpler...
On the flip side of this, is there any reason that we can't just pretend NT handles are KMT handles?
- Would it make more sense to use wineserver to manage shared
resources instead of a separate driver, and thereby avoid introducing hacks into ntoskrnl?
I think I addressed this in my response to question 4.
[1] https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12devi...
On 5/3/21 7:54 PM, Zebediah Figura (she/her) wrote:
On 5/3/21 4:08 PM, Derek Lesho wrote:
On 5/3/21 4:14 PM, Zebediah Figura (she/her) wrote:
Hello Derek, it looks like you've put a lot of thought into this problem. I assume you've already discussed this privately in detail with our Direct3D maintainers, but for the benefit of the rest of us, I have a few questions:
- How do you plan to accomodate the following (whether in the near or
distant future):
CL_KHR_d3d11_sharing
CL_KHR_gl_sharing
Shared resources in d3d9-11, when using the OpenGL backend
Shared resources in d3d12
My plan is to add an optional map inside the resource objects in which graphics API implementations (DXVK, vkd3d-proton, wined3d ...) and wine layers (openGL, openCL) can store extra data as needed. There would be an interface in the driver which takes a key (probably a GUID) and either accept or return an arbitrary blob of data. The benefit to this approach, in my view, is that it would allow some flexibility as requirements are understood and evolve for sharing between all these APIs. For example, at first, DXVK might roll its own data structure / interface for sharing between its D3D9 and D3D11 implementations, and maybe later on as the problem is better understood, an interface could come into being for sharing between DXVK's d3d11 and wined3d's d3d9.
I was kind of hoping for some more specific answers, i.e. rough descriptions of what the code does. Nevertheless, according to my understanding, this really doesn't account for all of the specifics of each API I've listed. Put simply, I'm worried that this patch set fits Vulkan and nothing else.
I admit that I haven't thought about openCL or D3D12 much, but for the other use-cases I'm really not sure what problems you are thinking of. Could you perhaps explain what concerns you have with them? From my perspective, all I've constructed is a generic way to associate data with objects, and a way to look the objects up by name and KMT handle. The only way I could see this not working would be if the FDs output by hosts APIs weren't compatible.
- Shared resources between d3d9-12 devices, when using wined3d or
libvkd3d on Windows
- Shared resources between d3d9-12 devices and OpenGL or Vulkan, when
using wined3d or libvkd3d on Windows
I decided not to worry about the situation on windows, as it seems much more complex to me. There seems to be a number of sparsely documented D3DKMT APIs for tracking information about shared resources, and it seemed to me that it would be improper to try to implement these interfaces given how different the stack is on wine. As for a custom interface unrelated to the native one working on Windows, I'm really not sure how feasible that would be, and what benefits it would provide. We need to be able to associate metadata to the objects underlying the HANDLEs, as well as provide a global lookup for these objects, all in usermode. You could try implementing a global memory section that stores this metadata associated to KMT (global) handles, but you would have trouble doing the same for regular NT handles, since as far as I can see it is impossible to fetch a KMT handle from a regular NT handle on the windows implementation. If a solution does come about for this problem, I doubt it would be a good fit for wine.
I would strongly advise not throwing these possibilities out the window immediately.
I just get the impression that if there were a good way to implement this stuff without support from wine, the authors of DXVK would have figured it out by now given how it's one of the last major hurdles for the project. It has been discussed quite a bit.
- What is the reason for the "weak" reference counting introduced in
this patch?
In wine's ntoskrnl's object manager code, object structure data isn't freed until there are no more references to the object in the server. This is because a driver could reasonably expect that even though they don't hold any references to an object, they can expect for their pointer to valid if they or anyone else holds a handle to the object. The weak references allow a driver to safely store their own data alongside a weakly-referenced object, as with a weak reference, the destruction of the object structure will be deferred until there are no more weak references. The reason why the driver can't just hold a full reference is that it has no indication on when to free it, an application can request a HANDLE for a graphics resource, then completely de-initialize the graphics API, keeping the HANDLE. That is to say, there's no single point in the winevulkan code where we can make a call to the driver to free that object. On the other hand, if the driver were to keep no reference to the object, but just the pointer, it would have no way of knowing whether the object pointers are still valid when the client asks for information about an object.
The code, as it stands, looks all sorts of wrong. The right approach would be to let the server notify ntoskrnl of destruction, the way it already does.
In a sense, this is what the current approach does. It's just that instead of executing code straight from the thread that received the DISPATCH_FREE irp, it deduces whether or not that DISPATCH_FREE irp has come yet by finding out whether the object has any real references left. I'm not claiming that it's the most idiomatic approach, but it's a quite unintrusive and small helper that gives us the functionality we need.
That's easier said than done, of course, and it takes some thought. One possible approach that comes to mind is to have shared resource handles be (NT) handles to device file objects. These files expose a pair of ioctls IOCTL_RESOURCE_SET_INFO and IOCTL_RESOURCE_GET_INFO, which store and retrieve a structure like:
Yeah, that could also work, I guess we would retain the global device WineVideoResourceManager, and create the per-fd device objects via IOCTLs to that global device. Seems like it would accomplish approximately the same thing with more code, with the advantage being that it wouldn't include the weak reference code, which I assume is what you consider all sorts of wrong.
struct resource_info { obj_handle_t fd_handle; struct wined3d_resource_desc desc; unsigned int layer_count; unsigned int level_count; };
I still think it would be unwise to lock ourselves into a structure like this. Different APIs will have different requirements and this will probably evolve some as we find more API surface to this. It also makes it a headache for out-of-tree projects to sync with whatever version of the interface is in the wine they are running on top of.
"file" is itself a handle, which was created through wine_server_fd_to_handle(), and will be duplicated anew into the calling process through each IOCTL_RESOURCE_GET_INFO call. The remaining parameters are stored and returned transparently.
When winevulkan needs to create a shared resource handle, it does something like this:
vkGetMemoryFdKHR(..., &fd); file = CreateFile(...);
As I previously mentioned, I'm currently not aware of how we could create a previously-unexisting, possibly unnamed object through CreateFile. If there's something I'm missing here, could you please explain it?
resource_info.fd_handle = wine_server_fd_to_handle(fd); /* fill resource_info.desc &c. */ DeviceIoControl(file, ..., &resource_info, ...); CloseHandle(fd_handle);
and eventually returns "file" from vkGetMemoryWin32HandleKHR().
When wined3d, Vulkan, or OpenGL needs to open a shared resource handle, it uses IOCTL_RESOURCE_GET_INFO on that handle, followed by wine_server_handle_to_fd(). wined3d will subsequently use the other fields of struct resource_info; other APIs will ignore them.
Extending this to objects which aren't resources (e.g. semaphores, fences) is left as an exercise to the reader; it could be done with a separate pair of ioctls or by extending the above structure.
I will of course disclaim that I have thought this approach through completely, or that it is the best approach (in particular, I still am inclined to think that server-side management probably isn't that bad)
Yeah I'm not necessarily opposed to server-side management either, but at the very least it seemed to me like it was off the table in 2019.
, but it seems like a reasonable way to use the existing ntoskrnl infrastructure and avoid any awful hacks.
Note that this still doesn't handle OpenGL, and that's something that should really be resolved before anything is implemented.
Another advantage to the approach of letting the client APIs decide on how to store metadata is that, as long as we know that OpenGL and Vulkan (and any other potential host API) can export FDs which are compatible with one another, we can be assured that our current implementation isn't incompatible with the prospective implementation(/s) of the other API.
- Why is winevideo.sys necessary at all, if
wine_server_fd_to_handle() and wine_server_handle_to_fd() provide the necessary wrapping of an external FD object?
Because we need more information to be stored in the object behind the HANDLE, and we need a way to lookup these objects globally. For example, with D3D11 shared resources, we must be able to lookup and object by its KMT handle and name, and we must be able to store extra information about the object such as width, height, and layers, as Vulkan doesn't do this for us. Of course, we could figure add a way to put these objects into the server's object namespace, but that still leaves the KMT handles to worry about.
- What kind of objects are Vulkan or Direct3D shared handles on
Windows? E.g. what does NtQueryObject(ObjectTypeInformation) return? Are named Vulkan objects implemented using the NT namespace? How is this different between e.g. D3D11_RESOURCE_MISC_SHARED and D3D11_RESOURCE_MISC_SHARED_NTHANDLE, or between VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT and VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT?
KMT handles are in their own namespace, completely separated from the NT object system. None of the usual object APIs work with them. The objects behind the NT handles do have a custom type, and named Vulkan objects are implemented using the NT namespace. This is not reflected in the implementation I sent. I previously sent a patchset to implement these resources in 2019, and back then, the concern was that implementing these custom object types complicated wineserver code unnecessarily since the fd<->HANDLE APIs were already present. This is the reason why I had to come up with the new approach.
That is an answer to one of the questions I asked, though not really the others.
In particular, I suspect (especially given [1]) that the name parameter is really an NT name, or maps to one, which means that name assignment and lookup should be done in a way consistent with other NT objects.
In my previous email I did note that "named Vulkan objects are implemented using the NT namespace". In my 2019 version, I did implement naming of these objects through the NT namespace, but since this implementation doesn't have it's own wineserver type, it's not possible without adding such functionality to the existing fd type used for wine_server_fd_to_handle and wine_server_handle_to_fd. One problem with adding this functionality is that these ntdll APIs are public so we'd probably have to add an additional wine_server_fd_to_named_handle function for this. While it would be nice to be more accurate and put these objects into the NT namespace, in practice I don't think any applications are looking up these objects by any other method than using the graphics APIs.
- Are D3D11_RESOURCE_MISC_SHARED / KMT handles cross-process? If not,
should they be implemented using a process-local handle table somewhere instead of using NT handles? (Could/should we use D3DKMT* APIs from gdi32?)
I'd always assumed that they were, and I must admit I wrote the rest of this email under that assumption. I'll write tests tomorrow to find out. If they aren't, that would indeed make things a lot simpler...
On the flip side of this, is there any reason that we can't just pretend NT handles are KMT handles?
I'm assuming you mean returning NT handles when the app requests a KMT handle? If so, that wouldn't work as I've confirmed that KMT handles are indeed global. Additionally, Vulkan specifies that KMT handles don't hold a reference to the underlying object, as soon as the Vulkan object is freed, the KMT handles become invalid.
- Would it make more sense to use wineserver to manage shared
resources instead of a separate driver, and thereby avoid introducing hacks into ntoskrnl?
I think I addressed this in my response to question 4.
[1] https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12devi...
On 5/4/21 22:39, Derek Lesho wrote:
Extending this to objects which aren't resources (e.g. semaphores, fences) is left as an exercise to the reader; it could be done with a separate pair of ioctls or by extending the above structure.
I will of course disclaim that I have thought this approach through completely, or that it is the best approach (in particular, I still am inclined to think that server-side management probably isn't that bad)
Yeah I'm not necessarily opposed to server-side management either, but at the very least it seemed to me like it was off the table in 2019.
I am sorry, I might have not following the topic precisely and my question might be a bit off, but still, why additional support for the handles is needed at all? Given the exact replication of related Windows handles mechanics is not planned?
Can't those handles be just, say, mapping handles with the specific resource description, fd etc. data filled in and interpreted by the interested graphic layers?
On 5/4/21 3:56 PM, Paul Gofman wrote:
On 5/4/21 22:39, Derek Lesho wrote:
Extending this to objects which aren't resources (e.g. semaphores, fences) is left as an exercise to the reader; it could be done with a separate pair of ioctls or by extending the above structure.
I will of course disclaim that I have thought this approach through completely, or that it is the best approach (in particular, I still am inclined to think that server-side management probably isn't that bad)
Yeah I'm not necessarily opposed to server-side management either, but at the very least it seemed to me like it was off the table in 2019.
I am sorry, I might have not following the topic precisely and my question might be a bit off, but still, why additional support for the handles is needed at all? Given the exact replication of related Windows handles mechanics is not planned?
Can't those handles be just, say, mapping handles with the specific resource description, fd etc. data filled in and interpreted by the interested graphic layers?
The main reason why this wouldn't be feasible is the edge case of a process which creates a shared resource then shuts down. The process into which the resource was copied would still have an open handle to the mapping, but the mapping wouldn't hold a reference to the actual resource. (It would probably just contain the PID of the process which created the resource + that process's HANDLE, for use in DuplicateHandle).
On 5/4/21 23:05, Derek Lesho wrote:
On 5/4/21 3:56 PM, Paul Gofman wrote:
Can't those handles be just, say, mapping handles with the specific resource description, fd etc. data filled in and interpreted by the interested graphic layers?
The main reason why this wouldn't be feasible is the edge case of a process which creates a shared resource then shuts down. The process into which the resource was copied would still have an open handle to the mapping, but the mapping wouldn't hold a reference to the actual resource.
If a separate process (say, ntoskrnl.exe) can hold all the required data independently of the process which created the shared resource, can't shared memory hold that instead (possibly using some other global [named?] objects already supported by wineserver?
On 5/4/21 4:13 PM, Paul Gofman wrote:
On 5/4/21 23:05, Derek Lesho wrote:
On 5/4/21 3:56 PM, Paul Gofman wrote:
Can't those handles be just, say, mapping handles with the specific resource description, fd etc. data filled in and interpreted by the interested graphic layers?
The main reason why this wouldn't be feasible is the edge case of a process which creates a shared resource then shuts down. The process into which the resource was copied would still have an open handle to the mapping, but the mapping wouldn't hold a reference to the actual resource.
If a separate process (say, ntoskrnl.exe) can hold all the required data independently of the process which created the shared resource, can't shared memory hold that instead (possibly using some other global [named?] objects already supported by wineserver?
I think the problem is, if you find a way to store a reference to the object in the shared memory object, how do you know when to release that reference once the shared memory object is destroyed. As for making all the objects global, I'm not sure how that solves the problem, what code is releasing the reference that was in the shared memory before destruction?
On 5/4/21 23:21, Derek Lesho wrote:
On 5/4/21 4:13 PM, Paul Gofman wrote:
On 5/4/21 23:05, Derek Lesho wrote:
On 5/4/21 3:56 PM, Paul Gofman wrote:
Can't those handles be just, say, mapping handles with the specific resource description, fd etc. data filled in and interpreted by the interested graphic layers?
The main reason why this wouldn't be feasible is the edge case of a process which creates a shared resource then shuts down. The process into which the resource was copied would still have an open handle to the mapping, but the mapping wouldn't hold a reference to the actual resource.
If a separate process (say, ntoskrnl.exe) can hold all the required data independently of the process which created the shared resource, can't shared memory hold that instead (possibly using some other global [named?] objects already supported by wineserver?
I think the problem is, if you find a way to store a reference to the object in the shared memory object, how do you know when to release that reference once the shared memory object is destroyed. As for making all the objects global, I'm not sure how that solves the problem, what code is releasing the reference that was in the shared memory before destruction?
I guess when the graphics API accepts the handle, it may map the shared memory as well as get the handles for all the referenced objects. When it closes the shared handle, it should close the inner handles as well. If a process holding those handles dies (would it be the original creator or any process using the shared object), all the handles are closed by wineserver. I might be missing a lot of important details now of course.
On 5/4/21 4:31 PM, Paul Gofman wrote:
On 5/4/21 23:21, Derek Lesho wrote:
On 5/4/21 4:13 PM, Paul Gofman wrote:
On 5/4/21 23:05, Derek Lesho wrote:
On 5/4/21 3:56 PM, Paul Gofman wrote:
Can't those handles be just, say, mapping handles with the specific resource description, fd etc. data filled in and interpreted by the interested graphic layers?
The main reason why this wouldn't be feasible is the edge case of a process which creates a shared resource then shuts down. The process into which the resource was copied would still have an open handle to the mapping, but the mapping wouldn't hold a reference to the actual resource.
If a separate process (say, ntoskrnl.exe) can hold all the required data independently of the process which created the shared resource, can't shared memory hold that instead (possibly using some other global [named?] objects already supported by wineserver?
I think the problem is, if you find a way to store a reference to the object in the shared memory object, how do you know when to release that reference once the shared memory object is destroyed. As for making all the objects global, I'm not sure how that solves the problem, what code is releasing the reference that was in the shared memory before destruction?
I guess when the graphics API accepts the handle, it may map the shared memory as well as get the handles for all the referenced objects. When it closes the shared handle, it should close the inner handles as well.
The problem is not with the graphics APIs closing the handles they use to get the FDs or the shared memory handles, the problem is with the user code closing the shared memory object and wine having no way to know how to release the reference that now-destroyed shared memory holds to the fd object. This also assumes that there is a indeed a way to hold a global reference to the fd object from the shared memory, which I don't think there is.
If a process holding those handles dies (would it be the original creator or any process using the shared object), all the handles are closed by wineserver. I might be missing a lot of important details now of course.
On 5/4/21 23:44, Derek Lesho wrote:
The problem is not with the graphics APIs closing the handles they use to get the FDs or the shared memory handles, the problem is with the user code closing the shared memory object and wine having no way to know how to release the reference that now-destroyed shared memory holds to the fd object. This also assumes that there is a indeed a way to hold a global reference to the fd object from the shared memory, which I don't think there is.
Yes, indeed, at least my limited (to say the least) involvement into this problem doesn't allow me to see such a solution. Yet it looks a bit weird me to introduce a whole driver taking a separate process and acting just as a placeholder for handle and shared object. Are you sure there is no good way to add some support to wineserver (maybe keeping away some specific 3D details from it)?
On 5/5/21 6:37 AM, Paul Gofman wrote:
On 5/4/21 23:44, Derek Lesho wrote:
The problem is not with the graphics APIs closing the handles they use to get the FDs or the shared memory handles, the problem is with the user code closing the shared memory object and wine having no way to know how to release the reference that now-destroyed shared memory holds to the fd object. This also assumes that there is a indeed a way to hold a global reference to the fd object from the shared memory, which I don't think there is.
Yes, indeed, at least my limited (to say the least) involvement into this problem doesn't allow me to see such a solution. Yet it looks a bit weird me to introduce a whole driver taking a separate process and acting just as a placeholder for handle and shared object. Are you sure there is no good way to add some support to wineserver (maybe keeping away some specific 3D details from it)?
Well, we could add a separate API to ntdll that allows naming the objects created through fds, add a server call to associate private data with either these types of objects or any objects, and for KMT handles try to erect a global memory section with a table that contains pairs of object HANDLEs and process PIDs, removed by the Vulkan object's destruction routine. For the KMT handles, we could also add support for global non-referencing handles for non-named objects to wineserver.
As for my opinion, I think we should either fully commit to the wineserver path or the device object path, and not try to go with the wineserver path while keeping stuff generic. Since there are accuracy benefits to be reaped by going with wineserver (object type information could be accurate, and access/permissions could be handled better), I think we should take advantage of that if we go down that path.
All in all, I still think the least intrusive system is the .sys using device objects as frontends for fd objects.
On Tue, 4 May 2021 at 21:39, Derek Lesho dlesho@codeweavers.com wrote:
I just get the impression that if there were a good way to implement this stuff without support from wine, the authors of DXVK would have figured it out by now given how it's one of the last major hurdles for the project. It has been discussed quite a bit.
Is the implication of the above that you intend for this to be public (i.e., external to Wine) API?
On 5/4/21 4:23 PM, Henri Verbeet wrote:
On Tue, 4 May 2021 at 21:39, Derek Lesho dlesho@codeweavers.com wrote:
I just get the impression that if there were a good way to implement this stuff without support from wine, the authors of DXVK would have figured it out by now given how it's one of the last major hurdles for the project. It has been discussed quite a bit.
Is the implication of the above that you intend for this to be public (i.e., external to Wine) API?
Yep, that's the intent.
On 5/4/21 2:39 PM, Derek Lesho wrote:
On 5/3/21 7:54 PM, Zebediah Figura (she/her) wrote:
On 5/3/21 4:08 PM, Derek Lesho wrote:
On 5/3/21 4:14 PM, Zebediah Figura (she/her) wrote:
Hello Derek, it looks like you've put a lot of thought into this problem. I assume you've already discussed this privately in detail with our Direct3D maintainers, but for the benefit of the rest of us, I have a few questions:
- How do you plan to accomodate the following (whether in the near or
distant future):
CL_KHR_d3d11_sharing
CL_KHR_gl_sharing
Shared resources in d3d9-11, when using the OpenGL backend
Shared resources in d3d12
My plan is to add an optional map inside the resource objects in which graphics API implementations (DXVK, vkd3d-proton, wined3d ...) and wine layers (openGL, openCL) can store extra data as needed. There would be an interface in the driver which takes a key (probably a GUID) and either accept or return an arbitrary blob of data. The benefit to this approach, in my view, is that it would allow some flexibility as requirements are understood and evolve for sharing between all these APIs. For example, at first, DXVK might roll its own data structure / interface for sharing between its D3D9 and D3D11 implementations, and maybe later on as the problem is better understood, an interface could come into being for sharing between DXVK's d3d11 and wined3d's d3d9.
I was kind of hoping for some more specific answers, i.e. rough descriptions of what the code does. Nevertheless, according to my understanding, this really doesn't account for all of the specifics of each API I've listed. Put simply, I'm worried that this patch set fits Vulkan and nothing else.
I admit that I haven't thought about openCL or D3D12 much, but for the other use-cases I'm really not sure what problems you are thinking of. Could you perhaps explain what concerns you have with them? From my perspective, all I've constructed is a generic way to associate data with objects, and a way to look the objects up by name and KMT handle. The only way I could see this not working would be if the FDs output by hosts APIs weren't compatible.
Ideally this sort of research would be done by the patch submitter, but anyway, one problem is that unlike Vulkan, OpenGL has no API to export a resource. As far as I can see, from just a bit of research, in order to share OpenGL resources one must create the destination context as "shared" with the source. It's not clear at all that "a generic way to associate data with objects" is enough to accomodate this.
- Shared resources between d3d9-12 devices, when using wined3d or
libvkd3d on Windows
- Shared resources between d3d9-12 devices and OpenGL or Vulkan, when
using wined3d or libvkd3d on Windows
I decided not to worry about the situation on windows, as it seems much more complex to me. There seems to be a number of sparsely documented D3DKMT APIs for tracking information about shared resources, and it seemed to me that it would be improper to try to implement these interfaces given how different the stack is on wine. As for a custom interface unrelated to the native one working on Windows, I'm really not sure how feasible that would be, and what benefits it would provide. We need to be able to associate metadata to the objects underlying the HANDLEs, as well as provide a global lookup for these objects, all in usermode. You could try implementing a global memory section that stores this metadata associated to KMT (global) handles, but you would have trouble doing the same for regular NT handles, since as far as I can see it is impossible to fetch a KMT handle from a regular NT handle on the windows implementation. If a solution does come about for this problem, I doubt it would be a good fit for wine.
I would strongly advise not throwing these possibilities out the window immediately.
I just get the impression that if there were a good way to implement this stuff without support from wine, the authors of DXVK would have figured it out by now given how it's one of the last major hurdles for the project. It has been discussed quite a bit.
This is a good example of why such discussion should not happen in a private, secluded space (and, worse, without including any of the several Wine developers actually involved in Direct3D), or, if it must, why the results and thought process should be fully explained. I don't avow a better knowledge of these APIs than said authors, but even if I were to limit myself to Vulkan, and forget about OpenGL, it's not obvious to me at all why this can't work.
- What is the reason for the "weak" reference counting introduced in
this patch?
In wine's ntoskrnl's object manager code, object structure data isn't freed until there are no more references to the object in the server. This is because a driver could reasonably expect that even though they don't hold any references to an object, they can expect for their pointer to valid if they or anyone else holds a handle to the object. The weak references allow a driver to safely store their own data alongside a weakly-referenced object, as with a weak reference, the destruction of the object structure will be deferred until there are no more weak references. The reason why the driver can't just hold a full reference is that it has no indication on when to free it, an application can request a HANDLE for a graphics resource, then completely de-initialize the graphics API, keeping the HANDLE. That is to say, there's no single point in the winevulkan code where we can make a call to the driver to free that object. On the other hand, if the driver were to keep no reference to the object, but just the pointer, it would have no way of knowing whether the object pointers are still valid when the client asks for information about an object.
The code, as it stands, looks all sorts of wrong. The right approach would be to let the server notify ntoskrnl of destruction, the way it already does.
In a sense, this is what the current approach does. It's just that instead of executing code straight from the thread that received the DISPATCH_FREE irp, it deduces whether or not that DISPATCH_FREE irp has come yet by finding out whether the object has any real references left. I'm not claiming that it's the most idiomatic approach, but it's a quite unintrusive and small helper that gives us the functionality we need.
Well, yes, that's exactly what I'm saying looks wrong. It's the sort of thing that looks like a hack bolted on to the existing code rather than fitting into its design (and, if necessary, changing that design to accomodate). Lack of intrusiveness is rarely, in my experience, worthwhile.
That's easier said than done, of course, and it takes some thought. One possible approach that comes to mind is to have shared resource handles be (NT) handles to device file objects. These files expose a pair of ioctls IOCTL_RESOURCE_SET_INFO and IOCTL_RESOURCE_GET_INFO, which store and retrieve a structure like:
Yeah, that could also work, I guess we would retain the global device WineVideoResourceManager, and create the per-fd device objects via IOCTLs to that global device. Seems like it would accomplish approximately the same thing with more code, with the advantage being that it wouldn't include the weak reference code, which I assume is what you consider all sorts of wrong.
struct resource_info { obj_handle_t fd_handle; struct wined3d_resource_desc desc; unsigned int layer_count; unsigned int level_count; };
I still think it would be unwise to lock ourselves into a structure like this. Different APIs will have different requirements and this will probably evolve some as we find more API surface to this. It also makes it a headache for out-of-tree projects to sync with whatever version of the interface is in the wine they are running on top of.
I'm not particularly advocating locking us into anything at the moment, but quite frankly, that's the burden that said out-of-tree projects must shoulder, by virtue of remaining out-of-tree (and, more saliently, refusing to interact with upstream).
"file" is itself a handle, which was created through wine_server_fd_to_handle(), and will be duplicated anew into the calling process through each IOCTL_RESOURCE_GET_INFO call. The remaining parameters are stored and returned transparently.
When winevulkan needs to create a shared resource handle, it does something like this:
vkGetMemoryFdKHR(..., &fd); file = CreateFile(...);
As I previously mentioned, I'm currently not aware of how we could create a previously-unexisting, possibly unnamed object through CreateFile. If there's something I'm missing here, could you please explain it?
CreateFile internally creates a FILE_OBJECT, which can be accessed from IRPs via the "FileObject" parameter. Private data can be allocated and stored in the "FsContext" pointer. See http.sys for an example.
resource_info.fd_handle = wine_server_fd_to_handle(fd); /* fill resource_info.desc &c. */ DeviceIoControl(file, ..., &resource_info, ...); CloseHandle(fd_handle);
and eventually returns "file" from vkGetMemoryWin32HandleKHR().
When wined3d, Vulkan, or OpenGL needs to open a shared resource handle, it uses IOCTL_RESOURCE_GET_INFO on that handle, followed by wine_server_handle_to_fd(). wined3d will subsequently use the other fields of struct resource_info; other APIs will ignore them.
Extending this to objects which aren't resources (e.g. semaphores, fences) is left as an exercise to the reader; it could be done with a separate pair of ioctls or by extending the above structure.
I will of course disclaim that I have thought this approach through completely, or that it is the best approach (in particular, I still am inclined to think that server-side management probably isn't that bad)
Yeah I'm not necessarily opposed to server-side management either, but at the very least it seemed to me like it was off the table in 2019.
I assume you're referring to the thread beginning with [2] (p.s. in the future, it'd be nice to link to old mail conversations you're referencing).
Personally, I'd interpret statements like "you'll have to make a very convincing argument that the existing primitives can't be used for this" as exactly that: if it really is unreasonable to implement something one way, explain why before trying to do it in a less obvious way. Particularly unintuitive code can count as "unreasonable".
, but it seems like a reasonable way to use the existing ntoskrnl infrastructure and avoid any awful hacks.
Note that this still doesn't handle OpenGL, and that's something that should really be resolved before anything is implemented.
Another advantage to the approach of letting the client APIs decide on how to store metadata is that, as long as we know that OpenGL and Vulkan (and any other potential host API) can export FDs which are compatible with one another, we can be assured that our current implementation isn't incompatible with the prospective implementation(/s) of the other API.
- Why is winevideo.sys necessary at all, if
wine_server_fd_to_handle() and wine_server_handle_to_fd() provide the necessary wrapping of an external FD object?
Because we need more information to be stored in the object behind the HANDLE, and we need a way to lookup these objects globally. For example, with D3D11 shared resources, we must be able to lookup and object by its KMT handle and name, and we must be able to store extra information about the object such as width, height, and layers, as Vulkan doesn't do this for us. Of course, we could figure add a way to put these objects into the server's object namespace, but that still leaves the KMT handles to worry about.
- What kind of objects are Vulkan or Direct3D shared handles on
Windows? E.g. what does NtQueryObject(ObjectTypeInformation) return? Are named Vulkan objects implemented using the NT namespace? How is this different between e.g. D3D11_RESOURCE_MISC_SHARED and D3D11_RESOURCE_MISC_SHARED_NTHANDLE, or between VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT and VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT?
KMT handles are in their own namespace, completely separated from the NT object system. None of the usual object APIs work with them. The objects behind the NT handles do have a custom type, and named Vulkan objects are implemented using the NT namespace. This is not reflected in the implementation I sent. I previously sent a patchset to implement these resources in 2019, and back then, the concern was that implementing these custom object types complicated wineserver code unnecessarily since the fd<->HANDLE APIs were already present. This is the reason why I had to come up with the new approach.
That is an answer to one of the questions I asked, though not really the others.
In particular, I suspect (especially given [1]) that the name parameter is really an NT name, or maps to one, which means that name assignment and lookup should be done in a way consistent with other NT objects.
In my previous email I did note that "named Vulkan objects are implemented using the NT namespace". In my 2019 version, I did implement naming of these objects through the NT namespace, but since this implementation doesn't have it's own wineserver type, it's not possible without adding such functionality to the existing fd type used for wine_server_fd_to_handle and wine_server_handle_to_fd. One problem with adding this functionality is that these ntdll APIs are public so we'd probably have to add an additional wine_server_fd_to_named_handle function for this. While it would be nice to be more accurate and put these objects into the NT namespace, in practice I don't think any applications are looking up these objects by any other method than using the graphics APIs.
Accuracy is not so much a concern to me as is leveraging existing lookup code rather than implementing our own.
- Are D3D11_RESOURCE_MISC_SHARED / KMT handles cross-process? If not,
should they be implemented using a process-local handle table somewhere instead of using NT handles? (Could/should we use D3DKMT* APIs from gdi32?)
I'd always assumed that they were, and I must admit I wrote the rest of this email under that assumption. I'll write tests tomorrow to find out. If they aren't, that would indeed make things a lot simpler...
On the flip side of this, is there any reason that we can't just pretend NT handles are KMT handles?
I'm assuming you mean returning NT handles when the app requests a KMT handle? If so, that wouldn't work as I've confirmed that KMT handles are indeed global. Additionally, Vulkan specifies that KMT handles don't hold a reference to the underlying object, as soon as the Vulkan object is freed, the KMT handles become invalid.
- Would it make more sense to use wineserver to manage shared
resources instead of a separate driver, and thereby avoid introducing hacks into ntoskrnl?
I think I addressed this in my response to question 4.
[1] https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12devi...
[2] https://www.winehq.org/pipermail/wine-devel/2019-November/153787.html
On 5/4/21 6:50 PM, Zebediah Figura (she/her) wrote:
On 5/4/21 2:39 PM, Derek Lesho wrote:
On 5/3/21 7:54 PM, Zebediah Figura (she/her) wrote:
On 5/3/21 4:08 PM, Derek Lesho wrote:
On 5/3/21 4:14 PM, Zebediah Figura (she/her) wrote:
Hello Derek, it looks like you've put a lot of thought into this problem. I assume you've already discussed this privately in detail with our Direct3D maintainers, but for the benefit of the rest of us, I have a few questions:
- How do you plan to accomodate the following (whether in the
near or distant future):
CL_KHR_d3d11_sharing
CL_KHR_gl_sharing
Shared resources in d3d9-11, when using the OpenGL backend
Shared resources in d3d12
My plan is to add an optional map inside the resource objects in which graphics API implementations (DXVK, vkd3d-proton, wined3d ...) and wine layers (openGL, openCL) can store extra data as needed. There would be an interface in the driver which takes a key (probably a GUID) and either accept or return an arbitrary blob of data. The benefit to this approach, in my view, is that it would allow some flexibility as requirements are understood and evolve for sharing between all these APIs. For example, at first, DXVK might roll its own data structure / interface for sharing between its D3D9 and D3D11 implementations, and maybe later on as the problem is better understood, an interface could come into being for sharing between DXVK's d3d11 and wined3d's d3d9.
I was kind of hoping for some more specific answers, i.e. rough descriptions of what the code does. Nevertheless, according to my understanding, this really doesn't account for all of the specifics of each API I've listed. Put simply, I'm worried that this patch set fits Vulkan and nothing else.
I admit that I haven't thought about openCL or D3D12 much, but for the other use-cases I'm really not sure what problems you are thinking of. Could you perhaps explain what concerns you have with them? From my perspective, all I've constructed is a generic way to associate data with objects, and a way to look the objects up by name and KMT handle. The only way I could see this not working would be if the FDs output by hosts APIs weren't compatible.
Ideally this sort of research would be done by the patch submitter, but anyway, one problem is that unlike Vulkan, OpenGL has no API to export a resource. As far as I can see, from just a bit of research, in order to share OpenGL resources one must create the destination context as "shared" with the source. It's not clear at all that "a generic way to associate data with objects" is enough to accomodate this.
From what I've seen, openGL has accumulated many different ways to share resources over the years. GL_EXT_memory_object_win32 seems to be the extension analogous to the VK_EXT_external_memory_win32, and it seems to correspond quite nicely, with HANDLEs being compatible between openGL and Vulkan users, as well as fds being compatible on the host side. The only mention of gl context related shared resources that I see is from a stack overflow post where egl is used, and I think that is a system unto itself (not related to the broader windows environment).
https://stackoverflow.com/questions/31954439/can-i-share-a-external-texture-...
- Shared resources between d3d9-12 devices, when using wined3d or
libvkd3d on Windows
- Shared resources between d3d9-12 devices and OpenGL or Vulkan, when
using wined3d or libvkd3d on Windows
I decided not to worry about the situation on windows, as it seems much more complex to me. There seems to be a number of sparsely documented D3DKMT APIs for tracking information about shared resources, and it seemed to me that it would be improper to try to implement these interfaces given how different the stack is on wine. As for a custom interface unrelated to the native one working on Windows, I'm really not sure how feasible that would be, and what benefits it would provide. We need to be able to associate metadata to the objects underlying the HANDLEs, as well as provide a global lookup for these objects, all in usermode. You could try implementing a global memory section that stores this metadata associated to KMT (global) handles, but you would have trouble doing the same for regular NT handles, since as far as I can see it is impossible to fetch a KMT handle from a regular NT handle on the windows implementation. If a solution does come about for this problem, I doubt it would be a good fit for wine.
I would strongly advise not throwing these possibilities out the window immediately.
I just get the impression that if there were a good way to implement this stuff without support from wine, the authors of DXVK would have figured it out by now given how it's one of the last major hurdles for the project. It has been discussed quite a bit.
This is a good example of why such discussion should not happen in a private, secluded space (and, worse, without including any of the several Wine developers actually involved in Direct3D)
The VKx Discord is public. https://discord.gg/mjWm8DK . Anyone is welcome to join, the discussions around stuff like this would probably happen in the channels with the -dev suffix.
, or, if it must, why the results and thought process should be fully explained. I don't avow a better knowledge of these APIs than said authors, but even if I were to limit myself to Vulkan, and forget about OpenGL, it's not obvious to me at all why this can't work.
I'm not sure what you're referring to by `this`, do you mean the idea I mentioned of using a global memory section, or trying to work with the Windows KMT APIs?
- What is the reason for the "weak" reference counting introduced in
this patch?
In wine's ntoskrnl's object manager code, object structure data isn't freed until there are no more references to the object in the server. This is because a driver could reasonably expect that even though they don't hold any references to an object, they can expect for their pointer to valid if they or anyone else holds a handle to the object. The weak references allow a driver to safely store their own data alongside a weakly-referenced object, as with a weak reference, the destruction of the object structure will be deferred until there are no more weak references. The reason why the driver can't just hold a full reference is that it has no indication on when to free it, an application can request a HANDLE for a graphics resource, then completely de-initialize the graphics API, keeping the HANDLE. That is to say, there's no single point in the winevulkan code where we can make a call to the driver to free that object. On the other hand, if the driver were to keep no reference to the object, but just the pointer, it would have no way of knowing whether the object pointers are still valid when the client asks for information about an object.
The code, as it stands, looks all sorts of wrong. The right approach would be to let the server notify ntoskrnl of destruction, the way it already does.
In a sense, this is what the current approach does. It's just that instead of executing code straight from the thread that received the DISPATCH_FREE irp, it deduces whether or not that DISPATCH_FREE irp has come yet by finding out whether the object has any real references left. I'm not claiming that it's the most idiomatic approach, but it's a quite unintrusive and small helper that gives us the functionality we need.
Well, yes, that's exactly what I'm saying looks wrong. It's the sort of thing that looks like a hack bolted on to the existing code rather than fitting into its design (and, if necessary, changing that design to accomodate). Lack of intrusiveness is rarely, in my experience, worthwhile.
Upon closer inspection, I found an edge case where using device objects as a front-end for the file objects as you suggested would prevent problems, so it looks like we won't have to discuss which approach we find more desirable after all 😁. When a Vulkan object is created to export KMT handles, the Vulkan resource always remains the sole reference to the underlying payload, so that once the initial object is destroyed, the only valid operations on the Vulkan objects that have imported the KMT handle is to destroy them. In my initial implementation, I accepted that I didn't yet have a solution for this problem, as by virtue of holding the fd handles, the payload would be preserved by the driver. If we add an object of indirection between the application visible handle and the fd handle, this should sort it self out.
That's easier said than done, of course, and it takes some thought. One possible approach that comes to mind is to have shared resource handles be (NT) handles to device file objects. These files expose a pair of ioctls IOCTL_RESOURCE_SET_INFO and IOCTL_RESOURCE_GET_INFO, which store and retrieve a structure like:
Yeah, that could also work, I guess we would retain the global device WineVideoResourceManager, and create the per-fd device objects via IOCTLs to that global device. Seems like it would accomplish approximately the same thing with more code, with the advantage being that it wouldn't include the weak reference code, which I assume is what you consider all sorts of wrong.
struct resource_info { obj_handle_t fd_handle; struct wined3d_resource_desc desc; unsigned int layer_count; unsigned int level_count; };
I still think it would be unwise to lock ourselves into a structure like this. Different APIs will have different requirements and this will probably evolve some as we find more API surface to this. It also makes it a headache for out-of-tree projects to sync with whatever version of the interface is in the wine they are running on top of.
I'm not particularly advocating locking us into anything at the moment, but quite frankly, that's the burden that said out-of-tree projects must shoulder, by virtue of remaining out-of-tree (and, more saliently, refusing to interact with upstream).
I don't think we need to discuss the politics and history behind the reasons why there exists out-of-tree projects to accept that, at the current moment, some of the most-used d3d implementations exist out of tree, and that if there's a way to accommodate them better at minimal cost to the wine project, it'd be preferable.
"file" is itself a handle, which was created through wine_server_fd_to_handle(), and will be duplicated anew into the calling process through each IOCTL_RESOURCE_GET_INFO call. The remaining parameters are stored and returned transparently.
When winevulkan needs to create a shared resource handle, it does something like this:
vkGetMemoryFdKHR(..., &fd); file = CreateFile(...);
As I previously mentioned, I'm currently not aware of how we could create a previously-unexisting, possibly unnamed object through CreateFile. If there's something I'm missing here, could you please explain it?
CreateFile internally creates a FILE_OBJECT, which can be accessed from IRPs via the "FileObject" parameter. Private data can be allocated and stored in the "FsContext" pointer. See http.sys for an example.
I'm aware, my question is, what path would we provide to CreateFile? Even if there is a way, I think that using IOCTLs from a primary device would make more sense, maybe you can take a look at what I mean when I finish v2.
resource_info.fd_handle = wine_server_fd_to_handle(fd); /* fill resource_info.desc &c. */ DeviceIoControl(file, ..., &resource_info, ...); CloseHandle(fd_handle);
and eventually returns "file" from vkGetMemoryWin32HandleKHR().
When wined3d, Vulkan, or OpenGL needs to open a shared resource handle, it uses IOCTL_RESOURCE_GET_INFO on that handle, followed by wine_server_handle_to_fd(). wined3d will subsequently use the other fields of struct resource_info; other APIs will ignore them.
Extending this to objects which aren't resources (e.g. semaphores, fences) is left as an exercise to the reader; it could be done with a separate pair of ioctls or by extending the above structure.
I will of course disclaim that I have thought this approach through completely, or that it is the best approach (in particular, I still am inclined to think that server-side management probably isn't that bad)
Yeah I'm not necessarily opposed to server-side management either, but at the very least it seemed to me like it was off the table in 2019.
I assume you're referring to the thread beginning with [2] (p.s. in the future, it'd be nice to link to old mail conversations you're referencing).
Personally, I'd interpret statements like "you'll have to make a very convincing argument that the existing primitives can't be used for this" as exactly that: if it really is unreasonable to implement something one way, explain why before trying to do it in a less obvious way. Particularly unintuitive code can count as "unreasonable".
If you think the solution you laid out involving a device object layer of the file handle is intuitive, I guess we can consider this resolved?
, but it seems like a reasonable way to use the existing ntoskrnl infrastructure and avoid any awful hacks.
Note that this still doesn't handle OpenGL, and that's something that should really be resolved before anything is implemented.
Another advantage to the approach of letting the client APIs decide on how to store metadata is that, as long as we know that OpenGL and Vulkan (and any other potential host API) can export FDs which are compatible with one another, we can be assured that our current implementation isn't incompatible with the prospective implementation(/s) of the other API.
- Why is winevideo.sys necessary at all, if
wine_server_fd_to_handle() and wine_server_handle_to_fd() provide the necessary wrapping of an external FD object?
Because we need more information to be stored in the object behind the HANDLE, and we need a way to lookup these objects globally. For example, with D3D11 shared resources, we must be able to lookup and object by its KMT handle and name, and we must be able to store extra information about the object such as width, height, and layers, as Vulkan doesn't do this for us. Of course, we could figure add a way to put these objects into the server's object namespace, but that still leaves the KMT handles to worry about.
- What kind of objects are Vulkan or Direct3D shared handles on
Windows? E.g. what does NtQueryObject(ObjectTypeInformation) return? Are named Vulkan objects implemented using the NT namespace? How is this different between e.g. D3D11_RESOURCE_MISC_SHARED and D3D11_RESOURCE_MISC_SHARED_NTHANDLE, or between VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT and VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT?
KMT handles are in their own namespace, completely separated from the NT object system. None of the usual object APIs work with them. The objects behind the NT handles do have a custom type, and named Vulkan objects are implemented using the NT namespace. This is not reflected in the implementation I sent. I previously sent a patchset to implement these resources in 2019, and back then, the concern was that implementing these custom object types complicated wineserver code unnecessarily since the fd<->HANDLE APIs were already present. This is the reason why I had to come up with the new approach.
That is an answer to one of the questions I asked, though not really the others.
In particular, I suspect (especially given [1]) that the name parameter is really an NT name, or maps to one, which means that name assignment and lookup should be done in a way consistent with other NT objects.
In my previous email I did note that "named Vulkan objects are implemented using the NT namespace". In my 2019 version, I did implement naming of these objects through the NT namespace, but since this implementation doesn't have it's own wineserver type, it's not possible without adding such functionality to the existing fd type used for wine_server_fd_to_handle and wine_server_handle_to_fd. One problem with adding this functionality is that these ntdll APIs are public so we'd probably have to add an additional wine_server_fd_to_named_handle function for this. While it would be nice to be more accurate and put these objects into the NT namespace, in practice I don't think any applications are looking up these objects by any other method than using the graphics APIs.
Accuracy is not so much a concern to me as is leveraging existing lookup code rather than implementing our own.
This should be resolved with the device object layer approach I think.
- Are D3D11_RESOURCE_MISC_SHARED / KMT handles cross-process? If
not, should they be implemented using a process-local handle table somewhere instead of using NT handles? (Could/should we use D3DKMT* APIs from gdi32?)
I'd always assumed that they were, and I must admit I wrote the rest of this email under that assumption. I'll write tests tomorrow to find out. If they aren't, that would indeed make things a lot simpler...
On the flip side of this, is there any reason that we can't just pretend NT handles are KMT handles?
I'm assuming you mean returning NT handles when the app requests a KMT handle? If so, that wouldn't work as I've confirmed that KMT handles are indeed global. Additionally, Vulkan specifies that KMT handles don't hold a reference to the underlying object, as soon as the Vulkan object is freed, the KMT handles become invalid.
- Would it make more sense to use wineserver to manage shared
resources instead of a separate driver, and thereby avoid introducing hacks into ntoskrnl?
I think I addressed this in my response to question 4.
[1] https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12devi...
[2] https://www.winehq.org/pipermail/wine-devel/2019-November/153787.html
On Mon, 3 May 2021 at 22:14, Zebediah Figura (she/her) zfigura@codeweavers.com wrote:
Hello Derek, it looks like you've put a lot of thought into this problem. I assume you've already discussed this privately in detail with our Direct3D maintainers, but for the benefit of the rest of us, I have a few questions:
For the record, this is the first time I've seen this code; for better or worse, it's not something I had a hand in creating.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winevideo.sys/winevideo.c | 77 ++++++++++++++++++++++++ dlls/winevulkan/vulkan.c | 107 +++++++++++++++++++++++++++++---- 2 files changed, 173 insertions(+), 11 deletions(-)
diff --git a/dlls/winevideo.sys/winevideo.c b/dlls/winevideo.sys/winevideo.c index 04e171bea3f..d75db685abe 100644 --- a/dlls/winevideo.sys/winevideo.c +++ b/dlls/winevideo.sys/winevideo.c @@ -17,6 +17,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(winevideo);
#define IOCTL_VIDEO_RESOURCE_CREATE CTL_CODE(FILE_DEVICE_VIDEO, 0, METHOD_BUFFERED, FILE_WRITE_ACCESS) #define IOCTL_VIDEO_RESOURCE_FIND_BY_NAME CTL_CODE(FILE_DEVICE_VIDEO, 1, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_VIDEO_RESOURCE_FIND_BY_KMT CTL_CODE(FILE_DEVICE_VIDEO, 2, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_VIDEO_RESOURCE_GET_KMT_HANDLE CTL_CODE(FILE_DEVICE_VIDEO, 3, METHOD_BUFFERED, FILE_READ_ACCESS)
struct video_resource { @@ -229,6 +231,69 @@ static NTSTATUS find_resource_by_name(void *buff, SIZE_T insize, SIZE_T outsize, return STATUS_OBJECT_NAME_NOT_FOUND; }
+static NTSTATUS find_resource_by_kmt(void *buff, SIZE_T insize, SIZE_T outsize, IO_STATUS_BLOCK *iosb) +{ + unsigned int idx; + obj_handle_t ret; + + if (insize < sizeof(obj_handle_t)) + return STATUS_INFO_LENGTH_MISMATCH; + + idx = ( *(obj_handle_t *)buff / 4) - 1; + + if (idx >= video_resources_size) + return STATUS_NOT_FOUND; + + if (!video_resources[idx].object) + return STATUS_NOT_FOUND; + + if (!ObGetObjectPointerCount(video_resources[idx].object)) + return STATUS_NOT_FOUND; + + if (!(ret = open_client_handle(video_resources[idx].object))) + return STATUS_INTERNAL_ERROR; + + if (outsize < sizeof(obj_handle_t)) + return STATUS_INFO_LENGTH_MISMATCH; + + iosb->Information = sizeof(obj_handle_t); + *(obj_handle_t *)buff = ret; + + return STATUS_SUCCESS; +} + +static NTSTATUS get_kmt_handle(void *buff, SIZE_T insize, SIZE_T outsize, IO_STATUS_BLOCK *iosb) +{ + obj_handle_t *handle = buff; + unsigned int i; + void *object; + + if (insize < sizeof(obj_handle_t)) + return STATUS_INFO_LENGTH_MISMATCH; + + if (!(object = reference_client_handle(*handle))) + return STATUS_INVALID_HANDLE; + + for (i = 0; i < video_resources_size; i++) + { + if (video_resources[i].object == object) + break; + } + + ObDereferenceObject(object); + + if (i == video_resources_size) + return STATUS_INVALID_HANDLE; + + if (outsize < sizeof(obj_handle_t)) + return STATUS_INFO_LENGTH_MISMATCH; + + iosb->Information = sizeof(obj_handle_t); + *handle = (i + 1) * 4; + + return STATUS_SUCCESS; +} + static NTSTATUS WINAPI winevideo_ioctl(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); @@ -253,6 +318,18 @@ static NTSTATUS WINAPI winevideo_ioctl(DEVICE_OBJECT *device, IRP *irp) irpsp->Parameters.DeviceIoControl.OutputBufferLength, &irp->IoStatus ); break; + case IOCTL_VIDEO_RESOURCE_FIND_BY_KMT: + status = find_resource_by_kmt( irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.InputBufferLength, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, + &irp->IoStatus ); + break; + case IOCTL_VIDEO_RESOURCE_GET_KMT_HANDLE: + status = get_kmt_handle( irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.InputBufferLength, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, + &irp->IoStatus ); + break; default: FIXME( "ioctl %x not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode ); status = STATUS_NOT_SUPPORTED; diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 65f53aa679e..9614c68d1d3 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -1376,15 +1376,17 @@ void WINAPI wine_vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice ph
TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties);
- if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + if (buffer_info_dup.handleType & (VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT)) buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
thunk_vkGetPhysicalDeviceExternalBufferProperties(phys_dev, &buffer_info_dup, properties);
if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) - properties->externalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + properties->externalMemoryProperties.exportFromImportedHandleTypes = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) - properties->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + properties->externalMemoryProperties.compatibleHandleTypes = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; }
void WINAPI wine_vkGetPhysicalDeviceExternalBufferPropertiesKHR(VkPhysicalDevice phys_dev, @@ -1394,15 +1396,17 @@ void WINAPI wine_vkGetPhysicalDeviceExternalBufferPropertiesKHR(VkPhysicalDevice
TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties);
- if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + if (buffer_info_dup.handleType & (VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT)) buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
thunk_vkGetPhysicalDeviceExternalBufferPropertiesKHR(phys_dev, &buffer_info_dup, properties);
if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) - properties->externalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + properties->externalMemoryProperties.exportFromImportedHandleTypes = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) - properties->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + properties->externalMemoryProperties.compatibleHandleTypes = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; }
static VkResult wine_vk_get_physical_device_image_format_properties_2(VkPhysicalDevice phys_dev, @@ -1417,7 +1421,7 @@ static VkResult wine_vk_get_physical_device_image_format_properties_2(VkPhysical
if ((external_image_info = wine_vk_find_struct(format_info, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO))) { - if (external_image_info->handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + if (external_image_info->handleType & (VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT)) { if ((res = convert_VkPhysicalDeviceImageFormatInfo2_struct_chain(format_info->pNext, &format_info_host)) < 0) { @@ -1428,7 +1432,7 @@ static VkResult wine_vk_get_physical_device_image_format_properties_2(VkPhysical external_image_info_host = wine_vk_find_struct(&format_info_host, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO); external_image_info_host->handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; } - if (external_image_info->handleType &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + if (external_image_info->handleType &~ (VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT)) { WARN("Unsupported handle type %#x.\n", external_image_info->handleType); return VK_ERROR_FORMAT_NOT_SUPPORTED; @@ -1446,12 +1450,12 @@ static VkResult wine_vk_get_physical_device_image_format_properties_2(VkPhysical if (p->exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) { p->exportFromImportedHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; - p->exportFromImportedHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + p->exportFromImportedHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; } if (p->compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) { p->compatibleHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; - p->compatibleHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + p->compatibleHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; } }
@@ -2044,6 +2048,61 @@ static HANDLE open_video_resource_by_name(LPCWSTR name) return wine_server_ptr_handle(handle_out); }
+#define IOCTL_VIDEO_RESOURCE_FIND_BY_KMT CTL_CODE(FILE_DEVICE_VIDEO, 2, METHOD_BUFFERED, FILE_READ_ACCESS) + +static HANDLE open_video_resource_by_kmt(HANDLE kmt_handle) +{ + obj_handle_t handle_in, handle_out; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + if (!winevideo_device) + return INVALID_HANDLE_VALUE; + + handle_in = wine_server_obj_handle(kmt_handle); + + if ((status = NtDeviceIoControlFile(winevideo_device, NULL, NULL, NULL, &iosb, IOCTL_VIDEO_RESOURCE_FIND_BY_KMT, + &handle_in, sizeof(handle_in), &handle_out, sizeof(handle_out)))) + { + ERR("Failed to open video resource by KMT handle, status %#x.\n", status); + return INVALID_HANDLE_VALUE; + } + + if (iosb.Information < sizeof(handle_out)) + { + ERR("Unexpected out size.\n"); + return INVALID_HANDLE_VALUE; + } + + return wine_server_ptr_handle(handle_out); +} + +#define IOCTL_VIDEO_RESOURCE_GET_KMT_HANDLE CTL_CODE(FILE_DEVICE_VIDEO, 3, METHOD_BUFFERED, FILE_READ_ACCESS) + +static HANDLE get_video_resource_kmt_handle(HANDLE video_resource) +{ + obj_handle_t handle_in, handle_out; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + if (!winevideo_device) + return INVALID_HANDLE_VALUE; + + handle_in = wine_server_obj_handle(video_resource); + + if ((status = NtDeviceIoControlFile(winevideo_device, NULL, NULL, NULL, &iosb, IOCTL_VIDEO_RESOURCE_GET_KMT_HANDLE, + &handle_in, sizeof(handle_in), &handle_out, sizeof(handle_out)))) + { + ERR("Failed to get KMT handle for video resource, status %#x.\n", status); + return INVALID_HANDLE_VALUE; + } + + if (iosb.Information < sizeof(handle_out)) + ERR("Unexpected out size.\n"); + + return wine_server_ptr_handle(handle_out); +} + VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *allocate_info, const VkAllocationCallbacks *allocator, VkDeviceMemory *memory_out) { @@ -2111,7 +2170,7 @@ VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInf { export_info = (VkExportMemoryAllocateInfo *)header; object->handle_types = export_info->handleTypes; - if (object->handle_types &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + if (object->handle_types &~ (VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT|VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT)) { res = VK_ERROR_OUT_OF_HOST_MEMORY; goto done; @@ -2137,6 +2196,15 @@ VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInf else if (handle_import_info->name) object->handle = open_video_resource_by_name( handle_import_info->name ); break; + case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT: + /* FIXME: the spec says that device memory imported from a KMT handle doesn't keep a reference to the underyling payload. + This means that in cases where on windows an application leaks VkDeviceMemory objects, we leak the full payload. To + fix this, we would need wine_dev_mem objects to store no reference to the payload, that means no host VkDeviceMemory + object (as objects imported from FDs hold a reference to the payload), and no win32 handle to the object. We would then + extend make_vulkan to have the thunks converting wine_dev_mem to native handles open the VkDeviceMemory from the KMT + handle, use it in the host function, then close it again. */ + object->handle = open_video_resource_by_kmt( handle_import_info->handle ); + break; default: WARN("Invalid handle type %08x passed in.\n", handle_import_info->handleType); res = VK_ERROR_INVALID_EXTERNAL_HANDLE; @@ -2213,6 +2281,7 @@ VkResult WINAPI wine_vkGetMemoryWin32HandleKHR(VkDevice device, { struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle_info->memory); const VkBaseInStructure *chain; + HANDLE ret;
TRACE("%p, %p %p\n", device, handle_info, handle);
@@ -2227,6 +2296,13 @@ VkResult WINAPI wine_vkGetMemoryWin32HandleKHR(VkDevice device, case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: return !NtDuplicateObject( NtCurrentProcess(), dev_mem->handle, NtCurrentProcess(), handle, dev_mem->access, dev_mem->inherit ? OBJ_INHERIT : 0, 0) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT: + { + if ((ret = get_video_resource_kmt_handle(dev_mem->handle)) == INVALID_HANDLE_VALUE) + return VK_ERROR_OUT_OF_HOST_MEMORY; + *handle = ret; + return VK_SUCCESS; + } default: return VK_ERROR_UNKNOWN; } @@ -2251,6 +2327,7 @@ VkResult WINAPI wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, VkExternalMemoryHandleTypeFlagBits type, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *properties) { VkMemoryFdPropertiesKHR fd_props; + HANDLE video_resource; VkResult res; int fd = -1;
@@ -2260,6 +2337,14 @@ VkResult WINAPI wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, { wine_server_handle_to_fd(handle, FILE_READ_DATA, &fd, NULL); } + else if (type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) + { + video_resource = open_video_resource_by_kmt(handle); + + wine_server_handle_to_fd(video_resource, FILE_READ_DATA, &fd, NULL); + + NtClose(video_resource); + }
if (fd == -1) return VK_ERROR_INVALID_EXTERNAL_HANDLE;
On 03.05.21 18:46, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
This commit had to be squashed in with the tests, as data structures for the extension are missing from the headers when the extension is blacklisted by make_vulkan.
dlls/vulkan-1/tests/vulkan.c | 242 +++++++++++++++++-- dlls/winevulkan/loader_thunks.c | 12 + dlls/winevulkan/loader_thunks.h | 2 + dlls/winevulkan/make_vulkan | 63 ++++- dlls/winevulkan/vulkan.c | 385 +++++++++++++++++++++++++++++-- dlls/winevulkan/vulkan_private.h | 22 ++ 6 files changed, 679 insertions(+), 47 deletions(-)
Hi,
I would prefer if you could split this up in to multiple patches, e.g. the host only functions and struct conversion changes in make_vulkan could each be in their own commits.
diff --git a/dlls/vulkan-1/tests/vulkan.c b/dlls/vulkan-1/tests/vulkan.c index 9061b2b6db8..c1350da70ee 100644 --- a/dlls/vulkan-1/tests/vulkan.c +++ b/dlls/vulkan-1/tests/vulkan.c @@ -144,7 +144,7 @@ static void test_instance_version(void) VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)); }
-static void enumerate_physical_device(VkPhysicalDevice vk_physical_device) +static void enumerate_physical_device(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkPhysicalDeviceProperties properties;
@@ -224,7 +224,7 @@ static void test_enumerate_physical_device2(void) vkDestroyInstance(vk_instance, NULL); }
-static void enumerate_device_queues(VkPhysicalDevice vk_physical_device) +static void enumerate_device_queues(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkPhysicalDeviceProperties device_properties; VkQueueFamilyProperties *properties; @@ -283,7 +283,7 @@ static void test_physical_device_groups(void) trace("Group[%u] count %u, subset allocation %#x\n", i, properties[i].physicalDeviceCount, properties[i].subsetAllocation); for (j = 0; j < properties[i].physicalDeviceCount; ++j)
enumerate_physical_device(properties[i].physicalDevices[j]);
enumerate_physical_device(vk_instance, properties[i].physicalDevices[j]); } if ((vr = create_device(properties->physicalDevices[0], 0, NULL, NULL, &vk_device)) < 0)
@@ -307,7 +307,7 @@ static void test_physical_device_groups(void) vkDestroyInstance(vk_instance, NULL); }
-static void test_destroy_command_pool(VkPhysicalDevice vk_physical_device) +static void test_destroy_command_pool(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkCommandBufferAllocateInfo allocate_info; VkCommandPoolCreateInfo pool_info; @@ -366,7 +366,7 @@ static void test_unsupported_instance_extensions(void) } }
-static void test_unsupported_device_extensions(VkPhysicalDevice vk_physical_device) +static void test_unsupported_device_extensions(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkDevice vk_device; unsigned int i; @@ -387,7 +387,7 @@ static void test_unsupported_device_extensions(VkPhysicalDevice vk_physical_devi } }
-static void test_private_data(VkPhysicalDevice vk_physical_device) +static void test_private_data(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { PFN_vkDestroyPrivateDataSlotEXT pfn_vkDestroyPrivateDataSlotEXT; PFN_vkCreatePrivateDataSlotEXT pfn_vkCreatePrivateDataSlotEXT; @@ -437,7 +437,218 @@ static void test_private_data(VkPhysicalDevice vk_physical_device) vkDestroyDevice(vk_device, NULL); }
-static void for_each_device(void (*test_func)(VkPhysicalDevice)) +uint32_t find_memory_type(VkPhysicalDevice vk_physical_device, VkMemoryPropertyFlagBits flags) +{
- VkPhysicalDeviceMemoryProperties properties = {0};
- unsigned int i;
- vkGetPhysicalDeviceMemoryProperties(vk_physical_device, &properties);
- for(i = 0; i < properties.memoryTypeCount; i++)
- {
if (properties.memoryTypes[i].propertyFlags & flags)
return i;
- }
- return -1;
+}
+static void test_external_memory(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) +{
- PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR;
- PFN_vkGetMemoryWin32HandleKHR pfn_vkGetMemoryWin32HandleKHR;
- VkPhysicalDeviceExternalBufferInfoKHR external_buffer_info;
- VkExternalBufferPropertiesKHR external_buffer_properties;
- VkMemoryDedicatedAllocateInfoKHR dedicated_alloc_info;
- VkExportMemoryWin32HandleInfoKHR export_handle_info;
- VkImportMemoryWin32HandleInfoKHR import_handle_info;
- VkExportMemoryAllocateInfoKHR export_memory_info;
- VkMemoryGetWin32HandleInfoKHR get_handle_info;
- VkBufferCreateInfo buffer_create_info;
- VkMemoryAllocateInfo alloc_info;
- uint32_t queue_family_index;
- HANDLE nt_handle, kmt_handle;
- VkDeviceMemory vk_memory, vk_memory_import;
- VkBuffer vk_buffer;
- VkDevice vk_device;
- VkResult vr;
- static const char *extensions[] =
- {
"VK_KHR_dedicated_allocation",
"VK_KHR_external_memory",
"VK_KHR_external_memory_win32",
- };
- pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR =
(void*) vkGetInstanceProcAddr(vk_instance, "vkGetPhysicalDeviceExternalBufferPropertiesKHR");
- if ((vr = create_device(vk_physical_device, ARRAY_SIZE(extensions), extensions, NULL, &vk_device)))
- {
skip("Failed to create device with external memory extensions, VkResult %d.\n", vr);
return;
- }
- pfn_vkGetMemoryWin32HandleKHR = (void *) vkGetDeviceProcAddr(vk_device, "vkGetMemoryWin32HandleKHR");
- find_queue_family(vk_physical_device, VK_QUEUE_GRAPHICS_BIT, &queue_family_index);
- /* Most implementations only support exporting dedicated allocations */
- buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- buffer_create_info.pNext = NULL;
- buffer_create_info.flags = 0;
- buffer_create_info.size = 1;
- buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- buffer_create_info.sharingMode = VK_SHARING_MODE_CONCURRENT;
- buffer_create_info.queueFamilyIndexCount = 1;
- buffer_create_info.pQueueFamilyIndices = &queue_family_index;
- if ((vr = vkCreateBuffer(vk_device, &buffer_create_info, NULL, &vk_buffer)))
- {
skip("Failed to create generic buffer, VkResult %d.\n", vr);
vkDestroyDevice(vk_device, NULL);
return;
- }
- dedicated_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
- dedicated_alloc_info.pNext = NULL;
- dedicated_alloc_info.image = VK_NULL_HANDLE;
- dedicated_alloc_info.buffer = vk_buffer;
- external_buffer_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR;
- external_buffer_info.pNext = NULL;
- external_buffer_info.flags = 0;
- external_buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- external_buffer_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
- memset(&external_buffer_properties, 0, sizeof(external_buffer_properties));
- external_buffer_properties.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR;
- pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR(vk_physical_device, &external_buffer_info, &external_buffer_properties);
- if (!(external_buffer_properties.externalMemoryProperties.externalMemoryFeatures &
(VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR|VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR)))
skip("With desired parameters, buffers are not exportable to and importable from an NT handle.\n");
- else
- {
ok(external_buffer_properties.externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR,
"Unexpected compatibleHandleTypes %#x.\n", external_buffer_properties.externalMemoryProperties.compatibleHandleTypes);
export_memory_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
export_memory_info.pNext = &dedicated_alloc_info;
export_memory_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
export_handle_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR;
export_handle_info.pNext = &export_memory_info;
export_handle_info.name = L"wine_test_buffer_export_name";
export_handle_info.dwAccess = GENERIC_ALL;
export_handle_info.pAttributes = NULL;
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = &export_handle_info;
alloc_info.allocationSize = 1;
alloc_info.memoryTypeIndex = find_memory_type(vk_physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
ok(alloc_info.memoryTypeIndex != -1, "Device local memory type index was not found.\n");
vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory);
ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
get_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
get_handle_info.pNext = NULL;
get_handle_info.memory = vk_memory;
get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
vr = pfn_vkGetMemoryWin32HandleKHR(vk_device, &get_handle_info, &nt_handle);
ok(vr == VK_SUCCESS, "vkGetMemoryWin32HandleKHR failed, VkResult %d.\n", vr);
import_handle_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR;
import_handle_info.pNext = &dedicated_alloc_info;
import_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
import_handle_info.handle = nt_handle;
import_handle_info.name = NULL;
alloc_info.pNext = &import_handle_info;
vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import);
ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
vkFreeMemory(vk_device, vk_memory_import, NULL);
import_handle_info.handle = NULL;
import_handle_info.name = L"wine_test_buffer_export_name";
vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import);
- todo_wine
ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
if (vr == VK_SUCCESS)
{
ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
vkFreeMemory(vk_device, vk_memory_import, NULL);
}
vkFreeMemory(vk_device, vk_memory, NULL);
CloseHandle(nt_handle);
- }
- external_buffer_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
- memset(&external_buffer_properties, 0, sizeof(external_buffer_properties));
- external_buffer_properties.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR;
- pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR(vk_physical_device, &external_buffer_info, &external_buffer_properties);
- if (!(external_buffer_properties.externalMemoryProperties.externalMemoryFeatures &
(VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR|VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR)))
skip("With desired parameters, buffers are not exportable to and importable from a KMT handle.\n");
- else
- {
ok(external_buffer_properties.externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR,
"Unexpected compatibleHandleTypes %#x.\n", external_buffer_properties.externalMemoryProperties.compatibleHandleTypes);
export_memory_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
export_memory_info.pNext = &dedicated_alloc_info;
export_memory_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = &export_memory_info;
alloc_info.allocationSize = 1;
alloc_info.memoryTypeIndex = find_memory_type(vk_physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
ok(alloc_info.memoryTypeIndex != -1, "Device local memory type index was not found.\n");
vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory);
ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
get_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
get_handle_info.pNext = NULL;
get_handle_info.memory = vk_memory;
get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
vr = pfn_vkGetMemoryWin32HandleKHR(vk_device, &get_handle_info, &kmt_handle);
ok(vr == VK_SUCCESS, "vkGetMemoryWin32HandleKHR failed, VkResult %d.\n", vr);
import_handle_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR;
import_handle_info.pNext = &dedicated_alloc_info;
import_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
import_handle_info.handle = kmt_handle;
import_handle_info.name = NULL;
alloc_info.pNext = &import_handle_info;
vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import);
ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
vkFreeMemory(vk_device, vk_memory_import, NULL);
vkFreeMemory(vk_device, vk_memory, NULL);
- }
- vkDestroyBuffer(vk_device, vk_buffer, NULL);
- vkDestroyDevice(vk_device, NULL);
+}
+static void for_each_device(void (*test_func)(VkInstance,VkPhysicalDevice), uint32_t extension_count,
{ VkPhysicalDevice *vk_physical_devices; VkInstance vk_instance;const char * const *enabled_extensions)
@@ -445,7 +656,7 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) uint32_t count; VkResult vr;
- if ((vr = create_instance_skip(0, NULL, &vk_instance)) < 0)
- if ((vr = create_instance_skip(extension_count, enabled_extensions, &vk_instance)) < 0) return; ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr);
@@ -463,7 +674,7 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr);
for (i = 0; i < count; ++i)
test_func(vk_physical_devices[i]);
test_func(vk_instance, vk_physical_devices[i]); heap_free(vk_physical_devices);
@@ -472,13 +683,16 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice))
START_TEST(vulkan) {
- static const char *external_memory_capabilities = "VK_KHR_external_memory_capabilities";
test_instance_version();
- for_each_device(enumerate_physical_device);
- for_each_device(enumerate_physical_device, 0, NULL); test_enumerate_physical_device2();
- for_each_device(enumerate_device_queues);
- for_each_device(enumerate_device_queues, 0, NULL); test_physical_device_groups();
- for_each_device(test_destroy_command_pool);
- for_each_device(test_destroy_command_pool, 0, NULL); test_unsupported_instance_extensions();
- for_each_device(test_unsupported_device_extensions);
- for_each_device(test_private_data);
- for_each_device(test_unsupported_device_extensions, 0, NULL);
- for_each_device(test_private_data, 0, NULL);
- for_each_device(test_external_memory, 1, &external_memory_capabilities); }
diff --git a/dlls/winevulkan/loader_thunks.c b/dlls/winevulkan/loader_thunks.c index 69fcd44160e..471999a3821 100644 --- a/dlls/winevulkan/loader_thunks.c +++ b/dlls/winevulkan/loader_thunks.c @@ -1448,6 +1448,16 @@ VkResult WINAPI vkGetMemoryHostPointerPropertiesEXT(VkDevice device, VkExternalM return unix_funcs->p_vkGetMemoryHostPointerPropertiesEXT(device, handleType, pHostPointer, pMemoryHostPointerProperties); }
+VkResult WINAPI vkGetMemoryWin32HandleKHR(VkDevice device, const VkMemoryGetWin32HandleInfoKHR *pGetWin32HandleInfo, HANDLE *pHandle) +{
- return unix_funcs->p_vkGetMemoryWin32HandleKHR(device, pGetWin32HandleInfo, pHandle);
+}
+VkResult WINAPI vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *pMemoryWin32HandleProperties) +{
- return unix_funcs->p_vkGetMemoryWin32HandlePropertiesKHR(device, handleType, handle, pMemoryWin32HandleProperties);
+}
- VkResult WINAPI vkGetPerformanceParameterINTEL(VkDevice device, VkPerformanceParameterTypeINTEL parameter, VkPerformanceValueINTEL *pValue) { return unix_funcs->p_vkGetPerformanceParameterINTEL(device, parameter, pValue);
@@ -2223,6 +2233,8 @@ static const struct vulkan_func vk_device_dispatch_table[] = {"vkGetImageSparseMemoryRequirements2KHR", &vkGetImageSparseMemoryRequirements2KHR}, {"vkGetImageSubresourceLayout", &vkGetImageSubresourceLayout}, {"vkGetMemoryHostPointerPropertiesEXT", &vkGetMemoryHostPointerPropertiesEXT},
- {"vkGetMemoryWin32HandleKHR", &vkGetMemoryWin32HandleKHR},
- {"vkGetMemoryWin32HandlePropertiesKHR", &vkGetMemoryWin32HandlePropertiesKHR}, {"vkGetPerformanceParameterINTEL", &vkGetPerformanceParameterINTEL}, {"vkGetPipelineCacheData", &vkGetPipelineCacheData}, {"vkGetPipelineExecutableInternalRepresentationsKHR", &vkGetPipelineExecutableInternalRepresentationsKHR},
diff --git a/dlls/winevulkan/loader_thunks.h b/dlls/winevulkan/loader_thunks.h index e7257cef508..031b7f5652b 100644 --- a/dlls/winevulkan/loader_thunks.h +++ b/dlls/winevulkan/loader_thunks.h @@ -304,6 +304,8 @@ struct unix_funcs void (WINAPI *p_vkGetImageSparseMemoryRequirements2KHR)(VkDevice, const VkImageSparseMemoryRequirementsInfo2 *, uint32_t *, VkSparseImageMemoryRequirements2 *); void (WINAPI *p_vkGetImageSubresourceLayout)(VkDevice, VkImage, const VkImageSubresource *, VkSubresourceLayout *); VkResult (WINAPI *p_vkGetMemoryHostPointerPropertiesEXT)(VkDevice, VkExternalMemoryHandleTypeFlagBits, const void *, VkMemoryHostPointerPropertiesEXT *);
- VkResult (WINAPI *p_vkGetMemoryWin32HandleKHR)(VkDevice, const VkMemoryGetWin32HandleInfoKHR *, HANDLE *);
- VkResult (WINAPI *p_vkGetMemoryWin32HandlePropertiesKHR)(VkDevice, VkExternalMemoryHandleTypeFlagBits, HANDLE, VkMemoryWin32HandlePropertiesKHR *); VkResult (WINAPI *p_vkGetPerformanceParameterINTEL)(VkDevice, VkPerformanceParameterTypeINTEL, VkPerformanceValueINTEL *); VkResult (WINAPI *p_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT)(VkPhysicalDevice, uint32_t *, VkTimeDomainEXT *); VkResult (WINAPI *p_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV)(VkPhysicalDevice, uint32_t *, VkCooperativeMatrixPropertiesNV *);
diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index b1877acda17..0a0e3bac299 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -99,7 +99,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_pipeline_creation_feedback", "VK_GOOGLE_display_timing", "VK_KHR_external_fence_win32",
- "VK_KHR_external_memory_win32", "VK_KHR_external_semaphore_win32", # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work.
@@ -109,7 +108,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_external_memory_dma_buf", "VK_EXT_image_drm_format_modifier", "VK_KHR_external_fence_fd",
"VK_KHR_external_memory_fd", "VK_KHR_external_semaphore_fd",
# Extensions which require callback handling
@@ -154,6 +152,8 @@ class ThunkType(Enum): # - PRIVATE thunks can be used in custom implementations for # struct conversion. # - loader_thunk sets whether to create a thunk for unix_funcs. +# - host_only sets whether to preclude the function from the +# win32-facing dispatch table. FUNCTION_OVERRIDES = { # Global functions "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, @@ -169,7 +169,7 @@ FUNCTION_OVERRIDES = { "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE}, "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
- "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
- "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
@@ -178,11 +178,13 @@ FUNCTION_OVERRIDES = {
# Device functions "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
- "vkAllocateMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
- "vkFreeMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE}, "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
@@ -213,7 +215,7 @@ FUNCTION_OVERRIDES = { "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
# VK_KHR_external_memory_capabilities
- "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
"vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
# VK_KHR_external_semaphore_capabilities
@@ -249,12 +251,22 @@ FUNCTION_OVERRIDES = { # VK_EXT_debug_marker "vkDebugMarkerSetObjectNameEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkDebugMarkerSetObjectTagEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
- # VK_KHR_external_memory_win32
- "vkGetMemoryWin32HandleKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
- "vkGetMemoryWin32HandlePropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
- # VK_KHR_external_memory_fd
- "vkGetMemoryFdKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "host_only" : True},
- "vkGetMemoryFdPropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "host_only" : True},
Would it make sense to added a new value to ThunkType instead, e.g. ThunkType.NOT_EXPOSED?
}
-STRUCT_CHAIN_CONVERSIONS = [
- "VkDeviceCreateInfo",
- "VkInstanceCreateInfo",
-] +STRUCT_CHAIN_CONVERSIONS = {
- "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"],
- "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"],
- "VkPhysicalDeviceImageFormatInfo2": [],
- "VkMemoryAllocateInfo": ["VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR", "VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR"],
+}
class Direction(Enum): @@ -532,6 +544,7 @@ class VkFunction(object): self.driver = func_info["driver"] if func_info else False self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC
self.host_only = func_info["host_only"] if func_info and "host_only" in func_info else False # Required is set while parsing which APIs and types are required # and is used by the code generation.
@@ -651,6 +664,9 @@ class VkFunction(object): def needs_private_thunk(self): return self.thunk_type == ThunkType.PRIVATE
- def needs_win_exclusion(self):
return self.host_only
def pfn(self, prefix="p", call_conv=None, conv=False): """ Create function pointer. """
@@ -1028,6 +1044,8 @@ class VkHandle(object): return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name) if self.name == "VkSurfaceKHR": return "wine_surface_from_handle({0})->surface".format(name)
if self.name == "VkDeviceMemory":
return "wine_dev_mem_from_handle({0})->dev_mem".format(name) native_handle_name = None
@@ -2115,9 +2133,10 @@ class FreeFunction(object):
class StructChainConversionFunction(object):
- def __init__(self, direction, struct):
def __init__(self, direction, struct, ignores): self.direction = direction self.struct = struct
self.ignores = ignores self.type = struct.name self.name = "convert_{0}_struct_chain".format(self.type)
@@ -2143,8 +2162,8 @@ class StructChainConversionFunction(object): body += " {\n"
# Ignore to not confuse host loader.
This comment is now misplaced.
body += " case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO:\n"
body += " case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO:\n"
for i in self.ignores:
body += " case {0}:\n".format(i) body += " break;\n\n" for e in self.struct.struct_extensions:
@@ -2153,6 +2172,9 @@ class StructChainConversionFunction(object):
stype = next(x for x in e.members if x.name == "sType")
if stype.values in self.ignores:
continue
body += " case {0}:\n".format(stype.values) body += " {\n"
@@ -2250,7 +2272,7 @@ class VkGenerator(object):
for struct in self.registry.structs: if struct.name in STRUCT_CHAIN_CONVERSIONS:
self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct))
self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct, STRUCT_CHAIN_CONVERSIONS[struct.name])) self.struct_chain_conversions.append(FreeStructChainFunction(struct)) def _generate_copyright(self, f, spec_file=False):
@@ -2292,6 +2314,9 @@ class VkGenerator(object): if not vk_func.is_required(): continue
if vk_func.needs_win_exclusion():
continue
if vk_func.is_global_func(): continue
@@ -2382,6 +2407,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue
if vk_func.needs_win_exclusion():
continue if vk_func.loader_thunk_type == ThunkType.NONE: continue
@@ -2403,6 +2430,8 @@ class VkGenerator(object): continue if vk_func.needs_thunk() and not vk_func.needs_private_thunk(): continue
if vk_func.needs_win_exclusion():
continue if vk_func.is_core_func(): f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix=prefix)))
@@ -2510,6 +2539,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue
if vk_func.needs_win_exclusion():
continue if vk_func.loader_thunk_type != ThunkType.PUBLIC: continue
@@ -2519,6 +2550,8 @@ class VkGenerator(object): for vk_func in self.registry.device_funcs: if not vk_func.is_required(): continue
if vk_func.needs_win_exclusion():
continue f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name)) f.write("};\n\n")
@@ -2527,6 +2560,8 @@ class VkGenerator(object): for vk_func in self.registry.phys_dev_funcs: if not vk_func.is_required(): continue
if vk_func.needs_win_exclusion():
continue f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name)) f.write("};\n\n")
@@ -2535,6 +2570,8 @@ class VkGenerator(object): for vk_func in self.registry.instance_funcs: if not vk_func.is_required(): continue
if vk_func.needs_win_exclusion():
continue f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name)) f.write("};\n\n")
@@ -2592,6 +2629,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue
if vk_func.needs_win_exclusion():
continue if vk_func.loader_thunk_type == ThunkType.NONE: continue
diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 45eda78e997..e95860cb3fa 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -25,6 +25,7 @@ #include <time.h> #include <stdarg.h> #include <stdlib.h> +#include <unistd.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -34,6 +35,8 @@ #include "winuser.h" #include "winternl.h"
+#include "wine/server.h"
#include "vulkan_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(vulkan);
@@ -265,6 +268,14 @@ static struct VkPhysicalDevice_T *wine_vk_physical_device_alloc(struct VkInstanc { if (wine_vk_device_extension_supported(host_properties[i].extensionName)) {
if (!strcmp(host_properties[i].extensionName, "VK_KHR_external_memory_fd"))
{
TRACE("Substituting VK_KHR_external_memory_fd for VK_KHR_external_memory_win32\n");
snprintf(host_properties[i].extensionName, sizeof(host_properties[i].extensionName),
VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
host_properties[i].specVersion = VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION;
} TRACE("Enabling extension '%s' for physical device %p\n", host_properties[i].extensionName, object); num_properties++; }
@@ -368,12 +379,16 @@ static void wine_vk_device_free_create_info(VkDeviceCreateInfo *create_info) }
free_VkDeviceCreateInfo_struct_chain(create_info);
if (create_info->enabledExtensionCount)
free((void *)create_info->ppEnabledExtensionNames);
}
static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src, VkDeviceCreateInfo *dst) { VkDeviceGroupDeviceCreateInfo *group_info;
const char **enabled_extensions; unsigned int i; VkResult res;
@@ -406,6 +421,39 @@ static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src dst->enabledLayerCount = 0; dst->ppEnabledLayerNames = NULL;
- if (src->enabledExtensionCount > 0)
- {
enabled_extensions = calloc(src->enabledExtensionCount, sizeof(*src->ppEnabledExtensionNames));
if (!enabled_extensions)
{
if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO)))
free((void *)group_info->pPhysicalDevices);
free_VkDeviceCreateInfo_struct_chain(dst);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
for (i = 0; i < src->enabledExtensionCount; i++)
{
if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_fd"))
{
if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO)))
free((void *)group_info->pPhysicalDevices);
free_VkDeviceCreateInfo_struct_chain(dst);
free(enabled_extensions);
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
Why do you check this? It shouldn't be exposed to the app in any way.
else if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_win32"))
{
enabled_extensions[i] = "VK_KHR_external_memory_fd";
}
else
{
enabled_extensions[i] = src->ppEnabledExtensionNames[i];
}
}
dst->ppEnabledExtensionNames = enabled_extensions;
- }
TRACE("Enabled %u extensions.\n", dst->enabledExtensionCount); for (i = 0; i < dst->enabledExtensionCount; i++) {
@@ -1300,57 +1348,106 @@ void WINAPI wine_vkGetPhysicalDeviceExternalFencePropertiesKHR(VkPhysicalDevice void WINAPI wine_vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice phys_dev, const VkPhysicalDeviceExternalBufferInfo *buffer_info, VkExternalBufferProperties *properties) {
- VkPhysicalDeviceExternalBufferInfo buffer_info_dup = *buffer_info;
TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties);
- memset(&properties->externalMemoryProperties, 0, sizeof(properties->externalMemoryProperties));
if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
thunk_vkGetPhysicalDeviceExternalBufferProperties(phys_dev, &buffer_info_dup, properties);
if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
properties->externalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
properties->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
}
void WINAPI wine_vkGetPhysicalDeviceExternalBufferPropertiesKHR(VkPhysicalDevice phys_dev, const VkPhysicalDeviceExternalBufferInfo *buffer_info, VkExternalBufferProperties *properties) {
VkPhysicalDeviceExternalBufferInfo buffer_info_dup = *buffer_info;
TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties);
- memset(&properties->externalMemoryProperties, 0, sizeof(properties->externalMemoryProperties));
- if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
- thunk_vkGetPhysicalDeviceExternalBufferPropertiesKHR(phys_dev, &buffer_info_dup, properties);
- if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
properties->externalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
- if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
}properties->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
-VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice phys_dev, +static VkResult wine_vk_get_physical_device_image_format_properties_2(VkPhysicalDevice phys_dev,
VkResult (*p_vkGetPhysicalDeviceImageFormatProperties2)(VkPhysicalDevice, const VkPhysicalDeviceImageFormatInfo2 *, VkImageFormatProperties2 *),
vkGetPhysicalDeviceImageFormatProperties2KHR and vkGetPhysicalDeviceImageFormatProperties2 are equivalent, you should just always use thunk_vkGetPhysicalDeviceImageFormatProperties2.
const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties)
{
- VkPhysicalDeviceExternalImageFormatInfo *external_image_info_host = NULL;
- const VkPhysicalDeviceExternalImageFormatInfo *external_image_info;
- VkPhysicalDeviceImageFormatInfo2 format_info_host = *format_info; VkExternalImageFormatProperties *external_image_properties; VkResult res;
- TRACE("%p, %p, %p\n", phys_dev, format_info, properties);
- if ((external_image_info = wine_vk_find_struct(format_info, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO)))
- {
if (external_image_info->handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
{
if ((res = convert_VkPhysicalDeviceImageFormatInfo2_struct_chain(format_info->pNext, &format_info_host)) < 0)
{
WARN("Failed to convert VkPhysicalDeviceImageFormatInfo2 pNext chain, res=%d.\n", res);
return res;
}
external_image_info_host = wine_vk_find_struct(&format_info_host, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO);
external_image_info_host->handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
}
if (external_image_info->handleType &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
{
WARN("Unsupported handle type %#x.\n", external_image_info->handleType);
return VK_ERROR_FORMAT_NOT_SUPPORTED;
}
- }
- res = p_vkGetPhysicalDeviceImageFormatProperties2(phys_dev, &format_info_host, properties);
- res = thunk_vkGetPhysicalDeviceImageFormatProperties2(phys_dev, format_info, properties);
if (external_image_info_host)
free_VkPhysicalDeviceImageFormatInfo2_struct_chain(&format_info_host); if ((external_image_properties = wine_vk_find_struct(properties, EXTERNAL_IMAGE_FORMAT_PROPERTIES))) { VkExternalMemoryProperties *p = &external_image_properties->externalMemoryProperties;
p->externalMemoryFeatures = 0;
p->exportFromImportedHandleTypes = 0;
p->compatibleHandleTypes = 0;
if (p->exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
{
p->exportFromImportedHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
p->exportFromImportedHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
}
if (p->compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
{
p->compatibleHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
p->compatibleHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
} > } return res;
}
-VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice phys_dev, +VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice phys_dev, const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) {
VkExternalImageFormatProperties *external_image_properties;
VkResult res;
TRACE("%p, %p, %p\n", phys_dev, format_info, properties);
res = thunk_vkGetPhysicalDeviceImageFormatProperties2KHR(phys_dev, format_info, properties);
- return wine_vk_get_physical_device_image_format_properties_2(phys_dev, thunk_vkGetPhysicalDeviceImageFormatProperties2, format_info, properties);
+}
- if ((external_image_properties = wine_vk_find_struct(properties, EXTERNAL_IMAGE_FORMAT_PROPERTIES)))
- {
VkExternalMemoryProperties *p = &external_image_properties->externalMemoryProperties;
p->externalMemoryFeatures = 0;
p->exportFromImportedHandleTypes = 0;
p->compatibleHandleTypes = 0;
- }
+VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice phys_dev,
const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties)
+{
- TRACE("%p, %p, %p\n", phys_dev, format_info, properties);
- return res;
return wine_vk_get_physical_device_image_format_properties_2(phys_dev, thunk_vkGetPhysicalDeviceImageFormatProperties2KHR, format_info, properties); }
/* From ntdll/unix/sync.c */
@@ -1860,3 +1957,249 @@ VkResult WINAPI wine_vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebu
return thunk_vkDebugMarkerSetObjectNameEXT(device, &wine_name_info);
}
+static HANDLE create_video_resource(int fd, LPCWSTR name) +{
- HANDLE ret = INVALID_HANDLE_VALUE;
- if (name)
FIXME("Naming video resources not supported.\n");
- wine_server_fd_to_handle(fd, GENERIC_ALL, 0, &ret);
- return ret;
+}
+VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *allocate_info,
- const VkAllocationCallbacks *allocator, VkDeviceMemory *memory_out)
+{
- const VkImportMemoryWin32HandleInfoKHR *handle_import_info = NULL;
- const VkExportMemoryWin32HandleInfoKHR *handle_export_info = NULL;
- VkMemoryAllocateInfo allocate_info_dup = *allocate_info;
- VkExportMemoryAllocateInfo *export_info = NULL;
- VkImportMemoryFdInfoKHR fd_import_info;
- const VkBaseOutStructure *header;
- struct wine_dev_mem *object;
- VkResult res;
- int fd;
+#if defined(USE_STRUCT_CONVERSION)
VkMemoryAllocateInfo_host allocate_info_host;
VkMemoryGetFdInfoKHR_host get_fd_info;
+#else
VkMemoryAllocateInfo allocate_info_host;
VkMemoryGetFdInfoKHR get_fd_info;
+#endif
- TRACE("%p %p %p %p\n", device, allocate_info, allocator, memory_out);
- if (allocator)
FIXME("Support for allocation callbacks not implemented yet\n");
- if ((res = convert_VkMemoryAllocateInfo_struct_chain(allocate_info->pNext, &allocate_info_dup)) < 0)
- {
WARN("Failed to convert VkMemoryAllocateInfo pNext chain, res=%d.\n", res);
return res;
- }
- if (!(object = calloc(1, sizeof(*object))))
- {
free_VkMemoryAllocateInfo_struct_chain(&allocate_info_dup);
return VK_ERROR_OUT_OF_HOST_MEMORY;
- }
- object->dev_mem = VK_NULL_HANDLE;
- object->handle = INVALID_HANDLE_VALUE;
- fd_import_info.fd = -1;
- fd_import_info.pNext = NULL;
- /* find and process handle import/export info and grab it */
- for (header = allocate_info->pNext; header; header = header->pNext)
- {
switch (header->sType)
{
case VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR:
handle_import_info = (const VkImportMemoryWin32HandleInfoKHR *)header;
break;
case VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR:
handle_export_info = (const VkExportMemoryWin32HandleInfoKHR *)header;
if (handle_export_info->pAttributes && handle_export_info->pAttributes->lpSecurityDescriptor)
FIXME("Support for custom security descriptor not implemented.\n");
break;
default:
break;
}
- }
IMO you should just wine_vk_find_struct twice here.
- for (header = allocate_info_dup.pNext; header; header = header->pNext)
- {
if (header->sType == VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO)
{
export_info = (VkExportMemoryAllocateInfo *)header;
object->handle_types = export_info->handleTypes;
if (object->handle_types &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
{
res = VK_ERROR_OUT_OF_HOST_MEMORY;
Why VK_ERROR_OUT_OF_HOST_MEMORY?
goto done;
}
if (object->handle_types)
export_info->handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
break;
}
- }
- /* Important to note is that Vulkan does consume imported FDs, but it doesn't consume imported HANDLEs */
- if (handle_import_info)
- {
fd_import_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
fd_import_info.pNext = allocate_info_dup.pNext;
fd_import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
switch (handle_import_info->handleType)
{
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT:
if (handle_import_info->handle)
NtDuplicateObject( NtCurrentProcess(), handle_import_info->handle, NtCurrentProcess(), &object->handle, 0, 0, DUPLICATE_SAME_ACCESS );
else if (handle_import_info->name)
FIXME("Importing device memory by resource name not supported.\n");
break;
default:
WARN("Invalid handle type %08x passed in.\n", handle_import_info->handleType);
res = VK_ERROR_INVALID_EXTERNAL_HANDLE;
goto done;
}
if (object->handle != INVALID_HANDLE_VALUE)
wine_server_handle_to_fd(object->handle, FILE_READ_DATA, &fd_import_info.fd, NULL);
if (fd_import_info.fd == -1)
{
TRACE("Couldn't access resource handle or name. type=%08x handle=%p name=%s\n", handle_import_info->handleType, handle_import_info->handle,
handle_import_info->name ? debugstr_w(handle_import_info->name) : "");
res = VK_ERROR_INVALID_EXTERNAL_HANDLE;
goto done;
}
- }
- allocate_info_host.sType = allocate_info_dup.sType;
- allocate_info_host.pNext = fd_import_info.fd == -1 ? allocate_info_dup.pNext : &fd_import_info;
- allocate_info_host.allocationSize = allocate_info_dup.allocationSize;
- allocate_info_host.memoryTypeIndex = allocate_info_dup.memoryTypeIndex;
- if ((res = device->funcs.p_vkAllocateMemory(device->device, &allocate_info_host, NULL, &object->dev_mem)) == VK_SUCCESS)
- {
if (object->handle == INVALID_HANDLE_VALUE && export_info && export_info->handleTypes)
{
get_fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
get_fd_info.pNext = NULL;
get_fd_info.memory = object->dev_mem;
get_fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
if (device->funcs.p_vkGetMemoryFdKHR(device->device, &get_fd_info, &fd) == VK_SUCCESS)
{
object->handle = create_video_resource(fd, handle_export_info ? handle_export_info->name : NULL);
object->access = handle_export_info ? handle_export_info->dwAccess : GENERIC_ALL;
if (handle_export_info && handle_export_info->pAttributes)
object->inherit = handle_export_info->pAttributes->bInheritHandle;
else
object->inherit = FALSE;
close(fd);
}
if (object->handle == INVALID_HANDLE_VALUE)
{
res = VK_ERROR_OUT_OF_HOST_MEMORY;
goto done;
}
}
*memory_out = wine_dev_mem_to_handle(object);
You should call WINE_VK_ADD_NON_DISPATCHABLE_MAPPING here, and WINE_VK_REMOVE_HANDLE_MAPPING in vkFreeMemory.
- }
- done:
- if (res != VK_SUCCESS)
- {
if (object->dev_mem != VK_NULL_HANDLE)
device->funcs.p_vkFreeMemory(device->device, object->dev_mem, NULL);
No need to check for VK_NULL_HANDLE here, vkFreeMemory already does that.
if (fd_import_info.fd != -1)
close(fd_import_info.fd);
if (object->handle != INVALID_HANDLE_VALUE)
NtClose(object->handle);
free(object);
- }
- free_VkMemoryAllocateInfo_struct_chain(&allocate_info_dup);
- return res;
+}
+VkResult WINAPI wine_vkGetMemoryWin32HandleKHR(VkDevice device,
- const VkMemoryGetWin32HandleInfoKHR *handle_info, HANDLE *handle)
+{
- struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle_info->memory);
- const VkBaseInStructure *chain;
- TRACE("%p, %p %p\n", device, handle_info, handle);
- if (!(dev_mem->handle_types & handle_info->handleType))
return VK_ERROR_OUT_OF_HOST_MEMORY;
- if ((chain = handle_info->pNext))
FIXME("Ignoring a linked structure of type %u.\n", chain->sType);
- switch(handle_info->handleType)
- {
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT:
return !NtDuplicateObject( NtCurrentProcess(), dev_mem->handle, NtCurrentProcess(), handle, dev_mem->access, dev_mem->inherit ? OBJ_INHERIT : 0, 0) ?
VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY;
default:
return VK_ERROR_UNKNOWN;
- }
+}
+void WINAPI wine_vkFreeMemory(VkDevice device, VkDeviceMemory handle, const VkAllocationCallbacks *allocator) +{
- struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle);
- TRACE("%p 0x%s, %p\n", device, wine_dbgstr_longlong(handle), allocator);
- if (allocator)
FIXME("Support for allocation callbacks not implemented yet\n");
- device->funcs.p_vkFreeMemory(device->device, dev_mem->dev_mem, NULL);
- if (dev_mem->handle != INVALID_HANDLE_VALUE)
NtClose(dev_mem->handle);
- free(dev_mem);
+}
+VkResult WINAPI wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device,
VkExternalMemoryHandleTypeFlagBits type, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *properties)
+{
- VkMemoryFdPropertiesKHR fd_props;
- VkResult res;
- int fd = -1;
- TRACE("%p %u %p %p\n", device, type, handle, properties);
- if (type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
VUID-vkGetMemoryWin32HandlePropertiesKHR-handleType-00666 handleType must not be one of the handle types defined as opaque
- {
wine_server_handle_to_fd(handle, FILE_READ_DATA, &fd, NULL);
- }
- if (fd == -1)
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
- res = device->funcs.p_vkGetMemoryFdPropertiesKHR(device->device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, fd, &fd_props);
VUID-vkGetMemoryFdPropertiesKHR-handleType-00674 handleType must not be VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR
And you have to init the sType and pNext of fd_props.
- close(fd);
- if (res != VK_SUCCESS)
return res;
- properties->sType = VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR;
- properties->pNext = NULL;
You shouldn't touch those two, the application will set them.
- properties->memoryTypeBits = fd_props.memoryTypeBits;
- return res;
+} diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 83dc90ca15e..468ab890dab 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -204,6 +204,28 @@ static inline VkSurfaceKHR wine_surface_to_handle(struct wine_surface *surface) return (VkSurfaceKHR)(uintptr_t)surface; }
+struct wine_dev_mem +{
- VkDeviceMemory dev_mem;
- VkExternalMemoryHandleTypeFlagBits handle_types;
- BOOL inherit;
- DWORD access;
- HANDLE handle;
+};
Wrapping VkDeviceMemory is problematic, because generated thunks won't unwrap handles in struct parameters. With this code functions using these structs will be broken:
VkBindAccelerationStructureMemoryInfoNV, VkBindBufferMemoryInfo, VkBindImageMemoryInfo, VkDeviceMemoryOpaqueCaptureAddressInfo, VkMappedMemoryRange, VkSparseImageMemoryBind, VkSparseMemoryBind
So you either have to either: 1. Manually implement all those functions. 2. Handle it in make_vulkan. 3. Find another way to store data per VkDeviceMemory object, e.g with VK_EXT_private_data.
I'm against the first option because it's a lot of manually written functions and future spec updates might add new problematic functions. The provisional video extensions add VkVideoBindMemoryKHR for example.
The best option would be the second, if you can make it work, because we currently have some code in vulkan.c that should be generated but has the same problem.
Thanks,
Georg Lehmann
+static inline struct wine_dev_mem *wine_dev_mem_from_handle(VkDeviceMemory handle) +{
- return (struct wine_dev_mem *)(uintptr_t)handle;
+}
+static inline VkDeviceMemory wine_dev_mem_to_handle(struct wine_dev_mem *dev_mem) +{
- return (VkDeviceMemory)(uintptr_t)dev_mem;
+}
- BOOL wine_vk_device_extension_supported(const char *name) DECLSPEC_HIDDEN; BOOL wine_vk_instance_extension_supported(const char *name) DECLSPEC_HIDDEN;
On 03.05.21 23:44, Georg Lehmann wrote:
On 03.05.21 18:46, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
This commit had to be squashed in with the tests, as data structures for the extension are missing from the headers when the extension is blacklisted by make_vulkan.
dlls/vulkan-1/tests/vulkan.c | 242 +++++++++++++++++-- dlls/winevulkan/loader_thunks.c | 12 + dlls/winevulkan/loader_thunks.h | 2 + dlls/winevulkan/make_vulkan | 63 ++++- dlls/winevulkan/vulkan.c | 385 +++++++++++++++++++++++++++++-- dlls/winevulkan/vulkan_private.h | 22 ++ 6 files changed, 679 insertions(+), 47 deletions(-)
Hi,
I would prefer if you could split this up in to multiple patches, e.g. the host only functions and struct conversion changes in make_vulkan could each be in their own commits.
diff --git a/dlls/vulkan-1/tests/vulkan.c b/dlls/vulkan-1/tests/vulkan.c index 9061b2b6db8..c1350da70ee 100644 --- a/dlls/vulkan-1/tests/vulkan.c +++ b/dlls/vulkan-1/tests/vulkan.c @@ -144,7 +144,7 @@ static void test_instance_version(void) VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)); } -static void enumerate_physical_device(VkPhysicalDevice vk_physical_device) +static void enumerate_physical_device(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkPhysicalDeviceProperties properties; @@ -224,7 +224,7 @@ static void test_enumerate_physical_device2(void) vkDestroyInstance(vk_instance, NULL); } -static void enumerate_device_queues(VkPhysicalDevice vk_physical_device) +static void enumerate_device_queues(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkPhysicalDeviceProperties device_properties; VkQueueFamilyProperties *properties; @@ -283,7 +283,7 @@ static void test_physical_device_groups(void) trace("Group[%u] count %u, subset allocation %#x\n", i, properties[i].physicalDeviceCount, properties[i].subsetAllocation); for (j = 0; j < properties[i].physicalDeviceCount; ++j) - enumerate_physical_device(properties[i].physicalDevices[j]); + enumerate_physical_device(vk_instance, properties[i].physicalDevices[j]); } if ((vr = create_device(properties->physicalDevices[0], 0, NULL, NULL, &vk_device)) < 0) @@ -307,7 +307,7 @@ static void test_physical_device_groups(void) vkDestroyInstance(vk_instance, NULL); } -static void test_destroy_command_pool(VkPhysicalDevice vk_physical_device) +static void test_destroy_command_pool(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkCommandBufferAllocateInfo allocate_info; VkCommandPoolCreateInfo pool_info; @@ -366,7 +366,7 @@ static void test_unsupported_instance_extensions(void) } } -static void test_unsupported_device_extensions(VkPhysicalDevice vk_physical_device) +static void test_unsupported_device_extensions(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkDevice vk_device; unsigned int i; @@ -387,7 +387,7 @@ static void test_unsupported_device_extensions(VkPhysicalDevice vk_physical_devi } } -static void test_private_data(VkPhysicalDevice vk_physical_device) +static void test_private_data(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { PFN_vkDestroyPrivateDataSlotEXT pfn_vkDestroyPrivateDataSlotEXT; PFN_vkCreatePrivateDataSlotEXT pfn_vkCreatePrivateDataSlotEXT; @@ -437,7 +437,218 @@ static void test_private_data(VkPhysicalDevice vk_physical_device) vkDestroyDevice(vk_device, NULL); } -static void for_each_device(void (*test_func)(VkPhysicalDevice)) +uint32_t find_memory_type(VkPhysicalDevice vk_physical_device, VkMemoryPropertyFlagBits flags) +{ + VkPhysicalDeviceMemoryProperties properties = {0}; + unsigned int i;
+ vkGetPhysicalDeviceMemoryProperties(vk_physical_device, &properties);
+ for(i = 0; i < properties.memoryTypeCount; i++) + { + if (properties.memoryTypes[i].propertyFlags & flags) + return i; + } + return -1; +}
+static void test_external_memory(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) +{ + PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR; + PFN_vkGetMemoryWin32HandleKHR pfn_vkGetMemoryWin32HandleKHR; + VkPhysicalDeviceExternalBufferInfoKHR external_buffer_info; + VkExternalBufferPropertiesKHR external_buffer_properties; + VkMemoryDedicatedAllocateInfoKHR dedicated_alloc_info; + VkExportMemoryWin32HandleInfoKHR export_handle_info; + VkImportMemoryWin32HandleInfoKHR import_handle_info; + VkExportMemoryAllocateInfoKHR export_memory_info; + VkMemoryGetWin32HandleInfoKHR get_handle_info; + VkBufferCreateInfo buffer_create_info; + VkMemoryAllocateInfo alloc_info; + uint32_t queue_family_index; + HANDLE nt_handle, kmt_handle; + VkDeviceMemory vk_memory, vk_memory_import; + VkBuffer vk_buffer; + VkDevice vk_device; + VkResult vr;
+ static const char *extensions[] = + { + "VK_KHR_dedicated_allocation", + "VK_KHR_external_memory", + "VK_KHR_external_memory_win32", + };
+ pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR = + (void*) vkGetInstanceProcAddr(vk_instance, "vkGetPhysicalDeviceExternalBufferPropertiesKHR");
+ if ((vr = create_device(vk_physical_device, ARRAY_SIZE(extensions), extensions, NULL, &vk_device))) + { + skip("Failed to create device with external memory extensions, VkResult %d.\n", vr); + return; + }
+ pfn_vkGetMemoryWin32HandleKHR = (void *) vkGetDeviceProcAddr(vk_device, "vkGetMemoryWin32HandleKHR");
+ find_queue_family(vk_physical_device, VK_QUEUE_GRAPHICS_BIT, &queue_family_index);
+ /* Most implementations only support exporting dedicated allocations */
+ buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.pNext = NULL; + buffer_create_info.flags = 0; + buffer_create_info.size = 1; + buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + buffer_create_info.sharingMode = VK_SHARING_MODE_CONCURRENT; + buffer_create_info.queueFamilyIndexCount = 1; + buffer_create_info.pQueueFamilyIndices = &queue_family_index; + if ((vr = vkCreateBuffer(vk_device, &buffer_create_info, NULL, &vk_buffer))) + { + skip("Failed to create generic buffer, VkResult %d.\n", vr); + vkDestroyDevice(vk_device, NULL); + return; + }
+ dedicated_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + dedicated_alloc_info.pNext = NULL; + dedicated_alloc_info.image = VK_NULL_HANDLE; + dedicated_alloc_info.buffer = vk_buffer;
+ external_buffer_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR; + external_buffer_info.pNext = NULL; + external_buffer_info.flags = 0; + external_buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + external_buffer_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
+ memset(&external_buffer_properties, 0, sizeof(external_buffer_properties)); + external_buffer_properties.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR;
pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR(vk_physical_device, &external_buffer_info, &external_buffer_properties);
+ if (!(external_buffer_properties.externalMemoryProperties.externalMemoryFeatures &
(VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR|VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR)))
+ skip("With desired parameters, buffers are not exportable to and importable from an NT handle.\n"); + else + {
ok(external_buffer_properties.externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR, + "Unexpected compatibleHandleTypes %#x.\n", external_buffer_properties.externalMemoryProperties.compatibleHandleTypes);
+ export_memory_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + export_memory_info.pNext = &dedicated_alloc_info; + export_memory_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
+ export_handle_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + export_handle_info.pNext = &export_memory_info; + export_handle_info.name = L"wine_test_buffer_export_name"; + export_handle_info.dwAccess = GENERIC_ALL; + export_handle_info.pAttributes = NULL;
+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = &export_handle_info; + alloc_info.allocationSize = 1; + alloc_info.memoryTypeIndex = find_memory_type(vk_physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ ok(alloc_info.memoryTypeIndex != -1, "Device local memory type index was not found.\n");
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
+ get_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR; + get_handle_info.pNext = NULL; + get_handle_info.memory = vk_memory; + get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
+ vr = pfn_vkGetMemoryWin32HandleKHR(vk_device, &get_handle_info, &nt_handle); + ok(vr == VK_SUCCESS, "vkGetMemoryWin32HandleKHR failed, VkResult %d.\n", vr);
+ import_handle_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + import_handle_info.pNext = &dedicated_alloc_info; + import_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR; + import_handle_info.handle = nt_handle; + import_handle_info.name = NULL;
+ alloc_info.pNext = &import_handle_info;
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
+ vkFreeMemory(vk_device, vk_memory_import, NULL);
+ import_handle_info.handle = NULL; + import_handle_info.name = L"wine_test_buffer_export_name";
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + todo_wine + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + if (vr == VK_SUCCESS) + { + ok(vk_memory_import != vk_memory, "Expected new memory object.\n"); + vkFreeMemory(vk_device, vk_memory_import, NULL); + } + vkFreeMemory(vk_device, vk_memory, NULL); + CloseHandle(nt_handle); + }
+ external_buffer_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
+ memset(&external_buffer_properties, 0, sizeof(external_buffer_properties)); + external_buffer_properties.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR;
pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR(vk_physical_device, &external_buffer_info, &external_buffer_properties);
+ if (!(external_buffer_properties.externalMemoryProperties.externalMemoryFeatures &
(VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR|VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR)))
+ skip("With desired parameters, buffers are not exportable to and importable from a KMT handle.\n"); + else + {
ok(external_buffer_properties.externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR, + "Unexpected compatibleHandleTypes %#x.\n", external_buffer_properties.externalMemoryProperties.compatibleHandleTypes);
+ export_memory_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + export_memory_info.pNext = &dedicated_alloc_info; + export_memory_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = &export_memory_info; + alloc_info.allocationSize = 1; + alloc_info.memoryTypeIndex = find_memory_type(vk_physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ ok(alloc_info.memoryTypeIndex != -1, "Device local memory type index was not found.\n");
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
+ get_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR; + get_handle_info.pNext = NULL; + get_handle_info.memory = vk_memory; + get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
+ vr = pfn_vkGetMemoryWin32HandleKHR(vk_device, &get_handle_info, &kmt_handle); + ok(vr == VK_SUCCESS, "vkGetMemoryWin32HandleKHR failed, VkResult %d.\n", vr);
+ import_handle_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + import_handle_info.pNext = &dedicated_alloc_info; + import_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR; + import_handle_info.handle = kmt_handle; + import_handle_info.name = NULL;
+ alloc_info.pNext = &import_handle_info;
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
+ vkFreeMemory(vk_device, vk_memory_import, NULL); + vkFreeMemory(vk_device, vk_memory, NULL); + }
+ vkDestroyBuffer(vk_device, vk_buffer, NULL); + vkDestroyDevice(vk_device, NULL); +}
+static void for_each_device(void (*test_func)(VkInstance,VkPhysicalDevice), uint32_t extension_count, + const char * const *enabled_extensions) { VkPhysicalDevice *vk_physical_devices; VkInstance vk_instance; @@ -445,7 +656,7 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) uint32_t count; VkResult vr; - if ((vr = create_instance_skip(0, NULL, &vk_instance)) < 0) + if ((vr = create_instance_skip(extension_count, enabled_extensions, &vk_instance)) < 0) return; ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr); @@ -463,7 +674,7 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr); for (i = 0; i < count; ++i) - test_func(vk_physical_devices[i]); + test_func(vk_instance, vk_physical_devices[i]); heap_free(vk_physical_devices); @@ -472,13 +683,16 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) START_TEST(vulkan) { + static const char *external_memory_capabilities = "VK_KHR_external_memory_capabilities";
test_instance_version(); - for_each_device(enumerate_physical_device); + for_each_device(enumerate_physical_device, 0, NULL); test_enumerate_physical_device2(); - for_each_device(enumerate_device_queues); + for_each_device(enumerate_device_queues, 0, NULL); test_physical_device_groups(); - for_each_device(test_destroy_command_pool); + for_each_device(test_destroy_command_pool, 0, NULL); test_unsupported_instance_extensions(); - for_each_device(test_unsupported_device_extensions); - for_each_device(test_private_data); + for_each_device(test_unsupported_device_extensions, 0, NULL); + for_each_device(test_private_data, 0, NULL); + for_each_device(test_external_memory, 1, &external_memory_capabilities); } diff --git a/dlls/winevulkan/loader_thunks.c b/dlls/winevulkan/loader_thunks.c index 69fcd44160e..471999a3821 100644 --- a/dlls/winevulkan/loader_thunks.c +++ b/dlls/winevulkan/loader_thunks.c @@ -1448,6 +1448,16 @@ VkResult WINAPI vkGetMemoryHostPointerPropertiesEXT(VkDevice device, VkExternalM return unix_funcs->p_vkGetMemoryHostPointerPropertiesEXT(device, handleType, pHostPointer, pMemoryHostPointerProperties); } +VkResult WINAPI vkGetMemoryWin32HandleKHR(VkDevice device, const VkMemoryGetWin32HandleInfoKHR *pGetWin32HandleInfo, HANDLE *pHandle) +{ + return unix_funcs->p_vkGetMemoryWin32HandleKHR(device, pGetWin32HandleInfo, pHandle); +}
+VkResult WINAPI vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *pMemoryWin32HandleProperties) +{ + return unix_funcs->p_vkGetMemoryWin32HandlePropertiesKHR(device, handleType, handle, pMemoryWin32HandleProperties); +}
VkResult WINAPI vkGetPerformanceParameterINTEL(VkDevice device, VkPerformanceParameterTypeINTEL parameter, VkPerformanceValueINTEL *pValue) { return unix_funcs->p_vkGetPerformanceParameterINTEL(device, parameter, pValue); @@ -2223,6 +2233,8 @@ static const struct vulkan_func vk_device_dispatch_table[] = {"vkGetImageSparseMemoryRequirements2KHR", &vkGetImageSparseMemoryRequirements2KHR}, {"vkGetImageSubresourceLayout", &vkGetImageSubresourceLayout}, {"vkGetMemoryHostPointerPropertiesEXT", &vkGetMemoryHostPointerPropertiesEXT}, + {"vkGetMemoryWin32HandleKHR", &vkGetMemoryWin32HandleKHR}, + {"vkGetMemoryWin32HandlePropertiesKHR", &vkGetMemoryWin32HandlePropertiesKHR}, {"vkGetPerformanceParameterINTEL", &vkGetPerformanceParameterINTEL}, {"vkGetPipelineCacheData", &vkGetPipelineCacheData}, {"vkGetPipelineExecutableInternalRepresentationsKHR", &vkGetPipelineExecutableInternalRepresentationsKHR}, diff --git a/dlls/winevulkan/loader_thunks.h b/dlls/winevulkan/loader_thunks.h index e7257cef508..031b7f5652b 100644 --- a/dlls/winevulkan/loader_thunks.h +++ b/dlls/winevulkan/loader_thunks.h @@ -304,6 +304,8 @@ struct unix_funcs void (WINAPI *p_vkGetImageSparseMemoryRequirements2KHR)(VkDevice, const VkImageSparseMemoryRequirementsInfo2 *, uint32_t *, VkSparseImageMemoryRequirements2 *); void (WINAPI *p_vkGetImageSubresourceLayout)(VkDevice, VkImage, const VkImageSubresource *, VkSubresourceLayout *); VkResult (WINAPI *p_vkGetMemoryHostPointerPropertiesEXT)(VkDevice, VkExternalMemoryHandleTypeFlagBits, const void *, VkMemoryHostPointerPropertiesEXT *); + VkResult (WINAPI *p_vkGetMemoryWin32HandleKHR)(VkDevice, const VkMemoryGetWin32HandleInfoKHR *, HANDLE *); + VkResult (WINAPI *p_vkGetMemoryWin32HandlePropertiesKHR)(VkDevice, VkExternalMemoryHandleTypeFlagBits, HANDLE, VkMemoryWin32HandlePropertiesKHR *); VkResult (WINAPI *p_vkGetPerformanceParameterINTEL)(VkDevice, VkPerformanceParameterTypeINTEL, VkPerformanceValueINTEL *); VkResult (WINAPI *p_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT)(VkPhysicalDevice, uint32_t *, VkTimeDomainEXT *); VkResult (WINAPI *p_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV)(VkPhysicalDevice, uint32_t *, VkCooperativeMatrixPropertiesNV *); diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index b1877acda17..0a0e3bac299 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -99,7 +99,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_pipeline_creation_feedback", "VK_GOOGLE_display_timing", "VK_KHR_external_fence_win32", - "VK_KHR_external_memory_win32", "VK_KHR_external_semaphore_win32", # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work. @@ -109,7 +108,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_external_memory_dma_buf", "VK_EXT_image_drm_format_modifier", "VK_KHR_external_fence_fd", - "VK_KHR_external_memory_fd", "VK_KHR_external_semaphore_fd", # Extensions which require callback handling @@ -154,6 +152,8 @@ class ThunkType(Enum): # - PRIVATE thunks can be used in custom implementations for # struct conversion. # - loader_thunk sets whether to create a thunk for unix_funcs. +# - host_only sets whether to preclude the function from the +# win32-facing dispatch table. FUNCTION_OVERRIDES = { # Global functions "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, @@ -169,7 +169,7 @@ FUNCTION_OVERRIDES = { "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE}, "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, - "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, @@ -178,11 +178,13 @@ FUNCTION_OVERRIDES = { # Device functions "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkAllocateMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkFreeMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE}, "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, @@ -213,7 +215,7 @@ FUNCTION_OVERRIDES = { "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, # VK_KHR_external_memory_capabilities - "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, # VK_KHR_external_semaphore_capabilities @@ -249,12 +251,22 @@ FUNCTION_OVERRIDES = { # VK_EXT_debug_marker "vkDebugMarkerSetObjectNameEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkDebugMarkerSetObjectTagEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
+ # VK_KHR_external_memory_win32 + "vkGetMemoryWin32HandleKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetMemoryWin32HandlePropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
+ # VK_KHR_external_memory_fd + "vkGetMemoryFdKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "host_only" : True}, + "vkGetMemoryFdPropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "host_only" : True},
Would it make sense to added a new value to ThunkType instead, e.g. ThunkType.NOT_EXPOSED?
} -STRUCT_CHAIN_CONVERSIONS = [ - "VkDeviceCreateInfo", - "VkInstanceCreateInfo", -] +STRUCT_CHAIN_CONVERSIONS = { + "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"], + "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"], + "VkPhysicalDeviceImageFormatInfo2": [], + "VkMemoryAllocateInfo": ["VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR", "VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR"], +} class Direction(Enum): @@ -532,6 +544,7 @@ class VkFunction(object): self.driver = func_info["driver"] if func_info else False self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC + self.host_only = func_info["host_only"] if func_info and "host_only" in func_info else False # Required is set while parsing which APIs and types are required # and is used by the code generation. @@ -651,6 +664,9 @@ class VkFunction(object): def needs_private_thunk(self): return self.thunk_type == ThunkType.PRIVATE + def needs_win_exclusion(self): + return self.host_only
def pfn(self, prefix="p", call_conv=None, conv=False): """ Create function pointer. """ @@ -1028,6 +1044,8 @@ class VkHandle(object): return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name)
if self.name == "VkSurfaceKHR": return "wine_surface_from_handle({0})->surface".format(name) + if self.name == "VkDeviceMemory": + return "wine_dev_mem_from_handle({0})->dev_mem".format(name) native_handle_name = None @@ -2115,9 +2133,10 @@ class FreeFunction(object): class StructChainConversionFunction(object): - def __init__(self, direction, struct): + def __init__(self, direction, struct, ignores): self.direction = direction self.struct = struct + self.ignores = ignores self.type = struct.name self.name = "convert_{0}_struct_chain".format(self.type) @@ -2143,8 +2162,8 @@ class StructChainConversionFunction(object): body += " {\n" # Ignore to not confuse host loader.
This comment is now misplaced.
- body += " case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO:\n" - body += " case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO:\n" + for i in self.ignores: + body += " case {0}:\n".format(i) body += " break;\n\n" for e in self.struct.struct_extensions: @@ -2153,6 +2172,9 @@ class StructChainConversionFunction(object): stype = next(x for x in e.members if x.name == "sType") + if stype.values in self.ignores: + continue
body += " case {0}:\n".format(stype.values) body += " {\n" @@ -2250,7 +2272,7 @@ class VkGenerator(object): for struct in self.registry.structs: if struct.name in STRUCT_CHAIN_CONVERSIONS:
self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct))
self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct, STRUCT_CHAIN_CONVERSIONS[struct.name]))
self.struct_chain_conversions.append(FreeStructChainFunction(struct)) def _generate_copyright(self, f, spec_file=False): @@ -2292,6 +2314,9 @@ class VkGenerator(object): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue
if vk_func.is_global_func(): continue @@ -2382,6 +2407,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type == ThunkType.NONE: continue @@ -2403,6 +2430,8 @@ class VkGenerator(object): continue if vk_func.needs_thunk() and not vk_func.needs_private_thunk(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.is_core_func(): f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix=prefix))) @@ -2510,6 +2539,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type != ThunkType.PUBLIC: continue @@ -2519,6 +2550,8 @@ class VkGenerator(object): for vk_func in self.registry.device_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2527,6 +2560,8 @@ class VkGenerator(object): for vk_func in self.registry.phys_dev_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2535,6 +2570,8 @@ class VkGenerator(object): for vk_func in self.registry.instance_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2592,6 +2629,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type == ThunkType.NONE: continue diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 45eda78e997..e95860cb3fa 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -25,6 +25,7 @@ #include <time.h> #include <stdarg.h> #include <stdlib.h> +#include <unistd.h> #include "ntstatus.h" #define WIN32_NO_STATUS @@ -34,6 +35,8 @@ #include "winuser.h" #include "winternl.h" +#include "wine/server.h"
#include "vulkan_private.h" WINE_DEFAULT_DEBUG_CHANNEL(vulkan); @@ -265,6 +268,14 @@ static struct VkPhysicalDevice_T *wine_vk_physical_device_alloc(struct VkInstanc { if (wine_vk_device_extension_supported(host_properties[i].extensionName)) { + if (!strcmp(host_properties[i].extensionName, "VK_KHR_external_memory_fd")) + { + TRACE("Substituting VK_KHR_external_memory_fd for VK_KHR_external_memory_win32\n");
+ snprintf(host_properties[i].extensionName, sizeof(host_properties[i].extensionName), + VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME); + host_properties[i].specVersion = VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION; + } TRACE("Enabling extension '%s' for physical device %p\n", host_properties[i].extensionName, object); num_properties++; } @@ -368,12 +379,16 @@ static void wine_vk_device_free_create_info(VkDeviceCreateInfo *create_info) } free_VkDeviceCreateInfo_struct_chain(create_info);
+ if (create_info->enabledExtensionCount) + free((void *)create_info->ppEnabledExtensionNames); } static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src, VkDeviceCreateInfo *dst) { VkDeviceGroupDeviceCreateInfo *group_info; + const char **enabled_extensions; unsigned int i; VkResult res; @@ -406,6 +421,39 @@ static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src dst->enabledLayerCount = 0; dst->ppEnabledLayerNames = NULL; + if (src->enabledExtensionCount > 0) + { + enabled_extensions = calloc(src->enabledExtensionCount, sizeof(*src->ppEnabledExtensionNames)); + if (!enabled_extensions) + { + if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO))) + free((void *)group_info->pPhysicalDevices); + free_VkDeviceCreateInfo_struct_chain(dst); + return VK_ERROR_OUT_OF_HOST_MEMORY; + }
+ for (i = 0; i < src->enabledExtensionCount; i++) + { + if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_fd")) + { + if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO))) + free((void *)group_info->pPhysicalDevices); + free_VkDeviceCreateInfo_struct_chain(dst); + free(enabled_extensions); + return VK_ERROR_EXTENSION_NOT_PRESENT; + }
Why do you check this? It shouldn't be exposed to the app in any way.
+ else if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_win32")) + { + enabled_extensions[i] = "VK_KHR_external_memory_fd"; + } + else + { + enabled_extensions[i] = src->ppEnabledExtensionNames[i]; + } + } + dst->ppEnabledExtensionNames = enabled_extensions; + }
TRACE("Enabled %u extensions.\n", dst->enabledExtensionCount); for (i = 0; i < dst->enabledExtensionCount; i++) { @@ -1300,57 +1348,106 @@ void WINAPI wine_vkGetPhysicalDeviceExternalFencePropertiesKHR(VkPhysicalDevice void WINAPI wine_vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice phys_dev, const VkPhysicalDeviceExternalBufferInfo *buffer_info, VkExternalBufferProperties *properties) { + VkPhysicalDeviceExternalBufferInfo buffer_info_dup = *buffer_info;
TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties); - memset(&properties->externalMemoryProperties, 0, sizeof(properties->externalMemoryProperties));
+ if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ thunk_vkGetPhysicalDeviceExternalBufferProperties(phys_dev, &buffer_info_dup, properties);
+ if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
properties->externalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + properties->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; } void WINAPI wine_vkGetPhysicalDeviceExternalBufferPropertiesKHR(VkPhysicalDevice phys_dev, const VkPhysicalDeviceExternalBufferInfo *buffer_info, VkExternalBufferProperties *properties) { + VkPhysicalDeviceExternalBufferInfo buffer_info_dup = *buffer_info;
TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties); - memset(&properties->externalMemoryProperties, 0, sizeof(properties->externalMemoryProperties));
+ if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ thunk_vkGetPhysicalDeviceExternalBufferPropertiesKHR(phys_dev, &buffer_info_dup, properties);
+ if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
properties->externalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + properties->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; } -VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice phys_dev, +static VkResult wine_vk_get_physical_device_image_format_properties_2(VkPhysicalDevice phys_dev, + VkResult (*p_vkGetPhysicalDeviceImageFormatProperties2)(VkPhysicalDevice, const VkPhysicalDeviceImageFormatInfo2 *, VkImageFormatProperties2 *),
vkGetPhysicalDeviceImageFormatProperties2KHR and vkGetPhysicalDeviceImageFormatProperties2 are equivalent, you should just always use thunk_vkGetPhysicalDeviceImageFormatProperties2.
Ignore this part, I see why it's needed, one might not be exposed on the host.
const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) { + VkPhysicalDeviceExternalImageFormatInfo *external_image_info_host = NULL; + const VkPhysicalDeviceExternalImageFormatInfo *external_image_info; + VkPhysicalDeviceImageFormatInfo2 format_info_host = *format_info; VkExternalImageFormatProperties *external_image_properties; VkResult res; - TRACE("%p, %p, %p\n", phys_dev, format_info, properties); + if ((external_image_info = wine_vk_find_struct(format_info, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO))) + { + if (external_image_info->handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + if ((res = convert_VkPhysicalDeviceImageFormatInfo2_struct_chain(format_info->pNext, &format_info_host)) < 0) + { + WARN("Failed to convert VkPhysicalDeviceImageFormatInfo2 pNext chain, res=%d.\n", res); + return res; + }
+ external_image_info_host = wine_vk_find_struct(&format_info_host, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO); + external_image_info_host->handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + } + if (external_image_info->handleType &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + WARN("Unsupported handle type %#x.\n", external_image_info->handleType); + return VK_ERROR_FORMAT_NOT_SUPPORTED; + } + }
+ res = p_vkGetPhysicalDeviceImageFormatProperties2(phys_dev, &format_info_host, properties); - res = thunk_vkGetPhysicalDeviceImageFormatProperties2(phys_dev, format_info, properties); + if (external_image_info_host)
free_VkPhysicalDeviceImageFormatInfo2_struct_chain(&format_info_host); if ((external_image_properties = wine_vk_find_struct(properties, EXTERNAL_IMAGE_FORMAT_PROPERTIES))) { VkExternalMemoryProperties *p = &external_image_properties->externalMemoryProperties; - p->externalMemoryFeatures = 0; - p->exportFromImportedHandleTypes = 0; - p->compatibleHandleTypes = 0; + if (p->exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + { + p->exportFromImportedHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + p->exportFromImportedHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + } + if (p->compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + { + p->compatibleHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + p->compatibleHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + } > } return res; } -VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice phys_dev, +VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice phys_dev, const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) { - VkExternalImageFormatProperties *external_image_properties; - VkResult res;
TRACE("%p, %p, %p\n", phys_dev, format_info, properties); - res = thunk_vkGetPhysicalDeviceImageFormatProperties2KHR(phys_dev, format_info, properties); + return wine_vk_get_physical_device_image_format_properties_2(phys_dev, thunk_vkGetPhysicalDeviceImageFormatProperties2, format_info, properties); +} - if ((external_image_properties = wine_vk_find_struct(properties, EXTERNAL_IMAGE_FORMAT_PROPERTIES))) - { - VkExternalMemoryProperties *p = &external_image_properties->externalMemoryProperties; - p->externalMemoryFeatures = 0; - p->exportFromImportedHandleTypes = 0; - p->compatibleHandleTypes = 0; - } +VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice phys_dev, + const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) +{ + TRACE("%p, %p, %p\n", phys_dev, format_info, properties); - return res; + return wine_vk_get_physical_device_image_format_properties_2(phys_dev, thunk_vkGetPhysicalDeviceImageFormatProperties2KHR, format_info, properties); } /* From ntdll/unix/sync.c */ @@ -1860,3 +1957,249 @@ VkResult WINAPI wine_vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebu return thunk_vkDebugMarkerSetObjectNameEXT(device, &wine_name_info); }
+static HANDLE create_video_resource(int fd, LPCWSTR name) +{ + HANDLE ret = INVALID_HANDLE_VALUE;
+ if (name) + FIXME("Naming video resources not supported.\n");
+ wine_server_fd_to_handle(fd, GENERIC_ALL, 0, &ret);
+ return ret; +}
+VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *allocate_info, + const VkAllocationCallbacks *allocator, VkDeviceMemory *memory_out) +{ + const VkImportMemoryWin32HandleInfoKHR *handle_import_info = NULL; + const VkExportMemoryWin32HandleInfoKHR *handle_export_info = NULL; + VkMemoryAllocateInfo allocate_info_dup = *allocate_info; + VkExportMemoryAllocateInfo *export_info = NULL; + VkImportMemoryFdInfoKHR fd_import_info; + const VkBaseOutStructure *header; + struct wine_dev_mem *object; + VkResult res; + int fd;
+#if defined(USE_STRUCT_CONVERSION) + VkMemoryAllocateInfo_host allocate_info_host; + VkMemoryGetFdInfoKHR_host get_fd_info; +#else + VkMemoryAllocateInfo allocate_info_host; + VkMemoryGetFdInfoKHR get_fd_info; +#endif
+ TRACE("%p %p %p %p\n", device, allocate_info, allocator, memory_out);
+ if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n");
+ if ((res = convert_VkMemoryAllocateInfo_struct_chain(allocate_info->pNext, &allocate_info_dup)) < 0) + { + WARN("Failed to convert VkMemoryAllocateInfo pNext chain, res=%d.\n", res); + return res; + }
+ if (!(object = calloc(1, sizeof(*object)))) + { + free_VkMemoryAllocateInfo_struct_chain(&allocate_info_dup); + return VK_ERROR_OUT_OF_HOST_MEMORY; + }
+ object->dev_mem = VK_NULL_HANDLE; + object->handle = INVALID_HANDLE_VALUE; + fd_import_info.fd = -1; + fd_import_info.pNext = NULL;
+ /* find and process handle import/export info and grab it */ + for (header = allocate_info->pNext; header; header = header->pNext) + { + switch (header->sType) + { + case VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR: + handle_import_info = (const VkImportMemoryWin32HandleInfoKHR *)header; + break; + case VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR: + handle_export_info = (const VkExportMemoryWin32HandleInfoKHR *)header; + if (handle_export_info->pAttributes && handle_export_info->pAttributes->lpSecurityDescriptor) + FIXME("Support for custom security descriptor not implemented.\n"); + break; + default: + break; + } + }
IMO you should just wine_vk_find_struct twice here.
+ for (header = allocate_info_dup.pNext; header; header = header->pNext) + { + if (header->sType == VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO) + { + export_info = (VkExportMemoryAllocateInfo *)header; + object->handle_types = export_info->handleTypes; + if (object->handle_types &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + res = VK_ERROR_OUT_OF_HOST_MEMORY;
Why VK_ERROR_OUT_OF_HOST_MEMORY?
+ goto done; + } + if (object->handle_types) + export_info->handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + break; + } + }
+ /* Important to note is that Vulkan does consume imported FDs, but it doesn't consume imported HANDLEs */ + if (handle_import_info) + { + fd_import_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; + fd_import_info.pNext = allocate_info_dup.pNext; + fd_import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ switch (handle_import_info->handleType) + { + case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: + if (handle_import_info->handle) + NtDuplicateObject( NtCurrentProcess(), handle_import_info->handle, NtCurrentProcess(), &object->handle, 0, 0, DUPLICATE_SAME_ACCESS ); + else if (handle_import_info->name) + FIXME("Importing device memory by resource name not supported.\n"); + break; + default: + WARN("Invalid handle type %08x passed in.\n", handle_import_info->handleType); + res = VK_ERROR_INVALID_EXTERNAL_HANDLE; + goto done; + }
+ if (object->handle != INVALID_HANDLE_VALUE) + wine_server_handle_to_fd(object->handle, FILE_READ_DATA, &fd_import_info.fd, NULL);
+ if (fd_import_info.fd == -1) + { + TRACE("Couldn't access resource handle or name. type=%08x handle=%p name=%s\n", handle_import_info->handleType, handle_import_info->handle, + handle_import_info->name ? debugstr_w(handle_import_info->name) : ""); + res = VK_ERROR_INVALID_EXTERNAL_HANDLE; + goto done; + } + }
+ allocate_info_host.sType = allocate_info_dup.sType; + allocate_info_host.pNext = fd_import_info.fd == -1 ? allocate_info_dup.pNext : &fd_import_info; + allocate_info_host.allocationSize = allocate_info_dup.allocationSize; + allocate_info_host.memoryTypeIndex = allocate_info_dup.memoryTypeIndex;
+ if ((res = device->funcs.p_vkAllocateMemory(device->device, &allocate_info_host, NULL, &object->dev_mem)) == VK_SUCCESS) + { + if (object->handle == INVALID_HANDLE_VALUE && export_info && export_info->handleTypes) + { + get_fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + get_fd_info.pNext = NULL; + get_fd_info.memory = object->dev_mem; + get_fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ if (device->funcs.p_vkGetMemoryFdKHR(device->device, &get_fd_info, &fd) == VK_SUCCESS) + { + object->handle = create_video_resource(fd, handle_export_info ? handle_export_info->name : NULL); + object->access = handle_export_info ? handle_export_info->dwAccess : GENERIC_ALL; + if (handle_export_info && handle_export_info->pAttributes) + object->inherit = handle_export_info->pAttributes->bInheritHandle; + else + object->inherit = FALSE; + close(fd); + }
+ if (object->handle == INVALID_HANDLE_VALUE) + { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto done; + } + }
+ *memory_out = wine_dev_mem_to_handle(object);
You should call WINE_VK_ADD_NON_DISPATCHABLE_MAPPING here, and WINE_VK_REMOVE_HANDLE_MAPPING in vkFreeMemory.
+ }
+ done:
+ if (res != VK_SUCCESS) + { + if (object->dev_mem != VK_NULL_HANDLE) + device->funcs.p_vkFreeMemory(device->device, object->dev_mem, NULL);
No need to check for VK_NULL_HANDLE here, vkFreeMemory already does that.
+ if (fd_import_info.fd != -1) + close(fd_import_info.fd); + if (object->handle != INVALID_HANDLE_VALUE) + NtClose(object->handle); + free(object); + }
+ free_VkMemoryAllocateInfo_struct_chain(&allocate_info_dup);
+ return res; +}
+VkResult WINAPI wine_vkGetMemoryWin32HandleKHR(VkDevice device, + const VkMemoryGetWin32HandleInfoKHR *handle_info, HANDLE *handle) +{ + struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle_info->memory); + const VkBaseInStructure *chain;
+ TRACE("%p, %p %p\n", device, handle_info, handle);
+ if (!(dev_mem->handle_types & handle_info->handleType)) + return VK_ERROR_OUT_OF_HOST_MEMORY;
+ if ((chain = handle_info->pNext)) + FIXME("Ignoring a linked structure of type %u.\n", chain->sType);
+ switch(handle_info->handleType) + { + case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: + return !NtDuplicateObject( NtCurrentProcess(), dev_mem->handle, NtCurrentProcess(), handle, dev_mem->access, dev_mem->inherit ? OBJ_INHERIT : 0, 0) ? + VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + default: + return VK_ERROR_UNKNOWN; + } +}
+void WINAPI wine_vkFreeMemory(VkDevice device, VkDeviceMemory handle, const VkAllocationCallbacks *allocator) +{ + struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle);
+ TRACE("%p 0x%s, %p\n", device, wine_dbgstr_longlong(handle), allocator);
+ if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n");
+ device->funcs.p_vkFreeMemory(device->device, dev_mem->dev_mem, NULL); + if (dev_mem->handle != INVALID_HANDLE_VALUE) + NtClose(dev_mem->handle); + free(dev_mem); +}
+VkResult WINAPI wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, + VkExternalMemoryHandleTypeFlagBits type, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *properties) +{ + VkMemoryFdPropertiesKHR fd_props; + VkResult res; + int fd = -1;
+ TRACE("%p %u %p %p\n", device, type, handle, properties);
+ if (type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
VUID-vkGetMemoryWin32HandlePropertiesKHR-handleType-00666 handleType must not be one of the handle types defined as opaque
+ { + wine_server_handle_to_fd(handle, FILE_READ_DATA, &fd, NULL); + }
+ if (fd == -1) + return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ res = device->funcs.p_vkGetMemoryFdPropertiesKHR(device->device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, fd, &fd_props);
VUID-vkGetMemoryFdPropertiesKHR-handleType-00674 handleType must not be VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR
And you have to init the sType and pNext of fd_props.
+ close(fd);
+ if (res != VK_SUCCESS) + return res;
+ properties->sType = VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR; + properties->pNext = NULL;
You shouldn't touch those two, the application will set them.
+ properties->memoryTypeBits = fd_props.memoryTypeBits;
+ return res; +} diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 83dc90ca15e..468ab890dab 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -204,6 +204,28 @@ static inline VkSurfaceKHR wine_surface_to_handle(struct wine_surface *surface) return (VkSurfaceKHR)(uintptr_t)surface; } +struct wine_dev_mem +{ + VkDeviceMemory dev_mem;
+ VkExternalMemoryHandleTypeFlagBits handle_types;
+ BOOL inherit; + DWORD access;
+ HANDLE handle; +};
Wrapping VkDeviceMemory is problematic, because generated thunks won't unwrap handles in struct parameters. With this code functions using these structs will be broken:
VkBindAccelerationStructureMemoryInfoNV, VkBindBufferMemoryInfo, VkBindImageMemoryInfo, VkDeviceMemoryOpaqueCaptureAddressInfo, VkMappedMemoryRange, VkSparseImageMemoryBind, VkSparseMemoryBind
So you either have to either:
- Manually implement all those functions.
- Handle it in make_vulkan.
- Find another way to store data per VkDeviceMemory object, e.g with
VK_EXT_private_data.
I'm against the first option because it's a lot of manually written functions and future spec updates might add new problematic functions. The provisional video extensions add VkVideoBindMemoryKHR for example.
The best option would be the second, if you can make it work, because we currently have some code in vulkan.c that should be generated but has the same problem.
Thanks,
Georg Lehmann
+static inline struct wine_dev_mem *wine_dev_mem_from_handle(VkDeviceMemory handle) +{ + return (struct wine_dev_mem *)(uintptr_t)handle; +}
+static inline VkDeviceMemory wine_dev_mem_to_handle(struct wine_dev_mem *dev_mem) +{ + return (VkDeviceMemory)(uintptr_t)dev_mem; +}
BOOL wine_vk_device_extension_supported(const char *name) DECLSPEC_HIDDEN; BOOL wine_vk_instance_extension_supported(const char *name) DECLSPEC_HIDDEN;
On 5/3/21 5:44 PM, Georg Lehmann wrote:
On 03.05.21 18:46, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
This commit had to be squashed in with the tests, as data structures for the extension are missing from the headers when the extension is blacklisted by make_vulkan.
dlls/vulkan-1/tests/vulkan.c | 242 +++++++++++++++++-- dlls/winevulkan/loader_thunks.c | 12 + dlls/winevulkan/loader_thunks.h | 2 + dlls/winevulkan/make_vulkan | 63 ++++- dlls/winevulkan/vulkan.c | 385 +++++++++++++++++++++++++++++-- dlls/winevulkan/vulkan_private.h | 22 ++ 6 files changed, 679 insertions(+), 47 deletions(-)
Hi,
I would prefer if you could split this up in to multiple patches, e.g. the host only functions and struct conversion changes in make_vulkan could each be in their own commits.
Sounds good.
diff --git a/dlls/vulkan-1/tests/vulkan.c b/dlls/vulkan-1/tests/vulkan.c index 9061b2b6db8..c1350da70ee 100644 --- a/dlls/vulkan-1/tests/vulkan.c +++ b/dlls/vulkan-1/tests/vulkan.c @@ -144,7 +144,7 @@ static void test_instance_version(void) VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)); } -static void enumerate_physical_device(VkPhysicalDevice vk_physical_device) +static void enumerate_physical_device(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkPhysicalDeviceProperties properties; @@ -224,7 +224,7 @@ static void test_enumerate_physical_device2(void) vkDestroyInstance(vk_instance, NULL); } -static void enumerate_device_queues(VkPhysicalDevice vk_physical_device) +static void enumerate_device_queues(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkPhysicalDeviceProperties device_properties; VkQueueFamilyProperties *properties; @@ -283,7 +283,7 @@ static void test_physical_device_groups(void) trace("Group[%u] count %u, subset allocation %#x\n", i, properties[i].physicalDeviceCount, properties[i].subsetAllocation); for (j = 0; j < properties[i].physicalDeviceCount; ++j)
- enumerate_physical_device(properties[i].physicalDevices[j]);
+ enumerate_physical_device(vk_instance, properties[i].physicalDevices[j]); } if ((vr = create_device(properties->physicalDevices[0], 0, NULL, NULL, &vk_device)) < 0) @@ -307,7 +307,7 @@ static void test_physical_device_groups(void) vkDestroyInstance(vk_instance, NULL); } -static void test_destroy_command_pool(VkPhysicalDevice vk_physical_device) +static void test_destroy_command_pool(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkCommandBufferAllocateInfo allocate_info; VkCommandPoolCreateInfo pool_info; @@ -366,7 +366,7 @@ static void test_unsupported_instance_extensions(void) } } -static void test_unsupported_device_extensions(VkPhysicalDevice vk_physical_device) +static void test_unsupported_device_extensions(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { VkDevice vk_device; unsigned int i; @@ -387,7 +387,7 @@ static void test_unsupported_device_extensions(VkPhysicalDevice vk_physical_devi } } -static void test_private_data(VkPhysicalDevice vk_physical_device) +static void test_private_data(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) { PFN_vkDestroyPrivateDataSlotEXT pfn_vkDestroyPrivateDataSlotEXT; PFN_vkCreatePrivateDataSlotEXT pfn_vkCreatePrivateDataSlotEXT; @@ -437,7 +437,218 @@ static void test_private_data(VkPhysicalDevice vk_physical_device) vkDestroyDevice(vk_device, NULL); } -static void for_each_device(void (*test_func)(VkPhysicalDevice)) +uint32_t find_memory_type(VkPhysicalDevice vk_physical_device, VkMemoryPropertyFlagBits flags) +{ + VkPhysicalDeviceMemoryProperties properties = {0}; + unsigned int i;
+ vkGetPhysicalDeviceMemoryProperties(vk_physical_device, &properties);
+ for(i = 0; i < properties.memoryTypeCount; i++) + { + if (properties.memoryTypes[i].propertyFlags & flags) + return i; + } + return -1; +}
+static void test_external_memory(VkInstance vk_instance, VkPhysicalDevice vk_physical_device) +{ + PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR; + PFN_vkGetMemoryWin32HandleKHR pfn_vkGetMemoryWin32HandleKHR; + VkPhysicalDeviceExternalBufferInfoKHR external_buffer_info; + VkExternalBufferPropertiesKHR external_buffer_properties; + VkMemoryDedicatedAllocateInfoKHR dedicated_alloc_info; + VkExportMemoryWin32HandleInfoKHR export_handle_info; + VkImportMemoryWin32HandleInfoKHR import_handle_info; + VkExportMemoryAllocateInfoKHR export_memory_info; + VkMemoryGetWin32HandleInfoKHR get_handle_info; + VkBufferCreateInfo buffer_create_info; + VkMemoryAllocateInfo alloc_info; + uint32_t queue_family_index; + HANDLE nt_handle, kmt_handle; + VkDeviceMemory vk_memory, vk_memory_import; + VkBuffer vk_buffer; + VkDevice vk_device; + VkResult vr;
+ static const char *extensions[] = + { + "VK_KHR_dedicated_allocation", + "VK_KHR_external_memory", + "VK_KHR_external_memory_win32", + };
+ pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR = + (void*) vkGetInstanceProcAddr(vk_instance, "vkGetPhysicalDeviceExternalBufferPropertiesKHR");
+ if ((vr = create_device(vk_physical_device, ARRAY_SIZE(extensions), extensions, NULL, &vk_device))) + { + skip("Failed to create device with external memory extensions, VkResult %d.\n", vr); + return; + }
+ pfn_vkGetMemoryWin32HandleKHR = (void *) vkGetDeviceProcAddr(vk_device, "vkGetMemoryWin32HandleKHR");
+ find_queue_family(vk_physical_device, VK_QUEUE_GRAPHICS_BIT, &queue_family_index);
+ /* Most implementations only support exporting dedicated allocations */
+ buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.pNext = NULL; + buffer_create_info.flags = 0; + buffer_create_info.size = 1; + buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + buffer_create_info.sharingMode = VK_SHARING_MODE_CONCURRENT; + buffer_create_info.queueFamilyIndexCount = 1; + buffer_create_info.pQueueFamilyIndices = &queue_family_index; + if ((vr = vkCreateBuffer(vk_device, &buffer_create_info, NULL, &vk_buffer))) + { + skip("Failed to create generic buffer, VkResult %d.\n", vr); + vkDestroyDevice(vk_device, NULL); + return; + }
+ dedicated_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + dedicated_alloc_info.pNext = NULL; + dedicated_alloc_info.image = VK_NULL_HANDLE; + dedicated_alloc_info.buffer = vk_buffer;
+ external_buffer_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR; + external_buffer_info.pNext = NULL; + external_buffer_info.flags = 0; + external_buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + external_buffer_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
+ memset(&external_buffer_properties, 0, sizeof(external_buffer_properties)); + external_buffer_properties.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR;
pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR(vk_physical_device, &external_buffer_info, &external_buffer_properties);
+ if (!(external_buffer_properties.externalMemoryProperties.externalMemoryFeatures &
(VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR|VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR))) + skip("With desired parameters, buffers are not exportable to and importable from an NT handle.\n"); + else + {
ok(external_buffer_properties.externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR, + "Unexpected compatibleHandleTypes %#x.\n", external_buffer_properties.externalMemoryProperties.compatibleHandleTypes);
+ export_memory_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + export_memory_info.pNext = &dedicated_alloc_info; + export_memory_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
+ export_handle_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + export_handle_info.pNext = &export_memory_info; + export_handle_info.name = L"wine_test_buffer_export_name"; + export_handle_info.dwAccess = GENERIC_ALL; + export_handle_info.pAttributes = NULL;
+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = &export_handle_info; + alloc_info.allocationSize = 1; + alloc_info.memoryTypeIndex = find_memory_type(vk_physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ ok(alloc_info.memoryTypeIndex != -1, "Device local memory type index was not found.\n");
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
+ get_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR; + get_handle_info.pNext = NULL; + get_handle_info.memory = vk_memory; + get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
+ vr = pfn_vkGetMemoryWin32HandleKHR(vk_device, &get_handle_info, &nt_handle); + ok(vr == VK_SUCCESS, "vkGetMemoryWin32HandleKHR failed, VkResult %d.\n", vr);
+ import_handle_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + import_handle_info.pNext = &dedicated_alloc_info; + import_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR; + import_handle_info.handle = nt_handle; + import_handle_info.name = NULL;
+ alloc_info.pNext = &import_handle_info;
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
+ vkFreeMemory(vk_device, vk_memory_import, NULL);
+ import_handle_info.handle = NULL; + import_handle_info.name = L"wine_test_buffer_export_name";
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + todo_wine + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + if (vr == VK_SUCCESS) + { + ok(vk_memory_import != vk_memory, "Expected new memory object.\n"); + vkFreeMemory(vk_device, vk_memory_import, NULL); + } + vkFreeMemory(vk_device, vk_memory, NULL); + CloseHandle(nt_handle); + }
+ external_buffer_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
+ memset(&external_buffer_properties, 0, sizeof(external_buffer_properties)); + external_buffer_properties.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR;
pfn_vkGetPhysicalDeviceExternalBufferPropertiesKHR(vk_physical_device, &external_buffer_info, &external_buffer_properties);
+ if (!(external_buffer_properties.externalMemoryProperties.externalMemoryFeatures &
(VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR|VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR))) + skip("With desired parameters, buffers are not exportable to and importable from a KMT handle.\n"); + else + {
ok(external_buffer_properties.externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR, + "Unexpected compatibleHandleTypes %#x.\n", external_buffer_properties.externalMemoryProperties.compatibleHandleTypes);
+ export_memory_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + export_memory_info.pNext = &dedicated_alloc_info; + export_memory_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = &export_memory_info; + alloc_info.allocationSize = 1; + alloc_info.memoryTypeIndex = find_memory_type(vk_physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ ok(alloc_info.memoryTypeIndex != -1, "Device local memory type index was not found.\n");
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr);
+ get_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR; + get_handle_info.pNext = NULL; + get_handle_info.memory = vk_memory; + get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
+ vr = pfn_vkGetMemoryWin32HandleKHR(vk_device, &get_handle_info, &kmt_handle); + ok(vr == VK_SUCCESS, "vkGetMemoryWin32HandleKHR failed, VkResult %d.\n", vr);
+ import_handle_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + import_handle_info.pNext = &dedicated_alloc_info; + import_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR; + import_handle_info.handle = kmt_handle; + import_handle_info.name = NULL;
+ alloc_info.pNext = &import_handle_info;
+ vr = vkAllocateMemory(vk_device, &alloc_info, NULL, &vk_memory_import); + ok(vr == VK_SUCCESS, "vkAllocateMemory failed, VkResult %d.\n", vr); + ok(vk_memory_import != vk_memory, "Expected new memory object.\n");
+ vkFreeMemory(vk_device, vk_memory_import, NULL); + vkFreeMemory(vk_device, vk_memory, NULL); + }
+ vkDestroyBuffer(vk_device, vk_buffer, NULL); + vkDestroyDevice(vk_device, NULL); +}
+static void for_each_device(void (*test_func)(VkInstance,VkPhysicalDevice), uint32_t extension_count, + const char * const *enabled_extensions) { VkPhysicalDevice *vk_physical_devices; VkInstance vk_instance; @@ -445,7 +656,7 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) uint32_t count; VkResult vr; - if ((vr = create_instance_skip(0, NULL, &vk_instance)) < 0) + if ((vr = create_instance_skip(extension_count, enabled_extensions, &vk_instance)) < 0) return; ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr); @@ -463,7 +674,7 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr); for (i = 0; i < count; ++i) - test_func(vk_physical_devices[i]); + test_func(vk_instance, vk_physical_devices[i]); heap_free(vk_physical_devices); @@ -472,13 +683,16 @@ static void for_each_device(void (*test_func)(VkPhysicalDevice)) START_TEST(vulkan) { + static const char *external_memory_capabilities = "VK_KHR_external_memory_capabilities";
test_instance_version(); - for_each_device(enumerate_physical_device); + for_each_device(enumerate_physical_device, 0, NULL); test_enumerate_physical_device2(); - for_each_device(enumerate_device_queues); + for_each_device(enumerate_device_queues, 0, NULL); test_physical_device_groups(); - for_each_device(test_destroy_command_pool); + for_each_device(test_destroy_command_pool, 0, NULL); test_unsupported_instance_extensions(); - for_each_device(test_unsupported_device_extensions); - for_each_device(test_private_data); + for_each_device(test_unsupported_device_extensions, 0, NULL); + for_each_device(test_private_data, 0, NULL); + for_each_device(test_external_memory, 1, &external_memory_capabilities); } diff --git a/dlls/winevulkan/loader_thunks.c b/dlls/winevulkan/loader_thunks.c index 69fcd44160e..471999a3821 100644 --- a/dlls/winevulkan/loader_thunks.c +++ b/dlls/winevulkan/loader_thunks.c @@ -1448,6 +1448,16 @@ VkResult WINAPI vkGetMemoryHostPointerPropertiesEXT(VkDevice device, VkExternalM return unix_funcs->p_vkGetMemoryHostPointerPropertiesEXT(device, handleType, pHostPointer, pMemoryHostPointerProperties); } +VkResult WINAPI vkGetMemoryWin32HandleKHR(VkDevice device, const VkMemoryGetWin32HandleInfoKHR *pGetWin32HandleInfo, HANDLE *pHandle) +{ + return unix_funcs->p_vkGetMemoryWin32HandleKHR(device, pGetWin32HandleInfo, pHandle); +}
+VkResult WINAPI vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *pMemoryWin32HandleProperties) +{ + return unix_funcs->p_vkGetMemoryWin32HandlePropertiesKHR(device, handleType, handle, pMemoryWin32HandleProperties); +}
VkResult WINAPI vkGetPerformanceParameterINTEL(VkDevice device, VkPerformanceParameterTypeINTEL parameter, VkPerformanceValueINTEL *pValue) { return unix_funcs->p_vkGetPerformanceParameterINTEL(device, parameter, pValue); @@ -2223,6 +2233,8 @@ static const struct vulkan_func vk_device_dispatch_table[] = {"vkGetImageSparseMemoryRequirements2KHR", &vkGetImageSparseMemoryRequirements2KHR}, {"vkGetImageSubresourceLayout", &vkGetImageSubresourceLayout}, {"vkGetMemoryHostPointerPropertiesEXT", &vkGetMemoryHostPointerPropertiesEXT}, + {"vkGetMemoryWin32HandleKHR", &vkGetMemoryWin32HandleKHR}, + {"vkGetMemoryWin32HandlePropertiesKHR", &vkGetMemoryWin32HandlePropertiesKHR}, {"vkGetPerformanceParameterINTEL", &vkGetPerformanceParameterINTEL}, {"vkGetPipelineCacheData", &vkGetPipelineCacheData}, {"vkGetPipelineExecutableInternalRepresentationsKHR", &vkGetPipelineExecutableInternalRepresentationsKHR}, diff --git a/dlls/winevulkan/loader_thunks.h b/dlls/winevulkan/loader_thunks.h index e7257cef508..031b7f5652b 100644 --- a/dlls/winevulkan/loader_thunks.h +++ b/dlls/winevulkan/loader_thunks.h @@ -304,6 +304,8 @@ struct unix_funcs void (WINAPI *p_vkGetImageSparseMemoryRequirements2KHR)(VkDevice, const VkImageSparseMemoryRequirementsInfo2 *, uint32_t *, VkSparseImageMemoryRequirements2 *); void (WINAPI *p_vkGetImageSubresourceLayout)(VkDevice, VkImage, const VkImageSubresource *, VkSubresourceLayout *); VkResult (WINAPI *p_vkGetMemoryHostPointerPropertiesEXT)(VkDevice, VkExternalMemoryHandleTypeFlagBits, const void *, VkMemoryHostPointerPropertiesEXT *); + VkResult (WINAPI *p_vkGetMemoryWin32HandleKHR)(VkDevice, const VkMemoryGetWin32HandleInfoKHR *, HANDLE *); + VkResult (WINAPI *p_vkGetMemoryWin32HandlePropertiesKHR)(VkDevice, VkExternalMemoryHandleTypeFlagBits, HANDLE, VkMemoryWin32HandlePropertiesKHR *); VkResult (WINAPI *p_vkGetPerformanceParameterINTEL)(VkDevice, VkPerformanceParameterTypeINTEL, VkPerformanceValueINTEL *); VkResult (WINAPI *p_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT)(VkPhysicalDevice, uint32_t *, VkTimeDomainEXT *); VkResult (WINAPI *p_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV)(VkPhysicalDevice, uint32_t *, VkCooperativeMatrixPropertiesNV *); diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index b1877acda17..0a0e3bac299 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -99,7 +99,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_pipeline_creation_feedback", "VK_GOOGLE_display_timing", "VK_KHR_external_fence_win32", - "VK_KHR_external_memory_win32", "VK_KHR_external_semaphore_win32", # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work. @@ -109,7 +108,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_external_memory_dma_buf", "VK_EXT_image_drm_format_modifier", "VK_KHR_external_fence_fd", - "VK_KHR_external_memory_fd", "VK_KHR_external_semaphore_fd", # Extensions which require callback handling @@ -154,6 +152,8 @@ class ThunkType(Enum): # - PRIVATE thunks can be used in custom implementations for # struct conversion. # - loader_thunk sets whether to create a thunk for unix_funcs. +# - host_only sets whether to preclude the function from the +# win32-facing dispatch table. FUNCTION_OVERRIDES = { # Global functions "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, @@ -169,7 +169,7 @@ FUNCTION_OVERRIDES = { "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE}, "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, - "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, @@ -178,11 +178,13 @@ FUNCTION_OVERRIDES = { # Device functions "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkAllocateMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkFreeMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE}, "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, @@ -213,7 +215,7 @@ FUNCTION_OVERRIDES = { "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, # VK_KHR_external_memory_capabilities - "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, # VK_KHR_external_semaphore_capabilities @@ -249,12 +251,22 @@ FUNCTION_OVERRIDES = { # VK_EXT_debug_marker "vkDebugMarkerSetObjectNameEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkDebugMarkerSetObjectTagEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
+ # VK_KHR_external_memory_win32 + "vkGetMemoryWin32HandleKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkGetMemoryWin32HandlePropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
+ # VK_KHR_external_memory_fd + "vkGetMemoryFdKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "host_only" : True}, + "vkGetMemoryFdPropertiesKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "host_only" : True},
Would it make sense to added a new value to ThunkType instead, e.g. ThunkType.NOT_EXPOSED?
Yeah, it'd probably be better to include it there. Would be a little bit strange in that the option affects the loader thunks too but we wouldn't want to support the presence of NOT_EXPOSED in "loader_thunk" and not "thunk" for example.
} -STRUCT_CHAIN_CONVERSIONS = [ - "VkDeviceCreateInfo", - "VkInstanceCreateInfo", -] +STRUCT_CHAIN_CONVERSIONS = { + "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"], + "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"], + "VkPhysicalDeviceImageFormatInfo2": [], + "VkMemoryAllocateInfo": ["VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR", "VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR"], +} class Direction(Enum): @@ -532,6 +544,7 @@ class VkFunction(object): self.driver = func_info["driver"] if func_info else False self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC + self.host_only = func_info["host_only"] if func_info and "host_only" in func_info else False # Required is set while parsing which APIs and types are required # and is used by the code generation. @@ -651,6 +664,9 @@ class VkFunction(object): def needs_private_thunk(self): return self.thunk_type == ThunkType.PRIVATE + def needs_win_exclusion(self): + return self.host_only
def pfn(self, prefix="p", call_conv=None, conv=False): """ Create function pointer. """ @@ -1028,6 +1044,8 @@ class VkHandle(object): return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name) if self.name == "VkSurfaceKHR": return "wine_surface_from_handle({0})->surface".format(name) + if self.name == "VkDeviceMemory": + return "wine_dev_mem_from_handle({0})->dev_mem".format(name) native_handle_name = None @@ -2115,9 +2133,10 @@ class FreeFunction(object): class StructChainConversionFunction(object): - def __init__(self, direction, struct): + def __init__(self, direction, struct, ignores): self.direction = direction self.struct = struct + self.ignores = ignores self.type = struct.name self.name = "convert_{0}_struct_chain".format(self.type) @@ -2143,8 +2162,8 @@ class StructChainConversionFunction(object): body += " {\n" # Ignore to not confuse host loader.
This comment is now misplaced.
👌
- body += " case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO:\n" - body += " case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO:\n" + for i in self.ignores: + body += " case {0}:\n".format(i) body += " break;\n\n" for e in self.struct.struct_extensions: @@ -2153,6 +2172,9 @@ class StructChainConversionFunction(object): stype = next(x for x in e.members if x.name == "sType") + if stype.values in self.ignores: + continue
body += " case {0}:\n".format(stype.values) body += " {\n" @@ -2250,7 +2272,7 @@ class VkGenerator(object): for struct in self.registry.structs: if struct.name in STRUCT_CHAIN_CONVERSIONS:
self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct))
self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct, STRUCT_CHAIN_CONVERSIONS[struct.name])) self.struct_chain_conversions.append(FreeStructChainFunction(struct)) def _generate_copyright(self, f, spec_file=False): @@ -2292,6 +2314,9 @@ class VkGenerator(object): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue
if vk_func.is_global_func(): continue @@ -2382,6 +2407,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type == ThunkType.NONE: continue @@ -2403,6 +2430,8 @@ class VkGenerator(object): continue if vk_func.needs_thunk() and not vk_func.needs_private_thunk(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.is_core_func(): f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix=prefix))) @@ -2510,6 +2539,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type != ThunkType.PUBLIC: continue @@ -2519,6 +2550,8 @@ class VkGenerator(object): for vk_func in self.registry.device_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2527,6 +2560,8 @@ class VkGenerator(object): for vk_func in self.registry.phys_dev_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2535,6 +2570,8 @@ class VkGenerator(object): for vk_func in self.registry.instance_funcs: if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue f.write(" {{"{0}", &{0}}},\n".format(vk_func.name)) f.write("};\n\n") @@ -2592,6 +2629,8 @@ class VkGenerator(object): for vk_func in self.registry.funcs.values(): if not vk_func.is_required(): continue + if vk_func.needs_win_exclusion(): + continue if vk_func.loader_thunk_type == ThunkType.NONE: continue diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 45eda78e997..e95860cb3fa 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -25,6 +25,7 @@ #include <time.h> #include <stdarg.h> #include <stdlib.h> +#include <unistd.h> #include "ntstatus.h" #define WIN32_NO_STATUS @@ -34,6 +35,8 @@ #include "winuser.h" #include "winternl.h" +#include "wine/server.h"
#include "vulkan_private.h" WINE_DEFAULT_DEBUG_CHANNEL(vulkan); @@ -265,6 +268,14 @@ static struct VkPhysicalDevice_T *wine_vk_physical_device_alloc(struct VkInstanc { if (wine_vk_device_extension_supported(host_properties[i].extensionName)) { + if (!strcmp(host_properties[i].extensionName, "VK_KHR_external_memory_fd")) + { + TRACE("Substituting VK_KHR_external_memory_fd for VK_KHR_external_memory_win32\n");
+ snprintf(host_properties[i].extensionName, sizeof(host_properties[i].extensionName),
- VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
+ host_properties[i].specVersion = VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION; + } TRACE("Enabling extension '%s' for physical device %p\n", host_properties[i].extensionName, object); num_properties++; } @@ -368,12 +379,16 @@ static void wine_vk_device_free_create_info(VkDeviceCreateInfo *create_info) } free_VkDeviceCreateInfo_struct_chain(create_info);
+ if (create_info->enabledExtensionCount) + free((void *)create_info->ppEnabledExtensionNames); } static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src, VkDeviceCreateInfo *dst) { VkDeviceGroupDeviceCreateInfo *group_info; + const char **enabled_extensions; unsigned int i; VkResult res; @@ -406,6 +421,39 @@ static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src dst->enabledLayerCount = 0; dst->ppEnabledLayerNames = NULL; + if (src->enabledExtensionCount > 0) + { + enabled_extensions = calloc(src->enabledExtensionCount, sizeof(*src->ppEnabledExtensionNames)); + if (!enabled_extensions) + { + if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO))) + free((void *)group_info->pPhysicalDevices); + free_VkDeviceCreateInfo_struct_chain(dst); + return VK_ERROR_OUT_OF_HOST_MEMORY; + }
+ for (i = 0; i < src->enabledExtensionCount; i++) + { + if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_fd")) + { + if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO))) + free((void *)group_info->pPhysicalDevices); + free_VkDeviceCreateInfo_struct_chain(dst); + free(enabled_extensions); + return VK_ERROR_EXTENSION_NOT_PRESENT; + }
Why do you check this? It shouldn't be exposed to the app in any way.
It seems to be exposed to apps without this (the `test_unsupported_device_extensions` wine test in dlls/vulkan-1 fails).
+ else if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_win32")) + { + enabled_extensions[i] = "VK_KHR_external_memory_fd"; + } + else + { + enabled_extensions[i] = src->ppEnabledExtensionNames[i]; + } + } + dst->ppEnabledExtensionNames = enabled_extensions; + }
TRACE("Enabled %u extensions.\n", dst->enabledExtensionCount); for (i = 0; i < dst->enabledExtensionCount; i++) { @@ -1300,57 +1348,106 @@ void WINAPI wine_vkGetPhysicalDeviceExternalFencePropertiesKHR(VkPhysicalDevice void WINAPI wine_vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice phys_dev, const VkPhysicalDeviceExternalBufferInfo *buffer_info, VkExternalBufferProperties *properties) { + VkPhysicalDeviceExternalBufferInfo buffer_info_dup = *buffer_info;
TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties); - memset(&properties->externalMemoryProperties, 0, sizeof(properties->externalMemoryProperties));
+ if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ thunk_vkGetPhysicalDeviceExternalBufferProperties(phys_dev, &buffer_info_dup, properties);
+ if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
- properties->externalMemoryProperties.exportFromImportedHandleTypes
= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
- properties->externalMemoryProperties.compatibleHandleTypes =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; } void WINAPI wine_vkGetPhysicalDeviceExternalBufferPropertiesKHR(VkPhysicalDevice phys_dev, const VkPhysicalDeviceExternalBufferInfo *buffer_info, VkExternalBufferProperties *properties) { + VkPhysicalDeviceExternalBufferInfo buffer_info_dup = *buffer_info;
TRACE("%p, %p, %p\n", phys_dev, buffer_info, properties); - memset(&properties->externalMemoryProperties, 0, sizeof(properties->externalMemoryProperties));
+ if (buffer_info_dup.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + buffer_info_dup.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
- thunk_vkGetPhysicalDeviceExternalBufferPropertiesKHR(phys_dev,
&buffer_info_dup, properties);
+ if (properties->externalMemoryProperties.exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
- properties->externalMemoryProperties.exportFromImportedHandleTypes
= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + if (properties->externalMemoryProperties.compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
- properties->externalMemoryProperties.compatibleHandleTypes =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; } -VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice phys_dev, +static VkResult wine_vk_get_physical_device_image_format_properties_2(VkPhysicalDevice phys_dev, + VkResult (*p_vkGetPhysicalDeviceImageFormatProperties2)(VkPhysicalDevice, const VkPhysicalDeviceImageFormatInfo2 *, VkImageFormatProperties2 *),
vkGetPhysicalDeviceImageFormatProperties2KHR and vkGetPhysicalDeviceImageFormatProperties2 are equivalent, you should just always use thunk_vkGetPhysicalDeviceImageFormatProperties2.
const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) { + VkPhysicalDeviceExternalImageFormatInfo *external_image_info_host = NULL; + const VkPhysicalDeviceExternalImageFormatInfo *external_image_info; + VkPhysicalDeviceImageFormatInfo2 format_info_host = *format_info; VkExternalImageFormatProperties *external_image_properties; VkResult res; - TRACE("%p, %p, %p\n", phys_dev, format_info, properties); + if ((external_image_info = wine_vk_find_struct(format_info, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO))) + { + if (external_image_info->handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + if ((res = convert_VkPhysicalDeviceImageFormatInfo2_struct_chain(format_info->pNext, &format_info_host)) < 0) + { + WARN("Failed to convert VkPhysicalDeviceImageFormatInfo2 pNext chain, res=%d.\n", res); + return res; + }
+ external_image_info_host = wine_vk_find_struct(&format_info_host, PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO); + external_image_info_host->handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + } + if (external_image_info->handleType &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + WARN("Unsupported handle type %#x.\n", external_image_info->handleType); + return VK_ERROR_FORMAT_NOT_SUPPORTED; + } + }
+ res = p_vkGetPhysicalDeviceImageFormatProperties2(phys_dev, &format_info_host, properties); - res = thunk_vkGetPhysicalDeviceImageFormatProperties2(phys_dev, format_info, properties); + if (external_image_info_host)
- free_VkPhysicalDeviceImageFormatInfo2_struct_chain(&format_info_host);
if ((external_image_properties = wine_vk_find_struct(properties, EXTERNAL_IMAGE_FORMAT_PROPERTIES))) { VkExternalMemoryProperties *p = &external_image_properties->externalMemoryProperties; - p->externalMemoryFeatures = 0; - p->exportFromImportedHandleTypes = 0; - p->compatibleHandleTypes = 0; + if (p->exportFromImportedHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + { + p->exportFromImportedHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + p->exportFromImportedHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + } + if (p->compatibleHandleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) + { + p->compatibleHandleTypes &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + p->compatibleHandleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + } > } return res; } -VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice phys_dev, +VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice phys_dev, const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) { - VkExternalImageFormatProperties *external_image_properties; - VkResult res;
TRACE("%p, %p, %p\n", phys_dev, format_info, properties); - res = thunk_vkGetPhysicalDeviceImageFormatProperties2KHR(phys_dev, format_info, properties); + return wine_vk_get_physical_device_image_format_properties_2(phys_dev, thunk_vkGetPhysicalDeviceImageFormatProperties2, format_info, properties); +} - if ((external_image_properties = wine_vk_find_struct(properties, EXTERNAL_IMAGE_FORMAT_PROPERTIES))) - { - VkExternalMemoryProperties *p = &external_image_properties->externalMemoryProperties; - p->externalMemoryFeatures = 0; - p->exportFromImportedHandleTypes = 0; - p->compatibleHandleTypes = 0; - } +VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice phys_dev, + const VkPhysicalDeviceImageFormatInfo2 *format_info, VkImageFormatProperties2 *properties) +{ + TRACE("%p, %p, %p\n", phys_dev, format_info, properties); - return res; + return wine_vk_get_physical_device_image_format_properties_2(phys_dev, thunk_vkGetPhysicalDeviceImageFormatProperties2KHR, format_info, properties); } /* From ntdll/unix/sync.c */ @@ -1860,3 +1957,249 @@ VkResult WINAPI wine_vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebu return thunk_vkDebugMarkerSetObjectNameEXT(device, &wine_name_info); }
+static HANDLE create_video_resource(int fd, LPCWSTR name) +{ + HANDLE ret = INVALID_HANDLE_VALUE;
+ if (name) + FIXME("Naming video resources not supported.\n");
+ wine_server_fd_to_handle(fd, GENERIC_ALL, 0, &ret);
+ return ret; +}
+VkResult WINAPI wine_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *allocate_info, + const VkAllocationCallbacks *allocator, VkDeviceMemory *memory_out) +{ + const VkImportMemoryWin32HandleInfoKHR *handle_import_info = NULL; + const VkExportMemoryWin32HandleInfoKHR *handle_export_info = NULL; + VkMemoryAllocateInfo allocate_info_dup = *allocate_info; + VkExportMemoryAllocateInfo *export_info = NULL; + VkImportMemoryFdInfoKHR fd_import_info; + const VkBaseOutStructure *header; + struct wine_dev_mem *object; + VkResult res; + int fd;
+#if defined(USE_STRUCT_CONVERSION) + VkMemoryAllocateInfo_host allocate_info_host; + VkMemoryGetFdInfoKHR_host get_fd_info; +#else + VkMemoryAllocateInfo allocate_info_host; + VkMemoryGetFdInfoKHR get_fd_info; +#endif
+ TRACE("%p %p %p %p\n", device, allocate_info, allocator, memory_out);
+ if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n");
+ if ((res = convert_VkMemoryAllocateInfo_struct_chain(allocate_info->pNext, &allocate_info_dup)) < 0) + { + WARN("Failed to convert VkMemoryAllocateInfo pNext chain, res=%d.\n", res); + return res; + }
+ if (!(object = calloc(1, sizeof(*object)))) + {
- free_VkMemoryAllocateInfo_struct_chain(&allocate_info_dup);
+ return VK_ERROR_OUT_OF_HOST_MEMORY; + }
+ object->dev_mem = VK_NULL_HANDLE; + object->handle = INVALID_HANDLE_VALUE; + fd_import_info.fd = -1; + fd_import_info.pNext = NULL;
+ /* find and process handle import/export info and grab it */ + for (header = allocate_info->pNext; header; header = header->pNext) + { + switch (header->sType) + { + case VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR: + handle_import_info = (const VkImportMemoryWin32HandleInfoKHR *)header; + break; + case VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR: + handle_export_info = (const VkExportMemoryWin32HandleInfoKHR *)header; + if (handle_export_info->pAttributes && handle_export_info->pAttributes->lpSecurityDescriptor) + FIXME("Support for custom security descriptor not implemented.\n"); + break; + default: + break; + } + }
IMO you should just wine_vk_find_struct twice here.
👌
+ for (header = allocate_info_dup.pNext; header; header = header->pNext) + { + if (header->sType == VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO) + { + export_info = (VkExportMemoryAllocateInfo *)header; + object->handle_types = export_info->handleTypes; + if (object->handle_types &~ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) + { + res = VK_ERROR_OUT_OF_HOST_MEMORY;
Why VK_ERROR_OUT_OF_HOST_MEMORY?
The spec just says that the handleTypes here must be supported, and doesn't list what return code should be used when it's not; so I just picked the most generic failure code in the list of possible returns. VK_ERROR_INVALID_EXTERNAL_HANDLE seems wrong since it's not an invalid external handle, it's an unsupported handle type.
+ goto done; + } + if (object->handle_types) + export_info->handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + break; + } + }
+ /* Important to note is that Vulkan does consume imported FDs, but it doesn't consume imported HANDLEs */ + if (handle_import_info) + { + fd_import_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; + fd_import_info.pNext = allocate_info_dup.pNext; + fd_import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ switch (handle_import_info->handleType) + { + case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: + if (handle_import_info->handle) + NtDuplicateObject( NtCurrentProcess(), handle_import_info->handle, NtCurrentProcess(), &object->handle, 0, 0, DUPLICATE_SAME_ACCESS ); + else if (handle_import_info->name) + FIXME("Importing device memory by resource name not supported.\n"); + break; + default: + WARN("Invalid handle type %08x passed in.\n", handle_import_info->handleType); + res = VK_ERROR_INVALID_EXTERNAL_HANDLE; + goto done; + }
+ if (object->handle != INVALID_HANDLE_VALUE) + wine_server_handle_to_fd(object->handle, FILE_READ_DATA, &fd_import_info.fd, NULL);
+ if (fd_import_info.fd == -1) + { + TRACE("Couldn't access resource handle or name. type=%08x handle=%p name=%s\n", handle_import_info->handleType, handle_import_info->handle, + handle_import_info->name ? debugstr_w(handle_import_info->name) : ""); + res = VK_ERROR_INVALID_EXTERNAL_HANDLE; + goto done; + } + }
+ allocate_info_host.sType = allocate_info_dup.sType; + allocate_info_host.pNext = fd_import_info.fd == -1 ? allocate_info_dup.pNext : &fd_import_info; + allocate_info_host.allocationSize = allocate_info_dup.allocationSize; + allocate_info_host.memoryTypeIndex = allocate_info_dup.memoryTypeIndex;
+ if ((res = device->funcs.p_vkAllocateMemory(device->device, &allocate_info_host, NULL, &object->dev_mem)) == VK_SUCCESS) + { + if (object->handle == INVALID_HANDLE_VALUE && export_info && export_info->handleTypes) + { + get_fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + get_fd_info.pNext = NULL; + get_fd_info.memory = object->dev_mem; + get_fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ if (device->funcs.p_vkGetMemoryFdKHR(device->device, &get_fd_info, &fd) == VK_SUCCESS) + { + object->handle = create_video_resource(fd, handle_export_info ? handle_export_info->name : NULL); + object->access = handle_export_info ? handle_export_info->dwAccess : GENERIC_ALL; + if (handle_export_info && handle_export_info->pAttributes) + object->inherit = handle_export_info->pAttributes->bInheritHandle; + else + object->inherit = FALSE; + close(fd); + }
+ if (object->handle == INVALID_HANDLE_VALUE) + { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto done; + } + }
+ *memory_out = wine_dev_mem_to_handle(object);
You should call WINE_VK_ADD_NON_DISPATCHABLE_MAPPING here, and WINE_VK_REMOVE_HANDLE_MAPPING in vkFreeMemory.
👌
+ }
+ done:
+ if (res != VK_SUCCESS) + { + if (object->dev_mem != VK_NULL_HANDLE) + device->funcs.p_vkFreeMemory(device->device, object->dev_mem, NULL);
No need to check for VK_NULL_HANDLE here, vkFreeMemory already does that.
👌
+ if (fd_import_info.fd != -1) + close(fd_import_info.fd); + if (object->handle != INVALID_HANDLE_VALUE) + NtClose(object->handle); + free(object); + }
- free_VkMemoryAllocateInfo_struct_chain(&allocate_info_dup);
+ return res; +}
+VkResult WINAPI wine_vkGetMemoryWin32HandleKHR(VkDevice device, + const VkMemoryGetWin32HandleInfoKHR *handle_info, HANDLE *handle) +{ + struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle_info->memory); + const VkBaseInStructure *chain;
+ TRACE("%p, %p %p\n", device, handle_info, handle);
+ if (!(dev_mem->handle_types & handle_info->handleType)) + return VK_ERROR_OUT_OF_HOST_MEMORY;
+ if ((chain = handle_info->pNext)) + FIXME("Ignoring a linked structure of type %u.\n", chain->sType);
+ switch(handle_info->handleType) + { + case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: + return !NtDuplicateObject( NtCurrentProcess(), dev_mem->handle, NtCurrentProcess(), handle, dev_mem->access, dev_mem->inherit ? OBJ_INHERIT : 0, 0) ? + VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + default: + return VK_ERROR_UNKNOWN; + } +}
+void WINAPI wine_vkFreeMemory(VkDevice device, VkDeviceMemory handle, const VkAllocationCallbacks *allocator) +{ + struct wine_dev_mem *dev_mem = wine_dev_mem_from_handle(handle);
+ TRACE("%p 0x%s, %p\n", device, wine_dbgstr_longlong(handle), allocator);
+ if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n");
+ device->funcs.p_vkFreeMemory(device->device, dev_mem->dev_mem, NULL); + if (dev_mem->handle != INVALID_HANDLE_VALUE) + NtClose(dev_mem->handle); + free(dev_mem); +}
+VkResult WINAPI wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, + VkExternalMemoryHandleTypeFlagBits type, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *properties) +{ + VkMemoryFdPropertiesKHR fd_props; + VkResult res; + int fd = -1;
+ TRACE("%p %u %p %p\n", device, type, handle, properties);
+ if (type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
VUID-vkGetMemoryWin32HandlePropertiesKHR-handleType-00666 handleType must not be one of the handle types defined as opaque
Ah okay thanks, looks like I can leave this out until the KMT support patch.
+ { + wine_server_handle_to_fd(handle, FILE_READ_DATA, &fd, NULL); + }
+ if (fd == -1) + return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ res = device->funcs.p_vkGetMemoryFdPropertiesKHR(device->device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, fd, &fd_props);
VUID-vkGetMemoryFdPropertiesKHR-handleType-00674 handleType must not be VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR
Ah okay, looks like I misunderstood how these APIs worked, I think I get it now, thanks.
And you have to init the sType and pNext of fd_props.
👌
+ close(fd);
+ if (res != VK_SUCCESS) + return res;
+ properties->sType = VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR; + properties->pNext = NULL;
You shouldn't touch those two, the application will set them.
👌
+ properties->memoryTypeBits = fd_props.memoryTypeBits;
+ return res; +} diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 83dc90ca15e..468ab890dab 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -204,6 +204,28 @@ static inline VkSurfaceKHR wine_surface_to_handle(struct wine_surface *surface) return (VkSurfaceKHR)(uintptr_t)surface; } +struct wine_dev_mem +{ + VkDeviceMemory dev_mem;
+ VkExternalMemoryHandleTypeFlagBits handle_types;
+ BOOL inherit; + DWORD access;
+ HANDLE handle; +};
Wrapping VkDeviceMemory is problematic, because generated thunks won't unwrap handles in struct parameters. With this code functions using these structs will be broken:
VkBindAccelerationStructureMemoryInfoNV, VkBindBufferMemoryInfo, VkBindImageMemoryInfo, VkDeviceMemoryOpaqueCaptureAddressInfo, VkMappedMemoryRange, VkSparseImageMemoryBind, VkSparseMemoryBind
So you either have to either:
- Manually implement all those functions.
- Handle it in make_vulkan.
- Find another way to store data per VkDeviceMemory object, e.g with
VK_EXT_private_data.
I'm against the first option because it's a lot of manually written functions and future spec updates might add new problematic functions. The provisional video extensions add VkVideoBindMemoryKHR for example.
The best option would be the second, if you can make it work, because we currently have some code in vulkan.c that should be generated but has the same problem.
Sounds like something fun to tackle, I'll do that.
Thanks,
Georg Lehmann
+static inline struct wine_dev_mem *wine_dev_mem_from_handle(VkDeviceMemory handle) +{ + return (struct wine_dev_mem *)(uintptr_t)handle; +}
+static inline VkDeviceMemory wine_dev_mem_to_handle(struct wine_dev_mem *dev_mem) +{ + return (VkDeviceMemory)(uintptr_t)dev_mem; +}
BOOL wine_vk_device_extension_supported(const char *name) DECLSPEC_HIDDEN; BOOL wine_vk_instance_extension_supported(const char *name) DECLSPEC_HIDDEN;