clutter/frame-clock: Add triple buffering support
This commit is contained in:
parent
e4df885306
commit
1328fa9ff8
1 changed files with 170 additions and 28 deletions
|
@ -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;
|
||||
|
@ -369,14 +374,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,
|
||||
|
@ -417,10 +443,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,10 +476,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,7 +506,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 refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION;
|
||||
{
|
||||
int64_t ret = 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
|
||||
|
@ -493,7 +550,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;
|
||||
int64_t next_smooth_presentation_time_us = 0;
|
||||
int64_t next_update_time_us;
|
||||
|
||||
now_us = g_get_monotonic_time ();
|
||||
|
@ -538,7 +595,27 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
|
|||
*
|
||||
*/
|
||||
last_presentation_time_us = frame_clock->last_presentation_time_us;
|
||||
next_smooth_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:
|
||||
next_smooth_presentation_time_us = last_presentation_time_us +
|
||||
3 * refresh_interval_us;
|
||||
break;
|
||||
}
|
||||
|
||||
next_presentation_time_us = next_smooth_presentation_time_us;
|
||||
|
||||
/*
|
||||
|
@ -738,7 +815,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;
|
||||
}
|
||||
|
||||
|
@ -774,10 +861,18 @@ 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:
|
||||
next_update_time_us = g_get_monotonic_time ();
|
||||
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;
|
||||
|
@ -806,7 +901,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
|
||||
|
@ -828,11 +922,23 @@ 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:
|
||||
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->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
return;
|
||||
}
|
||||
|
@ -861,7 +967,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
|
||||
|
@ -877,6 +982,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;
|
||||
|
@ -887,7 +994,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;
|
||||
}
|
||||
|
||||
|
@ -944,10 +1058,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++;
|
||||
|
||||
|
@ -978,26 +1109,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)
|
||||
{
|
||||
case CLUTTER_FRAME_CLOCK_STATE_INIT:
|
||||
g_warn_if_reached ();
|
||||
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_PENDING_PRESENTED:
|
||||
break;
|
||||
case CLUTTER_FRAME_RESULT_IDLE:
|
||||
/* The frame was aborted; nothing to paint/present */
|
||||
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 ();
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
@ -1033,6 +1174,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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue