Index: src/backends/native/meta-kms.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c --- a/src/backends/native/meta-kms.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms.c (date 1665619526485) @@ -23,6 +23,7 @@ #include "backends/native/meta-kms-private.h" #include "backends/native/meta-backend-native.h" +#include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-update-private.h" @@ -177,10 +178,17 @@ GList *pending_callbacks; guint callback_source_id; + + gboolean shutting_down; }; G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT) +static MetaKmsFeedback * +meta_kms_post_update_sync (MetaKms *kms, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags); + void meta_kms_discard_pending_updates (MetaKms *kms) { @@ -247,23 +255,115 @@ return NULL; } +MetaKmsUpdate * +meta_kms_ensure_pending_update_for_crtc (MetaKms *kms, + MetaKmsCrtc *crtc) +{ + MetaKmsUpdate *update; + + update = meta_kms_get_pending_update_for_crtc (kms, crtc); + if (update == NULL) + { + update = meta_kms_update_new (meta_kms_crtc_get_device (crtc)); + meta_kms_update_include_crtc (update, crtc); + meta_kms_add_pending_update (kms, update); + } + + return update; +} + +static MetaKmsUpdate * +meta_kms_find_compatible_update_for_crtc (MetaKms *kms, + MetaKmsCrtc *crtc, + gboolean take) +{ + MetaKmsDevice *device; + MetaKmsUpdate *update; + GList *l; + + for (l = kms->pending_updates; l; l = l->next) + { + update = l->data; + if (meta_kms_update_includes_crtc (update, crtc)) + goto found; + } + + device = meta_kms_crtc_get_device (crtc); + + for (l = kms->pending_updates; l; l = l->next) + { + update = l->data; + if (meta_kms_update_get_device (update) == device && + meta_kms_update_get_mode_sets (update)) + goto found; + } + + return NULL; + +found: + if (take) + kms->pending_updates = g_list_delete_link (kms->pending_updates, l); + return update; +} + +MetaKmsUpdate * +meta_kms_get_pending_update_for_crtc (MetaKms *kms, + MetaKmsCrtc *crtc) +{ + return meta_kms_find_compatible_update_for_crtc (kms, crtc, FALSE); +} + +static MetaKmsUpdate * +meta_kms_take_pending_update_for_crtc (MetaKms *kms, + MetaKmsCrtc *crtc) +{ + return meta_kms_find_compatible_update_for_crtc (kms, crtc, TRUE); +} + MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms *kms, MetaKmsDevice *device, MetaKmsUpdateFlag flags) { MetaKmsUpdate *update; + + update = meta_kms_take_pending_update (kms, device); + if (!update) + return NULL; + + return meta_kms_post_update_sync (kms, update, flags); +} + +MetaKmsFeedback * +meta_kms_post_pending_update_for_crtc_sync (MetaKms *kms, + MetaKmsCrtc *crtc, + MetaKmsUpdateFlag flags) +{ + MetaKmsUpdate *update; + + update = meta_kms_take_pending_update_for_crtc (kms, crtc); + if (!update) + return NULL; + + return meta_kms_post_update_sync (kms, update, flags); +} + +static MetaKmsFeedback * +meta_kms_post_update_sync (MetaKms *kms, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags) +{ + MetaKmsDevice *device = meta_kms_update_get_device (update); MetaKmsFeedback *feedback; GList *result_listeners; GList *l; + if (kms->shutting_down) + return NULL; + COGL_TRACE_BEGIN_SCOPED (MetaKmsPostUpdateSync, "KMS (post update)"); - update = meta_kms_take_pending_update (kms, device); - if (!update) - return NULL; - meta_kms_update_lock (update); feedback = meta_kms_device_process_update_sync (device, update, flags); @@ -752,6 +852,8 @@ void meta_kms_prepare_shutdown (MetaKms *kms) { + kms->shutting_down = TRUE; + meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL); flush_callbacks (kms); } Index: clutter/clutter/clutter-frame.h IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h --- a/clutter/clutter/clutter-frame.h (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/clutter/clutter/clutter-frame.h (date 1665619526418) @@ -33,4 +33,11 @@ CLUTTER_EXPORT gboolean clutter_frame_has_result (ClutterFrame *frame); +CLUTTER_EXPORT +void clutter_frame_set_hint (ClutterFrame *frame, + ClutterFrameHint hint); + +CLUTTER_EXPORT +ClutterFrameHint clutter_frame_get_hints (ClutterFrame *frame); + #endif /* CLUTTER_FRAME_H */ Index: clutter/clutter/clutter-frame.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c --- a/clutter/clutter/clutter-frame.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/clutter/clutter/clutter-frame.c (date 1665619526412) @@ -40,3 +40,16 @@ 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; +} Index: src/backends/native/meta-kms.h IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h --- a/src/backends/native/meta-kms.h (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms.h (date 1665619526492) @@ -39,9 +39,15 @@ MetaKmsUpdate * meta_kms_ensure_pending_update (MetaKms *kms, MetaKmsDevice *device); +MetaKmsUpdate * meta_kms_ensure_pending_update_for_crtc (MetaKms *kms, + MetaKmsCrtc *crtc); + MetaKmsUpdate * meta_kms_get_pending_update (MetaKms *kms, MetaKmsDevice *device); +MetaKmsUpdate * meta_kms_get_pending_update_for_crtc (MetaKms *kms, + MetaKmsCrtc *crtc); + MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms *kms, MetaKmsDevice *device, MetaKmsUpdateFlag flags); @@ -49,6 +55,10 @@ MetaKmsFeedback * meta_kms_post_test_update_sync (MetaKms *kms, MetaKmsUpdate *update); +MetaKmsFeedback * meta_kms_post_pending_update_for_crtc_sync (MetaKms *kms, + MetaKmsCrtc *device, + MetaKmsUpdateFlag flags); + void meta_kms_discard_pending_page_flips (MetaKms *kms); void meta_kms_notify_modes_set (MetaKms *kms); Index: src/backends/native/meta-kms-update.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c --- a/src/backends/native/meta-kms-update.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms-update.c (date 1665619599477) @@ -25,12 +25,14 @@ #include "backends/meta-display-config-shared.h" #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-crtc.h" +#include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms-mode-private.h" #include "backends/native/meta-kms-plane.h" struct _MetaKmsUpdate { MetaKmsDevice *device; + GHashTable *crtcs; gboolean is_locked; uint64_t sequence_number; @@ -149,6 +151,7 @@ meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment) { g_clear_pointer (&plane_assignment->fb_damage, meta_kms_fb_damage_free); + g_clear_object (&plane_assignment->buffer); g_free (plane_assignment); } @@ -228,7 +231,7 @@ .update = update, .crtc = crtc, .plane = plane, - .buffer = buffer, + .buffer = g_object_ref (buffer), .src_rect = src_rect, .dst_rect = dst_rect, .flags = flags, @@ -237,6 +240,8 @@ update->plane_assignments = g_list_prepend (update->plane_assignments, plane_assignment); + g_hash_table_add (update->crtcs, crtc); + return plane_assignment; } @@ -262,6 +267,8 @@ update->plane_assignments = g_list_prepend (update->plane_assignments, plane_assignment); + g_hash_table_add (update->crtcs, crtc); + return plane_assignment; } @@ -284,6 +291,8 @@ }; update->mode_sets = g_list_prepend (update->mode_sets, mode_set); + + g_hash_table_add (update->crtcs, crtc); } static MetaKmsConnectorUpdate * @@ -292,6 +301,8 @@ { GList *l; MetaKmsConnectorUpdate *connector_update; + MetaKmsDevice *device; + const MetaKmsConnectorState *state; for (l = update->connector_updates; l; l = l->next) { @@ -306,6 +317,23 @@ update->connector_updates = g_list_prepend (update->connector_updates, connector_update); + device = meta_kms_connector_get_device (connector); + state = meta_kms_connector_get_current_state (connector); + if (device && state && state->current_crtc_id) + { + GList *l; + + for (l = meta_kms_device_get_crtcs (device); l; l = l->next) + { + MetaKmsCrtc *kms_crtc = l->data; + + if (meta_kms_crtc_get_id (kms_crtc) == state->current_crtc_id) + { + g_hash_table_add (update->crtcs, kms_crtc); + break; + } + } + } return connector_update; } @@ -416,6 +444,8 @@ gamma = meta_kms_crtc_gamma_new (crtc, size, red, green, blue); update->crtc_gammas = g_list_prepend (update->crtc_gammas, gamma); + + g_hash_table_add (update->crtcs, crtc); } void @@ -679,6 +709,20 @@ return update->device; } +gboolean +meta_kms_update_includes_crtc (MetaKmsUpdate *update, + MetaKmsCrtc *crtc) +{ + return g_hash_table_contains (update->crtcs, crtc); +} + +void +meta_kms_update_include_crtc (MetaKmsUpdate *update, + MetaKmsCrtc *crtc) +{ + g_hash_table_add (update->crtcs, crtc); +} + MetaKmsCustomPageFlip * meta_kms_update_take_custom_page_flip_func (MetaKmsUpdate *update) { @@ -707,12 +751,15 @@ update->device = device; update->sequence_number = sequence_number++; + update->crtcs = g_hash_table_new (NULL, NULL); + return update; } void meta_kms_update_free (MetaKmsUpdate *update) { + g_hash_table_destroy (update->crtcs); g_list_free_full (update->result_listeners, (GDestroyNotify) meta_kms_result_listener_free); g_list_free_full (update->plane_assignments, Index: src/backends/native/meta-crtc-kms.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c --- a/src/backends/native/meta-crtc-kms.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-crtc-kms.c (date 1665619526465) @@ -394,7 +394,7 @@ if (!gamma) return; - kms_update = meta_kms_ensure_pending_update (kms, kms_device); + kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc); meta_kms_update_set_crtc_gamma (kms_update, kms_crtc, gamma->size, Index: src/backends/native/meta-kms-update-private.h IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms-update-private.h b/src/backends/native/meta-kms-update-private.h --- a/src/backends/native/meta-kms-update-private.h (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms-update-private.h (date 1665619599484) @@ -137,6 +137,12 @@ META_EXPORT_TEST MetaKmsDevice * meta_kms_update_get_device (MetaKmsUpdate *update); +gboolean meta_kms_update_includes_crtc (MetaKmsUpdate *update, + MetaKmsCrtc *crtc); + +void meta_kms_update_include_crtc (MetaKmsUpdate *update, + MetaKmsCrtc *crtc); + void meta_kms_plane_assignment_set_rotation (MetaKmsPlaneAssignment *plane_assignment, MetaKmsPlaneRotation rotation); Index: clutter/clutter/clutter-frame-private.h IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/clutter/clutter/clutter-frame-private.h b/clutter/clutter/clutter-frame-private.h --- a/clutter/clutter/clutter-frame-private.h (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/clutter/clutter/clutter-frame-private.h (date 1665619526435) @@ -24,6 +24,7 @@ { gboolean has_result; ClutterFrameResult result; + ClutterFrameHint hints; }; #define CLUTTER_FRAME_INIT ((ClutterFrame) { 0 }) Index: clutter/clutter/clutter-frame-clock.h IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h --- a/clutter/clutter/clutter-frame-clock.h (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/clutter/clutter/clutter-frame-clock.h (date 1665619526432) @@ -34,6 +34,12 @@ 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, @@ -90,8 +96,9 @@ 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); Index: src/backends/native/meta-kms-impl-device-atomic.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms-impl-device-atomic.c b/src/backends/native/meta-kms-impl-device-atomic.c --- a/src/backends/native/meta-kms-impl-device-atomic.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms-impl-device-atomic.c (date 1665619599454) @@ -457,6 +457,7 @@ { MetaKmsPlaneAssignment *plane_assignment = update_entry; MetaKmsPlane *plane = plane_assignment->plane; + MetaKmsUpdateFlag flags = (MetaKmsUpdateFlag) user_data; MetaDrmBuffer *buffer; MetaKmsFbDamage *fb_damage; uint32_t prop_id; @@ -609,6 +610,12 @@ error)) return FALSE; } + + if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) + meta_kms_crtc_remember_plane_buffer (plane_assignment->crtc, + meta_kms_plane_get_id (plane), + buffer); + return TRUE; } @@ -986,7 +993,7 @@ req, blob_ids, meta_kms_update_get_plane_assignments (update), - NULL, + GUINT_TO_POINTER (flags), process_plane_assignment, &error)) goto err; Index: clutter/clutter/clutter-frame-clock.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c --- a/clutter/clutter/clutter-frame-clock.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/clutter/clutter/clutter-frame-clock.c (date 1665619526428) @@ -45,6 +45,8 @@ int next_index; } EstimateQueue; +static gboolean triple_buffering_disabled = FALSE; + #define SYNC_DELAY_FALLBACK_FRACTION 0.875 typedef struct _ClutterFrameListener @@ -65,8 +67,9 @@ 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 @@ -96,6 +99,8 @@ /* Last KMS buffer submission time. */ int64_t last_flip_time_us; + ClutterFrameHint last_flip_hints; + /* Last few durations between dispatch start and buffer swap. */ EstimateQueue dispatch_to_swap_us; /* Last few durations between buffer swap and GPU rendering finish. */ @@ -228,6 +233,10 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, ClutterFrameInfo *frame_info) { + const char *debug_state = + frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ? + "Triple buffering" : "Double buffering"; + COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented, "Frame Clock (presented)"); @@ -290,7 +299,8 @@ frame_info->cpu_time_before_buffer_swap_us; CLUTTER_NOTE (FRAME_TIMINGS, - "dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", + "%s: dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", + debug_state, dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us); @@ -304,6 +314,10 @@ frame_clock->got_measurements_last_frame = TRUE; } + else + { + CLUTTER_NOTE (FRAME_TIMINGS, "%s", debug_state); + } if (frame_info->refresh_rate > 1.0) { @@ -318,11 +332,18 @@ 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; } } @@ -338,11 +359,18 @@ 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; } } @@ -354,6 +382,7 @@ int64_t max_swap_to_rendering_done_us = 0; int64_t max_swap_to_flip_us = 0; int64_t max_render_time_us; + int buffer_queue_latency_frames = 0; int i; refresh_interval_us = frame_clock->refresh_interval_us; @@ -376,6 +405,27 @@ frame_clock->swap_to_flip_us.values[i]); } + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + buffer_queue_latency_frames = 0; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + buffer_queue_latency_frames = 1; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + g_warn_if_reached (); + buffer_queue_latency_frames = 2; + break; + } + + max_swap_to_flip_us -= refresh_interval_us * buffer_queue_latency_frames; + if (max_swap_to_flip_us < 0) + max_swap_to_flip_us = 0; + /* Max render time shows how early the frame clock needs to be dispatched * to make it to the predicted next presentation time. It is composed of: * - An estimate of duration from dispatch start to buffer swap. @@ -392,8 +442,6 @@ 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; } @@ -563,8 +611,12 @@ 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; } @@ -600,11 +652,17 @@ case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: next_update_time_us = g_get_monotonic_time (); + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + 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; @@ -613,7 +671,6 @@ g_warn_if_fail (next_update_time_us != -1); g_source_set_ready_time (frame_clock->source, next_update_time_us); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; frame_clock->is_next_presentation_time_valid = FALSE; } @@ -632,6 +689,7 @@ { 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, @@ -639,11 +697,28 @@ &frame_clock->next_presentation_time_us); frame_clock->is_next_presentation_time_valid = (frame_clock->next_presentation_time_us != 0); + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_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: + if (frame_clock->last_flip_hints & CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED || + triple_buffering_disabled) + { + /* Force double buffering, disable triple buffering */ + frame_clock->pending_reschedule = TRUE; + return; + } + + calculate_next_update_time_us (frame_clock, + &next_update_time_us, + &frame_clock->next_presentation_time_us); + frame_clock->is_next_presentation_time_valid = + (frame_clock->next_presentation_time_us != 0); + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; return; } @@ -651,7 +726,6 @@ g_warn_if_fail (next_update_time_us != -1); g_source_set_ready_time (frame_clock->source, next_update_time_us); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; } static void @@ -675,7 +749,7 @@ 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; @@ -683,7 +757,21 @@ 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++; @@ -708,23 +796,29 @@ frame_clock->listener.user_data); COGL_TRACE_END (ClutterFrameClockFrame); - 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: - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - switch (result) - { - case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED; + switch (result) + { + case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: + break; + case CLUTTER_FRAME_RESULT_IDLE: + /* The frame was aborted; nothing to paint/present */ + 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 (); + break; + 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_RESULT_IDLE: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; maybe_reschedule_update (frame_clock); break; } @@ -759,10 +853,12 @@ } void -clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, - int64_t flip_time_us) +clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, + int64_t flip_time_us, + ClutterFrameHint hints) { frame_clock->last_flip_time_us = flip_time_us; + frame_clock->last_flip_hints = hints; } GString * @@ -893,6 +989,9 @@ { GObjectClass *object_class = G_OBJECT_CLASS (klass); + if (!g_strcmp0 (g_getenv ("MUTTER_DEBUG_DISABLE_TRIPLE_BUFFERING"), "1")) + triple_buffering_disabled = TRUE; + object_class->dispose = clutter_frame_clock_dispose; signals[DESTROY] = Index: src/backends/native/meta-renderer-native.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c --- a/src/backends/native/meta-renderer-native.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-renderer-native.c (date 1665619599514) @@ -677,12 +677,18 @@ 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; @@ -694,6 +700,9 @@ { 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 = @@ -1402,6 +1411,26 @@ return view; } +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 keep_current_onscreens_alive (MetaRenderer *renderer) { @@ -1430,6 +1459,7 @@ MetaRendererClass *parent_renderer_class = META_RENDERER_CLASS (meta_renderer_native_parent_class); + discard_pending_swaps (renderer); meta_kms_discard_pending_page_flips (kms); meta_kms_discard_pending_updates (kms); Index: cogl/cogl/cogl-onscreen.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c --- a/cogl/cogl/cogl-onscreen.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/cogl/cogl/cogl-onscreen.c (date 1665619526452) @@ -510,6 +510,14 @@ 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, Index: src/backends/native/meta-kms-crtc.h IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms-crtc.h b/src/backends/native/meta-kms-crtc.h --- a/src/backends/native/meta-kms-crtc.h (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms-crtc.h (date 1665619599437) @@ -25,6 +25,7 @@ #include #include "backends/native/meta-kms-types.h" +#include "backends/native/meta-drm-buffer.h" #include "core/util-private.h" #include "meta/boxes.h" @@ -84,4 +85,12 @@ const uint16_t *green, const uint16_t *blue); +void meta_kms_crtc_remember_plane_buffer (MetaKmsCrtc *crtc, + uint32_t plane_id, + MetaDrmBuffer *buffer); + +void meta_kms_crtc_on_scanout_started (MetaKmsCrtc *crtc); + +void meta_kms_crtc_release_buffers (MetaKmsCrtc *crtc); + #endif /* META_KMS_CRTC_H */ Index: clutter/clutter/clutter-stage-view.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c --- a/clutter/clutter/clutter-stage-view.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/clutter/clutter/clutter-stage-view.c (date 1665619526445) @@ -1252,8 +1252,9 @@ _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); Index: src/backends/native/meta-kms-crtc.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c --- a/src/backends/native/meta-kms-crtc.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms-crtc.c (date 1665619599417) @@ -32,6 +32,12 @@ MetaKmsProp props[META_KMS_CRTC_N_PROPS]; } MetaKmsCrtcPropTable; +typedef struct +{ + MetaDrmBuffer *front, *back; + gboolean back_is_set; +} PlaneState; + struct _MetaKmsCrtc { GObject parent; @@ -44,6 +50,8 @@ MetaKmsCrtcState current_state; MetaKmsCrtcPropTable prop_table; + + GHashTable *plane_states; }; G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT) @@ -403,26 +411,97 @@ return crtc; } +void +meta_kms_crtc_remember_plane_buffer (MetaKmsCrtc *crtc, + uint32_t plane_id, + MetaDrmBuffer *buffer) +{ + gpointer key = GUINT_TO_POINTER (plane_id); + PlaneState *plane_state; + + plane_state = g_hash_table_lookup (crtc->plane_states, key); + if (plane_state == NULL) + { + plane_state = g_new0 (PlaneState, 1); + g_hash_table_insert (crtc->plane_states, key, plane_state); + } + + plane_state->back_is_set = TRUE; /* note buffer may be NULL */ + g_set_object (&plane_state->back, buffer); +} + +static void +swap_plane_buffers (gpointer key, + gpointer value, + gpointer user_data) +{ + PlaneState *plane_state = value; + + if (plane_state->back_is_set) + { + g_set_object (&plane_state->front, plane_state->back); + g_clear_object (&plane_state->back); + plane_state->back_is_set = FALSE; + } +} + +void +meta_kms_crtc_on_scanout_started (MetaKmsCrtc *crtc) +{ + g_hash_table_foreach (crtc->plane_states, swap_plane_buffers, NULL); +} + +void +meta_kms_crtc_release_buffers (MetaKmsCrtc *crtc) +{ + g_hash_table_remove_all (crtc->plane_states); +} + +static void +meta_kms_crtc_dispose (GObject *object) +{ + MetaKmsCrtc *crtc = META_KMS_CRTC (object); + + meta_kms_crtc_release_buffers (crtc); + + G_OBJECT_CLASS (meta_kms_crtc_parent_class)->dispose (object); +} + static void meta_kms_crtc_finalize (GObject *object) { MetaKmsCrtc *crtc = META_KMS_CRTC (object); clear_gamma_state (&crtc->current_state); + g_hash_table_unref (crtc->plane_states); G_OBJECT_CLASS (meta_kms_crtc_parent_class)->finalize (object); } +static void +destroy_plane_state (gpointer data) +{ + PlaneState *plane_state = data; + + g_clear_object (&plane_state->front); + g_clear_object (&plane_state->back); + g_free (plane_state); +} + static void meta_kms_crtc_init (MetaKmsCrtc *crtc) { crtc->current_state.gamma.size = 0; + crtc->plane_states = g_hash_table_new_full (NULL, + NULL, + NULL, + destroy_plane_state); } static void 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; } Index: src/backends/native/meta-onscreen-native.h IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h --- a/src/backends/native/meta-onscreen-native.h (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-onscreen-native.h (date 1665619599501) @@ -42,6 +42,8 @@ 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); Index: src/backends/native/meta-cursor-renderer-native.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c --- a/src/backends/native/meta-cursor-renderer-native.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-cursor-renderer-native.c (date 1665619526475) @@ -58,19 +58,6 @@ #include "wayland/meta-wayland-buffer.h" #endif -/* When animating a cursor, we usually call drmModeSetCursor2 once per frame. - * Though, testing shows that we need to triple buffer the cursor buffer in - * order to avoid glitches when animating the cursor, at least when running on - * Intel. The reason for this might be (but is not confirmed to be) due to - * the user space gbm_bo cache, making us reuse and overwrite the kernel side - * buffer content before it was scanned out. To avoid this, we keep a user space - * reference to each buffer we set until at least one frame after it was drawn. - * In effect, this means we three active cursor gbm_bo's: one that that just has - * been set, one that was previously set and may or may not have been scanned - * out, and one pending that will be replaced if the cursor sprite changes. - */ -#define HW_CURSOR_BUFFER_COUNT 3 - static GQuark quark_cursor_sprite = 0; typedef struct _CrtcCursorData @@ -104,19 +91,10 @@ uint64_t cursor_height; } MetaCursorRendererNativeGpuData; -typedef enum _MetaCursorBufferState -{ - META_CURSOR_BUFFER_STATE_NONE, - META_CURSOR_BUFFER_STATE_SET, - META_CURSOR_BUFFER_STATE_INVALIDATED, -} MetaCursorBufferState; - typedef struct _MetaCursorNativeGpuState { MetaGpu *gpu; - unsigned int active_buffer_idx; - MetaCursorBufferState pending_buffer_state; - MetaDrmBuffer *buffers[HW_CURSOR_BUFFER_COUNT]; + MetaDrmBuffer *buffer; } MetaCursorNativeGpuState; typedef struct _MetaCursorNativePrivate @@ -197,44 +175,17 @@ G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object); } -static unsigned int -get_pending_cursor_sprite_buffer_index (MetaCursorNativeGpuState *cursor_gpu_state) -{ - return (cursor_gpu_state->active_buffer_idx + 1) % HW_CURSOR_BUFFER_COUNT; -} - -static MetaDrmBuffer * -get_pending_cursor_sprite_buffer (MetaCursorNativeGpuState *cursor_gpu_state) -{ - unsigned int pending_buffer_idx; - - pending_buffer_idx = - get_pending_cursor_sprite_buffer_index (cursor_gpu_state); - return cursor_gpu_state->buffers[pending_buffer_idx]; -} - -static MetaDrmBuffer * -get_active_cursor_sprite_buffer (MetaCursorNativeGpuState *cursor_gpu_state) -{ - return cursor_gpu_state->buffers[cursor_gpu_state->active_buffer_idx]; -} - static void -set_pending_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite, - MetaGpuKms *gpu_kms, - MetaDrmBuffer *buffer) +set_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite, + MetaGpuKms *gpu_kms, + MetaDrmBuffer *buffer) { MetaCursorNativePrivate *cursor_priv; MetaCursorNativeGpuState *cursor_gpu_state; - unsigned int pending_buffer_idx; cursor_priv = ensure_cursor_priv (cursor_sprite); cursor_gpu_state = ensure_cursor_gpu_state (cursor_priv, gpu_kms); - - pending_buffer_idx = - get_pending_cursor_sprite_buffer_index (cursor_gpu_state); - cursor_gpu_state->buffers[pending_buffer_idx] = buffer; - cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_SET; + cursor_gpu_state->buffer = buffer; } static void @@ -309,10 +260,7 @@ MetaKmsUpdate *kms_update; MetaKmsPlaneAssignment *plane_assignment; - if (cursor_gpu_state->pending_buffer_state == META_CURSOR_BUFFER_STATE_SET) - buffer = get_pending_cursor_sprite_buffer (cursor_gpu_state); - else - buffer = get_active_cursor_sprite_buffer (cursor_gpu_state); + buffer = cursor_gpu_state->buffer; kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); kms_device = meta_kms_crtc_get_device (kms_crtc); @@ -341,8 +289,8 @@ flags |= META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED; kms_update = - meta_kms_ensure_pending_update (meta_kms_device_get_kms (kms_device), - meta_kms_crtc_get_device (kms_crtc)); + meta_kms_ensure_pending_update_for_crtc (meta_kms_device_get_kms (kms_device), + kms_crtc); plane_assignment = meta_kms_update_assign_plane (kms_update, kms_crtc, cursor_plane, @@ -363,13 +311,6 @@ native); crtc_cursor_data->buffer = buffer; - - if (cursor_gpu_state->pending_buffer_state == META_CURSOR_BUFFER_STATE_SET) - { - cursor_gpu_state->active_buffer_idx = - (cursor_gpu_state->active_buffer_idx + 1) % HW_CURSOR_BUFFER_COUNT; - cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_NONE; - } } static float @@ -494,7 +435,7 @@ MetaKms *kms = meta_kms_device_get_kms (kms_device); MetaKmsUpdate *kms_update; - kms_update = meta_kms_ensure_pending_update (kms, kms_device); + kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc); meta_kms_update_unassign_plane (kms_update, kms_crtc, cursor_plane); } @@ -602,19 +543,7 @@ if (!cursor_gpu_state) return FALSE; - switch (cursor_gpu_state->pending_buffer_state) - { - case META_CURSOR_BUFFER_STATE_NONE: - return get_active_cursor_sprite_buffer (cursor_gpu_state) != NULL; - case META_CURSOR_BUFFER_STATE_SET: - return TRUE; - case META_CURSOR_BUFFER_STATE_INVALIDATED: - return FALSE; - } - - g_assert_not_reached (); - - return FALSE; + return cursor_gpu_state->buffer != NULL; } static void @@ -1119,16 +1048,14 @@ static void cursor_gpu_state_free (MetaCursorNativeGpuState *cursor_gpu_state) { - int i; MetaDrmBuffer *active_buffer; - active_buffer = get_active_cursor_sprite_buffer (cursor_gpu_state); + active_buffer = cursor_gpu_state->buffer; if (active_buffer) unset_crtc_cursor_renderer_privates (cursor_gpu_state->gpu, active_buffer); - for (i = 0; i < HW_CURSOR_BUFFER_COUNT; i++) - g_clear_object (&cursor_gpu_state->buffers[i]); + g_clear_object (&cursor_gpu_state->buffer); g_free (cursor_gpu_state); } @@ -1165,14 +1092,7 @@ g_hash_table_iter_init (&iter, cursor_priv->gpu_states); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cursor_gpu_state)) - { - unsigned int pending_buffer_idx; - - pending_buffer_idx = get_pending_cursor_sprite_buffer_index (cursor_gpu_state); - g_clear_object (&cursor_gpu_state->buffers[pending_buffer_idx]); - cursor_gpu_state->pending_buffer_state = - META_CURSOR_BUFFER_STATE_INVALIDATED; - } + g_clear_object (&cursor_gpu_state->buffer); } static void @@ -1410,35 +1330,7 @@ return; } - set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms, buffer); -} - -static gboolean -is_cursor_hw_state_valid (MetaCursorSprite *cursor_sprite, - MetaGpuKms *gpu_kms) -{ - MetaCursorNativePrivate *cursor_priv; - MetaCursorNativeGpuState *cursor_gpu_state; - - cursor_priv = get_cursor_priv (cursor_sprite); - if (!cursor_priv) - return FALSE; - - cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms); - if (!cursor_gpu_state) - return FALSE; - - switch (cursor_gpu_state->pending_buffer_state) - { - case META_CURSOR_BUFFER_STATE_SET: - case META_CURSOR_BUFFER_STATE_NONE: - return TRUE; - case META_CURSOR_BUFFER_STATE_INVALIDATED: - return FALSE; - } - - g_assert_not_reached (); - return FALSE; + set_cursor_sprite_buffer (cursor_sprite, gpu_kms, buffer); } static gboolean @@ -1605,7 +1497,7 @@ if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken) return; - if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) && + if (has_valid_cursor_sprite_buffer (cursor_sprite, gpu_kms) && is_cursor_scale_and_transform_valid (renderer, cursor_sprite)) return; @@ -1750,8 +1642,8 @@ return; } - set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms, - META_DRM_BUFFER (buffer_gbm)); + set_cursor_sprite_buffer (cursor_sprite, gpu_kms, + META_DRM_BUFFER (buffer_gbm)); } } #endif @@ -1775,7 +1667,7 @@ if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken) return; - if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) && + if (has_valid_cursor_sprite_buffer (cursor_sprite, gpu_kms) && is_cursor_scale_and_transform_valid (renderer, cursor_sprite)) return; Index: src/backends/native/meta-onscreen-native.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c --- a/src/backends/native/meta-onscreen-native.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-onscreen-native.c (date 1665619599497) @@ -67,13 +67,12 @@ struct { struct gbm_surface *surface; - MetaDrmBuffer *current_fb; - MetaDrmBuffer *next_fb; } gbm; struct { MetaDrmBufferDumb *current_dumb_fb; MetaDrmBufferDumb *dumb_fbs[2]; + MetaDrmBuffer *source_fbs[2]; } cpu; gboolean noted_primary_gpu_copy_ok; @@ -94,8 +93,8 @@ struct { struct gbm_surface *surface; - MetaDrmBuffer *current_fb; MetaDrmBuffer *next_fb; + MetaDrmBuffer *stalled_fb; } gbm; #ifdef HAVE_EGL_DEVICE @@ -107,69 +106,25 @@ #endif MetaRendererView *view; + + unsigned int swaps_pending; + struct { + int *rectangles; /* 4 x n_rectangles */ + int n_rectangles; + } next_post; }; G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, COGL_TYPE_ONSCREEN_EGL) +static void +try_post_latest_swap (CoglOnscreen *onscreen); + static gboolean init_secondary_gpu_state (MetaRendererNative *renderer_native, CoglOnscreen *onscreen, GError **error); -static void -swap_secondary_drm_fb (CoglOnscreen *onscreen) -{ - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - secondary_gpu_state = onscreen_native->secondary_gpu_state; - if (!secondary_gpu_state) - return; - - g_set_object (&secondary_gpu_state->gbm.current_fb, - secondary_gpu_state->gbm.next_fb); - g_clear_object (&secondary_gpu_state->gbm.next_fb); -} - -static void -free_current_secondary_bo (CoglOnscreen *onscreen) -{ - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - secondary_gpu_state = onscreen_native->secondary_gpu_state; - if (!secondary_gpu_state) - return; - - g_clear_object (&secondary_gpu_state->gbm.current_fb); -} - -static void -free_current_bo (CoglOnscreen *onscreen) -{ - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - - g_clear_object (&onscreen_native->gbm.current_fb); - free_current_secondary_bo (onscreen); -} - -static void -meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) -{ - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - - if (!onscreen_native->gbm.next_fb) - return; - - free_current_bo (onscreen); - - g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); - g_clear_object (&onscreen_native->gbm.next_fb); - - swap_secondary_drm_fb (onscreen); -} - static void maybe_update_frame_info (MetaCrtc *crtc, CoglFrameInfo *frame_info, @@ -205,7 +160,7 @@ 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); @@ -234,7 +189,7 @@ maybe_update_frame_info (crtc, frame_info, time_us, flags, sequence); meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_swap_drm_fb (onscreen); + try_post_latest_swap (onscreen); } static int64_t @@ -296,6 +251,7 @@ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); + try_post_latest_swap (onscreen); } static void @@ -345,7 +301,7 @@ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_swap_drm_fb (onscreen); + try_post_latest_swap (onscreen); } static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { @@ -406,18 +362,40 @@ } #endif /* HAVE_EGL_DEVICE */ -void -meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) +static void +drop_stalled_swap (CoglOnscreen *onscreen) { + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); CoglFrameInfo *frame_info; - 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, @@ -436,8 +414,7 @@ MetaKmsDevice *kms_device; MetaKms *kms; MetaKmsUpdate *kms_update; - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL; - MetaDrmBuffer *buffer; + g_autoptr (MetaDrmBuffer) buffer = NULL; MetaKmsPlaneAssignment *plane_assignment; COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs, @@ -446,7 +423,7 @@ gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); kms_device = meta_gpu_kms_get_kms_device (gpu_kms); kms = meta_kms_device_get_kms (kms_device); - kms_update = meta_kms_ensure_pending_update (kms, kms_device); + kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc); g_assert (meta_gpu_kms_is_crtc_active (gpu_kms, crtc)); @@ -455,15 +432,7 @@ switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: - if (gpu_kms == render_gpu) - { - buffer = onscreen_native->gbm.next_fb; - } - else - { - secondary_gpu_state = onscreen_native->secondary_gpu_state; - buffer = secondary_gpu_state->gbm.next_fb; - } + buffer = g_steal_pointer (&onscreen_native->gbm.next_fb); plane_assignment = meta_crtc_kms_assign_primary_plane (crtc_kms, buffer, @@ -509,7 +478,7 @@ COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeSetCrtcModes, "Onscreen (set CRTC modes)"); - kms_update = meta_kms_ensure_pending_update (kms, kms_device); + kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc); switch (renderer_gpu_data->mode) { @@ -537,13 +506,41 @@ kms_update); } +static void +hold_primary_gpu_fb_for_secondary_gpu_scanout (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, + MetaDrmBuffer *primary_gpu_fb, + MetaDrmBuffer *secondary_gpu_fb) +{ + if (META_IS_DRM_BUFFER_DUMB (secondary_gpu_fb)) + { + MetaDrmBufferDumb *dumb_fb = META_DRM_BUFFER_DUMB (secondary_gpu_fb); + int i; + const int n = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); + + for (i = 0; i < n; i++) + { + if (dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i]) + { + g_set_object (&secondary_gpu_state->cpu.source_fbs[i], + primary_gpu_fb); + break; + } + } + + g_warn_if_fail (i < n); + } +} + static void secondary_gpu_release_dumb (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { unsigned i; for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++) - g_clear_object (&secondary_gpu_state->cpu.dumb_fbs[i]); + { + g_clear_object (&secondary_gpu_state->cpu.dumb_fbs[i]); + g_clear_object (&secondary_gpu_state->cpu.source_fbs[i]); + } } static void @@ -568,8 +565,6 @@ NULL); } - g_clear_object (&secondary_gpu_state->gbm.current_fb); - g_clear_object (&secondary_gpu_state->gbm.next_fb); g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy); secondary_gpu_release_dumb (secondary_gpu_state); @@ -577,11 +572,11 @@ g_free (secondary_gpu_state); } -static gboolean +static MetaDrmBuffer * import_shared_framebuffer (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, + MetaDrmBuffer *primary_gpu_fb) { - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); MetaRenderDevice *render_device; g_autoptr (GError) error = NULL; MetaDrmBuffer *imported_buffer; @@ -589,7 +584,7 @@ render_device = secondary_gpu_state->renderer_gpu_data->render_device; imported_buffer = meta_render_device_import_dma_buf (render_device, - onscreen_native->gbm.next_fb, + primary_gpu_fb, &error); if (!imported_buffer) { @@ -603,15 +598,8 @@ META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE); secondary_gpu_state->import_status = META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED; - return FALSE; + return NULL; } - - /* - * next_fb may already contain a fallback buffer, so clear it only - * when we are sure to succeed. - */ - g_clear_object (&secondary_gpu_state->gbm.next_fb); - secondary_gpu_state->gbm.next_fb = imported_buffer; if (secondary_gpu_state->import_status == META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE) @@ -629,16 +617,16 @@ secondary_gpu_state->import_status = META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK; - return TRUE; + return imported_buffer; } -static void +static MetaDrmBuffer * copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, MetaRendererNativeGpuData *renderer_gpu_data, - gboolean *egl_context_changed) + gboolean *egl_context_changed, + MetaDrmBuffer *primary_gpu_fb) { - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); MetaGles3 *gles3 = meta_renderer_native_get_gles3 (renderer_native); @@ -654,9 +642,6 @@ COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferSecondaryGpu, "FB Copy (secondary GPU)"); - g_warn_if_fail (secondary_gpu_state->gbm.next_fb == NULL); - g_clear_object (&secondary_gpu_state->gbm.next_fb); - render_device = renderer_gpu_data->render_device; egl_display = meta_render_device_get_egl_display (render_device); @@ -669,13 +654,13 @@ { g_warning ("Failed to make current: %s", error->message); g_error_free (error); - return; + return NULL; } *egl_context_changed = TRUE; - buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb); + buffer_gbm = META_DRM_BUFFER_GBM (primary_gpu_fb); bo = meta_drm_buffer_gbm_get_bo (buffer_gbm); if (!meta_renderer_native_gles3_blit_shared_bo (egl, gles3, @@ -687,7 +672,7 @@ { g_warning ("Failed to blit shared framebuffer: %s", error->message); g_error_free (error); - return; + return NULL; } if (!meta_egl_swap_buffers (egl, @@ -697,7 +682,7 @@ { g_warning ("Failed to swap buffers: %s", error->message); g_error_free (error); - return; + return NULL; } use_modifiers = meta_renderer_native_use_modifiers (renderer_native); @@ -717,10 +702,10 @@ g_warning ("meta_drm_buffer_gbm_new_lock_front failed: %s", error->message); g_error_free (error); - return; + return NULL; } - secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); + return META_DRM_BUFFER (buffer_gbm); } static MetaDrmBufferDumb * @@ -735,7 +720,7 @@ return secondary_gpu_state->cpu.dumb_fbs[0]; } -static gboolean +static MetaDrmBuffer * copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscreen, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, const int *rectangles, @@ -761,13 +746,13 @@ if (!secondary_gpu_state || secondary_gpu_state->egl_surface == EGL_NO_SURFACE) - return FALSE; + return NULL; primary_gpu = meta_renderer_native_get_primary_gpu (renderer_native); primary_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, primary_gpu); if (!primary_gpu_data->secondary.has_EGL_EXT_image_dma_buf_import_modifiers) - return FALSE; + return NULL; buffer_dumb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state); buffer = META_DRM_BUFFER (buffer_dumb); @@ -790,7 +775,7 @@ { meta_topic (META_DEBUG_KMS, "Failed to create DMA buffer: %s", error->message); - return FALSE; + return NULL; } dmabuf_fb = @@ -808,7 +793,7 @@ meta_topic (META_DEBUG_KMS, "Failed to create DMA buffer for blitting: %s", error->message); - return FALSE; + return NULL; } /* Limit the number of individual copies to 16 */ #define MAX_RECTS 16 @@ -821,7 +806,7 @@ &error)) { g_object_unref (dmabuf_fb); - return FALSE; + return NULL; } } else @@ -838,20 +823,19 @@ &error)) { g_object_unref (dmabuf_fb); - return FALSE; + return NULL; } } } g_object_unref (dmabuf_fb); - g_set_object (&secondary_gpu_state->gbm.next_fb, buffer); secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb; - return TRUE; + return g_object_ref (buffer); } -static void +static MetaDrmBuffer * copy_shared_framebuffer_cpu (CoglOnscreen *onscreen, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, MetaRendererNativeGpuData *renderer_gpu_data) @@ -903,17 +887,19 @@ cogl_object_unref (dumb_bitmap); - g_set_object (&secondary_gpu_state->gbm.next_fb, buffer); secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb; + + return g_object_ref (buffer); } -static void +static MetaDrmBuffer * update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles) { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; + MetaDrmBuffer *copy = NULL; COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePreSwapBuffers, "Onscreen (secondary gpu pre-swap-buffers)"); @@ -939,10 +925,11 @@ /* prepare fallback */ G_GNUC_FALLTHROUGH; case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: - if (!copy_shared_framebuffer_primary_gpu (onscreen, - secondary_gpu_state, - rectangles, - n_rectangles)) + copy = copy_shared_framebuffer_primary_gpu (onscreen, + secondary_gpu_state, + rectangles, + n_rectangles); + if (!copy) { if (!secondary_gpu_state->noted_primary_gpu_copy_failed) { @@ -952,9 +939,9 @@ secondary_gpu_state->noted_primary_gpu_copy_failed = TRUE; } - copy_shared_framebuffer_cpu (onscreen, - secondary_gpu_state, - renderer_gpu_data); + copy = copy_shared_framebuffer_cpu (onscreen, + secondary_gpu_state, + renderer_gpu_data); } else if (!secondary_gpu_state->noted_primary_gpu_copy_ok) { @@ -966,11 +953,15 @@ break; } } + + return copy; } static void -update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, - gboolean *egl_context_changed) +update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, + gboolean *egl_context_changed, + MetaDrmBuffer *primary_gpu_fb, + MetaDrmBuffer **secondary_gpu_fb) { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); MetaRendererNative *renderer_native = onscreen_native->renderer_native; @@ -983,6 +974,7 @@ if (secondary_gpu_state) { MetaRendererNativeGpuData *renderer_gpu_data; + g_autoptr (MetaDrmBuffer) next_fb = NULL; renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, @@ -990,23 +982,30 @@ switch (renderer_gpu_data->secondary.copy_mode) { case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: - if (import_shared_framebuffer (onscreen, secondary_gpu_state)) + next_fb = import_shared_framebuffer (onscreen, + secondary_gpu_state, + primary_gpu_fb); + if (next_fb) break; - - /* The fallback was prepared in pre_swap_buffers */ + /* The fallback was prepared in pre_swap_buffers and is currently + * in secondary_gpu_fb. + */ renderer_gpu_data->secondary.copy_mode = META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY; G_GNUC_FALLTHROUGH; case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: - /* Done before eglSwapBuffers. */ + next_fb = g_object_ref (*secondary_gpu_fb); break; case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: - copy_shared_framebuffer_gpu (onscreen, - secondary_gpu_state, - renderer_gpu_data, - egl_context_changed); + next_fb = copy_shared_framebuffer_gpu (onscreen, + secondary_gpu_state, + renderer_gpu_data, + egl_context_changed, + primary_gpu_fb); break; } + + g_set_object (secondary_gpu_fb, next_fb); } } @@ -1040,34 +1039,39 @@ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; - MetaRenderer *renderer = META_RENDERER (renderer_native); - MetaBackend *backend = meta_renderer_get_backend (renderer); - MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); MetaGpuKms *render_gpu = onscreen_native->render_gpu; MetaDeviceFile *render_device_file; ClutterFrame *frame = user_data; CoglOnscreenClass *parent_class; gboolean egl_context_changed = FALSE; - MetaPowerSave power_save_mode; g_autoptr (GError) error = NULL; MetaDrmBufferFlags buffer_flags; MetaDrmBufferGbm *buffer_gbm; - MetaKmsCrtc *kms_crtc; - MetaKmsDevice *kms_device; - MetaKmsUpdateFlag flags; - g_autoptr (MetaKmsFeedback) kms_feedback = NULL; - const GError *feedback_error; + g_autoptr (MetaDrmBuffer) primary_gpu_fb = NULL; + g_autoptr (MetaDrmBuffer) secondary_gpu_fb = NULL; + size_t rectangles_size; COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, "Onscreen (swap-buffers)"); - update_secondary_gpu_state_pre_swap_buffers (onscreen, - rectangles, - n_rectangles); + if (meta_is_topic_enabled (META_DEBUG_KMS)) + { + unsigned int frames_pending = + 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, + n_rectangles); parent_class = COGL_ONSCREEN_CLASS (meta_onscreen_native_parent_class); parent_class->swap_buffers_with_damage (onscreen, @@ -1083,9 +1087,6 @@ switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: - g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); - g_clear_object (&onscreen_native->gbm.next_fb); - buffer_flags = META_DRM_BUFFER_FLAG_NONE; if (!meta_renderer_native_use_modifiers (renderer_native)) buffer_flags |= META_DRM_BUFFER_FLAG_DISABLE_MODIFIERS; @@ -1103,7 +1104,12 @@ return; } - onscreen_native->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); + primary_gpu_fb = META_DRM_BUFFER (g_steal_pointer (&buffer_gbm)); + + g_object_set_data_full (G_OBJECT (primary_gpu_fb), + "gbm_surface owner", + g_object_ref (onscreen), + (GDestroyNotify) g_object_unref); break; case META_RENDERER_NATIVE_MODE_SURFACELESS: @@ -1115,7 +1121,46 @@ #endif } - update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed); + update_secondary_gpu_state_post_swap_buffers (onscreen, + &egl_context_changed, + primary_gpu_fb, + &secondary_gpu_fb); + + switch (renderer_gpu_data->mode) + { + case META_RENDERER_NATIVE_MODE_GBM: + if (onscreen_native->gbm.next_fb != NULL) + { + g_warn_if_fail (onscreen_native->gbm.stalled_fb == NULL); + 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); + hold_primary_gpu_fb_for_secondary_gpu_scanout ( + onscreen_native->secondary_gpu_state, + primary_gpu_fb, + secondary_gpu_fb); + } + else + { + g_set_object (&onscreen_native->gbm.next_fb, primary_gpu_fb); + } + break; + case META_RENDERER_NATIVE_MODE_SURFACELESS: + break; +#ifdef HAVE_EGL_DEVICE + case META_RENDERER_NATIVE_MODE_EGL_DEVICE: + break; +#endif + } + + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); /* * If we changed EGL context, cogl will have the wrong idea about what is @@ -1126,23 +1171,71 @@ if (egl_context_changed) _cogl_winsys_egl_ensure_current (cogl_display); + rectangles_size = n_rectangles * 4 * sizeof (int); + onscreen_native->next_post.rectangles = + g_realloc (onscreen_native->next_post.rectangles, rectangles_size); + memcpy (onscreen_native->next_post.rectangles, rectangles, rectangles_size); + onscreen_native->next_post.n_rectangles = n_rectangles; + + onscreen_native->swaps_pending++; + 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); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + MetaPowerSave power_save_mode; + MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + MetaKmsUpdateFlag flags; + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; + const GError *feedback_error; + unsigned int frames_pending = cogl_onscreen_count_pending_frames (onscreen); + + if (onscreen_native->swaps_pending == 0) + return; + + g_assert (frames_pending >= onscreen_native->swaps_pending); + power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (power_save_mode == META_POWER_SAVE_ON) { + unsigned int posts_pending; + + posts_pending = frames_pending - onscreen_native->swaps_pending; + if (posts_pending > 0) + return; /* wait for the next frame notification and then try again */ + + drop_stalled_swap (onscreen); + g_return_if_fail (onscreen_native->swaps_pending > 0); + onscreen_native->swaps_pending--; + ensure_crtc_modes (onscreen); meta_onscreen_native_flip_crtc (onscreen, onscreen_native->view, onscreen_native->crtc, META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, - rectangles, - n_rectangles); + onscreen_native->next_post.rectangles, + onscreen_native->next_post.n_rectangles); } else { meta_renderer_native_queue_power_save_page_flip (renderer_native, onscreen); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } @@ -1160,9 +1253,6 @@ "Postponing primary plane composite update for CRTC %u (%s)", meta_kms_crtc_get_id (kms_crtc), meta_kms_device_get_path (kms_device)); - - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } else if (meta_renderer_native_has_pending_mode_set (renderer_native)) @@ -1172,8 +1262,6 @@ meta_renderer_native_notify_mode_sets_reset (renderer_native); meta_renderer_native_post_mode_set_updates (renderer_native); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } break; @@ -1186,8 +1274,6 @@ { meta_renderer_native_notify_mode_sets_reset (renderer_native); meta_renderer_native_post_mode_set_updates (renderer_native); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } break; @@ -1200,18 +1286,16 @@ meta_kms_device_get_path (kms_device)); flags = META_KMS_UPDATE_FLAG_NONE; - kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags); + kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms, + kms_crtc, + flags); + g_return_if_fail (kms_feedback != NULL); switch (meta_kms_feedback_get_result (kms_feedback)) { case META_KMS_FEEDBACK_PASSED: - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); break; case META_KMS_FEEDBACK_FAILED: - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - feedback_error = meta_kms_feedback_get_error (kms_feedback); if (!g_error_matches (feedback_error, G_IO_ERROR, @@ -1298,6 +1382,18 @@ 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); @@ -1350,7 +1446,9 @@ meta_kms_device_get_path (kms_device)); flags = META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR; - kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags); + kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms, + kms_crtc, + flags); switch (meta_kms_feedback_get_result (kms_feedback)) { case META_KMS_FEEDBACK_PASSED: @@ -1364,7 +1462,6 @@ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) break; - g_clear_object (&onscreen_native->gbm.next_fb); g_propagate_error (error, g_error_copy (feedback_error)); return FALSE; } @@ -1400,7 +1497,10 @@ g_autoptr (MetaKmsFeedback) kms_feedback = NULL; const GError *error; - kms_update = meta_kms_get_pending_update (kms, kms_device); + if (cogl_onscreen_count_pending_frames (onscreen) > 0) + return; + + kms_update = meta_kms_get_pending_update_for_crtc (kms, kms_crtc); if (!kms_update) { clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); @@ -1415,9 +1515,9 @@ g_object_unref); flags = META_KMS_UPDATE_FLAG_NONE; - kms_feedback = meta_kms_post_pending_update_sync (kms, - kms_device, - flags); + kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms, + kms_crtc, + flags); switch (meta_kms_feedback_get_result (kms_feedback)) { case META_KMS_FEEDBACK_PASSED: @@ -1439,6 +1539,17 @@ } } +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) { @@ -1987,6 +2098,21 @@ return DRM_FORMAT_INVALID; } +static void +dumb_toggle_notify (gpointer data, + GObject *object, + gboolean is_last_ref) +{ + MetaDrmBuffer **source_fb = data; + + g_return_if_fail (source_fb != NULL); + if (is_last_ref && *source_fb) + { + g_return_if_fail (META_IS_DRM_BUFFER (*source_fb)); + g_clear_object (source_fb); + } +} + static gboolean init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_native, CoglOnscreen *onscreen, @@ -2043,6 +2169,12 @@ } secondary_gpu_state->cpu.dumb_fbs[i] = META_DRM_BUFFER_DUMB (dumb_buffer); + g_object_add_toggle_ref (G_OBJECT (dumb_buffer), + dumb_toggle_notify, + &secondary_gpu_state->cpu.source_fbs[i]); + + /* It was incremented higher than we need by add_toggle_ref */ + g_object_unref (dumb_buffer); } /* @@ -2132,7 +2264,7 @@ onscreen_native->renderer_native = renderer_native; onscreen_native->render_gpu = render_gpu; onscreen_native->output = output; - onscreen_native->crtc = crtc; + onscreen_native->crtc = g_object_ref (crtc); return onscreen_native; } @@ -2153,7 +2285,6 @@ { 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 (); @@ -2181,9 +2312,12 @@ G_OBJECT_CLASS (meta_onscreen_native_parent_class)->dispose (object); + g_clear_object (&onscreen_native->crtc); g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy); g_clear_pointer (&onscreen_native->secondary_gpu_state, secondary_gpu_state_free); + g_clear_pointer (&onscreen_native->next_post.rectangles, g_free); + onscreen_native->next_post.n_rectangles = 0; } static void Index: cogl/cogl/cogl-onscreen-private.h IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h --- a/cogl/cogl/cogl-onscreen-private.h (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/cogl/cogl/cogl-onscreen-private.h (date 1665619526455) @@ -97,4 +97,7 @@ COGL_EXPORT CoglFrameInfo * cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen); +COGL_EXPORT unsigned int +cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen); + #endif /* __COGL_ONSCREEN_PRIVATE_H */ Index: src/backends/native/meta-kms-impl-device-simple.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c --- a/src/backends/native/meta-kms-impl-device-simple.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms-impl-device-simple.c (date 1665619599467) @@ -486,6 +486,8 @@ return FALSE; } + meta_kms_crtc_on_scanout_started (crtc); + if (drm_mode) { g_hash_table_replace (impl_device_simple->cached_mode_sets, @@ -550,7 +552,7 @@ typedef struct _RetryPageFlipData { MetaKmsCrtc *crtc; - uint32_t fb_id; + MetaDrmBuffer *fb; MetaKmsPageFlipData *page_flip_data; float refresh_rate; uint64_t retry_time_us; @@ -563,6 +565,7 @@ 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); } @@ -630,16 +633,21 @@ } 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); } @@ -726,7 +734,7 @@ 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) @@ -741,7 +749,7 @@ 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, @@ -875,6 +883,8 @@ return FALSE; } + meta_kms_crtc_on_scanout_started (crtc); + if (!impl_device_simple->mode_set_fallback_feedback_source) { GSource *source; @@ -999,20 +1009,20 @@ 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)); @@ -1302,7 +1312,7 @@ { 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, @@ -1316,7 +1326,7 @@ } else { - return TRUE; + goto assigned; } case META_KMS_PLANE_TYPE_OVERLAY: error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, @@ -1329,6 +1339,12 @@ } g_assert_not_reached (); + +assigned: + meta_kms_crtc_remember_plane_buffer (plane_assignment->crtc, + meta_kms_plane_get_id (plane), + plane_assignment->buffer); + return TRUE; } static gboolean Index: src/backends/meta-stage-impl.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c --- a/src/backends/meta-stage-impl.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/meta-stage-impl.c (date 1665619599521) @@ -721,6 +721,8 @@ { 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, Index: src/backends/native/meta-kms-impl-device.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c --- a/src/backends/native/meta-kms-impl-device.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms-impl-device.c (date 1665619599447) @@ -1206,8 +1206,12 @@ void meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device) { + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); + g_list_foreach (priv->crtcs, (GFunc) meta_kms_crtc_release_buffers, NULL); + if (klass->prepare_shutdown) klass->prepare_shutdown (impl_device); Index: src/backends/native/meta-kms-page-flip.c IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/backends/native/meta-kms-page-flip.c b/src/backends/native/meta-kms-page-flip.c --- a/src/backends/native/meta-kms-page-flip.c (revision bdf3470a8a89206a39dc45824a8080dc6a9666ca) +++ b/src/backends/native/meta-kms-page-flip.c (date 1665619599471) @@ -25,6 +25,7 @@ #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-private.h" #include "backends/native/meta-kms-update.h" +#include "backends/native/meta-kms-crtc.h" typedef struct _MetaKmsPageFlipClosure { @@ -150,6 +151,8 @@ meta_assert_not_in_kms_impl (kms); + meta_kms_crtc_on_scanout_started (page_flip_data->crtc); + for (l = page_flip_data->closures; l; l = l->next) { MetaKmsPageFlipClosure *closure = l->data;