clutter/frame-clock: Use single pair of maxima for render time estimate

Instead of separate pairs of short- and long-term maxima for each
measured step of the frame update process.

This should result in the render time estimate rising less often:
Previously it did whenever the measurement of any of at least 3 out of
4 steps reached a new maximum, even if that didn't result in a new
maximum for the whole update duration. Now it's only in the latter
case.

This should also result in a lower render time estimate (and thus
input→output latency) in general, since the variability of
measurements for each 3/4 steps doesn't always add up anymore. The flip
side of this is that it might result in missing a display refresh cycle
more often.

v2:
* Fix coding style in maybe_update_longterm_max_duration_us.
  (Robert Mader)

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3090>
This commit is contained in:
Michel Dänzer 2023-06-23 18:04:00 +02:00 committed by Marge Bot
parent c4c6d17649
commit 69afa7a142

View File

@ -88,20 +88,12 @@ struct _ClutterFrameClock
/* Last KMS buffer submission time. */
int64_t last_flip_time_us;
/* Last time we promoted short term durations to long term ones */
/* Last time we promoted short-term maximum to long-term one */
int64_t longterm_promotion_us;
/* Short & long term maximum values */
struct {
/* Duration between desired and effective dispatch start. */
int64_t max_dispatch_lateness_us;
/* Duration between dispatch start and buffer swap. */
int64_t max_dispatch_to_swap_us;
/* Duration between buffer swap and GPU rendering finish. */
int64_t max_swap_to_rendering_done_us;
/* Duration between buffer swap and KMS submission. */
int64_t max_swap_to_flip_us;
} shortterm, longterm;
/* Long-term maximum update duration */
int64_t longterm_max_update_duration_us;
/* Short-term maximum update duration */
int64_t shortterm_max_update_duration_us;
/* If we got new measurements last frame. */
gboolean got_measurements_last_frame;
@ -224,20 +216,33 @@ maybe_reschedule_update (ClutterFrameClock *frame_clock)
}
static void
update_longterm_max (int64_t *longterm_max,
int64_t *shortterm_max)
maybe_update_longterm_max_duration_us (ClutterFrameClock *frame_clock,
ClutterFrameInfo *frame_info)
{
/* Do not update long term max if there has been no measurement in over a second. */
if (!*shortterm_max)
/* Do not update long-term max if there has been no measurement */
if (!frame_clock->shortterm_max_update_duration_us)
return;
if (*longterm_max > *shortterm_max)
/* Exponential drop-off toward the short term max */
*longterm_max -= (*longterm_max - *shortterm_max) / 2;
else
*longterm_max = *shortterm_max;
if ((frame_info->presentation_time - frame_clock->longterm_promotion_us) <
G_USEC_PER_SEC)
return;
*shortterm_max = 0;
if (frame_clock->longterm_max_update_duration_us >
frame_clock->shortterm_max_update_duration_us)
{
/* Exponential drop-off toward the short-term max */
frame_clock->longterm_max_update_duration_us -=
(frame_clock->longterm_max_update_duration_us -
frame_clock->shortterm_max_update_duration_us) / 2;
}
else
{
frame_clock->longterm_max_update_duration_us =
frame_clock->shortterm_max_update_duration_us;
}
frame_clock->shortterm_max_update_duration_us = 0;
frame_clock->longterm_promotion_us = frame_info->presentation_time;
}
void
@ -343,12 +348,12 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
swap_to_rendering_done_us,
swap_to_flip_us);
frame_clock->shortterm.max_dispatch_to_swap_us =
MAX (frame_clock->shortterm.max_dispatch_to_swap_us, dispatch_to_swap_us);
frame_clock->shortterm.max_swap_to_rendering_done_us =
MAX (frame_clock->shortterm.max_swap_to_rendering_done_us, swap_to_rendering_done_us);
frame_clock->shortterm.max_swap_to_flip_us =
MAX (frame_clock->shortterm.max_swap_to_flip_us, swap_to_flip_us);
frame_clock->shortterm_max_update_duration_us =
MAX (frame_clock->shortterm_max_update_duration_us,
frame_clock->last_dispatch_lateness_us + dispatch_to_swap_us +
MAX (swap_to_rendering_done_us, swap_to_flip_us));
maybe_update_longterm_max_duration_us (frame_clock, frame_info);
frame_clock->got_measurements_last_frame = TRUE;
frame_clock->ever_got_measurements = TRUE;
@ -359,20 +364,6 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
frame_clock->last_dispatch_lateness_us);
}
if (frame_info->presentation_time - frame_clock->longterm_promotion_us > G_USEC_PER_SEC)
{
update_longterm_max (&frame_clock->longterm.max_dispatch_lateness_us,
&frame_clock->shortterm.max_dispatch_lateness_us);
update_longterm_max (&frame_clock->longterm.max_dispatch_to_swap_us,
&frame_clock->shortterm.max_dispatch_to_swap_us);
update_longterm_max (&frame_clock->longterm.max_swap_to_rendering_done_us,
&frame_clock->shortterm.max_swap_to_rendering_done_us);
update_longterm_max (&frame_clock->longterm.max_swap_to_flip_us,
&frame_clock->shortterm.max_swap_to_flip_us);
frame_clock->longterm_promotion_us = frame_info->presentation_time;
}
if (frame_info->refresh_rate > 1.0)
{
clutter_frame_clock_set_refresh_rate (frame_clock,
@ -418,10 +409,6 @@ static int64_t
clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
{
int64_t refresh_interval_us;
int64_t max_dispatch_lateness_us;
int64_t max_dispatch_to_swap_us;
int64_t max_swap_to_rendering_done_us;
int64_t max_swap_to_flip_us;
int64_t max_render_time_us;
refresh_interval_us = frame_clock->refresh_interval_us;
@ -431,34 +418,21 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME))
return refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION;
max_dispatch_lateness_us =
MAX (frame_clock->longterm.max_dispatch_lateness_us,
frame_clock->shortterm.max_dispatch_lateness_us);
max_dispatch_to_swap_us =
MAX (frame_clock->longterm.max_dispatch_to_swap_us,
frame_clock->shortterm.max_dispatch_to_swap_us);
max_swap_to_rendering_done_us =
MAX (frame_clock->longterm.max_swap_to_rendering_done_us,
frame_clock->shortterm.max_swap_to_rendering_done_us);
max_swap_to_flip_us =
MAX (frame_clock->longterm.max_swap_to_flip_us,
frame_clock->shortterm.max_swap_to_flip_us);
/* Max render time shows how early the frame clock needs to be dispatched
* to make it to the predicted next presentation time. It is composed of:
* - An estimate of dispatch start lateness.
* - An estimate of duration from dispatch start to buffer swap.
* - Maximum between estimates of duration from buffer swap to GPU rendering
* finish and duration from buffer swap to buffer submission to KMS. This
* is because both of these things need to happen before the vblank, and
* they are done in parallel.
* - Duration of the vblank.
* to make it to the predicted next presentation time. It is an estimate of
* the total update duration, which is composed of:
* - Dispatch start lateness.
* - The duration from dispatch start to buffer swap.
* - The maximum of duration from buffer swap to GPU rendering finish and
* duration from buffer swap to buffer submission to KMS. This is because
* both of these things need to happen before the vblank, and they are done
* in parallel.
* - The duration of vertical blank.
* - A constant to account for variations in the above estimates.
*/
max_render_time_us =
max_dispatch_lateness_us +
max_dispatch_to_swap_us +
MAX (max_swap_to_rendering_done_us, max_swap_to_flip_us) +
MAX (frame_clock->longterm_max_update_duration_us,
frame_clock->shortterm_max_update_duration_us) +
frame_clock->vblank_duration_us +
clutter_max_render_time_constant_us;
@ -764,10 +738,6 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
else
frame_clock->last_dispatch_lateness_us = lateness_us;
frame_clock->shortterm.max_dispatch_lateness_us =
MAX (frame_clock->shortterm.max_dispatch_lateness_us,
frame_clock->last_dispatch_lateness_us);
if (G_UNLIKELY (CLUTTER_HAS_DEBUG (FRAME_TIMINGS)))
{
int64_t dispatch_interval_us, jitter_us;
@ -873,10 +843,7 @@ clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock,
GString *
clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock)
{
int64_t max_dispatch_lateness_us;
int64_t max_dispatch_to_swap_us;
int64_t max_swap_to_rendering_done_us;
int64_t max_swap_to_flip_us;
int64_t max_update_duration_us;
GString *string;
string = g_string_new (NULL);
@ -888,29 +855,14 @@ clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clo
else
g_string_append_printf (string, " (no measurements last frame)");
max_dispatch_lateness_us =
MAX (frame_clock->longterm.max_dispatch_lateness_us,
frame_clock->shortterm.max_dispatch_lateness_us);
max_dispatch_to_swap_us =
MAX (frame_clock->longterm.max_dispatch_to_swap_us,
frame_clock->shortterm.max_dispatch_to_swap_us);
max_swap_to_rendering_done_us =
MAX (frame_clock->longterm.max_swap_to_rendering_done_us,
frame_clock->shortterm.max_swap_to_rendering_done_us);
max_swap_to_flip_us =
MAX (frame_clock->longterm.max_swap_to_flip_us,
frame_clock->shortterm.max_swap_to_flip_us);
max_update_duration_us =
MAX (frame_clock->longterm_max_update_duration_us,
frame_clock->shortterm_max_update_duration_us);
g_string_append_printf (string, "\nVblank duration: %ld µs +",
frame_clock->vblank_duration_us);
g_string_append_printf (string, "\nDispatch lateness: %ld µs +",
max_dispatch_lateness_us);
g_string_append_printf (string, "\nDispatch to swap: %ld µs +",
max_dispatch_to_swap_us);
g_string_append_printf (string, "\nmax(Swap to rendering done: %ld µs,",
max_swap_to_rendering_done_us);
g_string_append_printf (string, "\nSwap to flip: %ld µs) +",
max_swap_to_flip_us);
g_string_append_printf (string, "\nUpdate duration: %ld µs +",
max_update_duration_us);
g_string_append_printf (string, "\nConstant: %d µs",
clutter_max_render_time_constant_us);