summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Boxer <boxerab@protonmail.com>2022-09-27 12:12:54 -0400
committerDudemanguy <random342@airmail.cc>2022-10-26 18:41:47 +0000
commit7358b9d37126e824cbc3a226d832e9cc1d7b01ea (patch)
treec477ad6f03145563f31e9877e583a9a50c4c6946
parent964692ad4cec90888bb437064c53d8844db9f590 (diff)
downloadmpv-7358b9d37126e824cbc3a226d832e9cc1d7b01ea.tar.bz2
mpv-7358b9d37126e824cbc3a226d832e9cc1d7b01ea.tar.xz
vo_dmabuf_wayland: wayland VO displaying dmabuf buffers
Wayland VO that can display images from either vaapi or drm hwdec The PR adds the following changes: 1. a context_wldmabuf context with no gl dependencies 2. no-op ra_wldmabuf and dmabuf_interop_wldmabuf objects no-op because there is no need to map/unmap the drmprime buffer, and there is no need to manage any textures. Tested on both x86_64 and rk3399 AArch64
-rw-r--r--DOCS/man/options.rst5
-rw-r--r--filters/f_autoconvert.c12
-rw-r--r--filters/f_hwtransfer.c8
-rw-r--r--meson.build9
-rw-r--r--video/out/gpu/context.c7
-rw-r--r--video/out/hwdec/dmabuf_interop.h2
-rw-r--r--video/out/hwdec/dmabuf_interop_wl.c57
-rw-r--r--video/out/hwdec/hwdec_drmprime.c6
-rw-r--r--video/out/hwdec/hwdec_vaapi.c14
-rw-r--r--video/out/vo.c6
-rw-r--r--video/out/vo_dmabuf_wayland.c361
-rw-r--r--video/out/wlbuf_pool.c190
-rw-r--r--video/out/wlbuf_pool.h65
-rw-r--r--video/out/wldmabuf/context_wldmabuf.c44
-rw-r--r--video/out/wldmabuf/ra_wldmabuf.c44
-rw-r--r--video/out/wldmabuf/ra_wldmabuf.h22
-rw-r--r--wscript9
-rw-r--r--wscript_build.py5
18 files changed, 853 insertions, 13 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 682ab2d178..11e5cd732d 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -6150,6 +6150,8 @@ them.
X11/EGL
android
Android/EGL. Requires ``--wid`` be set to an ``android.view.Surface``.
+ wldmabuf
+ dmabuf buffers are rendered directly by Wayland compositor
``--gpu-api=<type>``
Controls which type of graphics APIs will be accepted:
@@ -6162,6 +6164,9 @@ them.
Allow only Vulkan (requires a valid/working ``--spirv-compiler``)
d3d11
Allow only ``--gpu-context=d3d11``
+ none
+ No graphics API is used - Wayland alone is used for rendering
+ (requires Wayland compositor)
``--opengl-es=<mode>``
Controls which type of OpenGL context will be accepted:
diff --git a/filters/f_autoconvert.c b/filters/f_autoconvert.c
index b55673b448..fd0bb557b4 100644
--- a/filters/f_autoconvert.c
+++ b/filters/f_autoconvert.c
@@ -195,12 +195,13 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log,
bool hw_to_sw = !imgfmt_is_sw && dst_have_sw;
if (dst_all_hw && num_fmts > 0) {
+ bool upload_created = false;
+ int sw_fmt = imgfmt_is_sw ? img->imgfmt : img->params.hw_subfmt;
+
for (int i = 0; i < num_fmts; i++) {
// We can probably use this! Very lazy and very approximate.
struct mp_hwupload *upload = mp_hwupload_create(conv, fmts[i]);
if (upload) {
- int sw_fmt = imgfmt_is_sw ? img->imgfmt : img->params.hw_subfmt;
-
mp_info(log, "HW-uploading to %s\n", mp_imgfmt_to_name(fmts[i]));
filters[2] = upload->f;
hwupload_fmt = mp_hwupload_find_upload_format(upload, sw_fmt);
@@ -215,9 +216,16 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log,
mp_err(log, "Format %s is not supported by %s\n",
mp_imgfmt_to_name(sw_fmt),
mp_imgfmt_to_name(p->imgfmts[i]));
+ continue;
}
+ upload_created = true;
+ break;
}
}
+ if (!upload_created) {
+ mp_err(log, "Failed to create HW uploader for format %s\n",
+ mp_imgfmt_to_name(sw_fmt));
+ }
}
int src_fmt = img->imgfmt;
diff --git a/filters/f_hwtransfer.c b/filters/f_hwtransfer.c
index 4615a982a0..83847b656a 100644
--- a/filters/f_hwtransfer.c
+++ b/filters/f_hwtransfer.c
@@ -309,7 +309,7 @@ static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt)
}
if (!ctx) {
- MP_ERR(u->f, "no support for this hw format\n");
+ MP_INFO(u->f, "no support for this hw format\n");
return false;
}
@@ -410,12 +410,12 @@ struct mp_hwupload *mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt)
mp_filter_add_pin(f, MP_PIN_OUT, "out");
if (!probe_formats(u, hw_imgfmt)) {
- MP_ERR(f, "hardware format not supported\n");
- goto error;
+ MP_INFO(f, "hardware format not supported\n");
+ goto fail;
}
return u;
-error:
+fail:
talloc_free(f);
return NULL;
}
diff --git a/meson.build b/meson.build
index 2783490a2e..c3b8c256c5 100644
--- a/meson.build
+++ b/meson.build
@@ -996,6 +996,9 @@ features += {'wayland': wayland_deps and wayland['header'] and wayland['scanner'
if features['wayland']
subdir(join_paths('generated', 'wayland'))
+ sources += files('video/out/wldmabuf/context_wldmabuf.c')
+ sources += files('video/out/wldmabuf/ra_wldmabuf.c')
+ sources += files('video/out/wlbuf_pool.c')
endif
features += {'memfd_create': false}
@@ -1404,6 +1407,12 @@ if features['dmabuf-interop-pl']
sources += files('video/out/hwdec/dmabuf_interop_pl.c')
endif
+features += {'dmabuf-wayland' : features['wayland'] and features['memfd_create'] and (features['vaapi-wayland'] or features['drm'])}
+if features['dmabuf-wayland']
+ sources += files('video/out/vo_dmabuf_wayland.c')
+ sources += files('video/out/hwdec/dmabuf_interop_wl.c')
+endif
+
vdpau_opt = get_option('vdpau').require(
features['x11'],
error_message: 'x11 was not found!',
diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c
index e0beb845c2..052e3cdc80 100644
--- a/video/out/gpu/context.c
+++ b/video/out/gpu/context.c
@@ -55,6 +55,9 @@ extern const struct ra_ctx_fns ra_ctx_vulkan_display;
/* Direct3D 11 */
extern const struct ra_ctx_fns ra_ctx_d3d11;
+/* No API */
+extern const struct ra_ctx_fns ra_ctx_wldmabuf;
+
static const struct ra_ctx_fns *contexts[] = {
#if HAVE_D3D11
&ra_ctx_d3d11,
@@ -108,7 +111,11 @@ static const struct ra_ctx_fns *contexts[] = {
&ra_ctx_vulkan_xlib,
#endif
&ra_ctx_vulkan_display,
+#endif
+/* No API contexts: */
+#if HAVE_WAYLAND
+ &ra_ctx_wldmabuf,
#endif
};
diff --git a/video/out/hwdec/dmabuf_interop.h b/video/out/hwdec/dmabuf_interop.h
index 32dba72bee..90c96399d7 100644
--- a/video/out/hwdec/dmabuf_interop.h
+++ b/video/out/hwdec/dmabuf_interop.h
@@ -53,3 +53,5 @@ bool dmabuf_interop_gl_init(const struct ra_hwdec *hw,
struct dmabuf_interop *dmabuf_interop);
bool dmabuf_interop_pl_init(const struct ra_hwdec *hw,
struct dmabuf_interop *dmabuf_interop);
+bool dmabuf_interop_wl_init(const struct ra_hwdec *hw,
+ struct dmabuf_interop *dmabuf_interop);
diff --git a/video/out/hwdec/dmabuf_interop_wl.c b/video/out/hwdec/dmabuf_interop_wl.c
new file mode 100644
index 0000000000..74b421d583
--- /dev/null
+++ b/video/out/hwdec/dmabuf_interop_wl.c
@@ -0,0 +1,57 @@
+/*
+ * This file is part of mpv.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include "video/out/wldmabuf/ra_wldmabuf.h"
+#include "config.h"
+#include "dmabuf_interop.h"
+
+static bool mapper_init(struct ra_hwdec_mapper *mapper,
+ const struct ra_imgfmt_desc *desc)
+{
+ struct dmabuf_interop_priv *p = mapper->priv;
+
+ p->num_planes = 1;
+ return true;
+}
+
+static void mapper_uninit(const struct ra_hwdec_mapper *mapper)
+{
+}
+
+static bool map(struct ra_hwdec_mapper *mapper,
+ struct dmabuf_interop *dmabuf_interop,
+ bool probing)
+{
+ return true;
+}
+
+static void unmap(struct ra_hwdec_mapper *mapper)
+{
+}
+
+bool dmabuf_interop_wl_init(const struct ra_hwdec *hw,
+ struct dmabuf_interop *dmabuf_interop)
+{
+ if (!ra_is_wldmabuf(hw->ra))
+ return false;
+
+ dmabuf_interop->interop_init = mapper_init;
+ dmabuf_interop->interop_uninit = mapper_uninit;
+ dmabuf_interop->interop_map = map;
+ dmabuf_interop->interop_unmap = unmap;
+
+ return true;
+}
diff --git a/video/out/hwdec/hwdec_drmprime.c b/video/out/hwdec/hwdec_drmprime.c
index b5b22431cb..1328e19162 100644
--- a/video/out/hwdec/hwdec_drmprime.c
+++ b/video/out/hwdec/hwdec_drmprime.c
@@ -56,6 +56,9 @@ const static dmabuf_interop_init interop_inits[] = {
#if HAVE_DMABUF_INTEROP_PL
dmabuf_interop_pl_init,
#endif
+#if HAVE_DMABUF_WAYLAND
+ dmabuf_interop_wl_init,
+#endif
NULL
};
@@ -167,7 +170,8 @@ static int mapper_init(struct ra_hwdec_mapper *mapper)
struct ra_imgfmt_desc desc = {0};
- if (!ra_get_imgfmt_desc(mapper->ra, mapper->dst_params.imgfmt, &desc))
+ if (mapper->ra->num_formats &&
+ !ra_get_imgfmt_desc(mapper->ra, mapper->dst_params.imgfmt, &desc))
return -1;
p->num_planes = desc.num_planes;
diff --git a/video/out/hwdec/hwdec_vaapi.c b/video/out/hwdec/hwdec_vaapi.c
index 2161544a07..9b1d191fb4 100644
--- a/video/out/hwdec/hwdec_vaapi.c
+++ b/video/out/hwdec/hwdec_vaapi.c
@@ -52,7 +52,11 @@ static VADisplay *create_x11_va_display(struct ra *ra)
static VADisplay *create_wayland_va_display(struct ra *ra)
{
struct wl_display *wl = ra_get_native_resource(ra, "wl");
- return wl ? vaGetDisplayWl(wl) : NULL;
+ VADisplay rc = wl ? vaGetDisplayWl(wl) : NULL;
+ if (rc)
+ ra_add_native_resource(ra, "VADisplay", rc);
+
+ return rc;
}
#endif
@@ -124,6 +128,9 @@ const static dmabuf_interop_init interop_inits[] = {
#if HAVE_DMABUF_INTEROP_PL
dmabuf_interop_pl_init,
#endif
+#if HAVE_DMABUF_WAYLAND
+ dmabuf_interop_wl_init,
+#endif
NULL
};
@@ -217,8 +224,9 @@ static int mapper_init(struct ra_hwdec_mapper *mapper)
struct ra_imgfmt_desc desc = {0};
- if (!ra_get_imgfmt_desc(mapper->ra, mapper->dst_params.imgfmt, &desc))
- return -1;
+ if (mapper->ra->num_formats &&
+ !ra_get_imgfmt_desc(mapper->ra, mapper->dst_params.imgfmt, &desc))
+ return -1;
p->num_planes = desc.num_planes;
mp_image_set_params(&p->layout, &mapper->dst_params);
diff --git a/video/out/vo.c b/video/out/vo.c
index a689e85eb3..f4ecd3d7bf 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -63,6 +63,7 @@ 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_dmabuf_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;
@@ -93,7 +94,10 @@ const struct vo_driver *const video_out_drivers[] =
#if HAVE_SDL2_VIDEO
&video_out_sdl,
#endif
-#if HAVE_VAAPI_WAYLAND && HAVE_MEMFD_CREATE
+#if HAVE_DMABUF_WAYLAND
+ &video_out_dmabuf_wayland,
+#endif
+#if HAVE_VAAPI_WAYLAND
&video_out_vaapi_wayland,
#endif
#if HAVE_VAAPI_X11 && HAVE_GPL
diff --git a/video/out/vo_dmabuf_wayland.c b/video/out/vo_dmabuf_wayland.c
new file mode 100644
index 0000000000..f3c1fe36f9
--- /dev/null
+++ b/video/out/vo_dmabuf_wayland.c
@@ -0,0 +1,361 @@
+/*
+ * Based on vo_gl.c by Reimar Doeffinger.
+ *
+ * This file is part of mpv.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include <unistd.h>
+
+#include "config.h"
+
+#ifdef HAVE_VAAPI
+#include <va/va_drmcommon.h>
+#include <libavutil/hwcontext_drm.h>
+#endif
+
+#include "mpv_talloc.h"
+#include "common/global.h"
+#include "vo.h"
+#include "video/mp_image.h"
+
+#include "gpu/hwdec.h"
+#include "gpu/video.h"
+
+#include "video/vaapi.h"
+#include "present_sync.h"
+#include "wayland_common.h"
+#include "generated/wayland/linux-dmabuf-unstable-v1.h"
+#include "generated/wayland/viewporter.h"
+#include "wlbuf_pool.h"
+
+struct priv {
+ struct mp_log *log;
+ struct ra_ctx *ctx;
+ struct mpv_global *global;
+ struct ra_hwdec_ctx hwdec_ctx;
+ int events;
+
+ struct wl_shm_pool *solid_buffer_pool;
+ struct wl_buffer *solid_buffer;
+ struct wlbuf_pool *wlbuf_pool;
+ bool want_reset;
+ uint64_t reset_count;
+
+#ifdef HAVE_VAAPI
+ VADisplay display;
+#endif
+};
+
+#ifdef HAVE_VAAPI
+static uintptr_t vaapi_key_provider(struct mp_image *src)
+{
+ return va_surface_id(src);
+}
+
+/* va-api dmabuf importer */
+static bool vaapi_dmabuf_importer(struct mp_image *src, struct wlbuf_pool_entry* entry,
+ struct zwp_linux_buffer_params_v1 *params)
+{
+ struct priv *p = entry->vo->priv;
+ VADRMPRIMESurfaceDescriptor desc;
+ bool dmabuf_imported = false;
+ /* composed has single layer */
+ int layer_no = 0;
+ VAStatus status = vaExportSurfaceHandle(p->display, entry->key, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
+ VA_EXPORT_SURFACE_COMPOSED_LAYERS | VA_EXPORT_SURFACE_READ_ONLY, &desc);
+
+ if (status == VA_STATUS_ERROR_INVALID_SURFACE) {
+ MP_VERBOSE(entry->vo, "VA export to composed layers not supported.\n");
+ } else if (!vo_wayland_supported_format(entry->vo, desc.layers[layer_no].drm_format)) {
+ MP_VERBOSE(entry->vo, "%s is not supported.\n", mp_tag_str(desc.layers[layer_no].drm_format));
+ } else if (CHECK_VA_STATUS(entry->vo, "vaExportSurfaceHandle()")) {
+ entry->drm_format = desc.layers[layer_no].drm_format;
+ for (int plane_no = 0; plane_no < desc.layers[layer_no].num_planes; ++plane_no) {
+ int object = desc.layers[layer_no].object_index[plane_no];
+ uint64_t modifier = desc.objects[object].drm_format_modifier;
+ zwp_linux_buffer_params_v1_add(params, desc.objects[object].fd, plane_no, desc.layers[layer_no].offset[plane_no],
+ desc.layers[layer_no].pitch[plane_no], modifier >> 32, modifier & 0xffffffff);
+ }
+ dmabuf_imported = true;
+ }
+
+ /* clean up descriptor */
+ if (status != VA_STATUS_ERROR_INVALID_SURFACE) {
+ for (int i = 0; i < desc.num_objects; i++) {
+ close(desc.objects[i].fd);
+ desc.objects[i].fd = 0;
+ }
+ }
+
+ return dmabuf_imported;
+}
+#endif
+
+#if HAVE_DRM
+
+static uintptr_t drmprime_key_provider(struct mp_image *src)
+{
+ struct AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->planes[0];
+
+ AVDRMObjectDescriptor object = desc->objects[0];
+ return (uintptr_t)object.fd;
+}
+
+static bool drmprime_dmabuf_importer(struct mp_image *src, struct wlbuf_pool_entry *entry,
+ struct zwp_linux_buffer_params_v1 *params)
+{
+ int layer_no, plane_no;
+ const AVDRMFrameDescriptor *avdesc = (AVDRMFrameDescriptor *)src->planes[0];
+
+ for (layer_no = 0; layer_no < avdesc->nb_layers; layer_no++) {
+ AVDRMLayerDescriptor layer = avdesc->layers[layer_no];
+
+ entry->drm_format = layer.format;
+ for (plane_no = 0; plane_no < layer.nb_planes; ++plane_no) {
+ AVDRMPlaneDescriptor plane = layer.planes[plane_no];
+ int object_index = plane.object_index;
+ AVDRMObjectDescriptor object = avdesc->objects[object_index];
+ uint64_t modifier = object.format_modifier;
+
+ zwp_linux_buffer_params_v1_add(params, object.fd, plane_no, plane.offset,
+ plane.pitch, modifier >> 32, modifier & 0xffffffff);
+ }
+ }
+
+ return true;
+}
+#endif
+
+static void resize(struct vo *vo)
+{
+ struct vo_wayland_state *wl = vo->wl;
+ struct mp_rect src;
+ struct mp_rect dst;
+ struct mp_osd_res osd;
+ const int width = wl->scaling * mp_rect_w(wl->geometry);
+ const int height = wl->scaling * mp_rect_h(wl->geometry);
+
+ vo_wayland_set_opaque_region(wl, 0);
+ vo->dwidth = width;
+ vo->dheight = height;
+ vo_get_src_dst_rects(vo, &src, &dst, &osd);
+
+ if (wl->viewport)
+ wp_viewport_set_destination(wl->viewport, 2 * dst.x0 + mp_rect_w(dst), 2 * dst.y0 + mp_rect_h(dst));
+
+ if (wl->video_viewport)
+ wp_viewport_set_destination(wl->video_viewport, mp_rect_w(dst), mp_rect_h(dst));
+ wl_subsurface_set_position(wl->video_subsurface, dst.x0, dst.y0);
+ vo->want_redraw = true;
+}
+
+static void draw_frame(struct vo *vo, struct vo_frame *frame)
+{
+ struct priv *p = vo->priv;
+ struct vo_wayland_state *wl = vo->wl;
+ struct wlbuf_pool_entry *entry;
+
+ if (!vo_wayland_check_visible(vo))
+ return;
+
+ // ensure the pool is reset after hwdec seek,
+ // to avoid stutter artifact
+ p->reset_count++;
+ if (p->want_reset && p->reset_count <= 2){
+ wlbuf_pool_clean(p->wlbuf_pool);
+ if (p->reset_count == 2)
+ p->want_reset = false;
+ }
+
+ /* lazy initialization of buffer pool */
+ if (!p->wlbuf_pool) {
+#if HAVE_VAAPI
+ p->display = (VADisplay)ra_get_native_resource(p->ctx->ra, "VADisplay");
+ if (p->display)
+ p->wlbuf_pool = wlbuf_pool_alloc(vo, wl, vaapi_key_provider, vaapi_dmabuf_importer);
+#endif
+#if HAVE_DRM
+ if (!p->wlbuf_pool)
+ p->wlbuf_pool = wlbuf_pool_alloc(vo, wl, drmprime_key_provider, drmprime_dmabuf_importer);
+#endif
+ }
+ entry = wlbuf_pool_get_entry(p->wlbuf_pool, frame->current);
+ if (!entry)
+ return;
+
+ MP_VERBOSE(entry->vo, "Schedule buffer pool entry : %lu\n",entry->key );
+ wl_surface_attach(wl->video_surface, entry->buffer, 0, 0);
+ wl_surface_damage_buffer(wl->video_surface, 0, 0, INT32_MAX, INT32_MAX);
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct vo_wayland_state *wl = vo->wl;
+
+ wl_surface_commit(wl->video_surface);
+ wl_surface_commit(wl->surface);
+ if (!wl->opts->disable_vsync)
+ vo_wayland_wait_frame(wl);
+ if (wl->use_present)
+ present_sync_swap(wl->present);
+}
+
+static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
+{
+ struct vo_wayland_state *wl = vo->wl;
+
+ if (wl->use_present)
+ present_sync_get_info(wl->present, info);
+}
+
+static bool is_supported_fmt(int fmt)
+{
+ return (fmt == IMGFMT_DRMPRIME || fmt == IMGFMT_VAAPI);
+}
+
+static int query_format(struct vo *vo, int format)
+{
+ return is_supported_fmt(format);
+}
+
+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 = vo_wayland_allocate_memfd(vo, 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;
+ wl_surface_attach(wl->surface, p->solid_buffer, 0, 0);
+ }
+ if (!vo_wayland_reconfig(vo))
+ return VO_ERROR;
+
+ return 0;
+}
+
+static void call_request_hwdec_api(void *ctx, struct hwdec_imgfmt_request *params)
+{
+ // Roundabout way to run hwdec loading on the VO thread.
+ // Redirects to request_hwdec_api().
+ vo_control(ctx, VOCTRL_LOAD_HWDEC_API, params);
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+ int events = 0;
+ int ret;
+
+ switch (request) {
+ case VOCTRL_LOAD_HWDEC_API:
+ assert(p->hwdec_ctx.ra);
+ struct hwdec_imgfmt_request* req = (struct hwdec_imgfmt_request*)data;
+ if (!is_supported_fmt(req->imgfmt))
+ return 0;
+ ra_hwdec_ctx_load_fmt(&p->hwdec_ctx, vo->hwdec_devs, req);
+ return (p->hwdec_ctx.num_hwdecs > 0);
+ break;
+ case VOCTRL_RESET:
+ p->want_reset = true;
+ p->reset_count = 0;
+ return VO_TRUE;
+ break;
+ }
+
+ ret = vo_wayland_control(vo, &events, request, data);
+ if (events & VO_EVENT_RESIZE)
+ resize(vo);
+ if (events & VO_EVENT_EXPOSE)
+ vo->want_redraw = true;
+ vo_event(vo, events);
+
+ return ret;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (p->solid_buffer_pool)
+ wl_shm_pool_destroy(p->solid_buffer_pool);
+ if (p->solid_buffer)
+ wl_buffer_destroy(p->solid_buffer);
+ ra_hwdec_ctx_uninit(&p->hwdec_ctx);
+ if (vo->hwdec_devs) {
+ hwdec_devices_set_loader(vo->hwdec_devs, NULL, NULL);
+ hwdec_devices_destroy(vo->hwdec_devs);
+ }
+ wlbuf_pool_free(p->wlbuf_pool);
+ vo_wayland_uninit(vo);
+ ra_ctx_destroy(&p->ctx);
+}
+
+static int preinit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ struct ra_ctx_opts ctx_opts;
+
+ p->log = vo->log;
+ p->global = vo->global;
+ ctx_opts.context_name = "wldmabuf";
+ ctx_opts.context_type = "none";
+ ctx_opts.probing = false;
+ p->ctx = ra_ctx_create(vo, ctx_opts);
+ if (!p->ctx)
+ goto err_out;
+ assert(p->ctx->ra);
+ vo->hwdec_devs = hwdec_devices_create();
+ hwdec_devices_set_loader(vo->hwdec_devs, call_request_hwdec_api, vo);
+ assert(!p->hwdec_ctx.ra);
+ p->hwdec_ctx = (struct ra_hwdec_ctx) {
+ .log = p->log,
+ .global = p->global,
+ .ra = p->ctx->ra,
+ };
+ ra_hwdec_ctx_init(&p->hwdec_ctx, vo->hwdec_devs, NULL, false);
+
+ return 0;
+err_out:
+ uninit(vo);
+
+ return VO_ERROR;
+}
+
+const struct vo_driver video_out_dmabuf_wayland = {
+ .description = "Wayland dmabuf video output",
+ .name = "dmabuf-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/wlbuf_pool.c b/video/out/wlbuf_pool.c
new file mode 100644
index 0000000000..6f0d43752e
--- /dev/null
+++ b/video/out/wlbuf_pool.c
@@ -0,0 +1,190 @@
+/*
+ * This file is part of mpv.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include <unistd.h>
+
+#include "mpv_talloc.h"
+#include "common/global.h"
+#include "vo.h"
+#include "video/mp_image.h"
+
+#include "wayland_common.h"
+#include "generated/wayland/linux-dmabuf-unstable-v1.h"
+#include "pthread.h"
+#include "wlbuf_pool.h"
+
+#define WLBUF_POOL_NUM_ALLOCATED_INIT 30
+
+static void wlbuf_pool_entry_free(struct wlbuf_pool_entry *entry);
+
+struct wlbuf_pool *wlbuf_pool_alloc(struct vo *vo, struct vo_wayland_state *wl, wlbuf_pool_key_provider key_provider,
+ wlbuf_pool_dmabuf_importer dmabuf_importer)
+{
+ struct wlbuf_pool *pool = talloc(NULL, struct wlbuf_pool);
+ memset(pool, 0, sizeof(struct wlbuf_pool));
+ pool->num_allocated = WLBUF_POOL_NUM_ALLOCATED_INIT;
+ pool->entries = talloc_array(pool, struct wlbuf_pool_entry *, pool->num_allocated);
+ memset(pool->entries, 0, pool->num_allocated * sizeof(struct wlbuf_pool_entry *));
+ pool->vo = vo;
+ pool->key_provider = key_provider;
+ pool->dmabuf_importer = dmabuf_importer;
+ pthread_mutex_init(&pool->lock, NULL);
+ pool->wl = wl;
+
+ return pool;
+}
+
+void wlbuf_pool_clean(struct wlbuf_pool *pool)
+{
+ int i;
+ if (!pool)
+ return;
+ pthread_mutex_lock(&pool->lock);
+ MP_VERBOSE(pool->vo, "Begin clean pool\n");
+ for (i = 0; i < pool->num_allocated; ++i) {
+ struct wlbuf_pool_entry *entry = pool->entries[i];
+ if (!entry)
+ continue;
+ // force frame unref
+ if (pool->final_clean && entry->frame){
+ mp_image_unrefp(&entry->frame);
+ entry->frame = NULL;
+ }
+ wlbuf_pool_entry_free(entry);
+ pool->entries[i] = NULL;
+ }
+ pool->num_entries = 0;
+ MP_VERBOSE(pool->vo, "End clean pool\n");
+ pthread_mutex_unlock(&pool->lock);
+}
+
+void wlbuf_pool_free(struct wlbuf_pool *pool)
+{
+ if (!pool)
+ return;
+ pool->final_clean = true;
+ wlbuf_pool_clean(pool);
+ pthread_mutex_destroy(&pool->lock);
+ talloc_free(pool);
+}
+
+static void wlbuf_pool_entry_free(struct wlbuf_pool_entry *entry)
+{
+ if (!entry)
+ return;
+ if (entry->frame) {
+ MP_VERBOSE(entry->vo, "Pending free buffer pool entry : %lu\n",entry->key );
+ entry->pending_delete = true;
+ }
+ else {
+ MP_VERBOSE(entry->vo, "Free buffer pool entry : %lu\n",entry->key );
+ if (entry->buffer)
+ wl_buffer_destroy(entry->buffer);
+ entry->buffer = NULL;
+ talloc_free(entry);
+ }
+}
+
+static void wlbuf_pool_entry_release(void *data, struct wl_buffer *wl_buffer)
+{
+ struct wlbuf_pool_entry *entry = (struct wlbuf_pool_entry*)data;
+ struct mp_image *frame;
+ pthread_mutex_t *lock = entry->pool_lock;
+
+ MP_VERBOSE(entry->vo, "Release buffer pool entry : %lu\n",entry->key );
+ pthread_mutex_lock(lock);
+ frame = entry->frame;
+ entry->frame = NULL;
+ if (entry->pending_delete)
+ wlbuf_pool_entry_free(entry);
+ if (frame)
+ mp_image_unrefp(&frame);
+ pthread_mutex_unlock(lock);
+}
+
+static const struct wl_buffer_listener wlbuf_pool_listener = {
+ wlbuf_pool_entry_release,
+};
+
+struct wlbuf_pool_entry *wlbuf_pool_get_entry(struct wlbuf_pool *pool, struct mp_image *src)
+{
+ uintptr_t key;
+ struct wlbuf_pool_entry *entry;
+ struct vo_wayland_state *wl = pool->wl;
+ bool import_successful;
+ struct zwp_linux_buffer_params_v1 *params;
+
+ if (!pool || !src)
+ return NULL;
+
+ /* 1. try to find existing entry in pool */
+ src = mp_image_new_ref(src);
+ key = pool->key_provider(src);
+ pthread_mutex_lock(&pool->lock);
+ for (int i = 0; i < pool->num_entries; ++i) {
+ struct wlbuf_pool_entry *item = pool->entries[i];
+ if (item->key == key) {
+ pthread_mutex_unlock(&pool->lock);
+ if (item->frame){
+ mp_image_unrefp(&src);
+ return NULL;
+ } else {
+ item->frame = src;
+ return item;
+ }
+ }
+ }
+ pthread_mutex_unlock(&pool->lock);
+ /* 2. otherwise allocate new entry and buffer */
+ entry = talloc(pool, struct wlbuf_pool_entry);
+ memset(entry, 0, sizeof(struct wlbuf_pool_entry));
+ entry->vo = pool->vo;
+ entry->key = pool->key_provider(src);
+ entry->pool_lock = &pool->lock;
+ MP_VERBOSE(entry->vo, "Allocate buffer pool entry : %lu\n",entry->key );
+ params = zwp_linux_dmabuf_v1_create_params(wl->dmabuf);
+ import_successful = pool->dmabuf_importer(src,entry,params);
+ if (!import_successful) {
+ MP_VERBOSE(entry->vo, "Failed to import\n");
+ wlbuf_pool_entry_free(entry);
+ } else {
+ entry->buffer = zwp_linux_buffer_params_v1_create_immed(params, src->params.w, src->params.h,
+ entry->drm_format, 0);
+ }
+ zwp_linux_buffer_params_v1_destroy(params);
+ if (!import_successful){
+ mp_image_unrefp(&src);
+ return NULL;
+ }
+
+ /* 3. add new entry to pool */
+ if (pool->num_entries == pool->num_allocated) {
+ int current_num_allocated = pool->num_allocated;
+ pool->num_allocated *= 2;
+ pthread_mutex_lock(&pool->lock);
+ pool->entries = talloc_realloc(pool, pool->entries, struct wlbuf_pool_entry *, pool->num_allocated);
+