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. So when
TIMESTAMP_QUERY support is missing we now take the same path as if
presentation timestamps are missing (introduced in 56fc09151d and
improved in 0555a5bbc1), which has the benefit of naturally switching
from double to triple buffering as required without actually having to
measure render times.

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.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441>
This commit is contained in:
Daniel van Vugt 2024-01-17 17:21:03 +08:00 committed by Marge Bot
parent 2dbca5ff39
commit f06913a2d1

View File

@ -616,25 +616,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
}
}
static int64_t
clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
static gboolean
clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock,
int64_t *max_render_time_us)
{
int64_t refresh_interval_us;
int64_t max_render_time_us;
refresh_interval_us = frame_clock->refresh_interval_us;
if (!frame_clock->ever_got_measurements ||
G_UNLIKELY (clutter_paint_debug_flags &
CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME))
{
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;
}
return FALSE;
/* 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
@ -648,15 +641,15 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
* - The duration of vertical blank.
* - 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,
frame_clock->shortterm_max_update_duration_us) +
frame_clock->vblank_duration_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
@ -674,12 +667,19 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
int64_t next_presentation_time_us;
int64_t next_smooth_presentation_time_us = 0;
int64_t next_update_time_us;
gboolean max_render_time_is_known;
now_us = g_get_monotonic_time ();
refresh_interval_us = frame_clock->refresh_interval_us;
if (!last_presentation || last_presentation->presentation_time_us == 0)
max_render_time_is_known =
clutter_frame_clock_compute_max_render_time_us (frame_clock,
&max_render_time_allowed_us);
if (!last_presentation ||
!max_render_time_is_known ||
last_presentation->presentation_time_us == 0)
{
const Frame *last_dispatch = frame_clock->prev_dispatch;
@ -695,8 +695,6 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
}
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)
min_render_time_allowed_us = max_render_time_allowed_us;
@ -854,7 +852,10 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock,
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;
@ -869,9 +870,6 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock,
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;
next_presentation_time_us = last_presentation_time_us + refresh_interval_us;
@ -1203,8 +1201,12 @@ clutter_frame_clock_schedule_update_later (ClutterFrameClock *frame_clock,
ready_time_us = extrapolated_presentation_time_us - max_render_time_us;
break;
case CLUTTER_FRAME_CLOCK_MODE_VARIABLE:
max_render_time_us =
clutter_frame_clock_compute_max_render_time_us (frame_clock);
if (!clutter_frame_clock_compute_max_render_time_us (frame_clock,
&max_render_time_us))
{
max_render_time_us = (int64_t) (frame_clock->refresh_interval_us *
SYNC_DELAY_FALLBACK_FRACTION);
}
ready_time_us = target_us - max_render_time_us;
break;
}
@ -1462,12 +1464,19 @@ GString *
clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock)
{
const Frame *last_presentation = frame_clock->prev_presentation;
int64_t max_render_time_us;
int64_t max_update_duration_us;
GString *string;
string = g_string_new (NULL);
g_string_append_printf (string, "Max render time: %ld µs",
clutter_frame_clock_compute_max_render_time_us (frame_clock));
string = g_string_new ("Max render time: ");
if (!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)
g_string_append_printf (string, " =");