diff options
author | Joakim Soderlund | 2023-05-03 20:26:54 +0200 |
---|---|---|
committer | Joakim Soderlund | 2023-05-06 22:56:03 +0200 |
commit | 871b43d6bc491c9f4ffac885ff8a627911e3ee86 (patch) | |
tree | 93451193da80a0ca3da72606cddd2038a8694829 | |
parent | bc570f3c3cbe2975cecd10e25a42797c1db4a241 (diff) | |
download | aur-871b43d6bc491c9f4ffac885ff8a627911e3ee86.tar.gz |
Upgrade !1441 to commit eb4f260f
-rw-r--r-- | .SRCINFO | 2 | ||||
-rw-r--r-- | PKGBUILD | 2 | ||||
-rw-r--r-- | mr1441.patch | 2154 |
3 files changed, 850 insertions, 1308 deletions
@@ -33,7 +33,7 @@ pkgbase = mutter-dynamic-buffering source = mutter-dynamic-buffering::git+https://gitlab.gnome.org/GNOME/mutter.git#commit=28a6447ff060ae1fbac8f20a13908d6e230eddc2 source = mr1441.patch sha256sums = SKIP - sha256sums = 690a31df2e61de3fbd0b51ec86ab4b84f54e6453926b06c14c9cd2ec93c27db0 + sha256sums = 3003d63a665d80cae58cdf3db37af25e214bcff5e322768fe2544acb51adcf9b pkgname = mutter-dynamic-buffering provides = mutter @@ -53,7 +53,7 @@ source=( ) sha256sums=( 'SKIP' - '690a31df2e61de3fbd0b51ec86ab4b84f54e6453926b06c14c9cd2ec93c27db0' + '3003d63a665d80cae58cdf3db37af25e214bcff5e322768fe2544acb51adcf9b' ) pkgver() { diff --git a/mr1441.patch b/mr1441.patch index e4df07f35cb9..977b714c7683 100644 --- a/mr1441.patch +++ b/mr1441.patch @@ -1,22 +1,29 @@ Author: Daniel van Vugt <daniel.van.vugt@canonical.com> Source: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441 -Commit: a6f23c151c94912c8fc074facd07b0d6aa70f939 -Rebase: Thu Feb 16 13:46:59 2023 +0800 +Commit: eb4f260f6921e65277ef1f0707ffdf39357438bb +Rebase: Tue May 2 15:05:35 2023 +0200 diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index 34d676f54..78f8dde9b 100644 +index 3aeb29042..b1d6f7164 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c -@@ -45,6 +45,8 @@ typedef struct _EstimateQueue - int next_index; - } EstimateQueue; +@@ -35,6 +35,15 @@ enum -+static gboolean triple_buffering_disabled = FALSE; + 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 -@@ -65,8 +67,9 @@ typedef enum _ClutterFrameClockState +@@ -55,8 +64,9 @@ typedef enum _ClutterFrameClockState CLUTTER_FRAME_CLOCK_STATE_INIT, CLUTTER_FRAME_CLOCK_STATE_IDLE, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, @@ -28,16 +35,25 @@ index 34d676f54..78f8dde9b 100644 } ClutterFrameClockState; struct _ClutterFrameClock -@@ -96,6 +99,8 @@ 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; - -+ ClutterFrameHint last_flip_hints; ++ int64_t prev_last_flip_time_us; + - /* Last few durations between dispatch start and buffer swap. */ - EstimateQueue dispatch_to_swap_us; - /* Last few durations between buffer swap and GPU rendering finish. */ -@@ -228,6 +233,12 @@ void ++ 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) { @@ -50,28 +66,59 @@ index 34d676f54..78f8dde9b 100644 COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented, "Frame Clock (presented)"); -@@ -290,7 +301,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, +@@ -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, -- "dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", -+ "%s: dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", +- "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, - swap_to_flip_us); -@@ -304,6 +316,10 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - - frame_clock->got_measurements_last_frame = TRUE; +@@ -352,7 +392,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, } -+ else -+ { -+ CLUTTER_NOTE (FRAME_TIMINGS, "%s", debug_state); -+ } - - if (frame_info->refresh_rate > 1.0) + else { -@@ -318,11 +334,18 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, +- 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; @@ -92,7 +139,7 @@ index 34d676f54..78f8dde9b 100644 } } -@@ -338,11 +361,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) +@@ -403,11 +451,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: g_warn_if_reached (); break; @@ -113,43 +160,24 @@ index 34d676f54..78f8dde9b 100644 } } -@@ -354,6 +384,7 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) - int64_t max_swap_to_rendering_done_us = 0; - int64_t max_swap_to_flip_us = 0; - int64_t max_render_time_us; -+ int buffer_queue_latency_frames = 0; - int i; - - refresh_interval_us = frame_clock->refresh_interval_us; -@@ -376,6 +407,27 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) - frame_clock->swap_to_flip_us.values[i]); - } - -+ switch (frame_clock->state) +@@ -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; + { -+ case CLUTTER_FRAME_CLOCK_STATE_INIT: -+ case CLUTTER_FRAME_CLOCK_STATE_IDLE: -+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: -+ buffer_queue_latency_frames = 0; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ buffer_queue_latency_frames = 1; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: -+ g_warn_if_reached (); -+ buffer_queue_latency_frames = 2; -+ break; -+ } ++ int64_t ret = refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; + -+ max_swap_to_flip_us -= refresh_interval_us * buffer_queue_latency_frames; -+ if (max_swap_to_flip_us < 0) -+ max_swap_to_flip_us = 0; ++ if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE && ++ triple_buffering_mode != TRIPLE_BUFFERING_MODE_NEVER) ++ ret += refresh_interval_us; + - /* Max render time shows how early the frame clock needs to be dispatched - * to make it to the predicted next presentation time. It is composed of: - * - An estimate of duration from dispatch start to buffer swap. -@@ -392,8 +444,6 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) ++ 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; @@ -158,7 +186,7 @@ index 34d676f54..78f8dde9b 100644 return max_render_time_us; } -@@ -407,7 +457,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, +@@ -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; @@ -167,7 +195,7 @@ index 34d676f54..78f8dde9b 100644 int64_t next_update_time_us; now_us = g_get_monotonic_time (); -@@ -451,7 +501,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, +@@ -520,7 +581,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, * */ last_presentation_time_us = frame_clock->last_presentation_time_us; @@ -193,7 +221,7 @@ index 34d676f54..78f8dde9b 100644 /* * However, the last presentation could have happened more than a frame ago. -@@ -562,8 +629,12 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) +@@ -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; @@ -208,35 +236,44 @@ index 34d676f54..78f8dde9b 100644 break; } -@@ -599,11 +670,17 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) - case CLUTTER_FRAME_CLOCK_STATE_INIT: +@@ -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_SCHEDULED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: - return; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: -- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ 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; + 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; -@@ -612,7 +689,6 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) - g_warn_if_fail (next_update_time_us != -1); +@@ -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; } -@@ -631,6 +707,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) +@@ -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 (); @@ -244,8 +281,8 @@ index 34d676f54..78f8dde9b 100644 break; case CLUTTER_FRAME_CLOCK_STATE_IDLE: calculate_next_update_time_us (frame_clock, -@@ -638,11 +715,28 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - &frame_clock->next_presentation_time_us); +@@ -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; @@ -256,35 +293,44 @@ index 34d676f54..78f8dde9b 100644 - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ if (frame_clock->last_flip_hints & CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED || -+ triple_buffering_disabled) ++ switch (current_mode) + { -+ /* Force double buffering, disable triple buffering */ ++ 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; + } -+ -+ calculate_next_update_time_us (frame_clock, -+ &next_update_time_us, -+ &frame_clock->next_presentation_time_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 CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; return; } -@@ -650,7 +744,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - g_warn_if_fail (next_update_time_us != -1); +@@ -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 -@@ -676,7 +769,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, - frame_clock->refresh_interval_us; +@@ -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) @@ -292,7 +338,11 @@ index 34d676f54..78f8dde9b 100644 frame_clock->last_dispatch_lateness_us = 0; else frame_clock->last_dispatch_lateness_us = lateness_us; -@@ -684,7 +777,21 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, +@@ -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); @@ -315,8 +365,8 @@ index 34d676f54..78f8dde9b 100644 frame_count = frame_clock->frame_count++; -@@ -709,25 +816,31 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, - frame_clock->listener.user_data); +@@ -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) @@ -359,7 +409,7 @@ index 34d676f54..78f8dde9b 100644 } break; } -@@ -760,10 +873,12 @@ frame_clock_source_dispatch (GSource *source, +@@ -848,10 +986,13 @@ frame_clock_source_dispatch (GSource *source, } void @@ -369,23 +419,39 @@ index 34d676f54..78f8dde9b 100644 + 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 * -@@ -894,6 +1009,9 @@ clutter_frame_clock_class_init (ClutterFrameClockClass *klass) +@@ -963,8 +1104,6 @@ clutter_frame_clock_dispose (GObject *object) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterFrameClock *frame_clock = CLUTTER_FRAME_CLOCK (object); -+ if (!g_strcmp0 (g_getenv ("MUTTER_DEBUG_DISABLE_TRIPLE_BUFFERING"), "1")) -+ triple_buffering_disabled = TRUE; +- 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; - signals[DESTROY] = diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h -index 91e6b3a13..d750404d5 100644 +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 @@ -401,7 +467,7 @@ index 91e6b3a13..d750404d5 100644 #define CLUTTER_TYPE_FRAME_CLOCK (clutter_frame_clock_get_type ()) CLUTTER_EXPORT G_DECLARE_FINAL_TYPE (ClutterFrameClock, clutter_frame_clock, -@@ -90,8 +96,9 @@ void clutter_frame_clock_remove_timeline (ClutterFrameClock *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); @@ -414,22 +480,22 @@ index 91e6b3a13..d750404d5 100644 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 e0088564f..06581492f 100644 +index 1eceb8bc0..fb97abeaf 100644 --- a/clutter/clutter/clutter-frame-private.h +++ b/clutter/clutter/clutter-frame-private.h -@@ -24,6 +24,7 @@ struct _ClutterFrame - { +@@ -35,6 +35,7 @@ struct _ClutterFrame + gboolean has_result; ClutterFrameResult result; + ClutterFrameHint hints; }; - #define CLUTTER_FRAME_INIT ((ClutterFrame) { 0 }) + CLUTTER_EXPORT diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c -index 3c708da9d..63ae302af 100644 +index 85baef274..413ce9c2b 100644 --- a/clutter/clutter/clutter-frame.c +++ b/clutter/clutter/clutter-frame.c -@@ -40,3 +40,16 @@ clutter_frame_set_result (ClutterFrame *frame, +@@ -113,3 +113,16 @@ clutter_frame_set_result (ClutterFrame *frame, frame->result = result; frame->has_result = TRUE; } @@ -447,10 +513,10 @@ index 3c708da9d..63ae302af 100644 + return frame->hints; +} diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h -index d3608e81c..06c5f7f28 100644 +index 6bac3f4e4..fa97e8bd5 100644 --- a/clutter/clutter/clutter-frame.h +++ b/clutter/clutter/clutter-frame.h -@@ -33,4 +33,11 @@ void clutter_frame_set_result (ClutterFrame *frame, +@@ -55,6 +55,13 @@ void clutter_frame_set_result (ClutterFrame *frame, CLUTTER_EXPORT gboolean clutter_frame_has_result (ClutterFrame *frame); @@ -461,22 +527,24 @@ index d3608e81c..06c5f7f28 100644 +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 14f21024c..914b91a18 100644 +index 5f66d6032..9e821155b 100644 --- a/clutter/clutter/clutter-stage-view.c +++ b/clutter/clutter/clutter-stage-view.c -@@ -1252,8 +1252,9 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock, +@@ -1266,8 +1266,9 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock, - _clutter_stage_window_redraw_view (stage_window, view, &frame); + _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_frame_get_hints (frame)); - clutter_stage_emit_after_paint (stage, view); + 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 @@ -491,10 +559,10 @@ index dffe018d2..e0215f750 100644 + #endif /* __COGL_ONSCREEN_PRIVATE_H */ diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c -index ff9e1749a..b25b4af4a 100644 +index 842ececf7..1e2b11dee 100644 --- a/cogl/cogl/cogl-onscreen.c +++ b/cogl/cogl/cogl-onscreen.c -@@ -510,6 +510,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen) +@@ -508,6 +508,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen) return g_queue_pop_head (&priv->pending_frame_infos); } @@ -510,10 +578,10 @@ index ff9e1749a..b25b4af4a 100644 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 ca6f1f44f..a4b1c8cd0 100644 +index db94c7e40..aed22960c 100644 --- a/src/backends/meta-stage-impl.c +++ b/src/backends/meta-stage-impl.c -@@ -721,6 +721,8 @@ meta_stage_impl_redraw_view (ClutterStageWindow *stage_window, +@@ -770,6 +770,8 @@ meta_stage_impl_redraw_view (ClutterStageWindow *stage_window, { g_autoptr (GError) error = NULL; @@ -522,24 +590,11 @@ index ca6f1f44f..a4b1c8cd0 100644 if (meta_stage_impl_scanout_view (stage_impl, stage_view, scanout, -diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c -index 0bfd3f5ad..3590608ee 100644 ---- a/src/backends/native/meta-crtc-kms.c -+++ b/src/backends/native/meta-crtc-kms.c -@@ -394,7 +394,7 @@ meta_crtc_kms_maybe_set_gamma (MetaCrtcKms *crtc_kms, - if (!gamma) - return; - -- kms_update = meta_kms_ensure_pending_update (kms, kms_device); -+ kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc); - meta_kms_update_set_crtc_gamma (kms_update, - kms_crtc, - gamma->size, diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c -index f4b42c56c..81ce0c627 100644 +index f1b7459fe..8080098a0 100644 --- a/src/backends/native/meta-cursor-renderer-native.c +++ b/src/backends/native/meta-cursor-renderer-native.c -@@ -58,19 +58,6 @@ +@@ -59,19 +59,6 @@ #include "wayland/meta-wayland-buffer.h" #endif @@ -559,7 +614,7 @@ index f4b42c56c..81ce0c627 100644 static GQuark quark_cursor_sprite = 0; typedef struct _CrtcCursorData -@@ -104,19 +91,10 @@ typedef struct _MetaCursorRendererNativeGpuData +@@ -105,19 +92,10 @@ typedef struct _MetaCursorRendererNativeGpuData uint64_t cursor_height; } MetaCursorRendererNativeGpuData; @@ -580,7 +635,7 @@ index f4b42c56c..81ce0c627 100644 } MetaCursorNativeGpuState; typedef struct _MetaCursorNativePrivate -@@ -197,44 +175,17 @@ meta_cursor_renderer_native_finalize (GObject *object) +@@ -198,44 +176,17 @@ meta_cursor_renderer_native_finalize (GObject *object) G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object); } @@ -629,7 +684,7 @@ index f4b42c56c..81ce0c627 100644 } static void -@@ -309,10 +260,7 @@ assign_cursor_plane (MetaCursorRendererNative *native, +@@ -312,10 +263,7 @@ assign_cursor_plane (MetaCursorRendererNative *native, MetaKmsUpdate *kms_update; MetaKmsPlaneAssignment *plane_assignment; @@ -641,18 +696,7 @@ index f4b42c56c..81ce0c627 100644 kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); kms_device = meta_kms_crtc_get_device (kms_crtc); -@@ -341,8 +289,8 @@ assign_cursor_plane (MetaCursorRendererNative *native, - flags |= META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED; - - kms_update = -- meta_kms_ensure_pending_update (meta_kms_device_get_kms (kms_device), -- meta_kms_crtc_get_device (kms_crtc)); -+ meta_kms_ensure_pending_update_for_crtc (meta_kms_device_get_kms (kms_device), -+ kms_crtc); - plane_assignment = meta_kms_update_assign_plane (kms_update, - kms_crtc, - cursor_plane, -@@ -363,13 +311,6 @@ assign_cursor_plane (MetaCursorRendererNative *native, +@@ -365,13 +313,6 @@ assign_cursor_plane (MetaCursorRendererNative *native, native); crtc_cursor_data->buffer = buffer; @@ -666,16 +710,7 @@ index f4b42c56c..81ce0c627 100644 } static float -@@ -494,7 +435,7 @@ unset_crtc_cursor (MetaCursorRendererNative *native, - MetaKms *kms = meta_kms_device_get_kms (kms_device); - MetaKmsUpdate *kms_update; - -- kms_update = meta_kms_ensure_pending_update (kms, kms_device); -+ kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc); - meta_kms_update_unassign_plane (kms_update, kms_crtc, cursor_plane); - } - -@@ -602,19 +543,7 @@ has_valid_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite, +@@ -613,19 +554,7 @@ has_valid_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite, if (!cursor_gpu_state) return FALSE; @@ -696,7 +731,7 @@ index f4b42c56c..81ce0c627 100644 } static void -@@ -1119,16 +1048,14 @@ unset_crtc_cursor_renderer_privates (MetaGpu *gpu, +@@ -1132,16 +1061,14 @@ unset_crtc_cursor_renderer_privates (MetaGpu *gpu, static void cursor_gpu_state_free (MetaCursorNativeGpuState *cursor_gpu_state) { @@ -715,7 +750,7 @@ index f4b42c56c..81ce0c627 100644 g_free (cursor_gpu_state); } -@@ -1165,14 +1092,7 @@ invalidate_cursor_gpu_state (MetaCursorSprite *cursor_sprite) +@@ -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)) @@ -731,7 +766,7 @@ index f4b42c56c..81ce0c627 100644 } static void -@@ -1410,35 +1330,7 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native, +@@ -1423,35 +1343,7 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native, return; } @@ -768,7 +803,7 @@ index f4b42c56c..81ce0c627 100644 } static gboolean -@@ -1605,7 +1497,7 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer, +@@ -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; @@ -777,7 +812,7 @@ index f4b42c56c..81ce0c627 100644 is_cursor_scale_and_transform_valid (renderer, cursor_sprite)) return; -@@ -1750,8 +1642,8 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer, +@@ -1783,8 +1675,8 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer, return; } @@ -788,7 +823,7 @@ index f4b42c56c..81ce0c627 100644 } } #endif -@@ -1775,7 +1667,7 @@ realize_cursor_sprite_from_xcursor_for_gpu (MetaCursorRenderer *renderer, +@@ -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; @@ -798,87 +833,41 @@ index f4b42c56c..81ce0c627 100644 return; diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c -index ee9e19d2d..0da9a7785 100644 +index e5405428c..48e864ca8 100644 --- a/src/backends/native/meta-kms-crtc.c +++ b/src/backends/native/meta-kms-crtc.c -@@ -32,6 +32,12 @@ typedef struct _MetaKmsCrtcPropTable - MetaKmsProp props[META_KMS_CRTC_N_PROPS]; - } MetaKmsCrtcPropTable; - -+typedef struct -+{ -+ MetaDrmBuffer *front, *back; -+ gboolean back_is_set; -+} PlaneState; -+ - struct _MetaKmsCrtc - { - GObject parent; -@@ -44,6 +50,8 @@ struct _MetaKmsCrtc +@@ -46,6 +46,8 @@ struct _MetaKmsCrtc MetaKmsCrtcState current_state; MetaKmsCrtcPropTable prop_table; + -+ GHashTable *plane_states; ++ MetaSwapChain *swap_chain; }; G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT) -@@ -403,20 +411,91 @@ meta_kms_crtc_new (MetaKmsImplDevice *impl_device, - return crtc; +@@ -97,6 +99,12 @@ meta_kms_crtc_get_prop_drm_value (MetaKmsCrtc *crtc, + return meta_kms_prop_convert_value (prop, value); } -+void -+meta_kms_crtc_remember_plane_buffer (MetaKmsCrtc *crtc, -+ uint32_t plane_id, -+ MetaDrmBuffer *buffer) -+{ -+ gpointer key = GUINT_TO_POINTER (plane_id); -+ PlaneState *plane_state; -+ -+ plane_state = g_hash_table_lookup (crtc->plane_states, key); -+ if (plane_state == NULL) -+ { -+ plane_state = g_new0 (PlaneState, 1); -+ g_hash_table_insert (crtc->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_kms_crtc_on_scanout_started (MetaKmsCrtc *crtc) ++MetaSwapChain * ++meta_kms_crtc_get_swap_chain (MetaKmsCrtc *crtc) +{ -+ g_hash_table_foreach (crtc->plane_states, swap_plane_buffers, NULL); -+} -+ -+void -+meta_kms_crtc_release_buffers (MetaKmsCrtc *crtc) -+{ -+ g_hash_table_remove_all (crtc->plane_states); ++ 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_kms_crtc_release_buffers (crtc); ++ meta_swap_chain_release_buffers (crtc->swap_chain); + + G_OBJECT_CLASS (meta_kms_crtc_parent_class)->dispose (object); +} @@ -888,34 +877,20 @@ index ee9e19d2d..0da9a7785 100644 { MetaKmsCrtc *crtc = META_KMS_CRTC (object); - clear_gamma_state (&crtc->current_state); -+ g_hash_table_unref (crtc->plane_states); + 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); } - -+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_kms_crtc_init (MetaKmsCrtc *crtc) +@@ -478,6 +497,7 @@ meta_kms_crtc_init (MetaKmsCrtc *crtc) { crtc->current_state.gamma.size = 0; -+ crtc->plane_states = g_hash_table_new_full (NULL, -+ NULL, -+ NULL, -+ destroy_plane_state); + crtc->current_state.gamma.value = NULL; ++ crtc->swap_chain = meta_swap_chain_new (); } static void -@@ -424,5 +503,6 @@ meta_kms_crtc_class_init (MetaKmsCrtcClass *klass) +@@ -485,5 +505,6 @@ meta_kms_crtc_class_init (MetaKmsCrtcClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -923,35 +898,29 @@ index ee9e19d2d..0da9a7785 100644 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 54801dd96..deafeb61e 100644 +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-drm-buffer.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); -@@ -84,4 +85,12 @@ MetaKmsCrtcGamma * meta_kms_crtc_gamma_new (MetaKmsCrtc *crtc, - const uint16_t *green, - const uint16_t *blue); - -+void meta_kms_crtc_remember_plane_buffer (MetaKmsCrtc *crtc, -+ uint32_t plane_id, -+ MetaDrmBuffer *buffer); -+ -+void meta_kms_crtc_on_scanout_started (MetaKmsCrtc *crtc); -+ -+void meta_kms_crtc_release_buffers (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 e938e4522..5271cbecf 100644 +index 83b217274..5024b876d 100644 --- a/src/backends/native/meta-kms-impl-device-atomic.c +++ b/src/backends/native/meta-kms-impl-device-atomic.c -@@ -457,6 +457,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, +@@ -507,6 +507,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, { MetaKmsPlaneAssignment *plane_assignment = update_entry; MetaKmsPlane *plane = plane_assignment->plane; @@ -959,20 +928,20 @@ index e938e4522..5271cbecf 100644 MetaDrmBuffer *buffer; MetaKmsFbDamage *fb_damage; uint32_t prop_id; -@@ -609,6 +610,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, +@@ -659,6 +660,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, error)) return FALSE; } + + if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) -+ meta_kms_crtc_remember_plane_buffer (plane_assignment->crtc, -+ meta_kms_plane_get_id (plane), -+ buffer); ++ 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; } -@@ -986,7 +993,7 @@ meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device, +@@ -1002,7 +1009,7 @@ meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device, req, blob_ids, meta_kms_update_get_plane_assignments (update), @@ -982,19 +951,19 @@ index e938e4522..5271cbecf 100644 &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 648de51c6..53d76291a 100644 +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_kms_crtc_on_scanout_started (crtc); ++ 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, -@@ -550,7 +552,7 @@ is_timestamp_earlier_than (uint64_t ts1, +@@ -555,7 +557,7 @@ is_timestamp_earlier_than (uint64_t ts1, typedef struct _RetryPageFlipData { MetaKmsCrtc *crtc; @@ -1003,7 +972,7 @@ index 648de51c6..53d76291a 100644 MetaKmsPageFlipData *page_flip_data; float refresh_rate; uint64_t retry_time_us; -@@ -563,6 +565,7 @@ retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data) +@@ -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); @@ -1011,7 +980,7 @@ index 648de51c6..53d76291a 100644 g_free (retry_page_flip_data); } -@@ -630,16 +633,21 @@ retry_page_flips (gpointer user_data) +@@ -635,16 +638,21 @@ retry_page_flips (gpointer user_data) } else { @@ -1035,7 +1004,7 @@ index 648de51c6..53d76291a 100644 DRM_MODE_PAGE_FLIP_EVENT, retry_page_flip_data->page_flip_data); } -@@ -726,7 +734,7 @@ retry_page_flips (gpointer user_data) +@@ -731,7 +739,7 @@ retry_page_flips (gpointer user_data) static void schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple, MetaKmsCrtc *crtc, @@ -1044,7 +1013,7 @@ index 648de51c6..53d76291a 100644 float refresh_rate, MetaKmsPageFlipData *page_flip_data, MetaKmsCustomPageFlip *custom_page_flip) -@@ -741,7 +749,7 @@ schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple, +@@ -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, @@ -1053,16 +1022,16 @@ index 648de51c6..53d76291a 100644 .page_flip_data = page_flip_data, .refresh_rate = refresh_rate, .retry_time_us = retry_time_us, -@@ -875,6 +883,8 @@ mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple, +@@ -880,6 +888,8 @@ mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple, return FALSE; } -+ meta_kms_crtc_on_scanout_started (crtc); ++ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (crtc)); + if (!impl_device_simple->mode_set_fallback_feedback_source) { GSource *source; -@@ -999,20 +1009,20 @@ dispatch_page_flip (MetaKmsImplDevice *impl_device, +@@ -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) { @@ -1087,7 +1056,7 @@ index 648de51c6..53d76291a 100644 refresh_rate, page_flip_data, g_steal_pointer (&custom_page_flip)); -@@ -1302,7 +1312,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, +@@ -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. */ @@ -1096,7 +1065,7 @@ index 648de51c6..53d76291a 100644 case META_KMS_PLANE_TYPE_CURSOR: if (!process_cursor_plane_assignment (impl_device, update, plane_assignment, -@@ -1316,7 +1326,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, +@@ -1312,7 +1322,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, } else { @@ -1105,24 +1074,37 @@ index 648de51c6..53d76291a 100644 } case META_KMS_PLANE_TYPE_OVERLAY: error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, -@@ -1329,6 +1339,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, +@@ -1325,6 +1335,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, } g_assert_not_reached (); + +assigned: -+ meta_kms_crtc_remember_plane_buffer (plane_assignment->crtc, -+ meta_kms_plane_get_id (plane), -+ plane_assignment->buffer); ++ 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 d9ea27637..4eeb182f6 100644 +index d56636d55..b7a0ddd24 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c -@@ -1204,8 +1204,12 @@ meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device, +@@ -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) { @@ -1130,13 +1112,13 @@ index d9ea27637..4eeb182f6 100644 + meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); -+ g_list_foreach (priv->crtcs, (GFunc) meta_kms_crtc_release_buffers, NULL); ++ 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..a6fbaf2f8 100644 +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 @@ @@ -1151,48 +1133,16 @@ index c1c29905c..a6fbaf2f8 100644 meta_assert_not_in_kms_impl (kms); -+ meta_kms_crtc_on_scanout_started (page_flip_data->crtc); ++ 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-private.h b/src/backends/native/meta-kms-update-private.h -index 3a648ba0d..cfcf4b050 100644 ---- a/src/backends/native/meta-kms-update-private.h -+++ b/src/backends/native/meta-kms-update-private.h -@@ -137,6 +137,12 @@ uint64_t meta_kms_update_get_sequence_number (MetaKmsUpdate *update); - META_EXPORT_TEST - MetaKmsDevice * meta_kms_update_get_device (MetaKmsUpdate *update); - -+gboolean meta_kms_update_includes_crtc (MetaKmsUpdate *update, -+ MetaKmsCrtc *crtc); -+ -+void meta_kms_update_include_crtc (MetaKmsUpdate *update, -+ MetaKmsCrtc *crtc); -+ - void meta_kms_plane_assignment_set_rotation (MetaKmsPlaneAssignment *plane_assignment, - MetaKmsPlaneRotation rotation); - diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c -index 4c34000ef..cbbf1a0da 100644 +index ceb298a80..02d9d0159 100644 --- a/src/backends/native/meta-kms-update.c +++ b/src/backends/native/meta-kms-update.c -@@ -25,12 +25,14 @@ - #include "backends/meta-display-config-shared.h" - #include "backends/native/meta-kms-connector.h" - #include "backends/native/meta-kms-crtc.h" -+#include "backends/native/meta-kms-device.h" - #include "backends/native/meta-kms-mode-private.h" - #include "backends/native/meta-kms-plane.h" - - struct _MetaKmsUpdate - { - MetaKmsDevice *device; -+ GHashTable *crtcs; - - gboolean is_locked; - uint64_t sequence_number; -@@ -149,6 +151,7 @@ static void +@@ -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); @@ -1200,7 +1150,7 @@ index 4c34000ef..cbbf1a0da 100644 g_free (plane_assignment); } -@@ -228,7 +231,7 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update, +@@ -265,7 +266,7 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update, .update = update, .crtc = crtc, .plane = plane, @@ -1209,134 +1159,11 @@ index 4c34000ef..cbbf1a0da 100644 .src_rect = src_rect, .dst_rect = dst_rect, .flags = flags, -@@ -237,6 +240,8 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update, - update->plane_assignments = g_list_prepend (update->plane_assignments, - plane_assignment); - -+ g_hash_table_add (update->crtcs, crtc); -+ - return plane_assignment; - } - -@@ -251,6 +256,8 @@ meta_kms_update_unassign_plane (MetaKmsUpdate *update, - g_assert (meta_kms_crtc_get_device (crtc) == update->device); - g_assert (meta_kms_plane_get_device (plane) == update->device); - -+ drop_plane_assignment (update, plane, NULL); -+ - plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1); - *plane_assignment = (MetaKmsPlaneAssignment) { - .update = update, -@@ -262,6 +269,8 @@ meta_kms_update_unassign_plane (MetaKmsUpdate *update, - update->plane_assignments = g_list_prepend (update->plane_assignments, - plane_assignment); - -+ g_hash_table_add (update->crtcs, crtc); -+ - return plane_assignment; - } - -@@ -284,6 +293,8 @@ meta_kms_update_mode_set (MetaKmsUpdate *update, - }; - - update->mode_sets = g_list_prepend (update->mode_sets, mode_set); -+ -+ g_hash_table_add (update->crtcs, crtc); - } - - static MetaKmsConnectorUpdate * -@@ -292,6 +303,8 @@ ensure_connector_update (MetaKmsUpdate *update, - { - GList *l; - MetaKmsConnectorUpdate *connector_update; -+ MetaKmsDevice *device; -+ const MetaKmsConnectorState *state; - - for (l = update->connector_updates; l; l = l->next) - { -@@ -306,6 +319,23 @@ ensure_connector_update (MetaKmsUpdate *update, - - update->connector_updates = g_list_prepend (update->connector_updates, - connector_update); -+ device = meta_kms_connector_get_device (connector); -+ state = meta_kms_connector_get_current_state (connector); -+ if (device && state && state->current_crtc_id) -+ { -+ GList *l; -+ -+ for (l = meta_kms_device_get_crtcs (device); l; l = l->next) -+ { -+ MetaKmsCrtc *kms_crtc = l->data; -+ -+ if (meta_kms_crtc_get_id (kms_crtc) == state->current_crtc_id) -+ { -+ g_hash_table_add (update->crtcs, kms_crtc); -+ break; -+ } -+ } -+ } - - return connector_update; - } -@@ -416,6 +446,8 @@ meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update, - gamma = meta_kms_crtc_gamma_new (crtc, size, red, green, blue); - - update->crtc_gammas = g_list_prepend (update->crtc_gammas, gamma); -+ -+ g_hash_table_add (update->crtcs, crtc); - } - - void -@@ -679,6 +711,20 @@ meta_kms_update_get_device (MetaKmsUpdate *update) - return update->device; - } - -+gboolean -+meta_kms_update_includes_crtc (MetaKmsUpdate *update, -+ MetaKmsCrtc *crtc) -+{ -+ return g_hash_table_contains (update->crtcs, crtc); -+} -+ -+void -+meta_kms_update_include_crtc (MetaKmsUpdate *update, -+ MetaKmsCrtc *crtc) -+{ -+ g_hash_table_add (update->crtcs, crtc); -+} -+ - MetaKmsCustomPageFlip * - meta_kms_update_take_custom_page_flip_func (MetaKmsUpdate *update) - { -@@ -707,12 +753,15 @@ meta_kms_update_new (MetaKmsDevice *device) - update->device = device; - update->sequence_number = sequence_number++; - -+ update->crtcs = g_hash_table_new (NULL, NULL); -+ - return update; - } - - void - meta_kms_update_free (MetaKmsUpdate *update) - { -+ g_hash_table_destroy (update->crtcs); - g_list_free_full (update->result_listeners, - (GDestroyNotify) meta_kms_result_listener_free); - g_list_free_full (update->plane_assignments, diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c -index 21be532a2..b8dba8d0e 100644 +index d3a840b31..ce6fb4337 100644 --- a/src/backends/native/meta-kms.c +++ b/src/backends/native/meta-kms.c -@@ -23,6 +23,7 @@ - #include "backends/native/meta-kms-private.h" - - #include "backends/native/meta-backend-native.h" -+#include "backends/native/meta-kms-crtc.h" - #include "backends/native/meta-kms-device-private.h" - #include "backends/native/meta-kms-impl.h" - #include "backends/native/meta-kms-update-private.h" -@@ -177,10 +178,17 @@ struct _MetaKms +@@ -177,6 +177,8 @@ struct _MetaKms GList *pending_callbacks; guint callback_source_id; @@ -1345,141 +1172,17 @@ index 21be532a2..b8dba8d0e 100644 }; G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT) - -+static MetaKmsFeedback * -+meta_kms_post_update_sync (MetaKms *kms, -+ MetaKmsUpdate *update, -+ MetaKmsUpdateFlag flags); -+ - void - meta_kms_discard_pending_updates (MetaKms *kms) - { -@@ -247,12 +255,105 @@ meta_kms_take_pending_update (MetaKms *kms, - return NULL; - } - -+MetaKmsUpdate * -+meta_kms_ensure_pending_update_for_crtc (MetaKms *kms, -+ MetaKmsCrtc *crtc) -+{ -+ MetaKmsUpdate *update; -+ -+ update = meta_kms_get_pending_update_for_crtc (kms, crtc); -+ if (update == NULL) -+ { -+ update = meta_kms_update_new (meta_kms_crtc_get_device (crtc)); -+ meta_kms_update_include_crtc (update, crtc); -+ meta_kms_add_pending_update (kms, update); -+ } -+ -+ return update; -+} -+ -+static MetaKmsUpdate * -+meta_kms_find_compatible_update_for_crtc (MetaKms *kms, -+ MetaKmsCrtc *crtc, -+ gboolean take) -+{ -+ MetaKmsDevice *device; -+ MetaKmsUpdate *update; -+ GList *l; -+ -+ for (l = kms->pending_updates; l; l = l->next) -+ { -+ update = l->data; -+ if (meta_kms_update_includes_crtc (update, crtc)) -+ goto found; -+ } -+ -+ device = meta_kms_crtc_get_device (crtc); -+ -+ for (l = kms->pending_updates; l; l = l->next) -+ { -+ update = l->data; -+ if (meta_kms_update_get_device (update) == device && -+ meta_kms_update_get_mode_sets (update)) -+ goto found; -+ } -+ -+ return NULL; -+ -+found: -+ if (take) -+ kms->pending_updates = g_list_delete_link (kms->pending_updates, l); -+ return update; -+} -+ -+MetaKmsUpdate * -+meta_kms_get_pending_update_for_crtc (MetaKms *kms, -+ MetaKmsCrtc *crtc) -+{ -+ return meta_kms_find_compatible_update_for_crtc (kms, crtc, FALSE); -+} -+ -+static MetaKmsUpdate * -+meta_kms_take_pending_update_for_crtc (MetaKms *kms, -+ MetaKmsCrtc *crtc) -+{ -+ return meta_kms_find_compatible_update_for_crtc (kms, crtc, TRUE); -+} -+ - MetaKmsFeedback * - meta_kms_post_pending_update_sync (MetaKms *kms, - MetaKmsDevice *device, - MetaKmsUpdateFlag flags) - { - MetaKmsUpdate *update; -+ -+ update = meta_kms_take_pending_update (kms, device); -+ if (!update) -+ return NULL; -+ -+ return meta_kms_post_update_sync (kms, update, flags); -+} -+ -+MetaKmsFeedback * -+meta_kms_post_pending_update_for_crtc_sync (MetaKms *kms, -+ MetaKmsCrtc *crtc, -+ MetaKmsUpdateFlag flags) -+{ -+ MetaKmsUpdate *update; -+ -+ update = meta_kms_take_pending_update_for_crtc (kms, crtc); -+ if (!update) -+ return NULL; -+ -+ return meta_kms_post_update_sync (kms, update, flags); -+} -+ -+static MetaKmsFeedback * -+meta_kms_post_update_sync (MetaKms *kms, -+ MetaKmsUpdate *update, -+ MetaKmsUpdateFlag flags) -+{ -+ MetaKmsDevice *device = meta_kms_update_get_device (update); - MetaKmsFeedback *feedback; - GList *result_listeners; - GList *l; -@@ -260,10 +361,6 @@ meta_kms_post_pending_update_sync (MetaKms *kms, - COGL_TRACE_BEGIN_SCOPED (MetaKmsPostUpdateSync, - "KMS (post update)"); - -- update = meta_kms_take_pending_update (kms, device); -- if (!update) -- return NULL; -- - meta_kms_update_lock (update); - - feedback = meta_kms_device_process_update_sync (device, update, flags); -@@ -752,10 +849,18 @@ prepare_shutdown_in_impl (MetaKmsImpl *impl, - void - meta_kms_prepare_shutdown (MetaKms *kms) +@@ -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) @@ -1491,39 +1194,12 @@ index 21be532a2..b8dba8d0e 100644 meta_kms_finalize (GObject *object) { diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h -index bd9fe5cea..e8fe2e418 100644 +index fe4fef1a0..88421ed28 100644 --- a/src/backends/native/meta-kms.h +++ b/src/backends/native/meta-kms.h -@@ -39,9 +39,15 @@ void meta_kms_discard_pending_updates (MetaKms *kms); - MetaKmsUpdate * meta_kms_ensure_pending_update (MetaKms *kms, - MetaKmsDevice *device); - -+MetaKmsUpdate * meta_kms_ensure_pending_update_for_crtc (MetaKms *kms, -+ MetaKmsCrtc *crtc); -+ - MetaKmsUpdate * meta_kms_get_pending_update (MetaKms *kms, - MetaKmsDevice *device); - -+MetaKmsUpdate * meta_kms_get_pending_update_for_crtc (MetaKms *kms, -+ MetaKmsCrtc *crtc); -+ - MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms *kms, - MetaKmsDevice *device, - MetaKmsUpdateFlag flags); -@@ -49,6 +55,10 @@ MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms *kms, - MetaKmsFeedback * meta_kms_post_test_update_sync (MetaKms *kms, - MetaKmsUpdate *update); - -+MetaKmsFeedback * meta_kms_post_pending_update_for_crtc_sync (MetaKms *kms, -+ MetaKmsCrtc *device, -+ MetaKmsUpdateFlag flags); -+ - void meta_kms_discard_pending_page_flips (MetaKms *kms); - - void meta_kms_notify_modes_set (MetaKms *kms); -@@ -68,6 +78,8 @@ MetaKmsDevice * meta_kms_create_device (MetaKms *kms, - - void meta_kms_prepare_shutdown (MetaKms *kms); +@@ -51,6 +51,8 @@ MetaKmsDevice * meta_kms_create_device (MetaKms *kms, + MetaKmsDeviceFlag flags, + GError **error); +gboolean meta_kms_is_shutting_down (MetaKms *kms); + @@ -1531,85 +1207,56 @@ index bd9fe5cea..e8fe2e418 100644 MetaKmsFlags flags, GError **error); diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index a107a99bb..79cf78963 100644 +index 1ab117eb9..1c157ecf3 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c -@@ -67,13 +67,12 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState - - struct { - struct gbm_surface *surface; -- MetaDrmBuffer *current_fb; -- MetaDrmBuffer *next_fb; - } gbm; +@@ -72,7 +72,7 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState struct { MetaDrmBufferDumb *current_dumb_fb; - MetaDrmBufferDumb *dumb_fbs[2]; + MetaDrmBufferDumb *dumb_fbs[3]; -+ MetaDrmBuffer *source_fbs[3]; } cpu; gboolean noted_primary_gpu_copy_ok; -@@ -94,8 +93,8 @@ struct _MetaOnscreenNative +@@ -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 -@@ -107,69 +106,25 @@ struct _MetaOnscreenNative - #endif - - MetaRendererView *view; +@@ -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, - COGL_TYPE_ONSCREEN_EGL) +@@ -123,40 +136,17 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, -+static void -+try_post_latest_swap (CoglOnscreen *onscreen); -+ - static gboolean - init_secondary_gpu_state (MetaRendererNative *renderer_native, - CoglOnscreen *onscreen, - GError **error); + static GQuark blit_source_quark = 0; --static void --swap_secondary_drm_fb (CoglOnscreen *onscreen) --{ -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -- MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; -- -- secondary_gpu_state = onscreen_native->secondary_gpu_state; -- if (!secondary_gpu_state) -- return; -- -- g_set_object (&secondary_gpu_state->gbm.current_fb, -- secondary_gpu_state->gbm.next_fb); -- g_clear_object (&secondary_gpu_state->gbm.next_fb); --} -- --static void --free_current_secondary_bo (CoglOnscreen *onscreen) --{ -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -- MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; -- -- secondary_gpu_state = onscreen_native->secondary_gpu_state; -- if (!secondary_gpu_state) -- return; -- -- g_clear_object (&secondary_gpu_state->gbm.current_fb); --} +-static gboolean +-init_secondary_gpu_state (MetaRendererNative *renderer_native, +- CoglOnscreen *onscreen, +- GError **error); - -static void -free_current_bo (CoglOnscreen *onscreen) @@ -1617,10 +1264,9 @@ index a107a99bb..79cf78963 100644 - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - - g_clear_object (&onscreen_native->gbm.current_fb); -- free_current_secondary_bo (onscreen); -} - --static void + static void -meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) -{ - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); @@ -1632,14 +1278,26 @@ index a107a99bb..79cf78963 100644 - - g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); - g_clear_object (&onscreen_native->gbm.next_fb); -- -- swap_secondary_drm_fb (onscreen); -} -- ++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, - CoglFrameInfo *frame_info, -@@ -205,7 +160,7 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen) +@@ -193,7 +183,7 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen) info = cogl_onscreen_pop_head_frame_info (onscreen); @@ -1648,7 +1306,7 @@ index a107a99bb..79cf78963 100644 _cogl_onscreen_notify_frame_sync (onscreen, info); _cogl_onscreen_notify_complete (onscreen, info); -@@ -234,7 +189,7 @@ notify_view_crtc_presented (MetaRendererView *view, +@@ -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); @@ -1657,24 +1315,33 @@ index a107a99bb..79cf78963 100644 } static int64_t -@@ -296,6 +251,7 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, +@@ -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 -@@ -345,7 +301,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, +@@ -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_swap_drm_fb (onscreen); +- meta_onscreen_native_clear_next_fb (onscreen); + try_post_latest_swap (onscreen); } static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { -@@ -406,18 +362,40 @@ custom_egl_stream_page_flip (gpointer custom_page_flip_data, +@@ -403,18 +391,40 @@ custom_egl_stream_page_flip (gpointer custom_page_flip_data, } #endif /* HAVE_EGL_DEVICE */ @@ -1683,10 +1350,9 @@ index a107a99bb..79cf78963 100644 +static void +drop_stalled_swap (CoglOnscreen *onscreen) { -+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); CoglFrameInfo *frame_info; - -- meta_onscreen_native_swap_drm_fb (onscreen); ++ 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. @@ -1695,7 +1361,8 @@ index a107a99bb..79cf78963 100644 + 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); @@ -1718,43 +1385,25 @@ index a107a99bb..79cf78963 100644 static void meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, MetaRendererView *view, -@@ -436,8 +414,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, - MetaKmsDevice *kms_device; - MetaKms *kms; - MetaKmsUpdate *kms_update; -- MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL; +@@ -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 +423,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, - gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); - kms_device = meta_gpu_kms_get_kms_device (gpu_kms); - kms = meta_kms_device_get_kms (kms_device); -- kms_update = meta_kms_ensure_pending_update (kms, kms_device); -+ kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc); - - g_assert (meta_gpu_kms_is_crtc_active (gpu_kms, crtc)); - -@@ -455,15 +432,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, +@@ -446,7 +456,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: -- if (gpu_kms == render_gpu) -- { -- buffer = onscreen_native->gbm.next_fb; -- } -- else -- { -- secondary_gpu_state = onscreen_native->secondary_gpu_state; -- buffer = secondary_gpu_state->gbm.next_fb; -- } +- 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, -@@ -474,6 +443,11 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, +@@ -457,6 +467,11 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, meta_kms_plane_assignment_set_fb_damage (plane_assignment, rectangles, n_rectangles); } @@ -1766,187 +1415,7 @@ index a107a99bb..79cf78963 100644 break; case META_RENDERER_NATIVE_MODE_SURFACELESS: g_assert_not_reached (); -@@ -509,7 +483,7 @@ meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen, - COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeSetCrtcModes, - "Onscreen (set CRTC modes)"); - -- kms_update = meta_kms_ensure_pending_update (kms, kms_device); -+ kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc); - - switch (renderer_gpu_data->mode) - { -@@ -537,13 +511,41 @@ meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen, - kms_update); - } - -+static void -+hold_primary_gpu_fb_for_secondary_gpu_scanout (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, -+ MetaDrmBuffer *primary_gpu_fb, -+ MetaDrmBuffer *secondary_gpu_fb) -+{ -+ if (META_IS_DRM_BUFFER_DUMB (secondary_gpu_fb)) -+ { -+ MetaDrmBufferDumb *dumb_fb = META_DRM_BUFFER_DUMB (secondary_gpu_fb); -+ int i; -+ const int n = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); -+ -+ for (i = 0; i < n; i++) -+ { -+ if (dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i]) -+ { -+ g_set_object (&secondary_gpu_state->cpu.source_fbs[i], -+ primary_gpu_fb); -+ break; -+ } -+ } -+ -+ g_warn_if_fail (i < n); -+ } -+} -+ - static void - secondary_gpu_release_dumb (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) - { - unsigned i; - - for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++) -- g_clear_object (&secondary_gpu_state->cpu.dumb_fbs[i]); -+ { -+ g_clear_object (&secondary_gpu_state->cpu.dumb_fbs[i]); -+ g_clear_object (&secondary_gpu_state->cpu.source_fbs[i]); -+ } - } - - static void -@@ -568,8 +570,6 @@ secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_sta - NULL); - } - -- g_clear_object (&secondary_gpu_state->gbm.current_fb); -- g_clear_object (&secondary_gpu_state->gbm.next_fb); - g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy); - - secondary_gpu_release_dumb (secondary_gpu_state); -@@ -577,11 +577,11 @@ secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_sta - g_free (secondary_gpu_state); - } - --static gboolean -+static MetaDrmBuffer * - import_shared_framebuffer (CoglOnscreen *onscreen, -- MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) -+ MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, -+ MetaDrmBuffer *primary_gpu_fb) - { -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaRenderDevice *render_device; - g_autoptr (GError) error = NULL; - MetaDrmBuffer *imported_buffer; -@@ -589,7 +589,7 @@ import_shared_framebuffer (CoglOnscreen *onscreen, - render_device = secondary_gpu_state->renderer_gpu_data->render_device; - imported_buffer = - meta_render_device_import_dma_buf (render_device, -- onscreen_native->gbm.next_fb, -+ primary_gpu_fb, - &error); - if (!imported_buffer) - { -@@ -603,16 +603,9 @@ import_shared_framebuffer (CoglOnscreen *onscreen, - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE); - secondary_gpu_state->import_status = - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED; -- return FALSE; -+ return NULL; - } - -- /* -- * next_fb may already contain a fallback buffer, so clear it only -- * when we are sure to succeed. -- */ -- g_clear_object (&secondary_gpu_state->gbm.next_fb); -- secondary_gpu_state->gbm.next_fb = imported_buffer; -- - if (secondary_gpu_state->import_status == - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE) - { -@@ -629,16 +622,16 @@ import_shared_framebuffer (CoglOnscreen *onscreen, - - secondary_gpu_state->import_status = - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK; -- return TRUE; -+ return imported_buffer; - } - --static void -+static MetaDrmBuffer * - copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, - MetaRendererNativeGpuData *renderer_gpu_data, -- gboolean *egl_context_changed) -+ gboolean *egl_context_changed, -+ MetaDrmBuffer *primary_gpu_fb) - { -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; - MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); - MetaGles3 *gles3 = meta_renderer_native_get_gles3 (renderer_native); -@@ -654,9 +647,6 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferSecondaryGpu, - "FB Copy (secondary GPU)"); - -- g_warn_if_fail (secondary_gpu_state->gbm.next_fb == NULL); -- g_clear_object (&secondary_gpu_state->gbm.next_fb); -- - render_device = renderer_gpu_data->render_device; - egl_display = meta_render_device_get_egl_display (render_device); - -@@ -669,13 +659,13 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - { - g_warning ("Failed to make current: %s", error->message); - g_error_free (error); -- return; -+ return NULL; - } - - *egl_context_changed = TRUE; - - -- buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb); -+ buffer_gbm = META_DRM_BUFFER_GBM (primary_gpu_fb); - bo = meta_drm_buffer_gbm_get_bo (buffer_gbm); - if (!meta_renderer_native_gles3_blit_shared_bo (egl, - gles3, -@@ -687,7 +677,7 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - { - g_warning ("Failed to blit shared framebuffer: %s", error->message); - g_error_free (error); -- return; -+ return NULL; - } - - if (!meta_egl_swap_buffers (egl, -@@ -697,7 +687,7 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - { - g_warning ("Failed to swap buffers: %s", error->message); - g_error_free (error); -- return; -+ return NULL; - } - - use_modifiers = meta_renderer_native_use_modifiers (renderer_native); -@@ -717,25 +707,30 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - g_warning ("meta_drm_buffer_gbm_new_lock_front failed: %s", - error->message); - g_error_free (error); -- return; -+ return NULL; - } - -- secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); -+ return META_DRM_BUFFER (buffer_gbm); - } - - static MetaDrmBufferDumb * +@@ -698,12 +713,17 @@ static MetaDrmBufferDumb * secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { MetaDrmBufferDumb *current_dumb_fb; @@ -1967,229 +1436,45 @@ index a107a99bb..79cf78963 100644 + return secondary_gpu_state->cpu.dumb_fbs[0]; } --static gboolean -+static MetaDrmBuffer * - copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, - const int *rectangles, -@@ -761,13 +756,13 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre - - if (!secondary_gpu_state || - secondary_gpu_state->egl_surface == EGL_NO_SURFACE) -- return FALSE; -+ return NULL; - - primary_gpu = meta_renderer_native_get_primary_gpu (renderer_native); - primary_gpu_data = - meta_renderer_native_get_gpu_data (renderer_native, primary_gpu); - if (!primary_gpu_data->secondary.has_EGL_EXT_image_dma_buf_import_modifiers) -- return FALSE; -+ return NULL; - - buffer_dumb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state); - buffer = META_DRM_BUFFER (buffer_dumb); -@@ -790,7 +785,7 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre - { - meta_topic (META_DEBUG_KMS, - "Failed to create DMA buffer: %s", error->message); -- return FALSE; -+ return NULL; - } - - dmabuf_fb = -@@ -808,7 +803,7 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre - meta_topic (META_DEBUG_KMS, - "Failed to create DMA buffer for blitting: %s", - error->message); -- return FALSE; -+ return NULL; - } - /* Limit the number of individual copies to 16 */ - #define MAX_RECTS 16 -@@ -821,7 +816,7 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre - &error)) - { - g_object_unref (dmabuf_fb); -- return FALSE; -+ return NULL; - } - } - else -@@ -838,20 +833,19 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre - &error)) - { - g_object_unref (dmabuf_fb); -- return FALSE; -+ return NULL; - } - } - } - - g_object_unref (dmabuf_fb); - -- g_set_object (&secondary_gpu_state->gbm.next_fb, buffer); - secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb; - -- return TRUE; -+ return g_object_ref (buffer); - } - --static void -+static MetaDrmBuffer * - copy_shared_framebuffer_cpu (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, - MetaRendererNativeGpuData *renderer_gpu_data) -@@ -903,17 +897,19 @@ copy_shared_framebuffer_cpu (CoglOnscreen *onscreen, - - cogl_object_unref (dumb_bitmap); - -- g_set_object (&secondary_gpu_state->gbm.next_fb, buffer); - secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb; -+ -+ return g_object_ref (buffer); - } + static MetaDrmBuffer * +@@ -1041,7 +1061,6 @@ on_swap_buffer_update_result (const MetaKmsFeedback *kms_feedback, + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; --static void -+static MetaDrmBuffer * - update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen, - const int *rectangles, - int n_rectangles) - { - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; -+ MetaDrmBuffer *copy = NULL; - - COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePreSwapBuffers, - "Onscreen (secondary gpu pre-swap-buffers)"); -@@ -939,10 +935,11 @@ update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen, - /* prepare fallback */ - G_GNUC_FALLTHROUGH; - case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: -- if (!copy_shared_framebuffer_primary_gpu (onscreen, -- secondary_gpu_state, -- rectangles, -- n_rectangles)) -+ copy = copy_shared_framebuffer_primary_gpu (onscreen, -+ secondary_gpu_state, -+ rectangles, -+ n_rectangles); -+ if (!copy) - { - if (!secondary_gpu_state->noted_primary_gpu_copy_failed) - { -@@ -952,9 +949,9 @@ update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen, - secondary_gpu_state->noted_primary_gpu_copy_failed = TRUE; - } - -- copy_shared_framebuffer_cpu (onscreen, -- secondary_gpu_state, -- renderer_gpu_data); -+ copy = copy_shared_framebuffer_cpu (onscreen, -+ secondary_gpu_state, -+ renderer_gpu_data); - } - else if (!secondary_gpu_state->noted_primary_gpu_copy_ok) - { -@@ -966,11 +963,15 @@ update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen, - break; - } - } -+ -+ return copy; + meta_onscreen_native_notify_frame_complete (onscreen); +- meta_onscreen_native_clear_next_fb (onscreen); } static void --update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, -- gboolean *egl_context_changed) -+update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, -+ gboolean *egl_context_changed, -+ MetaDrmBuffer *primary_gpu_fb, -+ MetaDrmBuffer **secondary_gpu_fb) - { - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaRendererNative *renderer_native = onscreen_native->renderer_native; -@@ -983,6 +984,7 @@ update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, - if (secondary_gpu_state) - { - MetaRendererNativeGpuData *renderer_gpu_data; -+ g_autoptr (MetaDrmBuffer) next_fb = NULL; - - renderer_gpu_data = - meta_renderer_native_get_gpu_data (renderer_native, -@@ -990,23 +992,30 @@ update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, - switch (renderer_gpu_data->secondary.copy_mode) - { - case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: -- if (import_shared_framebuffer (onscreen, secondary_gpu_state)) -+ next_fb = import_shared_framebuffer (onscreen, -+ secondary_gpu_state, -+ primary_gpu_fb); -+ if (next_fb) - break; -- -- /* The fallback was prepared in pre_swap_buffers */ -+ /* The fallback was prepared in pre_swap_buffers and is currently -+ * in secondary_gpu_fb. -+ */ - renderer_gpu_data->secondary.copy_mode = - META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY; - G_GNUC_FALLTHROUGH; - case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: -- /* Done before eglSwapBuffers. */ -+ next_fb = g_object_ref (*secondary_gpu_fb); - break; - case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: -- copy_shared_framebuffer_gpu (onscreen, -- secondary_gpu_state, -- renderer_gpu_data, -- egl_context_changed); -+ next_fb = copy_shared_framebuffer_gpu (onscreen, -+ secondary_gpu_state, -+ renderer_gpu_data, -+ egl_context_changed, -+ primary_gpu_fb); - break; - } -+ -+ g_set_object (secondary_gpu_fb, next_fb); - } - } - -@@ -1040,34 +1049,39 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, +@@ -1058,31 +1077,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); -- MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); -- MetaKms *kms = meta_backend_native_get_kms (backend_native); 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; -- MetaKmsUpdateFlag flags; - g_autoptr (MetaKmsFeedback) kms_feedback = NULL; -- const GError *feedback_error; -+ g_autoptr (MetaDrmBuffer) primary_gpu_fb = NULL; -+ g_autoptr (MetaDrmBuffer) secondary_gpu_fb = NULL; + size_t rectangles_size; COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, "Onscreen (swap-buffers)"); -- update_secondary_gpu_state_pre_swap_buffers (onscreen, -- rectangles, -- n_rectangles); + if (meta_is_topic_enabled (META_DEBUG_KMS)) + { + unsigned int frames_pending = @@ -2203,46 +1488,14 @@ index a107a99bb..79cf78963 100644 + "?"); + } + -+ secondary_gpu_fb = -+ update_secondary_gpu_state_pre_swap_buffers (onscreen, -+ rectangles, -+ n_rectangles); - - parent_class = COGL_ONSCREEN_CLASS (meta_onscreen_native_parent_class); - parent_class->swap_buffers_with_damage (onscreen, -@@ -1083,9 +1097,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + secondary_gpu_fb = + update_secondary_gpu_state_pre_swap_buffers (onscreen, + rectangles, +@@ -1138,7 +1161,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); -- g_clear_object (&onscreen_native->gbm.next_fb); -- - buffer_flags = META_DRM_BUFFER_FLAG_NONE; - if (!meta_renderer_native_use_modifiers (renderer_native)) - buffer_flags |= META_DRM_BUFFER_FLAG_DISABLE_MODIFIERS; -@@ -1103,8 +1114,7 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - return; - } - -- onscreen_native->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); -- -+ primary_gpu_fb = META_DRM_BUFFER (g_steal_pointer (&buffer_gbm)); - break; - case META_RENDERER_NATIVE_MODE_SURFACELESS: - g_assert_not_reached (); -@@ -1115,7 +1125,46 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - #endif - } - -- update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed); -+ update_secondary_gpu_state_post_swap_buffers (onscreen, -+ &egl_context_changed, -+ primary_gpu_fb, -+ &secondary_gpu_fb); -+ -+ switch (renderer_gpu_data->mode) -+ { -+ case META_RENDERER_NATIVE_MODE_GBM: + if (onscreen_native->gbm.next_fb != NULL) + { + g_warn_if_fail (onscreen_native->gbm.stalled_fb == NULL); @@ -2252,42 +1505,34 @@ index a107a99bb..79cf78963 100644 + 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); -+ hold_primary_gpu_fb_for_secondary_gpu_scanout ( -+ onscreen_native->secondary_gpu_state, -+ primary_gpu_fb, -+ secondary_gpu_fb); -+ } -+ else -+ { -+ g_set_object (&onscreen_native->gbm.next_fb, primary_gpu_fb); -+ } -+ break; -+ case META_RENDERER_NATIVE_MODE_SURFACELESS: -+ break; -+#ifdef HAVE_EGL_DEVICE -+ case META_RENDERER_NATIVE_MODE_EGL_DEVICE: -+ break; -+#endif -+ } -+ + if (onscreen_native->secondary_gpu_state) + g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb); + else +@@ -1152,6 +1183,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 -@@ -1126,23 +1175,77 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + * current, making it fail to set it when it needs to. Avoid that by making +@@ -1161,12 +1195,82 @@ 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); +} @@ -2304,47 +1549,63 @@ index a107a99bb..79cf78963 100644 + 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); -+ MetaKms *kms = meta_backend_native_get_kms (backend_native); + 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); -+ MetaKmsUpdateFlag flags; ++ MetaKmsUpdate *kms_update; + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; -+ const GError *feedback_error; -+ unsigned int frames_pending = cogl_onscreen_count_pending_frames (onscreen); ++ g_autoptr (ClutterFrame) frame = NULL; ++ MetaFrameNative *frame_native; ++ ++ if (onscreen_native->next_post.frame == NULL) ++ return; + + if (meta_kms_is_shutting_down (kms)) + { + meta_onscreen_native_discard_pending_swaps (onscreen); + return; + } -+ -+ if (onscreen_native->swaps_pending == 0) -+ return; -+ -+ g_assert (frames_pending >= onscreen_native->swaps_pending); -+ + 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); -+ g_return_if_fail (onscreen_native->swaps_pending > 0); + onscreen_native->swaps_pending--; + - ensure_crtc_modes (onscreen); - meta_onscreen_native_flip_crtc (onscreen, - onscreen_native->view, + kms_update = meta_frame_native_ensure_kms_update (frame_native, + kms_device); + meta_kms_update_add_result_listener (kms_update, +@@ -1179,15 +1283,14 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, onscreen_native->crtc, + kms_update, META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, - rectangles, - n_rectangles); @@ -2353,6 +1614,7 @@ index a107a99bb..79cf78963 100644 } else { ++ frame = g_steal_pointer (&onscreen_native->next_post.frame); meta_renderer_native_queue_power_save_page_flip (renderer_native, onscreen); - clutter_frame_set_result (frame, @@ -2360,58 +1622,68 @@ index a107a99bb..79cf78963 100644 return; } -@@ -1160,9 +1263,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - "Postponing primary plane composite update for CRTC %u (%s)", - meta_kms_crtc_get_id (kms_crtc), - meta_kms_device_get_path (kms_device)); -- +@@ -1207,8 +1310,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)) -@@ -1172,8 +1272,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, +@@ -1222,8 +1323,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - meta_renderer_native_notify_mode_sets_reset (renderer_native); + 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; -@@ -1186,8 +1284,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - { - meta_renderer_native_notify_mode_sets_reset (renderer_native); +@@ -1239,8 +1338,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; -@@ -1200,18 +1296,16 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - meta_kms_device_get_path (kms_device)); +@@ -1256,7 +1353,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); + } - flags = META_KMS_UPDATE_FLAG_NONE; -- kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags); -+ kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms, -+ kms_crtc, -+ flags); -+ g_return_if_fail (kms_feedback != NULL); + gboolean +@@ -1301,6 +1397,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); - switch (meta_kms_feedback_get_result (kms_feedback)) - { - case META_KMS_FEEDBACK_PASSED: -- clutter_frame_set_result (frame, -- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - break; - case META_KMS_FEEDBACK_FAILED: -- clutter_frame_set_result (frame, -- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); -- - feedback_error = meta_kms_feedback_get_error (kms_feedback); - if (!g_error_matches (feedback_error, - G_IO_ERROR, -@@ -1298,6 +1392,18 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, + error = meta_kms_feedback_get_error (kms_feedback); + if (!error) +@@ -1314,8 +1411,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 +1420,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 +1470,18 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, return FALSE; } @@ -2430,54 +1702,87 @@ index a107a99bb..79cf78963 100644 renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, render_gpu); -@@ -1350,7 +1456,9 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, - meta_kms_device_get_path (kms_device)); - - flags = META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR; -- kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags); -+ kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms, -+ kms_crtc, -+ flags); - switch (meta_kms_feedback_get_result (kms_feedback)) - { - case META_KMS_FEEDBACK_PASSED: -@@ -1364,7 +1472,6 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, - G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) - break; +@@ -1414,6 +1521,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 +1667,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; ++ } -- g_clear_object (&onscreen_native->gbm.next_fb); - g_propagate_error (error, g_error_copy (feedback_error)); - return FALSE; + kms_update = meta_frame_native_steal_kms_update (frame_native); + if (!kms_update) +@@ -1567,6 +1689,42 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, + return; } -@@ -1400,7 +1507,10 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, - g_autoptr (MetaKmsFeedback) kms_feedback = NULL; - const GError *error; -- kms_update = meta_kms_get_pending_update (kms, kms_device); + if (cogl_onscreen_count_pending_frames (onscreen) > 0) -+ return; ++ { ++ /* 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 +1745,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); ++} + -+ kms_update = meta_kms_get_pending_update_for_crtc (kms, kms_crtc); - if (!kms_update) - { - clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); -@@ -1415,9 +1525,9 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, - g_object_unref); - - flags = META_KMS_UPDATE_FLAG_NONE; -- kms_feedback = meta_kms_post_pending_update_sync (kms, -- kms_device, -- flags); -+ kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms, -+ kms_crtc, -+ flags); - switch (meta_kms_feedback_get_result (kms_feedback)) - { - case META_KMS_FEEDBACK_PASSED: -@@ -1439,6 +1549,17 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, - } - } - +void +meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen) +{ @@ -2487,81 +1792,34 @@ index a107a99bb..79cf78963 100644 + + g_clear_object (&onscreen_native->gbm.stalled_fb); + g_clear_object (&onscreen_native->gbm.next_fb); -+} -+ - static gboolean - should_surface_be_sharable (CoglOnscreen *onscreen) - { -@@ -1987,6 +2108,21 @@ pick_secondary_gpu_framebuffer_format_for_cpu (CoglOnscreen *onscreen) - return DRM_FORMAT_INVALID; } -+static void -+dumb_toggle_notify (gpointer data, -+ GObject *object, -+ gboolean is_last_ref) -+{ -+ MetaDrmBuffer **source_fb = data; -+ -+ g_return_if_fail (source_fb != NULL); -+ if (is_last_ref && *source_fb) -+ { -+ g_return_if_fail (META_IS_DRM_BUFFER (*source_fb)); -+ g_clear_object (source_fb); -+ } -+} -+ static gboolean - init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_native, - CoglOnscreen *onscreen, -@@ -2043,6 +2179,12 @@ init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_nat - } - - secondary_gpu_state->cpu.dumb_fbs[i] = META_DRM_BUFFER_DUMB (dumb_buffer); -+ g_object_add_toggle_ref (G_OBJECT (dumb_buffer), -+ dumb_toggle_notify, -+ &secondary_gpu_state->cpu.source_fbs[i]); -+ -+ /* It was incremented higher than we need by add_toggle_ref */ -+ g_object_unref (dumb_buffer); - } - - /* -@@ -2132,7 +2274,7 @@ meta_onscreen_native_new (MetaRendererNative *renderer_native, - onscreen_native->renderer_native = renderer_native; - onscreen_native->render_gpu = render_gpu; - onscreen_native->output = output; -- onscreen_native->crtc = crtc; -+ onscreen_native->crtc = g_object_ref (crtc); - - return onscreen_native; - } -@@ -2153,7 +2295,6 @@ meta_onscreen_native_dispose (GObject *object) +@@ -2414,7 +2582,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 (); -@@ -2181,9 +2322,12 @@ meta_onscreen_native_dispose (GObject *object) +@@ -2448,6 +2616,10 @@ meta_onscreen_native_dispose (GObject *object) - G_OBJECT_CLASS (meta_onscreen_native_parent_class)->dispose (object); - -+ g_clear_object (&onscreen_native->crtc); - g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy); - g_clear_pointer (&onscreen_native->secondary_gpu_state, - secondary_gpu_state_free); + 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 diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h -index 8ea7ec254..81b0eeaed 100644 +index 05a9ecdb8..a33ee3857 100644 --- a/src/backends/native/meta-onscreen-native.h +++ b/src/backends/native/meta-onscreen-native.h -@@ -42,6 +42,8 @@ void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, +@@ -45,6 +45,8 @@ void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen); @@ -2571,10 +1829,10 @@ index 8ea7ec254..81b0eeaed 100644 MetaDrmBuffer *fb); diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c -index e27805d91..a99142e97 100644 +index 4da361e2b..e5d995388 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c -@@ -684,12 +684,18 @@ static gboolean +@@ -708,12 +708,18 @@ static gboolean dummy_power_save_page_flip_cb (gpointer user_data) { MetaRendererNative *renderer_native = user_data; @@ -2595,7 +1853,7 @@ index e27805d91..a99142e97 100644 renderer_native->power_save_page_flip_source_id = 0; return G_SOURCE_REMOVE; -@@ -701,6 +707,9 @@ meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_na +@@ -725,6 +731,9 @@ meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_na { const unsigned int timeout_ms = 100; @@ -2605,8 +1863,8 @@ index e27805d91..a99142e97 100644 if (!renderer_native->power_save_page_flip_source_id) { renderer_native->power_save_page_flip_source_id = -@@ -1409,6 +1418,26 @@ meta_renderer_native_create_view (MetaRenderer *renderer, - return view; +@@ -1473,6 +1482,26 @@ detach_onscreens (MetaRenderer *renderer) + } } +static void @@ -2630,30 +1888,252 @@ index e27805d91..a99142e97 100644 +} + static void - keep_current_onscreens_alive (MetaRenderer *renderer) + meta_renderer_native_rebuild_views (MetaRenderer *renderer) { -@@ -1437,6 +1466,7 @@ 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); - meta_kms_discard_pending_updates (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 a4fe0850c..ec7617944 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/native-kms-render.c b/src/tests/native-kms-render.c -index 6595eb5a2..42f7e6276 100644 +index 1557b764e..c482d07e0 100644 --- a/src/tests/native-kms-render.c +++ b/src/tests/native-kms-render.c -@@ -36,6 +36,8 @@ +@@ -41,6 +41,8 @@ #include "tests/meta-wayland-test-driver.h" #include "tests/meta-wayland-test-utils.h" -+#define N_FRAMES_PER_TEST 10 ++#define N_FRAMES_PER_TEST 30 + typedef struct { int number_of_frames_left; -@@ -43,7 +45,9 @@ typedef struct +@@ -48,12 +50,15 @@ typedef struct struct { int n_paints; @@ -2664,7 +2144,13 @@ index 6595eb5a2..42f7e6276 100644 } scanout; gboolean wait_for_scanout; -@@ -72,7 +76,7 @@ meta_test_kms_render_basic (void) + + 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) { @@ -2673,7 +2159,7 @@ index 6595eb5a2..42f7e6276 100644 .loop = g_main_loop_new (NULL, FALSE), }; handler_id = g_signal_connect (stage, "after-update", -@@ -93,7 +97,6 @@ on_scanout_before_update (ClutterStage *stage, +@@ -125,7 +130,6 @@ on_scanout_before_update (ClutterStage *stage, KmsRenderingTest *test) { test->scanout.n_paints = 0; @@ -2681,7 +2167,7 @@ index 6595eb5a2..42f7e6276 100644 } static void -@@ -103,6 +106,7 @@ on_scanout_before_paint (ClutterStage *stage, +@@ -136,6 +140,7 @@ on_scanout_before_paint (ClutterStage *stage, { CoglScanout *scanout; MetaDrmBuffer *buffer; @@ -2689,7 +2175,7 @@ index 6595eb5a2..42f7e6276 100644 scanout = clutter_stage_view_peek_scanout (stage_view); if (!scanout) -@@ -110,8 +114,14 @@ on_scanout_before_paint (ClutterStage *stage, +@@ -143,8 +148,14 @@ on_scanout_before_paint (ClutterStage *stage, g_assert_true (META_IS_DRM_BUFFER (scanout)); buffer = META_DRM_BUFFER (scanout); @@ -2706,13 +2192,13 @@ index 6595eb5a2..42f7e6276 100644 } static void -@@ -138,12 +148,12 @@ on_scanout_presented (ClutterStage *stage, +@@ -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->scanout.n_paints > 0) +- if (test->wait_for_scanout && test->scanout.n_paints > 0) + if (test->wait_for_scanout && test->scanout.fb_ids == NULL) return; @@ -2722,7 +2208,7 @@ index 6595eb5a2..42f7e6276 100644 device_pool = meta_backend_native_get_device_pool (backend_native); -@@ -162,15 +172,41 @@ on_scanout_presented (ClutterStage *stage, +@@ -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); @@ -2768,7 +2254,7 @@ index 6595eb5a2..42f7e6276 100644 } typedef enum -@@ -209,7 +245,9 @@ meta_test_kms_render_client_scanout (void) +@@ -244,7 +281,9 @@ meta_test_kms_render_client_scanout (void) g_assert_nonnull (wayland_test_client); test = (KmsRenderingTest) { @@ -2778,7 +2264,7 @@ index 6595eb5a2..42f7e6276 100644 .wait_for_scanout = TRUE, }; -@@ -235,7 +273,8 @@ meta_test_kms_render_client_scanout (void) +@@ -270,7 +309,8 @@ meta_test_kms_render_client_scanout (void) clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); @@ -2788,7 +2274,7 @@ index 6595eb5a2..42f7e6276 100644 g_debug ("Unmake fullscreen"); window = meta_find_window_from_title (test_context, "dma-buf-scanout-test"); -@@ -257,10 +296,15 @@ meta_test_kms_render_client_scanout (void) +@@ -292,10 +332,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.y, ==, 10); test.wait_for_scanout = FALSE; @@ -2805,7 +2291,7 @@ index 6595eb5a2..42f7e6276 100644 g_debug ("Moving back to 0, 0"); meta_window_move_frame (window, TRUE, 0, 0); -@@ -272,10 +316,15 @@ meta_test_kms_render_client_scanout (void) +@@ -307,10 +352,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.y, ==, 0); test.wait_for_scanout = TRUE; @@ -2822,3 +2308,59 @@ index 6595eb5a2..42f7e6276 100644 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), + }; + |