clutter/master-clock-default: Sync timelines to hardware vsync

Previously clutter timelines advanced according to `g_source_get_time`.
But that meant the spatial stepping of animations was visibly sensitive to
any irregularities in the main loop. It also represented a time older [1]
than the intended presentation time of each frame.

Now we instead use `master_clock_get_next_presentation_time`. This ensures
we get the smoothness of hardware vsync as well as being closer to the
actual presentation time.

This means, for example, backends like Xorg that move the hardware cursor
independently of repaints will have their animations more closely matching
the hardware cursor position. So the cursor appears to stick more closely
when dragging windows or on the lock screen etc.

[1] "older" = (refresh_interval - sync_delay) = ~14ms for 60Hz

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/25

https://gitlab.gnome.org/GNOME/mutter/merge_requests/724
This commit is contained in:
Daniel van Vugt 2019-08-13 18:03:26 +08:00 committed by Robert Mader
parent 2c805524b4
commit 6f094bd399

View File

@ -190,6 +190,26 @@ master_clock_get_swap_wait_time (ClutterMasterClockDefault *master_clock)
} }
} }
static int64_t
master_clock_get_next_presentation_time (ClutterMasterClockDefault *master_clock)
{
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
const GSList *stages, *l;
int64_t earliest = -1;
stages = clutter_stage_manager_peek_stages (stage_manager);
for (l = stages; l != NULL; l = l->next)
{
gint64 t = _clutter_stage_get_next_presentation_time (l->data);
if (earliest == -1 || (t != -1 && t < earliest))
earliest = t;
}
return earliest;
}
static void static void
master_clock_schedule_stage_updates (ClutterMasterClockDefault *master_clock) master_clock_schedule_stage_updates (ClutterMasterClockDefault *master_clock)
{ {
@ -466,7 +486,11 @@ clutter_clock_dispatch (GSource *source,
COGL_TRACE_BEGIN (ClutterMasterClockTick, "Master Clock (tick)"); COGL_TRACE_BEGIN (ClutterMasterClockTick, "Master Clock (tick)");
/* Get the time to use for this frame */ /* Get the time to use for this frame */
master_clock->cur_tick = g_source_get_time (source); master_clock->cur_tick = master_clock_get_next_presentation_time (master_clock);
/* On the first frame the backend might not have an answer */
if (master_clock->cur_tick <= 0)
master_clock->cur_tick = g_source_get_time (source);
#ifdef CLUTTER_ENABLE_DEBUG #ifdef CLUTTER_ENABLE_DEBUG
master_clock->remaining_budget = master_clock->frame_budget; master_clock->remaining_budget = master_clock->frame_budget;