diff options
author | Aaron Boxer <boxerab@protonmail.com> | 2022-09-27 12:12:54 -0400 |
---|---|---|
committer | Dudemanguy <random342@airmail.cc> | 2022-10-26 18:41:47 +0000 |
commit | 7358b9d37126e824cbc3a226d832e9cc1d7b01ea (patch) | |
tree | c477ad6f03145563f31e9877e583a9a50c4c6946 | |
parent | 964692ad4cec90888bb437064c53d8844db9f590 (diff) | |
download | mpv-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.rst | 5 | ||||
-rw-r--r-- | filters/f_autoconvert.c | 12 | ||||
-rw-r--r-- | filters/f_hwtransfer.c | 8 | ||||
-rw-r--r-- | meson.build | 9 | ||||
-rw-r--r-- | video/out/gpu/context.c | 7 | ||||
-rw-r--r-- | video/out/hwdec/dmabuf_interop.h | 2 | ||||
-rw-r--r-- | video/out/hwdec/dmabuf_interop_wl.c | 57 | ||||
-rw-r--r-- | video/out/hwdec/hwdec_drmprime.c | 6 | ||||
-rw-r--r-- | video/out/hwdec/hwdec_vaapi.c | 14 | ||||
-rw-r--r-- | video/out/vo.c | 6 | ||||
-rw-r--r-- | video/out/vo_dmabuf_wayland.c | 361 | ||||
-rw-r--r-- | video/out/wlbuf_pool.c | 190 | ||||
-rw-r--r-- | video/out/wlbuf_pool.h | 65 | ||||
-rw-r--r-- | video/out/wldmabuf/context_wldmabuf.c | 44 | ||||
-rw-r--r-- | video/out/wldmabuf/ra_wldmabuf.c | 44 | ||||
-rw-r--r-- | video/out/wldmabuf/ra_wldmabuf.h | 22 | ||||
-rw-r--r-- | wscript | 9 | ||||
-rw-r--r-- | wscript_build.py | 5 |
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); + |