diff options
author | Joakim Soderlund | 2021-11-05 19:35:21 +0100 |
---|---|---|
committer | Joakim Soderlund | 2021-11-05 19:57:04 +0100 |
commit | fc50b639f5bdf1056ef439463066b837ff6c4bf0 (patch) | |
tree | 375a502cb8850b2fa071e6d45ca009a6af9170f5 | |
parent | 4bee016f1d8e76251182db62544104d4e9ede17c (diff) | |
download | aur-fc50b639f5bdf1056ef439463066b837ff6c4bf0.tar.gz |
Upgrade !1441 to commit 5bb8301f
-rw-r--r-- | .SRCINFO | 2 | ||||
-rw-r--r-- | PKGBUILD | 2 | ||||
-rw-r--r-- | mr1441.patch | 1222 |
3 files changed, 1068 insertions, 158 deletions
@@ -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 @@ -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 |