clutter/frame-clock: Conditionally disable triple buffering
1. When direct scanout is attempted There's no compositing during direct scanout so the "render" time is zero. Thus there is no need to implement triple buffering for direct scanouts. Stick to double buffering and enjoy the lower latency. 2. If disabled by environment variable MUTTER_DEBUG_TRIPLE_BUFFERING With possible values {never, auto, always} where auto is the default. 3. When VRR is in use VRR calls `clutter_frame_clock_schedule_update_now` which would keep the buffer queue full, which in turn prevented direct scanout mode. Because OnscreenNative currently only supports direct scanout with double buffering. We now break that feedback loop by preventing triple buffering from being scheduled when the frame clock mode becomes variable. Long term this could also be solved by supporting triple buffering in direct scanout mode. But whether or not that would be desirable given the latency penalty remains to be seen. (cherry picked from commit 280f7f6b26cd3e7a82706d1d001419295ea15d8b) 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
06d2bca831
commit
0615b2376b
1 changed files with 78 additions and 2 deletions
|
@ -42,6 +42,15 @@ enum
|
|||
|
||||
static guint signals[N_SIGNALS];
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TRIPLE_BUFFERING_MODE_NEVER,
|
||||
TRIPLE_BUFFERING_MODE_AUTO,
|
||||
TRIPLE_BUFFERING_MODE_ALWAYS,
|
||||
} TripleBufferingMode;
|
||||
|
||||
static TripleBufferingMode triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO;
|
||||
|
||||
#define SYNC_DELAY_FALLBACK_FRACTION 0.875f
|
||||
|
||||
#define MINIMUM_REFRESH_RATE 30.f
|
||||
|
@ -905,6 +914,35 @@ clutter_frame_clock_uninhibit (ClutterFrameClock *frame_clock)
|
|||
maybe_reschedule_update (frame_clock);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
want_triple_buffering (ClutterFrameClock *frame_clock)
|
||||
{
|
||||
switch (triple_buffering_mode)
|
||||
{
|
||||
case TRIPLE_BUFFERING_MODE_NEVER:
|
||||
return FALSE;
|
||||
case TRIPLE_BUFFERING_MODE_AUTO:
|
||||
{
|
||||
const Frame *last_flip;
|
||||
|
||||
if (frame_clock->mode == CLUTTER_FRAME_CLOCK_MODE_VARIABLE)
|
||||
return FALSE;
|
||||
|
||||
last_flip = frame_clock->prev_dispatch;
|
||||
if (!last_flip)
|
||||
return TRUE;
|
||||
|
||||
return !(last_flip->hints &
|
||||
CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED);
|
||||
}
|
||||
case TRIPLE_BUFFERING_MODE_ALWAYS:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
|
||||
{
|
||||
|
@ -927,11 +965,18 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
|
|||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW:
|
||||
return;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
|
||||
frame_clock->state =
|
||||
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW;
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
|
||||
if (want_triple_buffering (frame_clock))
|
||||
{
|
||||
frame_clock->state =
|
||||
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW;
|
||||
break;
|
||||
}
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
frame_clock->pending_reschedule_now = TRUE;
|
||||
|
@ -967,6 +1012,11 @@ void
|
|||
clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
||||
{
|
||||
int64_t next_update_time_us = -1;
|
||||
TripleBufferingMode current_mode = triple_buffering_mode;
|
||||
|
||||
if (current_mode == TRIPLE_BUFFERING_MODE_AUTO &&
|
||||
!want_triple_buffering (frame_clock))
|
||||
current_mode = TRIPLE_BUFFERING_MODE_NEVER;
|
||||
|
||||
if (frame_clock->inhibit_count > 0)
|
||||
{
|
||||
|
@ -990,7 +1040,23 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
|||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW:
|
||||
return;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
|
||||
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
|
||||
switch (current_mode)
|
||||
{
|
||||
case TRIPLE_BUFFERING_MODE_NEVER:
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
return;
|
||||
case TRIPLE_BUFFERING_MODE_AUTO:
|
||||
frame_clock->state =
|
||||
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
|
||||
break;
|
||||
case TRIPLE_BUFFERING_MODE_ALWAYS:
|
||||
next_update_time_us = g_get_monotonic_time ();
|
||||
frame_clock->next_presentation_time_us = 0;
|
||||
frame_clock->is_next_presentation_time_valid = FALSE;
|
||||
frame_clock->state =
|
||||
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
|
||||
goto got_update_time;
|
||||
}
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
|
@ -1017,6 +1083,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
|||
break;
|
||||
}
|
||||
|
||||
got_update_time:
|
||||
g_warn_if_fail (next_update_time_us != -1);
|
||||
|
||||
frame_clock->next_update_time_us = next_update_time_us;
|
||||
|
@ -1438,6 +1505,15 @@ static void
|
|||
clutter_frame_clock_class_init (ClutterFrameClockClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
const char *mode_str;
|
||||
|
||||
mode_str = g_getenv ("MUTTER_DEBUG_TRIPLE_BUFFERING");
|
||||
if (!g_strcmp0 (mode_str, "never"))
|
||||
triple_buffering_mode = TRIPLE_BUFFERING_MODE_NEVER;
|
||||
else if (!g_strcmp0 (mode_str, "auto"))
|
||||
triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO;
|
||||
else if (!g_strcmp0 (mode_str, "always"))
|
||||
triple_buffering_mode = TRIPLE_BUFFERING_MODE_ALWAYS;
|
||||
|
||||
object_class->dispose = clutter_frame_clock_dispose;
|
||||
|
||||
|
|
Loading…
Reference in a new issue