diff options
Diffstat (limited to 'video/out')
-rw-r--r-- | video/out/vo_wayland.c | 360 |
1 files changed, 229 insertions, 131 deletions
diff --git a/video/out/vo_wayland.c b/video/out/vo_wayland.c index ffea858a42..4d994f732d 100644 --- a/video/out/vo_wayland.c +++ b/video/out/vo_wayland.c @@ -29,12 +29,17 @@ #include <libavutil/common.h> #include "config.h" + #include "vo.h" -#include "sub/osd.h" #include "video/vfcap.h" #include "video/mp_image.h" -#include "common/msg.h" #include "video/sws_utils.h" +#include "video/memcpy_pic.h" + +#include "sub/osd.h" +#include "sub/img_convert.h" + +#include "common/msg.h" #include "wayland_common.h" #include "wayland-version.h" @@ -92,14 +97,31 @@ static const struct fmtentry fmttable[] = { #define DEFAULT_FORMAT_ENTRY 1 #define DEFAULT_ALPHA_FORMAT_ENTRY 0 +struct priv; + struct buffer { struct wl_buffer *wlbuf; bool is_busy; bool is_new; + bool is_attached; + bool to_resize; void *shm_data; size_t shm_size; }; +struct buffer_pool { + struct buffer *buffers; + struct buffer *front_buffer; // just pointers to any of the buffers + struct buffer *middle_buffer; // just pointers to any of the buffers + struct buffer *back_buffer; + uint32_t buffer_no; + uint32_t size; + uint32_t stride; + uint32_t bytes_per_pixel; + enum wl_shm_format format; // TODO use fmtentry here + struct wl_shm *shm; +}; + struct supported_format { const struct fmtentry *fmt; bool is_alpha; @@ -111,9 +133,7 @@ struct priv { struct vo_wayland_state *wl; struct wl_list format_list; - const struct fmtentry *pref_format; - int bytes_per_pixel; - int stride; + const struct fmtentry *video_format; struct mp_rect src; struct mp_rect dst; @@ -126,21 +146,19 @@ struct priv { struct wl_callback *redraw_callback; - struct buffer buffers[MAX_BUFFERS]; - struct buffer *front_buffer; - struct buffer *back_buffer; - struct buffer tmp_buffer; + struct buffer_pool video_bufpool; + struct buffer *attached_buffer; struct mp_image *original_image; int width; // width of the original image int height; int x, y; // coords for resizing - bool resize_attach; // options int enable_alpha; int use_rgb565; + int use_triplebuffering; }; /* copied from weston clients */ @@ -238,26 +256,7 @@ static const struct fmtentry * is_wayland_format_supported(struct priv *p, return NULL; } -static void buffer_swap(struct priv *p) -{ - if (!p->back_buffer->is_new) - return; - - struct buffer *tmp = p->back_buffer; - p->back_buffer = p->front_buffer; - p->front_buffer = tmp; -} - - -// returns NULL if the back_buffer contains a new image or if the back buffer -// is busy (unlikely) -static struct buffer * buffer_get_back(struct priv *p) -{ - if (p->back_buffer->is_busy) - return NULL; - - return p->back_buffer; -} +// buffer functions static bool buffer_finalise_back(struct buffer *buf) { @@ -265,96 +264,200 @@ static bool buffer_finalise_back(struct buffer *buf) return true; } -static struct buffer * buffer_get_front(struct priv *p) -{ - if (!p->front_buffer->is_new) - return NULL; - - p->front_buffer->is_busy = true; - return p->front_buffer; -} - static bool buffer_finalise_front(struct buffer *buf) { buf->is_new = false; // is_busy is reset on handle_release + buf->is_busy = true; + buf->is_attached = true; return true; } -static struct mp_image buffer_get_mp_image(struct priv *p, struct buffer *buf) +static void buffer_destroy_content(struct buffer *buf) { - struct mp_image img = {0}; - mp_image_set_params(&img, &p->sws->dst); - - img.planes[0] = buf->shm_data; - img.stride[0] = p->stride; - - return img; + if (buf->wlbuf) { + wl_buffer_destroy(buf->wlbuf); + buf->wlbuf = NULL; + } + if (buf->shm_data) { + munmap(buf->shm_data, buf->shm_size); + buf->shm_data = NULL; + buf->shm_size = 0; + } } -static bool create_shm_buffer(struct priv *p, - struct buffer *buffer, - int width, - int height, - uint32_t format) +static bool buffer_create_content(struct buffer_pool *pool, + struct buffer *buf, + int width, + int height) { - struct wl_shm_pool *pool; - int fd, size; + int fd; void *data; + struct wl_shm_pool *shm_pool; - p->stride = FFALIGN(width * p->bytes_per_pixel, SWS_MIN_BYTE_ALIGN); - size = p->stride * height; - - fd = os_create_anonymous_file(size); + fd = os_create_anonymous_file(pool->size); if (fd < 0) { - MP_ERR(p->wl, "creating a buffer file for %d B failed: %m", size); return false; } - data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + data = mmap(NULL, pool->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { - MP_ERR(p->wl, "mmap failed: %m\n"); close(fd); return false; } - pool = wl_shm_create_pool(p->wl->display.shm, fd, size); - buffer->wlbuf = wl_shm_pool_create_buffer(pool, 0, width, height, - p->stride, format); - wl_buffer_add_listener(buffer->wlbuf, &buffer_listener, buffer); - wl_shm_pool_destroy(pool); + // wl-buffers of the same shm_pool share it's content which might be useful + // if we resize the buffers (from the docs). + shm_pool = wl_shm_create_pool(pool->shm, fd, pool->size); + buf->wlbuf = wl_shm_pool_create_buffer(shm_pool, 0, width, height, + pool->stride, pool->format); + wl_buffer_add_listener(buf->wlbuf, &buffer_listener, buf); + + wl_shm_pool_destroy(shm_pool); close(fd); - buffer->shm_size = size; - buffer->shm_data = data; - buffer->is_new = false; - buffer->is_busy = false; + buf->shm_size = pool->size; + buf->shm_data = data; + buf->is_new = false; + buf->is_busy = false; return true; } -static void destroy_shm_buffer(struct buffer *buffer) +static bool buffer_resize(struct buffer_pool *pool, struct buffer *buf, + uint32_t width, uint32_t height) +{ + if (buf->is_attached) { + buf->to_resize = true; + return true; + } + + if (buf->shm_size == pool->size) + return true; + + buf->to_resize = false; + buffer_destroy_content(buf); + return buffer_create_content(pool, buf, width, height); +} + +static struct mp_image buffer_get_mp_image(struct priv *p, + struct buffer_pool *pool, + struct buffer *buf) +{ + struct mp_image img = {0}; + mp_image_set_params(&img, &p->sws->dst); + + img.planes[0] = buf->shm_data; + img.stride[0] = pool->stride; + + return img; +} + + +// buffer pool functions + +static void buffer_pool_init(struct priv *p, + struct buffer_pool *pool, + uint32_t buffer_no, + uint32_t width, uint32_t height, + const struct fmtentry *fmt, + struct wl_shm *shm) { - if (buffer->wlbuf) { - wl_buffer_destroy(buffer->wlbuf); - buffer->wlbuf = NULL; + pool->shm = shm; + pool->buffers = calloc(buffer_no, sizeof(struct buffer)); + pool->buffer_no = buffer_no; + pool->format = fmt->wl_fmt; + pool->bytes_per_pixel = mp_imgfmt_get_desc(fmt->mp_fmt).bytes[0]; + pool->stride = FFALIGN(width * pool->bytes_per_pixel, SWS_MIN_BYTE_ALIGN); + pool->size = pool->stride * height; + + for (uint32_t i = 0; i < buffer_no; ++i) + buffer_create_content(pool, &pool->buffers[i], width, height); + + if (buffer_no == 3) { + pool->back_buffer = &pool->buffers[0]; + pool->middle_buffer = &pool->buffers[1]; + pool->front_buffer = &pool->buffers[2]; } - if (buffer->shm_data) { - munmap(buffer->shm_data, buffer->shm_size); - buffer->shm_data = NULL; - buffer->shm_size = 0; + else if (buffer_no == 2) { + pool->back_buffer = &pool->buffers[0]; + pool->front_buffer = &pool->buffers[1]; + pool->middle_buffer = NULL; + } + else { + pool->back_buffer = NULL; + pool->middle_buffer = NULL; + pool->front_buffer = NULL; } } -static bool reinit_shm_buffers(struct priv *p, int width, int height) +static bool buffer_pool_resize(struct buffer_pool *pool, + int width, + int height) { bool ret = true; - enum wl_shm_format fmt = p->pref_format->wl_fmt; - for (int i = 0; ret && i < MAX_BUFFERS; ++i) { - destroy_shm_buffer(&p->buffers[i]); - ret = create_shm_buffer(p, &p->buffers[i], width, height, fmt); - } + + pool->stride = FFALIGN(width * pool->bytes_per_pixel, SWS_MIN_BYTE_ALIGN); + pool->size = pool->stride * height; + + for (uint32_t i = 0; ret && i < pool->buffer_no; ++i) + ret = buffer_resize(pool, &pool->buffers[i], width, height); + return ret; } +static void buffer_pool_destroy(struct buffer_pool *pool) +{ + for (uint32_t i = 0; i < pool->buffer_no; ++i) + buffer_destroy_content(&pool->buffers[i]); + + free(pool->buffers); + pool->front_buffer = NULL; + pool->back_buffer = NULL; + pool->buffers = NULL; +} + +static void buffer_pool_swap(struct buffer_pool *pool) +{ + if (pool->buffer_no == 3) { + if (pool->back_buffer->is_new) { + struct buffer *tmp = pool->back_buffer; + pool->back_buffer = pool->middle_buffer; + pool->middle_buffer = tmp; + } + if (!pool->front_buffer->is_busy && !pool->front_buffer->is_new) { + struct buffer *tmp = pool->front_buffer; + pool->front_buffer = pool->middle_buffer; + pool->middle_buffer = tmp; + } + } + else if (pool->buffer_no == 2) { + if (pool->back_buffer->is_new) { + struct buffer *tmp = pool->back_buffer; + pool->back_buffer = pool->front_buffer; + pool->front_buffer = tmp; + } + } +} + +// returns NULL if the back buffer is busy +static struct buffer * buffer_pool_get_back(struct buffer_pool *pool) +{ + if (!pool->back_buffer || pool->back_buffer->is_busy) + return NULL; + + return pool->back_buffer; +} + +// returns NULL if the front buffer is not new +static struct buffer * buffer_pool_get_front(struct buffer_pool *pool) +{ + if (!pool->front_buffer || !pool->front_buffer->is_new) + return NULL; + + pool->front_buffer->is_busy = true; + return pool->front_buffer; +} + + static bool redraw_frame(struct priv *p) { if (!p->original_image) @@ -376,12 +479,6 @@ static bool resize(struct priv *p) { struct vo_wayland_state *wl = p->wl; - // if the newly resized buffer isn't attached, then don't resize again, - // because the front buffer might be empty and the temporary buffer might - // still be valid - if (p->resize_attach) - return false; - int32_t x = wl->window.sh_x; int32_t y = wl->window.sh_y; wl->vo->dwidth = wl->window.sh_width; @@ -407,7 +504,7 @@ static bool resize(struct priv *p) mp_sws_set_from_cmdline(p->sws); p->sws->src = p->in_format; p->sws->dst = (struct mp_image_params) { - .imgfmt = p->pref_format->mp_fmt, + .imgfmt = p->video_format->mp_fmt, .w = p->dst_w, .h = p->dst_h, .d_w = p->dst_w, @@ -419,12 +516,7 @@ static bool resize(struct priv *p) if (mp_sws_reinit(p->sws) < 0) return false; - // copy pointers - p->tmp_buffer = *p->front_buffer; - p->front_buffer->shm_data = NULL; - p->front_buffer->wlbuf = NULL; - - if (!reinit_shm_buffers(p, p->dst_w, p->dst_h)) { + if (!buffer_pool_resize(&p->video_bufpool, p->dst_w, p->dst_h)) { MP_ERR(wl, "failed to resize buffers\n"); return false; } @@ -444,7 +536,6 @@ static bool resize(struct priv *p) p->x = x; p->y = y; - p->resize_attach = true; p->wl->window.events = 0; p->vo->want_redraw = true; return true; @@ -453,6 +544,7 @@ static bool resize(struct priv *p) /* wayland listeners */ + static void buffer_handle_release(void *data, struct wl_buffer *buffer) { struct buffer *buf = data; @@ -469,7 +561,8 @@ static void frame_handle_redraw(void *data, { struct priv *p = data; struct vo_wayland_state *wl = p->wl; - struct buffer *buf = buffer_get_front(p); + buffer_pool_swap(&p->video_bufpool); + struct buffer *buf = buffer_pool_get_front(&p->video_bufpool); if (buf) { wl_surface_attach(wl->window.surface, buf->wlbuf, p->x, p->y); @@ -481,15 +574,17 @@ static void frame_handle_redraw(void *data, p->redraw_callback = wl_surface_frame(wl->window.surface); wl_callback_add_listener(p->redraw_callback, &frame_listener, p); wl_surface_commit(wl->window.surface); - buffer_finalise_front(buf); - // to avoid multiple resizes of non-shown frames - if (p->resize_attach) { - destroy_shm_buffer(&p->tmp_buffer); - p->resize_attach = false; - p->x = 0; - p->y = 0; + // resize attached buffer + if (p->attached_buffer) { + p->attached_buffer->is_attached = false; + buffer_resize(&p->video_bufpool, p->attached_buffer, p->dst_w, p->dst_h); } + p->attached_buffer = buf; + buffer_finalise_front(buf); + + p->x = 0; + p->y = 0; } else { if (callback) @@ -508,7 +603,7 @@ static void shm_handle_format(void *data, uint32_t format) { struct priv *p = data; - for (int i = 0; i < MAX_FORMAT_ENTRIES; ++i) { + for (uint32_t i = 0; i < MAX_FORMAT_ENTRIES; ++i) { if (fmttable[i].wl_fmt == format) { MP_INFO(p->wl, "format %s supported by hw\n", mp_imgfmt_to_name(fmttable[i].mp_fmt)); @@ -530,20 +625,28 @@ static const struct wl_shm_listener shm_listener = { static void draw_image(struct vo *vo, mp_image_t *mpi) { struct priv *p = vo->priv; - struct buffer *buf = buffer_get_back(p); + struct buffer *buf = buffer_pool_get_back(&p->video_bufpool); if (!buf) { MP_VERBOSE(p->wl, "can't draw, back buffer is busy\n"); return; } + if (buf->to_resize) { + if (buf->is_attached) { + MP_WARN(p->wl, "resizing attached buffer, use triple-buffering\n"); + buf->is_attached = false; + } + buffer_resize(&p->video_bufpool, buf, p->dst_w, p->dst_h); + } + struct mp_image src = *mpi; struct mp_rect src_rc = p->src; src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, src.fmt.align_x); src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, src.fmt.align_y); mp_image_crop_rc(&src, src_rc); - struct mp_image img = buffer_get_mp_image(p, buf); + struct mp_image img = buffer_get_mp_image(p, &p->video_bufpool, buf); mp_sws_scale(p->sws, &img, &src); mp_image_setrefp(&p->original_image, mpi); @@ -553,15 +656,18 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) static void draw_osd(struct vo *vo, struct osd_state *osd) { struct priv *p = vo->priv; - struct mp_image img = buffer_get_mp_image(p, p->back_buffer); - osd_draw_on_image(osd, p->osd, osd_get_vo_pts(osd), 0, &img); + struct buffer *buf = buffer_pool_get_back(&p->video_bufpool); + if (buf) { + struct mp_image img = buffer_get_mp_image(p, &p->video_bufpool, buf); + osd_draw_on_image(osd, p->osd, osd_get_vo_pts(osd), 0, &img); + } } static void flip_page(struct vo *vo) { struct priv *p = vo->priv; - buffer_swap(p); + buffer_pool_swap(&p->video_bufpool); if (!p->redraw_callback) { MP_DBG(p->wl, "restart frame callback\n"); @@ -598,35 +704,30 @@ static int reconfig(struct vo *vo, struct mp_image_params *fmt, int flags) // find the matching format first wl_list_for_each(sf, &p->format_list, link) { if (sf->fmt->mp_fmt == fmt->imgfmt && (p->enable_alpha == sf->is_alpha)) { - p->pref_format = sf->fmt; + p->video_format = sf->fmt; break; } } - if (!p->pref_format) { + if (!p->video_format) { // if use default is enable overwrite the auto selected one if (p->enable_alpha) - p->pref_format = &fmttable[DEFAULT_ALPHA_FORMAT_ENTRY]; + p->video_format = &fmttable[DEFAULT_ALPHA_FORMAT_ENTRY]; else - p->pref_format = &fmttable[DEFAULT_FORMAT_ENTRY]; + p->video_format = &fmttable[DEFAULT_FORMAT_ENTRY]; } // overides alpha // use rgb565 if performance is your main concern if (p->use_rgb565) { - const struct fmtentry *mp_fmt = + const struct fmtentry *fmt = is_wayland_format_supported(p, WL_SHM_FORMAT_RGB565); - if (mp_fmt) - p->pref_format = mp_fmt; + if (fmt) + p->video_format = fmt; } - p->bytes_per_pixel = mp_imgfmt_get_desc(p->pref_format->mp_fmt).bytes[0]; - MP_VERBOSE(p->wl, "bytes per pixel: %d\n", p->bytes_per_pixel); - - if (!reinit_shm_buffers(p, p->width, p->height)) { - MP_ERR(p->wl, "failed to initialise buffers\n"); - return -1; - } + buffer_pool_init(p, &p->video_bufpool, (p->use_triplebuffering ? 3 : 2), + p->width, p->height, p->video_format, p->wl->display.shm); vo_wayland_config(vo, vo->dwidth, vo->dheight, flags); @@ -639,8 +740,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *fmt, int flags) static void uninit(struct vo *vo) { struct priv *p = vo->priv; - for (int i = 0; i < MAX_BUFFERS; ++i) - destroy_shm_buffer(&p->buffers[i]); + buffer_pool_destroy(&p->video_bufpool); if (p->redraw_callback) wl_callback_destroy(p->redraw_callback); @@ -661,9 +761,6 @@ static int preinit(struct vo *vo) p->wl = vo->wayland; p->sws = mp_sws_alloc(vo); - p->front_buffer = &p->buffers[1]; - p->back_buffer = &p->buffers[0]; - wl_list_init(&p->format_list); wl_shm_add_listener(p->wl->display.shm, &shm_listener, p); @@ -724,6 +821,7 @@ const struct vo_driver video_out_wayland = { .options = (const struct m_option[]) { OPT_FLAG("alpha", enable_alpha, 0), OPT_FLAG("rgb565", use_rgb565, 0), + OPT_FLAG("triple-buffering", use_triplebuffering, 0), {0} }, }; |