diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index a05436ae4..3248b4dcc 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -72,6 +72,9 @@ struct _ClutterFrameClock ClutterFrameClockState state; int64_t last_presentation_time_us; + gboolean is_next_presentation_time_valid; + int64_t next_presentation_time_us; + gboolean pending_reschedule; }; @@ -120,7 +123,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, static void calculate_next_update_time_us (ClutterFrameClock *frame_clock, - int64_t *out_next_update_time_us) + int64_t *out_next_update_time_us, + int64_t *out_next_presentation_time_us) { int64_t last_presentation_time_us; int64_t now_us; @@ -128,6 +132,8 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, int64_t refresh_interval_us; int64_t min_render_time_allowed_us; int64_t max_render_time_allowed_us; + int64_t last_next_presentation_time_us; + int64_t time_since_last_next_presentation_time_us; int64_t next_presentation_time_us; int64_t next_update_time_us; @@ -159,12 +165,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, next_presentation_time_us = logical_clock_phase_us + hw_clock_offset_us; } + /* Skip one interval if we got an early presented event. */ + last_next_presentation_time_us = frame_clock->next_presentation_time_us; + time_since_last_next_presentation_time_us = + next_presentation_time_us - last_next_presentation_time_us; + if (frame_clock->is_next_presentation_time_valid && + time_since_last_next_presentation_time_us < (refresh_interval_us / 2)) + { + next_presentation_time_us = + frame_clock->next_presentation_time_us + refresh_interval_us; + } + while (next_presentation_time_us < now_us + min_render_time_allowed_us) next_presentation_time_us += refresh_interval_us; next_update_time_us = next_presentation_time_us - max_render_time_allowed_us; *out_next_update_time_us = next_update_time_us; + *out_next_presentation_time_us = next_presentation_time_us; } void @@ -178,7 +196,10 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) next_update_time_us = g_get_monotonic_time (); break; case CLUTTER_FRAME_CLOCK_STATE_IDLE: - calculate_next_update_time_us (frame_clock, &next_update_time_us); + calculate_next_update_time_us (frame_clock, + &next_update_time_us, + &frame_clock->next_presentation_time_us); + frame_clock->is_next_presentation_time_valid = TRUE; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: return; diff --git a/src/tests/clutter/conform/frame-clock.c b/src/tests/clutter/conform/frame-clock.c index cf548e2c4..63927ce0b 100644 --- a/src/tests/clutter/conform/frame-clock.c +++ b/src/tests/clutter/conform/frame-clock.c @@ -158,6 +158,78 @@ frame_clock_schedule_update (void) g_source_unref (source); } +static gboolean +schedule_update_idle (gpointer user_data) +{ + ClutterFrameClock *frame_clock = user_data; + + clutter_frame_clock_schedule_update (frame_clock); + + return G_SOURCE_REMOVE; +} + +static ClutterFrameResult +immediate_frame_clock_frame (ClutterFrameClock *frame_clock, + int64_t frame_count, + gpointer user_data) +{ + GMainLoop *main_loop = user_data; + + g_assert_cmpint (frame_count, ==, expected_frame_count); + + expected_frame_count++; + + if (test_frame_count == 0) + { + g_main_loop_quit (main_loop); + return CLUTTER_FRAME_RESULT_IDLE; + } + + test_frame_count--; + + clutter_frame_clock_notify_presented (frame_clock, g_get_monotonic_time ()); + g_idle_add (schedule_update_idle, frame_clock); + + return CLUTTER_FRAME_RESULT_PENDING_PRESENTED; +} + +static const ClutterFrameListenerIface immediate_frame_listener_iface = { + .frame = immediate_frame_clock_frame, +}; + +static void +frame_clock_immediate_present (void) +{ + GMainLoop *main_loop; + ClutterFrameClock *frame_clock; + int64_t before_us; + int64_t after_us; + + test_frame_count = 10; + expected_frame_count = 0; + + main_loop = g_main_loop_new (NULL, FALSE); + frame_clock = clutter_frame_clock_new (refresh_rate, + &immediate_frame_listener_iface, + main_loop); + + before_us = g_get_monotonic_time (); + + clutter_frame_clock_schedule_update (frame_clock); + g_main_loop_run (main_loop); + + after_us = g_get_monotonic_time (); + + /* The initial frame will only be delayed by 2 ms, so we are checking one + * less. + */ + g_assert_cmpint (after_us - before_us, >, 9 * refresh_interval_us); + + g_main_loop_unref (main_loop); + g_object_unref (frame_clock); +} + CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/frame-clock/schedule-update", frame_clock_schedule_update) + CLUTTER_TEST_UNIT ("/frame-clock/immediate-present", frame_clock_immediate_present) )