1
0
Fork 0
mutter-performance-pkgbuild/mr1441.patch
Mingi Sung 6aefbdd910
Sync to gnome-44
Signed-off-by: Mingi Sung <fiestalake@disroot.org>
2023-07-18 15:21:23 +09:00

2409 lines
87 KiB
Diff

Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
Source: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441
Source: https://gitlab.gnome.org/Community/Ubuntu/mutter/-/tree/triple-buffering-v4-44
Editor: Mingi Sung <fiestalake@disroot.org>
Commit: 357ebf9a227bd5b4bd87b610dd6545d8a81ba265
Last Updated: 7/18/23 (Mutter 44.3)
---
Use triple buffering if and when the previous frame is running late.
This means the next frame will be dispatched on time instead of also starting
late.
It also triggers a GPU clock boost if deemed necessary by the driver.
Although frequency scaling is not required to get a performance gain here
because even a fixed frequency GPU will benefit from not over-sleeping anymore.
If the previous frame is not running late then we stick to double buffering so
there's no latency penalty when the system is able to maintain full frame rate.
---
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index 3aeb29042..b1d6f7164 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -35,6 +35,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.875
typedef struct _ClutterFrameListener
@@ -55,8 +64,9 @@ typedef enum _ClutterFrameClockState
CLUTTER_FRAME_CLOCK_STATE_INIT,
CLUTTER_FRAME_CLOCK_STATE_IDLE,
CLUTTER_FRAME_CLOCK_STATE_SCHEDULED,
- CLUTTER_FRAME_CLOCK_STATE_DISPATCHING,
- CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED,
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE,
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED,
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO,
} ClutterFrameClockState;
struct _ClutterFrameClock
@@ -73,6 +83,7 @@ struct _ClutterFrameClock
ClutterFrameClockState state;
int64_t last_dispatch_time_us;
+ int64_t prev_last_dispatch_time_us;
int64_t last_dispatch_lateness_us;
int64_t last_presentation_time_us;
int64_t next_update_time_us;
@@ -87,6 +98,9 @@ struct _ClutterFrameClock
int64_t vblank_duration_us;
/* Last KMS buffer submission time. */
int64_t last_flip_time_us;
+ int64_t prev_last_flip_time_us;
+
+ ClutterFrameHint last_flip_hints;
/* Last time we promoted short term durations to long term ones */
int64_t longterm_promotion_us;
@@ -241,6 +255,12 @@ void
clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
ClutterFrameInfo *frame_info)
{
+#ifdef CLUTTER_ENABLE_DEBUG
+ const char *debug_state =
+ frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ?
+ "Triple buffering" : "Double buffering";
+#endif
+
COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented,
"Frame Clock (presented)");
@@ -324,18 +344,38 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
frame_info->gpu_rendering_duration_ns != 0)
{
int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us;
+ int64_t dispatch_time_us = 0, flip_time_us = 0;
+
+ switch (frame_clock->state)
+ {
+ case CLUTTER_FRAME_CLOCK_STATE_INIT:
+ case CLUTTER_FRAME_CLOCK_STATE_IDLE:
+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ g_warn_if_reached ();
+ G_GNUC_FALLTHROUGH;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ dispatch_time_us = frame_clock->last_dispatch_time_us;
+ flip_time_us = frame_clock->last_flip_time_us;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
+ dispatch_time_us = frame_clock->prev_last_dispatch_time_us;
+ flip_time_us = frame_clock->prev_last_flip_time_us;
+ break;
+ }
dispatch_to_swap_us =
frame_info->cpu_time_before_buffer_swap_us -
- frame_clock->last_dispatch_time_us;
+ dispatch_time_us;
swap_to_rendering_done_us =
frame_info->gpu_rendering_duration_ns / 1000;
swap_to_flip_us =
- frame_clock->last_flip_time_us -
+ flip_time_us -
frame_info->cpu_time_before_buffer_swap_us;
CLUTTER_NOTE (FRAME_TIMINGS,
- "update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs",
+ "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs",
+ debug_state,
frame_clock->last_dispatch_lateness_us,
dispatch_to_swap_us,
swap_to_rendering_done_us,
@@ -352,7 +392,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
}
else
{
- CLUTTER_NOTE (FRAME_TIMINGS, "update2dispatch %ld µs",
+ CLUTTER_NOTE (FRAME_TIMINGS, "%s: update2dispatch %ld µs",
+ debug_state,
frame_clock->last_dispatch_lateness_us);
}
@@ -383,11 +424,18 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
g_warn_if_reached ();
break;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
maybe_reschedule_update (frame_clock);
break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
+ maybe_reschedule_update (frame_clock);
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ maybe_reschedule_update (frame_clock);
+ break;
}
}
@@ -403,11 +451,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
g_warn_if_reached ();
break;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
maybe_reschedule_update (frame_clock);
break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
+ maybe_reschedule_update (frame_clock);
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ maybe_reschedule_update (frame_clock);
+ break;
}
}
@@ -426,7 +481,15 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
if (!frame_clock->got_measurements_last_frame ||
G_UNLIKELY (clutter_paint_debug_flags &
CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME))
- return refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION;
+ {
+ int64_t ret = refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION;
+
+ if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE &&
+ triple_buffering_mode != TRIPLE_BUFFERING_MODE_NEVER)
+ ret += refresh_interval_us;
+
+ return ret;
+ }
max_dispatch_lateness_us =
MAX (frame_clock->longterm.max_dispatch_lateness_us,
@@ -459,8 +522,6 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
frame_clock->vblank_duration_us +
clutter_max_render_time_constant_us;
- max_render_time_us = CLAMP (max_render_time_us, 0, refresh_interval_us);
-
return max_render_time_us;
}
@@ -475,7 +536,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
int64_t refresh_interval_us;
int64_t min_render_time_allowed_us;
int64_t max_render_time_allowed_us;
- int64_t next_presentation_time_us;
+ int64_t next_presentation_time_us = 0;
int64_t next_update_time_us;
now_us = g_get_monotonic_time ();
@@ -520,7 +581,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
*
*/
last_presentation_time_us = frame_clock->last_presentation_time_us;
- next_presentation_time_us = last_presentation_time_us + refresh_interval_us;
+ switch (frame_clock->state)
+ {
+ case CLUTTER_FRAME_CLOCK_STATE_INIT:
+ case CLUTTER_FRAME_CLOCK_STATE_IDLE:
+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ next_presentation_time_us = last_presentation_time_us +
+ refresh_interval_us;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ next_presentation_time_us = last_presentation_time_us +
+ 2 * refresh_interval_us;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
+ next_presentation_time_us = last_presentation_time_us +
+ 3 * refresh_interval_us;
+ break;
+ }
/*
* However, the last presentation could have happened more than a frame ago.
@@ -635,8 +713,12 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock)
frame_clock->pending_reschedule = TRUE;
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
break;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ frame_clock->pending_reschedule = TRUE;
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
break;
}
@@ -673,9 +755,15 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
next_update_time_us = g_get_monotonic_time ();
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ next_update_time_us = g_get_monotonic_time ();
+ frame_clock->state =
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
break;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
frame_clock->pending_reschedule = TRUE;
frame_clock->pending_reschedule_now = TRUE;
return;
@@ -685,7 +773,6 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
frame_clock->next_update_time_us = next_update_time_us;
g_source_set_ready_time (frame_clock->source, next_update_time_us);
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
frame_clock->is_next_presentation_time_valid = FALSE;
}
@@ -693,6 +780,10 @@ void
clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
{
int64_t next_update_time_us = -1;
+ TripleBufferingMode current_mode = triple_buffering_mode;
+
+ if (frame_clock->last_flip_hints & CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED)
+ current_mode = TRIPLE_BUFFERING_MODE_NEVER;
if (frame_clock->inhibit_count > 0)
{
@@ -704,6 +795,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
{
case CLUTTER_FRAME_CLOCK_STATE_INIT:
next_update_time_us = g_get_monotonic_time ();
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
break;
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
calculate_next_update_time_us (frame_clock,
@@ -712,11 +804,37 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
&frame_clock->min_render_time_allowed_us);
frame_clock->is_next_presentation_time_valid =
(frame_clock->next_presentation_time_us != 0);
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
break;
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
return;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ switch (current_mode)
+ {
+ case TRIPLE_BUFFERING_MODE_NEVER:
+ frame_clock->pending_reschedule = TRUE;
+ return;
+ case TRIPLE_BUFFERING_MODE_AUTO:
+ calculate_next_update_time_us (frame_clock,
+ &next_update_time_us,
+ &frame_clock->next_presentation_time_us,
+ &frame_clock->min_render_time_allowed_us);
+ frame_clock->is_next_presentation_time_valid =
+ (frame_clock->next_presentation_time_us != 0);
+ 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;
+ break;
+ }
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
frame_clock->pending_reschedule = TRUE;
return;
}
@@ -725,7 +843,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
frame_clock->next_update_time_us = next_update_time_us;
g_source_set_ready_time (frame_clock->source, next_update_time_us);
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
}
static void
@@ -756,7 +873,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
frame_clock->refresh_interval_us;
lateness_us = time_us - ideal_dispatch_time_us;
- if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us)
+ if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us / 4)
frame_clock->last_dispatch_lateness_us = 0;
else
frame_clock->last_dispatch_lateness_us = lateness_us;
@@ -765,10 +882,25 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
MAX (frame_clock->shortterm.max_dispatch_lateness_us,
frame_clock->last_dispatch_lateness_us);
+ frame_clock->prev_last_dispatch_time_us = frame_clock->last_dispatch_time_us;
frame_clock->last_dispatch_time_us = time_us;
g_source_set_ready_time (frame_clock->source, -1);
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHING;
+ switch (frame_clock->state)
+ {
+ case CLUTTER_FRAME_CLOCK_STATE_INIT:
+ case CLUTTER_FRAME_CLOCK_STATE_IDLE:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
+ g_warn_if_reached ();
+ return;
+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO;
+ break;
+ }
frame_count = frame_clock->frame_count++;
@@ -797,25 +929,31 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
result = iface->frame (frame_clock, frame, frame_clock->listener.user_data);
COGL_TRACE_END (ClutterFrameClockFrame);
- switch (frame_clock->state)
+ switch (result)
{
- case CLUTTER_FRAME_CLOCK_STATE_INIT:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
- g_warn_if_reached ();
- break;
- case CLUTTER_FRAME_CLOCK_STATE_IDLE:
- case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ case CLUTTER_FRAME_RESULT_PENDING_PRESENTED:
break;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- switch (result)
+ case CLUTTER_FRAME_RESULT_IDLE:
+ /* The frame was aborted; nothing to paint/present */
+ switch (frame_clock->state)
{
- case CLUTTER_FRAME_RESULT_PENDING_PRESENTED:
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED;
+ case CLUTTER_FRAME_CLOCK_STATE_INIT:
+ case CLUTTER_FRAME_CLOCK_STATE_IDLE:
+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ g_warn_if_reached ();
break;
- case CLUTTER_FRAME_RESULT_IDLE:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
maybe_reschedule_update (frame_clock);
break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
+ maybe_reschedule_update (frame_clock);
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ maybe_reschedule_update (frame_clock);
+ break;
}
break;
}
@@ -848,10 +986,13 @@ frame_clock_source_dispatch (GSource *source,
}
void
-clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock,
- int64_t flip_time_us)
+clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock,
+ int64_t flip_time_us,
+ ClutterFrameHint hints)
{
+ frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us;
frame_clock->last_flip_time_us = flip_time_us;
+ frame_clock->last_flip_hints = hints;
}
GString *
@@ -963,8 +1104,6 @@ clutter_frame_clock_dispose (GObject *object)
{
ClutterFrameClock *frame_clock = CLUTTER_FRAME_CLOCK (object);
- g_warn_if_fail (frame_clock->state != CLUTTER_FRAME_CLOCK_STATE_DISPATCHING);
-
if (frame_clock->source)
{
g_signal_emit (frame_clock, signals[DESTROY], 0);
@@ -985,6 +1124,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;
diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h
index 6fd5de47a..6b44851a6 100644
--- a/clutter/clutter/clutter-frame-clock.h
+++ b/clutter/clutter/clutter-frame-clock.h
@@ -34,6 +34,12 @@ typedef enum _ClutterFrameResult
CLUTTER_FRAME_RESULT_IDLE,
} ClutterFrameResult;
+typedef enum _ClutterFrameHint
+{
+ CLUTTER_FRAME_HINT_NONE = 0,
+ CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED = 1 << 0,
+} ClutterFrameHint;
+
#define CLUTTER_TYPE_FRAME_CLOCK (clutter_frame_clock_get_type ())
CLUTTER_EXPORT
G_DECLARE_FINAL_TYPE (ClutterFrameClock, clutter_frame_clock,
@@ -92,8 +98,9 @@ void clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock,
CLUTTER_EXPORT
float clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock);
-void clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock,
- int64_t flip_time_us);
+void clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock,
+ int64_t flip_time_us,
+ ClutterFrameHint hints);
GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock);
diff --git a/clutter/clutter/clutter-frame-private.h b/clutter/clutter/clutter-frame-private.h
index 1eceb8bc0..fb97abeaf 100644
--- a/clutter/clutter/clutter-frame-private.h
+++ b/clutter/clutter/clutter-frame-private.h
@@ -35,6 +35,7 @@ struct _ClutterFrame
gboolean has_result;
ClutterFrameResult result;
+ ClutterFrameHint hints;
};
CLUTTER_EXPORT
diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c
index 85baef274..413ce9c2b 100644
--- a/clutter/clutter/clutter-frame.c
+++ b/clutter/clutter/clutter-frame.c
@@ -113,3 +113,16 @@ clutter_frame_set_result (ClutterFrame *frame,
frame->result = result;
frame->has_result = TRUE;
}
+
+void
+clutter_frame_set_hint (ClutterFrame *frame,
+ ClutterFrameHint hint)
+{
+ frame->hints |= hint;
+}
+
+ClutterFrameHint
+clutter_frame_get_hints (ClutterFrame *frame)
+{
+ return frame->hints;
+}
diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h
index 6bac3f4e4..fa97e8bd5 100644
--- a/clutter/clutter/clutter-frame.h
+++ b/clutter/clutter/clutter-frame.h
@@ -55,6 +55,13 @@ void clutter_frame_set_result (ClutterFrame *frame,
CLUTTER_EXPORT
gboolean clutter_frame_has_result (ClutterFrame *frame);
+CLUTTER_EXPORT
+void clutter_frame_set_hint (ClutterFrame *frame,
+ ClutterFrameHint hint);
+
+CLUTTER_EXPORT
+ClutterFrameHint clutter_frame_get_hints (ClutterFrame *frame);
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFrame, clutter_frame_unref)
#endif /* CLUTTER_FRAME_H */
diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c
index 5f66d6032..9e821155b 100644
--- a/clutter/clutter/clutter-stage-view.c
+++ b/clutter/clutter/clutter-stage-view.c
@@ -1266,8 +1266,9 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock,
_clutter_stage_window_redraw_view (stage_window, view, frame);
- clutter_frame_clock_record_flip_time (frame_clock,
- g_get_monotonic_time ());
+ clutter_frame_clock_record_flip (frame_clock,
+ g_get_monotonic_time (),
+ clutter_frame_get_hints (frame));
clutter_stage_emit_after_paint (stage, view, frame);
diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h
index dffe018d2..e0215f750 100644
--- a/cogl/cogl/cogl-onscreen-private.h
+++ b/cogl/cogl/cogl-onscreen-private.h
@@ -97,4 +97,7 @@ cogl_onscreen_peek_tail_frame_info (CoglOnscreen *onscreen);
COGL_EXPORT CoglFrameInfo *
cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen);
+COGL_EXPORT unsigned int
+cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen);
+
#endif /* __COGL_ONSCREEN_PRIVATE_H */
diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c
index 842ececf7..1e2b11dee 100644
--- a/cogl/cogl/cogl-onscreen.c
+++ b/cogl/cogl/cogl-onscreen.c
@@ -508,6 +508,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen)
return g_queue_pop_head (&priv->pending_frame_infos);
}
+unsigned int
+cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen)
+{
+ CoglOnscreenPrivate *priv = cogl_onscreen_get_instance_private (onscreen);
+
+ return g_queue_get_length (&priv->pending_frame_infos);
+}
+
CoglFrameClosure *
cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen,
CoglFrameCallback callback,
diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c
index db94c7e40..aed22960c 100644
--- a/src/backends/meta-stage-impl.c
+++ b/src/backends/meta-stage-impl.c
@@ -770,6 +770,8 @@ meta_stage_impl_redraw_view (ClutterStageWindow *stage_window,
{
g_autoptr (GError) error = NULL;
+ clutter_frame_set_hint (frame, CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED);
+
if (meta_stage_impl_scanout_view (stage_impl,
stage_view,
scanout,
diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
index f1b7459fe..8080098a0 100644
--- a/src/backends/native/meta-cursor-renderer-native.c
+++ b/src/backends/native/meta-cursor-renderer-native.c
@@ -59,19 +59,6 @@
#include "wayland/meta-wayland-buffer.h"
#endif
-/* When animating a cursor, we usually call drmModeSetCursor2 once per frame.
- * Though, testing shows that we need to triple buffer the cursor buffer in
- * order to avoid glitches when animating the cursor, at least when running on
- * Intel. The reason for this might be (but is not confirmed to be) due to
- * the user space gbm_bo cache, making us reuse and overwrite the kernel side
- * buffer content before it was scanned out. To avoid this, we keep a user space
- * reference to each buffer we set until at least one frame after it was drawn.
- * In effect, this means we three active cursor gbm_bo's: one that that just has
- * been set, one that was previously set and may or may not have been scanned
- * out, and one pending that will be replaced if the cursor sprite changes.
- */
-#define HW_CURSOR_BUFFER_COUNT 3
-
static GQuark quark_cursor_sprite = 0;
typedef struct _CrtcCursorData
@@ -105,19 +92,10 @@ typedef struct _MetaCursorRendererNativeGpuData
uint64_t cursor_height;
} MetaCursorRendererNativeGpuData;
-typedef enum _MetaCursorBufferState
-{
- META_CURSOR_BUFFER_STATE_NONE,
- META_CURSOR_BUFFER_STATE_SET,
- META_CURSOR_BUFFER_STATE_INVALIDATED,
-} MetaCursorBufferState;
-
typedef struct _MetaCursorNativeGpuState
{
MetaGpu *gpu;
- unsigned int active_buffer_idx;
- MetaCursorBufferState pending_buffer_state;
- MetaDrmBuffer *buffers[HW_CURSOR_BUFFER_COUNT];
+ MetaDrmBuffer *buffer;
} MetaCursorNativeGpuState;
typedef struct _MetaCursorNativePrivate
@@ -198,44 +176,17 @@ meta_cursor_renderer_native_finalize (GObject *object)
G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object);
}
-static unsigned int
-get_pending_cursor_sprite_buffer_index (MetaCursorNativeGpuState *cursor_gpu_state)
-{
- return (cursor_gpu_state->active_buffer_idx + 1) % HW_CURSOR_BUFFER_COUNT;
-}
-
-static MetaDrmBuffer *
-get_pending_cursor_sprite_buffer (MetaCursorNativeGpuState *cursor_gpu_state)
-{
- unsigned int pending_buffer_idx;
-
- pending_buffer_idx =
- get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
- return cursor_gpu_state->buffers[pending_buffer_idx];
-}
-
-static MetaDrmBuffer *
-get_active_cursor_sprite_buffer (MetaCursorNativeGpuState *cursor_gpu_state)
-{
- return cursor_gpu_state->buffers[cursor_gpu_state->active_buffer_idx];
-}
-
static void
-set_pending_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
- MetaGpuKms *gpu_kms,
- MetaDrmBuffer *buffer)
+set_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
+ MetaGpuKms *gpu_kms,
+ MetaDrmBuffer *buffer)
{
MetaCursorNativePrivate *cursor_priv;
MetaCursorNativeGpuState *cursor_gpu_state;
- unsigned int pending_buffer_idx;
cursor_priv = ensure_cursor_priv (cursor_sprite);
cursor_gpu_state = ensure_cursor_gpu_state (cursor_priv, gpu_kms);
-
- pending_buffer_idx =
- get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
- cursor_gpu_state->buffers[pending_buffer_idx] = buffer;
- cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_SET;
+ cursor_gpu_state->buffer = buffer;
}
static void
@@ -312,10 +263,7 @@ assign_cursor_plane (MetaCursorRendererNative *native,
MetaKmsUpdate *kms_update;
MetaKmsPlaneAssignment *plane_assignment;
- if (cursor_gpu_state->pending_buffer_state == META_CURSOR_BUFFER_STATE_SET)
- buffer = get_pending_cursor_sprite_buffer (cursor_gpu_state);
- else
- buffer = get_active_cursor_sprite_buffer (cursor_gpu_state);
+ buffer = cursor_gpu_state->buffer;
kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
kms_device = meta_kms_crtc_get_device (kms_crtc);
@@ -365,13 +313,6 @@ assign_cursor_plane (MetaCursorRendererNative *native,
native);
crtc_cursor_data->buffer = buffer;
-
- if (cursor_gpu_state->pending_buffer_state == META_CURSOR_BUFFER_STATE_SET)
- {
- cursor_gpu_state->active_buffer_idx =
- (cursor_gpu_state->active_buffer_idx + 1) % HW_CURSOR_BUFFER_COUNT;
- cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_NONE;
- }
}
static float
@@ -613,19 +554,7 @@ has_valid_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
if (!cursor_gpu_state)
return FALSE;
- switch (cursor_gpu_state->pending_buffer_state)
- {
- case META_CURSOR_BUFFER_STATE_NONE:
- return get_active_cursor_sprite_buffer (cursor_gpu_state) != NULL;
- case META_CURSOR_BUFFER_STATE_SET:
- return TRUE;
- case META_CURSOR_BUFFER_STATE_INVALIDATED:
- return FALSE;
- }
-
- g_assert_not_reached ();
-
- return FALSE;
+ return cursor_gpu_state->buffer != NULL;
}
static void
@@ -1132,16 +1061,14 @@ unset_crtc_cursor_renderer_privates (MetaGpu *gpu,
static void
cursor_gpu_state_free (MetaCursorNativeGpuState *cursor_gpu_state)
{
- int i;
MetaDrmBuffer *active_buffer;
- active_buffer = get_active_cursor_sprite_buffer (cursor_gpu_state);
+ active_buffer = cursor_gpu_state->buffer;
if (active_buffer)
unset_crtc_cursor_renderer_privates (cursor_gpu_state->gpu,
active_buffer);
- for (i = 0; i < HW_CURSOR_BUFFER_COUNT; i++)
- g_clear_object (&cursor_gpu_state->buffers[i]);
+ g_clear_object (&cursor_gpu_state->buffer);
g_free (cursor_gpu_state);
}
@@ -1178,14 +1105,7 @@ invalidate_cursor_gpu_state (MetaCursorSprite *cursor_sprite)
g_hash_table_iter_init (&iter, cursor_priv->gpu_states);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cursor_gpu_state))
- {
- unsigned int pending_buffer_idx;
-
- pending_buffer_idx = get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
- g_clear_object (&cursor_gpu_state->buffers[pending_buffer_idx]);
- cursor_gpu_state->pending_buffer_state =
- META_CURSOR_BUFFER_STATE_INVALIDATED;
- }
+ g_clear_object (&cursor_gpu_state->buffer);
}
static void
@@ -1423,35 +1343,7 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
return;
}
- set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms, buffer);
-}
-
-static gboolean
-is_cursor_hw_state_valid (MetaCursorSprite *cursor_sprite,
- MetaGpuKms *gpu_kms)
-{
- MetaCursorNativePrivate *cursor_priv;
- MetaCursorNativeGpuState *cursor_gpu_state;
-
- cursor_priv = get_cursor_priv (cursor_sprite);
- if (!cursor_priv)
- return FALSE;
-
- cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms);
- if (!cursor_gpu_state)
- return FALSE;
-
- switch (cursor_gpu_state->pending_buffer_state)
- {
- case META_CURSOR_BUFFER_STATE_SET:
- case META_CURSOR_BUFFER_STATE_NONE:
- return TRUE;
- case META_CURSOR_BUFFER_STATE_INVALIDATED:
- return FALSE;
- }
-
- g_assert_not_reached ();
- return FALSE;
+ set_cursor_sprite_buffer (cursor_sprite, gpu_kms, buffer);
}
static gboolean
@@ -1638,7 +1530,7 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer,
if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken)
return;
- if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) &&
+ if (has_valid_cursor_sprite_buffer (cursor_sprite, gpu_kms) &&
is_cursor_scale_and_transform_valid (renderer, cursor_sprite))
return;
@@ -1783,8 +1675,8 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer,
return;
}
- set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms,
- META_DRM_BUFFER (buffer_gbm));
+ set_cursor_sprite_buffer (cursor_sprite, gpu_kms,
+ META_DRM_BUFFER (buffer_gbm));
}
}
#endif
@@ -1808,7 +1700,7 @@ realize_cursor_sprite_from_xcursor_for_gpu (MetaCursorRenderer *renderer,
if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken)
return;
- if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) &&
+ if (has_valid_cursor_sprite_buffer (cursor_sprite, gpu_kms) &&
is_cursor_scale_and_transform_valid (renderer, cursor_sprite))
return;
diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c
index e5405428c..48e864ca8 100644
--- a/src/backends/native/meta-kms-crtc.c
+++ b/src/backends/native/meta-kms-crtc.c
@@ -46,6 +46,8 @@ struct _MetaKmsCrtc
MetaKmsCrtcState current_state;
MetaKmsCrtcPropTable prop_table;
+
+ MetaSwapChain *swap_chain;
};
G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT)
@@ -97,6 +99,12 @@ meta_kms_crtc_get_prop_drm_value (MetaKmsCrtc *crtc,
return meta_kms_prop_convert_value (prop, value);
}
+MetaSwapChain *
+meta_kms_crtc_get_swap_chain (MetaKmsCrtc *crtc)
+{
+ return crtc->swap_chain;
+}
+
gboolean
meta_kms_crtc_is_active (MetaKmsCrtc *crtc)
{
@@ -463,12 +471,23 @@ meta_kms_crtc_new (MetaKmsImplDevice *impl_device,
return crtc;
}
+static void
+meta_kms_crtc_dispose (GObject *object)
+{
+ MetaKmsCrtc *crtc = META_KMS_CRTC (object);
+
+ meta_swap_chain_release_buffers (crtc->swap_chain);
+
+ G_OBJECT_CLASS (meta_kms_crtc_parent_class)->dispose (object);
+}
+
static void
meta_kms_crtc_finalize (GObject *object)
{
MetaKmsCrtc *crtc = META_KMS_CRTC (object);
g_clear_pointer (&crtc->current_state.gamma.value, meta_gamma_lut_free);
+ g_clear_object (&crtc->swap_chain);
G_OBJECT_CLASS (meta_kms_crtc_parent_class)->finalize (object);
}
@@ -478,6 +497,7 @@ meta_kms_crtc_init (MetaKmsCrtc *crtc)
{
crtc->current_state.gamma.size = 0;
crtc->current_state.gamma.value = NULL;
+ crtc->swap_chain = meta_swap_chain_new ();
}
static void
@@ -485,5 +505,6 @@ meta_kms_crtc_class_init (MetaKmsCrtcClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = meta_kms_crtc_dispose;
object_class->finalize = meta_kms_crtc_finalize;
}
diff --git a/src/backends/native/meta-kms-crtc.h b/src/backends/native/meta-kms-crtc.h
index 25fb71edb..61013bebd 100644
--- a/src/backends/native/meta-kms-crtc.h
+++ b/src/backends/native/meta-kms-crtc.h
@@ -25,6 +25,7 @@
#include <xf86drmMode.h>
#include "backends/native/meta-kms-types.h"
+#include "backends/native/meta-swap-chain.h"
#include "backends/meta-backend-types.h"
#include "core/util-private.h"
#include "meta/boxes.h"
@@ -64,4 +65,6 @@ int meta_kms_crtc_get_idx (MetaKmsCrtc *crtc);
META_EXPORT_TEST
gboolean meta_kms_crtc_is_active (MetaKmsCrtc *crtc);
+MetaSwapChain * meta_kms_crtc_get_swap_chain (MetaKmsCrtc *crtc);
+
#endif /* META_KMS_CRTC_H */
diff --git a/src/backends/native/meta-kms-impl-device-atomic.c b/src/backends/native/meta-kms-impl-device-atomic.c
index 83b217274..5024b876d 100644
--- a/src/backends/native/meta-kms-impl-device-atomic.c
+++ b/src/backends/native/meta-kms-impl-device-atomic.c
@@ -507,6 +507,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
{
MetaKmsPlaneAssignment *plane_assignment = update_entry;
MetaKmsPlane *plane = plane_assignment->plane;
+ MetaKmsUpdateFlag flags = (MetaKmsUpdateFlag) user_data;
MetaDrmBuffer *buffer;
MetaKmsFbDamage *fb_damage;
uint32_t prop_id;
@@ -659,6 +660,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
error))
return FALSE;
}
+
+ if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY))
+ meta_swap_chain_push_buffer (meta_kms_crtc_get_swap_chain (plane_assignment->crtc),
+ meta_kms_plane_get_id (plane),
+ G_OBJECT (buffer));
+
return TRUE;
}
@@ -1002,7 +1009,7 @@ meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device,
req,
blob_ids,
meta_kms_update_get_plane_assignments (update),
- NULL,
+ GUINT_TO_POINTER (flags),
process_plane_assignment,
&error))
goto err;
diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c
index 341d54cc7..c739788ae 100644
--- a/src/backends/native/meta-kms-impl-device-simple.c
+++ b/src/backends/native/meta-kms-impl-device-simple.c
@@ -486,6 +486,8 @@ process_mode_set (MetaKmsImplDevice *impl_device,
return FALSE;
}
+ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (crtc));
+
if (drm_mode)
{
g_hash_table_replace (impl_device_simple->cached_mode_sets,
@@ -555,7 +557,7 @@ is_timestamp_earlier_than (uint64_t ts1,
typedef struct _RetryPageFlipData
{
MetaKmsCrtc *crtc;
- uint32_t fb_id;
+ MetaDrmBuffer *fb;
MetaKmsPageFlipData *page_flip_data;
float refresh_rate;
uint64_t retry_time_us;
@@ -568,6 +570,7 @@ retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data)
g_assert (!retry_page_flip_data->page_flip_data);
g_clear_pointer (&retry_page_flip_data->custom_page_flip,
meta_kms_custom_page_flip_free);
+ g_clear_object (&retry_page_flip_data->fb);
g_free (retry_page_flip_data);
}
@@ -635,16 +638,21 @@ retry_page_flips (gpointer user_data)
}
else
{
+ uint32_t fb_id =
+ retry_page_flip_data->fb ?
+ meta_drm_buffer_get_fb_id (retry_page_flip_data->fb) :
+ 0;
+
meta_topic (META_DEBUG_KMS,
"[simple] Retrying page flip on CRTC %u (%s) with %u",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device),
- retry_page_flip_data->fb_id);
+ fb_id);
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModePageFlip (fd,
meta_kms_crtc_get_id (crtc),
- retry_page_flip_data->fb_id,
+ fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
retry_page_flip_data->page_flip_data);
}
@@ -731,7 +739,7 @@ retry_page_flips (gpointer user_data)
static void
schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple,
MetaKmsCrtc *crtc,
- uint32_t fb_id,
+ MetaDrmBuffer *fb,
float refresh_rate,
MetaKmsPageFlipData *page_flip_data,
MetaKmsCustomPageFlip *custom_page_flip)
@@ -746,7 +754,7 @@ schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple,
retry_page_flip_data = g_new0 (RetryPageFlipData, 1);
*retry_page_flip_data = (RetryPageFlipData) {
.crtc = crtc,
- .fb_id = fb_id,
+ .fb = fb ? g_object_ref (fb) : NULL,
.page_flip_data = page_flip_data,
.refresh_rate = refresh_rate,
.retry_time_us = retry_time_us,
@@ -880,6 +888,8 @@ mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple,
return FALSE;
}
+ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (crtc));
+
if (!impl_device_simple->mode_set_fallback_feedback_source)
{
GSource *source;
@@ -1004,20 +1014,20 @@ dispatch_page_flip (MetaKmsImplDevice *impl_device,
cached_mode_set = get_cached_mode_set (impl_device_simple, crtc);
if (cached_mode_set)
{
- uint32_t fb_id;
+ MetaDrmBuffer *fb;
drmModeModeInfo *drm_mode;
float refresh_rate;
if (plane_assignment)
- fb_id = meta_drm_buffer_get_fb_id (plane_assignment->buffer);
+ fb = plane_assignment->buffer;
else
- fb_id = 0;
+ fb = NULL;
drm_mode = cached_mode_set->drm_mode;
refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
meta_kms_impl_device_hold_fd (impl_device);
schedule_retry_page_flip (impl_device_simple,
crtc,
- fb_id,
+ fb,
refresh_rate,
page_flip_data,
g_steal_pointer (&custom_page_flip));
@@ -1298,7 +1308,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
{
case META_KMS_PLANE_TYPE_PRIMARY:
/* Handled as part of the mode-set and page flip. */
- return TRUE;
+ goto assigned;
case META_KMS_PLANE_TYPE_CURSOR:
if (!process_cursor_plane_assignment (impl_device, update,
plane_assignment,
@@ -1312,7 +1322,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
}
else
{
- return TRUE;
+ goto assigned;
}
case META_KMS_PLANE_TYPE_OVERLAY:
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -1325,6 +1335,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
}
g_assert_not_reached ();
+
+assigned:
+ meta_swap_chain_push_buffer (meta_kms_crtc_get_swap_chain (plane_assignment->crtc),
+ meta_kms_plane_get_id (plane),
+ G_OBJECT (plane_assignment->buffer));
+ return TRUE;
}
static gboolean
diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c
index d56636d55..b7a0ddd24 100644
--- a/src/backends/native/meta-kms-impl-device.c
+++ b/src/backends/native/meta-kms-impl-device.c
@@ -1259,11 +1259,25 @@ meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device,
return TRUE;
}
+static void
+release_buffers (gpointer data,
+ gpointer user_data)
+{
+ MetaKmsCrtc *crtc = data;
+ MetaSwapChain *swap_chain = meta_kms_crtc_get_swap_chain (crtc);
+
+ meta_swap_chain_release_buffers (swap_chain);
+}
+
void
meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device)
{
+ MetaKmsImplDevicePrivate *priv =
+ meta_kms_impl_device_get_instance_private (impl_device);
MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
+ g_list_foreach (priv->crtcs, release_buffers, NULL);
+
if (klass->prepare_shutdown)
klass->prepare_shutdown (impl_device);
diff --git a/src/backends/native/meta-kms-page-flip.c b/src/backends/native/meta-kms-page-flip.c
index c1c29905c..09c2d8fad 100644
--- a/src/backends/native/meta-kms-page-flip.c
+++ b/src/backends/native/meta-kms-page-flip.c
@@ -25,6 +25,7 @@
#include "backends/native/meta-kms-impl.h"
#include "backends/native/meta-kms-private.h"
#include "backends/native/meta-kms-update.h"
+#include "backends/native/meta-kms-crtc.h"
typedef struct _MetaKmsPageFlipClosure
{
@@ -150,6 +151,8 @@ meta_kms_page_flip_data_flipped (MetaKms *kms,
meta_assert_not_in_kms_impl (kms);
+ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (page_flip_data->crtc));
+
for (l = page_flip_data->closures; l; l = l->next)
{
MetaKmsPageFlipClosure *closure = l->data;
diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c
index ceb298a80..02d9d0159 100644
--- a/src/backends/native/meta-kms-update.c
+++ b/src/backends/native/meta-kms-update.c
@@ -183,6 +183,7 @@ static void
meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment)
{
g_clear_pointer (&plane_assignment->fb_damage, meta_kms_fb_damage_free);
+ g_clear_object (&plane_assignment->buffer);
g_free (plane_assignment);
}
@@ -265,7 +266,7 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update,
.update = update,
.crtc = crtc,
.plane = plane,
- .buffer = buffer,
+ .buffer = g_object_ref (buffer),
.src_rect = src_rect,
.dst_rect = dst_rect,
.flags = flags,
diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c
index d3a840b31..ce6fb4337 100644
--- a/src/backends/native/meta-kms.c
+++ b/src/backends/native/meta-kms.c
@@ -177,6 +177,8 @@ struct _MetaKms
GList *pending_callbacks;
guint callback_source_id;
+
+ gboolean shutting_down;
};
G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT)
@@ -599,6 +601,7 @@ static void
on_prepare_shutdown (MetaBackend *backend,
MetaKms *kms)
{
+ kms->shutting_down = TRUE;
meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL);
flush_callbacks (kms);
}
@@ -639,6 +642,12 @@ meta_kms_new (MetaBackend *backend,
return kms;
}
+gboolean
+meta_kms_is_shutting_down (MetaKms *kms)
+{
+ return kms->shutting_down;
+}
+
static void
meta_kms_finalize (GObject *object)
{
diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h
index fe4fef1a0..88421ed28 100644
--- a/src/backends/native/meta-kms.h
+++ b/src/backends/native/meta-kms.h
@@ -51,6 +51,8 @@ MetaKmsDevice * meta_kms_create_device (MetaKms *kms,
MetaKmsDeviceFlag flags,
GError **error);
+gboolean meta_kms_is_shutting_down (MetaKms *kms);
+
MetaKms * meta_kms_new (MetaBackend *backend,
MetaKmsFlags flags,
GError **error);
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index 1ab117eb9..ffe95f8da 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -72,7 +72,7 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState
struct {
MetaDrmBufferDumb *current_dumb_fb;
- MetaDrmBufferDumb *dumb_fbs[2];
+ MetaDrmBufferDumb *dumb_fbs[3];
} cpu;
gboolean noted_primary_gpu_copy_ok;
@@ -93,8 +93,13 @@ struct _MetaOnscreenNative
struct {
struct gbm_surface *surface;
- MetaDrmBuffer *current_fb;
MetaDrmBuffer *next_fb;
+ MetaDrmBuffer *stalled_fb;
+
+ /* Temporary workaround for the scanout-failed signal wanting the buffer
+ * to live longer than it does, and then it doesn't use it anyway...
+ */
+ MetaDrmBuffer *direct_fb;
} gbm;
#ifdef HAVE_EGL_DEVICE
@@ -116,6 +121,14 @@ struct _MetaOnscreenNative
gulong privacy_screen_changed_handler_id;
gulong color_space_changed_handler_id;
gulong hdr_metadata_changed_handler_id;
+
+ unsigned int swaps_pending;
+
+ struct {
+ int *rectangles; /* 4 x n_rectangles */
+ int n_rectangles;
+ ClutterFrame *frame;
+ } next_post;
};
G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native,
@@ -123,40 +136,17 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native,
static GQuark blit_source_quark = 0;
-static gboolean
-init_secondary_gpu_state (MetaRendererNative *renderer_native,
- CoglOnscreen *onscreen,
- GError **error);
-
static void
-free_current_bo (CoglOnscreen *onscreen)
-{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
-
- g_clear_object (&onscreen_native->gbm.current_fb);
-}
-
-static void
-meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
-{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
-
- if (!onscreen_native->gbm.next_fb)
- return;
-
- free_current_bo (onscreen);
-
- g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb);
- g_clear_object (&onscreen_native->gbm.next_fb);
-}
+try_post_latest_swap (CoglOnscreen *onscreen);
static void
-meta_onscreen_native_clear_next_fb (CoglOnscreen *onscreen)
-{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
+post_finish_frame (MetaOnscreenNative *onscreen_native,
+ MetaKmsUpdate *kms_update);
- g_clear_object (&onscreen_native->gbm.next_fb);
-}
+static gboolean
+init_secondary_gpu_state (MetaRendererNative *renderer_native,
+ CoglOnscreen *onscreen,
+ GError **error);
static void
maybe_update_frame_info (MetaCrtc *crtc,
@@ -193,7 +183,7 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen)
info = cogl_onscreen_pop_head_frame_info (onscreen);
- g_assert (!cogl_onscreen_peek_head_frame_info (onscreen));
+ g_assert (info);
_cogl_onscreen_notify_frame_sync (onscreen, info);
_cogl_onscreen_notify_complete (onscreen, info);
@@ -228,7 +218,7 @@ notify_view_crtc_presented (MetaRendererView *view,
maybe_update_frame_info (crtc, frame_info, time_us, flags, sequence);
meta_onscreen_native_notify_frame_complete (onscreen);
- meta_onscreen_native_swap_drm_fb (onscreen);
+ try_post_latest_swap (onscreen);
}
static int64_t
@@ -284,15 +274,13 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc,
CoglFramebuffer *framebuffer =
clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view));
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
CoglFrameInfo *frame_info;
frame_info = cogl_onscreen_peek_head_frame_info (onscreen);
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
- g_warn_if_fail (!onscreen_native->gbm.next_fb);
-
meta_onscreen_native_notify_frame_complete (onscreen);
+ try_post_latest_swap (onscreen);
}
static void
@@ -342,7 +330,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc,
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
meta_onscreen_native_notify_frame_complete (onscreen);
- meta_onscreen_native_clear_next_fb (onscreen);
+ try_post_latest_swap (onscreen);
}
static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = {
@@ -403,18 +391,40 @@ custom_egl_stream_page_flip (gpointer custom_page_flip_data,
}
#endif /* HAVE_EGL_DEVICE */
-void
-meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen)
+static void
+drop_stalled_swap (CoglOnscreen *onscreen)
{
CoglFrameInfo *frame_info;
+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
+
+ /* Remember we can't compare stalled_fb because it's not used by
+ * META_RENDERER_NATIVE_MODE_EGL_DEVICE. So we judge stalled to be whenever
+ * swaps_pending > 1.
+ */
+ if (onscreen_native->swaps_pending <= 1)
+ return;
+
+ onscreen_native->swaps_pending--;
- meta_onscreen_native_swap_drm_fb (onscreen);
+ g_clear_object (&onscreen_native->gbm.stalled_fb);
frame_info = cogl_onscreen_peek_tail_frame_info (onscreen);
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
meta_onscreen_native_notify_frame_complete (onscreen);
}
+void
+meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen)
+{
+ drop_stalled_swap (onscreen);
+
+ /* If the monitor just woke up and the shell is fully idle (has nothing
+ * more to swap) then we just woke to an indefinitely black screen. Let's
+ * fix that using the last swap (which is never classified as "stalled").
+ */
+ try_post_latest_swap (onscreen);
+}
+
static void
meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
MetaRendererView *view,
@@ -431,7 +441,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
MetaRendererNativeGpuData *renderer_gpu_data;
MetaGpuKms *gpu_kms;
- MetaDrmBuffer *buffer;
+ g_autoptr (MetaDrmBuffer) buffer = NULL;
MetaKmsPlaneAssignment *plane_assignment;
COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs,
@@ -446,7 +456,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
- buffer = onscreen_native->gbm.next_fb;
+ buffer = g_steal_pointer (&onscreen_native->gbm.next_fb);
plane_assignment = meta_crtc_kms_assign_primary_plane (crtc_kms,
buffer,
@@ -457,6 +467,11 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
meta_kms_plane_assignment_set_fb_damage (plane_assignment,
rectangles, n_rectangles);
}
+
+ g_object_set_data_full (G_OBJECT (buffer),
+ "gbm_surface owner",
+ g_object_ref (onscreen),
+ (GDestroyNotify) g_object_unref);
break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
@@ -698,12 +713,17 @@ static MetaDrmBufferDumb *
secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
{
MetaDrmBufferDumb *current_dumb_fb;
+ const int n_dumb_fbs = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs);
+ int i;
current_dumb_fb = secondary_gpu_state->cpu.current_dumb_fb;
- if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[0])
- return secondary_gpu_state->cpu.dumb_fbs[1];
- else
- return secondary_gpu_state->cpu.dumb_fbs[0];
+ for (i = 0; i < n_dumb_fbs; i++)
+ {
+ if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i])
+ return secondary_gpu_state->cpu.dumb_fbs[(i + 1) % n_dumb_fbs];
+ }
+
+ return secondary_gpu_state->cpu.dumb_fbs[0];
}
static MetaDrmBuffer *
@@ -1038,10 +1058,15 @@ on_swap_buffer_update_result (const MetaKmsFeedback *kms_feedback,
g_warning ("Page flip failed: %s", error->message);
frame_info = cogl_onscreen_peek_head_frame_info (onscreen);
- frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
- meta_onscreen_native_notify_frame_complete (onscreen);
- meta_onscreen_native_clear_next_fb (onscreen);
+ /* After resuming from suspend, drop_stalled_swap might have done this
+ * already and emptied the frame_info queue.
+ */
+ if (frame_info)
+ {
+ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
+ meta_onscreen_native_notify_frame_complete (onscreen);
+ }
}
static void
@@ -1058,31 +1083,35 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
- MetaRenderer *renderer = META_RENDERER (renderer_native);
- MetaBackend *backend = meta_renderer_get_backend (renderer);
- MetaMonitorManager *monitor_manager =
- meta_backend_get_monitor_manager (backend);
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
MetaDeviceFile *render_device_file;
ClutterFrame *frame = user_data;
- MetaFrameNative *frame_native = meta_frame_native_from_frame (frame);
- MetaKmsUpdate *kms_update;
CoglOnscreenClass *parent_class;
gboolean egl_context_changed = FALSE;
- MetaPowerSave power_save_mode;
g_autoptr (GError) error = NULL;
MetaDrmBufferFlags buffer_flags;
MetaDrmBufferGbm *buffer_gbm;
g_autoptr (MetaDrmBuffer) primary_gpu_fb = NULL;
g_autoptr (MetaDrmBuffer) secondary_gpu_fb = NULL;
- MetaKmsCrtc *kms_crtc;
- MetaKmsDevice *kms_device;
- g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
+ size_t rectangles_size;
COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers,
"Onscreen (swap-buffers)");
+ if (meta_is_topic_enabled (META_DEBUG_KMS))
+ {
+ unsigned int frames_pending =
+ cogl_onscreen_count_pending_frames (onscreen);
+
+ meta_topic (META_DEBUG_KMS,
+ "Swap buffers: %u frames pending (%s-buffering)",
+ frames_pending,
+ frames_pending == 1 ? "double" :
+ frames_pending == 2 ? "triple" :
+ "?");
+ }
+
secondary_gpu_fb =
update_secondary_gpu_state_pre_swap_buffers (onscreen,
rectangles,
@@ -1138,7 +1167,15 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
- g_warn_if_fail (onscreen_native->gbm.next_fb == NULL);
+ if (onscreen_native->gbm.next_fb != NULL)
+ {
+ g_warn_if_fail (onscreen_native->gbm.stalled_fb == NULL);
+ drop_stalled_swap (onscreen);
+ g_assert (onscreen_native->gbm.stalled_fb == NULL);
+ onscreen_native->gbm.stalled_fb =
+ g_steal_pointer (&onscreen_native->gbm.next_fb);
+ }
+
if (onscreen_native->secondary_gpu_state)
g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb);
else
@@ -1152,6 +1189,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
#endif
}
+ clutter_frame_set_result (frame,
+ CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
+
/*
* If we changed EGL context, cogl will have the wrong idea about what is
* current, making it fail to set it when it needs to. Avoid that by making
@@ -1161,12 +1201,83 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
if (egl_context_changed)
_cogl_winsys_egl_ensure_current (cogl_display);
- kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc));
- kms_device = meta_kms_crtc_get_device (kms_crtc);
+ rectangles_size = n_rectangles * 4 * sizeof (int);
+ onscreen_native->next_post.rectangles =
+ g_realloc (onscreen_native->next_post.rectangles, rectangles_size);
+ memcpy (onscreen_native->next_post.rectangles, rectangles, rectangles_size);
+ onscreen_native->next_post.n_rectangles = n_rectangles;
+
+ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref);
+ onscreen_native->next_post.frame = clutter_frame_ref (frame);
+
+ onscreen_native->swaps_pending++;
+ try_post_latest_swap (onscreen);
+}
+
+static void
+try_post_latest_swap (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer);
+ CoglRenderer *cogl_renderer = cogl_context->display->renderer;
+ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
+ MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
+ MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
+ MetaRenderer *renderer = META_RENDERER (renderer_native);
+ MetaBackend *backend = meta_renderer_get_backend (renderer);
+ MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
+ MetaKms *kms = meta_backend_native_get_kms (backend_native);
+ MetaMonitorManager *monitor_manager =
+ meta_backend_get_monitor_manager (backend);
+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
+ MetaPowerSave power_save_mode;
+ MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc);
+ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
+ MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc);
+ MetaKmsUpdate *kms_update;
+ g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
+ g_autoptr (ClutterFrame) frame = NULL;
+ MetaFrameNative *frame_native;
+
+ if (onscreen_native->next_post.frame == NULL ||
+ onscreen_native->view == NULL)
+ return;
+
+ if (meta_kms_is_shutting_down (kms))
+ {
+ meta_onscreen_native_discard_pending_swaps (onscreen);
+ return;
+ }
power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager);
if (power_save_mode == META_POWER_SAVE_ON)
{
+ unsigned int frames_pending =
+ cogl_onscreen_count_pending_frames (onscreen);
+ unsigned int posts_pending;
+
+ g_assert (frames_pending >= onscreen_native->swaps_pending);
+ posts_pending = frames_pending - onscreen_native->swaps_pending;
+ if (posts_pending > 0)
+ return; /* wait for the next frame notification and then try again */
+
+ frame = g_steal_pointer (&onscreen_native->next_post.frame);
+ frame_native = meta_frame_native_from_frame (frame);
+
+ if (onscreen_native->swaps_pending == 0)
+ {
+ if (frame_native)
+ {
+ kms_update = meta_frame_native_steal_kms_update (frame_native);
+ if (kms_update)
+ post_finish_frame (onscreen_native, kms_update);
+ }
+ return;
+ }
+
+ drop_stalled_swap (onscreen);
+ onscreen_native->swaps_pending--;
+
kms_update = meta_frame_native_ensure_kms_update (frame_native,
kms_device);
meta_kms_update_add_result_listener (kms_update,
@@ -1179,15 +1290,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
onscreen_native->crtc,
kms_update,
META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE,
- rectangles,
- n_rectangles);
+ onscreen_native->next_post.rectangles,
+ onscreen_native->next_post.n_rectangles);
}
else
{
meta_renderer_native_queue_power_save_page_flip (renderer_native,
onscreen);
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
return;
}
@@ -1207,8 +1316,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
kms_update = meta_frame_native_steal_kms_update (frame_native);
meta_renderer_native_queue_mode_set_update (renderer_native,
kms_update);
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
return;
}
else if (meta_renderer_native_has_pending_mode_set (renderer_native))
@@ -1222,8 +1329,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
meta_frame_native_steal_kms_update (frame_native);
meta_renderer_native_post_mode_set_updates (renderer_native);
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
return;
}
break;
@@ -1239,8 +1344,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
kms_update);
meta_renderer_native_post_mode_set_updates (renderer_native);
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
return;
}
break;
@@ -1256,7 +1359,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
kms_feedback =
meta_kms_device_process_update_sync (kms_device, kms_update,
META_KMS_UPDATE_FLAG_NONE);
- clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
}
gboolean
@@ -1301,6 +1403,7 @@ on_scanout_update_result (const MetaKmsFeedback *kms_feedback,
CoglOnscreen *onscreen = COGL_ONSCREEN (onscreen_native);
const GError *error;
CoglFrameInfo *frame_info;
+ g_autoptr (MetaDrmBuffer) direct_fb = g_steal_pointer (&onscreen_native->gbm.direct_fb);
error = meta_kms_feedback_get_error (kms_feedback);
if (!error)
@@ -1314,8 +1417,7 @@ on_scanout_update_result (const MetaKmsFeedback *kms_feedback,
g_warning ("Direct scanout page flip failed: %s", error->message);
- cogl_scanout_notify_failed (COGL_SCANOUT (onscreen_native->gbm.next_fb),
- onscreen);
+ cogl_scanout_notify_failed (COGL_SCANOUT (direct_fb), onscreen);
clutter_stage_view_add_redraw_clip (view, NULL);
clutter_stage_view_schedule_update_now (view);
}
@@ -1324,7 +1426,6 @@ on_scanout_update_result (const MetaKmsFeedback *kms_feedback,
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
meta_onscreen_native_notify_frame_complete (onscreen);
- meta_onscreen_native_clear_next_fb (onscreen);
}
static gboolean
@@ -1375,6 +1476,18 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
return FALSE;
}
+ /* Our direct scanout frame counts as 1, so more than that means we would
+ * be jumping the queue (and post would fail).
+ */
+ if (cogl_onscreen_count_pending_frames (onscreen) > 1)
+ {
+ g_set_error_literal (error,
+ COGL_SCANOUT_ERROR,
+ COGL_SCANOUT_ERROR_INHIBITED,
+ "Direct scanout is inhibited during triple buffering");
+ return FALSE;
+ }
+
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
render_gpu);
@@ -1414,6 +1527,8 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
kms_device = meta_kms_crtc_get_device (kms_crtc);
kms_update = meta_frame_native_ensure_kms_update (frame_native, kms_device);
+ g_set_object (&onscreen_native->gbm.direct_fb,
+ onscreen_native->gbm.next_fb);
meta_kms_update_add_result_listener (kms_update,
on_scanout_update_result,
onscreen_native);
@@ -1558,7 +1673,20 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc);
MetaFrameNative *frame_native = meta_frame_native_from_frame (frame);
MetaKmsUpdate *kms_update;
- g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
+
+ if (meta_frame_native_has_kms_update (frame_native) &&
+ cogl_onscreen_count_pending_frames (onscreen) > 0 &&
+ onscreen_native->swaps_pending == 0)
+ {
+ /* Posts are busy AND we have no swaps pending so just retry the same
+ * frame later in try_post_latest_swap.
+ */
+ g_warn_if_fail (onscreen_native->next_post.frame == NULL);
+ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref);
+ onscreen_native->next_post.frame = clutter_frame_ref (frame);
+ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
+ return;
+ }
kms_update = meta_frame_native_steal_kms_update (frame_native);
if (!kms_update)
@@ -1567,6 +1695,42 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
return;
}
+ if (cogl_onscreen_count_pending_frames (onscreen) > 0)
+ {
+ /* Posts are busy AND we have swaps pending so best to merge our cursor
+ * update into the pending swap.
+ */
+ MetaFrameNative *older_frame_native;
+ MetaKmsUpdate *older_kms_update;
+
+ g_return_if_fail (onscreen_native->swaps_pending > 0);
+ g_return_if_fail (onscreen_native->next_post.frame != NULL);
+
+ older_frame_native =
+ meta_frame_native_from_frame (onscreen_native->next_post.frame);
+ older_kms_update =
+ meta_frame_native_ensure_kms_update (older_frame_native, kms_device);
+ meta_kms_update_merge_from (older_kms_update, kms_update);
+ meta_kms_update_free (kms_update);
+
+ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE);
+ return;
+ }
+
+ post_finish_frame (onscreen_native, kms_update);
+
+ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
+}
+
+static void
+post_finish_frame (MetaOnscreenNative *onscreen_native,
+ MetaKmsUpdate *kms_update)
+{
+ MetaCrtc *crtc = onscreen_native->crtc;
+ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc));
+ MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc);
+ g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
+
meta_kms_update_add_result_listener (kms_update,
on_finish_frame_update_result,
onscreen_native);
@@ -1587,7 +1751,17 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
kms_feedback =
meta_kms_device_process_update_sync (kms_device, kms_update,
META_KMS_UPDATE_FLAG_NONE);
- clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
+}
+
+void
+meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen)
+{
+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
+
+ onscreen_native->swaps_pending = 0;
+
+ g_clear_object (&onscreen_native->gbm.stalled_fb);
+ g_clear_object (&onscreen_native->gbm.next_fb);
}
static gboolean
@@ -2414,7 +2588,7 @@ meta_onscreen_native_dispose (GObject *object)
{
case META_RENDERER_NATIVE_MODE_GBM:
g_clear_object (&onscreen_native->gbm.next_fb);
- free_current_bo (onscreen);
+ g_clear_object (&onscreen_native->gbm.direct_fb);
break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
@@ -2448,6 +2622,10 @@ meta_onscreen_native_dispose (GObject *object)
g_clear_object (&onscreen_native->output);
g_clear_object (&onscreen_native->crtc);
+
+ g_clear_pointer (&onscreen_native->next_post.rectangles, g_free);
+ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref);
+ onscreen_native->next_post.n_rectangles = 0;
}
static void
@@ -2483,4 +2661,5 @@ void
meta_onscreen_native_detach (MetaOnscreenNative *onscreen_native)
{
clear_invalidation_handlers (onscreen_native);
+ onscreen_native->view = NULL;
}
diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h
index 05a9ecdb8..a33ee3857 100644
--- a/src/backends/native/meta-onscreen-native.h
+++ b/src/backends/native/meta-onscreen-native.h
@@ -45,6 +45,8 @@ void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen);
+void meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen);
+
gboolean meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen,
MetaDrmBuffer *fb);
diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
index 4da361e2b..e5d995388 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -708,12 +708,18 @@ static gboolean
dummy_power_save_page_flip_cb (gpointer user_data)
{
MetaRendererNative *renderer_native = user_data;
+ GList *old_list =
+ g_steal_pointer (&renderer_native->power_save_page_flip_onscreens);
- g_list_foreach (renderer_native->power_save_page_flip_onscreens,
+ g_list_foreach (old_list,
(GFunc) meta_onscreen_native_dummy_power_save_page_flip,
NULL);
- g_clear_list (&renderer_native->power_save_page_flip_onscreens,
+ g_clear_list (&old_list,
g_object_unref);
+
+ if (renderer_native->power_save_page_flip_onscreens != NULL)
+ return G_SOURCE_CONTINUE;
+
renderer_native->power_save_page_flip_source_id = 0;
return G_SOURCE_REMOVE;
@@ -725,6 +731,9 @@ meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_na
{
const unsigned int timeout_ms = 100;
+ if (g_list_find (renderer_native->power_save_page_flip_onscreens, onscreen))
+ return;
+
if (!renderer_native->power_save_page_flip_source_id)
{
renderer_native->power_save_page_flip_source_id =
@@ -1473,6 +1482,26 @@ detach_onscreens (MetaRenderer *renderer)
}
}
+static void
+discard_pending_swaps (MetaRenderer *renderer)
+{
+ GList *views = meta_renderer_get_views (renderer);;
+ GList *l;
+
+ for (l = views; l; l = l->next)
+ {
+ ClutterStageView *stage_view = l->data;
+ CoglFramebuffer *fb = clutter_stage_view_get_onscreen (stage_view);
+ CoglOnscreen *onscreen;
+
+ if (!COGL_IS_ONSCREEN (fb))
+ continue;
+
+ onscreen = COGL_ONSCREEN (fb);
+ meta_onscreen_native_discard_pending_swaps (onscreen);
+ }
+}
+
static void
meta_renderer_native_rebuild_views (MetaRenderer *renderer)
{
@@ -1483,6 +1512,7 @@ meta_renderer_native_rebuild_views (MetaRenderer *renderer)
MetaRendererClass *parent_renderer_class =
META_RENDERER_CLASS (meta_renderer_native_parent_class);
+ discard_pending_swaps (renderer);
meta_kms_discard_pending_page_flips (kms);
g_hash_table_remove_all (renderer_native->mode_set_updates);
diff --git a/src/backends/native/meta-swap-chain.c b/src/backends/native/meta-swap-chain.c
new file mode 100644
index 000000000..c3bed569d
--- /dev/null
+++ b/src/backends/native/meta-swap-chain.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
+ */
+
+#include "backends/native/meta-swap-chain.h"
+
+typedef struct
+{
+ GObject *front, *back;
+ gboolean back_is_set;
+} PlaneState;
+
+typedef struct _MetaSwapChainPrivate MetaSwapChainPrivate;
+struct _MetaSwapChainPrivate
+{
+ GHashTable *plane_states;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (MetaSwapChain, meta_swap_chain, G_TYPE_OBJECT)
+
+MetaSwapChain *
+meta_swap_chain_new (void)
+{
+ return g_object_new (META_TYPE_SWAP_CHAIN, NULL);
+}
+
+void
+meta_swap_chain_push_buffer (MetaSwapChain *swap_chain,
+ unsigned int plane_id,
+ GObject *buffer)
+{
+ MetaSwapChainPrivate *priv =
+ meta_swap_chain_get_instance_private (swap_chain);
+ gpointer key = GUINT_TO_POINTER (plane_id);
+ PlaneState *plane_state;
+
+ plane_state = g_hash_table_lookup (priv->plane_states, key);
+ if (plane_state == NULL)
+ {
+ plane_state = g_new0 (PlaneState, 1);
+ g_hash_table_insert (priv->plane_states, key, plane_state);
+ }
+
+ plane_state->back_is_set = TRUE; /* note buffer may be NULL */
+ g_set_object (&plane_state->back, buffer);
+}
+
+static void
+swap_plane_buffers (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ PlaneState *plane_state = value;
+
+ if (plane_state->back_is_set)
+ {
+ g_set_object (&plane_state->front, plane_state->back);
+ g_clear_object (&plane_state->back);
+ plane_state->back_is_set = FALSE;
+ }
+}
+
+void
+meta_swap_chain_swap_buffers (MetaSwapChain *swap_chain)
+{
+ MetaSwapChainPrivate *priv =
+ meta_swap_chain_get_instance_private (swap_chain);
+
+ g_hash_table_foreach (priv->plane_states, swap_plane_buffers, NULL);
+}
+
+void
+meta_swap_chain_release_buffers (MetaSwapChain *swap_chain)
+{
+ MetaSwapChainPrivate *priv =
+ meta_swap_chain_get_instance_private (swap_chain);
+
+ g_hash_table_remove_all (priv->plane_states);
+}
+
+static void
+meta_swap_chain_dispose (GObject *object)
+{
+ MetaSwapChain *swap_chain = META_SWAP_CHAIN (object);
+
+ meta_swap_chain_release_buffers (swap_chain);
+
+ G_OBJECT_CLASS (meta_swap_chain_parent_class)->dispose (object);
+}
+
+static void
+meta_swap_chain_finalize (GObject *object)
+{
+ MetaSwapChain *swap_chain = META_SWAP_CHAIN (object);
+ MetaSwapChainPrivate *priv =
+ meta_swap_chain_get_instance_private (swap_chain);
+
+ g_hash_table_unref (priv->plane_states);
+
+ G_OBJECT_CLASS (meta_swap_chain_parent_class)->finalize (object);
+}
+
+static void
+destroy_plane_state (gpointer data)
+{
+ PlaneState *plane_state = data;
+
+ g_clear_object (&plane_state->front);
+ g_clear_object (&plane_state->back);
+ g_free (plane_state);
+}
+
+static void
+meta_swap_chain_init (MetaSwapChain *swap_chain)
+{
+ MetaSwapChainPrivate *priv =
+ meta_swap_chain_get_instance_private (swap_chain);
+
+ priv->plane_states = g_hash_table_new_full (NULL,
+ NULL,
+ NULL,
+ destroy_plane_state);
+}
+
+static void
+meta_swap_chain_class_init (MetaSwapChainClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = meta_swap_chain_dispose;
+ object_class->finalize = meta_swap_chain_finalize;
+}
diff --git a/src/backends/native/meta-swap-chain.h b/src/backends/native/meta-swap-chain.h
new file mode 100644
index 000000000..bad772b89
--- /dev/null
+++ b/src/backends/native/meta-swap-chain.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
+ */
+
+#ifndef META_SWAP_CHAIN_H
+#define META_SWAP_CHAIN_H
+
+#include <glib-object.h>
+
+#define META_TYPE_SWAP_CHAIN (meta_swap_chain_get_type ())
+G_DECLARE_DERIVABLE_TYPE (MetaSwapChain,
+ meta_swap_chain,
+ META, SWAP_CHAIN,
+ GObject)
+
+struct _MetaSwapChainClass
+{
+ GObjectClass parent_class;
+};
+
+MetaSwapChain * meta_swap_chain_new (void);
+
+void meta_swap_chain_push_buffer (MetaSwapChain *swap_chain,
+ unsigned int plane_id,
+ GObject *buffer);
+
+void meta_swap_chain_swap_buffers (MetaSwapChain *swap_chain);
+
+void meta_swap_chain_release_buffers (MetaSwapChain *swap_chain);
+
+#endif /* META_SWAP_CHAIN_H */
diff --git a/src/meson.build b/src/meson.build
index 5e95e666f..5fc6b4e4a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -820,6 +820,8 @@ if have_native_backend
'backends/native/meta-seat-native.h',
'backends/native/meta-stage-native.c',
'backends/native/meta-stage-native.h',
+ 'backends/native/meta-swap-chain.c',
+ 'backends/native/meta-swap-chain.h',
'backends/native/meta-udev.c',
'backends/native/meta-udev.h',
'backends/native/meta-virtual-input-device-native.c',
diff --git a/src/tests/clutter/conform/event-delivery.c b/src/tests/clutter/conform/event-delivery.c
index 0f3ca256c..0c9fdf77b 100644
--- a/src/tests/clutter/conform/event-delivery.c
+++ b/src/tests/clutter/conform/event-delivery.c
@@ -326,6 +326,7 @@ event_delivery_implicit_grab_cancelled (void)
n_child_1_leave_events = n_child_2_leave_events = n_stage_leave_events = 0;
n_action_sequences_cancelled = 0;
clutter_actor_destroy (child_1);
+ wait_stage_updated (&was_updated);
g_assert_cmpint (n_child_1_enter_events, ==, 0);
g_assert_cmpint (n_child_1_leave_events, ==, 0);
g_assert_cmpint (n_action_sequences_cancelled, ==, 1);
diff --git a/src/tests/native-kms-render.c b/src/tests/native-kms-render.c
index 1557b764e..c482d07e0 100644
--- a/src/tests/native-kms-render.c
+++ b/src/tests/native-kms-render.c
@@ -41,6 +41,8 @@
#include "tests/meta-wayland-test-driver.h"
#include "tests/meta-wayland-test-utils.h"
+#define N_FRAMES_PER_TEST 30
+
typedef struct
{
int number_of_frames_left;
@@ -48,12 +50,15 @@ typedef struct
struct {
int n_paints;
- uint32_t fb_id;
+ int n_presentations;
+ int n_direct_scanouts;
+ GList *fb_ids;
} scanout;
gboolean wait_for_scanout;
struct {
+ int scanouts_attempted;
gboolean scanout_sabotaged;
gboolean fallback_painted;
guint repaint_guard_id;
@@ -103,7 +108,7 @@ meta_test_kms_render_basic (void)
gulong handler_id;
test = (KmsRenderingTest) {
- .number_of_frames_left = 10,
+ .number_of_frames_left = N_FRAMES_PER_TEST,
.loop = g_main_loop_new (NULL, FALSE),
};
handler_id = g_signal_connect (stage, "after-update",
@@ -125,7 +130,6 @@ on_scanout_before_update (ClutterStage *stage,
KmsRenderingTest *test)
{
test->scanout.n_paints = 0;
- test->scanout.fb_id = 0;
}
static void
@@ -136,6 +140,7 @@ on_scanout_before_paint (ClutterStage *stage,
{
CoglScanout *scanout;
MetaDrmBuffer *buffer;
+ uint32_t fb_id;
scanout = clutter_stage_view_peek_scanout (stage_view);
if (!scanout)
@@ -143,8 +148,14 @@ on_scanout_before_paint (ClutterStage *stage,
g_assert_true (META_IS_DRM_BUFFER (scanout));
buffer = META_DRM_BUFFER (scanout);
- test->scanout.fb_id = meta_drm_buffer_get_fb_id (buffer);
- g_assert_cmpuint (test->scanout.fb_id, >, 0);
+
+ fb_id = meta_drm_buffer_get_fb_id (buffer);
+ g_assert_cmpuint (fb_id, >, 0);
+ test->scanout.fb_ids = g_list_append (test->scanout.fb_ids,
+ GUINT_TO_POINTER (fb_id));
+
+ /* Triple buffering, but no higher */
+ g_assert_cmpuint (g_list_length (test->scanout.fb_ids), <=, 2);
}
static void
@@ -173,12 +184,12 @@ on_scanout_presented (ClutterStage *stage,
MetaDeviceFile *device_file;
GError *error = NULL;
drmModeCrtc *drm_crtc;
+ uint32_t first_fb_id_expected;
- if (test->wait_for_scanout && test->scanout.n_paints > 0)
+ if (test->wait_for_scanout && test->scanout.fb_ids == NULL)
return;
- if (test->wait_for_scanout && test->scanout.fb_id == 0)
- return;
+ test->scanout.n_presentations++;
device_pool = meta_backend_native_get_device_pool (backend_native);
@@ -197,15 +208,41 @@ on_scanout_presented (ClutterStage *stage,
drm_crtc = drmModeGetCrtc (meta_device_file_get_fd (device_file),
meta_kms_crtc_get_id (kms_crtc));
g_assert_nonnull (drm_crtc);
- if (test->scanout.fb_id == 0)
- g_assert_cmpuint (drm_crtc->buffer_id, !=, test->scanout.fb_id);
+
+ if (test->scanout.fb_ids)
+ {
+ test->scanout.n_direct_scanouts++;
+ first_fb_id_expected = GPOINTER_TO_UINT (test->scanout.fb_ids->data);
+ test->scanout.fb_ids = g_list_delete_link (test->scanout.fb_ids,
+ test->scanout.fb_ids);
+ }
else
- g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id);
+ {
+ first_fb_id_expected = 0;
+ }
+
+ /* The buffer ID won't match on the first frame because switching from
+ * triple buffered compositing to double buffered direct scanout takes
+ * an extra frame to drain the queue. Thereafter we are in direct scanout
+ * mode and expect the buffer IDs to match.
+ */
+ if (test->scanout.n_presentations > 1)
+ {
+ if (first_fb_id_expected == 0)
+ g_assert_cmpuint (drm_crtc->buffer_id, !=, first_fb_id_expected);
+ else
+ g_assert_cmpuint (drm_crtc->buffer_id, ==, first_fb_id_expected);
+ }
+
drmModeFreeCrtc (drm_crtc);
meta_device_file_release (device_file);
- g_main_loop_quit (test->loop);
+ test->number_of_frames_left--;
+ if (test->number_of_frames_left <= 0)
+ g_main_loop_quit (test->loop);
+ else
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
}
typedef enum
@@ -244,7 +281,9 @@ meta_test_kms_render_client_scanout (void)
g_assert_nonnull (wayland_test_client);
test = (KmsRenderingTest) {
+ .number_of_frames_left = N_FRAMES_PER_TEST,
.loop = g_main_loop_new (NULL, FALSE),
+ .scanout = {0},
.wait_for_scanout = TRUE,
};
@@ -270,7 +309,8 @@ meta_test_kms_render_client_scanout (void)
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
g_main_loop_run (test.loop);
- g_assert_cmpuint (test.scanout.fb_id, >, 0);
+ g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST);
+ g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST);
g_debug ("Unmake fullscreen");
window = meta_find_window_from_title (test_context, "dma-buf-scanout-test");
@@ -292,10 +332,15 @@ meta_test_kms_render_client_scanout (void)
g_assert_cmpint (buffer_rect.y, ==, 10);
test.wait_for_scanout = FALSE;
+ test.number_of_frames_left = N_FRAMES_PER_TEST;
+ test.scanout.n_presentations = 0;
+ test.scanout.n_direct_scanouts = 0;
+
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
g_main_loop_run (test.loop);
- g_assert_cmpuint (test.scanout.fb_id, ==, 0);
+ g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST);
+ g_assert_cmpint (test.scanout.n_direct_scanouts, ==, 0);
g_debug ("Moving back to 0, 0");
meta_window_move_frame (window, TRUE, 0, 0);
@@ -307,10 +352,15 @@ meta_test_kms_render_client_scanout (void)
g_assert_cmpint (buffer_rect.y, ==, 0);
test.wait_for_scanout = TRUE;
+ test.number_of_frames_left = N_FRAMES_PER_TEST;
+ test.scanout.n_presentations = 0;
+ test.scanout.n_direct_scanouts = 0;
+
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
g_main_loop_run (test.loop);
- g_assert_cmpuint (test.scanout.fb_id, >, 0);
+ g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST);
+ g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST);
g_signal_handler_disconnect (stage, before_update_handler_id);
g_signal_handler_disconnect (stage, before_paint_handler_id);
@@ -360,6 +410,15 @@ on_scanout_fallback_before_paint (ClutterStage *stage,
if (!scanout)
return;
+ test->scanout_fallback.scanouts_attempted++;
+
+ /* The first scanout candidate frame will get composited due to triple
+ * buffering draining the queue to drop to double buffering. So don't
+ * sabotage that first frame.
+ */
+ if (test->scanout_fallback.scanouts_attempted < 2)
+ return;
+
g_assert_false (test->scanout_fallback.scanout_sabotaged);
if (is_atomic_mode_setting (kms_device))
@@ -394,6 +453,15 @@ on_scanout_fallback_paint_view (ClutterStage *stage,
g_clear_handle_id (&test->scanout_fallback.repaint_guard_id,
g_source_remove);
test->scanout_fallback.fallback_painted = TRUE;
+ test->scanout_fallback.scanout_sabotaged = FALSE;
+ }
+ else if (test->scanout_fallback.scanouts_attempted == 1)
+ {
+ /* Now that we've seen the first scanout attempt that was inhibited by
+ * triple buffering, try a second frame. The second one should scanout
+ * and will be sabotaged.
+ */
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
}
}
@@ -403,11 +471,11 @@ on_scanout_fallback_presented (ClutterStage *stage,
ClutterFrameInfo *frame_info,
KmsRenderingTest *test)
{
- if (!test->scanout_fallback.scanout_sabotaged)
- return;
+ if (test->scanout_fallback.fallback_painted)
+ g_main_loop_quit (test->loop);
- g_assert_true (test->scanout_fallback.fallback_painted);
- g_main_loop_quit (test->loop);
+ test->number_of_frames_left--;
+ g_assert_cmpint (test->number_of_frames_left, >, 0);
}
static void
@@ -436,6 +504,7 @@ meta_test_kms_render_client_scanout_fallback (void)
g_assert_nonnull (wayland_test_client);
test = (KmsRenderingTest) {
+ .number_of_frames_left = N_FRAMES_PER_TEST,
.loop = g_main_loop_new (NULL, FALSE),
};