1
0
Fork 0

clutter/frame-clock: Add triple buffering support

Signed-off-by: Mingi Sung <sungmg@saltyming.net>
This commit is contained in:
Daniel van Vugt 2020-09-10 16:34:53 +08:00 committed by Mingi Sung
parent 2ae303bb95
commit 0b2e48db6f
Signed by: sungmg
GPG key ID: 41BAFD6FFD8036C5

View file

@ -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;
@ -371,14 +376,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,
@ -393,7 +419,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);
@ -420,10 +446,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;
}
}
@ -441,10 +479,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;
}
}
@ -459,7 +509,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
@ -479,7 +536,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;
}
@ -496,6 +553,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 ();
@ -540,7 +598,29 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
*
*/
last_presentation_time_us = frame_clock->last_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:
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:
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.
@ -607,7 +687,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
}
if (frame_clock->last_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
@ -739,7 +819,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;
}
@ -775,10 +865,17 @@ 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:
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;
@ -807,7 +904,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
@ -829,11 +925,17 @@ 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:
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;
}
@ -862,7 +964,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
@ -878,6 +979,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;
@ -888,7 +991,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;
}
@ -945,10 +1055,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++;
@ -979,26 +1106,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;
}
@ -1034,6 +1171,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;
}