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:
parent
d7b8793f34
commit
f2290877be
1 changed files with 46 additions and 24 deletions
|
@ -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, " =");
|
||||||
|
|
Loading…
Reference in a new issue