mirror of https://github.com/mpv-player/mpv
vo_dmabuf_wayland: rewrite around wl_list
vo_dmabuf_wayland worked by allocating entries to a pool and then having a lot of complex logic dealing with releasing buffers, pending entries, etc. along with some other not so nice things. Instead, we can rewrite this logic so that the wl_buffers created by the imported dmabuf is instead stored in a linked list, wl_list. We can simply append our buffers to the list when needed and destroy everything at the end. On every frame, we can check the ids of our surfaces and reuse existing buffers, so in practice there will only ever be a handful at a time. Some other small changes were made in an attempt to organize the vaapi/drmprime code a little better as well. An important change is to always enforce at least a minimum number of buffers. Certain formats would not make enough unique buffers, and this results in flickering/artifacts occuring. The old way to attempt to deal with this was to clear out all the existing buffers and remake them, but this gets complicated and also didn't always work. An easy solution to this is just create more buffers which appears to solve this problem. The actual number needed is not really based on anything solid, but 8 is a reasonable number to create for the lifetime of a file and it seems to do the trick. Additionally, seeking/loading new files can result in flicker artificts due to buffers being reused when they shouldn't. When that happens, we flip a bool so all the buffers get destroyed in draw_frame to avoid any visual glitches.
This commit is contained in:
parent
b9a5583f17
commit
c1de4bb745
|
@ -1420,7 +1420,6 @@ if features['dmabuf-wayland']
|
|||
sources += files('video/out/hwdec/dmabuf_interop_wl.c')
|
||||
sources += files('video/out/wldmabuf/context_wldmabuf.c')
|
||||
sources += files('video/out/wldmabuf/ra_wldmabuf.c')
|
||||
sources += files('video/out/wlbuf_pool.c')
|
||||
endif
|
||||
|
||||
vdpau_opt = get_option('vdpau').require(
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
* 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 <unistd.h>
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_VAAPI
|
||||
|
@ -25,21 +25,19 @@
|
|||
#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 "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"
|
||||
#include "wldmabuf/ra_wldmabuf.h"
|
||||
|
||||
// Generated from wayland-protocols
|
||||
#include "generated/wayland/linux-dmabuf-unstable-v1.h"
|
||||
|
@ -49,138 +47,272 @@
|
|||
#include "generated/wayland/single-pixel-buffer-v1.h"
|
||||
#endif
|
||||
|
||||
// We need at least enough buffers to avoid a
|
||||
// flickering artifact in certain formats.
|
||||
#define WL_BUFFERS_WANTED 8
|
||||
|
||||
enum hwdec_type {
|
||||
HWDEC_NONE,
|
||||
HWDEC_VAAPI,
|
||||
HWDEC_DRMPRIME,
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
struct vo *vo;
|
||||
struct wl_buffer *buffer;
|
||||
struct wl_list link;
|
||||
struct mp_image *image;
|
||||
|
||||
uint32_t drm_format;
|
||||
uintptr_t id;
|
||||
};
|
||||
|
||||
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;
|
||||
bool want_resize;
|
||||
struct mp_rect src;
|
||||
bool resized;
|
||||
struct wl_list buffer_list;
|
||||
|
||||
#if HAVE_VAAPI
|
||||
VADisplay display;
|
||||
#endif
|
||||
bool destroy_buffers;
|
||||
enum hwdec_type hwdec_type;
|
||||
};
|
||||
|
||||
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer)
|
||||
{
|
||||
struct buffer *buf = data;
|
||||
if (buf->image) {
|
||||
mp_image_unrefp(&buf->image);
|
||||
buf->image = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
buffer_handle_release,
|
||||
};
|
||||
|
||||
#if HAVE_VAAPI
|
||||
static uintptr_t vaapi_key_provider(struct mp_image *src)
|
||||
{
|
||||
return va_surface_id(src);
|
||||
}
|
||||
|
||||
static void close_file_descriptors(VADRMPRIMESurfaceDescriptor desc)
|
||||
{
|
||||
for (int i = 0; i < desc.num_objects; i++)
|
||||
close(desc.objects[i].fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* va-api dmabuf importer */
|
||||
static bool vaapi_dmabuf_importer(struct mp_image *src, struct wlbuf_pool_entry* entry,
|
||||
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 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");
|
||||
close_file_descriptors(desc);
|
||||
|
||||
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 (!ra_compatible_format(p->ctx->ra, drm_format, desc.objects[0].drm_format_modifier)) {
|
||||
MP_VERBOSE(entry->vo, "Surface with format %s; drm format '%s(%016lx)' is "
|
||||
"not supported by compositor.\n",
|
||||
mp_imgfmt_to_name(src->params.hw_subfmt),
|
||||
mp_tag_str(drm_format),
|
||||
desc.objects[0].drm_format_modifier);
|
||||
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;
|
||||
}
|
||||
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:
|
||||
close_file_descriptors(desc);
|
||||
|
||||
return success;
|
||||
}
|
||||
#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;
|
||||
#if HAVE_DRM
|
||||
struct AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->planes[0];
|
||||
|
||||
AVDRMObjectDescriptor object = desc->objects[0];
|
||||
return (uintptr_t)object.fd;
|
||||
id = (uintptr_t)object.fd;
|
||||
#endif
|
||||
return id;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
#if HAVE_DRM
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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 struct buffer *buffer_check(struct vo *vo, struct mp_image *src)
|
||||
{
|
||||
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, src);
|
||||
struct buffer *buf;
|
||||
wl_list_for_each(buf, &p->buffer_list, link) {
|
||||
if (buf->id == id) {
|
||||
if (buf->image) {
|
||||
mp_image_unrefp(&buf->image);
|
||||
buf->image = NULL;
|
||||
goto done;
|
||||
} else {
|
||||
buf->image = src;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct buffer *buffer_create(struct vo *vo, struct mp_image *src)
|
||||
{
|
||||
struct vo_wayland_state *wl = vo->wl;
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
struct buffer *buf = talloc_zero(vo, struct buffer);
|
||||
buf->vo = vo;
|
||||
buf->image = src;
|
||||
|
||||
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, src, params);
|
||||
break;
|
||||
case HWDEC_DRMPRIME:
|
||||
drmprime_dmabuf_importer(buf, src, params);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!buf->drm_format) {
|
||||
mp_image_unrefp(&buf->image);
|
||||
talloc_free(buf);
|
||||
zwp_linux_buffer_params_v1_destroy(params);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf->buffer = zwp_linux_buffer_params_v1_create_immed(params, src->params.w, src->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 mp_image *src)
|
||||
{
|
||||
/* Reuse existing buffer if possible. */
|
||||
struct buffer *buf = buffer_check(vo, src);
|
||||
if (buf) {
|
||||
return buf;
|
||||
} else {
|
||||
return buffer_create(vo, src);
|
||||
}
|
||||
}
|
||||
|
||||
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->image) {
|
||||
mp_image_unrefp(&buf->image);
|
||||
buf->image = NULL;
|
||||
}
|
||||
if (buf->buffer) {
|
||||
wl_buffer_destroy(buf->buffer);
|
||||
buf->buffer = NULL;
|
||||
}
|
||||
talloc_free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
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 (wl->video_viewport && !mp_rect_equals(&p->src, &src)) {
|
||||
// 1. update viewport source
|
||||
wp_viewport_set_source(wl->video_viewport, src.x0 << 8,
|
||||
src.y0 << 8, mp_rect_w(src) << 8,
|
||||
mp_rect_h(src) << 8);
|
||||
// 2. reset buffer pool
|
||||
p->want_reset = true;
|
||||
|
||||
// 3. update to new src dimensions
|
||||
p->src = src;
|
||||
}
|
||||
}
|
||||
|
||||
static void resize(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
struct vo_wayland_state *wl = vo->wl;
|
||||
struct mp_rect src;
|
||||
struct mp_rect dst;
|
||||
|
@ -188,6 +320,9 @@ static void resize(struct vo *vo)
|
|||
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;
|
||||
|
@ -208,83 +343,59 @@ static void resize(struct vo *vo)
|
|||
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);
|
||||
set_viewport_source(vo, src);
|
||||
|
||||
vo->want_redraw = true;
|
||||
p->resized = true;
|
||||
p->want_reset = true;
|
||||
p->want_resize = false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!vo_wayland_check_visible(vo) || !frame->current)
|
||||
return;
|
||||
|
||||
/* 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
|
||||
if (p->destroy_buffers)
|
||||
destroy_buffers(vo);
|
||||
|
||||
struct mp_image *src = mp_image_new_ref(frame->current);
|
||||
buf = buffer_get(vo, src);
|
||||
|
||||
if (buf && buf->image) {
|
||||
wl_surface_attach(wl->video_surface, buf->buffer, 0, 0);
|
||||
wl_surface_damage_buffer(wl->video_surface, 0, 0, buf->image->params.w,
|
||||
buf->image->params.h);
|
||||
}
|
||||
entry = wlbuf_pool_get_entry(p->wlbuf_pool, frame->current);
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
// ensure the pool is reset after hwdec seek,
|
||||
// to avoid stutter artifact
|
||||
if (p->want_reset)
|
||||
wlbuf_pool_clean(p->wlbuf_pool,false);
|
||||
if (p->want_resize)
|
||||
resize(vo);
|
||||
|
||||
MP_TRACE(entry->vo, "Schedule buffer pool entry : %lu\n",entry->key );
|
||||
wl_surface_attach(wl->video_surface, entry->buffer, 0, 0);
|
||||
wl_surface_damage_buffer(wl->video_surface, 0, 0, INT32_MAX, INT32_MAX);
|
||||
}
|
||||
|
||||
static void flip_page(struct vo *vo)
|
||||
{
|
||||
struct vo_wayland_state *wl = vo->wl;
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
wl_surface_commit(wl->video_surface);
|
||||
wl_surface_commit(wl->surface);
|
||||
|
||||
if (!wl->opts->disable_vsync)
|
||||
vo_wayland_wait_frame(wl);
|
||||
|
||||
if (wl->use_present)
|
||||
present_sync_swap(wl->present);
|
||||
if (p->want_reset) {
|
||||
wlbuf_pool_clean(p->wlbuf_pool,false);
|
||||
p->want_reset = false;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -292,6 +403,8 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
|
|||
if (!vo_wayland_reconfig(vo))
|
||||
return VO_ERROR;
|
||||
|
||||
// Immediately destroy all buffers if params change.
|
||||
destroy_buffers(vo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -309,9 +422,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
|
|||
int ret;
|
||||
|
||||
switch (request) {
|
||||
case VOCTRL_SET_PANSCAN:
|
||||
p->want_resize = true;
|
||||
return VO_TRUE;
|
||||
case VOCTRL_LOAD_HWDEC_API:
|
||||
assert(p->hwdec_ctx.ra);
|
||||
struct hwdec_imgfmt_request* req = (struct hwdec_imgfmt_request*)data;
|
||||
|
@ -319,19 +429,17 @@ static int control(struct vo *vo, uint32_t request, void *data)
|
|||
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;
|
||||
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);
|
||||
if (events & VO_EVENT_RESIZE){
|
||||
p->want_resize = true;
|
||||
if (!p->resized)
|
||||
resize(vo);
|
||||
}
|
||||
if (events & VO_EVENT_RESIZE)
|
||||
resize(vo);
|
||||
if (events & VO_EVENT_EXPOSE)
|
||||
vo->want_redraw = true;
|
||||
vo_event(vo, events);
|
||||
|
@ -343,7 +451,7 @@ static void uninit(struct vo *vo)
|
|||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
wlbuf_pool_free(p->wlbuf_pool);
|
||||
destroy_buffers(vo);
|
||||
if (p->solid_buffer_pool)
|
||||
wl_shm_pool_destroy(p->solid_buffer_pool);
|
||||
if (p->solid_buffer)
|
||||
|
@ -353,6 +461,7 @@ static void uninit(struct vo *vo)
|
|||
hwdec_devices_set_loader(vo->hwdec_devs, NULL, NULL);
|
||||
hwdec_devices_destroy(vo->hwdec_devs);
|
||||
}
|
||||
|
||||
vo_wayland_uninit(vo);
|
||||
ra_ctx_destroy(&p->ctx);
|
||||
}
|
||||
|
@ -368,6 +477,8 @@ static int preinit(struct vo *vo)
|
|||
goto err;
|
||||
assert(p->ctx->ra);
|
||||
|
||||
wl_list_init(&p->buffer_list);
|
||||
|
||||
if (!vo->wl->dmabuf) {
|
||||
MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n",
|
||||
zwp_linux_dmabuf_v1_interface.name);
|
||||
|
@ -424,9 +535,24 @@ static int preinit(struct vo *vo)
|
|||
.global = p->global,
|
||||
.ra = p->ctx->ra,
|
||||
};
|
||||
ra_hwdec_ctx_init(&p->hwdec_ctx, vo->hwdec_devs, NULL, true);
|
||||
p->src = (struct mp_rect){0, 0, 0, 0};
|
||||
|
||||
ra_hwdec_ctx_init(&p->hwdec_ctx, vo->hwdec_devs, NULL, true);
|
||||
|
||||
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!");
|
||||
goto err;
|
||||
}
|
||||
|
||||
p->src = (struct mp_rect){0, 0, 0, 0};
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
|
|
@ -1,229 +0,0 @@
|
|||
/*
|
||||
* 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 "mpv_talloc.h"
|
||||
#include "common/global.h"
|
||||
#include "vo.h"
|
||||
#include "video/mp_image.h"
|
||||
|
||||
#include "wayland_common.h"
|
||||
#include "generated/wayland/linux-dmabuf-unstable-v1.h"
|
||||
#include "pthread.h"
|
||||
#include "wlbuf_pool.h"
|
||||
|
||||
#define WLBUF_POOL_NUM_ALLOCATED_INIT 30
|
||||
|
||||
static void wlbuf_pool_entry_free(struct wlbuf_pool_entry *entry, bool force);
|
||||
|
||||
/**
|
||||
* When trying to free pool entries that are being held by Wayland,
|
||||
* these entries are set to pending free state. They will be freed
|
||||
* when either the frame listener gets called, or the vo is uninitialized
|
||||
*/
|
||||
static bool set_pending_free(struct wlbuf_pool_entry *entry) {
|
||||
if (entry->image) {
|
||||
MP_TRACE(entry->vo, "Pending free for buffer pool entry : %lu\n",
|
||||
entry->key);
|
||||
entry->pending_free = true;
|
||||
// add to purgatory
|
||||
struct wlbuf_pool *pool = entry->pool;
|
||||
for (int i = 0; i < WLBUF_NUM_PURG_ENTRIES; ++i) {
|
||||
if (pool->purg.entries[i] == NULL) {
|
||||
pool->purg.entries[i] = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entry->image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete pending free of entry.
|
||||
*
|
||||
*/
|
||||
static void complete_pending_free(struct wlbuf_pool_entry *entry) {
|
||||
if (entry->pending_free) {
|
||||
// 1. remove from purgatory
|
||||
struct wlbuf_pool *pool = entry->pool;
|
||||
for (int i = 0; i < WLBUF_NUM_PURG_ENTRIES; ++i) {
|
||||
if (pool->purg.entries[i] == entry) {
|
||||
pool->purg.entries[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 2. free entry
|
||||
wlbuf_pool_entry_free(entry, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free all purgatory entries
|
||||
*/
|
||||
static void free_purgatory(struct wlbuf_pool *pool){
|
||||
for (int i = 0; i < WLBUF_NUM_PURG_ENTRIES; ++i)
|
||||
wlbuf_pool_entry_free(pool->purg.entries[i], true);
|
||||
}
|
||||
|
||||
struct wlbuf_pool *wlbuf_pool_alloc(struct vo *vo, struct vo_wayland_state *wl, wlbuf_pool_key_provider key_provider,
|
||||
wlbuf_pool_dmabuf_importer dmabuf_importer)
|
||||
{
|
||||
struct wlbuf_pool *pool = talloc(NULL, struct wlbuf_pool);
|
||||
memset(pool, 0, sizeof(struct wlbuf_pool));
|
||||
pool->num_allocated = WLBUF_POOL_NUM_ALLOCATED_INIT;
|
||||
pool->entries = talloc_array(pool, struct wlbuf_pool_entry *, pool->num_allocated);
|
||||
memset(pool->entries, 0, pool->num_allocated * sizeof(struct wlbuf_pool_entry *));
|
||||
pool->vo = vo;
|
||||
pool->key_provider = key_provider;
|
||||
pool->dmabuf_importer = dmabuf_importer;
|
||||
pool->wl = wl;
|
||||
for (int i = 0; i < WLBUF_NUM_PURG_ENTRIES; ++i)
|
||||
pool->purg.entries[i] = NULL;
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
void wlbuf_pool_clean(struct wlbuf_pool *pool, bool final_clean)
|
||||
{
|
||||
int i;
|
||||
if (!pool)
|
||||
return;
|
||||
MP_TRACE(pool->vo, "Begin clean pool\n");
|
||||
if (final_clean)
|
||||
free_purgatory(pool);
|
||||
for (i = 0; i < pool->num_allocated; ++i) {
|
||||
struct wlbuf_pool_entry *entry = pool->entries[i];
|
||||
if (!entry)
|
||||
continue;
|
||||
wlbuf_pool_entry_free(entry, final_clean);
|
||||
pool->entries[i] = NULL;
|
||||
}
|
||||
pool->num_entries = 0;
|
||||
MP_TRACE(pool->vo, "End clean pool\n");
|
||||
}
|
||||
|
||||
void wlbuf_pool_free(struct wlbuf_pool *pool)
|
||||
{
|
||||
if (!pool)
|
||||
return;
|
||||
|
||||
wlbuf_pool_clean(pool, true);
|
||||
talloc_free(pool);
|
||||
}
|
||||
|
||||
static void wlbuf_pool_entry_free(struct wlbuf_pool_entry *entry, bool force)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
if (force && entry->image) {
|
||||
mp_image_unrefp(&entry->image);
|
||||
entry->image = NULL;
|
||||
}
|
||||
if (!set_pending_free(entry)) {
|
||||
MP_TRACE(entry->vo, "Free buffer pool entry : %lu\n", entry->key);
|
||||
if (entry->buffer)
|
||||
wl_buffer_destroy(entry->buffer);
|
||||
talloc_free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unref pool entry's image, and also free entry itself if it's set to pending_free
|
||||
*/
|
||||
static void wlbuf_pool_entry_release(void *data, struct wl_buffer *wl_buffer)
|
||||
{
|
||||
struct wlbuf_pool_entry *entry = (struct wlbuf_pool_entry*) data;
|
||||
|
||||
MP_TRACE(entry->vo, "Release buffer pool entry : %lu\n", entry->key);
|
||||
|
||||
// 1. always unref image
|
||||
if (entry->image)
|
||||
mp_image_unrefp(&entry->image);
|
||||
entry->image = NULL;
|
||||
|
||||
// 2. complete pending free if needed
|
||||
complete_pending_free(entry);
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener wlbuf_pool_listener = {
|
||||
wlbuf_pool_entry_release,
|
||||
};
|
||||
|
||||
struct wlbuf_pool_entry *wlbuf_pool_get_entry(struct wlbuf_pool *pool, struct mp_image *src)
|
||||
{
|
||||
uintptr_t key;
|
||||
struct wlbuf_pool_entry *entry;
|
||||
struct vo_wayland_state *wl = pool->wl;
|
||||
bool import_successful;
|
||||
struct zwp_linux_buffer_params_v1 *params;
|
||||
|
||||
if (!pool || !src)
|
||||
return NULL;
|
||||
|
||||
/* 1. try to find existing entry in pool */
|
||||
src = mp_image_new_ref(src);
|
||||
key = pool->key_provider(src);
|
||||
for (int i = 0; i < pool->num_entries; ++i) {
|
||||
struct wlbuf_pool_entry *item = pool->entries[i];
|
||||
if (!item)
|
||||
continue;
|
||||
if (item->key == key) {
|
||||
if (item->image) {
|
||||
mp_image_unrefp(&src);
|
||||
MP_DBG(item->vo, "Buffer already scheduled - returning NULL.\n");
|
||||
return NULL;
|
||||
} else {
|
||||
item->image = src;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 2. otherwise allocate new entry and buffer */
|
||||
entry = talloc(pool, struct wlbuf_pool_entry);
|
||||
memset(entry, 0, sizeof(struct wlbuf_pool_entry));
|
||||
entry->vo = pool->vo;
|
||||
entry->key = pool->key_provider(src);
|
||||
entry->pool = pool;
|
||||
MP_TRACE(entry->vo, "Allocate buffer pool entry : %lu\n", entry->key);
|
||||
params = zwp_linux_dmabuf_v1_create_params(wl->dmabuf);
|
||||
import_successful = pool->dmabuf_importer(src, entry, params);
|
||||
if (!import_successful) {
|
||||
MP_DBG(entry->vo, "Failed to import\n");
|
||||
wlbuf_pool_entry_free(entry, false);
|
||||
} else {
|
||||
entry->buffer = zwp_linux_buffer_params_v1_create_immed(params, src->params.w, src->params.h,
|
||||
entry->drm_format, 0);
|
||||
}
|
||||
zwp_linux_buffer_params_v1_destroy(params);
|
||||
if (!import_successful) {
|
||||
mp_image_unrefp(&src);
|
||||
return NULL;
|
||||
}
|
||||
/* 3. add new entry to pool */
|
||||
if (pool->num_entries == pool->num_allocated) {
|
||||
int current_num_allocated = pool->num_allocated;
|
||||
pool->num_allocated *= 2;
|
||||
pool->entries = talloc_realloc(pool, pool->entries, struct wlbuf_pool_entry *, pool->num_allocated);
|
||||
for (int i = current_num_allocated; i < pool->num_allocated; ++i)
|
||||
pool->entries[i] = NULL;
|
||||
}
|
||||
wl_buffer_add_listener(entry->buffer, &wlbuf_pool_listener, entry);
|
||||
entry->image = src;
|
||||
pool->entries[pool->num_entries++] = entry;
|
||||
|
||||
return entry;
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_VIDEO_OUT_WLBUF_POOL_H
|
||||
#define MPLAYER_VIDEO_OUT_WLBUF_POOL_H
|
||||
|
||||
#include "wayland_common.h"
|
||||
#include "generated/wayland/linux-dmabuf-unstable-v1.h"
|
||||
|
||||
struct wlbuf_pool_entry;
|
||||
|
||||
typedef uintptr_t (*wlbuf_pool_key_provider)(struct mp_image *src);
|
||||
typedef bool (*wlbuf_pool_dmabuf_importer)(struct mp_image *src, struct wlbuf_pool_entry* entry,
|
||||
struct zwp_linux_buffer_params_v1 *params);
|
||||
|
||||
#define WLBUF_NUM_PURG_ENTRIES 60
|
||||
|
||||
struct wlbuf_purgatory {
|
||||
struct wlbuf_pool_entry *entries[WLBUF_NUM_PURG_ENTRIES];
|
||||
};
|
||||
|
||||
struct wlbuf_pool {
|
||||
struct vo *vo;
|
||||
struct vo_wayland_state *wl;
|
||||
struct wlbuf_pool_entry **entries;
|
||||
int num_entries;
|
||||
int num_allocated;
|
||||
wlbuf_pool_key_provider key_provider;
|
||||
wlbuf_pool_dmabuf_importer dmabuf_importer;
|
||||
struct wlbuf_purgatory purg;
|
||||
};
|
||||
|
||||
struct wlbuf_pool_entry {
|
||||
uintptr_t key;
|
||||
struct vo *vo;
|
||||
struct wl_buffer *buffer;
|
||||
uint32_t drm_format;
|
||||
struct mp_image *image;
|
||||
bool pending_free;
|
||||
struct wlbuf_pool *pool;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate pool
|
||||
*/
|
||||
struct wlbuf_pool *wlbuf_pool_alloc(struct vo *vo, struct vo_wayland_state *wl, wlbuf_pool_key_provider key_provider,
|
||||
wlbuf_pool_dmabuf_importer dmabuf_importer);
|
||||
|
||||
/**
|
||||
* Free pool entries but leave pool itself intact
|
||||
*/
|
||||
void wlbuf_pool_clean(struct wlbuf_pool *pool, bool final_clean);
|
||||
|
||||
/**
|
||||
* Free pool
|
||||
*/
|
||||
void wlbuf_pool_free(struct wlbuf_pool *pool);
|
||||
|
||||
/**
|
||||
* Get pool entry - will allocate entry if not present in pool.
|
||||
*/
|
||||
struct wlbuf_pool_entry *wlbuf_pool_get_entry(struct wlbuf_pool *pool, struct mp_image *src);
|
||||
|
||||
#endif
|
|
@ -520,7 +520,6 @@ def build(ctx):
|
|||
( "video/out/present_sync.c", "wayland || x11" ),
|
||||
( "video/out/wldmabuf/context_wldmabuf.c", "dmabuf-wayland" ),
|
||||
( "video/out/wldmabuf/ra_wldmabuf.c", "dmabuf-wayland" ),
|
||||
( "video/out/wlbuf_pool.c", "dmabuf-wayland" ),
|
||||
( "video/out/vo.c" ),
|
||||
( "video/out/vo_caca.c", "caca" ),
|
||||
( "video/out/vo_direct3d.c", "direct3d" ),
|
||||
|
|
Loading…
Reference in New Issue