summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDudemanguy <random342@airmail.cc>2020-04-09 11:17:03 -0500
committerDudemanguy <random342@airmail.cc>2020-04-20 21:02:02 +0000
commit055a490cef384922d77367d25cecb813cafa8024 (patch)
treecdaf6146698eeda892c7f4967be89b04971ffc14
parenta09c7691d7ecaf49666d16f5276b9e7a14197c37 (diff)
downloadmpv-055a490cef384922d77367d25cecb813cafa8024.tar.bz2
mpv-055a490cef384922d77367d25cecb813cafa8024.tar.xz
wayland: use mp_time deltas for presentation time
One not-so-nice hack in the wayland code is the assumption of when a window is hidden (out of view from the compositor) and an arbitrary delay for enabling/disabling the usage of presentation time. Since you do not receive any presentation feedback when a window is hidden on wayland (a feature or misfeature depending on who you ask), the ust is updated based on the refresh_nsec statistic gathered from the previous feedback event. The flaw with this is that refresh_nsec basically just reports back the display's refresh rate (1 / refresh_rate * 10^9). It doesn't tell you how long the vsync interval really was. So as a video is left playing out of view, the wl->last_queue_display_time becomes increasingly inaccurate. This led to a vsync spike when bringing the mpv window back into sight after it was hidden for a period of time. The hack for working around this is to just wait a while before enabling presentation time again. The discrepancy between the "bogus" wl->last_queue_display_time and the actual value you get from the feedback only happens initially after a switch. If you just discard those values, you avoid the dramatic vsync spike. It turns out that there's a smarter way to do this. Just use mp_time_us deltas. The whole reason for these hacks is because wl->last_queue_display_time wasn't close enough to how long it would take for a frame to actually display if it wasn't hidden. Instead, mpv's internal timer can be used, and the difference between wayland_sync_swap calls is a close enough proxy for the vsync interval (certainly better than using the monitor's refresh rate). This avoids the entire conundrum of massive vsync spikes when bringing the player back into view, and it means we can get rid of extra crap like wl->hidden.
-rw-r--r--video/out/opengl/context_wayland.c3
-rw-r--r--video/out/vulkan/context_wayland.c3
-rw-r--r--video/out/wayland_common.c31
-rw-r--r--video/out/wayland_common.h3
4 files changed, 10 insertions, 30 deletions
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index f951b9466a..49835668f0 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -92,7 +92,6 @@ static void feedback_presented(void *data, struct wp_presentation_feedback *fbac
wl->sync[index].sbc = wl->user_sbc;
wl->sync[index].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
- wl->sync[index].refresh_usec = (uint64_t)refresh_nsec/1000;
wl->sync[index].filled = true;
}
@@ -153,7 +152,7 @@ static void wayland_egl_swap_buffers(struct ra_ctx *ctx)
static void wayland_egl_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
{
struct vo_wayland_state *wl = ctx->vo->wl;
- if (wl->presentation && !wl->hidden) {
+ if (wl->presentation) {
info->vsync_duration = wl->vsync_duration;
info->skipped_vsyncs = wl->last_skipped_vsyncs;
info->last_queue_display_time = wl->last_queue_display_time;
diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c
index 83d4617057..ffe1e0facc 100644
--- a/video/out/vulkan/context_wayland.c
+++ b/video/out/vulkan/context_wayland.c
@@ -82,7 +82,6 @@ static void feedback_presented(void *data, struct wp_presentation_feedback *fbac
wl->sync[index].sbc = wl->user_sbc;
wl->sync[index].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
- wl->sync[index].refresh_usec = (uint64_t)refresh_nsec/1000;
wl->sync[index].filled = true;
}
@@ -122,7 +121,7 @@ static void wayland_vk_swap_buffers(struct ra_ctx *ctx)
static void wayland_vk_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
{
struct vo_wayland_state *wl = ctx->vo->wl;
- if (wl->presentation && !wl->hidden) {
+ if (wl->presentation) {
info->vsync_duration = wl->vsync_duration;
info->skipped_vsyncs = wl->last_skipped_vsyncs;
info->last_queue_display_time = wl->last_queue_display_time;
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 55a4c36f1e..70e72b491c 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -1581,16 +1581,19 @@ void queue_new_sync(struct vo_wayland_state *wl)
void wayland_sync_swap(struct vo_wayland_state *wl)
{
int index = wl->sync_size - 1;
+ int64_t mp_time = mp_time_us();
wl->last_skipped_vsyncs = 0;
- // If these are the same (can happen if a frame takes too long), update
- // the ust/msc/sbc based on when the next frame is expected to arrive.
+ // If these are the same, presentation feedback has not been received.
+ // This will happen if the window is obscured/hidden in some way. Update
+ // the values based on the difference in mp_time.
if (wl->sync[index].ust == wl->last_ust && wl->last_ust) {
- wl->sync[index].ust += wl->sync[index].refresh_usec;
+ wl->sync[index].ust += mp_time - wl->sync[index].last_mp_time;
wl->sync[index].msc += 1;
wl->sync[index].sbc += 1;
}
+ wl->sync[index].last_mp_time = mp_time;
int64_t ust_passed = wl->sync[index].ust ? wl->sync[index].ust - wl->last_ust: 0;
wl->last_ust = wl->sync[index].ust;
@@ -1609,7 +1612,7 @@ void wayland_sync_swap(struct vo_wayland_state *wl)
}
uint64_t now_monotonic = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
- uint64_t ust_mp_time = mp_time_us() - (now_monotonic - wl->sync[index].ust);
+ uint64_t ust_mp_time = mp_time - (now_monotonic - wl->sync[index].ust);
wl->last_sbc_mp_time = ust_mp_time;
}
@@ -1650,26 +1653,6 @@ void vo_wayland_wait_frame(struct vo_wayland_state *wl)
wl_display_read_events(wl->display);
wl_display_roundtrip(wl->display);
}
-
- if (wl->frame_wait) {
- if (!wl->hidden) {
- wl->timeout_count += 1;
- } else {
- wl->timeout_count = 0;
- }
- } else {
- if (wl->hidden) {
- wl->timeout_count -= 1;
- } else {
- wl->timeout_count = 0;
- }
- }
-
- if (wl->timeout_count > wl->current_output->refresh_rate) {
- wl->hidden = true;
- } else if (wl->timeout_count < -1*wl->current_output->refresh_rate) {
- wl->hidden = false;
- }
}
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 0c27472e3f..4d26b0130d 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -35,7 +35,7 @@ struct vo_wayland_sync {
int64_t ust;
int64_t msc;
int64_t sbc;
- int64_t refresh_usec;
+ int64_t last_mp_time;
bool filled;
};
@@ -75,7 +75,6 @@ struct vo_wayland_state {
int reduced_height;
bool configured;
bool frame_wait;
- bool hidden;
int timeout_count;
int wakeup_pipe[2];
int pending_vo_events;