[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