clutter/frame-clock: Evenly space updates when presentation times are zero

This is for the Nvidia-X11 driver where `last_presentation_time_us` is
always zero. Other drivers should be unaffected.

The existing `calculate_next_update_time_us` algorithm only provides a
guarantee of not scheduling faster than the refresh rate in the presence
of a valid `last_presentation_time_us`. When `last_presentation_time_us`
is zero there is no solid foundation to guarantee we're not occasionally
scheduling too early. So introduce one now.

By introducing a hard guarantee that updates are never scheduled faster
than the refresh rate, we avoid keeping Nvidia's triple (or quad?) buffer
queue full. So this avoids the high latency and random stalls experienced
on Nvidia.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/818,
        https://gitlab.gnome.org/GNOME/mutter/-/issues/1273,
        https://gitlab.gnome.org/GNOME/mutter/-/issues/1287,
        https://gitlab.gnome.org/GNOME/mutter/-/issues/1291,
        https://gitlab.gnome.org/GNOME/mutter/-/issues/1583

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1726>
This commit is contained in:
Daniel van Vugt 2021-02-11 15:48:40 +08:00 committed by Marge Bot
parent 75cff03aed
commit 56fc09151d

View File

@ -70,6 +70,7 @@ struct _ClutterFrameClock
int64_t frame_count; int64_t frame_count;
ClutterFrameClockState state; ClutterFrameClockState state;
int64_t last_dispatch_time_us;
int64_t last_presentation_time_us; int64_t last_presentation_time_us;
gboolean is_next_presentation_time_valid; gboolean is_next_presentation_time_valid;
@ -240,6 +241,17 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
refresh_rate = frame_clock->refresh_rate; refresh_rate = frame_clock->refresh_rate;
refresh_interval_us = (int64_t) (0.5 + G_USEC_PER_SEC / refresh_rate); refresh_interval_us = (int64_t) (0.5 + G_USEC_PER_SEC / refresh_rate);
if (frame_clock->last_presentation_time_us == 0)
{
*out_next_update_time_us =
frame_clock->last_dispatch_time_us ?
frame_clock->last_dispatch_time_us + refresh_interval_us :
now_us;
*out_next_presentation_time_us = 0;
return;
}
min_render_time_allowed_us = refresh_interval_us / 2; min_render_time_allowed_us = refresh_interval_us / 2;
max_render_time_allowed_us = refresh_interval_us - SYNC_DELAY_US; max_render_time_allowed_us = refresh_interval_us - SYNC_DELAY_US;
@ -444,7 +456,8 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
calculate_next_update_time_us (frame_clock, calculate_next_update_time_us (frame_clock,
&next_update_time_us, &next_update_time_us,
&frame_clock->next_presentation_time_us); &frame_clock->next_presentation_time_us);
frame_clock->is_next_presentation_time_valid = TRUE; frame_clock->is_next_presentation_time_valid =
(frame_clock->next_presentation_time_us != 0);
break; break;
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
return; return;
@ -469,6 +482,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockDispatch, "Frame Clock (dispatch)"); COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockDispatch, "Frame Clock (dispatch)");
frame_clock->last_dispatch_time_us = time_us;
g_source_set_ready_time (frame_clock->source, -1); g_source_set_ready_time (frame_clock->source, -1);
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHING; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHING;