From 2c805524b49150793fe97908cb309d9b8c52f8ad Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Tue, 13 Aug 2019 17:33:19 +0800 Subject: [PATCH 1/2] clutter/stage: Add API to get_next_presentation_time https://gitlab.gnome.org/GNOME/mutter/merge_requests/724 --- clutter/clutter/clutter-stage-private.h | 1 + clutter/clutter/clutter-stage-window.c | 16 ++++++++++++ clutter/clutter/clutter-stage-window.h | 4 +++ clutter/clutter/clutter-stage.c | 15 +++++++++++ clutter/clutter/cogl/clutter-stage-cogl.c | 32 ++++++++++++++++++++++- clutter/clutter/cogl/clutter-stage-cogl.h | 1 + 6 files changed, 68 insertions(+), 1 deletion(-) diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index c8c1ef34ac..a8abd05648 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -78,6 +78,7 @@ void _clutter_stage_schedule_update (ClutterStage *stage); gint64 _clutter_stage_get_update_time (ClutterStage *stage); void _clutter_stage_clear_update_time (ClutterStage *stage); gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); +int64_t _clutter_stage_get_next_presentation_time (ClutterStage *stage); void clutter_stage_log_pick (ClutterStage *stage, const graphene_point_t *vertices, diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c index 3c80124a94..a3782a175c 100644 --- a/clutter/clutter/clutter-stage-window.c +++ b/clutter/clutter/clutter-stage-window.c @@ -178,6 +178,22 @@ _clutter_stage_window_clear_update_time (ClutterStageWindow *window) iface->clear_update_time (window); } +int64_t +_clutter_stage_window_get_next_presentation_time (ClutterStageWindow *window) +{ + ClutterStageWindowInterface *iface; + + g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), 0); + + iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); + + /* If not implemented then just revert to the old behaviour... */ + if (iface->get_next_presentation_time == NULL) + return _clutter_stage_window_get_update_time (window); + + return iface->get_next_presentation_time (window); +} + void _clutter_stage_window_set_accept_focus (ClutterStageWindow *window, gboolean accept_focus) diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h index eb529416ed..78d15b7438 100644 --- a/clutter/clutter/clutter-stage-window.h +++ b/clutter/clutter/clutter-stage-window.h @@ -61,6 +61,8 @@ struct _ClutterStageWindowInterface GList *(* get_views) (ClutterStageWindow *stage_window); int64_t (* get_frame_counter) (ClutterStageWindow *stage_window); void (* finish_frame) (ClutterStageWindow *stage_window); + + int64_t (* get_next_presentation_time) (ClutterStageWindow *stage_window); }; ClutterActor * _clutter_stage_window_get_wrapper (ClutterStageWindow *window); @@ -101,6 +103,8 @@ void _clutter_stage_window_finish_frame (ClutterStageWin int64_t _clutter_stage_window_get_frame_counter (ClutterStageWindow *window); +int64_t _clutter_stage_window_get_next_presentation_time (ClutterStageWindow *window); + G_END_DECLS #endif /* __CLUTTER_STAGE_WINDOW_H__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 07ba9dfaef..fb73a027d5 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -3751,6 +3751,21 @@ _clutter_stage_clear_update_time (ClutterStage *stage) _clutter_stage_window_clear_update_time (stage_window); } +int64_t +_clutter_stage_get_next_presentation_time (ClutterStage *stage) +{ + ClutterStageWindow *stage_window; + + if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) + return 0; + + stage_window = _clutter_stage_get_window (stage); + if (stage_window == NULL) + return 0; + + return _clutter_stage_window_get_next_presentation_time (stage_window); +} + ClutterPaintVolume * _clutter_stage_paint_volume_stack_allocate (ClutterStage *stage) { diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c index d48e976417..28b33455f2 100644 --- a/clutter/clutter/cogl/clutter-stage-cogl.c +++ b/clutter/clutter/cogl/clutter-stage-cogl.c @@ -235,7 +235,12 @@ clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window, stage_cogl->update_time = next_presentation_time - max_render_time_allowed; if (stage_cogl->update_time == stage_cogl->last_update_time) - stage_cogl->update_time = stage_cogl->last_update_time + refresh_interval; + { + stage_cogl->update_time += refresh_interval; + next_presentation_time += refresh_interval; + } + + stage_cogl->next_presentation_time = next_presentation_time; } static gint64 @@ -256,6 +261,29 @@ clutter_stage_cogl_clear_update_time (ClutterStageWindow *stage_window) stage_cogl->last_update_time = stage_cogl->update_time; stage_cogl->update_time = -1; + stage_cogl->next_presentation_time = -1; +} + +static int64_t +clutter_stage_cogl_get_next_presentation_time (ClutterStageWindow *stage_window) +{ + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); + int64_t now = g_get_monotonic_time (); + + if (stage_cogl->next_presentation_time > 0 && + stage_cogl->next_presentation_time <= now) + { + CLUTTER_NOTE (BACKEND, + "Missed some frames. Something blocked for over " + "%" G_GINT64_FORMAT "ms.", + (now - stage_cogl->next_presentation_time) / 1000); + + stage_cogl->update_time = -1; + clutter_stage_cogl_schedule_update (stage_window, + stage_cogl->last_sync_delay); + } + + return stage_cogl->next_presentation_time; } static ClutterActor * @@ -1008,6 +1036,7 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) iface->schedule_update = clutter_stage_cogl_schedule_update; iface->get_update_time = clutter_stage_cogl_get_update_time; iface->clear_update_time = clutter_stage_cogl_clear_update_time; + iface->get_next_presentation_time = clutter_stage_cogl_get_next_presentation_time; iface->redraw = clutter_stage_cogl_redraw; } @@ -1053,6 +1082,7 @@ _clutter_stage_cogl_init (ClutterStageCogl *stage) stage->refresh_rate = 0.0; stage->update_time = -1; + stage->next_presentation_time = -1; } static void diff --git a/clutter/clutter/cogl/clutter-stage-cogl.h b/clutter/clutter/cogl/clutter-stage-cogl.h index 1eaa02e8fe..634f856d4b 100644 --- a/clutter/clutter/cogl/clutter-stage-cogl.h +++ b/clutter/clutter/cogl/clutter-stage-cogl.h @@ -48,6 +48,7 @@ struct _ClutterStageCogl gint64 last_presentation_time; gint64 update_time; int64_t last_update_time; + int64_t next_presentation_time; /* We only enable clipped redraws after 2 frames, since we've seen * a lot of drivers can struggle to get going and may output some -- GitLab From 6f094bd399f4197e7036d8306337b4b0ee72ca65 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Tue, 13 Aug 2019 18:03:26 +0800 Subject: [PATCH 2/2] 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 --- .../clutter/clutter-master-clock-default.c | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/clutter/clutter/clutter-master-clock-default.c b/clutter/clutter/clutter-master-clock-default.c index 8d50ab8f3a..ac55f1b424 100644 --- a/clutter/clutter/clutter-master-clock-default.c +++ b/clutter/clutter/clutter-master-clock-default.c @@ -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 master_clock_schedule_stage_updates (ClutterMasterClockDefault *master_clock) { @@ -466,7 +486,11 @@ clutter_clock_dispatch (GSource *source, COGL_TRACE_BEGIN (ClutterMasterClockTick, "Master Clock (tick)"); /* 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 master_clock->remaining_budget = master_clock->frame_budget; -- GitLab