From: Elizabeth Figura <zfigura@codeweavers.com> --- dlls/win32u/vulkan.c | 2 + dlls/wined3d/adapter_vk.c | 6 ++ dlls/wined3d/context_vk.c | 28 +++++++++ dlls/wined3d/decoder.c | 56 ++++++++++++++++- dlls/wined3d/unixlib.h | 5 ++ dlls/wined3d/va.c | 124 +++++++++++++++++++++++++++++++++++++- dlls/wined3d/wined3d_vk.h | 6 ++ 7 files changed, 223 insertions(+), 4 deletions(-) diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 5303a8e9bd5..a91f477859c 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -691,6 +691,8 @@ static VkResult convert_device_create_info( struct vulkan_physical_device *physi /* For Direct3D VA support. */ device->extensions.has_VK_EXT_external_memory_dma_buf = physical_device->extensions.has_VK_EXT_external_memory_dma_buf; + device->extensions.has_VK_EXT_image_drm_format_modifier = physical_device->extensions.has_VK_EXT_image_drm_format_modifier; + device->extensions.has_VK_KHR_image_format_list = physical_device->extensions.has_VK_KHR_image_format_list; device->extensions.has_VK_EXT_physical_device_drm = physical_device->extensions.has_VK_EXT_physical_device_drm; if (physical_device->map_placed_align) diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c index a88faf2c9ce..3e349247899 100644 --- a/dlls/wined3d/adapter_vk.c +++ b/dlls/wined3d/adapter_vk.c @@ -2414,6 +2414,12 @@ static bool wined3d_adapter_vk_init_device_extensions(struct wined3d_adapter_vk {VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, ~0u}, {VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, ~0u}, {VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, ~0u}, + {VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, ~0u}, + /* FIXME: We don't use KHR_external_memory_win32 yet, but we do use + * KHR_external_memory_fd for VA support, and we need to enable the + * fd extension by enabling the win32 extension. */ + {VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, ~0u}, + {VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, ~0u}, {VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_API_VERSION_1_1, true}, {VK_KHR_MAINTENANCE2_EXTENSION_NAME, VK_API_VERSION_1_1}, {VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,VK_API_VERSION_1_2}, diff --git a/dlls/wined3d/context_vk.c b/dlls/wined3d/context_vk.c index 119c498c78b..96d8c15ad7c 100644 --- a/dlls/wined3d/context_vk.c +++ b/dlls/wined3d/context_vk.c @@ -1159,6 +1159,30 @@ void wined3d_context_vk_destroy_vk_video_parameters(struct wined3d_context_vk *c o->command_buffer_id = command_buffer_id; } +void wined3d_context_vk_destroy_va_decoder(struct wined3d_context_vk *context_vk, + uint64_t handle, uint64_t command_buffer_id) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + struct wined3d_retired_object_vk *o; + + if (context_vk->completed_command_buffer_id >= command_buffer_id) + { + wined3d_decoder_va_vk_destroy_va_decoder(device_vk, handle); + TRACE("Destroyed VA decoder 0x%s.\n", wine_dbgstr_longlong(handle)); + return; + } + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking VA decoder 0x%s.\n", wine_dbgstr_longlong(handle)); + return; + } + + o->type = WINED3D_RETIRED_DECODER_VA_VK; + o->u.va_decoder = handle; + o->command_buffer_id = command_buffer_id; +} + void wined3d_context_vk_destroy_image(struct wined3d_context_vk *context_vk, struct wined3d_image_vk *image) { wined3d_context_vk_destroy_vk_image(context_vk, image->vk_image, image->command_buffer_id); @@ -1472,6 +1496,10 @@ static void wined3d_context_vk_cleanup_resources(struct wined3d_context_vk *cont o->u.aux_command_buffer.pool, &o->u.aux_command_buffer.buffer); break; + case WINED3D_RETIRED_DECODER_VA_VK: + wined3d_decoder_va_vk_destroy_va_decoder(device_vk, o->u.va_decoder); + break; + default: ERR("Unhandled object type %#x.\n", o->type); break; diff --git a/dlls/wined3d/decoder.c b/dlls/wined3d/decoder.c index ce92568d642..e335adce7cb 100644 --- a/dlls/wined3d/decoder.c +++ b/dlls/wined3d/decoder.c @@ -1362,6 +1362,12 @@ struct wined3d_decoder_va_vk { struct wined3d_decoder d; uint64_t va_decoder; + + struct wined3d_decoder_image_va_vk + { + VkImage image; + uint64_t command_buffer_id; + } images[VA_DECODER_SURFACE_COUNT]; }; static struct wined3d_decoder_va_vk *wined3d_decoder_va_vk(struct wined3d_decoder *decoder) @@ -1395,6 +1401,7 @@ static void wined3d_decoder_va_vk_cs_init(void *object) struct wined3d_adapter_vk *adapter_vk = wined3d_adapter_vk(decoder_va->d.device->adapter); struct wined3d_device_vk *device_vk = wined3d_device_vk(decoder_va->d.device); struct va_decoder_create_vk_params params; + struct wined3d_context_vk *context_vk; HRESULT hr; params.desc = decoder_va->d.desc; @@ -1410,6 +1417,25 @@ static void wined3d_decoder_va_vk_cs_init(void *object) } decoder_va->va_decoder = params.decoder; + + context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); + + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + { + struct wined3d_decoder_image_va_vk *image = &decoder_va->images[i]; + VkImageSubresourceRange vk_range = {0}; + + image->image = params.surfaces[i].image; + + vk_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vk_range.levelCount = 1; + vk_range.layerCount = 1; + wined3d_context_vk_image_barrier(context_vk, wined3d_context_vk_get_command_buffer(context_vk), + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, image->image, &vk_range); + } + + context_release(&context_vk->c); } static HRESULT wined3d_decoder_va_vk_create(struct wined3d_device *device, @@ -1442,16 +1468,40 @@ static HRESULT wined3d_decoder_va_vk_create(struct wined3d_device *device, return WINED3D_OK; } +void wined3d_decoder_va_vk_destroy_va_decoder(struct wined3d_device_vk *device_vk, uint64_t handle) +{ + struct va_decoder_destroy_vk_params params; + + params.device = (uintptr_t)device_vk->vk_device; + params.decoder = handle; + WINE_UNIX_CALL(unix_va_decoder_destroy_vk, ¶ms); +} + static void wined3d_decoder_va_vk_destroy_object(void *object) { struct wined3d_decoder_va_vk *decoder_va = object; - struct va_decoder_destroy_vk_params params; + struct wined3d_context_vk *context_vk; + uint64_t command_buffer_id = 0; TRACE("decoder_va %p.\n", decoder_va); - params.decoder = decoder_va->va_decoder; - WINE_UNIX_CALL(unix_va_decoder_destroy_vk, ¶ms); + context_vk = wined3d_context_vk(context_acquire(decoder_va->d.device, NULL, 0)); + + for (unsigned int i = 0; i < ARRAY_SIZE(decoder_va->images); ++i) + { + struct wined3d_decoder_image_va_vk *image = &decoder_va->images[i]; + + wined3d_context_vk_destroy_vk_image(context_vk, image->image, image->command_buffer_id); + command_buffer_id = max(command_buffer_id, image->command_buffer_id); + } + + /* We probably don't need to wait to destroy the VA decoder, since it's not + * a Vulkan object, but we did allocate Vulkan memory on the Unix side + * which we need to make sure isn't freed until the GPU is done with it. */ + wined3d_context_vk_destroy_va_decoder(context_vk, decoder_va->va_decoder, command_buffer_id); free(decoder_va); + + context_release(&context_vk->c); } static void wined3d_decoder_va_vk_destroy(struct wined3d_decoder *decoder) diff --git a/dlls/wined3d/unixlib.h b/dlls/wined3d/unixlib.h index 9ed19aed67d..ec53d636afb 100644 --- a/dlls/wined3d/unixlib.h +++ b/dlls/wined3d/unixlib.h @@ -46,10 +46,15 @@ struct va_decoder_create_vk_params UINT64 decoder; unsigned int width; unsigned int height; + struct + { + UINT64 image; /* VkImage */ + } surfaces[VA_DECODER_SURFACE_COUNT]; }; struct va_decoder_destroy_vk_params { + UINT64 device; /* VkDevice */ UINT64 decoder; }; diff --git a/dlls/wined3d/va.c b/dlls/wined3d/va.c index 50a300be607..53f9797892b 100644 --- a/dlls/wined3d/va.c +++ b/dlls/wined3d/va.c @@ -48,6 +48,7 @@ struct va_decoder VAConfigID config; VAContextID context; VASurfaceID surfaces[VA_DECODER_SURFACE_COUNT]; + VkDeviceMemory vk_memory[VA_DECODER_SURFACE_COUNT]; }; static NTSTATUS open_va_display(UINT64 handle, VADisplay *ret_display, int *ret_fd) @@ -60,7 +61,9 @@ static NTSTATUS open_va_display(UINT64 handle, VADisplay *ret_display, int *ret_ VAStatus status; char *path; - if (!device->extensions.has_VK_EXT_physical_device_drm) + if (!device->extensions.has_VK_EXT_external_memory_dma_buf + || !device->extensions.has_VK_EXT_image_drm_format_modifier + || !device->extensions.has_VK_EXT_physical_device_drm) return E_NOTIMPL; properties.pNext = &drm_properties; @@ -138,16 +141,30 @@ static NTSTATUS va_get_profiles_vk(void *args) static NTSTATUS va_decoder_create_vk(void *args) { struct va_decoder_create_vk_params *params = args; + VkImageDrmFormatModifierExplicitCreateInfoEXT drm_format_desc = {.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT}; + VkExternalMemoryImageCreateInfo external_image_desc = {.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO}; + VkMemoryDedicatedAllocateInfo dedicated_desc = {.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO}; + VkMemoryDedicatedRequirements dedicated_reqs = {.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS}; + VkImageMemoryRequirementsInfo2 reqs_desc = {.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2}; + VkMemoryFdPropertiesKHR fd_properties = {.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR}; + VkImportMemoryFdInfoKHR fd_desc = {.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR}; + VkMemoryRequirements2 memory_reqs = {.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2}; + VkMemoryAllocateInfo memory_desc = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; + struct vulkan_device *device = vulkan_device_from_handle((VkDevice)params->device); + VkImageCreateInfo image_desc = {.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + VADRMPRIMESurfaceDescriptor descriptor; VASurfaceAttrib surface_attribs[1]; VAConfigAttrib config_attribs[1]; unsigned int format, fourcc; struct va_decoder *decoder; VAStatus status; + VkResult vr; if (params->desc.output_format == WINED3DFMT_NV12_PLANAR) { format = VA_RT_FORMAT_YUV420; fourcc = VA_FOURCC_NV12; + image_desc.format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM; } else { @@ -199,6 +216,108 @@ static NTSTATUS va_decoder_create_vk(void *args) goto fail; } + image_desc.imageType = VK_IMAGE_TYPE_2D; + image_desc.extent.width = params->width; + image_desc.extent.height = params->height; + image_desc.extent.depth = 1; + image_desc.mipLevels = 1; + image_desc.arrayLayers = 1; + image_desc.samples = 1; + image_desc.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + image_desc.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + image_desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + image_desc.pNext = &external_image_desc; + external_image_desc.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; + + external_image_desc.pNext = &drm_format_desc; + + memory_desc.pNext = &fd_desc; + fd_desc.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; + + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + { + VkSubresourceLayout plane_layouts[4] = {0}; + VkImage image; + DWORD index; + + if ((status = vaExportSurfaceHandle(decoder->display, decoder->surfaces[i], VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + VA_EXPORT_SURFACE_READ_ONLY | VA_EXPORT_SURFACE_COMPOSED_LAYERS, &descriptor))) + ERR("Failed to export, error %#x.\n", status); + + if (descriptor.num_objects != 1) + FIXME("Unhandled object count %u.\n", descriptor.num_objects); + /* We passed VA_EXPORT_SURFACE_COMPOSED_LAYERS; this shouldn't happen. */ + if (descriptor.num_layers != 1) + ERR("Unexpected layer count %u.\n", descriptor.num_layers); + + if ((vr = device->p_vkGetMemoryFdPropertiesKHR(device->host.device, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, descriptor.objects[0].fd, &fd_properties)) != VK_SUCCESS) + { + ERR("Failed to get fd properties, vr %#x.\n", vr); + continue; + } + + if (!BitScanForward(&index, fd_properties.memoryTypeBits)) + { + ERR("No valid type bits.\n"); + return E_NOTIMPL; + } + memory_desc.memoryTypeIndex = index; + + memory_desc.allocationSize = descriptor.objects[0].size; + + for (unsigned int j = 0; j < descriptor.layers[0].num_planes; ++j) + { + plane_layouts[j].offset = descriptor.layers[0].offset[j]; + plane_layouts[j].rowPitch = descriptor.layers[0].pitch[j]; + } + drm_format_desc.drmFormatModifier = descriptor.objects[0].drm_format_modifier; + drm_format_desc.drmFormatModifierPlaneCount = descriptor.layers[0].num_planes; + drm_format_desc.pPlaneLayouts = plane_layouts; + + /* Images aren't wrapped; we can just call the host version. */ + if ((vr = device->p_vkCreateImage(device->host.device, &image_desc, NULL, &image)) != VK_SUCCESS) + { + ERR("Failed to create image, vr %#x.\n", vr); + continue; + } + + if (device->extensions.has_VK_KHR_dedicated_allocation) + { + memory_reqs.pNext = &dedicated_reqs; + reqs_desc.image = image; + device->p_vkGetImageMemoryRequirements2(device->host.device, &reqs_desc, &memory_reqs); + + if (dedicated_reqs.prefersDedicatedAllocation) + { + fd_desc.pNext = &dedicated_desc; + dedicated_desc.image = image; + } + } + + fd_desc.fd = descriptor.objects[0].fd; + + if ((vr = device->p_vkAllocateMemory(device->host.device, + &memory_desc, NULL, &decoder->vk_memory[i])) != VK_SUCCESS) + { + ERR("Failed to allocate memory, vr %d.\n", vr); + device->p_vkDestroyImage(device->host.device, image, NULL); + continue; + } + + if ((vr = device->p_vkBindImageMemory(device->host.device, image, decoder->vk_memory[i], 0)) != VK_SUCCESS) + { + ERR("Failed to bind memory, vr %d.\n", vr); + device->p_vkFreeMemory(device->host.device, decoder->vk_memory[i], NULL); + device->p_vkDestroyImage(device->host.device, image, NULL); + continue; + } + + params->surfaces[i].image = image; + } + TRACE("Created VA decoder %p.\n", decoder); params->decoder = (uintptr_t)decoder; @@ -213,7 +332,10 @@ static NTSTATUS va_decoder_destroy_vk(void *args) { struct va_decoder_destroy_vk_params *params = args; struct va_decoder *decoder = (struct va_decoder *)(uintptr_t)params->decoder; + struct vulkan_device *device = vulkan_device_from_handle((VkDevice)params->device); + for (unsigned int i = 0; i < ARRAY_SIZE(decoder->vk_memory); ++i) + device->p_vkFreeMemory(device->host.device, decoder->vk_memory[i], NULL); vaDestroyContext(decoder->display, decoder->context); vaDestroySurfaces(decoder->display, decoder->surfaces, VA_DECODER_SURFACE_COUNT); vaDestroyConfig(decoder->display, decoder->config); diff --git a/dlls/wined3d/wined3d_vk.h b/dlls/wined3d/wined3d_vk.h index 937a958760c..434e9b5e013 100644 --- a/dlls/wined3d/wined3d_vk.h +++ b/dlls/wined3d/wined3d_vk.h @@ -475,6 +475,7 @@ enum wined3d_retired_object_type_vk WINED3D_RETIRED_VIDEO_SESSION_VK, WINED3D_RETIRED_VIDEO_PARAMETERS_VK, WINED3D_RETIRED_AUX_COMMAND_BUFFER_VK, + WINED3D_RETIRED_DECODER_VA_VK, }; struct wined3d_retired_object_vk @@ -512,6 +513,7 @@ struct wined3d_retired_object_vk struct wined3d_aux_command_pool_vk *pool; struct wined3d_aux_command_buffer_vk buffer; } aux_command_buffer; + uint64_t va_decoder; } u; uint64_t command_buffer_id; }; @@ -758,6 +760,8 @@ void wined3d_context_vk_destroy_bo(struct wined3d_context_vk *context_vk, const struct wined3d_bo_vk *bo); void wined3d_context_vk_destroy_image(struct wined3d_context_vk *context_vk, struct wined3d_image_vk *image_vk); +void wined3d_context_vk_destroy_va_decoder(struct wined3d_context_vk *context_vk, + uint64_t handle, uint64_t command_buffer_id); void wined3d_context_vk_destroy_vk_buffer_view(struct wined3d_context_vk *context_vk, VkBufferView vk_view, uint64_t command_buffer_id); void wined3d_context_vk_destroy_vk_framebuffer(struct wined3d_context_vk *context_vk, @@ -1150,6 +1154,8 @@ HRESULT wined3d_decoder_output_view_vk_init(struct wined3d_decoder_output_view_v const struct wined3d_view_desc *desc, struct wined3d_texture *texture, void *parent, const struct wined3d_parent_ops *parent_ops); +void wined3d_decoder_va_vk_destroy_va_decoder(struct wined3d_device_vk *device_vk, uint64_t handle); + struct wined3d_swapchain_vk { struct wined3d_swapchain s; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11535