summaryrefslogtreecommitdiffstats
path: root/video/out/vo_dmabuf_wayland.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/vo_dmabuf_wayland.c')
-rw-r--r--video/out/vo_dmabuf_wayland.c756
1 files changed, 611 insertions, 145 deletions
diff --git a/video/out/vo_dmabuf_wayland.c b/video/out/vo_dmabuf_wayland.c
index e6ce97f643..be8b0f1272 100644
--- a/video/out/vo_dmabuf_wayland.c
+++ b/video/out/vo_dmabuf_wayland.c
@@ -1,6 +1,4 @@
/*
- * Based on vo_gl.c by Reimar Doeffinger.
- *
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
@@ -16,200 +14,628 @@
* 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 <libavutil/hwcontext_drm.h>
+#include <sys/mman.h>
+#include <unistd.h>
#include "config.h"
#if HAVE_VAAPI
#include <va/va_drmcommon.h>
#endif
-#if HAVE_DRM
-#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 "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
-#include "present_sync.h"
-#include "wayland_common.h"
-#include "wlbuf_pool.h"
// Generated from wayland-protocols
-#include "generated/wayland/linux-dmabuf-unstable-v1.h"
-#include "generated/wayland/viewporter.h"
+#include "linux-dmabuf-unstable-v1.h"
+#include "viewporter.h"
#if HAVE_WAYLAND_PROTOCOLS_1_27
-#include "generated/wayland/single-pixel-buffer-v1.h"
+#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 ra_ctx *ctx;
+ struct mp_rect src;
struct mpv_global *global;
+
+ struct ra_ctx *ctx;
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;
+ 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
- VADisplay display;
+static void close_file_descriptors(const 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
-static uintptr_t vaapi_key_provider(struct mp_image *src)
+ id = (uintptr_t)va_surface_id(src);
+#endif
+ return id;
+}
+
+static bool vaapi_drm_format(struct vo *vo, struct mp_image *src)
{
- return va_surface_id(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;
}
-/* va-api dmabuf importer */
-static bool vaapi_dmabuf_importer(struct mp_image *src, struct wlbuf_pool_entry* entry,
+static void vaapi_dmabuf_importer(struct buffer *buf, struct mp_image *src,
struct zwp_linux_buffer_params_v1 *params)
{
- struct priv *p = entry->vo->priv;
- VADRMPRIMESurfaceDescriptor desc = { 0 };
+#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;
- VAStatus status = vaExportSurfaceHandle(p->display, entry->key, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
+ 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(entry->vo, "vaExportSurfaceHandle()")) {
+ if (!CHECK_VA_STATUS(vo, "vaExportSurfaceHandle()")) {
/* invalid surface warning => composed layers not supported */
if (status == VA_STATUS_ERROR_INVALID_SURFACE)
- MP_VERBOSE(entry->vo, "vaExportSurfaceHandle: composed layers not supported.\n");
- return false;
+ MP_VERBOSE(vo, "vaExportSurfaceHandle: composed layers not supported.\n");
+ goto done;
}
- bool success = false;
- uint32_t drm_format = desc.layers[layer_no].drm_format;
- if (!vo_wayland_supported_format(entry->vo, drm_format, desc.objects[0].drm_format_modifier)) {
- MP_VERBOSE(entry->vo, "%s(%016lx) is not supported.\n",
- mp_tag_str(drm_format), desc.objects[0].drm_format_modifier);
+ 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;
}
- entry->drm_format = 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);
}
- success = true;
done:
- for (int i = 0; i < desc.num_objects; i++)
- close(desc.objects[i].fd);
-
- return success;
-}
+ close_file_descriptors(&desc);
#endif
+}
-#if HAVE_DRM
-
-static uintptr_t drmprime_key_provider(struct mp_image *src)
+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];
- return (uintptr_t)object.fd;
+ 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 bool drmprime_dmabuf_importer(struct mp_image *src, struct wlbuf_pool_entry *entry,
+static void drmprime_dmabuf_importer(struct buffer *buf, struct mp_image *src,
struct zwp_linux_buffer_params_v1 *params)
{
int layer_no, plane_no;
- const AVDRMFrameDescriptor *avdesc = (AVDRMFrameDescriptor *)src->planes[0];
+ int max_planes = 0;
+ const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->planes[0];
+ if (!desc)
+ return;
- for (layer_no = 0; layer_no < avdesc->nb_layers; layer_no++) {
- AVDRMLayerDescriptor layer = avdesc->layers[layer_no];
+ buf->id = drmprime_surface_id(src);
+ for (layer_no = 0; layer_no < desc->nb_layers; layer_no++) {
+ AVDRMLayerDescriptor layer = desc->layers[layer_no];
- entry->drm_format = layer.format;
+ 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 = avdesc->objects[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);
}
}
+}
- return true;
+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 (!mp_rect_equals(&p->src, &src)) {
+ wp_viewport_set_source(wl->video_viewport, wl_fixed_from_int(src.x0),
+ wl_fixed_from_int(src.y0), wl_fixed_from_int(mp_rect_w(src)),
+ wl_fixed_from_int(mp_rect_h(src)));
+ p->src = src;
+ }
}
-#endif
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_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);
+ 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;
- 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));
+ 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, lround(window_w / wl->scaling),
+ lround(window_h / wl->scaling));
+
+ //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, lround(mp_rect_w(dst) / wl->scaling),
+ lround(mp_rect_h(dst) / wl->scaling));
+ wl_subsurface_set_position(wl->video_subsurface, lround(dst.x0 / wl->scaling), lround(dst.y0 / wl->scaling));
+ wp_viewport_set_destination(wl->osd_viewport, lround(vo->dwidth / wl->scaling),
+ lround(vo->dheight / wl->scaling));
+ wl_subsurface_set_position(wl->osd_subsurface, lround((0 - dst.x0) / wl->scaling), lround((0 - dst.y0) / wl->scaling));
+ 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;
- 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;
+ 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 wlbuf_pool_entry *entry;
-
- if (!vo_wayland_check_visible(vo))
+ struct buffer *buf;
+ struct osd_buffer *osd_buf;
+ double pts;
+
+ if (!vo_wayland_check_visible(vo)) {
+ if (frame->current)
+ talloc_free(frame);
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;
+ 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);
}
- /* 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
+ 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);
+
+ }
}
- 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);
+ 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)
@@ -217,44 +643,63 @@ 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);
+ 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);
+ return (fmt == IMGFMT_DRMPRIME || fmt == IMGFMT_VAAPI);
}
static int query_format(struct vo *vo, int format)
{
- return is_supported_fmt(format);
+ return is_supported_fmt(format);
}
-static int reconfig(struct vo *vo, struct mp_image_params *params)
+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;
- return 0;
-}
+ wl_surface_set_buffer_transform(vo->wl->video_surface, img->params.rotate / 90);
-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);
+ // Immediately destroy all buffers if params change.
+ destroy_buffers(vo);
+ return 0;
}
static int control(struct vo *vo, uint32_t request, void *data)
@@ -264,19 +709,12 @@ static int control(struct vo *vo, uint32_t request, void *data)
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;
+ 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);
@@ -293,6 +731,10 @@ 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)
@@ -302,7 +744,7 @@ static void uninit(struct vo *vo)
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);
}
@@ -314,35 +756,31 @@ static int preinit(struct vo *vo)
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_out;
+ goto err;
+
assert(p->ctx->ra);
- if (!vo->wl->dmabuf) {
- MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n",
+ 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);
- return VO_ERROR;
+ goto err;
}
if (!vo->wl->shm) {
MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n",
wl_shm_interface.name);
- return VO_ERROR;
+ goto err;
}
if (!vo->wl->video_subsurface) {
MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n",
wl_subcompositor_interface.name);
- return VO_ERROR;
- }
-
- if (!vo->wl->viewport) {
- MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n",
- wp_viewporter_interface.name);
- return VO_ERROR;
+ 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(
@@ -354,41 +792,69 @@ static int preinit(struct vo *vo)
int stride = MP_ALIGN_UP(width * 4, 16);
int fd = vo_wayland_allocate_memfd(vo, stride);
if (fd < 0)
- return VO_ERROR;
+ goto err;
p->solid_buffer_pool = wl_shm_create_pool(vo->wl->shm, fd, height * stride);
+ close(fd);
if (!p->solid_buffer_pool)
- return VO_ERROR;
+ 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)
- return VO_ERROR;
+ goto err;
wl_surface_attach(vo->wl->surface, p->solid_buffer, 0, 0);
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_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, &params);
+ }
+ }
+
+ 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_out:
- uninit(vo);
- return VO_ERROR;
+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,
- .reconfig = reconfig,
+ .reconfig2 = reconfig,
.control = control,
.draw_frame = draw_frame,
.flip_page = flip_page,