[PATCH v3 0/7] MR11535: wined3d: VA hardware decode support.
-- v3: wined3d: Implement VA H.264 decoding. wined3d: Share VA surfaces with Vulkan. wined3d: Create a VA decoding session. wined3d: Query VA for H.264 support. https://gitlab.winehq.org/wine/wine/-/merge_requests/11535
From: Elizabeth Figura <zfigura@codeweavers.com> --- dlls/wined3d/adapter_vk.c | 8 +++- dlls/wined3d/decoder.c | 67 ++++++++++++++++++++++++++++++++++ dlls/wined3d/wined3d_main.c | 14 +++++++ dlls/wined3d/wined3d_private.h | 9 +++++ 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c index 7f68edb3651..a88faf2c9ce 100644 --- a/dlls/wined3d/adapter_vk.c +++ b/dlls/wined3d/adapter_vk.c @@ -2608,7 +2608,13 @@ static BOOL wined3d_adapter_vk_init(struct wined3d_adapter_vk *adapter_vk, adapter->fragment_pipe = wined3d_spirv_fragment_pipe_init_vk(); adapter->misc_state_template = misc_state_template_vk; adapter->shader_backend = wined3d_spirv_shader_backend_init_vk(); - adapter->decoder_ops = &wined3d_decoder_vk_ops; + + if (wined3d_settings.decoder_backend == WINED3D_DECODER_BACKEND_VA + || (wined3d_settings.decoder_backend == WINED3D_DECODER_BACKEND_AUTO + && !vk_info->supported[WINED3D_VK_KHR_VIDEO_DECODE_H264])) + adapter->decoder_ops = &wined3d_decoder_va_vk_ops; + else + adapter->decoder_ops = &wined3d_decoder_vk_ops; wined3d_adapter_vk_init_d3d_info(adapter_vk, wined3d_creation_flags); diff --git a/dlls/wined3d/decoder.c b/dlls/wined3d/decoder.c index ef37fe05f0f..18f987b6ed6 100644 --- a/dlls/wined3d/decoder.c +++ b/dlls/wined3d/decoder.c @@ -1354,6 +1354,73 @@ const struct wined3d_decoder_ops wined3d_decoder_vk_ops = .decode = wined3d_decoder_vk_decode, }; +struct wined3d_decoder_va_vk +{ + struct wined3d_decoder d; +}; + +static struct wined3d_decoder_va_vk *wined3d_decoder_va_vk(struct wined3d_decoder *decoder) +{ + return CONTAINING_RECORD(decoder, struct wined3d_decoder_va_vk, d); +} + +static void wined3d_decoder_va_vk_get_profiles(struct wined3d_adapter *adapter, unsigned int *count, GUID *profiles) +{ + *count = 0; +} + +static HRESULT wined3d_decoder_va_vk_create(struct wined3d_device *device, + const struct wined3d_decoder_desc *desc, struct wined3d_decoder **decoder) +{ + struct wined3d_decoder_va_vk *object; + HRESULT hr; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + if (FAILED(hr = wined3d_decoder_init(&object->d, device, desc))) + { + free(object); + return hr; + } + + TRACE("Created decoder %p.\n", object); + *decoder = &object->d; + + return WINED3D_OK; +} + +static void wined3d_decoder_va_vk_destroy_object(void *object) +{ + struct wined3d_decoder_va_vk *decoder_va = object; + + TRACE("decoder_va %p.\n", decoder_va); + + free(decoder_va); +} + +static void wined3d_decoder_va_vk_destroy(struct wined3d_decoder *decoder) +{ + struct wined3d_decoder_va_vk *decoder_va = wined3d_decoder_va_vk(decoder); + + wined3d_decoder_cleanup(&decoder_va->d); + wined3d_cs_destroy_object(decoder->device->cs, wined3d_decoder_va_vk_destroy_object, decoder_va); +} + +static void wined3d_decoder_va_vk_decode(struct wined3d_context *context, + struct wined3d_decoder *decoder, struct wined3d_decoder_output_view *output_view, + unsigned int bitstream_size, unsigned int slice_control_size) +{ + FIXME("Not implemented.\n"); +} + +const struct wined3d_decoder_ops wined3d_decoder_va_vk_ops = +{ + .get_profiles = wined3d_decoder_va_vk_get_profiles, + .create = wined3d_decoder_va_vk_create, + .destroy = wined3d_decoder_va_vk_destroy, + .decode = wined3d_decoder_va_vk_decode, +}; + struct wined3d_resource * CDECL wined3d_decoder_get_buffer( struct wined3d_decoder *decoder, enum wined3d_decoder_buffer_type type) { diff --git a/dlls/wined3d/wined3d_main.c b/dlls/wined3d/wined3d_main.c index 91d8dd567ff..976ea72d69c 100644 --- a/dlls/wined3d/wined3d_main.c +++ b/dlls/wined3d/wined3d_main.c @@ -129,6 +129,7 @@ struct wined3d_settings wined3d_settings = .max_sm_cs = UINT_MAX, .renderer = WINED3D_RENDERER_AUTO, .shader_backend = WINED3D_SHADER_BACKEND_AUTO, + .decoder_backend = WINED3D_DECODER_BACKEND_AUTO, }; enum wined3d_renderer CDECL wined3d_get_renderer(void) @@ -363,6 +364,19 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL) wined3d_settings.shader_backend = WINED3D_SHADER_BACKEND_GLSL; } } + if (!get_config_key(hkey, appkey, env, "decoder_backend", buffer, size)) + { + if (!stricmp(buffer, "vulkan")) + { + ERR_(winediag)("Using the Vulkan video decoder backend.\n"); + wined3d_settings.decoder_backend = WINED3D_DECODER_BACKEND_VULKAN; + } + else if (!stricmp(buffer, "va")) + { + ERR_(winediag)("Using the VA video decoder backend.\n"); + wined3d_settings.decoder_backend = WINED3D_DECODER_BACKEND_VA; + } + } if (!get_config_key_dword(hkey, appkey, env, "VideoPciDeviceID", &tmpvalue)) { int pci_device_id = tmpvalue; diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 9537670f7c5..1be740c94b3 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -457,6 +457,13 @@ enum wined3d_shader_backend WINED3D_SHADER_BACKEND_GLSL_VKD3D, }; +enum wined3d_decoder_backend +{ + WINED3D_DECODER_BACKEND_AUTO, + WINED3D_DECODER_BACKEND_VULKAN, + WINED3D_DECODER_BACKEND_VA, +}; + #define WINED3D_CSMT_ENABLE 0x00000001 #define WINED3D_CSMT_SERIALIZE 0x00000002 @@ -482,6 +489,7 @@ struct wined3d_settings unsigned int max_sm_cs; enum wined3d_renderer renderer; enum wined3d_shader_backend shader_backend; + enum wined3d_decoder_backend decoder_backend; bool check_float_constants; bool cb_access_map_w; bool ffp_hlsl; @@ -4520,6 +4528,7 @@ struct wined3d_decoder_ops }; extern const struct wined3d_decoder_ops wined3d_decoder_vk_ops; +extern const struct wined3d_decoder_ops wined3d_decoder_va_vk_ops; extern const struct wined3d_decoder_ops wined3d_null_decoder_ops; /* DirectDraw utility functions */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11535
From: Elizabeth Figura <zfigura@codeweavers.com> --- dlls/wined3d/decoder.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dlls/wined3d/decoder.c b/dlls/wined3d/decoder.c index 18f987b6ed6..6a7ca54554f 100644 --- a/dlls/wined3d/decoder.c +++ b/dlls/wined3d/decoder.c @@ -84,7 +84,7 @@ static bool is_supported_codec(struct wined3d_adapter *adapter, const GUID *code } static HRESULT wined3d_decoder_init(struct wined3d_decoder *decoder, - struct wined3d_device *device, const struct wined3d_decoder_desc *desc) + struct wined3d_device *device, const struct wined3d_decoder_desc *desc, bool gpu_bitstream) { HRESULT hr; @@ -128,9 +128,12 @@ static HRESULT wined3d_decoder_init(struct wined3d_decoder *decoder, * is at most 1 byte). AMD makes it larger than that. * Go with the smaller of the two. */ buffer_desc.byte_width = desc->width * desc->height; - buffer_desc.bind_flags = WINED3D_BIND_DECODER_SRC; - buffer_desc.access = WINED3D_RESOURCE_ACCESS_GPU | WINED3D_RESOURCE_ACCESS_MAP_W; - buffer_desc.usage = WINED3DUSAGE_DYNAMIC; + if (gpu_bitstream) + { + buffer_desc.bind_flags = WINED3D_BIND_DECODER_SRC; + buffer_desc.access = WINED3D_RESOURCE_ACCESS_GPU | WINED3D_RESOURCE_ACCESS_MAP_W; + buffer_desc.usage = WINED3DUSAGE_DYNAMIC; + } if (FAILED(hr = wined3d_buffer_create(device, &buffer_desc, NULL, NULL, &wined3d_null_parent_ops, &decoder->bitstream))) @@ -616,7 +619,7 @@ static HRESULT wined3d_decoder_vk_create(struct wined3d_device *device, if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; - if (FAILED(hr = wined3d_decoder_init(&object->d, device, desc))) + if (FAILED(hr = wined3d_decoder_init(&object->d, device, desc, true))) { free(object); return hr; @@ -1377,7 +1380,7 @@ static HRESULT wined3d_decoder_va_vk_create(struct wined3d_device *device, if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; - if (FAILED(hr = wined3d_decoder_init(&object->d, device, desc))) + if (FAILED(hr = wined3d_decoder_init(&object->d, device, desc, false))) { free(object); return hr; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11535
From: Elizabeth Figura <zfigura@codeweavers.com> --- dlls/win32u/vulkan.c | 4 ++++ dlls/winevulkan/make_vulkan | 2 +- include/wine/vulkan.h | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 6feb6a0416e..5303a8e9bd5 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -689,6 +689,10 @@ static VkResult convert_device_create_info( struct vulkan_physical_device *physi device->extensions.has_VK_KHR_external_fence_win32 = 0; device->extensions.has_VK_KHR_external_semaphore_win32 = 0; + /* 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_physical_device_drm = physical_device->extensions.has_VK_EXT_physical_device_drm; + if (physical_device->map_placed_align) { VkPhysicalDeviceMapMemoryPlacedFeaturesEXT *map_placed_features; diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index f8375c96e32..05af73601da 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -103,7 +103,6 @@ UNSUPPORTED_EXTENSIONS = { # Extensions for other platforms "VK_EXT_metal_objects", - "VK_EXT_physical_device_drm", "VK_GOOGLE_surfaceless_query", "VK_SEC_amigo_profiling", # Angle specific. @@ -129,6 +128,7 @@ UNEXPOSED_EXTENSIONS = { "VK_EXT_external_memory_dma_buf", "VK_EXT_image_drm_format_modifier", "VK_EXT_metal_surface", + "VK_EXT_physical_device_drm", "VK_KHR_external_fence_fd", "VK_KHR_external_memory_fd", "VK_EXT_external_memory_metal", diff --git a/include/wine/vulkan.h b/include/wine/vulkan.h index 9b00aeeec04..a53df3e8230 100644 --- a/include/wine/vulkan.h +++ b/include/wine/vulkan.h @@ -479,6 +479,8 @@ typedef void* VkRemoteAddressNV; #define VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_SPEC_VERSION 1 #define VK_EXT_PCI_BUS_INFO_EXTENSION_NAME "VK_EXT_pci_bus_info" #define VK_EXT_PCI_BUS_INFO_SPEC_VERSION 2 +#define VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME "VK_EXT_physical_device_drm" +#define VK_EXT_PHYSICAL_DEVICE_DRM_SPEC_VERSION 1 #define VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME "VK_EXT_pipeline_creation_cache_control" #define VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_SPEC_VERSION 3 #define VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME "VK_EXT_pipeline_creation_feedback" @@ -6544,6 +6546,7 @@ typedef enum VkStructureType VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT = 1000352000, VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT = 1000352001, VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT = 1000352002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT = 1000353000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ADDRESS_BINDING_REPORT_FEATURES_EXT = 1000354000, VK_STRUCTURE_TYPE_DEVICE_ADDRESS_BINDING_CALLBACK_DATA_EXT = 1000354001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT = 1000355000, @@ -15630,6 +15633,18 @@ typedef struct VkPhysicalDeviceDriverProperties } VkPhysicalDeviceDriverProperties; typedef VkPhysicalDeviceDriverProperties VkPhysicalDeviceDriverPropertiesKHR; +typedef struct VkPhysicalDeviceDrmPropertiesEXT +{ + VkStructureType sType; + void *pNext; + VkBool32 hasPrimary; + VkBool32 hasRender; + int64_t primaryMajor; + int64_t primaryMinor; + int64_t renderMajor; + int64_t renderMinor; +} VkPhysicalDeviceDrmPropertiesEXT; + typedef struct VkPhysicalDeviceDynamicRenderingFeatures { VkStructureType sType; @@ -23752,6 +23767,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl USE_VK_EXT(VK_EXT_external_memory_metal) \ USE_VK_EXT(VK_EXT_image_drm_format_modifier) \ USE_VK_EXT(VK_EXT_map_memory_placed) \ + USE_VK_EXT(VK_EXT_physical_device_drm) \ USE_VK_EXT(VK_KHR_external_fence_fd) \ USE_VK_EXT(VK_KHR_external_memory_fd) \ USE_VK_EXT(VK_KHR_external_semaphore_fd) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11535
From: Elizabeth Figura <zfigura@codeweavers.com> --- configure.ac | 17 ++++ dlls/wined3d/Makefile.in | 4 + dlls/wined3d/decoder.c | 24 +++++ dlls/wined3d/unixlib.h | 44 +++++++++ dlls/wined3d/va.c | 191 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 280 insertions(+) create mode 100644 dlls/wined3d/unixlib.h create mode 100644 dlls/wined3d/va.c diff --git a/configure.ac b/configure.ac index ee0b1c483d4..2a953798099 100644 --- a/configure.ac +++ b/configure.ac @@ -59,6 +59,7 @@ AC_ARG_WITH(sdl, AS_HELP_STRING([--without-sdl],[do not use SDL])) AC_ARG_WITH(udev, AS_HELP_STRING([--without-udev],[do not use udev (plug and play support)])) AC_ARG_WITH(usb, AS_HELP_STRING([--without-usb],[do not use the libusb library])) AC_ARG_WITH(v4l2, AS_HELP_STRING([--without-v4l2],[do not use v4l2 (video capture)])) +AC_ARG_WITH(va, AS_HELP_STRING([--without-va],[do not use VA-API (hardware decode)])) AC_ARG_WITH(vulkan, AS_HELP_STRING([--without-vulkan],[do not use Vulkan])) AC_ARG_WITH(wayland, AS_HELP_STRING([--without-wayland],[do not build the Wayland driver])) AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]), @@ -1618,6 +1619,22 @@ fi WINE_NOTICE_WITH(v4l2,[test "x$ac_cv_lib_soname_v4l2" = "x"], [libv4l2 ${notice_platform}development files not found.]) +dnl **** Check for libva **** +if test "x$with_va" != "xno" +then + WINE_PACKAGE_FLAGS(VA,[libva libva-drm],[-lva-drm -lva],,, + [AC_CHECK_HEADERS(va/va_drm.h) + if test "$ac_cv_header_va_va_drm_h" = "yes" + then + WINE_CHECK_SONAME(va-drm, vaGetDisplayDRM,,[VA_CFLAGS=""]) + WINE_CHECK_SONAME(va, vaInitialize,,[VA_CFLAGS=""]) + else + VA_CFLAGS="" + fi]) +fi +WINE_NOTICE_WITH(va,[test "$ac_cv_lib_soname_va_drm" != "yes" -o "$ac_cv_lib_soname_va" != "yes"], + [libva-drm ${notice_platform}development files not found, VA video acceleration won't be supported.]) + dnl **** Check for libgphoto2 **** if test "x$with_gphoto" != "xno" then diff --git a/dlls/wined3d/Makefile.in b/dlls/wined3d/Makefile.in index bbf8f260b02..39ed68ecb9c 100644 --- a/dlls/wined3d/Makefile.in +++ b/dlls/wined3d/Makefile.in @@ -1,7 +1,10 @@ MODULE = wined3d.dll +UNIXLIB = wined3d.so IMPORTLIB = wined3d IMPORTS = $(VKD3D_PE_LIBS) dxguid opengl32 user32 gdi32 advapi32 EXTRAINCL = $(VKD3D_PE_CFLAGS) +UNIX_CFLAGS = $(VA_CFLAGS) +UNIX_LIBS = -lwin32u VER_FILEDESCRIPTION_STR = "Wine D3D" @@ -36,6 +39,7 @@ SOURCES = \ texture_gl.c \ texture_vk.c \ utils.c \ + va.c \ vertexdeclaration.c \ view.c \ wined3d_main.c diff --git a/dlls/wined3d/decoder.c b/dlls/wined3d/decoder.c index 6a7ca54554f..2cb1c5fdbfb 100644 --- a/dlls/wined3d/decoder.c +++ b/dlls/wined3d/decoder.c @@ -18,6 +18,7 @@ #include "wined3d_private.h" #include "wined3d_vk.h" +#include "unixlib.h" WINE_DEFAULT_DEBUG_CHANNEL(d3d); @@ -1369,15 +1370,38 @@ static struct wined3d_decoder_va_vk *wined3d_decoder_va_vk(struct wined3d_decode static void wined3d_decoder_va_vk_get_profiles(struct wined3d_adapter *adapter, unsigned int *count, GUID *profiles) { + struct va_get_profiles_vk_params params; + NTSTATUS status; + *count = 0; + + /* We are under wined3d_mutex, so this is thread safe. */ + if ((status = __wine_init_unix_call())) + { + WARN("Failed to load Unix library, status %#lx.\n", status); + return; + } + + params.physical_device = (uintptr_t)wined3d_adapter_vk(adapter)->physical_device; + params.count = (uintptr_t)count; + params.profiles = (uintptr_t)profiles; + WINE_UNIX_CALL(unix_va_get_profiles_vk, ¶ms); } static HRESULT wined3d_decoder_va_vk_create(struct wined3d_device *device, const struct wined3d_decoder_desc *desc, struct wined3d_decoder **decoder) { struct wined3d_decoder_va_vk *object; + NTSTATUS status; HRESULT hr; + /* We are under wined3d_mutex, so this is thread safe. */ + if ((status = __wine_init_unix_call())) + { + WARN("Failed to load Unix library, status %#lx.\n", status); + return E_FAIL; + } + if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; if (FAILED(hr = wined3d_decoder_init(&object->d, device, desc, false))) diff --git a/dlls/wined3d/unixlib.h b/dlls/wined3d/unixlib.h new file mode 100644 index 00000000000..45630418d08 --- /dev/null +++ b/dlls/wined3d/unixlib.h @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Elizabeth Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINED3D_UNIXLIB_H +#define __WINE_WINED3D_UNIXLIB_H + +/* We cannot put these definitions in wined3d_private.h, because + * wined3d_private.h uses vkd3d-shader definitions, and we cannot access + * vkd3d_shader.h from the Unix side. */ + +#define COBJMACROS +#include <stdint.h> +#include "objbase.h" +#include "wine/wined3d.h" +#include "wine/unixlib.h" + +struct va_get_profiles_vk_params +{ + UINT64 physical_device; /* VkPhysicalDevice */ + UINT64 count; /* unsigned int * */ + UINT64 profiles; /* GUID * */ +}; + +enum unix_funcs +{ + unix_va_get_profiles_vk, +}; + +#endif diff --git a/dlls/wined3d/va.c b/dlls/wined3d/va.c new file mode 100644 index 00000000000..949ca22381e --- /dev/null +++ b/dlls/wined3d/va.c @@ -0,0 +1,191 @@ +/* + * Copyright 2024 Elizabeth Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#if defined(SONAME_LIBVA) && defined(SONAME_LIBVA_DRM) + +#include "initguid.h" +#include "unixlib.h" +#include "dxva.h" +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <unistd.h> +#include <va/va.h> +#include <va/va_drm.h> +#include <va/va_drmcommon.h> +#include "ntgdi.h" +#include "wine/vulkan_driver.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); + +struct va_context +{ + void *libva_handle, *libva_drm_handle; +#define MAKE_FUNCPTR(f) typeof(f) *f + MAKE_FUNCPTR(vaGetDisplayDRM); + MAKE_FUNCPTR(vaInitialize); + MAKE_FUNCPTR(vaMaxNumProfiles); + MAKE_FUNCPTR(vaQueryConfigProfiles); + MAKE_FUNCPTR(vaTerminate); +#undef MAKE_FUNCPTR + + VADisplay display; + int fd; +}; + +static void close_va_display(struct va_context *ctx) +{ + if (ctx->fd != -1) + close(ctx->fd); + ctx->vaTerminate(ctx->display); + dlclose(ctx->libva_drm_handle); + dlclose(ctx->libva_handle); +} + +static NTSTATUS open_va_display(UINT64 handle, struct va_context *ctx) +{ + VkPhysicalDeviceDrmPropertiesEXT drm_properties = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT}; + VkPhysicalDeviceProperties2 properties = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2}; + const struct vulkan_physical_device *device = vulkan_physical_device_from_handle((VkPhysicalDevice)handle); + int major, minor; + VAStatus status; + char *path; + + ctx->display = 0; + ctx->fd = -1; + + if (!device->extensions.has_VK_EXT_physical_device_drm) + return E_NOTIMPL; + + if (!(ctx->libva_handle = dlopen(SONAME_LIBVA, RTLD_NOW))) + return E_NOTIMPL; + if (!(ctx->libva_drm_handle = dlopen(SONAME_LIBVA_DRM, RTLD_NOW))) + { + dlclose(ctx->libva_handle); + return E_NOTIMPL; + } + +#define LOAD_FUNCPTR(f) \ + if (!(ctx->f = dlsym(ctx->libva_handle, #f))) \ + { \ + ERR("Failed to load function %s.\n", #f); \ + goto fail; \ + } + LOAD_FUNCPTR(vaInitialize); + LOAD_FUNCPTR(vaMaxNumProfiles); + LOAD_FUNCPTR(vaQueryConfigProfiles); + LOAD_FUNCPTR(vaTerminate); +#undef LOAD_FUNCPTR + + if (!(ctx->vaGetDisplayDRM = dlsym(ctx->libva_drm_handle, "vaGetDisplayDRM"))) + { + ERR("Failed to load function vaGetDisplayDRM.\n"); + goto fail; + } + + properties.pNext = &drm_properties; + device->instance->p_vkGetPhysicalDeviceProperties2KHR(device->host.physical_device, &properties); + + if (drm_properties.hasPrimary) + { + asprintf(&path, "/dev/dri/card%"PRIu64, drm_properties.primaryMinor); + } + else if (drm_properties.hasRender) + { + asprintf(&path, "/dev/dri/renderD%"PRIu64, drm_properties.primaryMinor); + } + else + { + ERR("Vulkan device has neither a primary nor render node.\n"); + goto fail; + } + + if ((ctx->fd = open(path, O_RDWR | O_CLOEXEC)) < 0) + { + ERR("Failed to open %s: %s\n", path, strerror(errno)); + free(path); + goto fail; + } + free(path); + + ctx->display = ctx->vaGetDisplayDRM(ctx->fd); + if ((status = ctx->vaInitialize(ctx->display, &major, &minor)) != VA_STATUS_SUCCESS) + { + ERR("Failed to initialize VA, error %#x.\n", status); + goto fail; + } + + return S_OK; + +fail: + close_va_display(ctx); + return E_NOTIMPL; +} + +static NTSTATUS va_get_profiles_vk(void *args) +{ + struct va_get_profiles_vk_params *params = args; + unsigned int *count = (unsigned int *)(uintptr_t)params->count; + GUID *profiles = (GUID *)(uintptr_t)params->profiles; + VAProfile *va_profiles; + struct va_context ctx; + NTSTATUS status; + int max_count; + + if ((status = open_va_display(params->physical_device, &ctx))) + return status; + + max_count = ctx.vaMaxNumProfiles(ctx.display); + va_profiles = malloc(max_count * sizeof(*va_profiles)); + ctx.vaQueryConfigProfiles(ctx.display, va_profiles, &max_count); + + for (int i = 0; i < max_count; ++i) + { + if (va_profiles[i] == VAProfileH264High) + { + profiles[(*count)++] = DXVA_ModeH264_VLD_NoFGT; + /* FIXME: Native GPUs also support DXVA2_ModeH264_VLD_Stereo_NoFGT + * and DXVA2_ModeH264_VLD_Stereo_Progressive_NoFGT. */ + } + } + + free(va_profiles); + close_va_display(&ctx); + return S_OK; +} + +const unixlib_entry_t __wine_unix_call_funcs[] = +{ + [unix_va_get_profiles_vk] = va_get_profiles_vk, +}; + +const unixlib_entry_t __wine_unix_call_wow64_funcs[] = +{ + [unix_va_get_profiles_vk] = va_get_profiles_vk, +}; + +#endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11535
From: Elizabeth Figura <zfigura@codeweavers.com> --- dlls/wined3d/decoder.c | 29 +++++++++++ dlls/wined3d/unixlib.h | 19 +++++++ dlls/wined3d/va.c | 111 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) diff --git a/dlls/wined3d/decoder.c b/dlls/wined3d/decoder.c index 2cb1c5fdbfb..ce92568d642 100644 --- a/dlls/wined3d/decoder.c +++ b/dlls/wined3d/decoder.c @@ -1361,6 +1361,7 @@ const struct wined3d_decoder_ops wined3d_decoder_vk_ops = struct wined3d_decoder_va_vk { struct wined3d_decoder d; + uint64_t va_decoder; }; static struct wined3d_decoder_va_vk *wined3d_decoder_va_vk(struct wined3d_decoder *decoder) @@ -1388,6 +1389,29 @@ static void wined3d_decoder_va_vk_get_profiles(struct wined3d_adapter *adapter, WINE_UNIX_CALL(unix_va_get_profiles_vk, ¶ms); } +static void wined3d_decoder_va_vk_cs_init(void *object) +{ + struct wined3d_decoder_va_vk *decoder_va = 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; + HRESULT hr; + + params.desc = decoder_va->d.desc; + params.physical_device = (uintptr_t)adapter_vk->physical_device; + params.device = (uintptr_t)device_vk->vk_device; + params.width = decoder_va->d.width; + params.height = decoder_va->d.height; + + if (FAILED(hr = WINE_UNIX_CALL(unix_va_decoder_create_vk, ¶ms))) + { + ERR("Failed to initialize decoder, hr %#lx.\n", hr); + return; + } + + decoder_va->va_decoder = params.decoder; +} + static HRESULT wined3d_decoder_va_vk_create(struct wined3d_device *device, const struct wined3d_decoder_desc *desc, struct wined3d_decoder **decoder) { @@ -1410,6 +1434,8 @@ static HRESULT wined3d_decoder_va_vk_create(struct wined3d_device *device, return hr; } + wined3d_cs_init_object(device->cs, wined3d_decoder_va_vk_cs_init, object); + TRACE("Created decoder %p.\n", object); *decoder = &object->d; @@ -1419,9 +1445,12 @@ static HRESULT wined3d_decoder_va_vk_create(struct wined3d_device *device, 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; TRACE("decoder_va %p.\n", decoder_va); + params.decoder = decoder_va->va_decoder; + WINE_UNIX_CALL(unix_va_decoder_destroy_vk, ¶ms); free(decoder_va); } diff --git a/dlls/wined3d/unixlib.h b/dlls/wined3d/unixlib.h index 45630418d08..9ed19aed67d 100644 --- a/dlls/wined3d/unixlib.h +++ b/dlls/wined3d/unixlib.h @@ -29,6 +29,8 @@ #include "wine/wined3d.h" #include "wine/unixlib.h" +#define VA_DECODER_SURFACE_COUNT 17 + struct va_get_profiles_vk_params { UINT64 physical_device; /* VkPhysicalDevice */ @@ -36,9 +38,26 @@ struct va_get_profiles_vk_params UINT64 profiles; /* GUID * */ }; +struct va_decoder_create_vk_params +{ + struct wined3d_decoder_desc desc; + UINT64 physical_device; /* VkPhysicalDevice */ + UINT64 device; /* VkDevice */ + UINT64 decoder; + unsigned int width; + unsigned int height; +}; + +struct va_decoder_destroy_vk_params +{ + UINT64 decoder; +}; + enum unix_funcs { unix_va_get_profiles_vk, + unix_va_decoder_create_vk, + unix_va_decoder_destroy_vk, }; #endif diff --git a/dlls/wined3d/va.c b/dlls/wined3d/va.c index 949ca22381e..f855d81855c 100644 --- a/dlls/wined3d/va.c +++ b/dlls/wined3d/va.c @@ -46,6 +46,12 @@ struct va_context { void *libva_handle, *libva_drm_handle; #define MAKE_FUNCPTR(f) typeof(f) *f + MAKE_FUNCPTR(vaCreateConfig); + MAKE_FUNCPTR(vaCreateContext); + MAKE_FUNCPTR(vaCreateSurfaces); + MAKE_FUNCPTR(vaDestroyConfig); + MAKE_FUNCPTR(vaDestroyContext); + MAKE_FUNCPTR(vaDestroySurfaces); MAKE_FUNCPTR(vaGetDisplayDRM); MAKE_FUNCPTR(vaInitialize); MAKE_FUNCPTR(vaMaxNumProfiles); @@ -57,6 +63,14 @@ struct va_context int fd; }; +struct va_decoder +{ + struct va_context ctx; + VAConfigID config; + VAContextID context; + VASurfaceID surfaces[VA_DECODER_SURFACE_COUNT]; +}; + static void close_va_display(struct va_context *ctx) { if (ctx->fd != -1) @@ -95,6 +109,12 @@ static NTSTATUS open_va_display(UINT64 handle, struct va_context *ctx) ERR("Failed to load function %s.\n", #f); \ goto fail; \ } + LOAD_FUNCPTR(vaCreateConfig); + LOAD_FUNCPTR(vaCreateContext); + LOAD_FUNCPTR(vaCreateSurfaces); + LOAD_FUNCPTR(vaDestroyConfig); + LOAD_FUNCPTR(vaDestroyContext); + LOAD_FUNCPTR(vaDestroySurfaces); LOAD_FUNCPTR(vaInitialize); LOAD_FUNCPTR(vaMaxNumProfiles); LOAD_FUNCPTR(vaQueryConfigProfiles); @@ -178,14 +198,105 @@ static NTSTATUS va_get_profiles_vk(void *args) return S_OK; } +static NTSTATUS va_decoder_create_vk(void *args) +{ + struct va_decoder_create_vk_params *params = args; + VASurfaceAttrib surface_attribs[1]; + VAConfigAttrib config_attribs[1]; + unsigned int format, fourcc; + struct va_decoder *decoder; + struct va_context *ctx; + VAStatus status; + + if (params->desc.output_format == WINED3DFMT_NV12_PLANAR) + { + format = VA_RT_FORMAT_YUV420; + fourcc = VA_FOURCC_NV12; + } + else + { + FIXME("Unhandled output format %#x.\n", params->desc.output_format); + return E_NOTIMPL; + } + + if (!(decoder = calloc(1, sizeof(*decoder)))) + return E_OUTOFMEMORY; + ctx = &decoder->ctx; + + if ((status = open_va_display(params->physical_device, ctx))) + return status; + + config_attribs[0].type = VAConfigAttribRTFormat; + config_attribs[0].value = format; + + if ((status = ctx->vaCreateConfig(ctx->display, VAProfileH264High, VAEntrypointVLD, + config_attribs, ARRAY_SIZE(config_attribs), &decoder->config)) != VA_STATUS_SUCCESS) + { + ERR("Failed to create config, error %#x.\n", status); + close_va_display(ctx); + goto fail; + } + + surface_attribs[0].type = VASurfaceAttribPixelFormat; + surface_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; + surface_attribs[0].value.type = VAGenericValueTypeInteger; + surface_attribs[0].value.value.i = fourcc; + + if ((status = ctx->vaCreateSurfaces(ctx->display, format, params->width, params->height, decoder->surfaces, + VA_DECODER_SURFACE_COUNT, surface_attribs, ARRAY_SIZE(surface_attribs))) != VA_STATUS_SUCCESS) + { + ERR("Failed to create surfaces, error %#x.\n", status); + ctx->vaDestroyConfig(ctx->display, decoder->config); + close_va_display(ctx); + goto fail; + } + + if ((status = ctx->vaCreateContext(ctx->display, decoder->config, params->desc.width, params->desc.height, + VA_PROGRESSIVE, decoder->surfaces, ARRAY_SIZE(decoder->surfaces), &decoder->context)) != VA_STATUS_SUCCESS) + { + ERR("Failed to create context, error %#x.\n", status); + ctx->vaDestroySurfaces(ctx->display, decoder->surfaces, VA_DECODER_SURFACE_COUNT); + ctx->vaDestroyConfig(ctx->display, decoder->config); + close_va_display(ctx); + goto fail; + } + + TRACE("Created VA decoder %p.\n", decoder); + + params->decoder = (uintptr_t)decoder; + return S_OK; + +fail: + free(decoder); + return E_FAIL; +} + +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 va_context *ctx = &decoder->ctx; + + ctx->vaDestroyContext(ctx->display, decoder->context); + ctx->vaDestroySurfaces(ctx->display, decoder->surfaces, VA_DECODER_SURFACE_COUNT); + ctx->vaDestroyConfig(ctx->display, decoder->config); + close_va_display(ctx); + free(decoder); + return S_OK; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { [unix_va_get_profiles_vk] = va_get_profiles_vk, + [unix_va_decoder_create_vk] = va_decoder_create_vk, + [unix_va_decoder_destroy_vk] = va_decoder_destroy_vk, }; const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { [unix_va_get_profiles_vk] = va_get_profiles_vk, + [unix_va_decoder_create_vk] = va_decoder_create_vk, + [unix_va_decoder_destroy_vk] = va_decoder_destroy_vk, }; #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11535
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 | 126 +++++++++++++++++++++++++++++++++++++- dlls/wined3d/wined3d_vk.h | 6 ++ 7 files changed, 225 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 f855d81855c..59d3b3c938e 100644 --- a/dlls/wined3d/va.c +++ b/dlls/wined3d/va.c @@ -52,6 +52,7 @@ struct va_context MAKE_FUNCPTR(vaDestroyConfig); MAKE_FUNCPTR(vaDestroyContext); MAKE_FUNCPTR(vaDestroySurfaces); + MAKE_FUNCPTR(vaExportSurfaceHandle); MAKE_FUNCPTR(vaGetDisplayDRM); MAKE_FUNCPTR(vaInitialize); MAKE_FUNCPTR(vaMaxNumProfiles); @@ -69,6 +70,7 @@ struct va_decoder VAConfigID config; VAContextID context; VASurfaceID surfaces[VA_DECODER_SURFACE_COUNT]; + VkDeviceMemory vk_memory[VA_DECODER_SURFACE_COUNT]; }; static void close_va_display(struct va_context *ctx) @@ -92,7 +94,9 @@ static NTSTATUS open_va_display(UINT64 handle, struct va_context *ctx) ctx->display = 0; ctx->fd = -1; - 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; if (!(ctx->libva_handle = dlopen(SONAME_LIBVA, RTLD_NOW))) @@ -115,6 +119,7 @@ static NTSTATUS open_va_display(UINT64 handle, struct va_context *ctx) LOAD_FUNCPTR(vaDestroyConfig); LOAD_FUNCPTR(vaDestroyContext); LOAD_FUNCPTR(vaDestroySurfaces); + LOAD_FUNCPTR(vaExportSurfaceHandle); LOAD_FUNCPTR(vaInitialize); LOAD_FUNCPTR(vaMaxNumProfiles); LOAD_FUNCPTR(vaQueryConfigProfiles); @@ -201,17 +206,31 @@ 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; struct va_context *ctx; 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 { @@ -261,6 +280,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 = ctx->vaExportSurfaceHandle(ctx->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; @@ -274,9 +395,12 @@ fail: static NTSTATUS va_decoder_destroy_vk(void *args) { struct va_decoder_destroy_vk_params *params = args; + struct vulkan_device *device = vulkan_device_from_handle((VkDevice)params->device); struct va_decoder *decoder = (struct va_decoder *)(uintptr_t)params->decoder; struct va_context *ctx = &decoder->ctx; + for (unsigned int i = 0; i < ARRAY_SIZE(decoder->vk_memory); ++i) + device->p_vkFreeMemory(device->host.device, decoder->vk_memory[i], NULL); ctx->vaDestroyContext(ctx->display, decoder->context); ctx->vaDestroySurfaces(ctx->display, decoder->surfaces, VA_DECODER_SURFACE_COUNT); ctx->vaDestroyConfig(ctx->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
From: Elizabeth Figura <zfigura@codeweavers.com> --- dlls/wined3d/decoder.c | 214 ++++++++++++++++++++++++++++++++--- dlls/wined3d/unixlib.h | 9 ++ dlls/wined3d/va.c | 248 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 455 insertions(+), 16 deletions(-) diff --git a/dlls/wined3d/decoder.c b/dlls/wined3d/decoder.c index e335adce7cb..bf59265c128 100644 --- a/dlls/wined3d/decoder.c +++ b/dlls/wined3d/decoder.c @@ -1367,6 +1367,8 @@ struct wined3d_decoder_va_vk { VkImage image; uint64_t command_buffer_id; + bool valid, used; + uint8_t dxva_index; } images[VA_DECODER_SURFACE_COUNT]; }; @@ -1395,13 +1397,12 @@ static void wined3d_decoder_va_vk_get_profiles(struct wined3d_adapter *adapter, WINE_UNIX_CALL(unix_va_get_profiles_vk, ¶ms); } -static void wined3d_decoder_va_vk_cs_init(void *object) +static void wined3d_decoder_va_vk_create_va_decoder(struct wined3d_decoder_va_vk *decoder_va, + struct wined3d_context_vk *context_vk) { - struct wined3d_decoder_va_vk *decoder_va = 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; @@ -1418,8 +1419,6 @@ 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]; @@ -1434,7 +1433,15 @@ static void wined3d_decoder_va_vk_cs_init(void *object) 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); } +} +static void wined3d_decoder_va_vk_cs_init(void *object) +{ + struct wined3d_decoder_va_vk *decoder_va = object; + struct wined3d_context_vk *context_vk; + + context_vk = wined3d_context_vk(context_acquire(decoder_va->d.device, NULL, 0)); + wined3d_decoder_va_vk_create_va_decoder(decoder_va, context_vk); context_release(&context_vk->c); } @@ -1477,16 +1484,11 @@ void wined3d_decoder_va_vk_destroy_va_decoder(struct wined3d_device_vk *device_v WINE_UNIX_CALL(unix_va_decoder_destroy_vk, ¶ms); } -static void wined3d_decoder_va_vk_destroy_object(void *object) +static void wined3d_decoder_va_vk_cleanup(struct wined3d_decoder_va_vk *decoder_va, + struct wined3d_context_vk *context_vk) { - struct wined3d_decoder_va_vk *decoder_va = object; - struct wined3d_context_vk *context_vk; uint64_t command_buffer_id = 0; - TRACE("decoder_va %p.\n", decoder_va); - - 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]; @@ -1499,9 +1501,19 @@ static void wined3d_decoder_va_vk_destroy_object(void *object) * 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); +} + +static void wined3d_decoder_va_vk_destroy_object(void *object) +{ + struct wined3d_decoder_va_vk *decoder_va = object; + struct wined3d_context_vk *context_vk; + + TRACE("decoder_va %p.\n", decoder_va); + context_vk = wined3d_context_vk(context_acquire(decoder_va->d.device, NULL, 0)); + wined3d_decoder_va_vk_cleanup(decoder_va, context_vk); context_release(&context_vk->c); + free(decoder_va); } static void wined3d_decoder_va_vk_destroy(struct wined3d_decoder *decoder) @@ -1512,11 +1524,185 @@ static void wined3d_decoder_va_vk_destroy(struct wined3d_decoder *decoder) wined3d_cs_destroy_object(decoder->device->cs, wined3d_decoder_va_vk_destroy_object, decoder_va); } +static bool decoder_va_vk_find_reference_slot(struct wined3d_decoder_va_vk *decoder_va, + uint8_t dxva_index, unsigned int *slot) +{ + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + { + if (decoder_va->images[i].valid && decoder_va->images[i].dxva_index == dxva_index) + { + *slot = i; + return true; + } + } + + ERR("Reference index %u was never written.\n", dxva_index); + return false; +} + +static unsigned int decoder_va_vk_find_available_output_slot(struct wined3d_decoder_va_vk *decoder_va, + struct wined3d_context_vk *context_vk) +{ + uint64_t earliest_command_buffer_id = UINT64_MAX; + unsigned int earliest_slot = 0; + + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + { + struct wined3d_decoder_image_va_vk *image = &decoder_va->images[i]; + + if (image->used) + continue; + + if (!image->valid || context_vk->completed_command_buffer_id >= image->command_buffer_id) + return i; + + if (image->command_buffer_id < earliest_command_buffer_id) + { + earliest_command_buffer_id = image->command_buffer_id; + earliest_slot = i; + } + } + + /* We need to make sure that Vulkan is done reading from this image. + * Unfortunately, we cannot do GPU-side synchronization between VA and + * Vulkan. We need to do a CPU wait. */ + if (earliest_command_buffer_id == context_vk->current_command_buffer.id) + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); + wined3d_context_vk_wait_command_buffer(context_vk, earliest_command_buffer_id); + return earliest_slot; +} + +/* Copy from the shared VA image to the application destination image. */ +static void blit_va_image(struct wined3d_decoder_va_vk *decoder_va, struct wined3d_context_vk *context_vk, + unsigned int output_idx, struct wined3d_decoder_output_view_vk *output_view_vk) +{ + struct wined3d_texture_vk *texture_vk = wined3d_texture_vk(output_view_vk->v.texture); + unsigned int sub_resource_idx = output_view_vk->v.desc.u.texture.layer_idx; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkCommandBuffer command_buffer; + VkImageSubresourceRange range; + VkImageLayout dst_layout; + VkImageCopy region = {0}; + + command_buffer = wined3d_context_vk_get_command_buffer(context_vk); + + if (texture_vk->layout == VK_IMAGE_LAYOUT_GENERAL) + dst_layout = VK_IMAGE_LAYOUT_GENERAL; + else + dst_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + range.aspectMask = vk_aspect_mask_from_format(texture_vk->t.resource.format); + range.baseMipLevel = sub_resource_idx % texture_vk->t.level_count; + range.levelCount = 1; + range.baseArrayLayer = sub_resource_idx / texture_vk->t.level_count; + range.layerCount = 1; + + wined3d_context_vk_image_barrier(context_vk, command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + vk_access_mask_from_bind_flags(texture_vk->t.resource.bind_flags), + VK_ACCESS_TRANSFER_WRITE_BIT, + texture_vk->layout, dst_layout, texture_vk->image.vk_image, &range); + + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT; + region.srcSubresource.baseArrayLayer = 0; + region.srcSubresource.layerCount = 1; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT; + region.dstSubresource.baseArrayLayer = sub_resource_idx; + region.dstSubresource.layerCount = 1; + region.extent.width = decoder_va->d.width; + region.extent.height = decoder_va->d.height; + region.extent.depth = 1; + + VK_CALL(vkCmdCopyImage(command_buffer, decoder_va->images[output_idx].image, + VK_IMAGE_LAYOUT_GENERAL, texture_vk->image.vk_image, dst_layout, 1, ®ion)); + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT; + region.extent.width /= 2; + region.extent.height /= 2; + VK_CALL(vkCmdCopyImage(command_buffer, decoder_va->images[output_idx].image, + VK_IMAGE_LAYOUT_GENERAL, texture_vk->image.vk_image, dst_layout, 1, ®ion)); + + wined3d_context_vk_image_barrier(context_vk, command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + vk_access_mask_from_bind_flags(texture_vk->t.resource.bind_flags), + dst_layout, texture_vk->layout, texture_vk->image.vk_image, &range); + + decoder_va->images[output_idx].command_buffer_id = context_vk->current_command_buffer.id; +} + static void wined3d_decoder_va_vk_decode(struct wined3d_context *context, struct wined3d_decoder *decoder, struct wined3d_decoder_output_view *output_view, unsigned int bitstream_size, unsigned int slice_control_size) { - FIXME("Not implemented.\n"); + struct wined3d_decoder_output_view_vk *output_view_vk = wined3d_decoder_output_view_vk(output_view); + const DXVA_PicParams_H264 *h264_params = wined3d_buffer_load_sysmem(decoder->parameters, context); + struct wined3d_decoder_va_vk *decoder_va = wined3d_decoder_va_vk(decoder); + unsigned int sub_resource_idx = output_view_vk->v.desc.u.texture.layer_idx; + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + struct wined3d_texture *texture = output_view->texture; + struct va_decoder_decode_params params; + unsigned int output_idx, width, height; + + wined3d_texture_prepare_location(texture, sub_resource_idx, &context_vk->c, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_validate_location(texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(texture, sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB); + + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + decoder_va->images[i].used = false; + + for (unsigned int i = 0; i < ARRAY_SIZE(h264_params->RefFrameList); ++i) + { + unsigned int slot_index; + + if (h264_params->RefFrameList[i].bPicEntry == 0xff) + continue; + + /* NVidia's DXVA implementation apparently expects each frame to appear + * in its own references list. VA does not expect or need this. */ + if (h264_params->RefFrameList[i].Index7Bits == h264_params->CurrPic.Index7Bits) + continue; + + if (!decoder_va_vk_find_reference_slot(decoder_va, h264_params->RefFrameList[i].Index7Bits, &slot_index)) + return; + + decoder_va->images[slot_index].used = true; + } + + output_idx = decoder_va_vk_find_available_output_slot(decoder_va, context_vk); + decoder_va->images[output_idx].valid = true; + decoder_va->images[output_idx].dxva_index = h264_params->CurrPic.Index7Bits; + + width = (h264_params->wFrameWidthInMbsMinus1 + 1) * 16; + height = (h264_params->wFrameHeightInMbsMinus1 + 1) * 16; + if (width != decoder_va->d.width || height != decoder_va->d.height) + { + TRACE("Dynamic resize from %ux%u to %ux%u.\n", decoder_va->d.width, decoder_va->d.height, width, height); + wined3d_decoder_va_vk_cleanup(decoder_va, context_vk); + decoder_va->d.width = width; + decoder_va->d.height = height; + wined3d_decoder_va_vk_create_va_decoder(decoder_va, context_vk); + } + + params.decoder = decoder_va->va_decoder; + params.bitstream = (uintptr_t)wined3d_buffer_load_sysmem(decoder_va->d.bitstream, context); + params.parameters = (uintptr_t)wined3d_buffer_load_sysmem(decoder_va->d.parameters, context); + params.matrix = (uintptr_t)wined3d_buffer_load_sysmem(decoder_va->d.matrix, context); + params.slice_control = (uintptr_t)wined3d_buffer_load_sysmem(decoder_va->d.slice_control, context); + params.bitstream_size = bitstream_size; + params.slice_control_size = slice_control_size; + params.output_idx = output_idx; + WINE_UNIX_CALL(unix_va_decoder_decode, ¶ms); + + blit_va_image(decoder_va, context_vk, output_idx, output_view_vk); + + EnterCriticalSection(&decoder_va->d.feedback_cs); + decoder_va->d.feedback_number = h264_params->StatusReportFeedbackNumber; + decoder_va->d.feedback_pic_entry = h264_params->CurrPic.bPicEntry; + decoder_va->d.feedback_field = h264_params->field_pic_flag; + LeaveCriticalSection(&decoder_va->d.feedback_cs); + + wined3d_context_vk_reference_texture(context_vk, wined3d_texture_vk(texture)); } const struct wined3d_decoder_ops wined3d_decoder_va_vk_ops = diff --git a/dlls/wined3d/unixlib.h b/dlls/wined3d/unixlib.h index ec53d636afb..6e5a83da21a 100644 --- a/dlls/wined3d/unixlib.h +++ b/dlls/wined3d/unixlib.h @@ -58,11 +58,20 @@ struct va_decoder_destroy_vk_params UINT64 decoder; }; +struct va_decoder_decode_params +{ + UINT64 decoder; + UINT64 bitstream, parameters, matrix, slice_control; + UINT32 bitstream_size, slice_control_size; + UINT32 output_idx; +}; + enum unix_funcs { unix_va_get_profiles_vk, unix_va_decoder_create_vk, unix_va_decoder_destroy_vk, + unix_va_decoder_decode, }; #endif diff --git a/dlls/wined3d/va.c b/dlls/wined3d/va.c index 59d3b3c938e..74030a8f341 100644 --- a/dlls/wined3d/va.c +++ b/dlls/wined3d/va.c @@ -46,17 +46,23 @@ struct va_context { void *libva_handle, *libva_drm_handle; #define MAKE_FUNCPTR(f) typeof(f) *f + MAKE_FUNCPTR(vaBeginPicture); + MAKE_FUNCPTR(vaCreateBuffer); MAKE_FUNCPTR(vaCreateConfig); MAKE_FUNCPTR(vaCreateContext); MAKE_FUNCPTR(vaCreateSurfaces); + MAKE_FUNCPTR(vaDestroyBuffer); MAKE_FUNCPTR(vaDestroyConfig); MAKE_FUNCPTR(vaDestroyContext); MAKE_FUNCPTR(vaDestroySurfaces); + MAKE_FUNCPTR(vaEndPicture); MAKE_FUNCPTR(vaExportSurfaceHandle); MAKE_FUNCPTR(vaGetDisplayDRM); MAKE_FUNCPTR(vaInitialize); MAKE_FUNCPTR(vaMaxNumProfiles); MAKE_FUNCPTR(vaQueryConfigProfiles); + MAKE_FUNCPTR(vaRenderPicture); + MAKE_FUNCPTR(vaSyncSurface); MAKE_FUNCPTR(vaTerminate); #undef MAKE_FUNCPTR @@ -71,6 +77,12 @@ struct va_decoder VAContextID context; VASurfaceID surfaces[VA_DECODER_SURFACE_COUNT]; VkDeviceMemory vk_memory[VA_DECODER_SURFACE_COUNT]; + struct + { + bool valid; + uint8_t dxva_index; + } references[VA_DECODER_SURFACE_COUNT]; + bool long_slice_info; }; static void close_va_display(struct va_context *ctx) @@ -113,16 +125,22 @@ static NTSTATUS open_va_display(UINT64 handle, struct va_context *ctx) ERR("Failed to load function %s.\n", #f); \ goto fail; \ } + LOAD_FUNCPTR(vaBeginPicture); + LOAD_FUNCPTR(vaCreateBuffer); LOAD_FUNCPTR(vaCreateConfig); LOAD_FUNCPTR(vaCreateContext); LOAD_FUNCPTR(vaCreateSurfaces); + LOAD_FUNCPTR(vaDestroyBuffer); LOAD_FUNCPTR(vaDestroyConfig); LOAD_FUNCPTR(vaDestroyContext); LOAD_FUNCPTR(vaDestroySurfaces); + LOAD_FUNCPTR(vaEndPicture); LOAD_FUNCPTR(vaExportSurfaceHandle); LOAD_FUNCPTR(vaInitialize); LOAD_FUNCPTR(vaMaxNumProfiles); LOAD_FUNCPTR(vaQueryConfigProfiles); + LOAD_FUNCPTR(vaRenderPicture); + LOAD_FUNCPTR(vaSyncSurface); LOAD_FUNCPTR(vaTerminate); #undef LOAD_FUNCPTR @@ -270,8 +288,8 @@ static NTSTATUS va_decoder_create_vk(void *args) goto fail; } - if ((status = ctx->vaCreateContext(ctx->display, decoder->config, params->desc.width, params->desc.height, - VA_PROGRESSIVE, decoder->surfaces, ARRAY_SIZE(decoder->surfaces), &decoder->context)) != VA_STATUS_SUCCESS) + if ((status = ctx->vaCreateContext(ctx->display, decoder->config, params->width, params->height, VA_PROGRESSIVE, + decoder->surfaces, ARRAY_SIZE(decoder->surfaces), &decoder->context)) != VA_STATUS_SUCCESS) { ERR("Failed to create context, error %#x.\n", status); ctx->vaDestroySurfaces(ctx->display, decoder->surfaces, VA_DECODER_SURFACE_COUNT); @@ -382,6 +400,8 @@ static NTSTATUS va_decoder_create_vk(void *args) params->surfaces[i].image = image; } + decoder->long_slice_info = params->desc.long_slice_info; + TRACE("Created VA decoder %p.\n", decoder); params->decoder = (uintptr_t)decoder; @@ -409,11 +429,234 @@ static NTSTATUS va_decoder_destroy_vk(void *args) return S_OK; } +static bool find_reference_slot(struct va_decoder *decoder, uint8_t dxva_index, unsigned int *idx) +{ + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + { + if (decoder->references[i].valid && decoder->references[i].dxva_index == dxva_index) + { + *idx = i; + return true; + } + } + + ERR("Reference index %u was never written.\n", dxva_index); + return false; +} + +static NTSTATUS va_decoder_decode(void *args) +{ + struct va_decoder_decode_params *params = args; + const DXVA_PicParams_H264 *dxva_params = (const void *)(uintptr_t)params->parameters; + const DXVA_Qmatrix_H264 *dxva_matrix = (const void *)(uintptr_t)params->matrix; + struct va_decoder *decoder = (struct va_decoder *)(uintptr_t)params->decoder; + VAPictureParameterBufferH264 va_params = {0}; + struct va_context *ctx = &decoder->ctx; + VAIQMatrixBufferH264 va_matrix; + unsigned int ref_count = 0; + unsigned int slice_count; + VABufferID buffers[3]; + VAStatus status; + + if (decoder->long_slice_info) + slice_count = params->slice_control_size / sizeof(DXVA_Slice_H264_Long); + else + slice_count = params->slice_control_size / sizeof(DXVA_Slice_H264_Short); + + if ((status = ctx->vaBeginPicture(ctx->display, decoder->context, + decoder->surfaces[params->output_idx])) != VA_STATUS_SUCCESS) + ERR("Failed to begin picture, error %#x.\n", status); + + for (unsigned int i = 0; i < slice_count; ++i) + { + VASliceParameterBufferH264 slice_params = {0}; + VABufferID slice_buffer; + + if (decoder->long_slice_info) + { + const DXVA_Slice_H264_Long *slices = (const void *)(uintptr_t)params->slice_control; + + /* VA doesn't want the start codes. */ + slice_params.slice_data_size = slices[i].SliceBytesInBuffer - 3; + slice_params.slice_data_offset = slices[i].BSNALunitDataLocation + 3; + slice_params.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; + + slice_params.num_ref_idx_l0_active_minus1 = slices[i].num_ref_idx_l0_active_minus1; + slice_params.num_ref_idx_l1_active_minus1 = slices[i].num_ref_idx_l1_active_minus1; + slice_params.slice_type = slices[i].slice_type; + } + else + { + const DXVA_Slice_H264_Short *slices = (const void *)(uintptr_t)params->slice_control; + + slice_params.slice_data_size = slices[i].SliceBytesInBuffer - 3; + slice_params.slice_data_offset = slices[i].BSNALunitDataLocation + 3; + slice_params.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; + + /* FIXME: We can't fill any of the other parameters. + * Mesa doesn't care about most of them, but it does care about + * these two. However, it also treats them as per-picture, and we + * have the "default" values presumably from the PPS, so provide + * them here. */ + slice_params.num_ref_idx_l0_active_minus1 = dxva_params->num_ref_idx_l0_active_minus1; + slice_params.num_ref_idx_l1_active_minus1 = dxva_params->num_ref_idx_l1_active_minus1; + } + + if ((status = ctx->vaCreateBuffer(ctx->display, decoder->context, VASliceParameterBufferType, + sizeof(slice_params), 1, &slice_params, &slice_buffer)) != VA_STATUS_SUCCESS) + ERR("Failed to create slice parameters, error %#x.\n", status); + + if ((status = ctx->vaRenderPicture(ctx->display, decoder->context, + &slice_buffer, 1)) != VA_STATUS_SUCCESS) + ERR("Failed to render slice parameters, error %#x.\n", status); + + ctx->vaDestroyBuffer(ctx->display, slice_buffer); + } + + TRACE("Decoding frame %02x/%02x/%u, RefPicFlag %#x, reference frames", + dxva_params->CurrPic.bPicEntry, dxva_params->frame_num, params->output_idx, dxva_params->RefPicFlag); + + for (unsigned int i = 0; i < ARRAY_SIZE(dxva_params->RefFrameList); ++i) + { + unsigned int field_flags = ((dxva_params->UsedForReferenceFlags >> (2 * i)) & 3u); + VAPictureH264 *va_ref = &va_params.ReferenceFrames[ref_count]; + unsigned int ref_idx; + + if (dxva_params->RefFrameList[i].bPicEntry == 0xff) + continue; + + /* NVidia's DXVA implementation apparently expects each frame to appear + * in its own references list. VA does not expect or need this. */ + if (dxva_params->RefFrameList[i].Index7Bits == dxva_params->CurrPic.Index7Bits) + continue; + + if (!find_reference_slot(decoder, dxva_params->RefFrameList[i].Index7Bits, &ref_idx)) + return E_FAIL; + + TRACE(" %02x/%02x/%u", dxva_params->RefFrameList[i].bPicEntry, dxva_params->FrameNumList[i], ref_idx); + + va_ref->picture_id = decoder->surfaces[ref_idx]; + va_ref->frame_idx = dxva_params->FrameNumList[i]; + if (dxva_params->RefFrameList[i].AssociatedFlag) + va_ref->flags = VA_PICTURE_H264_LONG_TERM_REFERENCE; + else + va_ref->flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE; + + if (field_flags == 1) + va_ref->flags |= VA_PICTURE_H264_TOP_FIELD; + else if (field_flags == 2) + va_ref->flags |= VA_PICTURE_H264_BOTTOM_FIELD; + + va_ref->TopFieldOrderCnt = dxva_params->FieldOrderCntList[i][0]; + va_ref->BottomFieldOrderCnt = dxva_params->FieldOrderCntList[i][1]; + + ++ref_count; + } + + for (unsigned int i = ref_count; i < ARRAY_SIZE(va_params.ReferenceFrames); ++i) + { + va_params.ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; + va_params.ReferenceFrames[i].picture_id = VA_INVALID_SURFACE; + } + + TRACE(".\n"); + + va_params.CurrPic.picture_id = decoder->surfaces[params->output_idx]; + va_params.CurrPic.frame_idx = dxva_params->frame_num; + /* FIXME: What on earth do we put here? For some reason DXVA supplies these + * flags for reference frames, but not for the current frame. + * Mesa doesn't care about most of these, but it does care whether the + * current frame is a bottom field. */ + va_params.CurrPic.flags = 0; + va_params.CurrPic.TopFieldOrderCnt = dxva_params->CurrFieldOrderCnt[0]; + va_params.CurrPic.BottomFieldOrderCnt = dxva_params->CurrFieldOrderCnt[1]; + + va_params.picture_width_in_mbs_minus1 = dxva_params->wFrameWidthInMbsMinus1; + va_params.picture_height_in_mbs_minus1 = dxva_params->wFrameHeightInMbsMinus1; + va_params.bit_depth_luma_minus8 = dxva_params->bit_depth_luma_minus8; + va_params.bit_depth_chroma_minus8 = dxva_params->bit_depth_chroma_minus8; + va_params.num_ref_frames = dxva_params->num_ref_frames; + va_params.seq_fields.bits.chroma_format_idc = dxva_params->chroma_format_idc; + va_params.seq_fields.bits.residual_colour_transform_flag = dxva_params->residual_colour_transform_flag; + /* We don't have this value, so we have to say it's allowed. */ + va_params.seq_fields.bits.gaps_in_frame_num_value_allowed_flag = 1; + va_params.seq_fields.bits.frame_mbs_only_flag = dxva_params->frame_mbs_only_flag; + /* We don't have mb_adaptive_frame_field_flag, but we do have MbaffFrameFlag + * which is (mb_adaptive_frame_field_flag && !field_pic_flag). + * If field_pic_flag is 1, we don't know, so we set it to 1, which is the + * less constrained option. */ + if (!dxva_params->field_pic_flag) + va_params.seq_fields.bits.mb_adaptive_frame_field_flag = dxva_params->MbaffFrameFlag; + else + va_params.seq_fields.bits.mb_adaptive_frame_field_flag = 1; + va_params.seq_fields.bits.direct_8x8_inference_flag = dxva_params->direct_8x8_inference_flag; + va_params.seq_fields.bits.MinLumaBiPredSize8x8 = dxva_params->MinLumaBipredSize8x8Flag; + va_params.seq_fields.bits.log2_max_frame_num_minus4 = dxva_params->log2_max_frame_num_minus4; + va_params.seq_fields.bits.pic_order_cnt_type = dxva_params->pic_order_cnt_type; + va_params.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = dxva_params->log2_max_pic_order_cnt_lsb_minus4; + va_params.seq_fields.bits.delta_pic_order_always_zero_flag = dxva_params->delta_pic_order_always_zero_flag; + /* The slice group fields are "va_deprecated" and GStreamer doesn't fill them. */ + va_params.pic_init_qp_minus26 = dxva_params->pic_init_qp_minus26; + va_params.pic_init_qs_minus26 = dxva_params->pic_init_qs_minus26; + va_params.chroma_qp_index_offset = dxva_params->chroma_qp_index_offset; + va_params.second_chroma_qp_index_offset = dxva_params->second_chroma_qp_index_offset; + va_params.pic_fields.bits.entropy_coding_mode_flag = dxva_params->entropy_coding_mode_flag; + va_params.pic_fields.bits.weighted_pred_flag = dxva_params->weighted_pred_flag; + va_params.pic_fields.bits.weighted_bipred_idc = dxva_params->weighted_bipred_idc; + va_params.pic_fields.bits.transform_8x8_mode_flag = dxva_params->transform_8x8_mode_flag; + va_params.pic_fields.bits.field_pic_flag = dxva_params->field_pic_flag; + va_params.pic_fields.bits.constrained_intra_pred_flag = dxva_params->constrained_intra_pred_flag; + va_params.pic_fields.bits.pic_order_present_flag = dxva_params->pic_order_present_flag; + va_params.pic_fields.bits.deblocking_filter_control_present_flag = dxva_params->deblocking_filter_control_present_flag; + va_params.pic_fields.bits.redundant_pic_cnt_present_flag = dxva_params->redundant_pic_cnt_present_flag; + /* This seems to be equivalent, although the GStreamer code is not the easiest to follow. */ + va_params.pic_fields.bits.reference_pic_flag = dxva_params->RefPicFlag; + /* This too. */ + va_params.frame_num = dxva_params->frame_num; + + decoder->references[params->output_idx].valid = true; + decoder->references[params->output_idx].dxva_index = dxva_params->CurrPic.Index7Bits; + + /* The DXVA and VA matrices are byte-compatible. */ + memcpy(&va_matrix, dxva_matrix, sizeof(*dxva_matrix)); + memset(va_matrix.va_reserved, 0, sizeof(va_matrix.va_reserved)); + + /* The parameters need to be submitted first, or Mesa fails. */ + + if ((status = ctx->vaCreateBuffer(ctx->display, decoder->context, VAPictureParameterBufferType, + sizeof(va_params), 1, &va_params, &buffers[0])) != VA_STATUS_SUCCESS) + ERR("Failed to create parameters buffer, error %#x.\n", status); + + if ((status = ctx->vaCreateBuffer(ctx->display, decoder->context, VAIQMatrixBufferType, + sizeof(va_matrix), 1, &va_matrix, &buffers[1])) != VA_STATUS_SUCCESS) + ERR("Failed to create parameters buffer, error %#x.\n", status); + + if ((status = ctx->vaCreateBuffer(ctx->display, decoder->context, VASliceDataBufferType, + params->bitstream_size, 1, (void *)(uintptr_t)params->bitstream, &buffers[2])) != VA_STATUS_SUCCESS) + ERR("Failed to create bitstream buffer, error %#x.\n", status); + + if ((status = ctx->vaRenderPicture(ctx->display, decoder->context, + buffers, ARRAY_SIZE(buffers))) != VA_STATUS_SUCCESS) + ERR("Failed to render buffers, error %#x.\n", status); + + ctx->vaDestroyBuffer(ctx->display, buffers[0]); + ctx->vaDestroyBuffer(ctx->display, buffers[1]); + ctx->vaDestroyBuffer(ctx->display, buffers[2]); + + if ((status = ctx->vaEndPicture(ctx->display, decoder->context)) != VA_STATUS_SUCCESS) + ERR("Failed to end picture, error %#x.\n", status); + + ctx->vaSyncSurface(ctx->display, decoder->surfaces[params->output_idx]); + + return S_OK; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { [unix_va_get_profiles_vk] = va_get_profiles_vk, [unix_va_decoder_create_vk] = va_decoder_create_vk, [unix_va_decoder_destroy_vk] = va_decoder_destroy_vk, + [unix_va_decoder_decode] = va_decoder_decode, }; const unixlib_entry_t __wine_unix_call_wow64_funcs[] = @@ -421,6 +664,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = [unix_va_get_profiles_vk] = va_get_profiles_vk, [unix_va_decoder_create_vk] = va_decoder_create_vk, [unix_va_decoder_destroy_vk] = va_decoder_destroy_vk, + [unix_va_decoder_decode] = va_decoder_decode, }; #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11535
participants (2)
-
Elizabeth Figura -
Elizabeth Figura (@zfigura)