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
This commit is contained in:
Aaron Boxer 2022-09-27 12:12:54 -04:00 committed by Dudemanguy
parent 964692ad4c
commit 7358b9d371
18 changed files with 853 additions and 13 deletions

View File

@ -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:

View File

@ -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;

View File

@ -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;
}

View File

@ -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!',

View File

@ -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
};

View File

@ -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);

View File

@ -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;
}

View File

@ -55,6 +55,9 @@ const static dmabuf_interop_init interop_inits[] = {
#endif
#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;

View File

@ -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
@ -123,6 +127,9 @@ const static dmabuf_interop_init interop_inits[] = {
#endif
#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);

View File

@ -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

View File

@ -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),
};

190
video/out/wlbuf_pool.c Normal file
View File

@ -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);
for (int i = current_num_allocated; i < pool->num_allocated; ++i)
pool->entries[i] = NULL;
pthread_mutex_unlock(&pool->lock);
}
wl_buffer_add_listener(entry->buffer, &wlbuf_pool_listener, entry);
entry->frame = src;
pthread_mutex_lock(&pool->lock);
pool->entries[pool->num_entries++] = entry;
pthread_mutex_unlock(&pool->lock);
return entry;
}

65
video/out/wlbuf_pool.h Normal file
View File

@ -0,0 +1,65 @@
/*
* 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/>.
*/
struct wlbuf_pool_entry;
typedef uintptr_t (*wlbuf_pool_key_provider)(struct mp_image *src);
typedef bool (*wlbuf_pool_dmabuf_importer)(struct mp_image *src, struct wlbuf_pool_entry* entry,
struct zwp_linux_buffer_params_v1 *params);
struct wlbuf_pool {
struct vo *vo;
struct vo_wayland_state *wl;
struct wlbuf_pool_entry **entries;
int num_entries;
int num_allocated;
wlbuf_pool_key_provider key_provider;
wlbuf_pool_dmabuf_importer dmabuf_importer;
pthread_mutex_t lock;
bool final_clean;
};
struct wlbuf_pool_entry {
uintptr_t key;
struct vo *vo;
struct wl_buffer *buffer;
uint32_t drm_format;
struct mp_image *frame;
bool pending_delete;
pthread_mutex_t *pool_lock;
};
/**
* Allocate pool
*/
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);
/**
* Free pool entries but leave pool itself intact
*/
void wlbuf_pool_clean(struct wlbuf_pool *pool);
/**
* Free pool
*/
void wlbuf_pool_free(struct wlbuf_pool *pool);
/**
* Get pool entry - will allocate entry if not present in pool.
*/
struct wlbuf_pool_entry *wlbuf_pool_get_entry(struct wlbuf_pool *pool, struct mp_image *src);

View File

@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "video/out/wayland_common.h"
#include "video/out/opengl/context.h"
#include "ra_wldmabuf.h"
static void uninit(struct ra_ctx *ctx)
{
ra_free(&ctx->ra);
vo_wayland_uninit(ctx->vo);
}
static bool init(struct ra_ctx *ctx)
{
if (!vo_wayland_init(ctx->vo)) {
vo_wayland_uninit(ctx->vo);
return false;
}
ctx->ra = ra_create_wayland(ctx->log, ctx->vo->wl->display);
return true;
}
const struct ra_ctx_fns ra_ctx_wldmabuf = {
.type = "none",
.name = "wldmabuf",
.init = init,
.uninit = uninit,
};

View File

@ -0,0 +1,44 @@
/*
* 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/wayland_common.h"
#include "video/out/gpu/ra.h"
#include "ra_wldmabuf.h"
static void destroy(struct ra *ra)
{
}
static struct ra_fns ra_fns_wldmabuf = {
.destroy = destroy,
};
struct ra *ra_create_wayland(struct mp_log *log, struct wl_display *display)
{
struct ra *ra = talloc_zero(NULL, struct ra);
ra->fns = &ra_fns_wldmabuf;
ra->log = log;
ra_add_native_resource(ra, "wl", display);
return ra;
}
bool ra_is_wldmabuf(struct ra *ra)
{
return (ra->fns == &ra_fns_wldmabuf);
}

View File

@ -0,0 +1,22 @@
/*
* 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/>.
*/
#pragma once
#include "video/out/wayland_common.h"
struct ra *ra_create_wayland(struct mp_log *log, struct wl_display *display);
bool ra_is_wldmabuf(struct ra *ra);

View File

@ -665,9 +665,14 @@ video_output_features = [
'deps': 'vaapi && gl-wayland',
'func': check_pkg_config('libva-wayland', '>= 1.1.0'),
}, {
'name': 'vaapi-wayland-memfd',
'desc': 'VAAPI (Wayland dmabuf support)',
'deps': 'vaapi-wayland && memfd_create',
'name': 'vaapi-wayland-memfd',
'desc': 'Wayland vaapi support',
'func': check_true,
}, {
'name': 'dmabuf-wayland',
'desc': 'Wayland dmabuf support',
'deps': 'wayland && memfd_create && (vaapi-wayland || drm)',
'func': check_true,
}, {
'name': '--vaapi-drm',

View File

@ -478,6 +478,7 @@ def build(ctx):
( "video/out/hwdec/hwdec_vaapi.c", "vaapi-egl || vaapi-libplacebo" ),
( "video/out/hwdec/dmabuf_interop_gl.c", "dmabuf-interop-gl" ),
( "video/out/hwdec/dmabuf_interop_pl.c", "dmabuf-interop-pl" ),
( "video/out/hwdec/dmabuf_interop_wl.c", "dmabuf-wayland" ),
( "video/out/libmpv_sw.c" ),
( "video/out/placebo/ra_pl.c", "libplacebo" ),
( "video/out/placebo/utils.c", "libplacebo" ),
@ -507,6 +508,9 @@ def build(ctx):
( "video/out/opengl/ra_gl.c", "gl" ),
( "video/out/opengl/utils.c", "gl" ),
( "video/out/present_sync.c", "wayland || x11" ),
( "video/out/wldmabuf/context_wldmabuf.c", "dmabuf-wayland" ),
( "video/out/wldmabuf/ra_wldmabuf.c", "dmabuf-wayland" ),
( "video/out/wlbuf_pool.c", "dmabuf-wayland" ),
( "video/out/vo.c" ),
( "video/out/vo_caca.c", "caca" ),
( "video/out/vo_direct3d.c", "direct3d" ),
@ -524,6 +528,7 @@ def build(ctx):
( "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_dmabuf_wayland.c", "dmabuf-wayland" ),
( "video/out/vo_vdpau.c", "vdpau" ),
( "video/out/vo_wlshm.c", "wayland && memfd_create" ),
( "video/out/vo_x11.c" , "x11" ),