[RFC PATCH 4/4] winevulkan: Add support for KMT handles in external_memory_win32.

Derek Lesho dlesho at codeweavers.com
Mon May 3 11:46:57 CDT 2021


Signed-off-by: Derek Lesho <dlesho at 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;
-- 
2.31.1




More information about the wine-devel mailing list