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=`` 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=`` 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 . + */ +#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 @@ -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; 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 @@ -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); 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 . + */ +#include + +#include "config.h" + +#ifdef HAVE_VAAPI +#include +#include +#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 . + */ +#include + +#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; +} diff --git a/video/out/wlbuf_pool.h b/video/out/wlbuf_pool.h new file mode 100644 index 0000000000..cf86f523d6 --- /dev/null +++ b/video/out/wlbuf_pool.h @@ -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 . + */ + +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); diff --git a/video/out/wldmabuf/context_wldmabuf.c b/video/out/wldmabuf/context_wldmabuf.c new file mode 100644 index 0000000000..a22cb2bf44 --- /dev/null +++ b/video/out/wldmabuf/context_wldmabuf.c @@ -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 . + */ + +#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, +}; diff --git a/video/out/wldmabuf/ra_wldmabuf.c b/video/out/wldmabuf/ra_wldmabuf.c new file mode 100644 index 0000000000..8b59bd4a07 --- /dev/null +++ b/video/out/wldmabuf/ra_wldmabuf.c @@ -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 . + */ + +#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); +} diff --git a/video/out/wldmabuf/ra_wldmabuf.h b/video/out/wldmabuf/ra_wldmabuf.h new file mode 100644 index 0000000000..231b4c3ce8 --- /dev/null +++ b/video/out/wldmabuf/ra_wldmabuf.h @@ -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 . + */ + +#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); diff --git a/wscript b/wscript index 851b1a5c7b..a9f3927eaa 100644 --- a/wscript +++ b/wscript @@ -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', diff --git a/wscript_build.py b/wscript_build.py index dc87e45d4c..7588ed7e47 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -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" ),