1
0
Fork 0

clutter/frame-clock: Optimize latency for platforms missing TIMESTAMP_QUERY

Previously if we had no measurements then `compute_max_render_time_us`
would pessimise its answer to ensure triple buffering could be reached:
```
if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE)
  ret += refresh_interval_us;
```
But that also meant entering triple buffering even when not required.

Now we make `compute_max_render_time_us` more honest and return failure
if the answer isn't known (or is disabled). This in turn allows us to
optimize `calculate_next_update_time_us` for this special case, ensuring
triple buffering can be used, but isn't blindly always used.

This makes a visible difference to the latency when dragging windows in
Xorg, but will also help Wayland sessions on platforms lacking
TIMESTAMP_QUERY such as Raspberry Pi.

(cherry picked from commit 7852451a9e93f5116fa853350020a318d31cb710)
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441>
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
This commit is contained in:
Daniel van Vugt 2024-01-17 17:21:03 +08:00 committed by Mingi Sung
parent d7b8793f34
commit f2290877be
Signed by: sungmg
GPG key ID: 41BAFD6FFD8036C5

View file

@ -563,25 +563,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
} }
} }
static int64_t static gboolean
clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock,
int64_t *max_render_time_us)
{ {
int64_t refresh_interval_us; int64_t refresh_interval_us;
int64_t max_render_time_us;
refresh_interval_us = frame_clock->refresh_interval_us; refresh_interval_us = frame_clock->refresh_interval_us;
if (!frame_clock->ever_got_measurements || if (!frame_clock->ever_got_measurements ||
G_UNLIKELY (clutter_paint_debug_flags & G_UNLIKELY (clutter_paint_debug_flags &
CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME)) CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME))
{ return FALSE;
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 /* 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 * to make it to the predicted next presentation time. It is an estimate of
@ -595,15 +588,15 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
* - The duration of vertical blank. * - The duration of vertical blank.
* - A constant to account for variations in the above estimates. * - A constant to account for variations in the above estimates.
*/ */
max_render_time_us = *max_render_time_us =
MAX (frame_clock->longterm_max_update_duration_us, MAX (frame_clock->longterm_max_update_duration_us,
frame_clock->shortterm_max_update_duration_us) + frame_clock->shortterm_max_update_duration_us) +
frame_clock->vblank_duration_us + frame_clock->vblank_duration_us +
clutter_max_render_time_constant_us; clutter_max_render_time_constant_us;
max_render_time_us = CLAMP (max_render_time_us, 0, 2 * refresh_interval_us); *max_render_time_us = CLAMP (*max_render_time_us, 0, 2 * refresh_interval_us);
return max_render_time_us; return TRUE;
} }
static void static void
@ -621,6 +614,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
int64_t next_presentation_time_us; int64_t next_presentation_time_us;
int64_t next_smooth_presentation_time_us = 0; int64_t next_smooth_presentation_time_us = 0;
int64_t next_update_time_us; int64_t next_update_time_us;
gboolean max_render_time_is_known;
now_us = g_get_monotonic_time (); now_us = g_get_monotonic_time ();
@ -642,10 +636,13 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
} }
min_render_time_allowed_us = refresh_interval_us / 2; min_render_time_allowed_us = refresh_interval_us / 2;
max_render_time_allowed_us =
clutter_frame_clock_compute_max_render_time_us (frame_clock);
if (min_render_time_allowed_us > max_render_time_allowed_us) max_render_time_is_known =
clutter_frame_clock_compute_max_render_time_us (frame_clock,
&max_render_time_allowed_us);
if (max_render_time_is_known &&
min_render_time_allowed_us > max_render_time_allowed_us)
min_render_time_allowed_us = max_render_time_allowed_us; min_render_time_allowed_us = max_render_time_allowed_us;
/* /*
@ -767,6 +764,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
} }
else else
{ {
/* If the max render time isn't known then using the current value of
* next_presentation_time_us is suboptimal. Targeting always one frame
* prior to that we'd lose the ability to scale up to triple buffering
* on late presentation. But targeting two frames prior we would be
* always triple buffering even when not required.
* So the algorithm for deciding when to scale up to triple buffering
* in the absence of render time measurements is to simply target full
* frame rate. If we're keeping up then we'll stay double buffering. If
* we're not keeping up then this will switch us to triple buffering.
*/
if (!max_render_time_is_known)
{
max_render_time_allowed_us =
(int64_t) (refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION);
next_presentation_time_us =
last_presentation_time_us + refresh_interval_us;
}
while (next_presentation_time_us - min_render_time_allowed_us < now_us) while (next_presentation_time_us - min_render_time_allowed_us < now_us)
next_presentation_time_us += refresh_interval_us; next_presentation_time_us += refresh_interval_us;
@ -799,7 +814,10 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock,
refresh_interval_us = frame_clock->refresh_interval_us; refresh_interval_us = frame_clock->refresh_interval_us;
if (!last_presentation || last_presentation->presentation_time_us == 0) if (!last_presentation ||
last_presentation->presentation_time_us == 0 ||
!clutter_frame_clock_compute_max_render_time_us (frame_clock,
&max_render_time_allowed_us))
{ {
const Frame *last_dispatch = frame_clock->prev_dispatch; const Frame *last_dispatch = frame_clock->prev_dispatch;
@ -814,9 +832,6 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock,
return; return;
} }
max_render_time_allowed_us =
clutter_frame_clock_compute_max_render_time_us (frame_clock);
last_presentation_time_us = last_presentation->presentation_time_us; last_presentation_time_us = last_presentation->presentation_time_us;
next_presentation_time_us = last_presentation_time_us + refresh_interval_us; next_presentation_time_us = last_presentation_time_us + refresh_interval_us;
@ -1316,12 +1331,19 @@ GString *
clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock) clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock)
{ {
const Frame *last_presentation = frame_clock->prev_presentation; const Frame *last_presentation = frame_clock->prev_presentation;
int64_t max_render_time_us;
int64_t max_update_duration_us; int64_t max_update_duration_us;
GString *string; GString *string;
string = g_string_new (NULL); string = g_string_new ("Max render time: ");
g_string_append_printf (string, "Max render time: %ld µs", if (!clutter_frame_clock_compute_max_render_time_us (frame_clock,
clutter_frame_clock_compute_max_render_time_us (frame_clock)); &max_render_time_us))
{
g_string_append (string, "unknown");
return string;
}
g_string_append_printf (string, "%ld µs", max_render_time_us);
if (last_presentation && last_presentation->got_measurements) if (last_presentation && last_presentation->got_measurements)
g_string_append_printf (string, " ="); g_string_append_printf (string, " =");