vo: smooth out reported vsync time

Return the estimated/ideal flip time to the timing logic (meaning
vo_get_delay() returns a smoothed out time). In addition to this add
some lame but working drift compensation. (Useful especially if the
display FPS is wrong by a factor such as 1.001.)

Also remove some older leftovers. The vsync_interval_approx and
last_flip fields are redundant or unneeded.
This commit is contained in:
wm4 2015-11-25 22:11:58 +01:00
parent 44376d2d9b
commit 12eb8b2de8
1 changed files with 23 additions and 18 deletions

View File

@ -144,6 +144,7 @@ struct vo_internal {
int num_vsync_samples;
int64_t prev_vsync;
int64_t base_vsync;
int drop_point;
double estimated_vsync_interval;
double estimated_vsync_jitter;
bool expecting_vsync;
@ -165,8 +166,6 @@ struct vo_internal {
double display_fps;
// --- The following fields can be accessed from the VO thread only
int64_t vsync_interval_approx;
int64_t last_flip;
char *window_title;
};
@ -324,6 +323,7 @@ static void reset_vsync_timings(struct vo *vo)
{
struct vo_internal *in = vo->in;
in->num_vsync_samples = 0;
in->drop_point = 0;
in->prev_vsync = 0;
in->estimated_vsync_interval = 0;
in->estimated_vsync_jitter = -1;
@ -342,12 +342,17 @@ static void update_vsync_timing_after_swap(struct vo *vo)
}
int64_t now = mp_time_us();
if (in->num_vsync_samples >= 200)
int max_samples = 200;
if (in->num_vsync_samples >= max_samples)
in->num_vsync_samples -= 1;
MP_TARRAY_INSERT_AT(in, in->vsync_samples, in->num_vsync_samples, 0,
now - in->prev_vsync);
if (!in->base_vsync)
in->drop_point = MPMIN(in->drop_point + 1, in->num_vsync_samples);
if (in->base_vsync) {
in->base_vsync += in->vsync_interval;
} else {
in->base_vsync = now;
}
in->prev_vsync = now;
double avg = 0, jitter = 0;
@ -361,6 +366,7 @@ static void update_vsync_timing_after_swap(struct vo *vo)
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);
if (llabs(in->base_vsync - now) > in->vsync_interval * 2 / 3) {
// Assume a drop. An underflow can technically speaking not be a drop
@ -368,10 +374,19 @@ static void update_vsync_timing_after_swap(struct vo *vo)
// to treat it differently.
in->base_vsync = now;
in->delayed_count += 1;
in->drop_point = 0;
MP_STATS(vo, "vo-delayed");
}
in->base_vsync += in->vsync_interval;
// Smooth out drift.
int64_t t_r = now, t_e = in->base_vsync, diff = 0;
for (int n = 0; n < in->drop_point - 1; n++) {
diff += t_r - t_e;
t_r -= in->vsync_samples[n];
t_e -= in->vsync_interval;
}
if (in->drop_point > 10)
in->base_vsync += diff * 0.1 / in->num_vsync_samples;
}
// to be called from VO thread only
@ -700,7 +715,7 @@ static bool render_frame(struct vo *vo)
in->dropped_frame &= (vo->global->opts->frame_dropping & 1);
// Even if we're hopelessly behind, rather degrade to 10 FPS playback,
// instead of just freezing the display forever.
in->dropped_frame &= mp_time_us() - in->last_flip < 100 * 1000;
in->dropped_frame &= mp_time_us() - in->prev_vsync < 100 * 1000;
in->dropped_frame &= in->hasframe_rendered;
// Setup parameters for the next time this frame is drawn. ("frame" is the
@ -738,10 +753,6 @@ static bool render_frame(struct vo *vo)
vo->driver->flip_page(vo);
int64_t prev_flip = in->last_flip;
in->last_flip = mp_time_us();
in->vsync_interval_approx = in->last_flip - prev_flip;
MP_STATS(vo, "end video");
MP_STATS(vo, "video_end");
@ -749,10 +760,6 @@ static bool render_frame(struct vo *vo)
in->dropped_frame = prev_drop_count < vo->in->drop_count;
in->rendering = false;
double diff = (in->vsync_interval - in->vsync_interval_approx) / 1e6;
if (fabs(diff) < 0.150)
MP_STATS(vo, "value %f vsync-diff", diff);
update_vsync_timing_after_swap(vo);
}
@ -877,7 +884,6 @@ void vo_set_paused(struct vo *vo, bool paused)
in->paused = paused;
if (in->paused && in->dropped_frame)
in->request_redraw = true;
in->last_flip = 0;
reset_vsync_timings(vo);
}
pthread_mutex_unlock(&in->lock);
@ -925,7 +931,6 @@ void vo_seek_reset(struct vo *vo)
struct vo_internal *in = vo->in;
pthread_mutex_lock(&in->lock);
forget_frames(vo);
in->last_flip = 0;
reset_vsync_timings(vo);
in->send_reset = true;
wakeup_locked(vo);
@ -1066,8 +1071,8 @@ double vo_get_delay(struct vo *vo)
pthread_mutex_lock(&in->lock);
assert (!in->frame_queued);
int64_t res = 0;
if (in->last_flip && in->vsync_interval > 1 && in->current_frame) {
res = in->last_flip;
if (in->base_vsync && in->vsync_interval > 1 && in->current_frame) {
res = in->base_vsync;
int extra = !!in->rendering;
res += (in->current_frame->num_vsyncs + extra) * in->vsync_interval;
if (!in->current_frame->display_synced)