diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 7e9323c67c..21a984e480 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -1249,7 +1249,7 @@ Video :videotoolbox: requires ``--vo=gpu`` (macOS 10.8 and up), or ``--vo=libmpv`` (iOS 9.0 and up) :videotoolbox-copy: copies video back into system RAM (macOS 10.8 or iOS 9.0 and up) - :vaapi: requires ``--vo=gpu`` or ``--vo=vaapi`` (Linux only) + :vaapi: requires ``--vo=gpu``, ``--vo=vaapi`` or ``--vo=vaapi-wayland`` (Linux only) :vaapi-copy: copies video back into system RAM (Linux with some GPUs only) :nvdec: requires ``--vo=gpu`` (Any platform CUDA is available) :nvdec-copy: copies video back to system RAM (Any platform CUDA is available) diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index e453fdcca1..13d4050dc4 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -286,6 +286,14 @@ Available video output drivers are: ``--sdl-switch-mode`` Instruct SDL to switch the monitor video mode when going fullscreen. +``vaapi-wayland`` + Experimental Wayland output driver designed for use with VA API hardware decoding. + The driver is designed to avoid any GPU to CPU copies, and to perform scaling and + color space conversion using fixed-function hardware, if available, + rather than GPU shaders. This frees up GPU resources for other tasks. + Currently this driver is experimental and only works with the ``--hwdec=vaapi`` driver; + OSD is also not supported. Supported compositors : Weston and Sway. + ``vaapi`` Intel VA API video output driver with support for hardware decoding. Note that there is absolutely no reason to use this, other than compatibility. diff --git a/generated/wayland/meson.build b/generated/wayland/meson.build index 19ed4bc023..79247409b1 100644 --- a/generated/wayland/meson.build +++ b/generated/wayland/meson.build @@ -1,7 +1,9 @@ wl_protocol_dir = wayland['deps'][2].get_variable(pkgconfig: 'pkgdatadir') -protocols = [[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], - [wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], +protocols = [[wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], + [wl_protocol_dir, 'stable/viewporter/viewporter.xml'], + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml']] wl_protocols_source = [] wl_protocols_headers = [] diff --git a/meson.build b/meson.build index 25f399da64..0b07582a33 100644 --- a/meson.build +++ b/meson.build @@ -1432,10 +1432,16 @@ vaapi_wayland = { 'deps': dependency('libva-wayland', version: '>= 1.1.0', required: get_option('vaapi-wayland')), } vaapi_wayland += {'use': vaapi['use'] and egl_wayland['use'] and vaapi_wayland['deps'].found()} + if vaapi_wayland['use'] features += vaapi_wayland['name'] endif +if vaapi_wayland['use'] and memfd_create + features += 'vaapi-wayland-memfd' + sources += files('video/out/vo_vaapi_wayland.c') +endif + vaapi_x11 = { 'name': 'vaapi-x11', 'deps': dependency('libva-x11', version: '>= 1.1.0', required: get_option('vaapi-x11')), diff --git a/video/out/drm_common.c b/video/out/drm_common.c index dad9aaf2fb..cd79f3ee42 100644 --- a/video/out/drm_common.c +++ b/video/out/drm_common.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "config.h" @@ -1099,3 +1100,451 @@ fail: *closure->waiting_for_flip = false; talloc_free(closure); } + +const char* drm_format_string(uint drm_format) { + + switch (drm_format) { + /* Reserve 0 for the invalid format specifier */ + case DRM_FORMAT_INVALID: + return "DRM_FORMAT_INVALID"; + break; + /* color index */ + case DRM_FORMAT_C8: + return "DRM_FORMAT_C8"; + break; + /* 8 bpp Red */ + case DRM_FORMAT_R8: + return "DRM_FORMAT_R8"; + break; + /* these formats are not in the header for some reason */ +#if 0 + /* 10 bpp Red */ + case DRM_FORMAT_R10: + return "DRM_FORMAT_R10"; + break; + /* 12 bpp Red */ + case DRM_FORMAT_R12: + return "DRM_FORMAT_R12"; + break; +#endif + /* 16 bpp Red */ + case DRM_FORMAT_R16: + return "DRM_FORMAT_R16"; + break; + /* 16 bpp RG */ + case DRM_FORMAT_RG88: + return "DRM_FORMAT_RG88"; + break; + case DRM_FORMAT_GR88: + return "DRM_FORMAT_GR88"; + break; + /* 32 bpp RG */ + case DRM_FORMAT_RG1616: + return "DRM_FORMAT_RG1616"; + break; + case DRM_FORMAT_GR1616: + return "DRM_FORMAT_GR1616"; + break; + /* 8 bpp RGB */ + case DRM_FORMAT_RGB332: + return "DRM_FORMAT_RGB332"; + break; + case DRM_FORMAT_BGR233: + return "DRM_FORMAT_BGR233"; + break; + /* 16 bpp RGB */ + case DRM_FORMAT_XRGB4444: + return "DRM_FORMAT_XRGB4444"; + break; + case DRM_FORMAT_XBGR4444: + return "DRM_FORMAT_XBGR4444"; + break; + case DRM_FORMAT_RGBX4444: + return "DRM_FORMAT_RGBX4444"; + break; + case DRM_FORMAT_BGRX4444: + return "DRM_FORMAT_BGRX4444"; + break; + case DRM_FORMAT_ARGB4444: + return "DRM_FORMAT_ARGB4444"; + break; + case DRM_FORMAT_ABGR4444: + return "DRM_FORMAT_ABGR4444"; + break; + case DRM_FORMAT_RGBA4444: + return "DRM_FORMAT_RGBA4444"; + break; + case DRM_FORMAT_BGRA4444: + return "DRM_FORMAT_BGRA4444"; + break; + case DRM_FORMAT_XRGB1555: + return "DRM_FORMAT_XRGB1555"; + break; + case DRM_FORMAT_XBGR1555: + return "DRM_FORMAT_XBGR1555"; + break; + case DRM_FORMAT_RGBX5551: + return "DRM_FORMAT_RGBX5551"; + break; + case DRM_FORMAT_BGRX5551: + return "DRM_FORMAT_BGRX5551"; + break; + case DRM_FORMAT_ARGB1555: + return "DRM_FORMAT_ARGB1555"; + break; + case DRM_FORMAT_ABGR1555: + return "DRM_FORMAT_ABGR1555"; + break; + case DRM_FORMAT_RGBA5551: + return "DRM_FORMAT_RGBA5551"; + break; + case DRM_FORMAT_BGRA5551: + return "DRM_FORMAT_BGRA5551"; + break; + case DRM_FORMAT_RGB565: + return "DRM_FORMAT_RGB565"; + break; + case DRM_FORMAT_BGR565: + return "DRM_FORMAT_BGR565"; + break; + /* 24 bpp RGB */ + case DRM_FORMAT_RGB888: + return "DRM_FORMAT_RGB888"; + break; + case DRM_FORMAT_BGR888: + return "DRM_FORMAT_BGR888"; + break; + /* 32 bpp RGB */ + case DRM_FORMAT_XRGB8888: + return "DRM_FORMAT_XRGB8888"; + break; + case DRM_FORMAT_XBGR8888: + return "DRM_FORMAT_XBGR8888"; + break; + case DRM_FORMAT_RGBX8888: + return "DRM_FORMAT_RGBX8888"; + break; + case DRM_FORMAT_BGRX8888: + return "DRM_FORMAT_BGRX8888"; + break; + case DRM_FORMAT_ARGB8888: + return "DRM_FORMAT_ARGB8888"; + break; + case DRM_FORMAT_ABGR8888: + return "DRM_FORMAT_ABGR8888"; + break; + case DRM_FORMAT_RGBA8888: + return "DRM_FORMAT_RGBA8888"; + break; + case DRM_FORMAT_BGRA8888: + return "DRM_FORMAT_BGRA8888"; + break; + case DRM_FORMAT_XRGB2101010: + return "DRM_FORMAT_XRGB2101010"; + break; + case DRM_FORMAT_XBGR2101010: + return "DRM_FORMAT_XBGR2101010"; + break; + case DRM_FORMAT_RGBX1010102: + return "DRM_FORMAT_RGBX1010102"; + break; + case DRM_FORMAT_BGRX1010102: + return "DRM_FORMAT_BGRX1010102"; + break; + case DRM_FORMAT_ARGB2101010: + return "DRM_FORMAT_ARGB2101010"; + break; + case DRM_FORMAT_ABGR2101010: + return "DRM_FORMAT_ABGR2101010"; + break; + case DRM_FORMAT_RGBA1010102: + return "DRM_FORMAT_RGBA1010102"; + break; + case DRM_FORMAT_BGRA1010102: + return "DRM_FORMAT_BGRA1010102"; + break; + /* 64 bpp RGB */ + case DRM_FORMAT_XRGB16161616: + return "DRM_FORMAT_XRGB16161616"; + break; + case DRM_FORMAT_XBGR16161616: + return "DRM_FORMAT_XBGR16161616"; + break; + case DRM_FORMAT_ARGB16161616: + return "DRM_FORMAT_ARGB16161616"; + break; + case DRM_FORMAT_ABGR16161616: + return "DRM_FORMAT_ABGR16161616"; + break; + /* + * Floating point 64bpp RGB + * IEEE 754-2008 binary16 half-precision float + * [15:0] sign:exponent:mantissa 1:5:10 + */ + case DRM_FORMAT_XRGB16161616F: + return "DRM_FORMAT_XRGB16161616F"; + break; + case DRM_FORMAT_XBGR16161616F: + return "DRM_FORMAT_XBGR16161616F"; + break; + case DRM_FORMAT_ARGB16161616F: + return "DRM_FORMAT_ARGB16161616F"; + break; + case DRM_FORMAT_ABGR16161616F: + return "DRM_FORMAT_ABGR16161616F"; + break; + /* + * RGBA format with 10-bit components packed in 64-bit per pixel, with 6 bits + * of unused padding per component: + */ + case DRM_FORMAT_AXBXGXRX106106106106: + return "DRM_FORMAT_AXBXGXRX106106106106"; + break; + /* packed YCbCr */ + case DRM_FORMAT_YUYV: + return "DRM_FORMAT_YUYV"; + break; + case DRM_FORMAT_YVYU: + return "DRM_FORMAT_YVYU"; + break; + case DRM_FORMAT_UYVY: + return "DRM_FORMAT_UYVY"; + break; + case DRM_FORMAT_VYUY: + return "DRM_FORMAT_VYUY"; + break; + case DRM_FORMAT_AYUV: + return "DRM_FORMAT_AYUV"; + break; + case DRM_FORMAT_XYUV8888: + return "DRM_FORMAT_XYUV8888"; + break; + case DRM_FORMAT_VUY888: + return "DRM_FORMAT_VUY888"; + break; + case DRM_FORMAT_VUY101010: + return "DRM_FORMAT_VUY101010"; + break; + /* + * packed Y2xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb + */ + case DRM_FORMAT_Y210: + return "DRM_FORMAT_Y210"; + break; + case DRM_FORMAT_Y212: + return "DRM_FORMAT_Y212"; + break; + case DRM_FORMAT_Y216: + return "DRM_FORMAT_Y216"; + break; + /* + * packed Y4xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb except Y410 + */ + case DRM_FORMAT_Y410: + return "DRM_FORMAT_Y410"; + break; + case DRM_FORMAT_Y412: + return "DRM_FORMAT_Y412"; + break; + case DRM_FORMAT_Y416: + return "DRM_FORMAT_Y416"; + break; + case DRM_FORMAT_XVYU2101010: + return "DRM_FORMAT_XVYU2101010"; + break; + case DRM_FORMAT_XVYU12_16161616: + return "DRM_FORMAT_XVYU12_16161616"; + break; + case DRM_FORMAT_XVYU16161616: + return "DRM_FORMAT_XVYU16161616"; + break; + /* + * packed YCbCr420 2x2 tiled formats + * first 64 bits will contain Y,Cb,Cr components for a 2x2 tile + */ + /* [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ + case DRM_FORMAT_Y0L0: + return "DRM_FORMAT_Y0L0"; + break; + /* [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ + case DRM_FORMAT_X0L0: + return "DRM_FORMAT_X0L0"; + break; + /* [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ + case DRM_FORMAT_Y0L2: + return "DRM_FORMAT_Y0L2"; + break; + /* [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ + case DRM_FORMAT_X0L2: + return "DRM_FORMAT_X0L2"; + break; + /* + * 1-plane YUV 4:2:0 + * In these formats, the component ordering is specified (Y, followed by U + * then V), but the exact Linear layout is undefined. + * These formats can only be used with a non-Linear modifier. + */ + case DRM_FORMAT_YUV420_8BIT: + return "DRM_FORMAT_YUV420_8BIT"; + break; + case DRM_FORMAT_YUV420_10BIT: + return "DRM_FORMAT_YUV420_10BIT"; + break; + /* + * 2 plane RGB + A + * index 0 = RGB plane, same format as the corresponding non _A8 format has + * index 1 = A plane, [7:0] A + */ + case DRM_FORMAT_XRGB8888_A8: + return "DRM_FORMAT_XRGB8888_A8"; + break; + case DRM_FORMAT_XBGR8888_A8: + return "DRM_FORMAT_XBGR8888_A8"; + break; + case DRM_FORMAT_RGBX8888_A8: + return "DRM_FORMAT_RGBX8888_A8"; + break; + case DRM_FORMAT_BGRX8888_A8: + return "DRM_FORMAT_BGRX8888_A8"; + break; + case DRM_FORMAT_RGB888_A8: + return "DRM_FORMAT_RGB888_A8"; + break; + case DRM_FORMAT_BGR888_A8: + return "DRM_FORMAT_BGR888_A8"; + break; + case DRM_FORMAT_RGB565_A8: + return "DRM_FORMAT_RGB565_A8"; + break; + case DRM_FORMAT_BGR565_A8: + return "DRM_FORMAT_BGR565_A8"; + break; + /* + * 2 plane YCbCr + * index 0 = Y plane, [7:0] Y + * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian + * or + * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian + */ + case DRM_FORMAT_NV12: + return "DRM_FORMAT_NV12"; + break; + case DRM_FORMAT_NV21: + return "DRM_FORMAT_NV21"; + break; + case DRM_FORMAT_NV16: + return "DRM_FORMAT_NV16"; + break; + case DRM_FORMAT_NV61: + return "DRM_FORMAT_NV61"; + break; + case DRM_FORMAT_NV24: + return "DRM_FORMAT_NV24"; + break; + case DRM_FORMAT_NV42: + return "DRM_FORMAT_NV42"; + break; /* + * 2 plane YCbCr + * index 0 = Y plane, [39:0] Y3:Y2:Y1:Y0 little endian + * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian + */ + case DRM_FORMAT_NV15: + return "DRM_FORMAT_NV15"; + break; + /* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ + case DRM_FORMAT_P210: + return "DRM_FORMAT_P210"; + break; + /* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ + case DRM_FORMAT_P010: + return "DRM_FORMAT_P010"; + break; + /* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [12:4] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [12:4:12:4] little endian + */ + case DRM_FORMAT_P012: + return "DRM_FORMAT_P012"; + break; + /* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y little endian + * index 1 = Cr:Cb plane, [31:0] Cr:Cb [16:16] little endian + */ + case DRM_FORMAT_P016: + return "DRM_FORMAT_P016"; + break; + /* 3 plane non-subsampled (444) YCbCr + * 16 bits per component, but only 10 bits are used and 6 bits are padded + * index 0: Y plane, [15:0] Y:x [10:6] little endian + * index 1: Cb plane, [15:0] Cb:x [10:6] little endian + * index 2: Cr plane, [15:0] Cr:x [10:6] little endian + */ + case DRM_FORMAT_Q410: + return "DRM_FORMAT_Q410"; + break; + /* 3 plane non-subsampled (444) YCrCb + * 16 bits per component, but only 10 bits are used and 6 bits are padded + * index 0: Y plane, [15:0] Y:x [10:6] little endian + * index 1: Cr plane, [15:0] Cr:x [10:6] little endian + * index 2: Cb plane, [15:0] Cb:x [10:6] little endian + */ + case DRM_FORMAT_Q401: + return "DRM_FORMAT_Q401"; + break; + /* + * 3 plane YCbCr + * index 0: Y plane, [7:0] Y + * index 1: Cb plane, [7:0] Cb + * index 2: Cr plane, [7:0] Cr + * or + * index 1: Cr plane, [7:0] Cr + * index 2: Cb plane, [7:0] Cb + */ + case DRM_FORMAT_YUV410: + return "DRM_FORMAT_YUV410"; + break; + case DRM_FORMAT_YVU410: + return "DRM_FORMAT_YVU410"; + break; + case DRM_FORMAT_YUV411: + return "DRM_FORMAT_YUV411"; + break; + case DRM_FORMAT_YVU411: + return "DRM_FORMAT_YVU411"; + break; + case DRM_FORMAT_YUV420: + return "DRM_FORMAT_YUV420"; + break; + case DRM_FORMAT_YVU420: + return "DRM_FORMAT_YVU420"; + break; + case DRM_FORMAT_YUV422: + return "DRM_FORMAT_YUV422"; + break; + case DRM_FORMAT_YVU422: + return "DRM_FORMAT_YVU422"; + break; + case DRM_FORMAT_YUV444: + return "DRM_FORMAT_YUV444"; + break; + case DRM_FORMAT_YVU444: + return "DRM_FORMAT_YVU444"; + break; + default: + return "DRM_FORMAT_UNKNOWN"; + break; + } +} + diff --git a/video/out/drm_common.h b/video/out/drm_common.h index 5d884e3c80..cd7f87e506 100644 --- a/video/out/drm_common.h +++ b/video/out/drm_common.h @@ -97,4 +97,6 @@ double kms_get_display_fps(const struct kms *kms); void drm_pflip_cb(int fd, unsigned int msc, unsigned int sec, unsigned int usec, void *data); +const char* drm_format_string(uint drm_format); + #endif diff --git a/video/out/vo.c b/video/out/vo.c index 11ef596227..6b38711acf 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -62,6 +62,7 @@ extern const struct vo_driver video_out_drm; extern const struct vo_driver video_out_direct3d; extern const struct vo_driver video_out_sdl; extern const struct vo_driver video_out_vaapi; +extern const struct vo_driver video_out_vaapi_wayland; extern const struct vo_driver video_out_wlshm; extern const struct vo_driver video_out_rpi; extern const struct vo_driver video_out_tct; @@ -92,7 +93,9 @@ const struct vo_driver *const video_out_drivers[] = #if HAVE_SDL2_VIDEO &video_out_sdl, #endif -#if HAVE_VAAPI_X11 && HAVE_GPL +#if HAVE_VAAPI_WAYLAND + &video_out_vaapi_wayland, +#elif HAVE_VAAPI_X11 && HAVE_GPL &video_out_vaapi, #endif #if HAVE_X11 diff --git a/video/out/vo_vaapi_wayland.c b/video/out/vo_vaapi_wayland.c new file mode 100644 index 0000000000..931b88229a --- /dev/null +++ b/video/out/vo_vaapi_wayland.c @@ -0,0 +1,401 @@ +/* + * This file is part of mpv video player. + * + * mpv 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. + * + * mpv 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 mpv. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "sub/osd.h" +#include "video/vaapi.h" +#include "wayland_common.h" +#include "drm_common.h" +#include "generated/wayland/linux-dmabuf-unstable-v1.h" +#include "generated/wayland/viewporter.h" + +#define VA_POOL_NUM_ALLOCATED_INIT 30 + +struct va_pool_entry { + /* key */ + VASurfaceID surface; + + VADRMPRIMESurfaceDescriptor desc; + struct wl_buffer *buffer; + struct zwp_linux_buffer_params_v1 *params; + uint drm_format; +}; +struct va_pool { + struct vo *vo; + struct va_pool_entry **entries; + uint num_entries; + uint num_allocated; +}; + +struct priv { + struct vo *vo; + struct mp_rect src; + struct mp_rect dst; + struct mp_osd_res osd; + struct mp_log *log; + + VADisplay display; + struct mp_vaapi_ctx *mpvaapi; + struct va_image_formats *image_formats; + struct wl_shm_pool *solid_buffer_pool; + struct wl_buffer *solid_buffer; + struct va_pool *va_pool; +}; + +static void va_close_surface_descriptor(VADRMPRIMESurfaceDescriptor desc) { + for (uint i = 0; i < desc.num_objects; i++) { + close(desc.objects[i].fd); + desc.objects[i].fd = 0; + } +} + +static void va_free_entry(struct va_pool_entry *entry) { + if (!entry) + return; + va_close_surface_descriptor(entry->desc); + if (entry->buffer) + wl_buffer_destroy(entry->buffer); + if (entry->params) + zwp_linux_buffer_params_v1_destroy(entry->params); + talloc_free(entry); +} + +static VAStatus va_export_surface_handle(VADisplay display, VASurfaceID surface, + VADRMPRIMESurfaceDescriptor *desc) { + return vaExportSurfaceHandle(display, surface, + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + VA_EXPORT_SURFACE_COMPOSED_LAYERS | VA_EXPORT_SURFACE_READ_ONLY, + desc); +} + +static struct va_pool_entry* va_alloc_entry(struct vo *vo, struct mp_image *src) { + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + VAStatus status; + struct va_pool_entry *entry = talloc(NULL, struct va_pool_entry); + memset(entry, 0, sizeof(struct va_pool_entry)); + + /* extract dmabuf surface descriptor */ + entry->surface = va_surface_id(src); + status = va_export_surface_handle(p->display, entry->surface, &entry->desc); + if (status == VA_STATUS_ERROR_INVALID_SURFACE) { + MP_VERBOSE(vo, "VA export to composed layers not supported.\n"); + va_free_entry(entry); + return NULL; + } else if (!vo_wayland_supported_format(vo, + entry->desc.layers[0].drm_format)) { + MP_VERBOSE(vo, "%s not supported.\n", + drm_format_string(entry->desc.layers[0].drm_format)); + va_free_entry(entry); + return NULL; + } else if (!CHECK_VA_STATUS(vo, "vaExportSurfaceHandle()")) { + va_free_entry(entry); + return NULL; + } else { + uint i, j, plane = 0; + + entry->params = zwp_linux_dmabuf_v1_create_params(wl->dmabuf); + for (i = 0; i < entry->desc.num_layers; i++) { + entry->drm_format = entry->desc.layers[i].drm_format; + for (j = 0; j < entry->desc.layers[i].num_planes; ++j) { + int object = entry->desc.layers[i].object_index[j]; + uint64_t modifier = + entry->desc.objects[object].drm_format_modifier; + zwp_linux_buffer_params_v1_add(entry->params, + entry->desc.objects[object].fd, plane++, + entry->desc.layers[i].offset[j], + entry->desc.layers[i].pitch[j], modifier >> 32, + modifier & 0xffffffff); + } + } + } + entry->buffer = zwp_linux_buffer_params_v1_create_immed(entry->params, + src->params.w, src->params.h, entry->drm_format, 0); + + return entry; +} +static void va_pool_clean(struct va_pool *pool) { + if (!pool) + return; + + for (uint i = 0; i < pool->num_entries; ++i) + va_free_entry(pool->entries[i]); + pool->num_entries = 0; +} + +static void va_pool_free(struct va_pool *pool) { + if (!pool) + return; + + va_pool_clean(pool); + talloc_free(pool->entries); + talloc_free(pool); +} +static struct va_pool* va_pool_alloc(struct vo *vo) { + struct va_pool *pool = talloc(NULL, struct va_pool); + memset(pool, 0, sizeof(struct va_pool)); + pool->num_allocated = VA_POOL_NUM_ALLOCATED_INIT; + pool->entries = talloc_array(NULL,struct va_pool_entry*, pool->num_allocated); + memset(pool->entries,0,pool->num_allocated * sizeof(struct va_pool_entry*)); + pool->vo = vo; + + return pool; +} +static struct va_pool_entry* va_pool_alloc_entry(struct vo *vo, + struct va_pool *pool, struct mp_image *src) { + VASurfaceID surface; + + if (!pool) + return NULL; + + surface = va_surface_id(src); + for (uint i = 0; i < pool->num_entries; ++i) { + struct va_pool_entry *item = pool->entries[i]; + if (item->surface == surface) + return pool->entries[i]; + } + + struct va_pool_entry *entry = va_alloc_entry(pool->vo, src); + if (!entry) + return NULL; + + if (pool->num_entries == pool->num_allocated) { + uint current_num_allocated = pool->num_allocated; + pool->num_allocated *= 2; + pool->entries = talloc_realloc(NULL,pool->entries,struct va_pool_entry*, pool->num_allocated); + for (uint i = current_num_allocated; i < pool->num_allocated; ++i) + pool->entries[i] = NULL; + } + pool->entries[pool->num_entries++] = entry; + + return entry; +} + +struct va_image_formats { + VAImageFormat *entries; + int num; +}; + +static void va_get_formats(struct priv *ctx) { + struct va_image_formats *formats = talloc_ptrtype(ctx, formats); + + formats->num = vaMaxNumImageFormats(ctx->display); + formats->entries = talloc_array(formats, VAImageFormat, formats->num); + VAStatus status = vaQueryImageFormats(ctx->display, formats->entries, + &formats->num); + if (!CHECK_VA_STATUS(ctx, "vaQueryImageFormats()")) + return; + MP_VERBOSE(ctx, "%d image formats available:\n", formats->num); + for (int i = 0; i < formats->num; i++) + MP_VERBOSE(ctx, " %s\n", mp_tag_str(formats->entries[i].fourcc)); + ctx->image_formats = formats; +} + +static void uninit(struct vo *vo) { + struct priv *p = vo->priv; + + va_pool_free(p->va_pool); + if (p->solid_buffer_pool) + wl_shm_pool_destroy(p->solid_buffer_pool); + if (p->solid_buffer) + wl_buffer_destroy(p->solid_buffer); + vo_wayland_uninit(vo); + + if (vo->hwdec_devs) { + hwdec_devices_remove(vo->hwdec_devs, &p->mpvaapi->hwctx); + hwdec_devices_destroy(vo->hwdec_devs); + } + if (p->mpvaapi) + va_destroy(p->mpvaapi); +} +static int allocate_memfd(size_t size) { + int fd = memfd_create("mpv", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd < 0) + return VO_ERROR; + + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + + if (posix_fallocate(fd, 0, size) == 0) + return fd; + + close(fd); + return VO_ERROR; +} + +static int preinit(struct vo *vo) { + struct priv *p = vo->priv; + + p->vo = vo; + p->log = vo->log; + if (!vo_wayland_init(vo)) + return VO_ERROR; + p->display = vaGetDisplayWl(vo->wl->display); + if (!p->display) + return VO_ERROR; + p->mpvaapi = va_initialize(p->display, p->log, false); + if (!p->mpvaapi) { + vaTerminate(p->display); + p->display = NULL; + goto fail; + } + va_get_formats(p); + if (!p->image_formats) + goto fail; + + vo->hwdec_devs = hwdec_devices_create(); + hwdec_devices_add(vo->hwdec_devs, &p->mpvaapi->hwctx); + p->va_pool = va_pool_alloc(vo); + + return 0; + +fail: + uninit(vo); + + return VO_ERROR; +} + +static int query_format(struct vo *vo, int format) { + return format == IMGFMT_VAAPI; +} + +static int reconfig(struct vo *vo, struct mp_image_params *params) { + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + + if (!p->solid_buffer_pool) { + int width = 1; + int height = 1; + int stride = MP_ALIGN_UP(width * 4, 16); + int fd = allocate_memfd(stride); + if (fd < 0) + return VO_ERROR; + p->solid_buffer_pool = wl_shm_create_pool(wl->shm, fd, height * stride); + if (!p->solid_buffer_pool) + return VO_ERROR; + p->solid_buffer = wl_shm_pool_create_buffer(p->solid_buffer_pool, 0, + width, height, stride, WL_SHM_FORMAT_XRGB8888); + if (!p->solid_buffer) + return VO_ERROR; + } + if (!vo_wayland_reconfig(vo)) + return VO_ERROR; + + return 0; +} + +static int resize(struct vo *vo) { + struct vo_wayland_state *wl = vo->wl; + struct priv *p = vo->priv; + + wl_subsurface_set_sync(wl->video_subsurface); + vo_wayland_set_opaque_region(wl, 0); + const int32_t width = wl->scaling * mp_rect_w(wl->geometry); + const int32_t height = wl->scaling * mp_rect_h(wl->geometry); + vo->dwidth = width; + vo->dheight = height; + vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd); + wp_viewport_set_destination(wl->viewport, + (p->dst.x0 << 1) + mp_rect_w(p->dst), + (p->dst.y0 << 1) + mp_rect_h(p->dst)); + wp_viewport_set_destination(wl->video_viewport, mp_rect_w(p->dst), + mp_rect_h(p->dst)); + wl_subsurface_set_position(wl->video_subsurface, p->dst.x0, p->dst.y0); + vo->want_redraw = true; + wl_subsurface_set_desync(wl->video_subsurface); + + return VO_TRUE; +} + +static int control(struct vo *vo, uint32_t request, void *data) { + struct priv *p = vo->priv; + int events = 0; + int ret; + + switch (request) { + /* need to clean pool after seek to avoid artifacts */ + case VOCTRL_RESET: + va_pool_clean(p->va_pool); + break; + default: + break; + } + ret = vo_wayland_control(vo, &events, request, data); + if (events & VO_EVENT_RESIZE) + ret = resize(vo); + if (events & VO_EVENT_EXPOSE) + vo->want_redraw = true; + vo_event(vo, events); + + return ret; +} + +static void draw_frame(struct vo *vo, struct vo_frame *frame) { + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + + if (!vo_wayland_check_visible(vo)) + return; + + struct va_pool_entry *entry = va_pool_alloc_entry(vo, p->va_pool, + frame->current); + if (!entry) + return; + + wl_surface_attach(wl->surface, p->solid_buffer, 0, 0); + wl_surface_attach(wl->video_surface, entry->buffer, 0, 0); + wl_surface_damage_buffer(wl->video_surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(wl->video_surface); + wl_surface_commit(wl->surface); + + if (!wl->opts->disable_vsync) + vo_wayland_wait_frame(wl); + if (wl->presentation) + vo_wayland_sync_swap(wl); +} +static void flip_page(struct vo *vo) { + /* no-op */ +} + +static void get_vsync(struct vo *vo, struct vo_vsync_info *info) { + struct vo_wayland_state *wl = vo->wl; + if (wl->presentation) { + info->vsync_duration = wl->vsync_duration; + info->skipped_vsyncs = wl->last_skipped_vsyncs; + info->last_queue_display_time = wl->last_queue_display_time; + } +} + +const struct vo_driver video_out_vaapi_wayland = { .description = + "VA API with Wayland video output", + .name = "vaapi-wayland", + .preinit = preinit, + .query_format = query_format, + .reconfig = reconfig, + .control = control, + .draw_frame = draw_frame, + .flip_page = flip_page, + .get_vsync = get_vsync, + .wakeup = vo_wayland_wakeup, + .wait_events = vo_wayland_wait_events, + .uninit = uninit, + .priv_size = sizeof(struct priv) +}; diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index cd97c77bf6..93a44fde7a 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -32,17 +32,21 @@ #include "osdep/timer.h" #include "wayland_common.h" #include "win_state.h" +#include "drm_common.h" // Generated from wayland-protocols #include "generated/wayland/idle-inhibit-unstable-v1.h" #include "generated/wayland/presentation-time.h" #include "generated/wayland/xdg-decoration-unstable-v1.h" #include "generated/wayland/xdg-shell.h" +#include "generated/wayland/linux-dmabuf-unstable-v1.h" +#include "generated/wayland/viewporter.h" #if WAYLAND_VERSION_MAJOR > 1 || WAYLAND_VERSION_MINOR >= 20 #define HAVE_WAYLAND_1_20 #endif + static const struct mp_keymap keymap[] = { /* Special keys */ {XKB_KEY_Pause, MP_KEY_PAUSE}, {XKB_KEY_Escape, MP_KEY_ESC}, @@ -1039,6 +1043,41 @@ static const struct wl_callback_listener frame_listener = { frame_callback, }; +static void dmabuf_format(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) { + struct vo_wayland_state *wl = data; + + if (wl->drm_format_ct == wl->drm_format_ct_max) { + wl->drm_format_ct_max *= 2; + wl->drm_formats = talloc_realloc(NULL,wl->drm_formats,uint, wl->drm_format_ct_max); + } + wl->drm_formats[wl->drm_format_ct++] = format; + MP_VERBOSE(wl, "%s available\n", drm_format_string(format)); +} + +bool vo_wayland_supported_format(struct vo *vo, uint drm_format) { + struct vo_wayland_state *wl = vo->wl; + + for (uint i = 0; i < wl->drm_format_ct; ++i) { + if (drm_format == wl->drm_formats[i]) + return true; + } + + return false; +} + +/* currently unused */ +static void dmabuf_modifier(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format, + uint32_t modifier_hi, uint32_t modifier_lo) { + +} + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + dmabuf_format, + dmabuf_modifier +}; + static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) { @@ -1048,10 +1087,34 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id if (!strcmp(interface, wl_compositor_interface.name) && (ver >= 4) && found++) { wl->compositor = wl_registry_bind(reg, id, &wl_compositor_interface, 4); wl->surface = wl_compositor_create_surface(wl->compositor); + wl->video_surface = wl_compositor_create_surface(wl->compositor); + /* never accept input events on the video surface */ + struct wl_region *region = wl_compositor_create_region (wl->compositor); + wl_surface_set_input_region (wl->video_surface, region); + wl_region_destroy (region); wl->cursor_surface = wl_compositor_create_surface(wl->compositor); wl_surface_add_listener(wl->surface, &surface_listener, wl); } + if (!strcmp(interface, wl_subcompositor_interface.name) && (ver >= 1) && found++) { + wl->subcompositor = wl_registry_bind(reg, id, &wl_subcompositor_interface, 1); + wl->video_subsurface = wl_subcompositor_get_subsurface(wl->subcompositor, wl->video_surface, wl->surface); + wl_subsurface_set_desync (wl->video_subsurface); + } + + if (!strcmp (interface, zwp_linux_dmabuf_v1_interface.name) && (ver >= 2) && found++) { + wl->dmabuf = wl_registry_bind (reg, id, &zwp_linux_dmabuf_v1_interface, 2); + zwp_linux_dmabuf_v1_add_listener (wl->dmabuf, &dmabuf_listener, wl); + wl->drm_format_ct_max = 64; + wl->drm_formats = talloc_array(NULL,uint,wl->drm_format_ct_max); + } + + if (!strcmp (interface, wp_viewporter_interface.name) && (ver >= 1) && found++) { + wl->viewporter = wl_registry_bind (reg, id, &wp_viewporter_interface, 1); + wl->viewport = wp_viewporter_get_viewport (wl->viewporter, wl->surface); + wl->video_viewport = wp_viewporter_get_viewport (wl->viewporter,wl->video_surface); + } + if (!strcmp(interface, wl_data_device_manager_interface.name) && (ver >= 3) && found++) { wl->dnd_devman = wl_registry_bind(reg, id, &wl_data_device_manager_interface, 3); } @@ -1938,6 +2001,9 @@ void vo_wayland_uninit(struct vo *vo) if (wl->compositor) wl_compositor_destroy(wl->compositor); + if (wl->subcompositor) + wl_subcompositor_destroy(wl->subcompositor); + if (wl->current_output && wl->current_output->output) wl_output_destroy(wl->current_output->output); @@ -1980,6 +2046,18 @@ void vo_wayland_uninit(struct vo *vo) if (wl->registry) wl_registry_destroy(wl->registry); + if (wl->viewporter) + wp_viewporter_destroy (wl->viewporter); + + if (wl->viewport) + wp_viewport_destroy (wl->viewport); + + if (wl->video_viewport) + wp_viewport_destroy (wl->video_viewport); + + if (wl->dmabuf) + zwp_linux_dmabuf_v1_destroy (wl->dmabuf); + if (wl->seat) wl_seat_destroy(wl->seat); @@ -1989,6 +2067,12 @@ void vo_wayland_uninit(struct vo *vo) if (wl->surface) wl_surface_destroy(wl->surface); + if (wl->video_surface) + wl_surface_destroy(wl->video_surface); + + if (wl->video_subsurface) + wl_subsurface_destroy(wl->video_subsurface); + if (wl->wm_base) xdg_wm_base_destroy(wl->wm_base); @@ -2022,6 +2106,8 @@ void vo_wayland_uninit(struct vo *vo) wl_list_for_each_safe(output, tmp, &wl->output_list, link) remove_output(output); + talloc_free(wl->drm_formats); + talloc_free(wl->dnd_mime_type); for (int n = 0; n < 2; n++) diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index faca1cd19b..35999e2e20 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -36,10 +36,20 @@ struct vo_wayland_state { struct wayland_opts *opts; struct wl_callback *frame_callback; struct wl_compositor *compositor; + struct wl_subcompositor *subcompositor; struct wl_display *display; struct wl_registry *registry; + struct wp_viewporter *viewporter; + struct wp_viewport *viewport; + struct zwp_linux_dmabuf_v1 *dmabuf; + uint *drm_formats; + uint drm_format_ct; + uint drm_format_ct_max; struct wl_shm *shm; struct wl_surface *surface; + struct wl_surface *video_surface; + struct wl_subsurface *video_subsurface; + struct wp_viewport *video_viewport; /* Geometry */ struct mp_rect geometry; @@ -137,5 +147,6 @@ void vo_wayland_uninit(struct vo *vo); void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us); void vo_wayland_wait_frame(struct vo_wayland_state *wl); void vo_wayland_wakeup(struct vo *vo); +bool vo_wayland_supported_format(struct vo *vo,uint format); #endif /* MPLAYER_WAYLAND_COMMON_H */ diff --git a/wscript b/wscript index 2532a86ae4..1e70633e38 100644 --- a/wscript +++ b/wscript @@ -649,6 +649,11 @@ video_output_features = [ 'desc': 'VAAPI (Wayland support)', 'deps': 'vaapi && gl-wayland', 'func': check_pkg_config('libva-wayland', '>= 1.1.0'), + }, { + 'name': 'vaapi-wayland-memfd', + 'desc': 'VAAPI (Wayland support)', + 'deps': 'vaapi-wayland && memfd_create', + 'func': check_true, }, { 'name': '--vaapi-drm', 'desc': 'VAAPI (DRM/EGL support)', diff --git a/wscript_build.py b/wscript_build.py index 46191b8196..c05df1f964 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -127,6 +127,18 @@ def build(ctx): ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR, protocol = "unstable/xdg-decoration/xdg-decoration-unstable-v1", target = "generated/wayland/xdg-decoration-unstable-v1.h") + ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "unstable/linux-dmabuf/linux-dmabuf-unstable-v1", + target = "generated/wayland/linux-dmabuf-unstable-v1.c") + ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "unstable/linux-dmabuf/linux-dmabuf-unstable-v1", + target = "generated/wayland/linux-dmabuf-unstable-v1.h") + ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "stable/viewporter/viewporter", + target = "generated/wayland/viewporter.c") + ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "stable/viewporter/viewporter", + target = "generated/wayland/viewporter.h") ctx(features = "ebml_header", target = "generated/ebml_types.h") ctx(features = "ebml_definitions", target = "generated/ebml_defs.inc") @@ -506,6 +518,7 @@ def build(ctx): ( "video/out/vo_sixel.c", "sixel" ), ( "video/out/vo_tct.c" ), ( "video/out/vo_vaapi.c", "vaapi-x11 && gpl" ), + ( "video/out/vo_vaapi_wayland.c", "vaapi-wayland-memfd" ), ( "video/out/vo_vdpau.c", "vdpau" ), ( "video/out/vo_wlshm.c", "wayland && memfd_create" ), ( "video/out/vo_x11.c" , "x11" ), @@ -522,6 +535,8 @@ def build(ctx): ( "generated/wayland/presentation-time.c", "wayland" ), ( "generated/wayland/xdg-decoration-unstable-v1.c", "wayland" ), ( "generated/wayland/xdg-shell.c", "wayland" ), + ( "generated/wayland/linux-dmabuf-unstable-v1.c", "wayland" ), + ( "generated/wayland/viewporter.c", "wayland" ), ( "video/out/wayland_common.c", "wayland" ), ( "video/out/win32/displayconfig.c", "win32-desktop" ), ( "video/out/win32/droptarget.c", "win32-desktop" ),