From 0b2e48db6f9948ca87daab297a2fd8af928670e8 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Thu, 10 Sep 2020 16:34:53 +0800 Subject: [PATCH] clutter/frame-clock: Add triple buffering support Signed-off-by: Mingi Sung --- clutter/clutter/clutter-frame-clock.c | 198 ++++++++++++++++++++++---- 1 file changed, 168 insertions(+), 30 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index a9178ff74..7c974abe0 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -70,7 +70,10 @@ typedef enum _ClutterFrameClockState CLUTTER_FRAME_CLOCK_STATE_IDLE, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW, - CLUTTER_FRAME_CLOCK_STATE_DISPATCHED, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO, } ClutterFrameClockState; struct _ClutterFrameClock @@ -91,6 +94,7 @@ struct _ClutterFrameClock ClutterFrameClockMode mode; int64_t last_dispatch_time_us; + int64_t prev_last_dispatch_time_us; int64_t last_dispatch_lateness_us; int64_t last_presentation_time_us; int64_t next_update_time_us; @@ -112,6 +116,7 @@ struct _ClutterFrameClock int64_t vblank_duration_us; /* Last KMS buffer submission time. */ int64_t last_flip_time_us; + int64_t prev_last_flip_time_us; /* Last time we promoted short-term maximum to long-term one */ int64_t longterm_promotion_us; @@ -371,14 +376,35 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, frame_info->has_valid_gpu_rendering_duration) { int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us; + int64_t dispatch_time_us = 0, flip_time_us = 0; + + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + g_warn_if_reached (); + G_GNUC_FALLTHROUGH; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + dispatch_time_us = frame_clock->last_dispatch_time_us; + flip_time_us = frame_clock->last_flip_time_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + dispatch_time_us = frame_clock->prev_last_dispatch_time_us; + flip_time_us = frame_clock->prev_last_flip_time_us; + break; + } dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us - - frame_clock->last_dispatch_time_us; + dispatch_time_us; swap_to_rendering_done_us = frame_info->gpu_rendering_duration_ns / 1000; swap_to_flip_us = - frame_clock->last_flip_time_us - + flip_time_us - frame_info->cpu_time_before_buffer_swap_us; CLUTTER_NOTE (FRAME_TIMINGS, @@ -393,7 +419,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, MAX (swap_to_rendering_done_us, swap_to_flip_us) + frame_clock->deadline_evasion_us, frame_clock->shortterm_max_update_duration_us, - frame_clock->refresh_interval_us); + 2 * frame_clock->refresh_interval_us); maybe_update_longterm_max_duration_us (frame_clock, frame_info); @@ -420,10 +446,22 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: g_warn_if_reached (); break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } } @@ -441,10 +479,22 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: g_warn_if_reached (); break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } } @@ -459,7 +509,14 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) if (!frame_clock->ever_got_measurements || G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME)) - return (int64_t) (refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION); + { + int64_t ret = (int64_t) (refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION); + + if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE) + ret += refresh_interval_us; + + return ret; + } /* Max render time shows how early the frame clock needs to be dispatched * to make it to the predicted next presentation time. It is an estimate of @@ -479,7 +536,7 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) frame_clock->vblank_duration_us + clutter_max_render_time_constant_us; - max_render_time_us = CLAMP (max_render_time_us, 0, refresh_interval_us); + max_render_time_us = CLAMP (max_render_time_us, 0, 2 * refresh_interval_us); return max_render_time_us; } @@ -496,6 +553,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, int64_t min_render_time_allowed_us; int64_t max_render_time_allowed_us; int64_t next_presentation_time_us; + int64_t next_smooth_presentation_time_us = 0; int64_t next_update_time_us; now_us = g_get_monotonic_time (); @@ -540,7 +598,29 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, * */ last_presentation_time_us = frame_clock->last_presentation_time_us; - next_presentation_time_us = last_presentation_time_us + refresh_interval_us; + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + next_smooth_presentation_time_us = last_presentation_time_us + + refresh_interval_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + next_smooth_presentation_time_us = last_presentation_time_us + + 2 * refresh_interval_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + g_warn_if_reached (); /* quad buffering would be a bug */ + next_smooth_presentation_time_us = last_presentation_time_us + + 3 * refresh_interval_us; + break; + } + + next_presentation_time_us = next_smooth_presentation_time_us; /* * However, the last presentation could have happened more than a frame ago. @@ -607,7 +687,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, } if (frame_clock->last_presentation_flags & CLUTTER_FRAME_INFO_FLAG_VSYNC && - next_presentation_time_us != last_presentation_time_us + refresh_interval_us) + next_presentation_time_us != next_smooth_presentation_time_us) { /* There was an idle period since the last presentation, so there seems * be no constantly updating actor. In this case it's best to start @@ -739,7 +819,17 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) frame_clock->pending_reschedule_now = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->pending_reschedule = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: break; } @@ -775,10 +865,17 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; frame_clock->pending_reschedule_now = TRUE; return; @@ -807,7 +904,6 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) frame_clock->next_update_time_us = next_update_time_us; g_source_set_ready_time (frame_clock->source, next_update_time_us); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; } void @@ -829,11 +925,17 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; return; case CLUTTER_FRAME_CLOCK_STATE_IDLE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; return; } @@ -862,7 +964,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->next_update_time_us = next_update_time_us; g_source_set_ready_time (frame_clock->source, next_update_time_us); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; } void @@ -878,6 +979,8 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, { case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: frame_clock->pending_reschedule = TRUE; @@ -888,7 +991,14 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, frame_clock->pending_reschedule_now = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->pending_reschedule = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; break; } @@ -945,10 +1055,27 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, } #endif + frame_clock->prev_last_dispatch_time_us = frame_clock->last_dispatch_time_us; frame_clock->last_dispatch_time_us = time_us; g_source_set_ready_time (frame_clock->source, -1); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED; + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + g_warn_if_reached (); + return; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO; + break; + } frame_count = frame_clock->frame_count++; @@ -979,26 +1106,36 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, result = iface->frame (frame_clock, frame, frame_clock->listener.user_data); COGL_TRACE_END (ClutterFrameClockFrame); - switch (frame_clock->state) + switch (result) { - case CLUTTER_FRAME_CLOCK_STATE_INIT: - g_warn_if_reached (); + case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: break; - case CLUTTER_FRAME_CLOCK_STATE_IDLE: - /* Presentation completed synchronously in the above listener */ - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: - break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: - switch (result) + case CLUTTER_FRAME_RESULT_IDLE: + /* The frame was aborted; nothing to paint/present */ + switch (frame_clock->state) { - case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + g_warn_if_reached (); break; - case CLUTTER_FRAME_RESULT_IDLE: - /* The frame was aborted; nothing to paint/present */ + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } break; } @@ -1034,6 +1171,7 @@ void clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, int64_t flip_time_us) { + frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us; frame_clock->last_flip_time_us = flip_time_us; }