diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index d0864ed8f..302ca26eb 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -37,6 +37,8 @@ static guint signals[N_SIGNALS]; #define SYNC_DELAY_FALLBACK_FRACTION 0.875 +#define MINIMUM_REFRESH_RATE 30.f + typedef struct _ClutterFrameListener { const ClutterFrameListenerIface *iface; @@ -66,6 +68,8 @@ struct _ClutterFrameClock float refresh_rate; int64_t refresh_interval_us; + int64_t minimum_refresh_interval_us; + ClutterFrameListener listener; GSource *source; @@ -73,6 +77,8 @@ struct _ClutterFrameClock int64_t frame_count; ClutterFrameClockState state; + ClutterFrameClockMode mode; + int64_t last_dispatch_time_us; int64_t last_dispatch_lateness_us; int64_t last_presentation_time_us; @@ -607,6 +613,92 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, *out_next_frame_deadline_us = next_presentation_time_us - min_render_time_allowed_us; } +static void +calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock, + int64_t *out_next_update_time_us, + int64_t *out_next_presentation_time_us, + int64_t *out_next_frame_deadline_us) +{ + int64_t last_presentation_time_us; + int64_t now_us; + int64_t refresh_interval_us; + int64_t max_render_time_allowed_us; + int64_t next_presentation_time_us; + int64_t next_update_time_us; + int64_t next_frame_deadline_us; + + now_us = g_get_monotonic_time (); + + refresh_interval_us = frame_clock->refresh_interval_us; + + if (frame_clock->last_presentation_time_us == 0) + { + *out_next_update_time_us = + frame_clock->last_dispatch_time_us ? + ((frame_clock->last_dispatch_time_us - + frame_clock->last_dispatch_lateness_us) + refresh_interval_us) : + now_us; + + *out_next_presentation_time_us = 0; + *out_next_frame_deadline_us = 0; + return; + } + + max_render_time_allowed_us = + clutter_frame_clock_compute_max_render_time_us (frame_clock); + + last_presentation_time_us = frame_clock->last_presentation_time_us; + next_presentation_time_us = last_presentation_time_us + refresh_interval_us; + + next_update_time_us = next_presentation_time_us - max_render_time_allowed_us; + if (next_update_time_us < now_us) + next_update_time_us = now_us; + + if (next_presentation_time_us < next_update_time_us) + next_presentation_time_us = 0; + + next_frame_deadline_us = next_update_time_us; + if (next_frame_deadline_us == now_us) + next_frame_deadline_us += refresh_interval_us; + + *out_next_update_time_us = next_update_time_us; + *out_next_presentation_time_us = next_presentation_time_us; + *out_next_frame_deadline_us = next_frame_deadline_us; +} + +static void +calculate_next_variable_update_timeout_us (ClutterFrameClock *frame_clock, + int64_t *out_next_update_time_us) +{ + int64_t now_us; + int64_t last_presentation_time_us; + int64_t next_presentation_time_us; + int64_t timeout_interval_us; + + now_us = g_get_monotonic_time (); + + last_presentation_time_us = frame_clock->last_presentation_time_us; + + timeout_interval_us = frame_clock->minimum_refresh_interval_us; + + if (last_presentation_time_us == 0) + { + *out_next_update_time_us = + frame_clock->last_dispatch_time_us ? + ((frame_clock->last_dispatch_time_us - + frame_clock->last_dispatch_lateness_us) + timeout_interval_us) : + now_us; + return; + } + + next_presentation_time_us = last_presentation_time_us + timeout_interval_us; + + while (next_presentation_time_us < now_us) + next_presentation_time_us += timeout_interval_us; + + *out_next_update_time_us = next_presentation_time_us; +} + void clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) { @@ -665,7 +757,6 @@ 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: - next_update_time_us = g_get_monotonic_time (); break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: return; @@ -676,13 +767,30 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) return; } + switch (frame_clock->mode) + { + case CLUTTER_FRAME_CLOCK_MODE_FIXED: + next_update_time_us = g_get_monotonic_time (); + frame_clock->is_next_presentation_time_valid = FALSE; + frame_clock->has_next_frame_deadline = FALSE; + break; + case CLUTTER_FRAME_CLOCK_MODE_VARIABLE: + calculate_next_variable_update_time_us (frame_clock, + &next_update_time_us, + &frame_clock->next_presentation_time_us, + &frame_clock->next_frame_deadline_us); + frame_clock->is_next_presentation_time_valid = + (frame_clock->next_presentation_time_us != 0); + frame_clock->has_next_frame_deadline = + (frame_clock->next_frame_deadline_us != 0); + break; + } + g_warn_if_fail (next_update_time_us != -1); 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; - frame_clock->is_next_presentation_time_valid = FALSE; - frame_clock->has_next_frame_deadline = FALSE; } void @@ -700,16 +808,10 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) { case CLUTTER_FRAME_CLOCK_STATE_INIT: next_update_time_us = g_get_monotonic_time (); - break; + g_source_set_ready_time (frame_clock->source, next_update_time_us); + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + return; case CLUTTER_FRAME_CLOCK_STATE_IDLE: - calculate_next_update_time_us (frame_clock, - &next_update_time_us, - &frame_clock->next_presentation_time_us, - &frame_clock->next_frame_deadline_us); - frame_clock->is_next_presentation_time_valid = - (frame_clock->next_presentation_time_us != 0); - frame_clock->has_next_frame_deadline = - (frame_clock->next_frame_deadline_us != 0); break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: @@ -720,6 +822,26 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) return; } + switch (frame_clock->mode) + { + case CLUTTER_FRAME_CLOCK_MODE_FIXED: + calculate_next_update_time_us (frame_clock, + &next_update_time_us, + &frame_clock->next_presentation_time_us, + &frame_clock->next_frame_deadline_us); + frame_clock->is_next_presentation_time_valid = + (frame_clock->next_presentation_time_us != 0); + frame_clock->has_next_frame_deadline = + (frame_clock->next_frame_deadline_us != 0); + break; + case CLUTTER_FRAME_CLOCK_MODE_VARIABLE: + calculate_next_variable_update_timeout_us (frame_clock, + &next_update_time_us); + frame_clock->is_next_presentation_time_valid = FALSE; + frame_clock->has_next_frame_deadline = FALSE; + break; + } + g_warn_if_fail (next_update_time_us != -1); frame_clock->next_update_time_us = next_update_time_us; @@ -727,6 +849,37 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; } +void +clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, + ClutterFrameClockMode mode) +{ + if (frame_clock->mode == mode) + return; + + frame_clock->mode = mode; + + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + break; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + frame_clock->pending_reschedule = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + break; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: + case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + break; + } + + maybe_reschedule_update (frame_clock); +} + static void clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, int64_t time_us) @@ -944,6 +1097,10 @@ clutter_frame_clock_new (float refresh_rate, init_frame_clock_source (frame_clock); clutter_frame_clock_set_refresh_rate (frame_clock, refresh_rate); + + frame_clock->minimum_refresh_interval_us = + (int64_t) (0.5 + G_USEC_PER_SEC / MINIMUM_REFRESH_RATE); + frame_clock->vblank_duration_us = vblank_duration_us; frame_clock->output_name = g_strdup (output_name); @@ -981,6 +1138,7 @@ static void clutter_frame_clock_init (ClutterFrameClock *frame_clock) { frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_INIT; + frame_clock->mode = CLUTTER_FRAME_CLOCK_MODE_FIXED; } static void diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h index 825776abe..a7be5ef31 100644 --- a/clutter/clutter/clutter-frame-clock.h +++ b/clutter/clutter/clutter-frame-clock.h @@ -54,6 +54,12 @@ typedef struct _ClutterFrameListenerIface gpointer user_data); } ClutterFrameListenerIface; +typedef enum _ClutterFrameClockMode +{ + CLUTTER_FRAME_CLOCK_MODE_FIXED, + CLUTTER_FRAME_CLOCK_MODE_VARIABLE, +} ClutterFrameClockMode; + CLUTTER_EXPORT ClutterFrameClock * clutter_frame_clock_new (float refresh_rate, int64_t vblank_duration_us, @@ -64,6 +70,10 @@ ClutterFrameClock * clutter_frame_clock_new (float re CLUTTER_EXPORT void clutter_frame_clock_destroy (ClutterFrameClock *frame_clock); +CLUTTER_EXPORT +void clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, + ClutterFrameClockMode mode); + CLUTTER_EXPORT void clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, ClutterFrameInfo *frame_info);