Skip to content

Commit

Permalink
wayland: use the surface-suspension-v1 protocol
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Dudemanguy committed Jun 25, 2021
1 parent 5f76744 commit 98e792a
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 56 deletions.
3 changes: 1 addition & 2 deletions video/out/opengl/context_wayland.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions video/out/vo_wlshm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 1 addition & 3 deletions video/out/vulkan/context_wayland.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
100 changes: 56 additions & 44 deletions video/out/wayland_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);

Expand All @@ -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);
Expand All @@ -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++) {
Expand Down Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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)
Expand Down
11 changes: 6 additions & 5 deletions video/out/wayland_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions wscript
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions wscript_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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" ),
Expand Down

0 comments on commit 98e792a

Please sign in to comment.