From 41f2c653e27a0e4659458540f1bcc7786ec3339b Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 27 Nov 2015 14:36:13 +0100 Subject: [PATCH] vo: disregard system-reported display FPS if it's too imprecise If the system-reported display FPS (returned by the VO backends, or forced with --display-fps) is too imprecise (deviating frame duration by more than 1%). This works if the display FPS is off by almost 1 (typical for old/bad/broken OS APIs). Actually it even works if the FPs is completely wrong. Is it a good idea? Probably not. It might be better to only output a warning message. But unless there are reports about it going terribly wrong, I'll go with this for now. --- video/out/vo.c | 55 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/video/out/vo.c b/video/out/vo.c index 350e5d2583..808f543f1f 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -138,8 +138,9 @@ struct vo_internal { int queued_events; // event mask for the user int internal_events; // event mask for us - int64_t vsync_interval; + int64_t nominal_vsync_interval; + int64_t vsync_interval; int64_t *vsync_samples; int num_vsync_samples; int64_t prev_vsync; @@ -331,6 +332,17 @@ static void reset_vsync_timings(struct vo *vo) in->expecting_vsync = false; } +static double vsync_stddef(struct vo *vo, int64_t ref_vsync) +{ + struct vo_internal *in = vo->in; + double jitter = 0; + for (int n = 0; n < in->num_vsync_samples; n++) { + double diff = in->vsync_samples[n] - ref_vsync; + jitter += diff * diff; + } + return sqrt(jitter / in->num_vsync_samples); +} + // Always called locked. static void update_vsync_timing_after_swap(struct vo *vo) { @@ -355,15 +367,32 @@ static void update_vsync_timing_after_swap(struct vo *vo) } in->prev_vsync = now; - double avg = 0, jitter = 0; - for (int n = 0; n < in->num_vsync_samples; n++) { - avg += in->vsync_samples[n] / 1e6; - double diff = in->vsync_samples[n] / (double)in->vsync_interval - 1.0; - jitter += diff * diff; + double avg = 0; + for (int n = 0; n < in->num_vsync_samples; n++) + avg += in->vsync_samples[n]; + in->estimated_vsync_interval = avg / in->num_vsync_samples; + in->estimated_vsync_jitter = + vsync_stddef(vo, in->vsync_interval) / in->vsync_interval; + + // Switch to assumed display FPS if it seems "better". (Note that small + // differences are handled as drift instead.) + if (in->num_vsync_samples == max_samples && + fabs((in->nominal_vsync_interval - in->estimated_vsync_interval)) + >= 0.01 * in->nominal_vsync_interval && + in->estimated_vsync_interval <= 1e6 / 20.0 && + in->estimated_vsync_interval >= 1e6 / 99.0) + { + double mjitter = vsync_stddef(vo, in->estimated_vsync_interval); + double njitter = vsync_stddef(vo, in->nominal_vsync_interval); + if (mjitter * 1.01 < njitter) { + if (in->vsync_interval == in->nominal_vsync_interval) { + MP_WARN(vo, "Reported display FPS seems incorrect.\n" + "Assuming a value closer to %.3f Hz.\n", + 1e6 / in->estimated_vsync_interval); + } + in->vsync_interval = in->estimated_vsync_interval; + } } - avg /= in->num_vsync_samples; - in->estimated_vsync_interval = avg; - in->estimated_vsync_jitter = sqrt(jitter / in->num_vsync_samples); MP_STATS(vo, "value %f jitter", in->estimated_vsync_jitter); MP_STATS(vo, "value %f vsync-diff", in->vsync_samples[0] / 1e6); @@ -422,6 +451,9 @@ static void update_display_fps(struct vo *vo) in->queued_events |= VO_EVENT_WIN_STATE; mp_input_wakeup(vo->input_ctx); } + + in->nominal_vsync_interval = in->display_fps > 0 ? 1e6 / in->display_fps : 0; + in->vsync_interval = MPMAX(in->nominal_vsync_interval, 1); } pthread_mutex_unlock(&in->lock); } @@ -682,9 +714,6 @@ static bool render_frame(struct vo *vo) pthread_mutex_lock(&in->lock); - vo->in->vsync_interval = in->display_fps > 0 ? 1e6 / in->display_fps : 0; - vo->in->vsync_interval = MPMAX(vo->in->vsync_interval, 1); - if (in->frame_queued) { talloc_free(in->current_frame); in->current_frame = in->frame_queued; @@ -1051,7 +1080,7 @@ double vo_get_estimated_vsync_interval(struct vo *vo) { struct vo_internal *in = vo->in; pthread_mutex_lock(&in->lock); - double res = in->estimated_vsync_interval; + double res = in->estimated_vsync_interval / 1e6; pthread_mutex_unlock(&in->lock); return res; }