summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDudemanguy <random342@airmail.cc>2021-05-23 14:36:19 -0500
committerDudemanguy <random342@airmail.cc>2021-05-24 19:20:31 +0000
commitc26d83348b730d083f3be313556039fd31d32505 (patch)
tree622fe6c614615db49405d31cccde0afc3a5bc0f6
parent193814497b3f72fddea17d04269759f1cf64943d (diff)
downloadmpv-c26d83348b730d083f3be313556039fd31d32505.tar.bz2
mpv-c26d83348b730d083f3be313556039fd31d32505.tar.xz
wayland: shuffle around the render loop again
Take two. f4e89dd went wrong by moving vo_wayland_wait_frame before start_frame was called. Whether or not this matters depends on the compositor, but some weird things can happen. Basically, it's a scheduling issue. vo_wayland_wait_frame queues all events and sends them to the server to process (with no blocking if presentation time is available). If mpv changes state while rendering (and this function is called before every frame is drawn), then that event also gets dispatched and sent to the compositor. This, in some cases, can cause some funny behavior because the next frame gets attached to the surface while the old buffer is getting released. It's safer to call this function after the swap already happens and well before mpv calls its next draw. There's no weird scheduling of events, and the compositor log is more normal. The second part of this is to fix some stuttering issues. This is mostly just conjecture, but probably what was happening was this thing called "composition". The easiest way to see this is to play a video on the default audio sync mode (probably easiest to see on a typical 23.976 video). Have that in a window and float it over firefox (floating windows are bloat on a tiling wm anyway). Then in firefox, do some short bursts of smooth scrolling (likely uses egl). Some stutter in video rendering could be observed, particularly in panning shots. Compositors are supposed to prevent tearing so what likely was happening was that the compositor was simply holding the buffer a wee bit longer to make sure it happened in sync with the smooth scrolling. Because the mpv code waits precisely on presentation time, the loop would timeout on occasion instead of receiving the frame callback. This would then lead to a skipped frame when rendering and thus causing stuttering. The fix is simple: just only count consecutive timeouts as not receiving frame callback. If a compositor holds the mpv buffer slightly longer to avoid tearing, then we will definitely receive frame callback on the next round of the render loop. This logic also appears to be sound for plasma (funfact: Plasma always returns frame callback even when the window is hidden. Not sure what's up with that, but luckily it doesn't matter to us.), so get rid of the goofy 1/vblank_time thing and just keep it a simple > 1 check.
-rw-r--r--video/out/opengl/context_wayland.c10
-rw-r--r--video/out/vo_wlshm.c12
-rw-r--r--video/out/vulkan/context_wayland.c11
-rw-r--r--video/out/wayland_common.c34
-rw-r--r--video/out/wayland_common.h3
5 files changed, 35 insertions, 35 deletions
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 27a9aab32a..eeaeb11cc1 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -63,12 +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 = true;
-
- if (!wl->opts->disable_vsync)
- render = vo_wayland_wait_frame(wl);
-
+ bool render = wl->render || wl->opts->disable_vsync;
wl->frame_wait = true;
+
return render ? ra_gl_ctx_start_frame(sw, out_fbo) : false;
}
@@ -80,6 +77,9 @@ static void wayland_egl_swap_buffers(struct ra_swapchain *sw)
eglSwapBuffers(p->egl_display, p->egl_surface);
+ if (!wl->opts->disable_vsync)
+ vo_wayland_wait_frame(wl);
+
if (wl->presentation)
wayland_sync_swap(wl);
}
diff --git a/video/out/vo_wlshm.c b/video/out/vo_wlshm.c
index 508da4261f..69cfdd9dcc 100644
--- a/video/out/vo_wlshm.c
+++ b/video/out/vo_wlshm.c
@@ -218,13 +218,10 @@ 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 callback = true;
-
- if (!wl->opts->disable_vsync)
- callback = vo_wayland_wait_frame(wl);
-
+ bool render = wl->render || wl->opts->disable_vsync;
wl->frame_wait = true;
- if (!callback)
+
+ if (!render)
return;
buf = p->free_buffers;
@@ -277,6 +274,9 @@ static void flip_page(struct vo *vo)
mp_rect_h(wl->geometry));
wl_surface_commit(wl->surface);
+ if (!wl->opts->disable_vsync)
+ vo_wayland_wait_frame(wl);
+
if (wl->presentation)
wayland_sync_swap(wl);
}
diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c
index d36ab89ed4..d711a855c4 100644
--- a/video/out/vulkan/context_wayland.c
+++ b/video/out/vulkan/context_wayland.c
@@ -32,18 +32,19 @@ struct priv {
static bool wayland_vk_start_frame(struct ra_ctx *ctx)
{
struct vo_wayland_state *wl = ctx->vo->wl;
- bool render = true;
-
- if (!wl->opts->disable_vsync)
- render = vo_wayland_wait_frame(wl);
-
+ bool render = wl->render || wl->opts->disable_vsync;
wl->frame_wait = true;
+
return render;
}
static void wayland_vk_swap_buffers(struct ra_ctx *ctx)
{
struct vo_wayland_state *wl = ctx->vo->wl;
+
+ if (!wl->opts->disable_vsync)
+ vo_wayland_wait_frame(wl);
+
if (wl->presentation)
wayland_sync_swap(wl);
}
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 2c0569a83b..3dcc9641e8 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -1865,7 +1865,7 @@ void vo_wayland_wakeup(struct vo *vo)
(void)write(wl->wakeup_pipe[1], &(char){0}, 1);
}
-bool vo_wayland_wait_frame(struct vo_wayland_state *wl)
+void vo_wayland_wait_frame(struct vo_wayland_state *wl)
{
int64_t vblank_time = 0;
struct pollfd fds[1] = {
@@ -1917,27 +1917,25 @@ bool vo_wayland_wait_frame(struct vo_wayland_state *wl)
}
/* If the compositor does not have presentatiom time, we cannot be sure
- * that this wait is accurate. Do some crap with wl_display_roundtrip
- * and randomly assume that if timeouts > refresh rate, the window is
- * hidden. This is neccesary otherwise we may mistakeningly skip rendering.*/
- if (!wl->presentation) {
- if (wl_display_get_error(wl->display) == 0)
- wl_display_roundtrip(wl->display);
- if (wl->frame_wait) {
- if (wl->timeout_count > ((1 / (double)vblank_time) * 1e6)) {
- return false;
- } else {
- wl->timeout_count += 1;
- return true;
- }
+ * 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 = 0;
- return true;
+ wl->timeout_count += 1;
+ wl->render = true;
+ return;
}
-
}
- return !wl->frame_wait;
+ 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 948859e79a..bbdfdf82d2 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -74,6 +74,7 @@ struct vo_wayland_state {
int reduced_width;
int reduced_height;
bool frame_wait;
+ bool render;
bool state_change;
bool toplevel_configured;
bool activated;
@@ -152,7 +153,7 @@ int last_available_sync(struct vo_wayland_state *wl);
void vo_wayland_uninit(struct vo *vo);
void vo_wayland_wakeup(struct vo *vo);
void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us);
-bool vo_wayland_wait_frame(struct vo_wayland_state *wl);
+void vo_wayland_wait_frame(struct vo_wayland_state *wl);
void vo_wayland_set_opaque_region(struct vo_wayland_state *wl, int alpha);
void wayland_sync_swap(struct vo_wayland_state *wl);
void vo_wayland_sync_shift(struct vo_wayland_state *wl);