From 98e792a15c846d9aae773644efd732b8182f7ed8 Mon Sep 17 00:00:00 2001 From: Dudemanguy Date: Fri, 25 Jun 2021 10:39:07 -0500 Subject: [PATCH] wayland: use the surface-suspension-v1 protocol Currently, mpv tries very hard to not do wasteful draws when the surface is hidden. This is accomplished by abusing vo_wayland_wait_frame and just guessing that consecutive timeouts == surface hidden. This works but it's pretty ugly (explaining why wl->timeout_count > 1 is an entire novel. Go dig up that commit if you're curious). Luckily, we now have the ability to just get suspended events from the compositor. Delete the entire timeout/hidden surface heuristic and use the surface_suspended and surface_resumed events instead. As a side effect of this, this means that mpv will always "overdraw" (i.e. render when not needed) if the compositor does not implement this protocol. If this bothers you, go bug your compositor developers to fix it and/or switch do a better one. --- video/out/opengl/context_wayland.c | 3 +- video/out/vo_wlshm.c | 3 +- video/out/vulkan/context_wayland.c | 4 +- video/out/wayland_common.c | 100 ++++++++++++++++------------- video/out/wayland_common.h | 11 ++-- wscript | 1 + wscript_build.py | 7 ++ 7 files changed, 73 insertions(+), 56 deletions(-) diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c index eeaeb11cc151d..595dcad217cf2 100644 --- a/video/out/opengl/context_wayland.c +++ b/video/out/opengl/context_wayland.c @@ -63,10 +63,9 @@ static bool wayland_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_ { struct ra_ctx *ctx = sw->ctx; struct vo_wayland_state *wl = ctx->vo->wl; - bool render = wl->render || wl->opts->disable_vsync; wl->frame_wait = true; - return render ? ra_gl_ctx_start_frame(sw, out_fbo) : false; + return wl->suspended ? false : ra_gl_ctx_start_frame(sw, out_fbo); } static void wayland_egl_swap_buffers(struct ra_swapchain *sw) diff --git a/video/out/vo_wlshm.c b/video/out/vo_wlshm.c index 69cfdd9dccca8..5288a61221e28 100644 --- a/video/out/vo_wlshm.c +++ b/video/out/vo_wlshm.c @@ -218,10 +218,9 @@ static void draw_image(struct vo *vo, struct mp_image *src) struct priv *p = vo->priv; struct vo_wayland_state *wl = vo->wl; struct buffer *buf; - bool render = wl->render || wl->opts->disable_vsync; wl->frame_wait = true; - if (!render) + if (wl->suspended) return; buf = p->free_buffers; diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c index d711a855c4798..0bbe2369e4b60 100644 --- a/video/out/vulkan/context_wayland.c +++ b/video/out/vulkan/context_wayland.c @@ -32,10 +32,8 @@ struct priv { static bool wayland_vk_start_frame(struct ra_ctx *ctx) { struct vo_wayland_state *wl = ctx->vo->wl; - bool render = wl->render || wl->opts->disable_vsync; wl->frame_wait = true; - - return render; + return !wl->suspended; } static void wayland_vk_swap_buffers(struct ra_ctx *ctx) diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index 850c8d362d4dd..ad187d6e6dc37 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -30,17 +30,12 @@ #include "win_state.h" #include "wayland_common.h" -// Generated from xdg-shell.xml -#include "generated/wayland/xdg-shell.h" - -// Generated from idle-inhibit-unstable-v1.xml +// Generated from wayland-protocols #include "generated/wayland/idle-inhibit-unstable-v1.h" - -// Generated from xdg-decoration-unstable-v1.xml -#include "generated/wayland/xdg-decoration-unstable-v1.h" - -// Generated from presentation-time.xml #include "generated/wayland/presentation-time.h" +#include "generated/wayland/surface-suspension.h" +#include "generated/wayland/xdg-decoration-unstable-v1.h" +#include "generated/wayland/xdg-shell.h" #define OPT_BASE_STRUCT struct wayland_opts const struct m_sub_options wayland_conf = { @@ -1013,6 +1008,26 @@ static const struct wl_callback_listener frame_listener = { frame_callback, }; +static void surface_suspended(void *data, struct wp_surface_suspension_v1 *surface_suspension) +{ + struct vo_wayland_state *wl = data; + wl->suspended = true; +} + +static void surface_resumed(void *data, struct wp_surface_suspension_v1 *surface_suspension) +{ + struct vo_wayland_state *wl = data; + wl->suspended = false; + + /* If the surface comes back into view, force a redraw. */ + wl->pending_vo_events |= VO_EVENT_EXPOSE; +} + +static const struct wp_surface_suspension_v1_listener surface_suspension_listener = { + surface_suspended, + surface_resumed, +}; + static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) { @@ -1026,6 +1041,10 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id wl_surface_add_listener(wl->surface, &surface_listener, wl); } + if (!strcmp(interface, wl_data_device_manager_interface.name) && (ver >= 3) && found++) { + wl->dnd_devman = wl_registry_bind(reg, id, &wl_data_device_manager_interface, 3); + } + if (!strcmp(interface, wl_output_interface.name) && (ver >= 2) && found++) { struct vo_wayland_output *output = talloc_zero(wl, struct vo_wayland_output); @@ -1038,12 +1057,6 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id wl_list_insert(&wl->output_list, &output->link); } - if (!strcmp(interface, xdg_wm_base_interface.name) && found++) { - ver = MPMIN(ver, 2); /* We can use either 1 or 2 */ - wl->wm_base = wl_registry_bind(reg, id, &xdg_wm_base_interface, ver); - xdg_wm_base_add_listener(wl->wm_base, &xdg_wm_base_listener, wl); - } - if (!strcmp(interface, wl_seat_interface.name) && found++) { wl->seat = wl_registry_bind(reg, id, &wl_seat_interface, 1); wl_seat_add_listener(wl->seat, &seat_listener, wl); @@ -1053,17 +1066,24 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id wl->shm = wl_registry_bind(reg, id, &wl_shm_interface, 1); } - if (!strcmp(interface, wl_data_device_manager_interface.name) && (ver >= 3) && found++) { - wl->dnd_devman = wl_registry_bind(reg, id, &wl_data_device_manager_interface, 3); + if (!strcmp(interface, wp_presentation_interface.name) && found++) { + wl->presentation = wl_registry_bind(reg, id, &wp_presentation_interface, 1); + wp_presentation_add_listener(wl->presentation, &pres_listener, wl); } - if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name) && found++) { - wl->xdg_decoration_manager = wl_registry_bind(reg, id, &zxdg_decoration_manager_v1_interface, 1); + if (!strcmp(interface, wp_surface_suspension_manager_v1_interface.name) && found++) { + wl->surface_suspension_manager = wl_registry_bind(reg, id, &wp_surface_suspension_manager_v1_interface, 1); } - if (!strcmp(interface, wp_presentation_interface.name) && found++) { - wl->presentation = wl_registry_bind(reg, id, &wp_presentation_interface, 1); - wp_presentation_add_listener(wl->presentation, &pres_listener, wl); + if (!strcmp(interface, xdg_wm_base_interface.name) && found++) { + ver = MPMIN(ver, 2); /* We can use either 1 or 2 */ + wl->wm_base = wl_registry_bind(reg, id, &xdg_wm_base_interface, ver); + xdg_wm_base_add_listener(wl->wm_base, &xdg_wm_base_listener, wl); + } + + + if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name) && found++) { + wl->xdg_decoration_manager = wl_registry_bind(reg, id, &zxdg_decoration_manager_v1_interface, 1); } if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) && found++) { @@ -1178,12 +1198,6 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel, { wl->focused = !wl->focused; wl->pending_vo_events |= VO_EVENT_FOCUS; - - if (wl->activated) { - /* If the surface comes back into view, force a redraw. */ - vo_wayland_wait_frame(wl); - wl->pending_vo_events |= VO_EVENT_EXPOSE; - } } } @@ -1363,6 +1377,14 @@ int vo_wayland_init(struct vo *vo) wp_presentation_interface.name); } + if (wl->surface_suspension_manager) { + wl->surface_suspension = wp_surface_suspension_manager_v1_get_surface_suspension(wl->surface_suspension_manager, wl->surface); + wp_surface_suspension_v1_add_listener(wl->surface_suspension, &surface_suspension_listener, wl); + } else { + MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n", + wp_surface_suspension_manager_v1_interface.name); + } + if (wl->xdg_decoration_manager) { wl->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(wl->xdg_decoration_manager, wl->xdg_toplevel); set_border_decorations(wl, wl->vo_opts->border); @@ -1453,6 +1475,12 @@ void vo_wayland_uninit(struct vo *vo) if (wl->surface) wl_surface_destroy(wl->surface); + if (wl->surface_suspension) + wp_surface_suspension_v1_destroy(wl->surface_suspension); + + if (wl->surface_suspension_manager) + wp_surface_suspension_manager_v1_destroy(wl->surface_suspension_manager); + if (wl->frame_callback) wl_callback_destroy(wl->frame_callback); @@ -1933,22 +1961,6 @@ void vo_wayland_wait_frame(struct vo_wayland_state *wl) * that this wait is accurate. Do a hacky block with wl_display_roundtrip. */ if (!wl->presentation && !wl_display_get_error(wl->display)) wl_display_roundtrip(wl->display); - - if (wl->frame_wait) { - // Only consider consecutive missed callbacks. - if (wl->timeout_count > 1) { - wl->render = false; - return; - } else { - wl->timeout_count += 1; - wl->render = true; - return; - } - } - - wl->timeout_count = 0; - wl->render = true; - return; } void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us) diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index bbdfdf82d2e50..38aa99358a5e3 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -73,14 +73,13 @@ struct vo_wayland_state { int gcd; int reduced_width; int reduced_height; + bool activated; + bool focused; bool frame_wait; - bool render; + bool has_keyboard_input; bool state_change; + bool suspended; bool toplevel_configured; - bool activated; - bool has_keyboard_input; - bool focused; - int timeout_count; int wakeup_pipe[2]; int pending_vo_events; int mouse_x; @@ -104,6 +103,8 @@ struct vo_wayland_state { struct xdg_surface *xdg_surface; struct wp_presentation *presentation; struct wp_presentation_feedback *feedback; + struct wp_surface_suspension_v1 *surface_suspension; + struct wp_surface_suspension_manager_v1 *surface_suspension_manager; struct zxdg_decoration_manager_v1 *xdg_decoration_manager; struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; diff --git a/wscript b/wscript index 1a5ff5aa650be..a4d7b6f77aa7c 100644 --- a/wscript +++ b/wscript @@ -498,6 +498,7 @@ video_output_features = [ 'desc': 'wayland-scanner', 'func': check_program('wayland-scanner', 'WAYSCAN') } , { + #TODO: add a version check here for whenever the protocol gets merged 'name': '--wayland-protocols', 'desc': 'wayland-protocols', 'func': check_wl_protocols diff --git a/wscript_build.py b/wscript_build.py index fbec5006f8c3e..8dced8c8c0fa2 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -109,6 +109,12 @@ def build(ctx): ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR, protocol = "stable/xdg-shell/xdg-shell", target = "generated/wayland/xdg-shell.h") + ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "staging/surface-suspension/surface-suspension-v1", + target = "generated/wayland/surface-suspension.c") + ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "staging/surface-suspension/surface-suspension-v1", + target = "generated/wayland/surface-suspension.h") ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR, protocol = "unstable/idle-inhibit/idle-inhibit-unstable-v1", target = "generated/wayland/idle-inhibit-unstable-v1.c") @@ -515,6 +521,7 @@ def swift(task): ( "video/out/w32_common.c", "win32-desktop" ), ( "generated/wayland/idle-inhibit-unstable-v1.c", "wayland" ), ( "generated/wayland/presentation-time.c", "wayland" ), + ( "generated/wayland/surface-suspension.c", "wayland" ), ( "generated/wayland/xdg-decoration-unstable-v1.c", "wayland" ), ( "generated/wayland/xdg-shell.c", "wayland" ), ( "video/out/wayland_common.c", "wayland" ),