diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:36:56 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:36:56 +0000 |
commit | 51de1d8436100f725f3576aefa24a2bd2057bc28 (patch) | |
tree | c6d1d5264b6d40a8d7ca34129f36b7d61e188af3 /video/out/vo_dmabuf_wayland.c | |
parent | Initial commit. (diff) | |
download | mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.tar.xz mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.zip |
Adding upstream version 0.37.0.upstream/0.37.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'video/out/vo_dmabuf_wayland.c')
-rw-r--r-- | video/out/vo_dmabuf_wayland.c | 872 |
1 files changed, 872 insertions, 0 deletions
diff --git a/video/out/vo_dmabuf_wayland.c b/video/out/vo_dmabuf_wayland.c new file mode 100644 index 0000000..e04ff5d --- /dev/null +++ b/video/out/vo_dmabuf_wayland.c @@ -0,0 +1,872 @@ +/* + * 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 <libavutil/hwcontext_drm.h> +#include <sys/mman.h> +#include <unistd.h> +#include "config.h" + +#if HAVE_VAAPI +#include <va/va_drmcommon.h> +#endif + +#include "common/global.h" +#include "gpu/hwdec.h" +#include "gpu/video.h" +#include "mpv_talloc.h" +#include "present_sync.h" +#include "sub/draw_bmp.h" +#include "video/fmt-conversion.h" +#include "video/mp_image.h" +#include "vo.h" +#include "wayland_common.h" +#include "wldmabuf/ra_wldmabuf.h" + +#if HAVE_VAAPI +#include "video/vaapi.h" +#endif + +// Generated from wayland-protocols +#include "linux-dmabuf-unstable-v1.h" +#include "viewporter.h" + +#if HAVE_WAYLAND_PROTOCOLS_1_27 +#include "single-pixel-buffer-v1.h" +#endif + +// We need at least enough buffers to avoid a +// flickering artifact in certain formats. +#define WL_BUFFERS_WANTED 15 + +enum hwdec_type { + HWDEC_NONE, + HWDEC_VAAPI, + HWDEC_DRMPRIME, +}; + +struct buffer { + struct vo *vo; + struct wl_buffer *buffer; + struct wl_list link; + struct vo_frame *frame; + + uint32_t drm_format; + uintptr_t id; +}; + +struct osd_buffer { + struct vo *vo; + struct wl_buffer *buffer; + struct wl_list link; + struct mp_image image; + size_t size; +}; + +struct priv { + struct mp_log *log; + struct mp_rect src; + struct mpv_global *global; + + struct ra_ctx *ctx; + struct ra_hwdec_ctx hwdec_ctx; + + struct wl_shm_pool *solid_buffer_pool; + struct wl_buffer *solid_buffer; + struct wl_list buffer_list; + struct wl_list osd_buffer_list; + + struct wl_shm_pool *osd_shm_pool; + uint8_t *osd_shm_data; + int osd_shm_width; + int osd_shm_stride; + int osd_shm_height; + bool osd_surface_is_mapped; + bool osd_surface_has_contents; + + struct osd_buffer *osd_buffer; + struct mp_draw_sub_cache *osd_cache; + struct mp_osd_res screen_osd_res; + + bool destroy_buffers; + bool force_window; + enum hwdec_type hwdec_type; + uint32_t drm_format; + uint64_t drm_modifier; +}; + +static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) +{ + struct buffer *buf = data; + if (buf->frame) { + talloc_free(buf->frame); + buf->frame = NULL; + } +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_handle_release, +}; + +static void osd_buffer_handle_release(void *data, struct wl_buffer *wl_buffer) +{ + struct osd_buffer *osd_buf = data; + wl_list_remove(&osd_buf->link); + if (osd_buf->buffer) { + wl_buffer_destroy(osd_buf->buffer); + osd_buf->buffer = NULL; + } + talloc_free(osd_buf); +} + +static const struct wl_buffer_listener osd_buffer_listener = { + osd_buffer_handle_release, +}; + +#if HAVE_VAAPI +static void close_file_descriptors(VADRMPRIMESurfaceDescriptor desc) +{ + for (int i = 0; i < desc.num_objects; i++) + close(desc.objects[i].fd); +} +#endif + +static uintptr_t vaapi_surface_id(struct mp_image *src) +{ + uintptr_t id = 0; +#if HAVE_VAAPI + id = (uintptr_t)va_surface_id(src); +#endif + return id; +} + +static bool vaapi_drm_format(struct vo *vo, struct mp_image *src) +{ + bool format = false; +#if HAVE_VAAPI + struct priv *p = vo->priv; + VADRMPRIMESurfaceDescriptor desc = {0}; + + uintptr_t id = vaapi_surface_id(src); + VADisplay display = ra_get_native_resource(p->ctx->ra, "VADisplay"); + VAStatus status = vaExportSurfaceHandle(display, id, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + VA_EXPORT_SURFACE_COMPOSED_LAYERS | VA_EXPORT_SURFACE_READ_ONLY, &desc); + + if (!CHECK_VA_STATUS(vo, "vaExportSurfaceHandle()")) { + /* invalid surface warning => composed layers not supported */ + if (status == VA_STATUS_ERROR_INVALID_SURFACE) + MP_VERBOSE(vo, "vaExportSurfaceHandle: composed layers not supported.\n"); + goto done; + } + p->drm_format = desc.layers[0].drm_format; + p->drm_modifier = desc.objects[0].drm_format_modifier; + format = true; +done: + close_file_descriptors(desc); +#endif + return format; +} + +static void vaapi_dmabuf_importer(struct buffer *buf, struct mp_image *src, + struct zwp_linux_buffer_params_v1 *params) +{ +#if HAVE_VAAPI + struct vo *vo = buf->vo; + struct priv *p = vo->priv; + VADRMPRIMESurfaceDescriptor desc = {0}; + VADisplay display = ra_get_native_resource(p->ctx->ra, "VADisplay"); + + /* composed has single layer */ + int layer_no = 0; + buf->id = vaapi_surface_id(src); + VAStatus status = vaExportSurfaceHandle(display, buf->id, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + VA_EXPORT_SURFACE_COMPOSED_LAYERS | VA_EXPORT_SURFACE_READ_ONLY, &desc); + + if (!CHECK_VA_STATUS(vo, "vaExportSurfaceHandle()")) { + /* invalid surface warning => composed layers not supported */ + if (status == VA_STATUS_ERROR_INVALID_SURFACE) + MP_VERBOSE(vo, "vaExportSurfaceHandle: composed layers not supported.\n"); + goto done; + } + buf->drm_format = desc.layers[layer_no].drm_format; + if (!ra_compatible_format(p->ctx->ra, buf->drm_format, desc.objects[0].drm_format_modifier)) { + MP_VERBOSE(vo, "%s(%016lx) is not supported.\n", + mp_tag_str(buf->drm_format), desc.objects[0].drm_format_modifier); + buf->drm_format = 0; + goto done; + } + 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); + } + +done: + close_file_descriptors(desc); +#endif +} + +static uintptr_t drmprime_surface_id(struct mp_image *src) +{ + uintptr_t id = 0; + struct AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->planes[0]; + + AVDRMObjectDescriptor object = desc->objects[0]; + id = (uintptr_t)object.fd; + return id; +} + +static bool drmprime_drm_format(struct vo *vo, struct mp_image *src) +{ + struct priv *p = vo->priv; + struct AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->planes[0]; + if (!desc) + return false; + + // Just check the very first layer/plane. + p->drm_format = desc->layers[0].format; + int object_index = desc->layers[0].planes[0].object_index; + p->drm_modifier = desc->objects[object_index].format_modifier; + return true; +} + +static void drmprime_dmabuf_importer(struct buffer *buf, struct mp_image *src, + struct zwp_linux_buffer_params_v1 *params) +{ + int layer_no, plane_no; + int max_planes = 0; + const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->planes[0]; + if (!desc) + return; + + buf->id = drmprime_surface_id(src); + for (layer_no = 0; layer_no < desc->nb_layers; layer_no++) { + AVDRMLayerDescriptor layer = desc->layers[layer_no]; + + buf->drm_format = layer.format; + max_planes = MPMAX(max_planes, layer.nb_planes); + 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 = desc->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); + } + } +} + +static intptr_t surface_id(struct vo *vo, struct mp_image *src) +{ + struct priv *p = vo->priv; + switch(p->hwdec_type) { + case HWDEC_VAAPI: + return vaapi_surface_id(src); + case HWDEC_DRMPRIME: + return drmprime_surface_id(src); + default: + return 0; + } +} + +static bool drm_format_check(struct vo *vo, struct mp_image *src) +{ + struct priv *p = vo->priv; + switch(p->hwdec_type) { + case HWDEC_VAAPI: + return vaapi_drm_format(vo, src); + case HWDEC_DRMPRIME: + return drmprime_drm_format(vo, src); + } + return false; +} + +static struct buffer *buffer_check(struct vo *vo, struct vo_frame *frame) +{ + struct priv *p = vo->priv; + + /* Make more buffers if we're not at the desired amount yet. */ + if (wl_list_length(&p->buffer_list) < WL_BUFFERS_WANTED) + goto done; + + uintptr_t id = surface_id(vo, frame->current); + struct buffer *buf; + wl_list_for_each(buf, &p->buffer_list, link) { + if (buf->id == id) { + if (buf->frame) + talloc_free(buf->frame); + buf->frame = frame; + return buf; + } + } + +done: + return NULL; +} + +static struct buffer *buffer_create(struct vo *vo, struct vo_frame *frame) +{ + struct vo_wayland_state *wl = vo->wl; + struct priv *p = vo->priv; + + struct buffer *buf = talloc_zero(vo, struct buffer); + buf->vo = vo; + buf->frame = frame; + + struct mp_image *image = buf->frame->current; + struct zwp_linux_buffer_params_v1 *params = zwp_linux_dmabuf_v1_create_params(wl->dmabuf); + + switch(p->hwdec_type) { + case HWDEC_VAAPI: + vaapi_dmabuf_importer(buf, image, params); + break; + case HWDEC_DRMPRIME: + drmprime_dmabuf_importer(buf, image, params); + break; + } + + if (!buf->drm_format) { + talloc_free(buf->frame); + talloc_free(buf); + zwp_linux_buffer_params_v1_destroy(params); + return NULL; + } + + buf->buffer = zwp_linux_buffer_params_v1_create_immed(params, image->params.w, image->params.h, + buf->drm_format, 0); + zwp_linux_buffer_params_v1_destroy(params); + wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); + wl_list_insert(&p->buffer_list, &buf->link); + return buf; +} + +static struct buffer *buffer_get(struct vo *vo, struct vo_frame *frame) +{ + /* Reuse existing buffer if possible. */ + struct buffer *buf = buffer_check(vo, frame); + if (buf) { + return buf; + } else { + return buffer_create(vo, frame); + } +} + +static void destroy_buffers(struct vo *vo) +{ + struct priv *p = vo->priv; + struct buffer *buf, *tmp; + p->destroy_buffers = false; + wl_list_for_each_safe(buf, tmp, &p->buffer_list, link) { + wl_list_remove(&buf->link); + if (buf->frame) { + talloc_free(buf->frame); + buf->frame = NULL; + } + if (buf->buffer) { + wl_buffer_destroy(buf->buffer); + buf->buffer = NULL; + } + talloc_free(buf); + } +} + +static void destroy_osd_buffers(struct vo *vo) +{ + if (!vo->wl) + return; + + // Remove any existing buffer before we destroy them. + wl_surface_attach(vo->wl->osd_surface, NULL, 0, 0); + wl_surface_commit(vo->wl->osd_surface); + + struct priv *p = vo->priv; + struct osd_buffer *osd_buf, *tmp; + wl_list_for_each_safe(osd_buf, tmp, &p->osd_buffer_list, link) { + wl_list_remove(&osd_buf->link); + munmap(osd_buf->image.planes[0], osd_buf->size); + if (osd_buf->buffer) { + wl_buffer_destroy(osd_buf->buffer); + osd_buf->buffer = NULL; + } + } +} + +static struct osd_buffer *osd_buffer_check(struct vo *vo) +{ + struct priv *p = vo->priv; + struct osd_buffer *osd_buf; + wl_list_for_each(osd_buf, &p->osd_buffer_list, link) { + return osd_buf; + } + return NULL; +} + +static struct osd_buffer *osd_buffer_create(struct vo *vo) +{ + struct priv *p = vo->priv; + struct osd_buffer *osd_buf = talloc_zero(vo, struct osd_buffer); + + osd_buf->vo = vo; + osd_buf->size = p->osd_shm_height * p->osd_shm_stride; + mp_image_set_size(&osd_buf->image, p->osd_shm_width, p->osd_shm_height); + osd_buf->image.planes[0] = p->osd_shm_data; + osd_buf->image.stride[0] = p->osd_shm_stride; + osd_buf->buffer = wl_shm_pool_create_buffer(p->osd_shm_pool, 0, + p->osd_shm_width, p->osd_shm_height, + p->osd_shm_stride, WL_SHM_FORMAT_ARGB8888); + + if (!osd_buf->buffer) { + talloc_free(osd_buf); + return NULL; + } + + wl_list_insert(&p->osd_buffer_list, &osd_buf->link); + wl_buffer_add_listener(osd_buf->buffer, &osd_buffer_listener, osd_buf); + return osd_buf; +} + +static struct osd_buffer *osd_buffer_get(struct vo *vo) +{ + struct osd_buffer *osd_buf = osd_buffer_check(vo); + if (osd_buf) { + return osd_buf; + } else { + return osd_buffer_create(vo); + } +} + +static void create_shm_pool(struct vo *vo) +{ + struct vo_wayland_state *wl = vo->wl; + struct priv *p = vo->priv; + + int stride = MP_ALIGN_UP(vo->dwidth * 4, 16); + size_t size = vo->dheight * stride; + int fd = vo_wayland_allocate_memfd(vo, size); + if (fd < 0) + return; + uint8_t *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + goto error1; + struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, fd, size); + if (!pool) + goto error2; + close(fd); + + destroy_osd_buffers(vo); + + if (p->osd_shm_pool) + wl_shm_pool_destroy(p->osd_shm_pool); + p->osd_shm_pool = pool; + p->osd_shm_width = vo->dwidth; + p->osd_shm_height = vo->dheight; + p->osd_shm_stride = stride; + p->osd_shm_data = data; + return; + +error2: + munmap(data, size); +error1: + close(fd); +} + +static void set_viewport_source(struct vo *vo, struct mp_rect src) +{ + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + + if (p->force_window) + return; + + if (wl->video_viewport && !mp_rect_equals(&p->src, &src)) { + wp_viewport_set_source(wl->video_viewport, src.x0 << 8, + src.y0 << 8, mp_rect_w(src) << 8, + mp_rect_h(src) << 8); + p->src = src; + } +} + +static void resize(struct vo *vo) +{ + struct vo_wayland_state *wl = vo->wl; + struct priv *p = vo->priv; + + struct mp_rect src; + struct mp_rect dst; + struct mp_vo_opts *vo_opts = wl->vo_opts; + + const int width = mp_rect_w(wl->geometry); + const int height = mp_rect_h(wl->geometry); + + if (width == 0 || height == 0) + return; + + vo_wayland_set_opaque_region(wl, false); + vo->dwidth = width; + vo->dheight = height; + + create_shm_pool(vo); + + // top level viewport is calculated with pan set to zero + vo->opts->pan_x = 0; + vo->opts->pan_y = 0; + vo_get_src_dst_rects(vo, &src, &dst, &p->screen_osd_res); + int window_w = p->screen_osd_res.ml + p->screen_osd_res.mr + mp_rect_w(dst); + int window_h = p->screen_osd_res.mt + p->screen_osd_res.mb + mp_rect_h(dst); + wp_viewport_set_destination(wl->viewport, window_w, window_h); + + //now we restore pan for video viewport calculation + vo->opts->pan_x = vo_opts->pan_x; + vo->opts->pan_y = vo_opts->pan_y; + vo_get_src_dst_rects(vo, &src, &dst, &p->screen_osd_res); + 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); + wp_viewport_set_destination(wl->osd_viewport, vo->dwidth, vo->dheight); + wl_subsurface_set_position(wl->osd_subsurface, 0 - dst.x0, 0 - dst.y0); + set_viewport_source(vo, src); +} + +static bool draw_osd(struct vo *vo, struct mp_image *cur, double pts) +{ + struct priv *p = vo->priv; + struct mp_osd_res *res = &p->screen_osd_res; + bool draw = false; + + struct sub_bitmap_list *sbs = osd_render(vo->osd, *res, pts, 0, mp_draw_sub_formats); + + if (!sbs) + return draw; + + struct mp_rect act_rc[1], mod_rc[64]; + int num_act_rc = 0, num_mod_rc = 0; + + if (!p->osd_cache) + p->osd_cache = mp_draw_sub_alloc(p, vo->global); + + struct mp_image *osd = mp_draw_sub_overlay(p->osd_cache, sbs, act_rc, + MP_ARRAY_SIZE(act_rc), &num_act_rc, + mod_rc, MP_ARRAY_SIZE(mod_rc), &num_mod_rc); + + p->osd_surface_has_contents = num_act_rc > 0; + + if (!osd || !num_mod_rc) + goto done; + + for (int n = 0; n < num_mod_rc; n++) { + struct mp_rect rc = mod_rc[n]; + + int rw = mp_rect_w(rc); + int rh = mp_rect_h(rc); + + void *src = mp_image_pixel_ptr(osd, 0, rc.x0, rc.y0); + void *dst = cur->planes[0] + rc.x0 * 4 + rc.y0 * cur->stride[0]; + + memcpy_pic(dst, src, rw * 4, rh, cur->stride[0], osd->stride[0]); + } + + draw = true; +done: + talloc_free(sbs); + return draw; +} + +static void draw_frame(struct vo *vo, struct vo_frame *frame) +{ + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + struct buffer *buf; + struct osd_buffer *osd_buf; + double pts; + + if (!vo_wayland_check_visible(vo)) { + if (frame->current) + talloc_free(frame); + return; + } + + if (p->destroy_buffers) + destroy_buffers(vo); + + // Reuse the solid buffer so the osd can be visible + if (p->force_window) { + wl_surface_attach(wl->video_surface, p->solid_buffer, 0, 0); + wl_surface_damage_buffer(wl->video_surface, 0, 0, 1, 1); + } + + pts = frame->current ? frame->current->pts : 0; + if (frame->current) { + buf = buffer_get(vo, frame); + + if (buf && buf->frame) { + struct mp_image *image = buf->frame->current; + wl_surface_attach(wl->video_surface, buf->buffer, 0, 0); + wl_surface_damage_buffer(wl->video_surface, 0, 0, image->w, + image->h); + + } + } + + osd_buf = osd_buffer_get(vo); + if (osd_buf && osd_buf->buffer) { + if (draw_osd(vo, &osd_buf->image, pts) && p->osd_surface_has_contents) { + wl_surface_attach(wl->osd_surface, osd_buf->buffer, 0, 0); + wl_surface_damage_buffer(wl->osd_surface, 0, 0, osd_buf->image.w, + osd_buf->image.h); + p->osd_surface_is_mapped = true; + } else if (!p->osd_surface_has_contents && p->osd_surface_is_mapped) { + wl_surface_attach(wl->osd_surface, NULL, 0, 0); + p->osd_surface_is_mapped = false; + } + } +} + +static void flip_page(struct vo *vo) +{ + struct vo_wayland_state *wl = vo->wl; + + wl_surface_commit(wl->video_surface); + wl_surface_commit(wl->osd_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 *img) +{ + struct priv *p = vo->priv; + + if (img->params.force_window) { + p->force_window = true; + goto done; + } + + if (!drm_format_check(vo, img)) { + MP_ERR(vo, "Unable to get drm format from hardware decoding!\n"); + return VO_ERROR; + } + + if (!ra_compatible_format(p->ctx->ra, p->drm_format, p->drm_modifier)) { + MP_ERR(vo, "Format '%s' with modifier '(%016lx)' is not supported by" + " the compositor.\n", mp_tag_str(p->drm_format), p->drm_modifier); + return VO_ERROR; + } + + p->force_window = false; +done: + if (!vo_wayland_reconfig(vo)) + return VO_ERROR; + + // mpv rotates clockwise but the wayland spec has counter-clockwise rotations + // swap 1 and 3 to match mpv's direction + int transform = (360 - img->params.rotate) % 360 / 90; + wl_surface_set_buffer_transform(vo->wl->video_surface, transform); + + // Immediately destroy all buffers if params change. + destroy_buffers(vo); + return 0; +} + +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_RESET: + p->destroy_buffers = true; + return VO_TRUE; + case VOCTRL_SET_PANSCAN: + resize(vo); + return VO_TRUE; + } + + 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; + + destroy_buffers(vo); + destroy_osd_buffers(vo); + if (p->osd_shm_pool) + wl_shm_pool_destroy(p->osd_shm_pool); + 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); + } + + vo_wayland_uninit(vo); + ra_ctx_destroy(&p->ctx); +} + +static int preinit(struct vo *vo) +{ + struct priv *p = vo->priv; + + p->log = vo->log; + p->global = vo->global; + p->ctx = ra_ctx_create_by_name(vo, "wldmabuf"); + wl_list_init(&p->buffer_list); + wl_list_init(&p->osd_buffer_list); + if (!p->ctx) + goto err; + + assert(p->ctx->ra); + + if (!vo->wl->dmabuf || !vo->wl->dmabuf_feedback) { + MP_FATAL(vo->wl, "Compositor doesn't support the %s (ver. 4) protocol!\n", + zwp_linux_dmabuf_v1_interface.name); + goto err; + } + + if (!vo->wl->shm) { + MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n", + wl_shm_interface.name); + goto err; + } + + if (!vo->wl->video_subsurface) { + MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n", + wl_subcompositor_interface.name); + goto err; + } + + if (!vo->wl->viewport) { + MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n", + wp_viewporter_interface.name); + goto err; + } + + if (vo->wl->single_pixel_manager) { +#if HAVE_WAYLAND_PROTOCOLS_1_27 + p->solid_buffer = wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer( + vo->wl->single_pixel_manager, 0, 0, 0, UINT32_MAX); /* R, G, B, A */ +#endif + } else { + 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) + goto err; + p->solid_buffer_pool = wl_shm_create_pool(vo->wl->shm, fd, height * stride); + close(fd); + if (!p->solid_buffer_pool) + goto err; + p->solid_buffer = wl_shm_pool_create_buffer( + p->solid_buffer_pool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888); + } + if (!p->solid_buffer) + goto err; + + wl_surface_attach(vo->wl->surface, p->solid_buffer, 0, 0); + + vo->hwdec_devs = hwdec_devices_create(); + p->hwdec_ctx = (struct ra_hwdec_ctx) { + .log = p->log, + .global = p->global, + .ra_ctx = p->ctx, + }; + ra_hwdec_ctx_init(&p->hwdec_ctx, vo->hwdec_devs, NULL, true); + + // Loop through hardware accelerated formats and only request known + // supported formats. + for (int i = IMGFMT_VDPAU_OUTPUT; i < IMGFMT_AVPIXFMT_START; ++i) { + if (is_supported_fmt(i)) { + struct hwdec_imgfmt_request params = { + .imgfmt = i, + .probing = false, + }; + ra_hwdec_ctx_load_fmt(&p->hwdec_ctx, vo->hwdec_devs, ¶ms); + } + } + + for (int i = 0; i < p->hwdec_ctx.num_hwdecs; i++) { + struct ra_hwdec *hw = p->hwdec_ctx.hwdecs[i]; + if (ra_get_native_resource(p->ctx->ra, "VADisplay")) { + p->hwdec_type = HWDEC_VAAPI; + } else if (strcmp(hw->driver->name, "drmprime") == 0) { + p->hwdec_type = HWDEC_DRMPRIME; + } + } + + if (p->hwdec_type == HWDEC_NONE) { + MP_ERR(vo, "No valid hardware decoding driver could be loaded!\n"); + goto err; + } + + p->src = (struct mp_rect){0, 0, 0, 0}; + return 0; + +err: + uninit(vo); + return -1; +} + +const struct vo_driver video_out_dmabuf_wayland = { + .description = "Wayland dmabuf video output", + .name = "dmabuf-wayland", + .caps = VO_CAP_ROTATE90, + .frame_owner = true, + .preinit = preinit, + .query_format = query_format, + .reconfig2 = 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), +}; |