6aefbdd910
Signed-off-by: Mingi Sung <fiestalake@disroot.org>
2409 lines
87 KiB
Diff
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),
|
|
};
|
|
|