From 6158bb5be22e83270f79a771e11a65b274e51182 Mon Sep 17 00:00:00 2001 From: Dudemanguy Date: Wed, 6 Apr 2022 22:36:30 -0500 Subject: [PATCH] x11: avoid wasteful rendering when possible Because wayland is a special snowflake, mpv wound up incorporating a lot of logic into its render loop where visibilty checks are performed before rendering anything (in the name of efficiency of course). Only wayland actually uses this, but there's no reason why other backends (x11 in this commit) can't be smarter. It's far easier on xorg since we can just query _NET_WM_STATE_HIDDEN directly and not have to do silly callback dances. The function, vo_x11_check_net_wm_state_change, already tracks net wm changes, including _NET_WM_STATE_HIDDEN. There is an already existing window_hidden variable but that is actually just for checking if the window was mapped and has nothing to do with this particular atom. mpv also currently assumes that a _NET_WM_STATE_HIDDEN is exactly the same as being minimized but according to the spec, that's not neccesarily true (in practice, it's likely that these are the same though). Anyways, just keep track of this state in a new variable (hidden) and use that for determing if mpv should render or not. There is one catch though: this cannot work if a display sync mode is used. This is why the previous commit is needed. The display sync modes in mpv require a blocking vsync implementation since its render loop is directly driven by vsync. In xorg, if nothing is actually rendered, then there's nothing for eglSwapBuffers (or FIFO for vulkan) to block on so it returns immediately. This, of course, results in completely broken video. We just need to check to make sure that we aren't in a display sync mode before trying to be smart about rendering. Display sync is power inefficient anyways, so no one is really being hurt here. As an aside, this happens to work in wayland because there's basically a custom (and ugly) vsync blocking function + timeout but that's off topic. --- video/out/opengl/context_glx.c | 6 ++++++ video/out/opengl/context_x11egl.c | 6 ++++++ video/out/vo_x11.c | 3 +++ video/out/vo_xv.c | 3 +++ video/out/vulkan/context_xlib.c | 9 ++++++++- video/out/x11_common.c | 9 +++++++++ video/out/x11_common.h | 2 ++ 7 files changed, 37 insertions(+), 1 deletion(-) diff --git a/video/out/opengl/context_glx.c b/video/out/opengl/context_glx.c index 677260c197..6ca9f19d3e 100644 --- a/video/out/opengl/context_glx.c +++ b/video/out/opengl/context_glx.c @@ -223,6 +223,11 @@ static void update_vsync_oml(struct ra_ctx *ctx) oml_sync_swap(&p->sync, ust, msc, sbc); } +static bool glx_check_visible(struct ra_ctx *ctx) +{ + return vo_x11_check_visible(ctx->vo); +} + static void glx_swap_buffers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -313,6 +318,7 @@ static bool glx_init(struct ra_ctx *ctx) goto uninit; struct ra_gl_ctx_params params = { + .check_visible = glx_check_visible, .swap_buffers = glx_swap_buffers, .get_vsync = glx_get_vsync, }; diff --git a/video/out/opengl/context_x11egl.c b/video/out/opengl/context_x11egl.c index f1596bcee4..4e0b277da7 100644 --- a/video/out/opengl/context_x11egl.c +++ b/video/out/opengl/context_x11egl.c @@ -75,6 +75,11 @@ static int pick_xrgba_config(void *user_data, EGLConfig *configs, int num_config return 0; } +static bool mpegl_check_visible(struct ra_ctx *ctx) +{ + return vo_x11_check_visible(ctx->vo); +} + static void mpegl_swap_buffers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -169,6 +174,7 @@ static bool mpegl_init(struct ra_ctx *ctx) mpegl_load_functions(&p->gl, ctx->log); struct ra_gl_ctx_params params = { + .check_visible = mpegl_check_visible, .swap_buffers = mpegl_swap_buffers, .get_vsync = mpegl_get_vsync, }; diff --git a/video/out/vo_x11.c b/video/out/vo_x11.c index 67fac3c9a7..6b7a797412 100644 --- a/video/out/vo_x11.c +++ b/video/out/vo_x11.c @@ -315,6 +315,9 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) struct priv *p = vo->priv; wait_for_completion(vo, 1); + bool render = vo_x11_check_visible(vo); + if (!render) + return; struct mp_image *img = &p->mp_ximages[p->current_buf]; diff --git a/video/out/vo_xv.c b/video/out/vo_xv.c index 43fc046f9f..d93673493f 100644 --- a/video/out/vo_xv.c +++ b/video/out/vo_xv.c @@ -697,6 +697,9 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) struct xvctx *ctx = vo->priv; wait_for_completion(vo, ctx->num_buffers - 1); + bool render = vo_x11_check_visible(vo); + if (!render) + return; struct mp_image xv_buffer = get_xv_buffer(vo, ctx->current_buf); if (mpi) { diff --git a/video/out/vulkan/context_xlib.c b/video/out/vulkan/context_xlib.c index 6278bc3c69..2d498723df 100644 --- a/video/out/vulkan/context_xlib.c +++ b/video/out/vulkan/context_xlib.c @@ -26,6 +26,11 @@ struct priv { struct mpvk_ctx vk; }; +static bool xlib_check_visible(struct ra_ctx *ctx) +{ + return vo_x11_check_visible(ctx->vo); +} + static void xlib_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -56,7 +61,9 @@ static bool xlib_init(struct ra_ctx *ctx) .window = ctx->vo->x11->window, }; - struct ra_vk_ctx_params params = {0}; + struct ra_vk_ctx_params params = { + .check_visible = xlib_check_visible, + }; VkInstance inst = vk->vkinst->instance; VkResult res = vkCreateXlibSurfaceKHR(inst, &xinfo, NULL, &vk->surface); diff --git a/video/out/x11_common.c b/video/out/x11_common.c index d1fb3b6f0a..d8cda687f8 100644 --- a/video/out/x11_common.c +++ b/video/out/x11_common.c @@ -1043,6 +1043,7 @@ static void vo_x11_check_net_wm_state_change(struct vo *vo) } opts->window_minimized = is_minimized; + x11->hidden = is_minimized; m_config_cache_write_opt(x11->opts_cache, &opts->window_minimized); opts->window_maximized = is_maximized; m_config_cache_write_opt(x11->opts_cache, &opts->window_maximized); @@ -1863,6 +1864,14 @@ static void vo_x11_set_geometry(struct vo *vo) } } +bool vo_x11_check_visible(struct vo *vo) { + struct vo_x11_state *x11 = vo->x11; + struct mp_vo_opts *opts = x11->opts; + + bool render = !x11->hidden || VS_IS_DISP(opts->video_sync); + return render; +} + int vo_x11_control(struct vo *vo, int *events, int request, void *arg) { struct vo_x11_state *x11 = vo->x11; diff --git a/video/out/x11_common.h b/video/out/x11_common.h index 45c8d04d21..acc08f2ee0 100644 --- a/video/out/x11_common.h +++ b/video/out/x11_common.h @@ -80,6 +80,7 @@ struct vo_x11_state { Colormap colormap; int wm_type; + bool hidden; // _NET_WM_STATE_HIDDEN bool window_hidden; // the window was mapped at least once bool pseudo_mapped; // not necessarily mapped, but known window size int fs; // whether we assume the window is in fullscreen mode @@ -142,6 +143,7 @@ bool vo_x11_screen_is_composited(struct vo *vo); bool vo_x11_create_vo_window(struct vo *vo, XVisualInfo *vis, const char *classname); void vo_x11_config_vo_window(struct vo *vo); +bool vo_x11_check_visible(struct vo *vo); int vo_x11_control(struct vo *vo, int *events, int request, void *arg); void vo_x11_wakeup(struct vo *vo); void vo_x11_wait_events(struct vo *vo, int64_t until_time_us);