From 394bf5ab24b8a623d242ffa10c07054752b27cc7 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 This replaces the DISPATCHED state with new sub-states that are possible with triple buffering: DISPATCHED_ONE: Double buffering DISPATCHED_ONE_AND_SCHEDULED: Scheduled switch to triple buffering DISPATCHED_ONE_AND_SCHEDULED_NOW: Scheduled switch to triple buffering DISPATCHED_ONE_AND_SCHEDULED_LATER: Scheduled switch to triple buffering DISPATCHED_TWO: Triple buffering Triple buffering is currently disabled until the test cases get updated to handle it in the next commit. Part-of: --- clutter/clutter/clutter-frame-clock.c | 256 +++++++++++++++++++++----- doc/clutter-frame-scheduling.md | 9 +- 2 files changed, 217 insertions(+), 48 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 72db41e8c..b8cf3bd2a 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -76,7 +76,11 @@ typedef enum _ClutterFrameClockState CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER, - 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_ONE_AND_SCHEDULED_LATER, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO, } ClutterFrameClockState; typedef struct _Frame @@ -112,9 +116,10 @@ struct _ClutterFrameClock int64_t next_update_time_us; - Frame frame_pool[2]; + Frame frame_pool[3]; Frame *prev_dispatch; Frame *next_presentation; + Frame *next_next_presentation; Frame *prev_presentation; gboolean is_next_presentation_time_valid; @@ -379,6 +384,11 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, ClutterFrameInfo *frame_info) { Frame *presented_frame; +#ifdef CLUTTER_ENABLE_DEBUG + const char *debug_state = + frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ? + "Triple buffering" : "Double buffering"; +#endif COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented, "Clutter::FrameClock::presented()"); @@ -389,6 +399,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, clear_frame (&frame_clock->prev_presentation); presented_frame = frame_clock->prev_presentation = g_steal_pointer (&frame_clock->next_presentation); + frame_clock->next_presentation = + g_steal_pointer (&frame_clock->next_next_presentation); presented_frame->next_presentation_time_us = frame_clock->next_presentation_time_us; @@ -478,18 +490,21 @@ 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 = presented_frame->dispatch_time_us; + int64_t flip_time_us = presented_frame->flip_time_us; dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us - - presented_frame->dispatch_time_us; + dispatch_time_us; swap_to_rendering_done_us = frame_info->gpu_rendering_duration_ns / 1000; swap_to_flip_us = - presented_frame->flip_time_us - + flip_time_us - frame_info->cpu_time_before_buffer_swap_us; CLUTTER_NOTE (FRAME_TIMINGS, - "update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", + "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", + debug_state, presented_frame->dispatch_lateness_us, dispatch_to_swap_us, swap_to_rendering_done_us, @@ -500,7 +515,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); @@ -509,7 +524,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, } else { - CLUTTER_NOTE (FRAME_TIMINGS, "update2dispatch %ld µs", + CLUTTER_NOTE (FRAME_TIMINGS, "%s: update2dispatch %ld µs", + debug_state, presented_frame->dispatch_lateness_us); } @@ -528,10 +544,26 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: 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_ONE_AND_SCHEDULED_LATER: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER; + 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; } } @@ -541,7 +573,10 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyReady, "Clutter::FrameClock::ready()"); COGL_TRACE_DESCRIBE (ClutterFrameClockNotifyReady, frame_clock->output_name); - clear_frame (&frame_clock->next_presentation); + if (frame_clock->next_next_presentation) + clear_frame (&frame_clock->next_next_presentation); + else + clear_frame (&frame_clock->next_presentation); switch (frame_clock->state) { @@ -552,10 +587,26 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: 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_ONE_AND_SCHEDULED_LATER: + 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; } } @@ -570,7 +621,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 @@ -590,7 +648,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; } @@ -608,6 +666,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 (); @@ -654,7 +713,31 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, * */ last_presentation_time_us = last_presentation->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: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: + 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: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_LATER: + 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. @@ -721,7 +804,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, } if (last_presentation->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 @@ -858,7 +941,21 @@ 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_AND_SCHEDULED_LATER: + frame_clock->pending_reschedule = 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; } @@ -877,6 +974,13 @@ clutter_frame_clock_uninhibit (ClutterFrameClock *frame_clock) maybe_reschedule_update (frame_clock); } +static gboolean +want_triple_buffering (ClutterFrameClock *frame_clock) +{ + /* disabled until test cases are updated */ + return FALSE; +} + void clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) { @@ -895,10 +999,25 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_IDLE: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: + 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_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_LATER: + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + if (want_triple_buffering (frame_clock)) + { + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; + break; + } + G_GNUC_FALLTHROUGH; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; frame_clock->pending_reschedule_now = TRUE; return; @@ -927,7 +1046,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 @@ -949,12 +1067,24 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; return; case CLUTTER_FRAME_CLOCK_STATE_IDLE: - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: + 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_SCHEDULED_LATER: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_LATER: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + if (want_triple_buffering (frame_clock)) + { + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + break; + } + G_GNUC_FALLTHROUGH; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; return; } @@ -983,7 +1113,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; } static void @@ -996,6 +1125,7 @@ clutter_frame_clock_schedule_update_later (ClutterFrameClock *frame_clock, int64_t ready_time_us = 0, extrapolated_presentation_time_us; int64_t max_render_time_us; int64_t cycles; + ClutterFrameClockState next_state = frame_clock->state; if (frame_clock->inhibit_count > 0) { @@ -1008,11 +1138,24 @@ clutter_frame_clock_schedule_update_later (ClutterFrameClock *frame_clock, case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: + next_state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + 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_AND_SCHEDULED_LATER: + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + if (want_triple_buffering (frame_clock)) + { + next_state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_LATER; + break; + } + G_GNUC_FALLTHROUGH; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; frame_clock->pending_reschedule_now = TRUE; return; @@ -1063,7 +1206,7 @@ clutter_frame_clock_schedule_update_later (ClutterFrameClock *frame_clock, g_source_set_ready_time (frame_clock->source, ready_time_us); frame_clock->pending_reschedule = TRUE; - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER; + frame_clock->state = next_state; } static int @@ -1105,6 +1248,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: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: @@ -1116,7 +1261,15 @@ 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: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_LATER: + 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; } @@ -1162,9 +1315,16 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, this_dispatch = frame_clock->prev_dispatch = clutter_frame_clock_new_frame (frame_clock); - /* This will need changing for triple buffering */ - g_warn_if_fail (frame_clock->next_presentation == NULL); - frame_clock->next_presentation = ref_frame (this_dispatch); + if (frame_clock->next_presentation == NULL) + { + frame_clock->next_presentation = ref_frame (this_dispatch); + } + else + { + g_warn_if_fail (frame_clock->next_next_presentation == NULL); + frame_clock->next_next_presentation = + ref_frame (this_dispatch); + } ideal_dispatch_time_us = frame_clock->next_update_time_us; @@ -1198,7 +1358,25 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, this_dispatch->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: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: + 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: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_LATER: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO; + break; + } frame_count = frame_clock->frame_count++; @@ -1229,27 +1407,13 @@ 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: - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: - break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: - switch (result) - { - case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: - break; - case CLUTTER_FRAME_RESULT_IDLE: - /* The frame was aborted; nothing to paint/present */ - clutter_frame_clock_notify_ready (frame_clock); - break; - } + case CLUTTER_FRAME_RESULT_IDLE: + /* The frame was aborted; nothing to paint/present */ + clutter_frame_clock_notify_ready (frame_clock); break; } diff --git a/doc/clutter-frame-scheduling.md b/doc/clutter-frame-scheduling.md index d4f452cc1..64633b917 100644 --- a/doc/clutter-frame-scheduling.md +++ b/doc/clutter-frame-scheduling.md @@ -8,6 +8,11 @@ stateDiagram IDLE --> SCHEDULED* : next frame scheduled SCHEDULED* --> IDLE : frame clock inhibited or mode changed SCHEDULED* --> SCHEDULED* : schedule upgraded to SCHEDULED_NOW - SCHEDULED* --> DISPATCHED : begin an update - DISPATCHED --> IDLE : frame was either presented or aborted with nothing to draw + SCHEDULED* --> DISPATCHED_ONE : begin an update + DISPATCHED_ONE --> IDLE : frame was either presented or aborted with nothing to draw + DISPATCHED_ONE --> DISPATCHED_ONE_AND_SCHEDULED* : entering triple buffering + DISPATCHED_ONE_AND_SCHEDULED* --> SCHEDULED* : frame was either presented or aborted with nothing to draw + DISPATCHED_ONE_AND_SCHEDULED* --> DISPATCHED_ONE : frame clock inhibited or mode changed + DISPATCHED_ONE_AND_SCHEDULED* --> DISPATCHED_TWO : start a second concurrent frame + DISPATCHED_TWO --> DISPATCHED_ONE : leaving triple buffering ```