summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoakim Soderlund2021-11-05 19:35:21 +0100
committerJoakim Soderlund2021-11-05 19:57:04 +0100
commitfc50b639f5bdf1056ef439463066b837ff6c4bf0 (patch)
tree375a502cb8850b2fa071e6d45ca009a6af9170f5
parent4bee016f1d8e76251182db62544104d4e9ede17c (diff)
downloadaur-fc50b639f5bdf1056ef439463066b837ff6c4bf0.tar.gz
Upgrade !1441 to commit 5bb8301f
-rw-r--r--.SRCINFO2
-rw-r--r--PKGBUILD2
-rw-r--r--mr1441.patch1222
3 files changed, 1068 insertions, 158 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 61021363af84..b81f8480f712 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -38,6 +38,6 @@ pkgbase = mutter-dynamic-buffering
source = mutter-dynamic-buffering::git+https://gitlab.gnome.org/GNOME/mutter.git#commit=8de96d3d7c40e6b5289fd707fdd5e6d604f33e8f
source = mr1441.patch
sha256sums = SKIP
- sha256sums = 07d3c9d6740e509b79adc4ca93d31e655317758ac2526b104a0c5c7fa435ebf0
+ sha256sums = 592c03f4a492d39d760b174e487b3b2a58e9caef9b9ef886f5aa09abb94b69d3
pkgname = mutter-dynamic-buffering
diff --git a/PKGBUILD b/PKGBUILD
index ce0429979d2e..c10a1c5636d3 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -24,7 +24,7 @@ _commit=8de96d3d7c40e6b5289fd707fdd5e6d604f33e8f # tags/41.1^0
source=("$pkgname::git+https://gitlab.gnome.org/GNOME/mutter.git#commit=$_commit"
'mr1441.patch')
sha256sums=('SKIP'
- '07d3c9d6740e509b79adc4ca93d31e655317758ac2526b104a0c5c7fa435ebf0')
+ '592c03f4a492d39d760b174e487b3b2a58e9caef9b9ef886f5aa09abb94b69d3')
pkgver() {
cd $pkgname
diff --git a/mr1441.patch b/mr1441.patch
index e8f757f3f510..e54521258a25 100644
--- a/mr1441.patch
+++ b/mr1441.patch
@@ -1,13 +1,13 @@
Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
Source: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441
-Commit: f655a5b728d5139ef9f0a879f6c3a27217fe2bfd
-Rebase: Thu Jul 1 19:49:18 2021 +0800
+Commit: 5bb8301fad0f10e5cb2f9058c2edd0e3e1a2fee6
+Rebase: Fri Sep 24 22:03:14 2021 +0800
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
-index 05e79e662..75c192739 100644
+index 16098b70f..27bbfd323 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
-@@ -54,8 +54,9 @@ typedef enum _ClutterFrameClockState
+@@ -68,8 +68,9 @@ typedef enum _ClutterFrameClockState
CLUTTER_FRAME_CLOCK_STATE_INIT,
CLUTTER_FRAME_CLOCK_STATE_IDLE,
CLUTTER_FRAME_CLOCK_STATE_SCHEDULED,
@@ -19,75 +19,40 @@ index 05e79e662..75c192739 100644
} ClutterFrameClockState;
struct _ClutterFrameClock
-@@ -63,6 +64,7 @@ struct _ClutterFrameClock
- GObject parent;
-
- float refresh_rate;
-+ int64_t refresh_interval_us;
- ClutterFrameListener listener;
-
- GSource *source;
-@@ -71,6 +73,7 @@ struct _ClutterFrameClock
-
- ClutterFrameClockState state;
- int64_t last_dispatch_time_us;
-+ int64_t last_update_time_us;
- int64_t last_presentation_time_us;
-
- gboolean is_next_presentation_time_valid;
-@@ -93,6 +96,15 @@ clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock)
- return frame_clock->refresh_rate;
- }
+@@ -99,6 +100,8 @@ struct _ClutterFrameClock
+ /* Last KMS buffer submission time. */
+ int64_t last_flip_time_us;
-+static void
-+clutter_frame_clock_set_refresh_rate (ClutterFrameClock *frame_clock,
-+ float refresh_rate)
-+{
-+ frame_clock->refresh_rate = refresh_rate;
-+ frame_clock->refresh_interval_us =
-+ (int64_t) (0.5 + G_USEC_PER_SEC / refresh_rate);
-+}
++ ClutterFrameHint last_flip_hints;
+
- void
- clutter_frame_clock_add_timeline (ClutterFrameClock *frame_clock,
- ClutterTimeline *timeline)
-@@ -185,7 +197,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
- frame_clock->last_presentation_time_us = frame_info->presentation_time;
-
- if (frame_info->refresh_rate > 1)
-- frame_clock->refresh_rate = frame_info->refresh_rate;
-+ clutter_frame_clock_set_refresh_rate (frame_clock,
-+ frame_info->refresh_rate);
-
- switch (frame_clock->state)
- {
-@@ -194,11 +207,23 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
+ /* Last few durations between dispatch start and buffer swap. */
+ EstimateQueue dispatch_to_swap_us;
+ /* Last few durations between buffer swap and GPU rendering finish. */
+@@ -278,9 +281,21 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
g_warn_if_reached ();
break;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
- maybe_reschedule_update (frame_clock);
- break;
++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
++ maybe_reschedule_update (frame_clock);
++ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ /* The GPU has caught up now so we can start using
-+ * last_presentation_time_us again. So rather than dropping back to
++ * last_presentation_time_us again. But rather than dropping back to
+ * SCHEDULED, let's force a reschedule from IDLE. This way we'll get a
+ * more precise next update time based on last_presentation_time_us.
+ */
-+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
+ clutter_frame_clock_schedule_update (frame_clock);
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
-+ maybe_reschedule_update (frame_clock);
-+ break;
+ maybe_reschedule_update (frame_clock);
+ break;
}
- }
-
-@@ -212,11 +237,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
+@@ -296,11 +311,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
g_warn_if_reached ();
break;
@@ -108,25 +73,17 @@ index 05e79e662..75c192739 100644
}
}
-@@ -227,7 +259,6 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
- {
- int64_t last_presentation_time_us;
- int64_t now_us;
-- float refresh_rate;
- int64_t refresh_interval_us;
- int64_t min_render_time_allowed_us;
- int64_t max_render_time_allowed_us;
-@@ -238,8 +269,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
+@@ -375,7 +397,8 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
- now_us = g_get_monotonic_time ();
+ refresh_interval_us = frame_clock->refresh_interval_us;
-- refresh_rate = frame_clock->refresh_rate;
-- refresh_interval_us = (int64_t) (0.5 + G_USEC_PER_SEC / refresh_rate);
-+ refresh_interval_us = frame_clock->refresh_interval_us;
-
- if (frame_clock->last_presentation_time_us == 0)
+- if (frame_clock->last_presentation_time_us == 0)
++ if (frame_clock->last_presentation_time_us == 0 ||
++ frame_clock->state >= CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE)
{
-@@ -382,8 +412,12 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock)
+ *out_next_update_time_us =
+ frame_clock->last_dispatch_time_us ?
+@@ -518,8 +541,12 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock)
frame_clock->pending_reschedule = TRUE;
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
break;
@@ -141,7 +98,7 @@ index 05e79e662..75c192739 100644
break;
}
-@@ -419,11 +453,17 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
+@@ -555,11 +582,17 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_INIT:
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
next_update_time_us = g_get_monotonic_time ();
@@ -161,16 +118,15 @@ index 05e79e662..75c192739 100644
frame_clock->pending_reschedule = TRUE;
frame_clock->pending_reschedule_now = TRUE;
return;
-@@ -432,7 +472,7 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
+@@ -568,7 +601,6 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
g_warn_if_fail (next_update_time_us != -1);
g_source_set_ready_time (frame_clock->source, next_update_time_us);
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
-+ frame_clock->last_update_time_us = next_update_time_us;
frame_clock->is_next_presentation_time_valid = FALSE;
}
-@@ -451,6 +491,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
+@@ -587,6 +619,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
{
case CLUTTER_FRAME_CLOCK_STATE_INIT:
next_update_time_us = g_get_monotonic_time ();
@@ -178,7 +134,7 @@ index 05e79e662..75c192739 100644
break;
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
calculate_next_update_time_us (frame_clock,
-@@ -458,11 +499,19 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
+@@ -594,11 +627,27 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
&frame_clock->next_presentation_time_us);
frame_clock->is_next_presentation_time_valid =
(frame_clock->next_presentation_time_us != 0);
@@ -190,26 +146,42 @@ index 05e79e662..75c192739 100644
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
-+ g_assert (frame_clock->last_update_time_us);
-+ next_update_time_us = frame_clock->last_update_time_us +
-+ frame_clock->refresh_interval_us;
-+ frame_clock->is_next_presentation_time_valid = FALSE;
++ if (frame_clock->last_flip_hints & CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED)
++ {
++ /* Force double buffering, disable triple buffering */
++ frame_clock->pending_reschedule = TRUE;
++ return;
++ }
++
++ 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;
}
-@@ -470,7 +519,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
+@@ -606,7 +655,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
g_warn_if_fail (next_update_time_us != -1);
g_source_set_ready_time (frame_clock->source, next_update_time_us);
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
-+ frame_clock->last_update_time_us = next_update_time_us;
}
static void
-@@ -485,7 +534,21 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
+@@ -624,7 +672,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
+ frame_clock->refresh_interval_us;
+
+ lateness_us = time_us - ideal_dispatch_time_us;
+- if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us)
++ if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us / 4)
+ frame_clock->last_dispatch_lateness_us = 0;
+ else
+ frame_clock->last_dispatch_lateness_us = lateness_us;
+@@ -632,7 +680,21 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
frame_clock->last_dispatch_time_us = time_us;
g_source_set_ready_time (frame_clock->source, -1);
@@ -232,7 +204,7 @@ index 05e79e662..75c192739 100644
frame_count = frame_clock->frame_count++;
-@@ -509,25 +572,31 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
+@@ -656,25 +718,31 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
frame_clock->listener.user_data);
COGL_TRACE_END (ClutterFrameClockFrame);
@@ -242,11 +214,11 @@ index 05e79e662..75c192739 100644
- case CLUTTER_FRAME_CLOCK_STATE_INIT:
- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
- g_warn_if_reached ();
-+ case CLUTTER_FRAME_RESULT_PENDING_PRESENTED:
- break;
+- break;
- case CLUTTER_FRAME_CLOCK_STATE_IDLE:
- case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
-- break;
++ case CLUTTER_FRAME_RESULT_PENDING_PRESENTED:
+ break;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
- switch (result)
+ case CLUTTER_FRAME_RESULT_IDLE:
@@ -276,84 +248,907 @@ index 05e79e662..75c192739 100644
}
break;
}
-@@ -591,7 +660,7 @@ clutter_frame_clock_new (float refresh_rate,
+@@ -696,10 +764,12 @@ frame_clock_source_dispatch (GSource *source,
+ }
+
+ void
+-clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock,
+- int64_t flip_time_us)
++clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock,
++ int64_t flip_time_us,
++ ClutterFrameHint hints)
+ {
+ frame_clock->last_flip_time_us = flip_time_us;
++ frame_clock->last_flip_hints = hints;
+ }
+
+ GString *
+diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h
+index e71b54987..a37024e2d 100644
+--- a/clutter/clutter/clutter-frame-clock.h
++++ b/clutter/clutter/clutter-frame-clock.h
+@@ -34,6 +34,12 @@ typedef enum _ClutterFrameResult
+ CLUTTER_FRAME_RESULT_IDLE,
+ } ClutterFrameResult;
+
++typedef enum _ClutterFrameHint
++{
++ CLUTTER_FRAME_HINT_NONE = 0,
++ CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED = 1 << 0,
++} ClutterFrameHint;
++
+ #define CLUTTER_TYPE_FRAME_CLOCK (clutter_frame_clock_get_type ())
+ CLUTTER_EXPORT
+ G_DECLARE_FINAL_TYPE (ClutterFrameClock, clutter_frame_clock,
+@@ -91,8 +97,9 @@ void clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock,
+ CLUTTER_EXPORT
+ float clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock);
+
+-void clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock,
+- int64_t flip_time_us);
++void clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock,
++ int64_t flip_time_us,
++ ClutterFrameHint hints);
+
+ GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock);
+
+diff --git a/clutter/clutter/clutter-frame-private.h b/clutter/clutter/clutter-frame-private.h
+index e0088564f..06581492f 100644
+--- a/clutter/clutter/clutter-frame-private.h
++++ b/clutter/clutter/clutter-frame-private.h
+@@ -24,6 +24,7 @@ struct _ClutterFrame
+ {
+ gboolean has_result;
+ ClutterFrameResult result;
++ ClutterFrameHint hints;
+ };
+
+ #define CLUTTER_FRAME_INIT ((ClutterFrame) { 0 })
+diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c
+index 3c708da9d..63ae302af 100644
+--- a/clutter/clutter/clutter-frame.c
++++ b/clutter/clutter/clutter-frame.c
+@@ -40,3 +40,16 @@ clutter_frame_set_result (ClutterFrame *frame,
+ frame->result = result;
+ frame->has_result = TRUE;
+ }
++
++void
++clutter_frame_set_hint (ClutterFrame *frame,
++ ClutterFrameHint hint)
++{
++ frame->hints |= hint;
++}
++
++ClutterFrameHint
++clutter_frame_get_hints (ClutterFrame *frame)
++{
++ return frame->hints;
++}
+diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h
+index d3608e81c..06c5f7f28 100644
+--- a/clutter/clutter/clutter-frame.h
++++ b/clutter/clutter/clutter-frame.h
+@@ -33,4 +33,11 @@ void clutter_frame_set_result (ClutterFrame *frame,
+ CLUTTER_EXPORT
+ gboolean clutter_frame_has_result (ClutterFrame *frame);
+
++CLUTTER_EXPORT
++void clutter_frame_set_hint (ClutterFrame *frame,
++ ClutterFrameHint hint);
++
++CLUTTER_EXPORT
++ClutterFrameHint clutter_frame_get_hints (ClutterFrame *frame);
++
+ #endif /* CLUTTER_FRAME_H */
+diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c
+index 9b7345983..f8da6e37c 100644
+--- a/clutter/clutter/clutter-stage-view.c
++++ b/clutter/clutter/clutter-stage-view.c
+@@ -1189,8 +1189,9 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock,
+
+ _clutter_stage_window_redraw_view (stage_window, view, &frame);
+
+- clutter_frame_clock_record_flip_time (frame_clock,
+- g_get_monotonic_time ());
++ clutter_frame_clock_record_flip (frame_clock,
++ g_get_monotonic_time (),
++ clutter_frame_get_hints (&frame));
+
+ clutter_stage_emit_after_paint (stage, view);
+
+diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h
+index dffe018d2..e0215f750 100644
+--- a/cogl/cogl/cogl-onscreen-private.h
++++ b/cogl/cogl/cogl-onscreen-private.h
+@@ -97,4 +97,7 @@ cogl_onscreen_peek_tail_frame_info (CoglOnscreen *onscreen);
+ COGL_EXPORT CoglFrameInfo *
+ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen);
+
++COGL_EXPORT unsigned int
++cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen);
++
+ #endif /* __COGL_ONSCREEN_PRIVATE_H */
+diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c
+index cff5df50c..6f159cbb2 100644
+--- a/cogl/cogl/cogl-onscreen.c
++++ b/cogl/cogl/cogl-onscreen.c
+@@ -497,6 +497,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen)
+ return g_queue_pop_head (&priv->pending_frame_infos);
+ }
+
++unsigned int
++cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen)
++{
++ CoglOnscreenPrivate *priv = cogl_onscreen_get_instance_private (onscreen);
++
++ return g_queue_get_length (&priv->pending_frame_infos);
++}
++
+ CoglFrameClosure *
+ cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen,
+ CoglFrameCallback callback,
+diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c
+index 28534ec81..bbed4fcf2 100644
+--- a/src/backends/meta-stage-impl.c
++++ b/src/backends/meta-stage-impl.c
+@@ -670,6 +670,8 @@ meta_stage_impl_redraw_view (ClutterStageWindow *stage_window,
+ {
+ g_autoptr (GError) error = NULL;
+
++ clutter_frame_set_hint (frame, CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED);
++
+ if (meta_stage_impl_scanout_view (stage_impl,
+ stage_view,
+ scanout,
+diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
+index effa0851d..24e089a88 100644
+--- a/src/backends/native/meta-cursor-renderer-native.c
++++ b/src/backends/native/meta-cursor-renderer-native.c
+@@ -64,19 +64,6 @@
+ #define DRM_CAP_CURSOR_HEIGHT 0x9
+ #endif
+
+-/* When animating a cursor, we usually call drmModeSetCursor2 once per frame.
+- * Though, testing shows that we need to triple buffer the cursor buffer in
+- * order to avoid glitches when animating the cursor, at least when running on
+- * Intel. The reason for this might be (but is not confirmed to be) due to
+- * the user space gbm_bo cache, making us reuse and overwrite the kernel side
+- * buffer content before it was scanned out. To avoid this, we keep a user space
+- * reference to each buffer we set until at least one frame after it was drawn.
+- * In effect, this means we three active cursor gbm_bo's: one that that just has
+- * been set, one that was previously set and may or may not have been scanned
+- * out, and one pending that will be replaced if the cursor sprite changes.
+- */
+-#define HW_CURSOR_BUFFER_COUNT 3
+-
+ static GQuark quark_cursor_sprite = 0;
+
+ typedef struct _CrtcCursorData
+@@ -110,19 +97,10 @@ typedef struct _MetaCursorRendererNativeGpuData
+ uint64_t cursor_height;
+ } MetaCursorRendererNativeGpuData;
+
+-typedef enum _MetaCursorBufferState
+-{
+- META_CURSOR_BUFFER_STATE_NONE,
+- META_CURSOR_BUFFER_STATE_SET,
+- META_CURSOR_BUFFER_STATE_INVALIDATED,
+-} MetaCursorBufferState;
+-
+ typedef struct _MetaCursorNativeGpuState
+ {
+ MetaGpu *gpu;
+- unsigned int active_buffer_idx;
+- MetaCursorBufferState pending_buffer_state;
+- MetaDrmBuffer *buffers[HW_CURSOR_BUFFER_COUNT];
++ MetaDrmBuffer *buffer;
+ } MetaCursorNativeGpuState;
+
+ typedef struct _MetaCursorNativePrivate
+@@ -199,44 +177,17 @@ meta_cursor_renderer_native_finalize (GObject *object)
+ G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object);
+ }
+
+-static unsigned int
+-get_pending_cursor_sprite_buffer_index (MetaCursorNativeGpuState *cursor_gpu_state)
+-{
+- return (cursor_gpu_state->active_buffer_idx + 1) % HW_CURSOR_BUFFER_COUNT;
+-}
+-
+-static MetaDrmBuffer *
+-get_pending_cursor_sprite_buffer (MetaCursorNativeGpuState *cursor_gpu_state)
+-{
+- unsigned int pending_buffer_idx;
+-
+- pending_buffer_idx =
+- get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
+- return cursor_gpu_state->buffers[pending_buffer_idx];
+-}
+-
+-static MetaDrmBuffer *
+-get_active_cursor_sprite_buffer (MetaCursorNativeGpuState *cursor_gpu_state)
+-{
+- return cursor_gpu_state->buffers[cursor_gpu_state->active_buffer_idx];
+-}
+-
+ static void
+-set_pending_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
+- MetaGpuKms *gpu_kms,
+- MetaDrmBuffer *buffer)
++set_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
++ MetaGpuKms *gpu_kms,
++ MetaDrmBuffer *buffer)
+ {
+ MetaCursorNativePrivate *cursor_priv;
+ MetaCursorNativeGpuState *cursor_gpu_state;
+- unsigned int pending_buffer_idx;
+
+ cursor_priv = ensure_cursor_priv (cursor_sprite);
+ cursor_gpu_state = ensure_cursor_gpu_state (cursor_priv, gpu_kms);
+-
+- pending_buffer_idx =
+- get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
+- cursor_gpu_state->buffers[pending_buffer_idx] = buffer;
+- cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_SET;
++ cursor_gpu_state->buffer = buffer;
+ }
+
+ static void
+@@ -311,10 +262,7 @@ assign_cursor_plane (MetaCursorRendererNative *native,
+ MetaKmsUpdate *kms_update;
+ MetaKmsPlaneAssignment *plane_assignment;
+
+- if (cursor_gpu_state->pending_buffer_state == META_CURSOR_BUFFER_STATE_SET)
+- buffer = get_pending_cursor_sprite_buffer (cursor_gpu_state);
+- else
+- buffer = get_active_cursor_sprite_buffer (cursor_gpu_state);
++ buffer = cursor_gpu_state->buffer;
+
+ kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
+ kms_device = meta_kms_crtc_get_device (kms_crtc);
+@@ -365,13 +313,6 @@ assign_cursor_plane (MetaCursorRendererNative *native,
+ native);
+
+ crtc_cursor_data->buffer = buffer;
+-
+- if (cursor_gpu_state->pending_buffer_state == META_CURSOR_BUFFER_STATE_SET)
+- {
+- cursor_gpu_state->active_buffer_idx =
+- (cursor_gpu_state->active_buffer_idx + 1) % HW_CURSOR_BUFFER_COUNT;
+- cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_NONE;
+- }
+ }
+
+ static float
+@@ -599,19 +540,7 @@ has_valid_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
+ if (!cursor_gpu_state)
+ return FALSE;
+
+- switch (cursor_gpu_state->pending_buffer_state)
+- {
+- case META_CURSOR_BUFFER_STATE_NONE:
+- return get_active_cursor_sprite_buffer (cursor_gpu_state) != NULL;
+- case META_CURSOR_BUFFER_STATE_SET:
+- return TRUE;
+- case META_CURSOR_BUFFER_STATE_INVALIDATED:
+- return FALSE;
+- }
+-
+- g_assert_not_reached ();
+-
+- return FALSE;
++ return cursor_gpu_state->buffer != NULL;
+ }
+
+ static void
+@@ -1114,16 +1043,14 @@ unset_crtc_cursor_renderer_privates (MetaGpu *gpu,
+ static void
+ cursor_gpu_state_free (MetaCursorNativeGpuState *cursor_gpu_state)
+ {
+- int i;
+ MetaDrmBuffer *active_buffer;
+
+- active_buffer = get_active_cursor_sprite_buffer (cursor_gpu_state);
++ active_buffer = cursor_gpu_state->buffer;
+ if (active_buffer)
+ unset_crtc_cursor_renderer_privates (cursor_gpu_state->gpu,
+ active_buffer);
+
+- for (i = 0; i < HW_CURSOR_BUFFER_COUNT; i++)
+- g_clear_object (&cursor_gpu_state->buffers[i]);
++ g_clear_object (&cursor_gpu_state->buffer);
+ g_free (cursor_gpu_state);
+ }
+
+@@ -1160,14 +1087,7 @@ invalidate_cursor_gpu_state (MetaCursorSprite *cursor_sprite)
+
+ g_hash_table_iter_init (&iter, cursor_priv->gpu_states);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cursor_gpu_state))
+- {
+- unsigned int pending_buffer_idx;
+-
+- pending_buffer_idx = get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
+- g_clear_object (&cursor_gpu_state->buffers[pending_buffer_idx]);
+- cursor_gpu_state->pending_buffer_state =
+- META_CURSOR_BUFFER_STATE_INVALIDATED;
+- }
++ g_clear_object (&cursor_gpu_state->buffer);
+ }
+
+ static void
+@@ -1302,8 +1222,8 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
+ return;
+ }
+
+- set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms,
+- META_DRM_BUFFER (buffer_gbm));
++ set_cursor_sprite_buffer (cursor_sprite, gpu_kms,
++ META_DRM_BUFFER (buffer_gbm));
+ }
+ else
+ {
+@@ -1311,34 +1231,6 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
+ }
+ }
+
+-static gboolean
+-is_cursor_hw_state_valid (MetaCursorSprite *cursor_sprite,
+- MetaGpuKms *gpu_kms)
+-{
+- MetaCursorNativePrivate *cursor_priv;
+- MetaCursorNativeGpuState *cursor_gpu_state;
+-
+- cursor_priv = get_cursor_priv (cursor_sprite);
+- if (!cursor_priv)
+- return FALSE;
+-
+- cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms);
+- if (!cursor_gpu_state)
+- return FALSE;
+-
+- switch (cursor_gpu_state->pending_buffer_state)
+- {
+- case META_CURSOR_BUFFER_STATE_SET:
+- case META_CURSOR_BUFFER_STATE_NONE:
+- return TRUE;
+- case META_CURSOR_BUFFER_STATE_INVALIDATED:
+- return FALSE;
+- }
+-
+- g_assert_not_reached ();
+- return FALSE;
+-}
+-
+ static gboolean
+ is_cursor_scale_and_transform_valid (MetaCursorRenderer *renderer,
+ MetaCursorSprite *cursor_sprite)
+@@ -1503,7 +1395,7 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer,
+ if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken)
+ return;
+
+- if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) &&
++ if (has_valid_cursor_sprite_buffer (cursor_sprite, gpu_kms) &&
+ is_cursor_scale_and_transform_valid (renderer, cursor_sprite))
+ return;
+
+@@ -1642,8 +1534,8 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer,
+ return;
+ }
+
+- set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms,
+- META_DRM_BUFFER (buffer_gbm));
++ set_cursor_sprite_buffer (cursor_sprite, gpu_kms,
++ META_DRM_BUFFER (buffer_gbm));
+ }
+ }
+ #endif
+@@ -1667,7 +1559,7 @@ realize_cursor_sprite_from_xcursor_for_gpu (MetaCursorRenderer *renderer,
+ if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken)
+ return;
+
+- if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) &&
++ if (has_valid_cursor_sprite_buffer (cursor_sprite, gpu_kms) &&
+ is_cursor_scale_and_transform_valid (renderer, cursor_sprite))
+ return;
+
+diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c
+index 3e0506026..75109aa20 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
+ MetaKmsCrtcState current_state;
+
+ MetaKmsCrtcPropTable prop_table;
++
++ GHashTable *plane_states;
+ };
+
+ G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT)
+@@ -435,20 +443,86 @@ meta_kms_crtc_new (MetaKmsImplDevice *impl_device,
+ return crtc;
+ }
+
++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_return_if_fail (plane_state);
++ 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)
++{
++ g_hash_table_foreach (crtc->plane_states, swap_plane_buffers, NULL);
++}
++
++static void
++meta_kms_crtc_dispose (GObject *object)
++{
++ MetaKmsCrtc *crtc = META_KMS_CRTC (object);
++
++ g_hash_table_remove_all (crtc->plane_states);
++
++ G_OBJECT_CLASS (meta_kms_crtc_parent_class)->dispose (object);
++}
++
+ static void
+ meta_kms_crtc_finalize (GObject *object)
+ {
+ MetaKmsCrtc *crtc = META_KMS_CRTC (object);
+
+ clear_gamma_state (&crtc->current_state);
++ g_hash_table_unref (crtc->plane_states);
+
+ 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)
+ {
+ crtc->current_state.gamma.size = 0;
++ crtc->plane_states = g_hash_table_new_full (NULL,
++ NULL,
++ NULL,
++ destroy_plane_state);
+ }
+
+ static void
+@@ -456,5 +530,6 @@ meta_kms_crtc_class_init (MetaKmsCrtcClass *klass)
+ {
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
++ object_class->dispose = meta_kms_crtc_dispose;
+ object_class->finalize = meta_kms_crtc_finalize;
+ }
+diff --git a/src/backends/native/meta-kms-crtc.h b/src/backends/native/meta-kms-crtc.h
+index 406ca3ac1..1a0d9f1b6 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 "meta/boxes.h"
+
+ typedef struct _MetaKmsCrtcState
+@@ -76,4 +77,10 @@ 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);
++
+ #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 674a24902..718f4b3e1 100644
+--- a/src/backends/native/meta-kms-impl-device-atomic.c
++++ b/src/backends/native/meta-kms-impl-device-atomic.c
+@@ -495,6 +495,10 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
+ error))
+ return FALSE;
+ }
++
++ meta_kms_crtc_remember_plane_buffer (plane_assignment->crtc,
++ meta_kms_plane_get_id (plane),
++ buffer);
+ }
+ else
+ {
+@@ -522,6 +526,10 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
+ error))
+ return FALSE;
+ }
++
++ meta_kms_crtc_remember_plane_buffer (plane_assignment->crtc,
++ meta_kms_plane_get_id (plane),
++ NULL);
+ }
+
+ if (plane_assignment->rotation)
+diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c
+index 28d512720..2f3c99928 100644
+--- a/src/backends/native/meta-kms-impl-device-simple.c
++++ b/src/backends/native/meta-kms-impl-device-simple.c
+@@ -1275,7 +1275,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
+ {
+ case META_KMS_PLANE_TYPE_PRIMARY:
+ /* Handled as part of the mode-set and page flip. */
+- return TRUE;
++ goto assigned;
+ case META_KMS_PLANE_TYPE_CURSOR:
+ if (!process_cursor_plane_assignment (impl_device, update,
+ plane_assignment,
+@@ -1289,7 +1289,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
+ }
+ else
+ {
+- return TRUE;
++ goto assigned;
+ }
+ case META_KMS_PLANE_TYPE_OVERLAY:
+ error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
+@@ -1302,6 +1302,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);
++ return TRUE;
+ }
+
+ static gboolean
+diff --git a/src/backends/native/meta-kms-page-flip.c b/src/backends/native/meta-kms-page-flip.c
+index 817f4e7c8..148d23740 100644
+--- a/src/backends/native/meta-kms-page-flip.c
++++ b/src/backends/native/meta-kms-page-flip.c
+@@ -24,6 +24,7 @@
+ #include "backends/native/meta-kms-impl.h"
+ #include "backends/native/meta-kms-private.h"
+ #include "backends/native/meta-kms-update.h"
++#include "backends/native/meta-kms-crtc.h"
+
+ typedef struct _MetaKmsPageFlipClosure
+ {
+@@ -149,6 +150,8 @@ meta_kms_page_flip_data_flipped (MetaKms *kms,
+
+ meta_assert_not_in_kms_impl (kms);
+
++ meta_kms_crtc_on_scanout_started (page_flip_data->crtc);
++
+ for (l = page_flip_data->closures; l; l = l->next)
+ {
+ MetaKmsPageFlipClosure *closure = l->data;
+diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c
+index 71e5b423f..a98e8ade6 100644
+--- a/src/backends/native/meta-kms-update.c
++++ b/src/backends/native/meta-kms-update.c
+@@ -140,6 +140,7 @@ static void
+ meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment)
+ {
+ g_clear_pointer (&plane_assignment->fb_damage, meta_kms_fb_damage_free);
++ g_clear_object (&plane_assignment->buffer);
+ g_free (plane_assignment);
+ }
- init_frame_clock_source (frame_clock);
+@@ -220,7 +221,7 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update,
+ .update = update,
+ .crtc = crtc,
+ .plane = plane,
+- .buffer = buffer,
++ .buffer = g_object_ref (buffer),
+ .src_rect = src_rect,
+ .dst_rect = dst_rect,
+ .flags = flags,
+diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c
+index 0750278ae..061460b38 100644
+--- a/src/backends/native/meta-kms.c
++++ b/src/backends/native/meta-kms.c
+@@ -177,6 +177,8 @@ struct _MetaKms
-- frame_clock->refresh_rate = refresh_rate;
-+ clutter_frame_clock_set_refresh_rate (frame_clock, refresh_rate);
+ GList *pending_callbacks;
+ guint callback_source_id;
++
++ gboolean shutting_down;
+ };
+
+ G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT)
+@@ -277,6 +279,9 @@ meta_kms_post_pending_update_sync (MetaKms *kms,
+ COGL_TRACE_BEGIN_SCOPED (MetaKmsPostUpdateSync,
+ "KMS (post update)");
- return frame_clock;
++ if (kms->shutting_down)
++ return NULL;
++
+ update = meta_kms_take_pending_update (kms, device);
+ if (!update)
+ return NULL;
+@@ -757,6 +762,7 @@ prepare_shutdown_in_impl (MetaKmsImpl *impl,
+ void
+ meta_kms_prepare_shutdown (MetaKms *kms)
+ {
++ kms->shutting_down = TRUE;
+ meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL);
+ flush_callbacks (kms);
}
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
-index da75b03ec..a9d1f1b31 100644
+index 00b2d9f89..eb1e7412a 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
-@@ -91,6 +91,7 @@ struct _MetaOnscreenNative
+@@ -46,6 +46,8 @@
+ #include "backends/native/meta-renderer-native-gles3.h"
+ #include "backends/native/meta-renderer-native-private.h"
+
++#define MAX_CONCURRENT_POSTS 1
++
+ typedef enum _MetaSharedFramebufferImportStatus
+ {
+ /* Not tried importing yet. */
+@@ -65,7 +67,6 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState
+
struct {
struct gbm_surface *surface;
- MetaDrmBuffer *current_fb;
-+ MetaDrmBuffer *pending_fb;
+- MetaDrmBuffer *current_fb;
MetaDrmBuffer *next_fb;
} gbm;
-@@ -113,6 +114,9 @@ init_secondary_gpu_state (MetaRendererNative *renderer_native,
- CoglOnscreen *onscreen,
- GError **error);
+@@ -92,8 +93,13 @@ struct _MetaOnscreenNative
-+static void
-+meta_onscreen_native_flip_next_fb (CoglOnscreen *onscreen);
+ struct {
+ struct gbm_surface *surface;
+- MetaDrmBuffer *current_fb;
+ MetaDrmBuffer *next_fb;
+
- static void
- swap_secondary_drm_fb (CoglOnscreen *onscreen)
- {
-@@ -155,15 +159,19 @@ meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
- {
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
++ struct {
++ uint32_t format;
++ uint32_t stride;
++ uint64_t modifier;
++ } last_flip;
+ } gbm;
-- if (!onscreen_native->gbm.next_fb)
-+ if (!onscreen_native->gbm.next_fb && !onscreen_native->gbm.pending_fb)
- return;
+ #ifdef HAVE_EGL_DEVICE
+@@ -105,69 +111,25 @@ struct _MetaOnscreenNative
+ #endif
- free_current_bo (onscreen);
+ MetaRendererView *view;
++
++ unsigned int swaps_pending;
++ struct {
++ int *rectangles; /* 4 x n_rectangles */
++ int n_rectangles;
++ } next_post;
+ };
-- g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb);
-- g_clear_object (&onscreen_native->gbm.next_fb);
-+ g_set_object (&onscreen_native->gbm.current_fb,
-+ onscreen_native->gbm.pending_fb);
-+ g_clear_object (&onscreen_native->gbm.pending_fb);
+ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native,
+ COGL_TYPE_ONSCREEN_EGL)
- swap_secondary_drm_fb (onscreen);
++static void
++post_latest_swap (CoglOnscreen *onscreen);
+
-+ if (onscreen_native->gbm.next_fb)
-+ meta_onscreen_native_flip_next_fb (onscreen);
- }
+ static gboolean
+ init_secondary_gpu_state (MetaRendererNative *renderer_native,
+ CoglOnscreen *onscreen,
+ GError **error);
+-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 void
+-free_current_bo (CoglOnscreen *onscreen)
+-{
+- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
+-
+- g_clear_object (&onscreen_native->gbm.current_fb);
+- free_current_secondary_bo (onscreen);
+-}
+-
+-static void
+-meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
+-{
+- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
+-
+- if (!onscreen_native->gbm.next_fb)
+- return;
+-
+- free_current_bo (onscreen);
+-
+- g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb);
+- g_clear_object (&onscreen_native->gbm.next_fb);
+-
+- swap_secondary_drm_fb (onscreen);
+-}
+-
static void
-@@ -201,8 +209,6 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen)
+ maybe_update_frame_info (MetaCrtc *crtc,
+ CoglFrameInfo *frame_info,
+@@ -203,7 +165,7 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen)
info = cogl_onscreen_pop_head_frame_info (onscreen);
- g_assert (!cogl_onscreen_peek_head_frame_info (onscreen));
--
++ g_assert (info);
+
_cogl_onscreen_notify_frame_sync (onscreen, info);
_cogl_onscreen_notify_complete (onscreen, info);
- cogl_object_unref (info);
-@@ -449,6 +455,8 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
+@@ -230,7 +192,7 @@ notify_view_crtc_presented (MetaRendererView *view,
+ maybe_update_frame_info (crtc, frame_info, time_us, flags, sequence);
+
+ meta_onscreen_native_notify_frame_complete (onscreen);
+- meta_onscreen_native_swap_drm_fb (onscreen);
++ post_latest_swap (onscreen);
+ }
+
+ static int64_t
+@@ -292,6 +254,7 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc,
+ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
+
+ meta_onscreen_native_notify_frame_complete (onscreen);
++ post_latest_swap (onscreen);
+ }
+
+ static void
+@@ -341,7 +304,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);
++ post_latest_swap (onscreen);
+ }
+
+ static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = {
+@@ -405,11 +368,10 @@ meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen)
+ {
+ CoglFrameInfo *frame_info;
+
+- meta_onscreen_native_swap_drm_fb (onscreen);
+-
+ frame_info = cogl_onscreen_peek_tail_frame_info (onscreen);
+ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
+ meta_onscreen_native_notify_frame_complete (onscreen);
++ post_latest_swap (onscreen);
+ }
+
+ static void
+@@ -431,7 +393,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
+ MetaKms *kms;
+ MetaKmsUpdate *kms_update;
+ MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL;
+- MetaDrmBuffer *buffer;
++ g_autoptr (MetaDrmBuffer) buffer = NULL;
+ MetaKmsPlaneAssignment *plane_assignment;
+
+ COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs,
+@@ -451,12 +413,14 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
+ case META_RENDERER_NATIVE_MODE_GBM:
if (gpu_kms == render_gpu)
{
- buffer = onscreen_native->gbm.next_fb;
-+ g_set_object (&onscreen_native->gbm.pending_fb, buffer);
+- buffer = onscreen_native->gbm.next_fb;
++ buffer = g_object_ref (onscreen_native->gbm.next_fb);
+ g_clear_object (&onscreen_native->gbm.next_fb);
}
else
{
-@@ -980,17 +988,10 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
- {
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer);
-- CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
- CoglRenderer *cogl_renderer = cogl_context->display->renderer;
+ secondary_gpu_state = onscreen_native->secondary_gpu_state;
+- buffer = secondary_gpu_state->gbm.next_fb;
++ buffer = g_object_ref (secondary_gpu_state->gbm.next_fb);
++ g_clear_object (&secondary_gpu_state->gbm.next_fb);
+ }
+
+ plane_assignment = meta_crtc_kms_assign_primary_plane (crtc_kms,
+@@ -468,6 +432,21 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
+ meta_kms_plane_assignment_set_fb_damage (plane_assignment,
+ rectangles, n_rectangles);
+ }
++
++ if (META_IS_DRM_BUFFER_GBM (buffer))
++ {
++ MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
++ struct gbm_bo *gbm_bo = meta_drm_buffer_gbm_get_bo (buffer_gbm);
++
++ onscreen_native->gbm.last_flip.format = gbm_bo_get_format (gbm_bo);
++ onscreen_native->gbm.last_flip.modifier = gbm_bo_get_modifier (gbm_bo);
++ onscreen_native->gbm.last_flip.stride = gbm_bo_get_stride (gbm_bo);
++
++ g_object_set_data_full (G_OBJECT (buffer_gbm),
++ "gbm_surface owner",
++ g_object_ref (onscreen),
++ (GDestroyNotify) g_object_unref);
++ }
+ break;
+ case META_RENDERER_NATIVE_MODE_SURFACELESS:
+ g_assert_not_reached ();
+@@ -556,7 +535,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);
+
+@@ -1004,12 +982,6 @@ 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;
@@ -365,8 +1160,8 @@ index da75b03ec..a9d1f1b31 100644
- MetaKms *kms = meta_backend_native_get_kms (backend_native);
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
- MetaKmsDevice *render_kms_device = meta_gpu_kms_get_kms_device (render_gpu);
-@@ -998,14 +999,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+ MetaDeviceFile *render_device_file;
+@@ -1017,14 +989,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
CoglOnscreenClass *parent_class;
gboolean egl_context_changed = FALSE;
gboolean use_modifiers;
@@ -376,27 +1171,44 @@ index da75b03ec..a9d1f1b31 100644
- MetaKmsCrtc *kms_crtc;
- MetaKmsDevice *kms_device;
- MetaKmsUpdateFlag flags;
- g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
+- g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
- const GError *feedback_error;
++ size_t rectangles_size;
COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers,
"Onscreen (swap-buffers)");
-@@ -1054,6 +1050,38 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+@@ -1073,6 +1040,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+ #endif
+ }
++ clutter_frame_set_result (frame,
++ CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
++
update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed);
-+ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
+ /*
+@@ -1084,6 +1054,48 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+ if (egl_context_changed)
+ _cogl_winsys_egl_ensure_current (cogl_display);
+
++ 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;
++
++ onscreen_native->swaps_pending++;
+
-+ if (!onscreen_native->gbm.pending_fb)
-+ meta_onscreen_native_flip_next_fb (onscreen);
++ /* The new frame is already counted by cogl so that's why it is "<=" */
++ if (cogl_onscreen_count_pending_frames (onscreen) <= MAX_CONCURRENT_POSTS)
++ post_latest_swap (onscreen);
+}
+
+static void
-+meta_onscreen_native_flip_next_fb (CoglOnscreen *onscreen)
++post_latest_swap (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer);
-+ CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
+ CoglRenderer *cogl_renderer = cogl_context->display->renderer;
+ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
+ MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
@@ -408,19 +1220,32 @@ index da75b03ec..a9d1f1b31 100644
+ meta_backend_get_monitor_manager (backend);
+ MetaKms *kms = meta_backend_native_get_kms (backend_native);
+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
-+ gboolean egl_context_changed = FALSE;
+ MetaPowerSave power_save_mode;
-+ g_autoptr (GError) error = NULL;
-+ MetaKmsCrtc *kms_crtc;
-+ MetaKmsDevice *kms_device;
++ 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;
+ g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
+ const GError *feedback_error;
+
- /*
- * If we changed EGL context, cogl will have the wrong idea about what is
- * current, making it fail to set it when it needs to. Avoid that by making
-@@ -1076,8 +1104,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
++ if (onscreen_native->swaps_pending == 0)
++ return;
++
++ onscreen_native->swaps_pending--;
++
+ power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager);
+ if (power_save_mode == META_POWER_SAVE_ON)
+ {
+@@ -1092,15 +1104,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+ onscreen_native->view,
+ onscreen_native->crtc,
+ META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE,
+- rectangles,
+- n_rectangles);
++ onscreen_native->next_post.rectangles,
++ onscreen_native->next_post.n_rectangles);
+ }
+ else
{
meta_renderer_native_queue_power_save_page_flip (renderer_native,
onscreen);
@@ -429,7 +1254,7 @@ index da75b03ec..a9d1f1b31 100644
return;
}
-@@ -1095,9 +1121,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+@@ -1118,9 +1128,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));
@@ -439,7 +1264,7 @@ index da75b03ec..a9d1f1b31 100644
return;
}
else if (meta_renderer_native_has_pending_mode_set (renderer_native))
-@@ -1107,8 +1130,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+@@ -1130,8 +1137,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
meta_renderer_native_notify_mode_sets_reset (renderer_native);
meta_renderer_native_post_mode_set_updates (renderer_native);
@@ -448,16 +1273,21 @@ index da75b03ec..a9d1f1b31 100644
return;
}
break;
-@@ -1121,8 +1142,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+@@ -1144,8 +1149,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
{
meta_renderer_native_notify_mode_sets_reset (renderer_native);
meta_renderer_native_post_mode_set_updates (renderer_native);
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
- return;
+ return;
}
break;
-@@ -1140,13 +1159,8 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+@@ -1159,17 +1162,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
+
+ flags = META_KMS_UPDATE_FLAG_NONE;
+ kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags);
++ g_return_if_fail (kms_feedback != NULL);
+
switch (meta_kms_feedback_get_result (kms_feedback))
{
case META_KMS_FEEDBACK_PASSED:
@@ -471,12 +1301,92 @@ index da75b03ec..a9d1f1b31 100644
feedback_error = meta_kms_feedback_get_error (kms_feedback);
if (!g_error_matches (feedback_error,
G_IO_ERROR,
-@@ -1284,7 +1298,7 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
+@@ -1187,8 +1186,6 @@ meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen,
+ {
+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
+ const MetaCrtcConfig *crtc_config;
+- MetaDrmBuffer *fb;
+- struct gbm_bo *gbm_bo;
+
+ crtc_config = meta_crtc_get_config (onscreen_native->crtc);
+ if (crtc_config->transform != META_MONITOR_TRANSFORM_NORMAL)
+@@ -1200,23 +1197,13 @@ meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen,
+ if (!onscreen_native->gbm.surface)
+ return FALSE;
+
+- fb = onscreen_native->gbm.current_fb ? onscreen_native->gbm.current_fb
+- : onscreen_native->gbm.next_fb;
+- if (!fb)
++ if (drm_format != onscreen_native->gbm.last_flip.format)
+ return FALSE;
+
+- if (!META_IS_DRM_BUFFER_GBM (fb))
++ if (drm_modifier != onscreen_native->gbm.last_flip.modifier)
+ return FALSE;
+
+- gbm_bo = meta_drm_buffer_gbm_get_bo (META_DRM_BUFFER_GBM (fb));
+-
+- if (gbm_bo_get_format (gbm_bo) != drm_format)
+- return FALSE;
+-
+- if (gbm_bo_get_modifier (gbm_bo) != drm_modifier)
+- return FALSE;
+-
+- if (gbm_bo_get_stride (gbm_bo) != stride)
++ if (stride != onscreen_native->gbm.last_flip.stride)
+ return FALSE;
+
+ return TRUE;
+@@ -1272,6 +1259,16 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
+ return FALSE;
+ }
+
++ /* The new frame is already counted by cogl so that's why it is ">" */
++ if (cogl_onscreen_count_pending_frames (onscreen) > MAX_CONCURRENT_POSTS)
++ {
++ g_set_error_literal (error,
++ COGL_SCANOUT_ERROR,
++ COGL_SCANOUT_ERROR_INHIBITED,
++ "Direct scanout is inhibited during triple buffering");
++ return FALSE;
++ }
++
+ renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
+ render_gpu);
+
+@@ -1338,7 +1335,6 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
break;
- g_clear_object (&onscreen_native->gbm.next_fb);
-+ g_clear_object (&onscreen_native->gbm.pending_fb);
g_propagate_error (error, g_error_copy (feedback_error));
return FALSE;
}
+@@ -1374,6 +1370,9 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
+ g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
+ const GError *error;
+
++ if (cogl_onscreen_count_pending_frames (onscreen) >= MAX_CONCURRENT_POSTS)
++ return;
++
+ kms_update = meta_kms_get_pending_update (kms, kms_device);
+ if (!kms_update)
+ {
+@@ -2095,8 +2094,6 @@ meta_onscreen_native_dispose (GObject *object)
+ /* flip state takes a reference on the onscreen so there should
+ * never be outstanding flips when we reach here. */
+ g_warn_if_fail (onscreen_native->gbm.next_fb == NULL);
+-
+- free_current_bo (onscreen);
+ break;
+ case META_RENDERER_NATIVE_MODE_SURFACELESS:
+ g_assert_not_reached ();
+@@ -2127,6 +2124,8 @@ meta_onscreen_native_dispose (GObject *object)
+ g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy);
+ g_clear_pointer (&onscreen_native->secondary_gpu_state,
+ secondary_gpu_state_free);
++ g_clear_pointer (&onscreen_native->next_post.rectangles, g_free);
++ onscreen_native->next_post.n_rectangles = 0;
+ }
+
+ static void