From 94fe57856dd8f42b62c5c28715a817196c22926d Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Wed, 10 Sep 2014 18:42:04 +0200 Subject: vo_wayland: pixel perfect buffers 1. Separate buffer and temporary file handling from the vo to make maintenance and reading code easier 2. Skip resizing as much as possible if back buffer is still busy. 3. Detach and mark osd buffers for deletion if we want to redraw them and they are still busy. This could be a possible case for the video buffers as well. Maybe better than double buffering. All the above steps made it possible to have resizing without any artifacts even for subtitles. Also fixes dozen of bugs only I knew, like broken subtitles for rgb565 buffers. I can now sleep at night again. --- video/out/vo_wayland.c | 472 ++++++++++++-------------------------------- video/out/wayland/buffer.c | 137 +++++++++++++ video/out/wayland/buffer.h | 102 ++++++++++ video/out/wayland/memfile.c | 105 ++++++++++ video/out/wayland/memfile.h | 26 +++ 5 files changed, 500 insertions(+), 342 deletions(-) create mode 100644 video/out/wayland/buffer.c create mode 100644 video/out/wayland/buffer.h create mode 100644 video/out/wayland/memfile.c create mode 100644 video/out/wayland/memfile.h (limited to 'video') diff --git a/video/out/vo_wayland.c b/video/out/vo_wayland.c index dc9fc748a1..86c52a440e 100644 --- a/video/out/vo_wayland.c +++ b/video/out/vo_wayland.c @@ -17,14 +17,8 @@ */ #include -#include -#include -#include #include - -#include -#include -#include +#include #include @@ -35,17 +29,15 @@ #include "video/mp_image.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 "input/input.h" - #include "osdep/timer.h" #include "wayland_common.h" -#include "wayland-version.h" + +#include "video/out/wayland/buffer.h" static void draw_image(struct vo *vo, mp_image_t *mpi); static void draw_osd(struct vo *vo); @@ -53,15 +45,8 @@ static void draw_osd(struct vo *vo); static const struct wl_callback_listener frame_listener; static const struct wl_buffer_listener buffer_listener; -struct fmtentry { - enum wl_shm_format wl_fmt; - enum mp_imgfmt mp_fmt; -}; - -// the first 2 Formats should be available on most platforms -// all other formats are optional -// the waylad byte order is sometimes reversed -static const struct fmtentry fmttable[] = { +// TODO: pay attention to the reported subpixel order +static const format_t const format_table[] = { {WL_SHM_FORMAT_ARGB8888, IMGFMT_BGRA}, // 8b 8g 8r 8a {WL_SHM_FORMAT_XRGB8888, IMGFMT_BGR0}, {WL_SHM_FORMAT_RGB332, IMGFMT_RGB8}, // 3b 3g 2r @@ -94,38 +79,24 @@ static const struct fmtentry fmttable[] = { {WL_SHM_FORMAT_BGRA8888, IMGFMT_ARGB}, }; -#define MAX_FORMAT_ENTRIES (sizeof(fmttable) / sizeof(fmttable[0])) +#define MAX_FORMAT_ENTRIES (sizeof(format_table) / sizeof(format_table[0])) #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 wl_shm_pool *shm_pool; // shares memory -}; - +// We only use double buffering but the creation and usage is still open to +// triple buffering. Tripple buffering is now removed, because double buffering +// is now pixel-perfect. 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; + shm_buffer_t **buffers; + shm_buffer_t *front_buffer; // just pointers to any of the buffers + shm_buffer_t *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; + format_t format; bool is_alpha; struct wl_list link; }; @@ -134,11 +105,8 @@ struct priv { struct vo *vo; struct vo_wayland_state *wl; - struct wl_surface *osd_surfaces[MAX_OSD_PARTS]; - struct wl_subsurface *osd_subsurfaces[MAX_OSD_PARTS]; - struct wl_list format_list; - const struct fmtentry *video_format; + const format_t *video_format; // pointer to element in supported_format list struct mp_rect src; struct mp_rect dst; @@ -152,8 +120,6 @@ struct priv { struct wl_callback *redraw_callback; struct buffer_pool video_bufpool; - struct buffer_pool osd_bufpool; - struct buffer *attached_buffer; struct mp_image *original_image; int width; // width of the original image @@ -161,6 +127,9 @@ struct priv { int x, y; // coords for resizing + struct wl_surface *osd_surfaces[MAX_OSD_PARTS]; + struct wl_subsurface *osd_subsurfaces[MAX_OSD_PARTS]; + shm_buffer_t *osd_buffers[MAX_OSD_PARTS]; // this id tells us if the subtitle part has changed or not int bitmap_pos_id[MAX_OSD_PARTS]; @@ -169,236 +138,79 @@ struct priv { // options int enable_alpha; int use_rgb565; - int use_triplebuffering; }; -/* copied from weston clients */ -static int set_cloexec_or_close(int fd) -{ - long flags; - - if (fd == -1) - return -1; - - if ((flags = fcntl(fd, F_GETFD)) == -1) - goto err; - - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) - goto err; - - return fd; - -err: - close(fd); - return -1; -} - -static int create_tmpfile_cloexec(char *tmpname) +static bool is_alpha_format(const format_t *fmt) { - int fd; - -#ifdef HAVE_MKOSTEMP - fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) - unlink(tmpname); -#else - fd = mkstemp(tmpname); - if (fd >= 0) { - fd = set_cloexec_or_close(fd); - unlink(tmpname); - } -#endif - - return fd; + return !!(mp_imgfmt_get_desc(fmt->mp_format).flags & MP_IMGFLAG_ALPHA); } -static int os_create_anonymous_file(off_t size) -{ - static const char template[] = "/mpv-temp-XXXXXX"; - const char *path; - char *name; - int fd; - - path = getenv("XDG_RUNTIME_DIR"); - if (!path) { - errno = ENOENT; - return -1; - } - - name = malloc(strlen(path) + sizeof(template)); - if (!name) - return -1; - - strcpy(name, path); - strcat(name, template); - - fd = create_tmpfile_cloexec(name); - - free(name); - - if (fd < 0) - return -1; - - if (ftruncate(fd, size) < 0) { - close(fd); - return -1; - } - - return fd; -} - -static bool is_alpha_format(const struct fmtentry *fmt) -{ - return !!(mp_imgfmt_get_desc(fmt->mp_fmt).flags & MP_IMGFLAG_ALPHA); -} - -static const struct fmtentry * is_wayland_format_supported(struct priv *p, - enum wl_shm_format fmt) +static const format_t* is_wayland_format_supported(struct priv *p, + enum wl_shm_format fmt) { struct supported_format *sf; // find the matching format first wl_list_for_each(sf, &p->format_list, link) { - if (sf->fmt->wl_fmt == fmt) { - return sf->fmt; + if (sf->format.wl_format == fmt) { + return &sf->format; } } return NULL; } -// buffer functions +// additinal buffer functions -static void buffer_finalise_front(struct buffer *buf) +static void buffer_finalise_front(shm_buffer_t *buf) { - buf->is_new = false; // is_busy is reset on handle_release - buf->is_busy = true; - buf->is_attached = true; + SHM_BUFFER_SET_BUSY(buf); + SHM_BUFFER_CLEAR_DIRTY(buf); } -static void buffer_finalise_back(struct buffer *buf) +static void buffer_finalise_back(shm_buffer_t *buf) { - buf->is_new = true; -} - -static void buffer_destroy_content(struct buffer *buf) -{ - 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 buffer_create_content(struct buffer_pool *pool, - struct buffer *buf, - int width, - int height) -{ - int fd; - void *data; - struct wl_shm_pool *shm_pool; - - fd = os_create_anonymous_file(pool->size); - if (fd < 0) { - return false; - } - - data = mmap(NULL, pool->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) { - close(fd); - return false; - } - - // 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); - - buf->shm_size = pool->size; - buf->shm_data = data; - buf->is_new = false; - buf->is_busy = false; - return true; -} - -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); + SHM_BUFFER_SET_DIRTY(buf); } static struct mp_image buffer_get_mp_image(struct priv *p, - struct buffer_pool *pool, - struct buffer *buf) + shm_buffer_t *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; + img.w = buf->stride / buf->bytes; + img.h = buf->height; + img.planes[0] = buf->data; + img.stride[0] = buf->stride; return img; } - // buffer pool functions static void buffer_pool_reinit(struct priv *p, struct buffer_pool *pool, uint32_t buffer_no, uint32_t width, uint32_t height, - const struct fmtentry *fmt, + format_t fmt, struct wl_shm *shm) { - pool->shm = shm; - if (!pool->buffers) - pool->buffers = calloc(buffer_no, sizeof(struct buffer)); + pool->buffers = calloc(buffer_no, sizeof(shm_buffer_t*)); 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_resize(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]; - } - 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; + + for (uint32_t i = 0; i < buffer_no; ++i) { + if (pool->buffers[i] == NULL) + pool->buffers[i] = shm_buffer_create(width, height, fmt, + shm, &buffer_listener); + else + shm_buffer_resize(pool->buffers[i], width, height); } + + pool->back_buffer = pool->buffers[0]; + pool->front_buffer = pool->buffers[1]; } static bool buffer_pool_resize(struct buffer_pool *pool, @@ -407,11 +219,8 @@ static bool buffer_pool_resize(struct buffer_pool *pool, { bool ret = true; - 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); + shm_buffer_resize(pool->buffers[i], width, height); return ret; } @@ -419,7 +228,7 @@ static bool buffer_pool_resize(struct buffer_pool *pool, 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]); + shm_buffer_destroy(pool->buffers[i]); free(pool->buffers); pool->front_buffer = NULL; @@ -429,54 +238,27 @@ static void buffer_pool_destroy(struct buffer_pool *pool) 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; - } + if (SHM_BUFFER_IS_DIRTY(pool->back_buffer)) { + shm_buffer_t *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) +static shm_buffer_t * buffer_pool_get_back(struct buffer_pool *pool) { - if (!pool->back_buffer || pool->back_buffer->is_busy) + if (!pool->back_buffer || SHM_BUFFER_IS_BUSY(pool->back_buffer)) 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) +static shm_buffer_t * 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 struct buffer * buffer_pool_get_no(struct buffer_pool *pool, uint32_t no) -{ - if (no >= pool->buffer_no) - return NULL; - - return &pool->buffers[no]; -} - static bool redraw_frame(struct priv *p) { draw_image(p->vo, NULL); @@ -495,6 +277,9 @@ static bool resize(struct priv *p) { struct vo_wayland_state *wl = p->wl; + if (SHM_BUFFER_IS_BUSY(p->video_bufpool.back_buffer)) + return false; // skip resizing if we can't garantuee pixel perfectness! + int32_t x = wl->window.sh_x; int32_t y = wl->window.sh_y; wl->vo->dwidth = wl->window.sh_width; @@ -522,7 +307,7 @@ static bool resize(struct priv *p) mp_sws_set_from_cmdline(p->sws, p->vo->opts->sws_opts); p->sws->src = p->in_format; p->sws->dst = (struct mp_image_params) { - .imgfmt = p->video_format->mp_fmt, + .imgfmt = p->video_format->mp_format, .w = p->dst_w, .h = p->dst_h, .d_w = p->dst_w, @@ -538,20 +323,6 @@ static bool resize(struct priv *p) MP_ERR(wl, "failed to resize video buffers\n"); return false; } - if (!buffer_pool_resize(&p->osd_bufpool, p->dst_w, p->dst_h)) { - MP_ERR(wl, "failed to resize osd buffers\n"); - return false; - } - - // attach NULL buffers to the surfaces to avoid artifacts - for (int i = 0; i < MAX_OSD_PARTS; ++i) { - wl_subsurface_set_desync(p->osd_subsurfaces[i]); - struct wl_surface *s = p->osd_surfaces[i]; - wl_surface_attach(s, NULL, 0, 0); - wl_surface_damage(s, 0, 0, p->dst_w, p->dst_h); - wl_surface_commit(s); - wl_subsurface_set_sync(p->osd_subsurfaces[i]); - } wl->window.width = p->dst_w; wl->window.height = p->dst_h; @@ -576,11 +347,18 @@ static bool resize(struct priv *p) /* wayland listeners */ - static void buffer_handle_release(void *data, struct wl_buffer *buffer) { - struct buffer *buf = data; - buf->is_busy = false; + shm_buffer_t *buf = data; + + if (SHM_BUFFER_IS_ONESHOT(buf)) { + shm_buffer_destroy(buf); + return; + } + + SHM_BUFFER_CLEAR_BUSY(buf); + // does nothing and returns 0 if no pending resize flag was set + shm_buffer_pending_resize(buf); } static const struct wl_buffer_listener buffer_listener = { @@ -593,11 +371,10 @@ static void frame_handle_redraw(void *data, { struct priv *p = data; struct vo_wayland_state *wl = p->wl; - buffer_pool_swap(&p->video_bufpool); - struct buffer *buf = buffer_pool_get_front(&p->video_bufpool); + shm_buffer_t *buf = buffer_pool_get_front(&p->video_bufpool); if (buf) { - wl_surface_attach(wl->window.video_surface, buf->wlbuf, p->x, p->y); + wl_surface_attach(wl->window.video_surface, buf->buffer, p->x, p->y); wl_surface_damage(wl->window.video_surface, 0, 0, p->dst_w, p->dst_h); if (callback) @@ -608,18 +385,11 @@ static void frame_handle_redraw(void *data, wl_surface_commit(wl->window.video_surface); buffer_finalise_front(buf); - // 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 { + MP_WARN(wl, "Should not happen anymore (frame)\n"); if (callback) wl_callback_destroy(callback); @@ -638,12 +408,12 @@ static void shm_handle_format(void *data, { struct priv *p = data; for (uint32_t i = 0; i < MAX_FORMAT_ENTRIES; ++i) { - if (fmttable[i].wl_fmt == format) { + if (format_table[i].wl_format == format) { MP_INFO(p->wl, "format %s supported by hw\n", - mp_imgfmt_to_name(fmttable[i].mp_fmt)); + mp_imgfmt_to_name(format_table[i].mp_format)); struct supported_format *sf = talloc(p, struct supported_format); - sf->fmt = &fmttable[i]; - sf->is_alpha = is_alpha_format(sf->fmt); + sf->format = format_table[i]; + sf->is_alpha = is_alpha_format(&sf->format); wl_list_insert(&p->format_list, &sf->link); } } @@ -659,7 +429,7 @@ 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_pool_get_back(&p->video_bufpool); + shm_buffer_t *buf = buffer_pool_get_back(&p->video_bufpool); if (mpi) { talloc_free(p->original_image); @@ -667,19 +437,13 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) } if (!buf) { + // TODO: use similar handling of busy buffers as the osd buffers + // if the need arises 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 img = buffer_get_mp_image(p, &p->video_bufpool, buf); + struct mp_image img = buffer_get_mp_image(p, buf); if (p->original_image) { struct mp_image src = *p->original_image; @@ -704,9 +468,6 @@ static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs) int id = imgs->render_index; struct wl_surface *s = p->osd_surfaces[id]; - struct buffer * buf = buffer_pool_get_no(&p->osd_bufpool, id); - if (!buf) - return; if (imgs->bitmap_pos_id != p->bitmap_pos_id[id]) { p->bitmap_pos_id[id] = imgs->bitmap_pos_id; @@ -715,25 +476,50 @@ static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs) if (!mp_sub_bitmaps_bb(imgs, &bb)) return; - struct mp_image wlimg = buffer_get_mp_image(p, &p->osd_bufpool, buf); - mp_image_clear(&wlimg, 0, 0, wlimg.w, wlimg.h); + int width = mp_rect_w(bb); + int height = mp_rect_h(bb); - for (int n = 0; n < imgs->num_parts; n++) { - struct sub_bitmap *sub = &imgs->parts[n]; + if (!p->osd_buffers[id]) { + p->osd_buffers[id] = shm_buffer_create(width, + height, + format_table[DEFAULT_ALPHA_FORMAT_ENTRY], + p->wl->display.shm, + &buffer_listener); + } + else if (SHM_BUFFER_IS_BUSY(p->osd_buffers[id])) { + // freed on release in buffer_listener + // garantuees pixel perfect resizing of subtitles and osd + SHM_BUFFER_SET_ONESHOT(p->osd_buffers[id]); + p->osd_buffers[id] = shm_buffer_create(width, + height, + format_table[DEFAULT_ALPHA_FORMAT_ENTRY], + p->wl->display.shm, + &buffer_listener); + } + else { + shm_buffer_resize(p->osd_buffers[id], width, height); + } + + shm_buffer_t *buf = p->osd_buffers[id]; + SHM_BUFFER_SET_BUSY(buf); - size_t dst = (bb.y0) * wlimg.stride[0] + - (bb.x0) * 4; + struct mp_image wlimg = buffer_get_mp_image(p, buf); - memcpy_pic(wlimg.planes[0] + dst, sub->bitmap, sub->w * 4, sub->h, + for (int n = 0; n < imgs->num_parts; n++) { + struct sub_bitmap *sub = &imgs->parts[n]; + memcpy_pic(wlimg.planes[0], sub->bitmap, sub->w * 4, sub->h, wlimg.stride[0], sub->stride); } + wl_subsurface_set_position(p->osd_subsurfaces[id], 0, 0); - wl_surface_attach(s, buf->wlbuf, 0, 0); - wl_surface_damage(s, bb.x0, bb.y0, bb.x1, bb.y1); + wl_surface_attach(s, buf->buffer, bb.x0, bb.y0); + wl_surface_damage(s, 0, 0, width, height); wl_surface_commit(s); } else { - wl_surface_attach(s, buf->wlbuf, 0, 0); + // p->osd_buffer, garantueed to exist here + assert(p->osd_buffers[id]); + wl_surface_attach(s, p->osd_buffers[id]->buffer, 0, 0); wl_surface_commit(s); } } @@ -745,6 +531,7 @@ static const bool osd_formats[SUBBITMAP_COUNT] = { static void draw_osd(struct vo *vo) { struct priv *p = vo->priv; + // deattach all buffers and attach all needed buffers in osd_draw // only the most recent attach & commit is applied once the parent surface // is committed @@ -776,7 +563,7 @@ static int query_format(struct vo *vo, uint32_t format) struct priv *p = vo->priv; struct supported_format *sf; wl_list_for_each_reverse(sf, &p->format_list, link) { - if (sf->fmt->mp_fmt == format) + if (sf->format.mp_format == format) return VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_CSP_SUPPORTED; } @@ -799,8 +586,10 @@ 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->video_format = sf->fmt; + if (sf->format.mp_format == fmt->imgfmt && + (p->enable_alpha == sf->is_alpha)) + { + p->video_format = &sf->format; break; } } @@ -808,24 +597,23 @@ static int reconfig(struct vo *vo, struct mp_image_params *fmt, int flags) if (!p->video_format) { // if use default is enable overwrite the auto selected one if (p->enable_alpha) - p->video_format = &fmttable[DEFAULT_ALPHA_FORMAT_ENTRY]; + p->video_format = &format_table[DEFAULT_ALPHA_FORMAT_ENTRY]; else - p->video_format = &fmttable[DEFAULT_FORMAT_ENTRY]; + p->video_format = &format_table[DEFAULT_FORMAT_ENTRY]; } // overides alpha // use rgb565 if performance is your main concern if (p->use_rgb565) { - const struct fmtentry *entry = + MP_INFO(p->wl, "using rgb565\n"); + const format_t *entry = is_wayland_format_supported(p, WL_SHM_FORMAT_RGB565); if (entry) p->video_format = entry; } - buffer_pool_reinit(p, &p->video_bufpool, (p->use_triplebuffering ? 3 : 2), - p->width, p->height, p->video_format, p->wl->display.shm); - buffer_pool_reinit(p, &p->osd_bufpool, MAX_OSD_PARTS, p->width, p->height, - &fmttable[DEFAULT_ALPHA_FORMAT_ENTRY], p->wl->display.shm); + buffer_pool_reinit(p, &p->video_bufpool, 2, p->width, p->height, + *p->video_format, p->wl->display.shm); vo_wayland_config(vo, flags); @@ -838,7 +626,6 @@ static void uninit(struct vo *vo) { struct priv *p = vo->priv; buffer_pool_destroy(&p->video_bufpool); - buffer_pool_destroy(&p->osd_bufpool); if (p->redraw_callback) wl_callback_destroy(p->redraw_callback); @@ -846,6 +633,7 @@ static void uninit(struct vo *vo) talloc_free(p->original_image); for (int i = 0; i < MAX_OSD_PARTS; ++i) { + shm_buffer_destroy(p->osd_buffers[i]); wl_subsurface_destroy(p->osd_subsurfaces[i]); wl_surface_destroy(p->osd_surfaces[i]); } @@ -891,7 +679,6 @@ static int preinit(struct vo *vo) } wl_region_destroy(input); - return 0; } @@ -902,8 +689,10 @@ static int control(struct vo *vo, uint32_t request, void *data) case VOCTRL_GET_PANSCAN: return VO_TRUE; case VOCTRL_SET_PANSCAN: + { resize(p); return VO_TRUE; + } case VOCTRL_REDRAW_FRAME: return redraw_frame(p); case VOCTRL_SCREENSHOT: @@ -943,7 +732,6 @@ 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} }, }; diff --git a/video/out/wayland/buffer.c b/video/out/wayland/buffer.c new file mode 100644 index 0000000000..26fd3d1cc8 --- /dev/null +++ b/video/out/wayland/buffer.c @@ -0,0 +1,137 @@ +/* + * This file is part of mpv video player. + * Copyright © 2014 Alexander Preisinger + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#include "buffer.h" +#include "memfile.h" + +#include +#include + +int8_t format_get_bytes(const format_t *fmt) +{ + return mp_imgfmt_get_desc(fmt->mp_format).bytes[0]; +} + +shm_buffer_t* shm_buffer_create(uint32_t width, + uint32_t height, + format_t fmt, + struct wl_shm *shm, + const struct wl_buffer_listener *listener) +{ + int8_t bytes = format_get_bytes(&fmt); + uint32_t stride = SHM_BUFFER_STRIDE(width, bytes); + uint32_t size = stride * height; + + shm_buffer_t *buffer = calloc(1, sizeof(shm_buffer_t)); + int fd = memfile_create(size); + + if (fd < 0) + return NULL; + + buffer->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (buffer->data == MAP_FAILED) { + close(fd); + return NULL; + } + + buffer->shm_pool = wl_shm_create_pool(shm, fd, size); + buffer->buffer = wl_shm_pool_create_buffer(buffer->shm_pool, + 0, width, height, stride, + fmt.wl_format); + + wl_buffer_add_listener(buffer->buffer, listener, buffer); + + buffer->fd = fd; + buffer->height = height; + buffer->stride = stride; + buffer->format = fmt; + buffer->bytes = bytes; + buffer->pool_size = size; + buffer->pending_height = 0; + buffer->pending_width = 0; + + return buffer; +} + +int shm_buffer_resize(shm_buffer_t *buffer, uint32_t width, uint32_t height) +{ + uint32_t new_stride = SHM_BUFFER_STRIDE(width, buffer->bytes); + uint32_t new_size = new_stride * height; + + if (!!(buffer->flags & SHM_BUFFER_BUSY)) { + buffer->flags |= SHM_BUFFER_RESIZE_LATER; + buffer->pending_width = width; + buffer->pending_height = height; + return SHM_BUFFER_BUSY; + } + + buffer->flags &= ~SHM_BUFFER_RESIZE_LATER; + + if (new_size > buffer->pool_size) { + munmap(buffer->data, buffer->pool_size); + ftruncate(buffer->fd, new_size); + + buffer->data = mmap(NULL, new_size, PROT_READ | PROT_WRITE, + MAP_SHARED, buffer->fd, 0); + + // TODO: the buffer should be destroyed when -1 is return + if (buffer->data == MAP_FAILED) + return -1; + + wl_shm_pool_resize(buffer->shm_pool, new_size); + buffer->pool_size = new_size; + } + + const void *listener = wl_proxy_get_listener((struct wl_proxy*)buffer->buffer); + + wl_buffer_destroy(buffer->buffer); + buffer->buffer = wl_shm_pool_create_buffer(buffer->shm_pool, + 0, width, height, new_stride, + buffer->format.wl_format); + + wl_buffer_add_listener(buffer->buffer, listener, buffer); + + buffer->height = height; + buffer->stride = new_stride; + + return 0; +} + +int shm_buffer_pending_resize(shm_buffer_t *buffer) +{ + if (SHM_BUFFER_PENDING_RESIZE(buffer)) { + SHM_BUFFER_CLEAR_PNDNG_RSZ(buffer); + return shm_buffer_resize(buffer, buffer->pending_width, buffer->pending_height); + } + else { + return 0; + } +} + +void shm_buffer_destroy(shm_buffer_t *buffer) +{ + if (!buffer) + return; + + wl_buffer_destroy(buffer->buffer); + wl_shm_pool_destroy(buffer->shm_pool); + munmap(buffer->data, buffer->pool_size); + close(buffer->fd); + free(buffer); +} diff --git a/video/out/wayland/buffer.h b/video/out/wayland/buffer.h new file mode 100644 index 0000000000..04e94b9d7f --- /dev/null +++ b/video/out/wayland/buffer.h @@ -0,0 +1,102 @@ +/* + * This file is part of mpv video player. + * Copyright © 2014 Alexander Preisinger + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#ifndef MPLAYER_WAYLAND_BUFFER_H +#define MPLAYER_WAYLAND_BUFFER_H + +#include +#include "video/sws_utils.h" +#include "video/img_format.h" +#include "video/out/wayland_common.h" + +#define SHM_BUFFER_STRIDE(width, bytes) \ + FFALIGN((width) * (bytes), SWS_MIN_BYTE_ALIGN) + +typedef struct format { + enum wl_shm_format wl_format; + enum mp_imgfmt mp_format; +} format_t; + +int8_t format_get_bytes(const format_t *fmt); + +typedef enum shm_buffer_flags { + SHM_BUFFER_BUSY = 1 << 0, // in use by the compositor + SHM_BUFFER_DIRTY = 1 << 1, // buffer contains new content + SHM_BUFFER_ONESHOT = 1 << 2, // free after release + SHM_BUFFER_RESIZE_LATER = 1 << 3, // free after release +} shm_buffer_flags_t; + +#define SHM_BUFFER_IS_BUSY(b) (!!((b)->flags & SHM_BUFFER_BUSY)) +#define SHM_BUFFER_IS_DIRTY(b) (!!((b)->flags & SHM_BUFFER_DIRTY)) +#define SHM_BUFFER_IS_ONESHOT(b) (!!((b)->flags & SHM_BUFFER_ONESHOT)) +#define SHM_BUFFER_PENDING_RESIZE(b) (!!((b)->flags & SHM_BUFFER_RESIZE_LATER)) + +#define SHM_BUFFER_SET_BUSY(b) (b)->flags |= SHM_BUFFER_BUSY +#define SHM_BUFFER_SET_DIRTY(b) (b)->flags |= SHM_BUFFER_DIRTY +#define SHM_BUFFER_SET_ONESHOT(b) (b)->flags |= SHM_BUFFER_ONESHOT +#define SHM_BUFFER_SET_PNDNG_RSZ(b) (b)->flags |= SHM_BUFFER_RESIZE_LATER + +#define SHM_BUFFER_CLEAR_BUSY(b) (b)->flags &= ~SHM_BUFFER_BUSY +#define SHM_BUFFER_CLEAR_DIRTY(b) (b)->flags &= ~SHM_BUFFER_DIRTY +#define SHM_BUFFER_CLEAR_ONESHOT(b) (b)->flags &= ~SHM_BUFFER_ONESHOT +#define SHM_BUFFER_CLEAR_PNDNG_RSZ(b) (b)->flags &= ~SHM_BUFFER_RESIZE_LATER + +typedef struct buffer { + struct wl_buffer *buffer; + + int flags; + + uint32_t height; + uint32_t stride; + uint32_t bytes; // bytes per pixel + // width = stride / bytes per pixel + // size = stride * height + + struct wl_shm_pool *shm_pool; // for growing buffers; + + int fd; + void *data; + uint32_t pool_size; // size of pool and data XXX + // pool_size can be far bigger than the buffer size + + format_t format; + + uint32_t pending_height; + uint32_t pending_width; +} shm_buffer_t; + +shm_buffer_t* shm_buffer_create(uint32_t width, + uint32_t height, + format_t fmt, + struct wl_shm *shm, + const struct wl_buffer_listener *listener); + +// shm pool is only able to grow and won't shrink +// returns 0 on success or buffer flags indicating the buffer status which +// prevent it from resizing +int shm_buffer_resize(shm_buffer_t *buffer, uint32_t width, uint32_t height); + +// if shm_buffer_resize returns SHM_BUFFER_BUSY this function can be called +// after the buffer is released to resize it afterwards +// returns 0 if no pending resize flag was set and -1 on errors +int shm_buffer_pending_resize(shm_buffer_t *buffer); + +// buffer is freed, don't use the buffer after calling this function on it +void shm_buffer_destroy(shm_buffer_t *buffer); + +#endif /* MPLAYER_WAYLAND_BUFFER_H */ diff --git a/video/out/wayland/memfile.c b/video/out/wayland/memfile.c new file mode 100644 index 0000000000..179abe8755 --- /dev/null +++ b/video/out/wayland/memfile.c @@ -0,0 +1,105 @@ +/* + * This file is part of mpv video player. + * Copyright © 2014 Alexander Preisinger + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "video/out/wayland/memfile.h" + +/* copied from weston clients */ +static int set_cloexec_or_close(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + if ((flags = fcntl(fd, F_GETFD)) == -1) + goto err; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + goto err; + + return fd; + +err: + close(fd); + return -1; +} + +static int create_tmpfile_cloexec(char *tmpname) +{ + int fd; + +#ifdef HAVE_MKOSTEMP + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); +#else + fd = mkstemp(tmpname); + if (fd >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } +#endif + + return fd; +} + +static int os_create_anonymous_file(off_t size) +{ + static const char template[] = "/mpv-temp-XXXXXX"; + const char *path; + char *name; + int fd; + + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + + name = malloc(strlen(path) + sizeof(template)); + if (!name) + return -1; + + strcpy(name, path); + strcat(name, template); + + fd = create_tmpfile_cloexec(name); + + free(name); + + if (fd < 0) + return -1; + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +int memfile_create(off_t size) +{ + return os_create_anonymous_file(size); +} diff --git a/video/out/wayland/memfile.h b/video/out/wayland/memfile.h new file mode 100644 index 0000000000..9d59111eec --- /dev/null +++ b/video/out/wayland/memfile.h @@ -0,0 +1,26 @@ +/* + * This file is part of mpv video player. + * Copyright © 2014 Alexander Preisinger + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#ifndef MPLAYER_WAYLAND_MEMFILE_H +#define MPLAYER_WAYLAND_MEMFILE_H + +// create file decsriptor to memory space without filesystem representation +// truncates to size immediately +int memfile_create(off_t size); + +#endif /* MPLAYER_WAYLAND_MEMFILE_H */ -- cgit v1.2.3