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;