diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index c10ea52db..c3b75204f 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -52,6 +52,11 @@ typedef struct _ClutterFrameListener gpointer user_data; } ClutterFrameListener; +typedef struct _DeferredTime +{ + int64_t target_time_us; +} DeferredTime; + typedef struct _ClutterClockSource { GSource source; @@ -70,6 +75,7 @@ typedef enum _ClutterFrameClockState CLUTTER_FRAME_CLOCK_STATE_IDLE, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW, + CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER, CLUTTER_FRAME_CLOCK_STATE_DISPATCHING, CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED, } ClutterFrameClockState; @@ -145,11 +151,17 @@ struct _ClutterFrameClock int64_t deadline_evasion_us; char *output_name; + + GQueue *deferred_times; }; G_DEFINE_TYPE (ClutterFrameClock, clutter_frame_clock, G_TYPE_OBJECT) +static void +clutter_frame_clock_schedule_update_later (ClutterFrameClock *frame_clock, + int64_t target_us); + float clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock) { @@ -273,9 +285,35 @@ advance_timelines (ClutterFrameClock *frame_clock, g_list_free_full (timelines, g_object_unref); } +static gboolean +clean_deferred_times (ClutterFrameClock *frame_clock) +{ + DeferredTime *head; + gboolean cleaned_times = FALSE; + int64_t current_time_us; + + if (frame_clock->is_next_presentation_time_valid) + current_time_us = frame_clock->next_presentation_time_us; + else + current_time_us = g_get_monotonic_time (); + + while ((head = g_queue_peek_head (frame_clock->deferred_times))) + { + if (current_time_us < head->target_time_us) + break; + + g_free (g_queue_pop_head (frame_clock->deferred_times)); + cleaned_times = TRUE; + } + return cleaned_times; +} + static void maybe_reschedule_update (ClutterFrameClock *frame_clock) { + DeferredTime *head; + gboolean cleaned; + if (frame_clock->pending_reschedule || frame_clock->timelines) { @@ -290,6 +328,20 @@ maybe_reschedule_update (ClutterFrameClock *frame_clock) { clutter_frame_clock_schedule_update (frame_clock); } + return; + } + + cleaned = clean_deferred_times (frame_clock); + head = g_queue_peek_head (frame_clock->deferred_times); + + if (head) + { + clutter_frame_clock_schedule_update_later (frame_clock, + head->target_time_us); + } + else if (cleaned) + { + clutter_frame_clock_schedule_update (frame_clock); } } @@ -474,6 +526,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, 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: g_warn_if_reached (); break; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: @@ -498,6 +551,7 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) 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: g_warn_if_reached (); break; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: @@ -798,6 +852,7 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_IDLE: break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: frame_clock->pending_reschedule = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; @@ -843,6 +898,7 @@ 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: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: return; @@ -898,6 +954,7 @@ 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: break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: @@ -935,6 +992,113 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; } +static void +clutter_frame_clock_schedule_update_later (ClutterFrameClock *frame_clock, + int64_t target_us) +{ + int64_t next_update_time_us = -1; + int64_t next_presentation_time_us; + int64_t next_frame_deadline_us; + int64_t ready_time_us = 0, extrapolated_presentation_time_us; + int64_t max_render_time_us; + int64_t cycles; + + if (frame_clock->inhibit_count > 0) + { + frame_clock->pending_reschedule = TRUE; + return; + } + + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: + break; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + return; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: + case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + return; + } + + switch (frame_clock->mode) + { + case CLUTTER_FRAME_CLOCK_MODE_FIXED: + calculate_next_update_time_us (frame_clock, + &next_update_time_us, + &next_presentation_time_us, + &next_frame_deadline_us); + break; + case CLUTTER_FRAME_CLOCK_MODE_VARIABLE: + calculate_next_variable_update_time_us (frame_clock, + &next_update_time_us, + &next_presentation_time_us, + &next_frame_deadline_us); + break; + } + + g_warn_if_fail (next_presentation_time_us != -1); + + if (next_presentation_time_us >= target_us) + { + clutter_frame_clock_schedule_update (frame_clock); + return; + } + + switch (frame_clock->mode) + { + case CLUTTER_FRAME_CLOCK_MODE_FIXED: + cycles = + (target_us - next_presentation_time_us + + frame_clock->refresh_interval_us - 1) / + frame_clock->refresh_interval_us; + extrapolated_presentation_time_us = + next_presentation_time_us + frame_clock->refresh_interval_us * cycles; + max_render_time_us = next_presentation_time_us - next_frame_deadline_us; + ready_time_us = extrapolated_presentation_time_us - max_render_time_us; + break; + case CLUTTER_FRAME_CLOCK_MODE_VARIABLE: + max_render_time_us = + clutter_frame_clock_compute_max_render_time_us (frame_clock); + ready_time_us = target_us - max_render_time_us; + break; + } + + 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; +} + +static int +compare_times (const DeferredTime *a, + const DeferredTime *b, + void *data) +{ + if (a->target_time_us > b->target_time_us) + return 1; + + if (a->target_time_us < b->target_time_us) + return -1; + + return 0; +} + +void +clutter_frame_clock_add_future_time (ClutterFrameClock *frame_clock, + int64_t when_us) +{ + DeferredTime *time = g_new (DeferredTime, 1); + time->target_time_us = when_us; + g_queue_insert_sorted (frame_clock->deferred_times, time, + (GCompareDataFunc)compare_times, NULL); + + maybe_reschedule_update (frame_clock); +} + void clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, ClutterFrameClockMode mode) @@ -950,6 +1114,7 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, case CLUTTER_FRAME_CLOCK_STATE_IDLE: break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER: frame_clock->pending_reschedule = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; @@ -1081,6 +1246,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, 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: break; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: switch (result) @@ -1292,6 +1458,8 @@ clutter_frame_clock_new (float refresh_rate, frame_clock->output_name = g_strdup (output_name); + frame_clock->deferred_times = g_queue_new (); + return frame_clock; } @@ -1318,6 +1486,10 @@ clutter_frame_clock_dispose (GObject *object) g_clear_pointer (&frame_clock->output_name, g_free); + if (frame_clock->deferred_times) + g_queue_free_full (g_steal_pointer (&frame_clock->deferred_times), g_free); + frame_clock->deferred_times = NULL; + G_OBJECT_CLASS (clutter_frame_clock_parent_class)->dispose (object); } diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h index 6a940f48b..829a65dfd 100644 --- a/clutter/clutter/clutter-frame-clock.h +++ b/clutter/clutter/clutter-frame-clock.h @@ -87,6 +87,10 @@ void clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock); CLUTTER_EXPORT void clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock); +CLUTTER_EXPORT +void clutter_frame_clock_add_future_time (ClutterFrameClock *frame_clock, + int64_t when_us); + CLUTTER_EXPORT void clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock);