From 27105307c5b8026dc8244b39111f72f0d39e1be5 Mon Sep 17 00:00:00 2001 From: Mingi Sung Date: Thu, 9 Nov 2023 16:48:47 +0900 Subject: [PATCH] Sync to gnome-45 Signed-off-by: Mingi Sung --- .SRCINFO | 8 +- PKGBUILD | 8 +- mr1441.patch | 4822 +++++++++++++++++++------------------------------- 3 files changed, 1806 insertions(+), 3032 deletions(-) diff --git a/.SRCINFO b/.SRCINFO index bdfe031..9abe64b 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -1,7 +1,7 @@ pkgbase = mutter-performance pkgdesc = A window manager for GNOME | Attempts to improve performances with non-upstreamed merge-requests and frequent stable branch resync - pkgver = 45.0+r49+g8d3d8b86e - pkgrel = 2 + pkgver = 45.1 + pkgrel = 1 epoch = 1 url = https://gitlab.gnome.org/GNOME/mutter arch = x86_64 @@ -37,11 +37,11 @@ pkgbase = mutter-performance depends = pipewire depends = startup-notification depends = xorg-xwayland - source = mutter-performance::git+https://gitlab.gnome.org/GNOME/mutter.git#commit=8d3d8b86e517c97accf11eb243078faf31dd72bb + source = mutter-performance::git+https://gitlab.gnome.org/GNOME/mutter.git#commit=c71a119de06d8937930e4d1adf06ff0c2b259653 source = mr1441.patch source = mr3327.patch sha256sums = SKIP - sha256sums = ac0806cb296e9690c084e23b74694c1fe0441342d35f10f31bf1451962b5f4a7 + sha256sums = 171f8bc4610452e27c82b72545b12760e76a2f83fb2c93d3182e8a9a48664a6c sha256sums = 2bf8e12fe60a35469352510cc14a76603722441c1cb47ae9548a79712a01a762 pkgname = mutter-performance diff --git a/PKGBUILD b/PKGBUILD index a3e9e42..20aa5c7 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -31,8 +31,8 @@ else pkgname=(mutter-performance mutter-performance-docs) fi epoch=1 -pkgver=45.0+r49+g8d3d8b86e -pkgrel=2 +pkgver=45.1 +pkgrel=1 pkgdesc="A window manager for GNOME | Attempts to improve performances with non-upstreamed merge-requests and frequent stable branch resync" url="https://gitlab.gnome.org/GNOME/mutter" arch=(x86_64 aarch64) @@ -74,12 +74,12 @@ makedepends=( if [ -n "$_enable_check" ]; then checkdepends=(gnome-session xorg-server-xvfb pipewire-session-manager python-dbusmock zenity) fi -_commit=8d3d8b86e517c97accf11eb243078faf31dd72bb # tags/45.0^49 +_commit=c71a119de06d8937930e4d1adf06ff0c2b259653 # tags/45.1^0 source=("$pkgname::git+https://gitlab.gnome.org/GNOME/mutter.git#commit=$_commit" 'mr1441.patch' 'mr3327.patch') sha256sums=('SKIP' - 'ac0806cb296e9690c084e23b74694c1fe0441342d35f10f31bf1451962b5f4a7' + '171f8bc4610452e27c82b72545b12760e76a2f83fb2c93d3182e8a9a48664a6c' '2bf8e12fe60a35469352510cc14a76603722441c1cb47ae9548a79712a01a762') pkgver() { diff --git a/mr1441.patch b/mr1441.patch index 03ac04f..84dc6e0 100644 --- a/mr1441.patch +++ b/mr1441.patch @@ -1,7 +1,7 @@ Author: Daniel van Vugt Source: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441 -Commit: 04094e9ff9b97019a1547f386cbf5d6fc258841e -Last Updated: 10/10/23 (Mutter 45.0) +Commit: 0a85ac26710327b36151e30b893806fe83fd4295 +Last Updated: 11/19/23 (Mutter 45.1) --- Use triple buffering if and when the previous frame is running late. @@ -15,80 +15,942 @@ If the previous frame is not running late then we stick to double buffering so there's no latency penalty when the system is able to maintain full frame rate. --- -From c9dcd393405c1f43fb64344da08b79936d1bc417 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 24 Sep 2021 21:49:45 +0800 -Subject: [PATCH 01/42] onscreen/native: Keep the onscreen alive longer than - `MetaDrmBufferGbm` - -Because `MetaDrmBufferGbm` uses `gbm_surface` in its destructor and -`gbm_surface` can't be refcounted directly. So we reference its owner -(the onscreen) instead. - -This avoids a crash that might otherwise occur when hotplugging monitors as -the old onscreen might get disposed before the new onscreen has presented -a replacement frame on the same `MetaKmsCrtc`. - -We could instead wrap and refcount all users of `gbm_surface`, but that -would be a lot more work for no additional benefit right now. ---- - src/backends/native/meta-onscreen-native.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 2388a44a28..7c0f883d5b 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -596,6 +596,16 @@ import_shared_framebuffer (CoglOnscreen *onscreen, - return imported_buffer; +diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c +index ab493e0b0..24f211a77 100644 +--- a/clutter/clutter/clutter-frame-clock.c ++++ b/clutter/clutter/clutter-frame-clock.c +@@ -35,6 +35,15 @@ enum + + static guint signals[N_SIGNALS]; + ++typedef enum ++{ ++ TRIPLE_BUFFERING_MODE_NEVER, ++ TRIPLE_BUFFERING_MODE_AUTO, ++ TRIPLE_BUFFERING_MODE_ALWAYS, ++} TripleBufferingMode; ++ ++static TripleBufferingMode triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; ++ + #define SYNC_DELAY_FALLBACK_FRACTION 0.875 + + typedef struct _ClutterFrameListener +@@ -55,8 +64,9 @@ typedef enum _ClutterFrameClockState + CLUTTER_FRAME_CLOCK_STATE_INIT, + CLUTTER_FRAME_CLOCK_STATE_IDLE, + CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, +- CLUTTER_FRAME_CLOCK_STATE_DISPATCHING, +- CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED, ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE, ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED, ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO, + } ClutterFrameClockState; + + struct _ClutterFrameClock +@@ -73,6 +83,7 @@ struct _ClutterFrameClock + + ClutterFrameClockState state; + int64_t last_dispatch_time_us; ++ int64_t prev_last_dispatch_time_us; + int64_t last_dispatch_lateness_us; + int64_t last_presentation_time_us; + int64_t next_update_time_us; +@@ -87,6 +98,9 @@ struct _ClutterFrameClock + int64_t vblank_duration_us; + /* Last KMS buffer submission time. */ + int64_t last_flip_time_us; ++ int64_t prev_last_flip_time_us; ++ ++ ClutterFrameHint last_flip_hints; + + /* Last time we promoted short-term maximum to long-term one */ + int64_t longterm_promotion_us; +@@ -219,10 +233,6 @@ static void + maybe_update_longterm_max_duration_us (ClutterFrameClock *frame_clock, + ClutterFrameInfo *frame_info) + { +- /* Do not update long-term max if there has been no measurement */ +- if (!frame_clock->shortterm_max_update_duration_us) +- return; +- + if ((frame_info->presentation_time - frame_clock->longterm_promotion_us) < + G_USEC_PER_SEC) + return; +@@ -249,6 +259,12 @@ void + clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + ClutterFrameInfo *frame_info) + { ++#ifdef CLUTTER_ENABLE_DEBUG ++ const char *debug_state = ++ frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ? ++ "Triple buffering" : "Double buffering"; ++#endif ++ + COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented, + "Frame Clock (presented)"); + +@@ -328,31 +344,58 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + + frame_clock->got_measurements_last_frame = FALSE; + +- if (frame_info->cpu_time_before_buffer_swap_us != 0) ++ if (frame_info->cpu_time_before_buffer_swap_us != 0 || ++ frame_clock->ever_got_measurements) + { + int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us; ++ int64_t dispatch_time_us = 0, flip_time_us = 0; + +- dispatch_to_swap_us = +- frame_info->cpu_time_before_buffer_swap_us - +- frame_clock->last_dispatch_time_us; ++ switch (frame_clock->state) ++ { ++ case CLUTTER_FRAME_CLOCK_STATE_INIT: ++ case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ g_warn_if_reached (); ++ G_GNUC_FALLTHROUGH; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ dispatch_time_us = frame_clock->last_dispatch_time_us; ++ flip_time_us = frame_clock->last_flip_time_us; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ dispatch_time_us = frame_clock->prev_last_dispatch_time_us; ++ flip_time_us = frame_clock->prev_last_flip_time_us; ++ break; ++ } ++ ++ if (frame_info->cpu_time_before_buffer_swap_us == 0) ++ { ++ /* Cursor-only updates with no "swap" or "flip" */ ++ dispatch_to_swap_us = 0; ++ swap_to_flip_us = 0; ++ } ++ else ++ { ++ dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us - ++ dispatch_time_us; ++ swap_to_flip_us = flip_time_us - ++ frame_info->cpu_time_before_buffer_swap_us; ++ } + swap_to_rendering_done_us = + frame_info->gpu_rendering_duration_ns / 1000; +- swap_to_flip_us = +- frame_clock->last_flip_time_us - +- frame_info->cpu_time_before_buffer_swap_us; + + CLUTTER_NOTE (FRAME_TIMINGS, +- "update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", ++ "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", ++ debug_state, + frame_clock->last_dispatch_lateness_us, + dispatch_to_swap_us, + swap_to_rendering_done_us, + swap_to_flip_us); + + frame_clock->shortterm_max_update_duration_us = +- CLAMP (frame_clock->last_dispatch_lateness_us + dispatch_to_swap_us + +- MAX (swap_to_rendering_done_us, swap_to_flip_us), +- frame_clock->shortterm_max_update_duration_us, +- frame_clock->refresh_interval_us); ++ MAX (frame_clock->shortterm_max_update_duration_us, ++ frame_clock->last_dispatch_lateness_us + dispatch_to_swap_us + ++ MAX (swap_to_rendering_done_us, swap_to_flip_us)); + + maybe_update_longterm_max_duration_us (frame_clock, frame_info); + +@@ -361,7 +404,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + } + else + { +- CLUTTER_NOTE (FRAME_TIMINGS, "update2dispatch %ld µs", ++ CLUTTER_NOTE (FRAME_TIMINGS, "%s: update2dispatch %ld µs", ++ debug_state, + frame_clock->last_dispatch_lateness_us); + } + +@@ -378,11 +422,18 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + g_warn_if_reached (); + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + maybe_reschedule_update (frame_clock); + break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ maybe_reschedule_update (frame_clock); ++ break; + } + } + +@@ -398,11 +449,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + g_warn_if_reached (); + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + maybe_reschedule_update (frame_clock); + break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ maybe_reschedule_update (frame_clock); ++ break; + } + } + +@@ -417,7 +475,14 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) + if (!frame_clock->ever_got_measurements || + G_UNLIKELY (clutter_paint_debug_flags & + CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME)) +- return refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; ++ { ++ int64_t ret = refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; ++ ++ if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE) ++ ret += refresh_interval_us; ++ ++ return ret; ++ } + + /* Max render time shows how early the frame clock needs to be dispatched + * to make it to the predicted next presentation time. It is an estimate of +@@ -437,8 +502,6 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) + frame_clock->vblank_duration_us + + clutter_max_render_time_constant_us; + +- max_render_time_us = CLAMP (max_render_time_us, 0, refresh_interval_us); +- + return max_render_time_us; + } + +@@ -453,7 +516,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, + int64_t refresh_interval_us; + int64_t min_render_time_allowed_us; + int64_t max_render_time_allowed_us; +- int64_t next_presentation_time_us; ++ int64_t next_presentation_time_us = 0; + int64_t next_update_time_us; + + now_us = g_get_monotonic_time (); +@@ -498,7 +561,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, + * + */ + last_presentation_time_us = frame_clock->last_presentation_time_us; +- next_presentation_time_us = last_presentation_time_us + refresh_interval_us; ++ switch (frame_clock->state) ++ { ++ case CLUTTER_FRAME_CLOCK_STATE_INIT: ++ case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ next_presentation_time_us = last_presentation_time_us + ++ refresh_interval_us; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ next_presentation_time_us = last_presentation_time_us + ++ 2 * refresh_interval_us; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ next_presentation_time_us = last_presentation_time_us + ++ 3 * refresh_interval_us; ++ break; ++ } + + /* + * However, the last presentation could have happened more than a frame ago. +@@ -607,8 +687,12 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) + frame_clock->pending_reschedule = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->pending_reschedule = TRUE; ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + break; + } + +@@ -645,9 +729,15 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + next_update_time_us = g_get_monotonic_time (); ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ next_update_time_us = g_get_monotonic_time (); ++ frame_clock->state = ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + return; +@@ -657,7 +747,6 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) + + frame_clock->next_update_time_us = next_update_time_us; + g_source_set_ready_time (frame_clock->source, next_update_time_us); +- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + frame_clock->is_next_presentation_time_valid = FALSE; + } + +@@ -665,6 +754,12 @@ void + clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) + { + int64_t next_update_time_us = -1; ++ TripleBufferingMode current_mode = triple_buffering_mode; ++ ++ if (current_mode == TRIPLE_BUFFERING_MODE_AUTO && ++ (frame_clock->last_flip_hints & ++ CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED)) ++ current_mode = TRIPLE_BUFFERING_MODE_NEVER; + + if (frame_clock->inhibit_count > 0) + { +@@ -676,6 +771,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + next_update_time_us = g_get_monotonic_time (); ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + break; + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + calculate_next_update_time_us (frame_clock, +@@ -684,11 +780,37 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) + &frame_clock->min_render_time_allowed_us); + frame_clock->is_next_presentation_time_valid = + (frame_clock->next_presentation_time_us != 0); ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + break; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + return; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ switch (current_mode) ++ { ++ case TRIPLE_BUFFERING_MODE_NEVER: ++ frame_clock->pending_reschedule = TRUE; ++ return; ++ case TRIPLE_BUFFERING_MODE_AUTO: ++ calculate_next_update_time_us (frame_clock, ++ &next_update_time_us, ++ &frame_clock->next_presentation_time_us, ++ &frame_clock->min_render_time_allowed_us); ++ frame_clock->is_next_presentation_time_valid = ++ (frame_clock->next_presentation_time_us != 0); ++ frame_clock->state = ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; ++ break; ++ case TRIPLE_BUFFERING_MODE_ALWAYS: ++ next_update_time_us = g_get_monotonic_time (); ++ frame_clock->next_presentation_time_us = 0; ++ frame_clock->is_next_presentation_time_valid = FALSE; ++ frame_clock->state = ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; ++ break; ++ } ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->pending_reschedule = TRUE; + return; + } +@@ -697,7 +819,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) + + frame_clock->next_update_time_us = next_update_time_us; + g_source_set_ready_time (frame_clock->source, next_update_time_us); +- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + } + + static void +@@ -728,7 +849,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; +@@ -749,10 +870,25 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, + } + #endif + ++ frame_clock->prev_last_dispatch_time_us = frame_clock->last_dispatch_time_us; + frame_clock->last_dispatch_time_us = time_us; + g_source_set_ready_time (frame_clock->source, -1); + +- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHING; ++ switch (frame_clock->state) ++ { ++ case CLUTTER_FRAME_CLOCK_STATE_INIT: ++ case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ g_warn_if_reached (); ++ return; ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO; ++ break; ++ } + + frame_count = frame_clock->frame_count++; + +@@ -781,25 +917,31 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, + result = iface->frame (frame_clock, frame, frame_clock->listener.user_data); + COGL_TRACE_END (ClutterFrameClockFrame); + +- switch (frame_clock->state) ++ switch (result) + { +- case CLUTTER_FRAME_CLOCK_STATE_INIT: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: +- g_warn_if_reached (); +- break; +- case CLUTTER_FRAME_CLOCK_STATE_IDLE: +- case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- switch (result) ++ case CLUTTER_FRAME_RESULT_IDLE: ++ /* The frame was aborted; nothing to paint/present */ ++ switch (frame_clock->state) + { +- case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: +- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED; ++ case CLUTTER_FRAME_CLOCK_STATE_INIT: ++ case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ g_warn_if_reached (); + break; +- case CLUTTER_FRAME_RESULT_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + maybe_reschedule_update (frame_clock); + break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ maybe_reschedule_update (frame_clock); ++ break; + } + break; + } +@@ -832,10 +974,13 @@ frame_clock_source_dispatch (GSource *source, + } + + void +-clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, +- int64_t flip_time_us) ++clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, ++ int64_t flip_time_us, ++ ClutterFrameHint hints) + { ++ frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us; + frame_clock->last_flip_time_us = flip_time_us; ++ frame_clock->last_flip_hints = hints; + } + + GString * +@@ -929,8 +1074,6 @@ clutter_frame_clock_dispose (GObject *object) + { + ClutterFrameClock *frame_clock = CLUTTER_FRAME_CLOCK (object); + +- g_warn_if_fail (frame_clock->state != CLUTTER_FRAME_CLOCK_STATE_DISPATCHING); +- + if (frame_clock->source) + { + g_signal_emit (frame_clock, signals[DESTROY], 0); +@@ -951,6 +1094,15 @@ static void + clutter_frame_clock_class_init (ClutterFrameClockClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ const char *mode_str; ++ ++ mode_str = g_getenv ("MUTTER_DEBUG_TRIPLE_BUFFERING"); ++ if (!g_strcmp0 (mode_str, "never")) ++ triple_buffering_mode = TRIPLE_BUFFERING_MODE_NEVER; ++ else if (!g_strcmp0 (mode_str, "auto")) ++ triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; ++ else if (!g_strcmp0 (mode_str, "always")) ++ triple_buffering_mode = TRIPLE_BUFFERING_MODE_ALWAYS; + + object_class->dispose = clutter_frame_clock_dispose; + +diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h +index 93ebc9438..e1fd6b986 100644 +--- a/clutter/clutter/clutter-frame-clock.h ++++ b/clutter/clutter/clutter-frame-clock.h +@@ -33,6 +33,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,7 +97,8 @@ 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 0a0226b0a..55c76df72 100644 +--- a/clutter/clutter/clutter-frame-private.h ++++ b/clutter/clutter/clutter-frame-private.h +@@ -34,6 +34,7 @@ struct _ClutterFrame + + gboolean has_result; + ClutterFrameResult result; ++ ClutterFrameHint hints; + }; + + CLUTTER_EXPORT +diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c +index 85baef274..413ce9c2b 100644 +--- a/clutter/clutter/clutter-frame.c ++++ b/clutter/clutter/clutter-frame.c +@@ -113,3 +113,16 @@ clutter_frame_set_result (ClutterFrame *frame, + frame->result = result; + frame->has_result = TRUE; + } ++ ++void ++clutter_frame_set_hint (ClutterFrame *frame, ++ ClutterFrameHint hint) ++{ ++ frame->hints |= hint; ++} ++ ++ClutterFrameHint ++clutter_frame_get_hints (ClutterFrame *frame) ++{ ++ return frame->hints; ++} +diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h +index 1d5660d68..0e7f618a4 100644 +--- a/clutter/clutter/clutter-frame.h ++++ b/clutter/clutter/clutter-frame.h +@@ -54,4 +54,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); ++ + G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFrame, clutter_frame_unref) +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 168746dd4..f0e36619e 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -1264,14 +1264,21 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock, + + _clutter_stage_window_redraw_view (stage_window, view, frame); + +- clutter_frame_clock_record_flip_time (frame_clock, +- g_get_monotonic_time ()); ++ clutter_frame_clock_record_flip (frame_clock, ++ g_get_monotonic_time (), ++ clutter_frame_get_hints (frame)); + + clutter_stage_emit_after_paint (stage, view, frame); + + if (_clutter_context_get_show_fps ()) + end_frame_timing_measurement (view); + } ++ else ++ { ++ clutter_frame_clock_record_flip (frame_clock, ++ g_get_monotonic_time (), ++ clutter_frame_get_hints (frame)); ++ } + + _clutter_stage_window_finish_frame (stage_window, view, frame); + +diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h +index 9dbecfd0c..681d91d2b 100644 +--- a/cogl/cogl/cogl-onscreen-private.h ++++ b/cogl/cogl/cogl-onscreen-private.h +@@ -95,3 +95,6 @@ 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); +diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c +index 73425e498..02c4474b2 100644 +--- a/cogl/cogl/cogl-onscreen.c ++++ b/cogl/cogl/cogl-onscreen.c +@@ -508,6 +508,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen) + return g_queue_pop_head (&priv->pending_frame_infos); + } + ++unsigned int ++cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen) ++{ ++ CoglOnscreenPrivate *priv = cogl_onscreen_get_instance_private (onscreen); ++ ++ return g_queue_get_length (&priv->pending_frame_infos); ++} ++ + CoglFrameClosure * + cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, + CoglFrameCallback callback, +diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c +index c35cb36e3..2130e4042 100644 +--- a/src/backends/meta-stage-impl.c ++++ b/src/backends/meta-stage-impl.c +@@ -775,6 +775,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-kms-crtc.c b/src/backends/native/meta-kms-crtc.c +index d89b12598..b17e8460d 100644 +--- a/src/backends/native/meta-kms-crtc.c ++++ b/src/backends/native/meta-kms-crtc.c +@@ -48,6 +48,8 @@ struct _MetaKmsCrtc + MetaKmsCrtcState current_state; + + MetaKmsCrtcPropTable prop_table; ++ ++ MetaSwapChain *swap_chain; + }; + + G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT) +@@ -99,6 +101,12 @@ meta_kms_crtc_get_prop_drm_value (MetaKmsCrtc *crtc, + return meta_kms_prop_convert_value (prop, value); + } + ++MetaSwapChain * ++meta_kms_crtc_get_swap_chain (MetaKmsCrtc *crtc) ++{ ++ return crtc->swap_chain; ++} ++ + gboolean + meta_kms_crtc_is_active (MetaKmsCrtc *crtc) + { +@@ -465,12 +473,23 @@ meta_kms_crtc_new (MetaKmsImplDevice *impl_device, + return crtc; } +static void -+reference_owning_gbm_surface (CoglOnscreen *onscreen, -+ MetaDrmBufferGbm *buffer_gbm) ++meta_kms_crtc_dispose (GObject *object) +{ -+ g_object_set_data_full (G_OBJECT (buffer_gbm), -+ "gbm_surface owner", -+ g_object_ref (onscreen), -+ (GDestroyNotify) g_object_unref); ++ MetaKmsCrtc *crtc = META_KMS_CRTC (object); ++ ++ meta_swap_chain_release_buffers (crtc->swap_chain); ++ ++ G_OBJECT_CLASS (meta_kms_crtc_parent_class)->dispose (object); +} + - static MetaDrmBuffer * - copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, -@@ -681,6 +691,8 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - return NULL; + static void + meta_kms_crtc_finalize (GObject *object) + { + MetaKmsCrtc *crtc = META_KMS_CRTC (object); + + g_clear_pointer (&crtc->current_state.gamma.value, meta_gamma_lut_free); ++ g_clear_object (&crtc->swap_chain); + + G_OBJECT_CLASS (meta_kms_crtc_parent_class)->finalize (object); + } +@@ -480,6 +499,7 @@ meta_kms_crtc_init (MetaKmsCrtc *crtc) + { + crtc->current_state.gamma.size = 0; + crtc->current_state.gamma.value = NULL; ++ crtc->swap_chain = meta_swap_chain_new (); + } + + static void +@@ -487,6 +507,7 @@ 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 b26b682dd..a30a6de6e 100644 +--- a/src/backends/native/meta-kms-crtc.h ++++ b/src/backends/native/meta-kms-crtc.h +@@ -22,6 +22,7 @@ + #include + + #include "backends/native/meta-kms-types.h" ++#include "backends/native/meta-swap-chain.h" + #include "backends/meta-backend-types.h" + #include "core/util-private.h" + #include "meta/boxes.h" +@@ -60,3 +61,5 @@ int meta_kms_crtc_get_idx (MetaKmsCrtc *crtc); + + META_EXPORT_TEST + gboolean meta_kms_crtc_is_active (MetaKmsCrtc *crtc); ++ ++MetaSwapChain * meta_kms_crtc_get_swap_chain (MetaKmsCrtc *crtc); +diff --git a/src/backends/native/meta-kms-impl-device-atomic.c b/src/backends/native/meta-kms-impl-device-atomic.c +index 2ca70326f..80c01413c 100644 +--- a/src/backends/native/meta-kms-impl-device-atomic.c ++++ b/src/backends/native/meta-kms-impl-device-atomic.c +@@ -505,6 +505,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, + { + MetaKmsPlaneAssignment *plane_assignment = update_entry; + MetaKmsPlane *plane = plane_assignment->plane; ++ MetaKmsUpdateFlag flags = (MetaKmsUpdateFlag) user_data; + MetaDrmBuffer *buffer; + MetaKmsFbDamage *fb_damage; + uint32_t prop_id; +@@ -657,6 +658,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, + error)) + return FALSE; + } ++ ++ if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) ++ meta_swap_chain_push_buffer (meta_kms_crtc_get_swap_chain (plane_assignment->crtc), ++ meta_kms_plane_get_id (plane), ++ G_OBJECT (buffer)); ++ + return TRUE; + } + +@@ -1005,7 +1012,7 @@ meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device, + req, + blob_ids, + meta_kms_update_get_plane_assignments (update), +- NULL, ++ GUINT_TO_POINTER (flags), + process_plane_assignment, + &error)) + goto err; +diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c +index 2d68ba11f..f4e23df07 100644 +--- a/src/backends/native/meta-kms-impl-device-simple.c ++++ b/src/backends/native/meta-kms-impl-device-simple.c +@@ -485,6 +485,8 @@ process_mode_set (MetaKmsImplDevice *impl_device, + return FALSE; } -+ reference_owning_gbm_surface (onscreen, buffer_gbm); ++ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (crtc)); + - g_object_set_qdata_full (G_OBJECT (buffer_gbm), - blit_source_quark, - g_object_ref (primary_gpu_fb), -@@ -1113,6 +1125,7 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - return; - } + if (drm_mode) + { + g_hash_table_replace (impl_device_simple->cached_mode_sets, +@@ -554,7 +556,7 @@ is_timestamp_earlier_than (uint64_t ts1, + typedef struct _RetryPageFlipData + { + MetaKmsCrtc *crtc; +- uint32_t fb_id; ++ MetaDrmBuffer *fb; + MetaKmsPageFlipData *page_flip_data; + float refresh_rate; + uint64_t retry_time_us; +@@ -567,6 +569,7 @@ retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data) + g_assert (!retry_page_flip_data->page_flip_data); + g_clear_pointer (&retry_page_flip_data->custom_page_flip, + meta_kms_custom_page_flip_free); ++ g_clear_object (&retry_page_flip_data->fb); + g_free (retry_page_flip_data); + } + +@@ -634,16 +637,21 @@ retry_page_flips (gpointer user_data) + } + else + { ++ uint32_t fb_id = ++ retry_page_flip_data->fb ? ++ meta_drm_buffer_get_fb_id (retry_page_flip_data->fb) : ++ 0; ++ + meta_topic (META_DEBUG_KMS, + "[simple] Retrying page flip on CRTC %u (%s) with %u", + meta_kms_crtc_get_id (crtc), + meta_kms_impl_device_get_path (impl_device), +- retry_page_flip_data->fb_id); ++ fb_id); + + fd = meta_kms_impl_device_get_fd (impl_device); + ret = drmModePageFlip (fd, + meta_kms_crtc_get_id (crtc), +- retry_page_flip_data->fb_id, ++ fb_id, + DRM_MODE_PAGE_FLIP_EVENT, + retry_page_flip_data->page_flip_data); + } +@@ -730,7 +738,7 @@ retry_page_flips (gpointer user_data) + static void + schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple, + MetaKmsCrtc *crtc, +- uint32_t fb_id, ++ MetaDrmBuffer *fb, + float refresh_rate, + MetaKmsPageFlipData *page_flip_data, + MetaKmsCustomPageFlip *custom_page_flip) +@@ -745,7 +753,7 @@ schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple, + retry_page_flip_data = g_new0 (RetryPageFlipData, 1); + *retry_page_flip_data = (RetryPageFlipData) { + .crtc = crtc, +- .fb_id = fb_id, ++ .fb = fb ? g_object_ref (fb) : NULL, + .page_flip_data = page_flip_data, + .refresh_rate = refresh_rate, + .retry_time_us = retry_time_us, +@@ -877,6 +885,8 @@ mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple, + return FALSE; + } + ++ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (crtc)); ++ + if (!impl_device_simple->mode_set_fallback_feedback_source) + { + MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); +@@ -1003,20 +1013,20 @@ dispatch_page_flip (MetaKmsImplDevice *impl_device, + cached_mode_set = get_cached_mode_set (impl_device_simple, crtc); + if (cached_mode_set) + { +- uint32_t fb_id; ++ MetaDrmBuffer *fb; + drmModeModeInfo *drm_mode; + float refresh_rate; + + if (plane_assignment) +- fb_id = meta_drm_buffer_get_fb_id (plane_assignment->buffer); ++ fb = plane_assignment->buffer; + else +- fb_id = 0; ++ fb = NULL; + drm_mode = cached_mode_set->drm_mode; + refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); + meta_kms_impl_device_hold_fd (impl_device); + schedule_retry_page_flip (impl_device_simple, + crtc, +- fb_id, ++ fb, + refresh_rate, + page_flip_data, + g_steal_pointer (&custom_page_flip)); +@@ -1299,7 +1309,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, +@@ -1313,7 +1323,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, +@@ -1326,6 +1336,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, + } + + g_assert_not_reached (); ++ ++assigned: ++ meta_swap_chain_push_buffer (meta_kms_crtc_get_swap_chain (plane_assignment->crtc), ++ meta_kms_plane_get_id (plane), ++ G_OBJECT (plane_assignment->buffer)); ++ return TRUE; + } + + static gboolean +diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c +index bce64d309..85441f47b 100644 +--- a/src/backends/native/meta-kms-impl-device.c ++++ b/src/backends/native/meta-kms-impl-device.c +@@ -1483,9 +1483,11 @@ meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device, + meta_kms_update_merge_from (crtc_frame->pending_update, update); + meta_kms_update_free (update); + update = g_steal_pointer (&crtc_frame->pending_update); +- disarm_crtc_frame_deadline_timer (crtc_frame); + } + ++ if (crtc_frame->deadline.armed) ++ disarm_crtc_frame_deadline_timer (crtc_frame); ++ + meta_kms_device_handle_flush (priv->device, latch_crtc); + + feedback = do_process (impl_device, latch_crtc, update, flags); +@@ -1862,6 +1864,16 @@ meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device, + return TRUE; + } + ++static void ++release_buffers (gpointer data, ++ gpointer user_data) ++{ ++ MetaKmsCrtc *crtc = data; ++ MetaSwapChain *swap_chain = meta_kms_crtc_get_swap_chain (crtc); ++ ++ meta_swap_chain_release_buffers (swap_chain); ++} ++ + void + meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device) + { +@@ -1869,6 +1881,8 @@ meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device) + meta_kms_impl_device_get_instance_private (impl_device); + MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); + ++ g_list_foreach (priv->crtcs, release_buffers, NULL); ++ + if (klass->prepare_shutdown) + klass->prepare_shutdown (impl_device); -+ reference_owning_gbm_surface (onscreen, buffer_gbm); - primary_gpu_fb = META_DRM_BUFFER (g_steal_pointer (&buffer_gbm)); - break; - case META_RENDERER_NATIVE_MODE_SURFACELESS: --- -GitLab - - -From 91bfbf5572d0290b3c01202db1dd6c9e5aeed5fc Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Mon, 16 Aug 2021 17:06:43 +0800 -Subject: [PATCH 02/42] kms/update: Hold a reference to the buffer used in a - plane assignment - ---- - src/backends/native/meta-kms-update.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c -index 5189c5ab32..bb7349ecf5 100644 +index 5189c5ab3..bb7349ecf 100644 --- a/src/backends/native/meta-kms-update.c +++ b/src/backends/native/meta-kms-update.c @@ -190,6 +190,7 @@ static void @@ -108,27 +970,876 @@ index 5189c5ab32..bb7349ecf5 100644 .src_rect = src_rect, .dst_rect = dst_rect, .flags = flags, --- -GitLab - - -From 7b8a1c624effdfe57b42972b73b720a4b87bd0ff Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Mon, 16 Aug 2021 18:04:10 +0800 -Subject: [PATCH 03/42] native: Introduce MetaSwapChain to hold plane buffer - references - ---- - src/backends/native/meta-swap-chain.c | 149 ++++++++++++++++++++++++++ - src/backends/native/meta-swap-chain.h | 48 +++++++++ - src/meson.build | 2 + - 3 files changed, 199 insertions(+) - create mode 100644 src/backends/native/meta-swap-chain.c - create mode 100644 src/backends/native/meta-swap-chain.h - +diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c +index ec009ec8c..c6708946e 100644 +--- a/src/backends/native/meta-kms.c ++++ b/src/backends/native/meta-kms.c +@@ -155,6 +155,8 @@ struct _MetaKms + int kernel_thread_inhibit_count; + + MetaKmsCursorManager *cursor_manager; ++ ++ gboolean shutting_down; + }; + + G_DEFINE_TYPE (MetaKms, meta_kms, META_TYPE_THREAD) +@@ -433,6 +435,7 @@ static void + on_prepare_shutdown (MetaBackend *backend, + MetaKms *kms) + { ++ kms->shutting_down = TRUE; + meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL); + meta_thread_flush_callbacks (META_THREAD (kms)); + +@@ -487,6 +490,12 @@ meta_kms_new (MetaBackend *backend, + return kms; + } + ++gboolean ++meta_kms_is_shutting_down (MetaKms *kms) ++{ ++ return kms->shutting_down; ++} ++ + static void + meta_kms_finalize (GObject *object) + { +diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h +index 743401406..f6b19520b 100644 +--- a/src/backends/native/meta-kms.h ++++ b/src/backends/native/meta-kms.h +@@ -60,6 +60,8 @@ MetaKmsDevice * meta_kms_create_device (MetaKms *kms, + MetaKmsDeviceFlag flags, + GError **error); + ++gboolean meta_kms_is_shutting_down (MetaKms *kms); ++ + MetaKms * meta_kms_new (MetaBackend *backend, + MetaKmsFlags flags, + GError **error); +diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c +index 2388a44a2..14d727c55 100644 +--- a/src/backends/native/meta-onscreen-native.c ++++ b/src/backends/native/meta-onscreen-native.c +@@ -72,7 +72,7 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState + + struct { + MetaDrmBufferDumb *current_dumb_fb; +- MetaDrmBufferDumb *dumb_fbs[2]; ++ MetaDrmBufferDumb *dumb_fbs[3]; + } cpu; + + gboolean noted_primary_gpu_copy_ok; +@@ -93,8 +93,13 @@ struct _MetaOnscreenNative + + struct { + struct gbm_surface *surface; +- MetaDrmBuffer *current_fb; + MetaDrmBuffer *next_fb; ++ MetaDrmBuffer *stalled_fb; ++ ++ /* Temporary workaround for the scanout-failed signal wanting the buffer ++ * to live longer than it does, and then it doesn't use it anyway... ++ */ ++ MetaDrmBuffer *direct_fb; + } gbm; + + #ifdef HAVE_EGL_DEVICE +@@ -116,6 +121,16 @@ struct _MetaOnscreenNative + gulong privacy_screen_changed_handler_id; + gulong color_space_changed_handler_id; + gulong hdr_metadata_changed_handler_id; ++ ++ gboolean needs_flush; ++ ++ unsigned int swaps_pending; ++ ++ struct { ++ int *rectangles; /* 4 x n_rectangles */ ++ int n_rectangles; ++ ClutterFrame *frame; ++ } next_post; + }; + + G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, +@@ -123,40 +138,17 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, + + static GQuark blit_source_quark = 0; + +-static gboolean +-init_secondary_gpu_state (MetaRendererNative *renderer_native, +- CoglOnscreen *onscreen, +- GError **error); +- + static void +-free_current_bo (CoglOnscreen *onscreen) +-{ +- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); +- +- g_clear_object (&onscreen_native->gbm.current_fb); +-} +- +-static void +-meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) +-{ +- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); +- +- if (!onscreen_native->gbm.next_fb) +- return; +- +- free_current_bo (onscreen); +- +- g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); +- g_clear_object (&onscreen_native->gbm.next_fb); +-} ++try_post_latest_swap (CoglOnscreen *onscreen); + + static void +-meta_onscreen_native_clear_next_fb (CoglOnscreen *onscreen) +-{ +- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); ++post_finish_frame (MetaOnscreenNative *onscreen_native, ++ MetaKmsUpdate *kms_update); + +- g_clear_object (&onscreen_native->gbm.next_fb); +-} ++static gboolean ++init_secondary_gpu_state (MetaRendererNative *renderer_native, ++ CoglOnscreen *onscreen, ++ GError **error); + + static void + maybe_update_frame_info (MetaCrtc *crtc, +@@ -193,7 +185,7 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen) + + info = cogl_onscreen_pop_head_frame_info (onscreen); + +- g_assert (!cogl_onscreen_peek_head_frame_info (onscreen)); ++ g_assert (info); + + _cogl_onscreen_notify_frame_sync (onscreen, info); + _cogl_onscreen_notify_complete (onscreen, info); +@@ -228,7 +220,8 @@ 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); ++ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (kms_crtc)); ++ try_post_latest_swap (onscreen); + } + + static void +@@ -278,15 +271,13 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, + CoglFramebuffer *framebuffer = + clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); +- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + CoglFrameInfo *frame_info; + + frame_info = cogl_onscreen_peek_head_frame_info (onscreen); + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + +- g_warn_if_fail (!onscreen_native->gbm.next_fb); +- + meta_onscreen_native_notify_frame_complete (onscreen); ++ try_post_latest_swap (onscreen); + } + + static void +@@ -336,7 +327,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + + meta_onscreen_native_notify_frame_complete (onscreen); +- meta_onscreen_native_clear_next_fb (onscreen); ++ try_post_latest_swap (onscreen); + } + + static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { +@@ -397,18 +388,40 @@ custom_egl_stream_page_flip (gpointer custom_page_flip_data, + } + #endif /* HAVE_EGL_DEVICE */ + +-void +-meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) ++static void ++drop_stalled_swap (CoglOnscreen *onscreen) + { + CoglFrameInfo *frame_info; ++ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + +- meta_onscreen_native_swap_drm_fb (onscreen); ++ /* Remember we can't compare stalled_fb because it's not used by ++ * META_RENDERER_NATIVE_MODE_EGL_DEVICE. So we judge stalled to be whenever ++ * swaps_pending > 1. ++ */ ++ if (onscreen_native->swaps_pending <= 1) ++ return; ++ ++ onscreen_native->swaps_pending--; ++ ++ g_clear_object (&onscreen_native->gbm.stalled_fb); + + frame_info = cogl_onscreen_peek_tail_frame_info (onscreen); + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + meta_onscreen_native_notify_frame_complete (onscreen); + } + ++void ++meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) ++{ ++ drop_stalled_swap (onscreen); ++ ++ /* If the monitor just woke up and the shell is fully idle (has nothing ++ * more to swap) then we just woke to an indefinitely black screen. Let's ++ * fix that using the last swap (which is never classified as "stalled"). ++ */ ++ try_post_latest_swap (onscreen); ++} ++ + static void + meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, + MetaRendererView *view, +@@ -425,7 +438,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); + MetaRendererNativeGpuData *renderer_gpu_data; + MetaGpuKms *gpu_kms; +- MetaDrmBuffer *buffer; ++ g_autoptr (MetaDrmBuffer) buffer = NULL; + MetaKmsPlaneAssignment *plane_assignment; + + COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs, +@@ -440,7 +453,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, + switch (renderer_gpu_data->mode) + { + case META_RENDERER_NATIVE_MODE_GBM: +- buffer = onscreen_native->gbm.next_fb; ++ buffer = g_steal_pointer (&onscreen_native->gbm.next_fb); + + plane_assignment = meta_crtc_kms_assign_primary_plane (crtc_kms, + buffer, +@@ -596,6 +609,16 @@ import_shared_framebuffer (CoglOnscreen *onscreen, + return imported_buffer; + } + ++static void ++reference_owning_gbm_surface (CoglOnscreen *onscreen, ++ MetaDrmBufferGbm *buffer_gbm) ++{ ++ g_object_set_data_full (G_OBJECT (buffer_gbm), ++ "gbm_surface owner", ++ g_object_ref (onscreen), ++ (GDestroyNotify) g_object_unref); ++} ++ + static MetaDrmBuffer * + copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, +@@ -681,6 +704,8 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, + return NULL; + } + ++ reference_owning_gbm_surface (onscreen, buffer_gbm); ++ + g_object_set_qdata_full (G_OBJECT (buffer_gbm), + blit_source_quark, + g_object_ref (primary_gpu_fb), +@@ -693,12 +718,17 @@ static MetaDrmBufferDumb * + secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) + { + MetaDrmBufferDumb *current_dumb_fb; ++ const int n_dumb_fbs = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); ++ int i; + + current_dumb_fb = secondary_gpu_state->cpu.current_dumb_fb; +- if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[0]) +- return secondary_gpu_state->cpu.dumb_fbs[1]; +- else +- return secondary_gpu_state->cpu.dumb_fbs[0]; ++ for (i = 0; i < n_dumb_fbs; i++) ++ { ++ if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i]) ++ return secondary_gpu_state->cpu.dumb_fbs[(i + 1) % n_dumb_fbs]; ++ } ++ ++ return secondary_gpu_state->cpu.dumb_fbs[0]; + } + + static MetaDrmBuffer * +@@ -1029,10 +1059,15 @@ swap_buffer_result_feedback (const MetaKmsFeedback *kms_feedback, + g_warning ("Page flip failed: %s", error->message); + + frame_info = cogl_onscreen_peek_head_frame_info (onscreen); +- frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + +- meta_onscreen_native_notify_frame_complete (onscreen); +- meta_onscreen_native_clear_next_fb (onscreen); ++ /* After resuming from suspend, drop_stalled_swap might have done this ++ * already and emptied the frame_info queue. ++ */ ++ if (frame_info) ++ { ++ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; ++ meta_onscreen_native_notify_frame_complete (onscreen); ++ } + } + + static const MetaKmsResultListenerVtable swap_buffer_result_listener_vtable = { +@@ -1053,30 +1088,35 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; + MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; +- MetaRenderer *renderer = META_RENDERER (renderer_native); +- MetaBackend *backend = meta_renderer_get_backend (renderer); +- MetaMonitorManager *monitor_manager = +- meta_backend_get_monitor_manager (backend); + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + MetaGpuKms *render_gpu = onscreen_native->render_gpu; + MetaDeviceFile *render_device_file; + ClutterFrame *frame = user_data; +- MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); +- MetaKmsUpdate *kms_update; + CoglOnscreenClass *parent_class; + gboolean egl_context_changed = FALSE; +- MetaPowerSave power_save_mode; + g_autoptr (GError) error = NULL; + MetaDrmBufferFlags buffer_flags; + MetaDrmBufferGbm *buffer_gbm; + g_autoptr (MetaDrmBuffer) primary_gpu_fb = NULL; + g_autoptr (MetaDrmBuffer) secondary_gpu_fb = NULL; +- MetaKmsCrtc *kms_crtc; +- MetaKmsDevice *kms_device; ++ size_t rectangles_size; + + COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, + "Onscreen (swap-buffers)"); + ++ if (meta_is_topic_enabled (META_DEBUG_KMS)) ++ { ++ unsigned int frames_pending = ++ cogl_onscreen_count_pending_frames (onscreen); ++ ++ meta_topic (META_DEBUG_KMS, ++ "Swap buffers: %u frames pending (%s-buffering)", ++ frames_pending, ++ frames_pending == 1 ? "double" : ++ frames_pending == 2 ? "triple" : ++ "?"); ++ } ++ + secondary_gpu_fb = + update_secondary_gpu_state_pre_swap_buffers (onscreen, + rectangles, +@@ -1113,6 +1153,7 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + return; + } + ++ reference_owning_gbm_surface (onscreen, buffer_gbm); + primary_gpu_fb = META_DRM_BUFFER (g_steal_pointer (&buffer_gbm)); + break; + case META_RENDERER_NATIVE_MODE_SURFACELESS: +@@ -1132,7 +1173,15 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + switch (renderer_gpu_data->mode) + { + case META_RENDERER_NATIVE_MODE_GBM: +- g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); ++ if (onscreen_native->gbm.next_fb != NULL) ++ { ++ g_warn_if_fail (onscreen_native->gbm.stalled_fb == NULL); ++ drop_stalled_swap (onscreen); ++ g_assert (onscreen_native->gbm.stalled_fb == NULL); ++ onscreen_native->gbm.stalled_fb = ++ g_steal_pointer (&onscreen_native->gbm.next_fb); ++ } ++ + if (onscreen_native->secondary_gpu_state) + g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb); + else +@@ -1146,6 +1195,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + #endif + } + ++ clutter_frame_set_result (frame, ++ CLUTTER_FRAME_RESULT_PENDING_PRESENTED); ++ + /* + * If we changed EGL context, cogl will have the wrong idea about what is + * current, making it fail to set it when it needs to. Avoid that by making +@@ -1155,12 +1207,83 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + if (egl_context_changed) + _cogl_winsys_egl_ensure_current (cogl_display); + +- kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); +- kms_device = meta_kms_crtc_get_device (kms_crtc); ++ rectangles_size = n_rectangles * 4 * sizeof (int); ++ onscreen_native->next_post.rectangles = ++ g_realloc (onscreen_native->next_post.rectangles, rectangles_size); ++ memcpy (onscreen_native->next_post.rectangles, rectangles, rectangles_size); ++ onscreen_native->next_post.n_rectangles = n_rectangles; ++ ++ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); ++ onscreen_native->next_post.frame = clutter_frame_ref (frame); ++ ++ onscreen_native->swaps_pending++; ++ try_post_latest_swap (onscreen); ++} ++ ++static void ++try_post_latest_swap (CoglOnscreen *onscreen) ++{ ++ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); ++ CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); ++ CoglRenderer *cogl_renderer = cogl_context->display->renderer; ++ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; ++ MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; ++ MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; ++ MetaRenderer *renderer = META_RENDERER (renderer_native); ++ MetaBackend *backend = meta_renderer_get_backend (renderer); ++ MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); ++ MetaKms *kms = meta_backend_native_get_kms (backend_native); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); ++ MetaPowerSave power_save_mode; ++ MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); ++ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); ++ MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); ++ MetaKmsUpdate *kms_update; ++ g_autoptr (MetaKmsFeedback) kms_feedback = NULL; ++ g_autoptr (ClutterFrame) frame = NULL; ++ MetaFrameNative *frame_native; ++ ++ if (onscreen_native->next_post.frame == NULL || ++ onscreen_native->view == NULL) ++ return; ++ ++ if (meta_kms_is_shutting_down (kms)) ++ { ++ meta_onscreen_native_discard_pending_swaps (onscreen); ++ return; ++ } + + power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); + if (power_save_mode == META_POWER_SAVE_ON) + { ++ unsigned int frames_pending = ++ cogl_onscreen_count_pending_frames (onscreen); ++ unsigned int posts_pending; ++ ++ g_assert (frames_pending >= onscreen_native->swaps_pending); ++ posts_pending = frames_pending - onscreen_native->swaps_pending; ++ if (posts_pending > 0) ++ return; /* wait for the next frame notification and then try again */ ++ ++ frame = g_steal_pointer (&onscreen_native->next_post.frame); ++ frame_native = meta_frame_native_from_frame (frame); ++ ++ if (onscreen_native->swaps_pending == 0) ++ { ++ if (frame_native) ++ { ++ kms_update = meta_frame_native_steal_kms_update (frame_native); ++ if (kms_update) ++ post_finish_frame (onscreen_native, kms_update); ++ } ++ return; ++ } ++ ++ drop_stalled_swap (onscreen); ++ onscreen_native->swaps_pending--; ++ + kms_update = meta_frame_native_ensure_kms_update (frame_native, + kms_device); + meta_kms_update_add_result_listener (kms_update, +@@ -1175,15 +1298,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + onscreen_native->crtc, + kms_update, + META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, +- rectangles, +- n_rectangles); ++ onscreen_native->next_post.rectangles, ++ onscreen_native->next_post.n_rectangles); + } + else + { + meta_renderer_native_queue_power_save_page_flip (renderer_native, + onscreen); +- clutter_frame_set_result (frame, +- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + +@@ -1203,8 +1324,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + kms_update = meta_frame_native_steal_kms_update (frame_native); + meta_renderer_native_queue_mode_set_update (renderer_native, + kms_update); +- clutter_frame_set_result (frame, +- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + else if (meta_renderer_native_has_pending_mode_set (renderer_native)) +@@ -1218,8 +1337,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + + meta_frame_native_steal_kms_update (frame_native); + meta_renderer_native_post_mode_set_updates (renderer_native); +- clutter_frame_set_result (frame, +- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + break; +@@ -1235,8 +1352,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + kms_update); + + meta_renderer_native_post_mode_set_updates (renderer_native); +- clutter_frame_set_result (frame, +- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + break; +@@ -1251,7 +1366,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + kms_update = meta_frame_native_steal_kms_update (frame_native); + meta_kms_device_post_update (kms_device, kms_update, + META_KMS_UPDATE_FLAG_NONE); +- clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + } + + gboolean +@@ -1296,6 +1410,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, + CoglOnscreen *onscreen = COGL_ONSCREEN (onscreen_native); + const GError *error; + CoglFrameInfo *frame_info; ++ g_autoptr (MetaDrmBuffer) direct_fb = g_steal_pointer (&onscreen_native->gbm.direct_fb); + + error = meta_kms_feedback_get_error (kms_feedback); + if (!error) +@@ -1309,8 +1424,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, + + g_warning ("Direct scanout page flip failed: %s", error->message); + +- cogl_scanout_notify_failed (COGL_SCANOUT (onscreen_native->gbm.next_fb), +- onscreen); ++ cogl_scanout_notify_failed (COGL_SCANOUT (direct_fb), onscreen); + clutter_stage_view_add_redraw_clip (view, NULL); + clutter_stage_view_schedule_update_now (view); + } +@@ -1319,7 +1433,6 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + + meta_onscreen_native_notify_frame_complete (onscreen); +- meta_onscreen_native_clear_next_fb (onscreen); + } + + static const MetaKmsResultListenerVtable scanout_result_listener_vtable = { +@@ -1371,6 +1484,18 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, + return FALSE; + } + ++ /* Our direct scanout frame counts as 1, so more than that means we would ++ * be jumping the queue (and post would fail). ++ */ ++ if (cogl_onscreen_count_pending_frames (onscreen) > 1) ++ { ++ g_set_error_literal (error, ++ COGL_SCANOUT_ERROR, ++ COGL_SCANOUT_ERROR_INHIBITED, ++ "Direct scanout is inhibited during triple buffering"); ++ return FALSE; ++ } ++ + renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, + render_gpu); + +@@ -1385,6 +1510,8 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, + kms_device = meta_kms_crtc_get_device (kms_crtc); + kms_update = meta_frame_native_ensure_kms_update (frame_native, kms_device); + ++ g_set_object (&onscreen_native->gbm.direct_fb, ++ onscreen_native->gbm.next_fb); + meta_kms_update_add_result_listener (kms_update, + &scanout_result_listener_vtable, + NULL, +@@ -1430,12 +1557,6 @@ void + meta_onscreen_native_before_redraw (CoglOnscreen *onscreen, + ClutterFrame *frame) + { +- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); +- MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); +- MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); +- +- meta_kms_device_await_flush (meta_kms_crtc_get_device (kms_crtc), +- kms_crtc); + } + + void +@@ -1555,22 +1676,79 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); + MetaKmsUpdate *kms_update; ++ unsigned int frames_pending = cogl_onscreen_count_pending_frames (onscreen); ++ unsigned int swaps_pending = onscreen_native->swaps_pending; ++ unsigned int posts_pending = frames_pending - swaps_pending; + +- kms_update = meta_frame_native_steal_kms_update (frame_native); +- if (!kms_update) ++ onscreen_native->needs_flush |= meta_kms_device_handle_flush (kms_device, ++ kms_crtc); ++ ++ if (!meta_frame_native_has_kms_update (frame_native)) + { +- if (meta_kms_device_handle_flush (kms_device, kms_crtc)) +- { +- kms_update = meta_kms_update_new (kms_device); +- meta_kms_update_set_flushing (kms_update, kms_crtc); +- } +- else ++ if (!onscreen_native->needs_flush || posts_pending) + { + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); + return; + } + } + ++ if (posts_pending && !swaps_pending) ++ { ++ g_return_if_fail (meta_frame_native_has_kms_update (frame_native)); ++ g_warn_if_fail (onscreen_native->next_post.frame == NULL); ++ ++ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); ++ onscreen_native->next_post.frame = clutter_frame_ref (frame); ++ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); ++ return; ++ } ++ ++ kms_update = meta_frame_native_steal_kms_update (frame_native); ++ ++ if (posts_pending && swaps_pending) ++ { ++ MetaFrameNative *older_frame_native; ++ MetaKmsUpdate *older_kms_update; ++ ++ g_return_if_fail (kms_update); ++ g_return_if_fail (onscreen_native->next_post.frame != NULL); ++ ++ older_frame_native = ++ meta_frame_native_from_frame (onscreen_native->next_post.frame); ++ older_kms_update = ++ meta_frame_native_ensure_kms_update (older_frame_native, kms_device); ++ meta_kms_update_merge_from (older_kms_update, kms_update); ++ meta_kms_update_free (kms_update); ++ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); ++ return; ++ } ++ ++ if (!kms_update) ++ { ++ kms_update = meta_kms_update_new (kms_device); ++ g_warn_if_fail (onscreen_native->needs_flush); ++ } ++ ++ if (onscreen_native->needs_flush) ++ { ++ meta_kms_update_set_flushing (kms_update, kms_crtc); ++ onscreen_native->needs_flush = FALSE; ++ } ++ ++ post_finish_frame (onscreen_native, kms_update); ++ ++ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); ++} ++ ++static void ++post_finish_frame (MetaOnscreenNative *onscreen_native, ++ MetaKmsUpdate *kms_update) ++{ ++ MetaCrtc *crtc = onscreen_native->crtc; ++ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); ++ MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); ++ g_autoptr (MetaKmsFeedback) kms_feedback = NULL; ++ + meta_kms_update_add_result_listener (kms_update, + &finish_frame_result_listener_vtable, + NULL, +@@ -1594,7 +1772,17 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, + meta_kms_update_set_flushing (kms_update, kms_crtc); + meta_kms_device_post_update (kms_device, kms_update, + META_KMS_UPDATE_FLAG_NONE); +- clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); ++} ++ ++void ++meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen) ++{ ++ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); ++ ++ onscreen_native->swaps_pending = 0; ++ ++ g_clear_object (&onscreen_native->gbm.stalled_fb); ++ g_clear_object (&onscreen_native->gbm.next_fb); + } + + static gboolean +@@ -2421,7 +2609,7 @@ meta_onscreen_native_dispose (GObject *object) + { + case META_RENDERER_NATIVE_MODE_GBM: + g_clear_object (&onscreen_native->gbm.next_fb); +- free_current_bo (onscreen); ++ g_clear_object (&onscreen_native->gbm.direct_fb); + break; + case META_RENDERER_NATIVE_MODE_SURFACELESS: + g_assert_not_reached (); +@@ -2455,6 +2643,10 @@ meta_onscreen_native_dispose (GObject *object) + + g_clear_object (&onscreen_native->output); + g_clear_object (&onscreen_native->crtc); ++ ++ g_clear_pointer (&onscreen_native->next_post.rectangles, g_free); ++ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); ++ onscreen_native->next_post.n_rectangles = 0; + } + + static void +diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h +index 91eb7b533..11bb5ba56 100644 +--- a/src/backends/native/meta-onscreen-native.h ++++ b/src/backends/native/meta-onscreen-native.h +@@ -45,6 +45,8 @@ void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, + + void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen); + ++void meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen); ++ + gboolean meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen, + MetaDrmBuffer *fb); + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index e6c653e26..7e39889bc 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -99,6 +99,7 @@ struct _MetaRendererNative + + GList *detached_onscreens; + GList *lingering_onscreens; ++ GList *disabled_crtcs; + guint release_unused_gpus_idle_id; + + GList *power_save_page_flip_onscreens; +@@ -683,6 +684,9 @@ configure_disabled_crtcs (MetaKmsDevice *kms_device, + + kms_update = ensure_mode_set_update (renderer_native, kms_device); + meta_kms_update_mode_set (kms_update, kms_crtc, NULL, NULL); ++ ++ renderer_native->disabled_crtcs = ++ g_list_prepend (renderer_native->disabled_crtcs, kms_crtc); + } + } + +@@ -690,12 +694,18 @@ static gboolean + dummy_power_save_page_flip_cb (gpointer user_data) + { + MetaRendererNative *renderer_native = user_data; ++ GList *old_list = ++ g_steal_pointer (&renderer_native->power_save_page_flip_onscreens); + +- g_list_foreach (renderer_native->power_save_page_flip_onscreens, ++ g_list_foreach (old_list, + (GFunc) meta_onscreen_native_dummy_power_save_page_flip, + NULL); +- g_clear_list (&renderer_native->power_save_page_flip_onscreens, ++ g_clear_list (&old_list, + g_object_unref); ++ ++ if (renderer_native->power_save_page_flip_onscreens != NULL) ++ return G_SOURCE_CONTINUE; ++ + renderer_native->power_save_page_flip_source_id = 0; + + return G_SOURCE_REMOVE; +@@ -707,6 +717,9 @@ meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_na + { + const unsigned int timeout_ms = 100; + ++ if (g_list_find (renderer_native->power_save_page_flip_onscreens, onscreen)) ++ return; ++ + if (!renderer_native->power_save_page_flip_source_id) + { + renderer_native->power_save_page_flip_source_id = +@@ -817,6 +830,22 @@ clear_detached_onscreens (MetaRendererNative *renderer_native) + g_object_unref); + } + ++static void ++clear_disabled_crtcs (MetaRendererNative *renderer_native) ++{ ++ GList *l; ++ ++ for (l = renderer_native->disabled_crtcs; l; l = l->next) ++ { ++ MetaKmsCrtc *kms_crtc = l->data; ++ MetaSwapChain *swap_chain = meta_kms_crtc_get_swap_chain (kms_crtc); ++ ++ meta_swap_chain_release_buffers (swap_chain); ++ } ++ ++ g_clear_list (&renderer_native->disabled_crtcs, NULL); ++} ++ + static void + mode_sets_update_result_feedback (const MetaKmsFeedback *kms_feedback, + gpointer user_data) +@@ -878,6 +907,7 @@ meta_renderer_native_post_mode_set_updates (MetaRendererNative *renderer_native) + post_mode_set_updates (renderer_native); + + clear_detached_onscreens (renderer_native); ++ clear_disabled_crtcs (renderer_native); + + meta_kms_notify_modes_set (kms); + +@@ -1467,6 +1497,26 @@ detach_onscreens (MetaRenderer *renderer) + } + } + ++static void ++discard_pending_swaps (MetaRenderer *renderer) ++{ ++ GList *views = meta_renderer_get_views (renderer);; ++ GList *l; ++ ++ for (l = views; l; l = l->next) ++ { ++ ClutterStageView *stage_view = l->data; ++ CoglFramebuffer *fb = clutter_stage_view_get_onscreen (stage_view); ++ CoglOnscreen *onscreen; ++ ++ if (!COGL_IS_ONSCREEN (fb)) ++ continue; ++ ++ onscreen = COGL_ONSCREEN (fb); ++ meta_onscreen_native_discard_pending_swaps (onscreen); ++ } ++} ++ + static void + meta_renderer_native_rebuild_views (MetaRenderer *renderer) + { +@@ -1477,6 +1527,7 @@ meta_renderer_native_rebuild_views (MetaRenderer *renderer) + MetaRendererClass *parent_renderer_class = + META_RENDERER_CLASS (meta_renderer_native_parent_class); + ++ discard_pending_swaps (renderer); + meta_kms_discard_pending_page_flips (kms); + g_hash_table_remove_all (renderer_native->mode_set_updates); + +@@ -2239,6 +2290,7 @@ meta_renderer_native_finalize (GObject *object) + g_clear_handle_id (&renderer_native->release_unused_gpus_idle_id, + g_source_remove); + clear_detached_onscreens (renderer_native); ++ clear_disabled_crtcs (renderer_native); + + g_hash_table_destroy (renderer_native->gpu_datas); + g_clear_object (&renderer_native->gles3); diff --git a/src/backends/native/meta-swap-chain.c b/src/backends/native/meta-swap-chain.c new file mode 100644 -index 0000000000..c3bed569d8 +index 000000000..c3bed569d --- /dev/null +++ b/src/backends/native/meta-swap-chain.c @@ -0,0 +1,149 @@ @@ -283,7 +1994,7 @@ index 0000000000..c3bed569d8 +} diff --git a/src/backends/native/meta-swap-chain.h b/src/backends/native/meta-swap-chain.h new file mode 100644 -index 0000000000..bad772b892 +index 000000000..bad772b89 --- /dev/null +++ b/src/backends/native/meta-swap-chain.h @@ -0,0 +1,48 @@ @@ -336,7 +2047,7 @@ index 0000000000..bad772b892 + +#endif /* META_SWAP_CHAIN_H */ diff --git a/src/meson.build b/src/meson.build -index ca2ef166cf..0038988bd2 100644 +index ca2ef166c..0038988bd 100644 --- a/src/meson.build +++ b/src/meson.build @@ -850,6 +850,8 @@ if have_native_backend @@ -348,2741 +2059,8 @@ index ca2ef166cf..0038988bd2 100644 'backends/native/meta-thread-impl.c', 'backends/native/meta-thread-impl.h', 'backends/native/meta-thread-private.h', --- -GitLab - - -From ff9ae92b7e1c89e1e7f65274f2b932b9467c8e98 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Wed, 18 Jan 2023 18:42:01 +0800 -Subject: [PATCH 04/42] kms/crtc: Add a local swap chain - ---- - src/backends/native/meta-kms-crtc.c | 21 +++++++++++++++++++++ - src/backends/native/meta-kms-crtc.h | 3 +++ - 2 files changed, 24 insertions(+) - -diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c -index 707da9dc17..932e5e6a08 100644 ---- a/src/backends/native/meta-kms-crtc.c -+++ b/src/backends/native/meta-kms-crtc.c -@@ -48,6 +48,8 @@ struct _MetaKmsCrtc - MetaKmsCrtcState current_state; - - MetaKmsCrtcPropTable prop_table; -+ -+ MetaSwapChain *swap_chain; - }; - - G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT) -@@ -99,6 +101,12 @@ meta_kms_crtc_get_prop_drm_value (MetaKmsCrtc *crtc, - return meta_kms_prop_convert_value (prop, value); - } - -+MetaSwapChain * -+meta_kms_crtc_get_swap_chain (MetaKmsCrtc *crtc) -+{ -+ return crtc->swap_chain; -+} -+ - gboolean - meta_kms_crtc_is_active (MetaKmsCrtc *crtc) - { -@@ -465,12 +473,23 @@ meta_kms_crtc_new (MetaKmsImplDevice *impl_device, - return crtc; - } - -+static void -+meta_kms_crtc_dispose (GObject *object) -+{ -+ MetaKmsCrtc *crtc = META_KMS_CRTC (object); -+ -+ meta_swap_chain_release_buffers (crtc->swap_chain); -+ -+ G_OBJECT_CLASS (meta_kms_crtc_parent_class)->dispose (object); -+} -+ - static void - meta_kms_crtc_finalize (GObject *object) - { - MetaKmsCrtc *crtc = META_KMS_CRTC (object); - - g_clear_pointer (&crtc->current_state.gamma.value, meta_gamma_lut_free); -+ g_clear_object (&crtc->swap_chain); - - G_OBJECT_CLASS (meta_kms_crtc_parent_class)->finalize (object); - } -@@ -480,6 +499,7 @@ meta_kms_crtc_init (MetaKmsCrtc *crtc) - { - crtc->current_state.gamma.size = 0; - crtc->current_state.gamma.value = NULL; -+ crtc->swap_chain = meta_swap_chain_new (); - } - - static void -@@ -487,6 +507,7 @@ 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 b26b682ddb..a30a6de6e7 100644 ---- a/src/backends/native/meta-kms-crtc.h -+++ b/src/backends/native/meta-kms-crtc.h -@@ -22,6 +22,7 @@ - #include - - #include "backends/native/meta-kms-types.h" -+#include "backends/native/meta-swap-chain.h" - #include "backends/meta-backend-types.h" - #include "core/util-private.h" - #include "meta/boxes.h" -@@ -60,3 +61,5 @@ int meta_kms_crtc_get_idx (MetaKmsCrtc *crtc); - - META_EXPORT_TEST - gboolean meta_kms_crtc_is_active (MetaKmsCrtc *crtc); -+ -+MetaSwapChain * meta_kms_crtc_get_swap_chain (MetaKmsCrtc *crtc); --- -GitLab - - -From 698464ef1757482d524f2953a821ff223aae2da1 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 17 Aug 2021 16:35:18 +0800 -Subject: [PATCH 05/42] onscreen/native: Pump the CRTC's buffer reference queue - on scanout - -Precise timing of this call is not important. We only need to notify -that the *previous* scanout is definitely no longer happening. So any -old buffers no longer used in the latest scanout can be released. ---- - src/backends/native/meta-onscreen-native.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 7c0f883d5b..e95d01a1ea 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -229,6 +229,7 @@ notify_view_crtc_presented (MetaRendererView *view, - - meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_swap_drm_fb (onscreen); -+ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (kms_crtc)); - } - - static void --- -GitLab - - -From 8f41552e05952cc144cc19e5e4b74f54daeceeaa Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Wed, 9 Feb 2022 16:44:09 +0800 -Subject: [PATCH 06/42] kms/impl-device: Release buffers from MetaKmsCrtc in - prepare_shutdown - -This allows them to release their buffers cleanly while all the -associated resources to do the release still exist. Otherwise we -might crash later in meta_drm_buffer_finalize. ---- - src/backends/native/meta-kms-impl-device.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c -index da372383d9..7bc5efa654 100644 ---- a/src/backends/native/meta-kms-impl-device.c -+++ b/src/backends/native/meta-kms-impl-device.c -@@ -1848,6 +1848,16 @@ meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device, - return TRUE; - } - -+static void -+release_buffers (gpointer data, -+ gpointer user_data) -+{ -+ MetaKmsCrtc *crtc = data; -+ MetaSwapChain *swap_chain = meta_kms_crtc_get_swap_chain (crtc); -+ -+ meta_swap_chain_release_buffers (swap_chain); -+} -+ - void - meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device) - { -@@ -1855,6 +1865,8 @@ meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device) - meta_kms_impl_device_get_instance_private (impl_device); - MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); - -+ g_list_foreach (priv->crtcs, release_buffers, NULL); -+ - if (klass->prepare_shutdown) - klass->prepare_shutdown (impl_device); - --- -GitLab - - -From 1f6fc877f03a9f4aac31a97b8131ae7cafc4732d Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 7 Dec 2021 18:31:16 +0800 -Subject: [PATCH 07/42] kms/impl-device/simple: Pump the CRTC's buffer - reference queue on scanout - -In this case "on scanout" means when `drmModeSetCrtc` succeeds. This is -the only case where there won't be a `meta_kms_page_flip_data_flipped` to -do it for us. ---- - src/backends/native/meta-kms-impl-device-simple.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c -index 2d68ba11f6..7196837f9c 100644 ---- a/src/backends/native/meta-kms-impl-device-simple.c -+++ b/src/backends/native/meta-kms-impl-device-simple.c -@@ -485,6 +485,8 @@ process_mode_set (MetaKmsImplDevice *impl_device, - return FALSE; - } - -+ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (crtc)); -+ - if (drm_mode) - { - g_hash_table_replace (impl_device_simple->cached_mode_sets, -@@ -877,6 +879,8 @@ mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple, - return FALSE; - } - -+ meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (crtc)); -+ - if (!impl_device_simple->mode_set_fallback_feedback_source) - { - MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); --- -GitLab - - -From 6345d86fd7d2d4b5f15ee6905ecd99bfcf043f8b Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 17 Aug 2021 17:40:57 +0800 -Subject: [PATCH 08/42] kms/impl-device/simple: Keep a reference to buffers - assigned to planes - -The MetaKmsCrtc will hold these references during future scanouts until -such time as a new buffer is assigned to the plane AND another scanout -has occurred after that. ---- - src/backends/native/meta-kms-impl-device-simple.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c -index 7196837f9c..26660e5f48 100644 ---- a/src/backends/native/meta-kms-impl-device-simple.c -+++ b/src/backends/native/meta-kms-impl-device-simple.c -@@ -1303,7 +1303,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, -@@ -1317,7 +1317,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, -@@ -1330,6 +1330,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, - } - - g_assert_not_reached (); -+ -+assigned: -+ meta_swap_chain_push_buffer (meta_kms_crtc_get_swap_chain (plane_assignment->crtc), -+ meta_kms_plane_get_id (plane), -+ G_OBJECT (plane_assignment->buffer)); -+ return TRUE; - } - - static gboolean --- -GitLab - - -From 8024d68dc0453a1d8bbf5118c8954de6264c5847 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 7 Dec 2021 17:54:15 +0800 -Subject: [PATCH 09/42] kms/impl-device/simple: Keep a reference to buffers - needed in retries - ---- - .../native/meta-kms-impl-device-simple.c | 24 ++++++++++++------- - 1 file changed, 15 insertions(+), 9 deletions(-) - -diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c -index 26660e5f48..f4e23df074 100644 ---- a/src/backends/native/meta-kms-impl-device-simple.c -+++ b/src/backends/native/meta-kms-impl-device-simple.c -@@ -556,7 +556,7 @@ is_timestamp_earlier_than (uint64_t ts1, - typedef struct _RetryPageFlipData - { - MetaKmsCrtc *crtc; -- uint32_t fb_id; -+ MetaDrmBuffer *fb; - MetaKmsPageFlipData *page_flip_data; - float refresh_rate; - uint64_t retry_time_us; -@@ -569,6 +569,7 @@ retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data) - g_assert (!retry_page_flip_data->page_flip_data); - g_clear_pointer (&retry_page_flip_data->custom_page_flip, - meta_kms_custom_page_flip_free); -+ g_clear_object (&retry_page_flip_data->fb); - g_free (retry_page_flip_data); - } - -@@ -636,16 +637,21 @@ retry_page_flips (gpointer user_data) - } - else - { -+ uint32_t fb_id = -+ retry_page_flip_data->fb ? -+ meta_drm_buffer_get_fb_id (retry_page_flip_data->fb) : -+ 0; -+ - meta_topic (META_DEBUG_KMS, - "[simple] Retrying page flip on CRTC %u (%s) with %u", - meta_kms_crtc_get_id (crtc), - meta_kms_impl_device_get_path (impl_device), -- retry_page_flip_data->fb_id); -+ fb_id); - - fd = meta_kms_impl_device_get_fd (impl_device); - ret = drmModePageFlip (fd, - meta_kms_crtc_get_id (crtc), -- retry_page_flip_data->fb_id, -+ fb_id, - DRM_MODE_PAGE_FLIP_EVENT, - retry_page_flip_data->page_flip_data); - } -@@ -732,7 +738,7 @@ retry_page_flips (gpointer user_data) - static void - schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple, - MetaKmsCrtc *crtc, -- uint32_t fb_id, -+ MetaDrmBuffer *fb, - float refresh_rate, - MetaKmsPageFlipData *page_flip_data, - MetaKmsCustomPageFlip *custom_page_flip) -@@ -747,7 +753,7 @@ schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple, - retry_page_flip_data = g_new0 (RetryPageFlipData, 1); - *retry_page_flip_data = (RetryPageFlipData) { - .crtc = crtc, -- .fb_id = fb_id, -+ .fb = fb ? g_object_ref (fb) : NULL, - .page_flip_data = page_flip_data, - .refresh_rate = refresh_rate, - .retry_time_us = retry_time_us, -@@ -1007,20 +1013,20 @@ dispatch_page_flip (MetaKmsImplDevice *impl_device, - cached_mode_set = get_cached_mode_set (impl_device_simple, crtc); - if (cached_mode_set) - { -- uint32_t fb_id; -+ MetaDrmBuffer *fb; - drmModeModeInfo *drm_mode; - float refresh_rate; - - if (plane_assignment) -- fb_id = meta_drm_buffer_get_fb_id (plane_assignment->buffer); -+ fb = plane_assignment->buffer; - else -- fb_id = 0; -+ fb = NULL; - drm_mode = cached_mode_set->drm_mode; - refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); - meta_kms_impl_device_hold_fd (impl_device); - schedule_retry_page_flip (impl_device_simple, - crtc, -- fb_id, -+ fb, - refresh_rate, - page_flip_data, - g_steal_pointer (&custom_page_flip)); --- -GitLab - - -From ff6752168208f156171aad34ce9faa1955ee1c4b Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 17 Aug 2021 16:36:25 +0800 -Subject: [PATCH 10/42] kms/impl-device/atomic: Keep a reference to buffers - assigned to planes - -The MetaKmsCrtc will hold these references during future scanouts until -such time as a new buffer is assigned to the plane AND another scanout -has occurred after that. ---- - src/backends/native/meta-kms-impl-device-atomic.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/src/backends/native/meta-kms-impl-device-atomic.c b/src/backends/native/meta-kms-impl-device-atomic.c -index d3fd772686..e7497607b5 100644 ---- a/src/backends/native/meta-kms-impl-device-atomic.c -+++ b/src/backends/native/meta-kms-impl-device-atomic.c -@@ -505,6 +505,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, - { - MetaKmsPlaneAssignment *plane_assignment = update_entry; - MetaKmsPlane *plane = plane_assignment->plane; -+ MetaKmsUpdateFlag flags = (MetaKmsUpdateFlag) user_data; - MetaDrmBuffer *buffer; - MetaKmsFbDamage *fb_damage; - uint32_t prop_id; -@@ -657,6 +658,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device, - error)) - return FALSE; - } -+ -+ if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) -+ meta_swap_chain_push_buffer (meta_kms_crtc_get_swap_chain (plane_assignment->crtc), -+ meta_kms_plane_get_id (plane), -+ G_OBJECT (buffer)); -+ - return TRUE; - } - -@@ -1001,7 +1008,7 @@ meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device, - req, - blob_ids, - meta_kms_update_get_plane_assignments (update), -- NULL, -+ GUINT_TO_POINTER (flags), - process_plane_assignment, - &error)) - goto err; --- -GitLab - - -From 36c9d57f7cec8b364ba2530028a6105d4cbe9401 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Wed, 18 Aug 2021 15:04:27 +0800 -Subject: [PATCH 11/42] onscreen/native: Stop holding the current onscreen - buffer - -The scanout lifetime of the buffer is now automatically managed via -meta_crtc_kms_assign_primary_plane. Se we don't need `current_fb` at -all, and `next_fb` can be unreferenced as soon as we've passed it to -meta_crtc_kms_assign_primary_plane. ---- - src/backends/native/meta-onscreen-native.c | 43 ++-------------------- - 1 file changed, 4 insertions(+), 39 deletions(-) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index e95d01a1ea..636dff1311 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -93,7 +93,6 @@ struct _MetaOnscreenNative - - struct { - struct gbm_surface *surface; -- MetaDrmBuffer *current_fb; - MetaDrmBuffer *next_fb; - } gbm; - -@@ -128,36 +127,6 @@ init_secondary_gpu_state (MetaRendererNative *renderer_native, - CoglOnscreen *onscreen, - GError **error); - --static void --free_current_bo (CoglOnscreen *onscreen) --{ -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -- -- g_clear_object (&onscreen_native->gbm.current_fb); --} -- --static void --meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) --{ -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -- -- if (!onscreen_native->gbm.next_fb) -- return; -- -- free_current_bo (onscreen); -- -- g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); -- g_clear_object (&onscreen_native->gbm.next_fb); --} -- --static void --meta_onscreen_native_clear_next_fb (CoglOnscreen *onscreen) --{ -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -- -- g_clear_object (&onscreen_native->gbm.next_fb); --} -- - static void - maybe_update_frame_info (MetaCrtc *crtc, - CoglFrameInfo *frame_info, -@@ -228,7 +197,6 @@ 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); - meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (kms_crtc)); - } - -@@ -337,7 +305,6 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - - meta_onscreen_native_notify_frame_complete (onscreen); -- meta_onscreen_native_clear_next_fb (onscreen); - } - - static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { -@@ -402,8 +369,9 @@ void - meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) - { - CoglFrameInfo *frame_info; -+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - -- meta_onscreen_native_swap_drm_fb (onscreen); -+ g_clear_object (&onscreen_native->gbm.next_fb); - - frame_info = cogl_onscreen_peek_tail_frame_info (onscreen); - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; -@@ -426,7 +394,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, - MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); - MetaRendererNativeGpuData *renderer_gpu_data; - MetaGpuKms *gpu_kms; -- MetaDrmBuffer *buffer; -+ g_autoptr (MetaDrmBuffer) buffer = NULL; - MetaKmsPlaneAssignment *plane_assignment; - - COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs, -@@ -441,7 +409,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, - switch (renderer_gpu_data->mode) - { - case META_RENDERER_NATIVE_MODE_GBM: -- buffer = onscreen_native->gbm.next_fb; -+ buffer = g_steal_pointer (&onscreen_native->gbm.next_fb); - - plane_assignment = meta_crtc_kms_assign_primary_plane (crtc_kms, - buffer, -@@ -1045,7 +1013,6 @@ swap_buffer_result_feedback (const MetaKmsFeedback *kms_feedback, - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - - meta_onscreen_native_notify_frame_complete (onscreen); -- meta_onscreen_native_clear_next_fb (onscreen); - } - - static const MetaKmsResultListenerVtable swap_buffer_result_listener_vtable = { -@@ -1333,7 +1300,6 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - - meta_onscreen_native_notify_frame_complete (onscreen); -- meta_onscreen_native_clear_next_fb (onscreen); - } - - static const MetaKmsResultListenerVtable scanout_result_listener_vtable = { -@@ -2435,7 +2401,6 @@ meta_onscreen_native_dispose (GObject *object) - { - case META_RENDERER_NATIVE_MODE_GBM: - g_clear_object (&onscreen_native->gbm.next_fb); -- free_current_bo (onscreen); - break; - case META_RENDERER_NATIVE_MODE_SURFACELESS: - g_assert_not_reached (); --- -GitLab - - -From 9f88429f51942f44b7902fe48c0e7f2a92029ff2 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 2 Mar 2023 18:01:26 +0800 -Subject: [PATCH 12/42] FIXME: Temporary workaround for the scanout-failed - signal - ---- - src/backends/native/meta-onscreen-native.c | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 636dff1311..74a5fcec1b 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -94,6 +94,11 @@ struct _MetaOnscreenNative - struct { - struct gbm_surface *surface; - MetaDrmBuffer *next_fb; -+ -+ /* Temporary workaround for the scanout-failed signal wanting the buffer -+ * to live longer than it does, and then it doesn't use it anyway... -+ */ -+ MetaDrmBuffer *direct_fb; - } gbm; - - #ifdef HAVE_EGL_DEVICE -@@ -1277,6 +1282,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, - CoglOnscreen *onscreen = COGL_ONSCREEN (onscreen_native); - const GError *error; - CoglFrameInfo *frame_info; -+ g_autoptr (MetaDrmBuffer) direct_fb = g_steal_pointer (&onscreen_native->gbm.direct_fb); - - error = meta_kms_feedback_get_error (kms_feedback); - if (!error) -@@ -1290,8 +1296,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, - - g_warning ("Direct scanout page flip failed: %s", error->message); - -- cogl_scanout_notify_failed (COGL_SCANOUT (onscreen_native->gbm.next_fb), -- onscreen); -+ cogl_scanout_notify_failed (COGL_SCANOUT (direct_fb), onscreen); - clutter_stage_view_add_redraw_clip (view, NULL); - clutter_stage_view_schedule_update_now (view); - } -@@ -1365,6 +1370,8 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, - kms_device = meta_kms_crtc_get_device (kms_crtc); - kms_update = meta_frame_native_ensure_kms_update (frame_native, kms_device); - -+ g_set_object (&onscreen_native->gbm.direct_fb, -+ onscreen_native->gbm.next_fb); - meta_kms_update_add_result_listener (kms_update, - &scanout_result_listener_vtable, - NULL, -@@ -2401,6 +2408,7 @@ meta_onscreen_native_dispose (GObject *object) - { - case META_RENDERER_NATIVE_MODE_GBM: - g_clear_object (&onscreen_native->gbm.next_fb); -+ g_clear_object (&onscreen_native->gbm.direct_fb); - break; - case META_RENDERER_NATIVE_MODE_SURFACELESS: - g_assert_not_reached (); --- -GitLab - - -From e3337d62869873fbb120076686941b482db9185d Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 15 Sep 2023 18:24:22 +0800 -Subject: [PATCH 13/42] renderer/native: Release buffers from disabled CRTCs on - hotplug - -CRTCs tend to live for the lifetime of the device, but they also -(currently) own the swap chains. So when a monitor is unplugged we -need to tell the CRTC when it's safe to clear its swap chain and release -all buffers. This only happens after the mode set to disable the CRTC -has completed. - -Without this change, CRTCs that had been used in the past would keep -their swap chains populated even after the monitor was unplugged and the -CRTC disabled. This led to a leak of the old onscreen too, because of: - -onscreen/native: Keep the onscreen alive longer than `MetaDrmBufferGbm` ---- - src/backends/native/meta-renderer-native.c | 22 ++++++++++++++++++++++ - 1 file changed, 22 insertions(+) - -diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c -index 57cb20b7e9..9502473d6c 100644 ---- a/src/backends/native/meta-renderer-native.c -+++ b/src/backends/native/meta-renderer-native.c -@@ -98,6 +98,7 @@ struct _MetaRendererNative - - GList *detached_onscreens; - GList *lingering_onscreens; -+ GList *disabled_crtcs; - guint release_unused_gpus_idle_id; - - GList *power_save_page_flip_onscreens; -@@ -676,6 +677,9 @@ configure_disabled_crtcs (MetaKmsDevice *kms_device, - - kms_update = ensure_mode_set_update (renderer_native, kms_device); - meta_kms_update_mode_set (kms_update, kms_crtc, NULL, NULL); -+ -+ renderer_native->disabled_crtcs = -+ g_list_prepend (renderer_native->disabled_crtcs, kms_crtc); - } - } - -@@ -810,6 +814,22 @@ clear_detached_onscreens (MetaRendererNative *renderer_native) - g_object_unref); - } - -+static void -+clear_disabled_crtcs (MetaRendererNative *renderer_native) -+{ -+ GList *l; -+ -+ for (l = renderer_native->disabled_crtcs; l; l = l->next) -+ { -+ MetaKmsCrtc *kms_crtc = l->data; -+ MetaSwapChain *swap_chain = meta_kms_crtc_get_swap_chain (kms_crtc); -+ -+ meta_swap_chain_release_buffers (swap_chain); -+ } -+ -+ g_clear_list (&renderer_native->disabled_crtcs, NULL); -+} -+ - static void - mode_sets_update_result_feedback (const MetaKmsFeedback *kms_feedback, - gpointer user_data) -@@ -871,6 +891,7 @@ meta_renderer_native_post_mode_set_updates (MetaRendererNative *renderer_native) - post_mode_set_updates (renderer_native); - - clear_detached_onscreens (renderer_native); -+ clear_disabled_crtcs (renderer_native); - - meta_kms_notify_modes_set (kms); - -@@ -2237,6 +2258,7 @@ meta_renderer_native_finalize (GObject *object) - g_clear_handle_id (&renderer_native->release_unused_gpus_idle_id, - g_source_remove); - clear_detached_onscreens (renderer_native); -+ clear_disabled_crtcs (renderer_native); - - g_hash_table_destroy (renderer_native->gpu_datas); - g_clear_object (&renderer_native->gles3); --- -GitLab - - -From 1d5a301654a16b7824813cfa611f2f2a9d5c42d7 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 17 Sep 2021 17:48:20 +0800 -Subject: [PATCH 14/42] cogl/onscreen: Add function - cogl_onscreen_count_pending_frames - ---- - cogl/cogl/cogl-onscreen-private.h | 3 +++ - cogl/cogl/cogl-onscreen.c | 8 ++++++++ - 2 files changed, 11 insertions(+) - -diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h -index 9dbecfd0c9..681d91d2b0 100644 ---- a/cogl/cogl/cogl-onscreen-private.h -+++ b/cogl/cogl/cogl-onscreen-private.h -@@ -95,3 +95,6 @@ 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); -diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c -index 73425e4985..02c4474b2d 100644 ---- a/cogl/cogl/cogl-onscreen.c -+++ b/cogl/cogl/cogl-onscreen.c -@@ -508,6 +508,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen) - return g_queue_pop_head (&priv->pending_frame_infos); - } - -+unsigned int -+cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen) -+{ -+ CoglOnscreenPrivate *priv = cogl_onscreen_get_instance_private (onscreen); -+ -+ return g_queue_get_length (&priv->pending_frame_infos); -+} -+ - CoglFrameClosure * - cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, - CoglFrameCallback callback, --- -GitLab - - -From 5429d2dd84954d292528b34f232a73edb015e46e Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Wed, 20 Apr 2022 18:33:43 +0800 -Subject: [PATCH 15/42] kms: Keep a shutting_down flag - ---- - src/backends/native/meta-kms.c | 9 +++++++++ - src/backends/native/meta-kms.h | 2 ++ - 2 files changed, 11 insertions(+) - -diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c -index ec009ec8cd..c6708946e8 100644 ---- a/src/backends/native/meta-kms.c -+++ b/src/backends/native/meta-kms.c -@@ -155,6 +155,8 @@ struct _MetaKms - int kernel_thread_inhibit_count; - - MetaKmsCursorManager *cursor_manager; -+ -+ gboolean shutting_down; - }; - - G_DEFINE_TYPE (MetaKms, meta_kms, META_TYPE_THREAD) -@@ -433,6 +435,7 @@ static void - on_prepare_shutdown (MetaBackend *backend, - MetaKms *kms) - { -+ kms->shutting_down = TRUE; - meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL); - meta_thread_flush_callbacks (META_THREAD (kms)); - -@@ -487,6 +490,12 @@ meta_kms_new (MetaBackend *backend, - return kms; - } - -+gboolean -+meta_kms_is_shutting_down (MetaKms *kms) -+{ -+ return kms->shutting_down; -+} -+ - static void - meta_kms_finalize (GObject *object) - { -diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h -index 7434014063..f6b19520be 100644 ---- a/src/backends/native/meta-kms.h -+++ b/src/backends/native/meta-kms.h -@@ -60,6 +60,8 @@ MetaKmsDevice * meta_kms_create_device (MetaKms *kms, - MetaKmsDeviceFlag flags, - GError **error); - -+gboolean meta_kms_is_shutting_down (MetaKms *kms); -+ - MetaKms * meta_kms_new (MetaBackend *backend, - MetaKmsFlags flags, - GError **error); --- -GitLab - - -From 5ab50e440426cf74e1e2c6726767dd6273e41ac8 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 26 Oct 2021 18:50:50 +0800 -Subject: [PATCH 16/42] renderer/native: Avoid requeuing the same onscreen for - a power save flip - -This is a case that triple buffering will encounter. We don't want it -to queue the same onscreen multiple times because that would represent -multiple flips occurring simultaneously. - -It's a linear search but the list length is typically only 1 or 2 so -no need for anything fancier yet. ---- - src/backends/native/meta-renderer-native.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c -index 9502473d6c..f47db924b9 100644 ---- a/src/backends/native/meta-renderer-native.c -+++ b/src/backends/native/meta-renderer-native.c -@@ -704,6 +704,9 @@ meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_na - { - const unsigned int timeout_ms = 100; - -+ if (g_list_find (renderer_native->power_save_page_flip_onscreens, onscreen)) -+ return; -+ - if (!renderer_native->power_save_page_flip_source_id) - { - renderer_native->power_save_page_flip_source_id = --- -GitLab - - -From 95fbd33b03dce1dc8a946172414360998f74c50c Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Mon, 1 Nov 2021 19:35:34 +0800 -Subject: [PATCH 17/42] renderer/native: Steal the power save flip list before - iterating over it - -Because a single iteration might also grow the list again. ---- - src/backends/native/meta-renderer-native.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c -index f47db924b9..f50c930eea 100644 ---- a/src/backends/native/meta-renderer-native.c -+++ b/src/backends/native/meta-renderer-native.c -@@ -687,12 +687,18 @@ static gboolean - dummy_power_save_page_flip_cb (gpointer user_data) - { - MetaRendererNative *renderer_native = user_data; -+ GList *old_list = -+ g_steal_pointer (&renderer_native->power_save_page_flip_onscreens); - -- g_list_foreach (renderer_native->power_save_page_flip_onscreens, -+ g_list_foreach (old_list, - (GFunc) meta_onscreen_native_dummy_power_save_page_flip, - NULL); -- g_clear_list (&renderer_native->power_save_page_flip_onscreens, -+ g_clear_list (&old_list, - g_object_unref); -+ -+ if (renderer_native->power_save_page_flip_onscreens != NULL) -+ return G_SOURCE_CONTINUE; -+ - renderer_native->power_save_page_flip_source_id = 0; - - return G_SOURCE_REMOVE; --- -GitLab - - -From 410ec19e45f6d370e9e260179836d87621631b62 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 10 Dec 2021 16:40:58 +0800 -Subject: [PATCH 18/42] onscreen/native: Log swapbuffers and N-buffering when - MUTTER_DEBUG=kms - ---- - src/backends/native/meta-onscreen-native.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 74a5fcec1b..5965845548 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -1062,6 +1062,19 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, - "Onscreen (swap-buffers)"); - -+ if (meta_is_topic_enabled (META_DEBUG_KMS)) -+ { -+ unsigned int frames_pending = -+ cogl_onscreen_count_pending_frames (onscreen); -+ -+ meta_topic (META_DEBUG_KMS, -+ "Swap buffers: %u frames pending (%s-buffering)", -+ frames_pending, -+ frames_pending == 1 ? "double" : -+ frames_pending == 2 ? "triple" : -+ "?"); -+ } -+ - secondary_gpu_fb = - update_secondary_gpu_state_pre_swap_buffers (onscreen, - rectangles, --- -GitLab - - -From 2c7956b62632c7822756547892cfd26dbccb0fc2 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Wed, 28 Jul 2021 16:35:56 +0800 -Subject: [PATCH 19/42] onscreen/native: Replace an assertion that double - buffering is the maximum - -Because it soon won't be the maximum. But we do want to verify that the -frame info queue is not empty, to avoid NULL dereferencing and catch logic -errors. ---- - src/backends/native/meta-onscreen-native.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 5965845548..b0e90528e8 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -167,7 +167,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); --- -GitLab - - -From a62bec8dc8f4bcdcb19aa9e80aac1a351230e5d7 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 16 Sep 2021 16:26:25 +0800 -Subject: [PATCH 20/42] onscreen/native: Deduplicate calls to - clutter_frame_set_result - -All paths out of `meta_onscreen_native_swap_buffers_with_damage` from -here onward would set the same `CLUTTER_FRAME_RESULT_PENDING_PRESENTED` -(or terminate with `g_assert_not_reached`). - -Even failed posts set this result because they will do a -`meta_onscreen_native_notify_frame_complete` in -`page_flip_feedback_discarded`. ---- - src/backends/native/meta-onscreen-native.c | 12 +++--------- - 1 file changed, 3 insertions(+), 9 deletions(-) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index b0e90528e8..420c685397 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -1145,6 +1145,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - #endif - } - -+ clutter_frame_set_result (frame, -+ CLUTTER_FRAME_RESULT_PENDING_PRESENTED); -+ - /* - * If we changed EGL context, cogl will have the wrong idea about what is - * current, making it fail to set it when it needs to. Avoid that by making -@@ -1181,8 +1184,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - { - meta_renderer_native_queue_power_save_page_flip (renderer_native, - onscreen); -- clutter_frame_set_result (frame, -- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - return; - } - -@@ -1202,8 +1203,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - kms_update = meta_frame_native_steal_kms_update (frame_native); - meta_renderer_native_queue_mode_set_update (renderer_native, - kms_update); -- clutter_frame_set_result (frame, -- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - return; - } - else if (meta_renderer_native_has_pending_mode_set (renderer_native)) -@@ -1217,8 +1216,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - - meta_frame_native_steal_kms_update (frame_native); - meta_renderer_native_post_mode_set_updates (renderer_native); -- clutter_frame_set_result (frame, -- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - return; - } - break; -@@ -1234,8 +1231,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - kms_update); - - meta_renderer_native_post_mode_set_updates (renderer_native); -- clutter_frame_set_result (frame, -- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - return; - } - break; -@@ -1250,7 +1245,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - kms_update = meta_frame_native_steal_kms_update (frame_native); - meta_kms_device_post_update (kms_device, kms_update, - META_KMS_UPDATE_FLAG_NONE); -- clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - } - - gboolean --- -GitLab - - -From 04e51088cb481173054e09e5c87dc56641206a51 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Wed, 28 Jul 2021 16:29:27 +0800 -Subject: [PATCH 21/42] onscreen/native: Split swap_buffers_with_damage into - two functions - -1. The EGL part: meta_onscreen_native_swap_buffers_with_damage -2. The KMS part: post_latest_swap ---- - src/backends/native/meta-onscreen-native.c | 66 +++++++++++++++++----- - 1 file changed, 53 insertions(+), 13 deletions(-) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 420c685397..7346c04d95 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -120,6 +120,12 @@ struct _MetaOnscreenNative - gulong privacy_screen_changed_handler_id; - gulong color_space_changed_handler_id; - gulong hdr_metadata_changed_handler_id; -+ -+ struct { -+ int *rectangles; /* 4 x n_rectangles */ -+ int n_rectangles; -+ ClutterFrame *frame; -+ } next_post; - }; - - G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, -@@ -127,6 +133,9 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, - - static GQuark blit_source_quark = 0; - -+static void -+post_latest_swap (CoglOnscreen *onscreen); -+ - static gboolean - init_secondary_gpu_state (MetaRendererNative *renderer_native, - CoglOnscreen *onscreen, -@@ -1038,26 +1047,18 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; - MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; - MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; -- MetaRenderer *renderer = META_RENDERER (renderer_native); -- MetaBackend *backend = meta_renderer_get_backend (renderer); -- MetaMonitorManager *monitor_manager = -- meta_backend_get_monitor_manager (backend); - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaGpuKms *render_gpu = onscreen_native->render_gpu; - MetaDeviceFile *render_device_file; - ClutterFrame *frame = user_data; -- MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); -- MetaKmsUpdate *kms_update; - CoglOnscreenClass *parent_class; - gboolean egl_context_changed = FALSE; -- MetaPowerSave power_save_mode; - g_autoptr (GError) error = NULL; - MetaDrmBufferFlags buffer_flags; - MetaDrmBufferGbm *buffer_gbm; - g_autoptr (MetaDrmBuffer) primary_gpu_fb = NULL; - g_autoptr (MetaDrmBuffer) secondary_gpu_fb = NULL; -- MetaKmsCrtc *kms_crtc; -- MetaKmsDevice *kms_device; -+ size_t rectangles_size; - - COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, - "Onscreen (swap-buffers)"); -@@ -1157,12 +1158,47 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - if (egl_context_changed) - _cogl_winsys_egl_ensure_current (cogl_display); - -- kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); -- kms_device = meta_kms_crtc_get_device (kms_crtc); -+ rectangles_size = n_rectangles * 4 * sizeof (int); -+ onscreen_native->next_post.rectangles = -+ g_realloc (onscreen_native->next_post.rectangles, rectangles_size); -+ memcpy (onscreen_native->next_post.rectangles, rectangles, rectangles_size); -+ onscreen_native->next_post.n_rectangles = n_rectangles; -+ -+ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); -+ onscreen_native->next_post.frame = clutter_frame_ref (frame); -+ -+ post_latest_swap (onscreen); -+} -+ -+static void -+post_latest_swap (CoglOnscreen *onscreen) -+{ -+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); -+ CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); -+ CoglRenderer *cogl_renderer = cogl_context->display->renderer; -+ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; -+ MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; -+ MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; -+ MetaRenderer *renderer = META_RENDERER (renderer_native); -+ MetaBackend *backend = meta_renderer_get_backend (renderer); -+ MetaMonitorManager *monitor_manager = -+ meta_backend_get_monitor_manager (backend); -+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -+ MetaPowerSave power_save_mode; -+ MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); -+ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); -+ MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); -+ MetaKmsUpdate *kms_update; -+ g_autoptr (MetaKmsFeedback) kms_feedback = NULL; -+ g_autoptr (ClutterFrame) frame = -+ g_steal_pointer (&onscreen_native->next_post.frame); -+ MetaFrameNative *frame_native; - - power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); - if (power_save_mode == META_POWER_SAVE_ON) - { -+ frame_native = meta_frame_native_from_frame (frame); -+ - kms_update = meta_frame_native_ensure_kms_update (frame_native, - kms_device); - meta_kms_update_add_result_listener (kms_update, -@@ -1177,8 +1213,8 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - onscreen_native->crtc, - kms_update, - META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, -- rectangles, -- n_rectangles); -+ onscreen_native->next_post.rectangles, -+ onscreen_native->next_post.n_rectangles); - } - else - { -@@ -2449,6 +2485,10 @@ meta_onscreen_native_dispose (GObject *object) - - g_clear_object (&onscreen_native->output); - g_clear_object (&onscreen_native->crtc); -+ -+ g_clear_pointer (&onscreen_native->next_post.rectangles, g_free); -+ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); -+ onscreen_native->next_post.n_rectangles = 0; - } - - static void --- -GitLab - - -From c8afe60de6a7d5244b6a560c555b0ed5a551b30d Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 28 Oct 2021 17:24:11 +0800 -Subject: [PATCH 22/42] onscreen/native: Add a missing frame notification - -In the unlikely event that swap_buffers_with_damage drops the previous swap -it was forgetting to notify about the discarded frame. That could lead to -frame clock freezes. ---- - src/backends/native/meta-onscreen-native.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 7346c04d95..ad1d4acab6 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -1133,6 +1133,15 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - { - case META_RENDERER_NATIVE_MODE_GBM: - g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); -+ if (onscreen_native->gbm.next_fb != NULL) -+ { -+ CoglFrameInfo *frame_info; -+ -+ frame_info = cogl_onscreen_peek_head_frame_info (onscreen); -+ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; -+ meta_onscreen_native_notify_frame_complete (onscreen); -+ } -+ - if (onscreen_native->secondary_gpu_state) - g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb); - else --- -GitLab - - -From a9e74e78ca6f57a1058aa13d1906fa0d552b87ab Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 17 Sep 2021 17:59:28 +0800 -Subject: [PATCH 23/42] onscreen/native: Defer posting if there's already a - post in progress - -And when the number of pending posts decreases we know it's safe to submit -a new one. Since KMS generally only supports one outstanding post right now, -"decreases" means equal to zero. ---- - src/backends/native/meta-onscreen-native.c | 186 +++++++++++++++++---- - 1 file changed, 158 insertions(+), 28 deletions(-) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index ad1d4acab6..8e26d54de2 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -94,6 +94,7 @@ struct _MetaOnscreenNative - struct { - struct gbm_surface *surface; - MetaDrmBuffer *next_fb; -+ MetaDrmBuffer *stalled_fb; - - /* Temporary workaround for the scanout-failed signal wanting the buffer - * to live longer than it does, and then it doesn't use it anyway... -@@ -121,6 +122,10 @@ struct _MetaOnscreenNative - gulong color_space_changed_handler_id; - gulong hdr_metadata_changed_handler_id; - -+ gboolean needs_flush; -+ -+ unsigned int swaps_pending; -+ - struct { - int *rectangles; /* 4 x n_rectangles */ - int n_rectangles; -@@ -134,7 +139,11 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, - static GQuark blit_source_quark = 0; - - static void --post_latest_swap (CoglOnscreen *onscreen); -+try_post_latest_swap (CoglOnscreen *onscreen); -+ -+static void -+post_finish_frame (MetaOnscreenNative *onscreen_native, -+ MetaKmsUpdate *kms_update); - - static gboolean - init_secondary_gpu_state (MetaRendererNative *renderer_native, -@@ -212,6 +221,7 @@ notify_view_crtc_presented (MetaRendererView *view, - - meta_onscreen_native_notify_frame_complete (onscreen); - meta_swap_chain_swap_buffers (meta_kms_crtc_get_swap_chain (kms_crtc)); -+ try_post_latest_swap (onscreen); - } - - static void -@@ -261,15 +271,13 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, - CoglFramebuffer *framebuffer = - clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); - CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - CoglFrameInfo *frame_info; - - frame_info = cogl_onscreen_peek_head_frame_info (onscreen); - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - -- g_warn_if_fail (!onscreen_native->gbm.next_fb); -- - meta_onscreen_native_notify_frame_complete (onscreen); -+ try_post_latest_swap (onscreen); - } - - static void -@@ -319,6 +327,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - - meta_onscreen_native_notify_frame_complete (onscreen); -+ try_post_latest_swap (onscreen); - } - - static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { -@@ -379,19 +388,40 @@ custom_egl_stream_page_flip (gpointer custom_page_flip_data, - } - #endif /* HAVE_EGL_DEVICE */ - --void --meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) -+static void -+drop_stalled_swap (CoglOnscreen *onscreen) - { - CoglFrameInfo *frame_info; - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - -- g_clear_object (&onscreen_native->gbm.next_fb); -+ /* Remember we can't compare stalled_fb because it's not used by -+ * META_RENDERER_NATIVE_MODE_EGL_DEVICE. So we judge stalled to be whenever -+ * swaps_pending > 1. -+ */ -+ if (onscreen_native->swaps_pending <= 1) -+ return; -+ -+ onscreen_native->swaps_pending--; -+ -+ g_clear_object (&onscreen_native->gbm.stalled_fb); - - frame_info = cogl_onscreen_peek_tail_frame_info (onscreen); - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - meta_onscreen_native_notify_frame_complete (onscreen); - } - -+void -+meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) -+{ -+ drop_stalled_swap (onscreen); -+ -+ /* If the monitor just woke up and the shell is fully idle (has nothing -+ * more to swap) then we just woke to an indefinitely black screen. Let's -+ * fix that using the last swap (which is never classified as "stalled"). -+ */ -+ try_post_latest_swap (onscreen); -+} -+ - static void - meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, - MetaRendererView *view, -@@ -1024,9 +1054,15 @@ swap_buffer_result_feedback (const MetaKmsFeedback *kms_feedback, - g_warning ("Page flip failed: %s", error->message); - - frame_info = cogl_onscreen_peek_head_frame_info (onscreen); -- frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - -- meta_onscreen_native_notify_frame_complete (onscreen); -+ /* After resuming from suspend, drop_stalled_swap might have done this -+ * already and emptied the frame_info queue. -+ */ -+ if (frame_info) -+ { -+ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; -+ meta_onscreen_native_notify_frame_complete (onscreen); -+ } - } - - static const MetaKmsResultListenerVtable swap_buffer_result_listener_vtable = { -@@ -1132,14 +1168,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - switch (renderer_gpu_data->mode) - { - case META_RENDERER_NATIVE_MODE_GBM: -- g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); - if (onscreen_native->gbm.next_fb != NULL) - { -- CoglFrameInfo *frame_info; -- -- frame_info = cogl_onscreen_peek_head_frame_info (onscreen); -- frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; -- meta_onscreen_native_notify_frame_complete (onscreen); -+ g_warn_if_fail (onscreen_native->gbm.stalled_fb == NULL); -+ drop_stalled_swap (onscreen); -+ g_assert (onscreen_native->gbm.stalled_fb == NULL); -+ onscreen_native->gbm.stalled_fb = -+ g_steal_pointer (&onscreen_native->gbm.next_fb); - } - - if (onscreen_native->secondary_gpu_state) -@@ -1176,11 +1211,12 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); - onscreen_native->next_post.frame = clutter_frame_ref (frame); - -- post_latest_swap (onscreen); -+ onscreen_native->swaps_pending++; -+ try_post_latest_swap (onscreen); - } - - static void --post_latest_swap (CoglOnscreen *onscreen) -+try_post_latest_swap (CoglOnscreen *onscreen) - { - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); -@@ -1199,15 +1235,41 @@ post_latest_swap (CoglOnscreen *onscreen) - MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); - MetaKmsUpdate *kms_update; - g_autoptr (MetaKmsFeedback) kms_feedback = NULL; -- g_autoptr (ClutterFrame) frame = -- g_steal_pointer (&onscreen_native->next_post.frame); -+ g_autoptr (ClutterFrame) frame = NULL; - MetaFrameNative *frame_native; - -+ if (onscreen_native->next_post.frame == NULL) -+ return; -+ - power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); - if (power_save_mode == META_POWER_SAVE_ON) - { -+ unsigned int frames_pending = -+ cogl_onscreen_count_pending_frames (onscreen); -+ unsigned int posts_pending; -+ -+ g_assert (frames_pending >= onscreen_native->swaps_pending); -+ posts_pending = frames_pending - onscreen_native->swaps_pending; -+ if (posts_pending > 0) -+ return; /* wait for the next frame notification and then try again */ -+ -+ frame = g_steal_pointer (&onscreen_native->next_post.frame); - frame_native = meta_frame_native_from_frame (frame); - -+ if (onscreen_native->swaps_pending == 0) -+ { -+ if (frame_native) -+ { -+ kms_update = meta_frame_native_steal_kms_update (frame_native); -+ if (kms_update) -+ post_finish_frame (onscreen_native, kms_update); -+ } -+ return; -+ } -+ -+ drop_stalled_swap (onscreen); -+ onscreen_native->swaps_pending--; -+ - kms_update = meta_frame_native_ensure_kms_update (frame_native, - kms_device); - meta_kms_update_add_result_listener (kms_update, -@@ -1408,6 +1470,18 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, - return FALSE; - } - -+ /* Our direct scanout frame counts as 1, so more than that means we would -+ * be jumping the queue (and post would fail). -+ */ -+ if (cogl_onscreen_count_pending_frames (onscreen) > 1) -+ { -+ g_set_error_literal (error, -+ COGL_SCANOUT_ERROR, -+ COGL_SCANOUT_ERROR_INHIBITED, -+ "Direct scanout is inhibited during triple buffering"); -+ return FALSE; -+ } -+ - renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, - render_gpu); - -@@ -1594,22 +1668,79 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, - MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); - MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); - MetaKmsUpdate *kms_update; -+ unsigned int frames_pending = cogl_onscreen_count_pending_frames (onscreen); -+ unsigned int swaps_pending = onscreen_native->swaps_pending; -+ unsigned int posts_pending = frames_pending - swaps_pending; - -- kms_update = meta_frame_native_steal_kms_update (frame_native); -- if (!kms_update) -+ onscreen_native->needs_flush |= meta_kms_device_handle_flush (kms_device, -+ kms_crtc); -+ -+ if (!meta_frame_native_has_kms_update (frame_native)) - { -- if (meta_kms_device_handle_flush (kms_device, kms_crtc)) -- { -- kms_update = meta_kms_update_new (kms_device); -- meta_kms_update_set_flushing (kms_update, kms_crtc); -- } -- else -+ if (!onscreen_native->needs_flush || posts_pending) - { - clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); - return; - } - } - -+ if (posts_pending && !swaps_pending) -+ { -+ g_return_if_fail (meta_frame_native_has_kms_update (frame_native)); -+ g_warn_if_fail (onscreen_native->next_post.frame == NULL); -+ -+ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); -+ onscreen_native->next_post.frame = clutter_frame_ref (frame); -+ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); -+ return; -+ } -+ -+ kms_update = meta_frame_native_steal_kms_update (frame_native); -+ -+ if (posts_pending && swaps_pending) -+ { -+ MetaFrameNative *older_frame_native; -+ MetaKmsUpdate *older_kms_update; -+ -+ g_return_if_fail (kms_update); -+ g_return_if_fail (onscreen_native->next_post.frame != NULL); -+ -+ older_frame_native = -+ meta_frame_native_from_frame (onscreen_native->next_post.frame); -+ older_kms_update = -+ meta_frame_native_ensure_kms_update (older_frame_native, kms_device); -+ meta_kms_update_merge_from (older_kms_update, kms_update); -+ meta_kms_update_free (kms_update); -+ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); -+ return; -+ } -+ -+ if (!kms_update) -+ { -+ kms_update = meta_kms_update_new (kms_device); -+ g_warn_if_fail (onscreen_native->needs_flush); -+ } -+ -+ if (onscreen_native->needs_flush) -+ { -+ meta_kms_update_set_flushing (kms_update, kms_crtc); -+ onscreen_native->needs_flush = FALSE; -+ } -+ -+ post_finish_frame (onscreen_native, kms_update); -+ -+ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); -+} -+ -+static void -+post_finish_frame (MetaOnscreenNative *onscreen_native, -+ MetaKmsUpdate *kms_update) -+{ -+ MetaCrtc *crtc = onscreen_native->crtc; -+ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); -+ MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); -+ g_autoptr (MetaKmsFeedback) kms_feedback = NULL; -+ - meta_kms_update_add_result_listener (kms_update, - &finish_frame_result_listener_vtable, - NULL, -@@ -1633,7 +1764,6 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, - meta_kms_update_set_flushing (kms_update, kms_crtc); - meta_kms_device_post_update (kms_device, kms_update, - META_KMS_UPDATE_FLAG_NONE); -- clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - } - - static gboolean --- -GitLab - - -From 1f8d2574b19be280b0fff714f643f6c597e8f1ee Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 9 Dec 2022 14:22:31 +0800 -Subject: [PATCH 24/42] onscreen/native: Increase secondary GPU dumb_fbs from 2 - to 3 - -So that they don't get overwritten prematurely during triple buffering -causing tearing. - -https://launchpad.net/bugs/1999216 ---- - src/backends/native/meta-onscreen-native.c | 15 ++++++++++----- - 1 file changed, 10 insertions(+), 5 deletions(-) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 8e26d54de2..820cb7cbe9 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -72,7 +72,7 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState - - struct { - MetaDrmBufferDumb *current_dumb_fb; -- MetaDrmBufferDumb *dumb_fbs[2]; -+ MetaDrmBufferDumb *dumb_fbs[3]; - } cpu; - - gboolean noted_primary_gpu_copy_ok; -@@ -718,12 +718,17 @@ static MetaDrmBufferDumb * - secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) - { - MetaDrmBufferDumb *current_dumb_fb; -+ const int n_dumb_fbs = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); -+ int i; - - current_dumb_fb = secondary_gpu_state->cpu.current_dumb_fb; -- if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[0]) -- return secondary_gpu_state->cpu.dumb_fbs[1]; -- else -- return secondary_gpu_state->cpu.dumb_fbs[0]; -+ for (i = 0; i < n_dumb_fbs; i++) -+ { -+ if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i]) -+ return secondary_gpu_state->cpu.dumb_fbs[(i + 1) % n_dumb_fbs]; -+ } -+ -+ return secondary_gpu_state->cpu.dumb_fbs[0]; - } - - static MetaDrmBuffer * --- -GitLab - - -From f2c3de4d7b91b5b7ba3ecffaa8c364271f4c66c3 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 4 Nov 2021 16:09:26 +0800 -Subject: [PATCH 25/42] onscreen/native: Add function - meta_onscreen_native_discard_pending_swaps - ---- - src/backends/native/meta-onscreen-native.c | 11 +++++++++++ - src/backends/native/meta-onscreen-native.h | 2 ++ - 2 files changed, 13 insertions(+) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 820cb7cbe9..80ea1da611 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -1771,6 +1771,17 @@ post_finish_frame (MetaOnscreenNative *onscreen_native, - META_KMS_UPDATE_FLAG_NONE); - } - -+void -+meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen) -+{ -+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -+ -+ onscreen_native->swaps_pending = 0; -+ -+ g_clear_object (&onscreen_native->gbm.stalled_fb); -+ g_clear_object (&onscreen_native->gbm.next_fb); -+} -+ - static gboolean - should_surface_be_sharable (CoglOnscreen *onscreen) - { -diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h -index 91eb7b533e..11bb5ba561 100644 ---- a/src/backends/native/meta-onscreen-native.h -+++ b/src/backends/native/meta-onscreen-native.h -@@ -45,6 +45,8 @@ void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, - - void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen); - -+void meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen); -+ - gboolean meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen, - MetaDrmBuffer *fb); - --- -GitLab - - -From 249645d383acbb07e777460fec6f06f04a384dd8 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Wed, 11 May 2022 16:00:32 +0800 -Subject: [PATCH 26/42] onscreen/native: Skip try_post_latest_swap if shutting - down - -Otherwise we could get: - - meta_kms_prepare_shutdown -> - flush_callbacks -> - ... -> - try_post_latest_swap -> - post and queue more callbacks - -So later in shutdown those callbacks would trigger an assertion failure -in meta_kms_impl_device_atomic_finalize: - - g_hash_table_size (impl_device_atomic->page_flip_datas) == 0 - -Also remember to discard_pending_swaps to break the circular reference -between MetaOnscreenNative and its next_fb caused by the association -"gbm_surface owner". Or else that would trigger another assertion failure: - - meta_device_pool_finalize: runtime check failed: (!pool->files) ---- - src/backends/native/meta-onscreen-native.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 80ea1da611..914ae6f4ab 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -1231,6 +1231,8 @@ try_post_latest_swap (CoglOnscreen *onscreen) - MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; - MetaRenderer *renderer = META_RENDERER (renderer_native); - MetaBackend *backend = meta_renderer_get_backend (renderer); -+ MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); -+ MetaKms *kms = meta_backend_native_get_kms (backend_native); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -@@ -1246,6 +1248,12 @@ try_post_latest_swap (CoglOnscreen *onscreen) - if (onscreen_native->next_post.frame == NULL) - return; - -+ if (meta_kms_is_shutting_down (kms)) -+ { -+ meta_onscreen_native_discard_pending_swaps (onscreen); -+ return; -+ } -+ - power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); - if (power_save_mode == META_POWER_SAVE_ON) - { --- -GitLab - - -From 8d1e26ecf4270576436d55c1dab4b996dfefe5aa Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 4 Nov 2021 16:09:52 +0800 -Subject: [PATCH 27/42] renderer/native: Discard pending swaps when rebuilding - views - -It's analogous to discard_pending_page_flips but represents swaps that -might become flips after the next frame notification callbacks, thanks -to triple buffering. Since the views are being rebuilt and their onscreens -are about to be destroyed, turning those swaps into more flips/posts would -just lead to unexpected behaviour (like trying to flip on a half-destroyed -inactive CRTC). ---- - src/backends/native/meta-renderer-native.c | 21 +++++++++++++++++++++ - 1 file changed, 21 insertions(+) - -diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c -index f50c930eea..6acc099181 100644 ---- a/src/backends/native/meta-renderer-native.c -+++ b/src/backends/native/meta-renderer-native.c -@@ -1523,6 +1523,26 @@ detach_onscreens (MetaRenderer *renderer) - } - } - -+static void -+discard_pending_swaps (MetaRenderer *renderer) -+{ -+ GList *views = meta_renderer_get_views (renderer);; -+ GList *l; -+ -+ for (l = views; l; l = l->next) -+ { -+ ClutterStageView *stage_view = l->data; -+ CoglFramebuffer *fb = clutter_stage_view_get_onscreen (stage_view); -+ CoglOnscreen *onscreen; -+ -+ if (!COGL_IS_ONSCREEN (fb)) -+ continue; -+ -+ onscreen = COGL_ONSCREEN (fb); -+ meta_onscreen_native_discard_pending_swaps (onscreen); -+ } -+} -+ - static void - meta_renderer_native_rebuild_views (MetaRenderer *renderer) - { -@@ -1533,6 +1553,7 @@ meta_renderer_native_rebuild_views (MetaRenderer *renderer) - MetaRendererClass *parent_renderer_class = - META_RENDERER_CLASS (meta_renderer_native_parent_class); - -+ discard_pending_swaps (renderer); - meta_kms_discard_pending_page_flips (kms); - g_hash_table_remove_all (renderer_native->mode_set_updates); - --- -GitLab - - -From 85647296275c86e7af25efcafa98f43d4c66f5e4 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 10 Aug 2021 17:46:49 +0800 -Subject: [PATCH 28/42] clutter/frame-clock: Lower the threshold for disabling - error diffusion - -Error diffusion was introduced in 0555a5bbc15 for Nvidia where last -presentation time is always unknown (zero). Dispatch times would drift -apart always being a fraction of a frame late, and accumulated to cause -periodic frame skips. So error diffusion corrected that precisely and -avoided the skips. - -That works great with double buffering but less great with triple -buffering. It's certainly still needed with triple buffering but -correcting for a lateness of many milliseconds isn't a good idea. That's -because a dispatch being that late is not due to main loop jitter but due -to Nvidia's swap buffers blocking when the queue is full. So scheduling -the next frame even earlier using last_dispatch_lateness_us would just -perpetuate the problem of swap buffers blocking for too long. - -So now we lower the threshold of when error diffusion gets disabled. It's -still high enough to fix the original smoothness problem it was for, but -now low enough to detect Nvidia's occasionally blocking swaps and backs -off in that case. - -Since the average duration of a blocking swap is half a frame interval -and we want to distinguish between that and sub-millisecond jitter, the -logical threshold is halfway again: refresh_interval_us/4. ---- - clutter/clutter/clutter-frame-clock.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index 30cc6fb12a..783baa8af6 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -734,7 +734,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; --- -GitLab - - -From 7bd6cf6964fc056f47b8a95672d8fc1f0c75dea2 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 24 Jul 2020 14:13:11 +0800 -Subject: [PATCH 29/42] clutter/frame-clock: Merge states DISPATCHING and - PENDING_PRESENTED - -Chronologically they already overlap in time as presentation may -complete in the middle of the dispatch function, otherwise they are -contiguous in time. And most switch statements treated the two states -the same already so they're easy to merge into a single `DISPATCHED` -state. - -Having fewer states now will make life easier when we add more states -later. ---- - clutter/clutter/clutter-frame-clock.c | 28 ++++++++++----------------- - 1 file changed, 10 insertions(+), 18 deletions(-) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index 783baa8af6..dcde9f260d 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -55,8 +55,7 @@ typedef enum _ClutterFrameClockState - CLUTTER_FRAME_CLOCK_STATE_INIT, - CLUTTER_FRAME_CLOCK_STATE_IDLE, - CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, -- CLUTTER_FRAME_CLOCK_STATE_DISPATCHING, -- CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED, -+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED, - } ClutterFrameClockState; - - struct _ClutterFrameClock -@@ -378,8 +377,7 @@ 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: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; - maybe_reschedule_update (frame_clock); - break; -@@ -398,8 +396,7 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - g_warn_if_reached (); - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: -- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; - maybe_reschedule_update (frame_clock); - break; -@@ -613,8 +610,7 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) - frame_clock->pending_reschedule = TRUE; - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: -- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: - break; - } - -@@ -652,8 +648,7 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - next_update_time_us = g_get_monotonic_time (); - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: -- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: - frame_clock->pending_reschedule = TRUE; - frame_clock->pending_reschedule_now = TRUE; - return; -@@ -693,8 +688,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - break; - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - return; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: -- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: - frame_clock->pending_reschedule = TRUE; - return; - } -@@ -758,7 +752,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, - frame_clock->last_dispatch_time_us = time_us; - g_source_set_ready_time (frame_clock->source, -1); - -- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHING; -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED; - - frame_count = frame_clock->frame_count++; - -@@ -790,19 +784,19 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, - switch (frame_clock->state) - { - case CLUTTER_FRAME_CLOCK_STATE_INIT: -- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: - g_warn_if_reached (); - break; - case CLUTTER_FRAME_CLOCK_STATE_IDLE: -+ /* Presentation completed synchronously in the above listener */ - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: - switch (result) - { - case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: -- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED; - break; - case CLUTTER_FRAME_RESULT_IDLE: -+ /* The frame was aborted; nothing to paint/present */ - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; - maybe_reschedule_update (frame_clock); - break; -@@ -935,8 +929,6 @@ clutter_frame_clock_dispose (GObject *object) - { - ClutterFrameClock *frame_clock = CLUTTER_FRAME_CLOCK (object); - -- g_warn_if_fail (frame_clock->state != CLUTTER_FRAME_CLOCK_STATE_DISPATCHING); -- - if (frame_clock->source) - { - g_signal_emit (frame_clock, signals[DESTROY], 0); --- -GitLab - - -From 393294b46e4f5b39e2e210b7df5088d2e4db6487 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Mon, 13 Dec 2021 17:03:44 +0800 -Subject: [PATCH 30/42] clutter/frame-clock: Don't clamp measurements - -Because values over refresh_interval_us are real and valid. They -tell us when we're not keeping up. - -This reverts commit 9c021b0bc2e4f0d2698f36db41224645ed31b6dc. ---- - clutter/clutter/clutter-frame-clock.c | 9 +++------ - 1 file changed, 3 insertions(+), 6 deletions(-) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index dcde9f260d..de217313c0 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -348,10 +348,9 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - swap_to_flip_us); - - frame_clock->shortterm_max_update_duration_us = -- CLAMP (frame_clock->last_dispatch_lateness_us + dispatch_to_swap_us + -- MAX (swap_to_rendering_done_us, swap_to_flip_us), -- frame_clock->shortterm_max_update_duration_us, -- frame_clock->refresh_interval_us); -+ MAX (frame_clock->shortterm_max_update_duration_us, -+ frame_clock->last_dispatch_lateness_us + dispatch_to_swap_us + -+ MAX (swap_to_rendering_done_us, swap_to_flip_us)); - - maybe_update_longterm_max_duration_us (frame_clock, frame_info); - -@@ -434,8 +433,6 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) - frame_clock->vblank_duration_us + - clutter_max_render_time_constant_us; - -- max_render_time_us = CLAMP (max_render_time_us, 0, refresh_interval_us); -- - return max_render_time_us; - } - --- -GitLab - - -From cc26cfac043564590834c532c96fceb46d4421d6 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 10 Sep 2020 16:34:53 +0800 -Subject: [PATCH 31/42] clutter/frame-clock: Add triple buffering support - ---- - clutter/clutter/clutter-frame-clock.c | 159 +++++++++++++++++++++----- - 1 file changed, 133 insertions(+), 26 deletions(-) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index de217313c0..3c70808184 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -55,7 +55,9 @@ typedef enum _ClutterFrameClockState - CLUTTER_FRAME_CLOCK_STATE_INIT, - CLUTTER_FRAME_CLOCK_STATE_IDLE, - CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, -- CLUTTER_FRAME_CLOCK_STATE_DISPATCHED, -+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE, -+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED, -+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO, - } ClutterFrameClockState; - - struct _ClutterFrameClock -@@ -72,6 +74,7 @@ struct _ClutterFrameClock - - ClutterFrameClockState state; - int64_t last_dispatch_time_us; -+ int64_t prev_last_dispatch_time_us; - int64_t last_dispatch_lateness_us; - int64_t last_presentation_time_us; - int64_t next_update_time_us; -@@ -86,6 +89,7 @@ struct _ClutterFrameClock - int64_t vblank_duration_us; - /* Last KMS buffer submission time. */ - int64_t last_flip_time_us; -+ int64_t prev_last_flip_time_us; - - /* Last time we promoted short-term maximum to long-term one */ - int64_t longterm_promotion_us; -@@ -330,14 +334,33 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - if (frame_info->cpu_time_before_buffer_swap_us != 0) - { - int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us; -+ int64_t dispatch_time_us = 0, flip_time_us = 0; -+ -+ switch (frame_clock->state) -+ { -+ case CLUTTER_FRAME_CLOCK_STATE_INIT: -+ case CLUTTER_FRAME_CLOCK_STATE_IDLE: -+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: -+ g_warn_if_reached (); -+ G_GNUC_FALLTHROUGH; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ dispatch_time_us = frame_clock->last_dispatch_time_us; -+ flip_time_us = frame_clock->last_flip_time_us; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: -+ dispatch_time_us = frame_clock->prev_last_dispatch_time_us; -+ flip_time_us = frame_clock->prev_last_flip_time_us; -+ break; -+ } - - dispatch_to_swap_us = - frame_info->cpu_time_before_buffer_swap_us - -- frame_clock->last_dispatch_time_us; -+ dispatch_time_us; - swap_to_rendering_done_us = - frame_info->gpu_rendering_duration_ns / 1000; - swap_to_flip_us = -- frame_clock->last_flip_time_us - -+ flip_time_us - - frame_info->cpu_time_before_buffer_swap_us; - - CLUTTER_NOTE (FRAME_TIMINGS, -@@ -376,10 +399,18 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - g_warn_if_reached (); - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; - maybe_reschedule_update (frame_clock); - break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; -+ maybe_reschedule_update (frame_clock); -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; -+ maybe_reschedule_update (frame_clock); -+ break; - } - } - -@@ -395,10 +426,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - g_warn_if_reached (); - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; - maybe_reschedule_update (frame_clock); - break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; -+ maybe_reschedule_update (frame_clock); -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; -+ maybe_reschedule_update (frame_clock); -+ break; - } - } - -@@ -413,7 +452,14 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) - if (!frame_clock->ever_got_measurements || - G_UNLIKELY (clutter_paint_debug_flags & - CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME)) -- return refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; -+ { -+ int64_t ret = refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; -+ -+ if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE) -+ ret += refresh_interval_us; -+ -+ return ret; -+ } - - /* Max render time shows how early the frame clock needs to be dispatched - * to make it to the predicted next presentation time. It is an estimate of -@@ -447,7 +493,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, - int64_t refresh_interval_us; - int64_t min_render_time_allowed_us; - int64_t max_render_time_allowed_us; -- int64_t next_presentation_time_us; -+ int64_t next_presentation_time_us = 0; - int64_t next_update_time_us; - - now_us = g_get_monotonic_time (); -@@ -492,7 +538,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, - * - */ - last_presentation_time_us = frame_clock->last_presentation_time_us; -- next_presentation_time_us = last_presentation_time_us + refresh_interval_us; -+ switch (frame_clock->state) -+ { -+ case CLUTTER_FRAME_CLOCK_STATE_INIT: -+ case CLUTTER_FRAME_CLOCK_STATE_IDLE: -+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: -+ next_presentation_time_us = last_presentation_time_us + -+ refresh_interval_us; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ next_presentation_time_us = last_presentation_time_us + -+ 2 * refresh_interval_us; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: -+ next_presentation_time_us = last_presentation_time_us + -+ 3 * refresh_interval_us; -+ break; -+ } - - /* - * However, the last presentation could have happened more than a frame ago. -@@ -607,7 +670,12 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) - frame_clock->pending_reschedule = TRUE; - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ frame_clock->pending_reschedule = TRUE; -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: - break; - } - -@@ -644,8 +712,15 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) - case CLUTTER_FRAME_CLOCK_STATE_IDLE: - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - next_update_time_us = g_get_monotonic_time (); -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ next_update_time_us = g_get_monotonic_time (); -+ frame_clock->state = -+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: - frame_clock->pending_reschedule = TRUE; - frame_clock->pending_reschedule_now = TRUE; - return; -@@ -655,7 +730,6 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) - - frame_clock->next_update_time_us = next_update_time_us; - g_source_set_ready_time (frame_clock->source, next_update_time_us); -- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; - frame_clock->is_next_presentation_time_valid = FALSE; - } - -@@ -674,6 +748,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - { - case CLUTTER_FRAME_CLOCK_STATE_INIT: - next_update_time_us = g_get_monotonic_time (); -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; - break; - case CLUTTER_FRAME_CLOCK_STATE_IDLE: - calculate_next_update_time_us (frame_clock, -@@ -682,10 +757,21 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - &frame_clock->min_render_time_allowed_us); - frame_clock->is_next_presentation_time_valid = - (frame_clock->next_presentation_time_us != 0); -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; - break; - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: - return; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ calculate_next_update_time_us (frame_clock, -+ &next_update_time_us, -+ &frame_clock->next_presentation_time_us, -+ &frame_clock->min_render_time_allowed_us); -+ frame_clock->is_next_presentation_time_valid = -+ (frame_clock->next_presentation_time_us != 0); -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: - frame_clock->pending_reschedule = TRUE; - return; - } -@@ -694,7 +780,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - - frame_clock->next_update_time_us = next_update_time_us; - g_source_set_ready_time (frame_clock->source, next_update_time_us); -- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; - } - - static void -@@ -746,10 +831,25 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, - } - #endif - -+ frame_clock->prev_last_dispatch_time_us = frame_clock->last_dispatch_time_us; - frame_clock->last_dispatch_time_us = time_us; - g_source_set_ready_time (frame_clock->source, -1); - -- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED; -+ switch (frame_clock->state) -+ { -+ case CLUTTER_FRAME_CLOCK_STATE_INIT: -+ case CLUTTER_FRAME_CLOCK_STATE_IDLE: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: -+ g_warn_if_reached (); -+ return; -+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO; -+ break; -+ } - - frame_count = frame_clock->frame_count++; - -@@ -778,25 +878,31 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, - result = iface->frame (frame_clock, frame, frame_clock->listener.user_data); - COGL_TRACE_END (ClutterFrameClockFrame); - -- switch (frame_clock->state) -+ switch (result) - { -- case CLUTTER_FRAME_CLOCK_STATE_INIT: -- g_warn_if_reached (); -- break; -- case CLUTTER_FRAME_CLOCK_STATE_IDLE: -- /* Presentation completed synchronously in the above listener */ -- case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: -+ case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: - break; -- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED: -- switch (result) -+ case CLUTTER_FRAME_RESULT_IDLE: -+ /* The frame was aborted; nothing to paint/present */ -+ switch (frame_clock->state) - { -- case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: -+ case CLUTTER_FRAME_CLOCK_STATE_INIT: -+ case CLUTTER_FRAME_CLOCK_STATE_IDLE: -+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: -+ g_warn_if_reached (); - break; -- case CLUTTER_FRAME_RESULT_IDLE: -- /* The frame was aborted; nothing to paint/present */ -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; - maybe_reschedule_update (frame_clock); - break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; -+ maybe_reschedule_update (frame_clock); -+ break; -+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: -+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; -+ maybe_reschedule_update (frame_clock); -+ break; - } - break; - } -@@ -832,6 +938,7 @@ void - clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, - int64_t flip_time_us) - { -+ frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us; - frame_clock->last_flip_time_us = flip_time_us; - } - --- -GitLab - - -From b07d2c10c779cc294eb44bbbb796512a35af33b1 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 10 Dec 2021 16:28:04 +0800 -Subject: [PATCH 32/42] clutter/frame-clock: Log N-buffers in - CLUTTTER_DEBUG=frame-timings - ---- - clutter/clutter/clutter-frame-clock.c | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index 3c70808184..93d2d3a114 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -252,6 +252,12 @@ void - clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - ClutterFrameInfo *frame_info) - { -+#ifdef CLUTTER_ENABLE_DEBUG -+ const char *debug_state = -+ frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ? -+ "Triple buffering" : "Double buffering"; -+#endif -+ - COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented, - "Frame Clock (presented)"); - -@@ -364,7 +370,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - frame_info->cpu_time_before_buffer_swap_us; - - CLUTTER_NOTE (FRAME_TIMINGS, -- "update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", -+ "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", -+ debug_state, - frame_clock->last_dispatch_lateness_us, - dispatch_to_swap_us, - swap_to_rendering_done_us, -@@ -382,7 +389,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - } - else - { -- CLUTTER_NOTE (FRAME_TIMINGS, "update2dispatch %ld µs", -+ CLUTTER_NOTE (FRAME_TIMINGS, "%s: update2dispatch %ld µs", -+ debug_state, - frame_clock->last_dispatch_lateness_us); - } - --- -GitLab - - -From 038305e8cb2dedfccb723395b621ae40e945661f Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 7 Sep 2021 19:08:15 +0800 -Subject: [PATCH 33/42] clutter/frame: Add ClutterFrameHint to ClutterFrame - -This will allow the backend to provide performance hints to the frame -clock in future. ---- - clutter/clutter/clutter-frame-clock.h | 6 ++++++ - clutter/clutter/clutter-frame-private.h | 1 + - clutter/clutter/clutter-frame.c | 13 +++++++++++++ - clutter/clutter/clutter-frame.h | 7 +++++++ - 4 files changed, 27 insertions(+) - -diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h -index 93ebc94386..b6650a5a05 100644 ---- a/clutter/clutter/clutter-frame-clock.h -+++ b/clutter/clutter/clutter-frame-clock.h -@@ -33,6 +33,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, -diff --git a/clutter/clutter/clutter-frame-private.h b/clutter/clutter/clutter-frame-private.h -index 0a0226b0a5..55c76df72e 100644 ---- a/clutter/clutter/clutter-frame-private.h -+++ b/clutter/clutter/clutter-frame-private.h -@@ -34,6 +34,7 @@ struct _ClutterFrame - - gboolean has_result; - ClutterFrameResult result; -+ ClutterFrameHint hints; - }; - - CLUTTER_EXPORT -diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c -index 85baef274e..413ce9c2b6 100644 ---- a/clutter/clutter/clutter-frame.c -+++ b/clutter/clutter/clutter-frame.c -@@ -113,3 +113,16 @@ clutter_frame_set_result (ClutterFrame *frame, - frame->result = result; - frame->has_result = TRUE; - } -+ -+void -+clutter_frame_set_hint (ClutterFrame *frame, -+ ClutterFrameHint hint) -+{ -+ frame->hints |= hint; -+} -+ -+ClutterFrameHint -+clutter_frame_get_hints (ClutterFrame *frame) -+{ -+ return frame->hints; -+} -diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h -index 1d5660d685..0e7f618a4e 100644 ---- a/clutter/clutter/clutter-frame.h -+++ b/clutter/clutter/clutter-frame.h -@@ -54,4 +54,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); -+ - G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFrame, clutter_frame_unref) --- -GitLab - - -From 0518dee8e72c2cf71509194cf973ef09e70d3ef3 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 7 Sep 2021 19:10:26 +0800 -Subject: [PATCH 34/42] backends: Flag that the frame attempted direct scanout - -We need this hint whether direct scanout succeeds or fails because it's -the mechanism by which we will tell the clock to enforce double buffering, -thus making direct scanout possible on future frames. Triple buffering -will be disabled until such time that direct scanout is not being attempted. ---- - src/backends/meta-stage-impl.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c -index c35cb36e32..2130e4042d 100644 ---- a/src/backends/meta-stage-impl.c -+++ b/src/backends/meta-stage-impl.c -@@ -775,6 +775,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, --- -GitLab - - -From 5a2892c74834706df0e1ad0948b3352569f6558a Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 7 Sep 2021 19:15:18 +0800 -Subject: [PATCH 35/42] clutter: Pass ClutterFrameHint(s) to the frame clock - ---- - clutter/clutter/clutter-frame-clock.c | 8 ++++++-- - clutter/clutter/clutter-frame-clock.h | 5 +++-- - clutter/clutter/clutter-stage-view.c | 11 +++++++++-- - 3 files changed, 18 insertions(+), 6 deletions(-) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index 93d2d3a114..80f256726c 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -91,6 +91,8 @@ struct _ClutterFrameClock - int64_t last_flip_time_us; - int64_t prev_last_flip_time_us; - -+ ClutterFrameHint last_flip_hints; -+ - /* Last time we promoted short-term maximum to long-term one */ - int64_t longterm_promotion_us; - /* Long-term maximum update duration */ -@@ -943,11 +945,13 @@ frame_clock_source_dispatch (GSource *source, - } - - void --clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, -- int64_t flip_time_us) -+clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, -+ int64_t flip_time_us, -+ ClutterFrameHint hints) - { - frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us; - frame_clock->last_flip_time_us = flip_time_us; -+ frame_clock->last_flip_hints = hints; - } - - GString * -diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h -index b6650a5a05..e1fd6b9861 100644 ---- a/clutter/clutter/clutter-frame-clock.h -+++ b/clutter/clutter/clutter-frame-clock.h -@@ -97,7 +97,8 @@ 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-stage-view.c b/clutter/clutter/clutter-stage-view.c -index 168746dd4a..f0e36619e8 100644 ---- a/clutter/clutter/clutter-stage-view.c -+++ b/clutter/clutter/clutter-stage-view.c -@@ -1264,14 +1264,21 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock, - - _clutter_stage_window_redraw_view (stage_window, view, frame); - -- clutter_frame_clock_record_flip_time (frame_clock, -- g_get_monotonic_time ()); -+ clutter_frame_clock_record_flip (frame_clock, -+ g_get_monotonic_time (), -+ clutter_frame_get_hints (frame)); - - clutter_stage_emit_after_paint (stage, view, frame); - - if (_clutter_context_get_show_fps ()) - end_frame_timing_measurement (view); - } -+ else -+ { -+ clutter_frame_clock_record_flip (frame_clock, -+ g_get_monotonic_time (), -+ clutter_frame_get_hints (frame)); -+ } - - _clutter_stage_window_finish_frame (stage_window, view, frame); - --- -GitLab - - -From b7f877643cb291788bf371265b3087e45f7ddb74 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 7 Sep 2021 19:15:55 +0800 -Subject: [PATCH 36/42] clutter/frame-clock: Throttle back to double buffering - for direct scanout - -There's no compositing during direct scanout so the "render" time is zero. -Thus there is no need to implement triple buffering for direct scanouts. -Stick to double buffering and enjoy the lower latency. ---- - clutter/clutter/clutter-frame-clock.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index 80f256726c..bd69623f7d 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -773,6 +773,13 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: - return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -+ 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, --- -GitLab - - -From 8d2b52858a40aa416ba6b1a1539e923282cd83cc Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 10 Mar 2022 16:44:14 +0800 -Subject: [PATCH 37/42] clutter/frame-clock: Add environment variable - MUTTER_DEBUG_TRIPLE_BUFFERING - -With possible values {never, auto, always} where auto is the default. - -This also allows some unification with automatic throttling for -direct scanout. ---- - clutter/clutter/clutter-frame-clock.c | 53 ++++++++++++++++++++++----- - 1 file changed, 43 insertions(+), 10 deletions(-) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index bd69623f7d..692056b380 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -35,6 +35,15 @@ enum - - static guint signals[N_SIGNALS]; - -+typedef enum -+{ -+ TRIPLE_BUFFERING_MODE_NEVER, -+ TRIPLE_BUFFERING_MODE_AUTO, -+ TRIPLE_BUFFERING_MODE_ALWAYS, -+} TripleBufferingMode; -+ -+static TripleBufferingMode triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; -+ - #define SYNC_DELAY_FALLBACK_FRACTION 0.875 - - typedef struct _ClutterFrameListener -@@ -747,6 +756,12 @@ void - clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - { - int64_t next_update_time_us = -1; -+ TripleBufferingMode current_mode = triple_buffering_mode; -+ -+ if (current_mode == TRIPLE_BUFFERING_MODE_AUTO && -+ (frame_clock->last_flip_hints & -+ CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED)) -+ current_mode = TRIPLE_BUFFERING_MODE_NEVER; - - if (frame_clock->inhibit_count > 0) - { -@@ -773,20 +788,29 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: - return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: -- if (frame_clock->last_flip_hints & CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED) -+ switch (current_mode) - { -- /* Force double buffering, disable triple buffering */ -+ case TRIPLE_BUFFERING_MODE_NEVER: - frame_clock->pending_reschedule = TRUE; - return; -+ case TRIPLE_BUFFERING_MODE_AUTO: -+ calculate_next_update_time_us (frame_clock, -+ &next_update_time_us, -+ &frame_clock->next_presentation_time_us, -+ &frame_clock->min_render_time_allowed_us); -+ frame_clock->is_next_presentation_time_valid = -+ (frame_clock->next_presentation_time_us != 0); -+ frame_clock->state = -+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; -+ break; -+ case TRIPLE_BUFFERING_MODE_ALWAYS: -+ next_update_time_us = g_get_monotonic_time (); -+ frame_clock->next_presentation_time_us = 0; -+ frame_clock->is_next_presentation_time_valid = FALSE; -+ frame_clock->state = -+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; -+ break; - } -- -- calculate_next_update_time_us (frame_clock, -- &next_update_time_us, -- &frame_clock->next_presentation_time_us, -- &frame_clock->min_render_time_allowed_us); -- frame_clock->is_next_presentation_time_valid = -- (frame_clock->next_presentation_time_us != 0); -- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; - break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: - frame_clock->pending_reschedule = TRUE; -@@ -1072,6 +1096,15 @@ static void - clutter_frame_clock_class_init (ClutterFrameClockClass *klass) - { - GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ const char *mode_str; -+ -+ mode_str = g_getenv ("MUTTER_DEBUG_TRIPLE_BUFFERING"); -+ if (!g_strcmp0 (mode_str, "never")) -+ triple_buffering_mode = TRIPLE_BUFFERING_MODE_NEVER; -+ else if (!g_strcmp0 (mode_str, "auto")) -+ triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; -+ else if (!g_strcmp0 (mode_str, "always")) -+ triple_buffering_mode = TRIPLE_BUFFERING_MODE_ALWAYS; - - object_class->dispose = clutter_frame_clock_dispose; - --- -GitLab - - -From 46a7c555a973701fa6a2db1d0e8ba49f6bdd1f3e Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 30 Jun 2022 18:56:06 +0800 -Subject: [PATCH 38/42] tests/native-kms-render: Fix failing client-scanout - test - -It was assuming an immediate transition from compositing (triple -buffering) to direct scanout (double buffering), whereas there is -a one frame delay in that transition as the buffer queue shrinks. -We don't lose any frames in the transition. ---- - src/tests/native-kms-render.c | 107 ++++++++++++++++++++++++++++------ - 1 file changed, 88 insertions(+), 19 deletions(-) - diff --git a/src/tests/native-kms-render.c b/src/tests/native-kms-render.c -index 31e74a9a42..259d315dce 100644 +index 90ea9b581..aafa682bd 100644 --- a/src/tests/native-kms-render.c +++ b/src/tests/native-kms-render.c @@ -39,6 +39,8 @@ @@ -3325,207 +2303,3 @@ index 31e74a9a42..259d315dce 100644 .loop = g_main_loop_new (NULL, FALSE), }; --- -GitLab - - -From dac1e526c714f3b6692db529ba9b0ad23cf7d94a Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Fri, 19 May 2023 14:31:07 +0800 -Subject: [PATCH 39/42] tests/clutter/event-delivery: Add a wait for - clutter_actor_destroy - -Because the destroy meant we were producing more frames/updates than -were waited on. This becomes a problem when triple buffering is enabled -because stage changes are then delayed by one update, causing spurious -test failures unless we wait on every change to keep it synchronous like -in double buffering. ---- - src/tests/clutter/conform/event-delivery.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/tests/clutter/conform/event-delivery.c b/src/tests/clutter/conform/event-delivery.c -index 383ad0bae0..3b259139a9 100644 ---- a/src/tests/clutter/conform/event-delivery.c -+++ b/src/tests/clutter/conform/event-delivery.c -@@ -329,6 +329,7 @@ event_delivery_implicit_grab_cancelled (void) - n_child_1_leave_events = n_child_2_leave_events = n_stage_leave_events = 0; - n_action_sequences_cancelled = 0; - clutter_actor_destroy (child_1); -+ wait_stage_updated (&was_updated); - g_assert_cmpint (n_child_1_enter_events, ==, 0); - g_assert_cmpint (n_child_1_leave_events, ==, 0); - g_assert_cmpint (n_action_sequences_cancelled, ==, 1); --- -GitLab - - -From 9f3459991092d8174ca80628c551649794abc663 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Thu, 22 Jun 2023 15:19:53 +0800 -Subject: [PATCH 40/42] onscreen/native: Avoid callbacks on "detached" - onscreens - -Detached onscreens have no valid view so avoid servicing callbacks on -them during/after sleep mode. As previously mentioned in 45bda2d969f. - -Fixes: https://launchpad.net/bugs/2020049 ---- - src/backends/native/meta-onscreen-native.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 914ae6f4ab..6f74011d11 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -1245,7 +1245,8 @@ try_post_latest_swap (CoglOnscreen *onscreen) - g_autoptr (ClutterFrame) frame = NULL; - MetaFrameNative *frame_native; - -- if (onscreen_native->next_post.frame == NULL) -+ if (onscreen_native->next_post.frame == NULL || -+ onscreen_native->view == NULL) - return; - - if (meta_kms_is_shutting_down (kms)) --- -GitLab - - -From 49eb4964826943f9d1c5897a1c23bdfebd54e00c Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Tue, 18 Jul 2023 16:08:25 +0800 -Subject: [PATCH 41/42] clutter/frame-clock: Record measurements of zero for - cursor-only updates - -But only if we've ever got actual swap measurements -(COGL_FEATURE_ID_TIMESTAMP_QUERY). If it's supported then we now drop to -double buffering and get optimal latency on a burst of cursor-only -updates. - -Fixes: https://launchpad.net/bugs/2023363 ---- - clutter/clutter/clutter-frame-clock.c | 26 +++++++++++++++----------- - 1 file changed, 15 insertions(+), 11 deletions(-) - -diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c -index 692056b380..d652a6e5fb 100644 ---- a/clutter/clutter/clutter-frame-clock.c -+++ b/clutter/clutter/clutter-frame-clock.c -@@ -233,10 +233,6 @@ static void - maybe_update_longterm_max_duration_us (ClutterFrameClock *frame_clock, - ClutterFrameInfo *frame_info) - { -- /* Do not update long-term max if there has been no measurement */ -- if (!frame_clock->shortterm_max_update_duration_us) -- return; -- - if ((frame_info->presentation_time - frame_clock->longterm_promotion_us) < - G_USEC_PER_SEC) - return; -@@ -348,7 +344,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - - frame_clock->got_measurements_last_frame = FALSE; - -- if (frame_info->cpu_time_before_buffer_swap_us != 0) -+ if (frame_info->cpu_time_before_buffer_swap_us != 0 || -+ frame_clock->ever_got_measurements) - { - int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us; - int64_t dispatch_time_us = 0, flip_time_us = 0; -@@ -371,14 +368,21 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, - break; - } - -- dispatch_to_swap_us = -- frame_info->cpu_time_before_buffer_swap_us - -- dispatch_time_us; -+ if (frame_info->cpu_time_before_buffer_swap_us == 0) -+ { -+ /* Cursor-only updates with no "swap" or "flip" */ -+ dispatch_to_swap_us = 0; -+ swap_to_flip_us = 0; -+ } -+ else -+ { -+ dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us - -+ dispatch_time_us; -+ swap_to_flip_us = flip_time_us - -+ frame_info->cpu_time_before_buffer_swap_us; -+ } - swap_to_rendering_done_us = - frame_info->gpu_rendering_duration_ns / 1000; -- swap_to_flip_us = -- flip_time_us - -- frame_info->cpu_time_before_buffer_swap_us; - - CLUTTER_NOTE (FRAME_TIMINGS, - "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", --- -GitLab - - -From 04094e9ff9b97019a1547f386cbf5d6fc258841e Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt -Date: Wed, 26 Jul 2023 17:27:10 +0800 -Subject: [PATCH 42/42] backends/native: Make await_flush later and inlined - -"Later" as in after GL is finished rendering so as to not block the deadline -timer from updating the cursor smoothly during long GL renders. Indeed this -could make a primary plane frame late in some cases but that's only when -going from idle to active, so there is no previous frame to observe stutter -within the primary plane. It does however fix observable stutter in the -cursor plane by not dropping/stalling an otherwise good cursor update in a -continuous stream of cursor updates. - -"Inlined" as in we don't need most of `meta_kms_device_await_flush`. The -important part is that it calls `disarm_crtc_frame_deadline_timer` to avoid -attempting two posts at once. So that's all that is kept. - -This also fixes a deadlock in triple buffering. By not cancelling the -cursor update during a GL render we're also not cancelling a primary plane -update that might have already been piggybacked onto it by `queue_update`. - -Cherry picked from !3149 ---- - src/backends/native/meta-kms-impl-device.c | 4 +++- - src/backends/native/meta-onscreen-native.c | 6 ------ - 2 files changed, 3 insertions(+), 7 deletions(-) - -diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c -index 7bc5efa654..972873ac48 100644 ---- a/src/backends/native/meta-kms-impl-device.c -+++ b/src/backends/native/meta-kms-impl-device.c -@@ -1483,9 +1483,11 @@ meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device, - meta_kms_update_merge_from (crtc_frame->pending_update, update); - meta_kms_update_free (update); - update = g_steal_pointer (&crtc_frame->pending_update); -- disarm_crtc_frame_deadline_timer (crtc_frame); - } - -+ if (crtc_frame->deadline.armed) -+ disarm_crtc_frame_deadline_timer (crtc_frame); -+ - meta_kms_device_handle_flush (priv->device, latch_crtc); - - feedback = do_process (impl_device, latch_crtc, update, flags); -diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c -index 6f74011d11..14d727c557 100644 ---- a/src/backends/native/meta-onscreen-native.c -+++ b/src/backends/native/meta-onscreen-native.c -@@ -1557,12 +1557,6 @@ void - meta_onscreen_native_before_redraw (CoglOnscreen *onscreen, - ClutterFrame *frame) - { -- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); -- MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); -- MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); -- -- meta_kms_device_await_flush (meta_kms_crtc_get_device (kms_crtc), -- kms_crtc); - } - - void --- -GitLab -