From 56260e3e0726c970c572a339e18b1f6ebed3069c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Sun, 30 May 2021 15:00:13 +0200 Subject: [PATCH] wayland/surface: Make sure transactions are applied in consistent order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If multiple transactions have entries for the same surface, they are applied in the same order as they were committed. Otherwise, they can be applied in any order. This is preparation for following changes, transactions are still applied as soon as they're committed. v2: * Move GQueue for transactions to MetaWaylandCompositor (Jonas Ådahl) v3 * Say "entry for" instead of "state for", since there can be transaction entries with no state (for surfaces which are getting destroyed). v4: * Use a hash table to keep track of all candidate transactions which might be newly ready to be applied. * Use clearer function / variable names. v5: * Use custom single-linked list instead of hash table for candidate transactions, ordered by the transaction commit sequence number, so that they're attempted to be applied in the same order as they were committed. * Rename transaction->queue to transaction->committed_queue, and simplify its handling. v6: (Carlos Garnacho) * Add spaces between type casts and values. * Use (gpointer *) instead of (void**). v7: (Jonas Ådahl) * Use G_MAXSIZE instead of ULONG_MAX. * Fix indentation of meta_wayland_transaction_apply & meta_wayland_transaction_maybe_apply_one parameters. * Refactor find_next_transaction_for_surface & ensure_next_candidate helper functions out of meta_wayland_transaction_apply. * Refactor has_unapplied_dependencies helper function out of meta_wayland_transaction_maybe_apply_one. * Make while (TRUE) loop in meta_wayland_transaction_maybe_apply consistent with general usage. * Drop unused value local from meta_wayland_transaction_commit. * Store pointer to compositor object in transactions, instead of pointer to the queue of committed transactions. * Drop tautological g_assert from meta_wayland_transaction_apply. (me) Part-of: --- src/wayland/meta-wayland-private.h | 6 + src/wayland/meta-wayland-surface.h | 7 + src/wayland/meta-wayland-transaction.c | 174 ++++++++++++++++++++++++- src/wayland/meta-wayland-transaction.h | 6 +- src/wayland/meta-wayland.c | 10 ++ src/wayland/meta-wayland.h | 2 + 6 files changed, 201 insertions(+), 4 deletions(-) diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h index dc4dba4b4..c9a236a2e 100644 --- a/src/wayland/meta-wayland-private.h +++ b/src/wayland/meta-wayland-private.h @@ -100,6 +100,12 @@ struct _MetaWaylandCompositor MetaWaylandPresentationTime presentation_time; MetaWaylandDmaBufManager *dma_buf_manager; + + /* + * Queue of transactions which have been committed but not applied yet, in the + * order they were committed. + */ + GQueue committed_transactions; }; #define META_TYPE_WAYLAND_COMPOSITOR (meta_wayland_compositor_get_type ()) diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index e0f7544c5..56bcbb189 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -254,6 +254,13 @@ struct _MetaWaylandSurface /* dma-buf feedback */ MetaCrtc *scanout_candidate; + + /* Transactions */ + struct { + /* First & last committed transaction which has an entry for this surface */ + MetaWaylandTransaction *first_committed; + MetaWaylandTransaction *last_committed; + } transaction; }; void meta_wayland_shell_init (MetaWaylandCompositor *compositor); diff --git a/src/wayland/meta-wayland-transaction.c b/src/wayland/meta-wayland-transaction.c index 37af40fa9..96a0b18bc 100644 --- a/src/wayland/meta-wayland-transaction.c +++ b/src/wayland/meta-wayland-transaction.c @@ -23,10 +23,18 @@ #include "wayland/meta-wayland-transaction.h" +#include "wayland/meta-wayland.h" #include "wayland/meta-wayland-subsurface.h" +#define META_WAYLAND_TRANSACTION_NONE ((void *)(uintptr_t) G_MAXSIZE) + struct _MetaWaylandTransaction { + GList node; + MetaWaylandCompositor *compositor; + MetaWaylandTransaction *next_candidate; + uint64_t committed_sequence; + GHashTable *entries; }; @@ -99,8 +107,46 @@ meta_wayland_transaction_compare (const void *key1, meta_wayland_surface_get_toplevel (surface2)) ? -1 : 1; } -void -meta_wayland_transaction_commit (MetaWaylandTransaction *transaction) +static MetaWaylandTransaction * +find_next_transaction_for_surface (MetaWaylandTransaction *transaction, + MetaWaylandSurface *surface) +{ + GList *node; + + for (node = transaction->node.next; node; node = node->next) + { + MetaWaylandTransaction *next = node->data; + + if (surface->transaction.last_committed == next || + g_hash_table_contains (next->entries, surface)) + return next; + } + + return NULL; +} + +static void +ensure_next_candidate (MetaWaylandTransaction *transaction, + MetaWaylandTransaction **first_candidate) +{ + MetaWaylandTransaction **candidate; + + if (transaction->next_candidate) + return; + + candidate = first_candidate; + while (*candidate != META_WAYLAND_TRANSACTION_NONE && + (*candidate)->committed_sequence < + transaction->committed_sequence) + candidate = &(*candidate)->next_candidate; + + transaction->next_candidate = *candidate; + *candidate = transaction; +} + +static void +meta_wayland_transaction_apply (MetaWaylandTransaction *transaction, + MetaWaylandTransaction **first_candidate) { g_autofree MetaWaylandSurface **surfaces = NULL; unsigned int num_surfaces; @@ -121,6 +167,23 @@ meta_wayland_transaction_commit (MetaWaylandTransaction *transaction) entry = meta_wayland_transaction_get_entry (transaction, surface); meta_wayland_surface_apply_state (surface, entry->state); + + if (surface->transaction.last_committed == transaction) + { + surface->transaction.first_committed = NULL; + surface->transaction.last_committed = NULL; + } + else + { + MetaWaylandTransaction *next_transaction; + + next_transaction = find_next_transaction_for_surface (transaction, surface); + if (next_transaction) + { + surface->transaction.first_committed = next_transaction; + ensure_next_candidate (next_transaction, first_candidate); + } + } } /* Synchronize child states from descendants to ancestors */ @@ -130,6 +193,81 @@ meta_wayland_transaction_commit (MetaWaylandTransaction *transaction) meta_wayland_transaction_free (transaction); } +static gboolean +has_unapplied_dependencies (MetaWaylandTransaction *transaction) +{ + GHashTableIter iter; + MetaWaylandSurface *surface; + + g_hash_table_iter_init (&iter, transaction->entries); + while (g_hash_table_iter_next (&iter, (gpointer *) &surface, NULL)) + { + if (surface->transaction.first_committed != transaction) + return TRUE; + } + + return FALSE; +} + +static void +meta_wayland_transaction_maybe_apply_one (MetaWaylandTransaction *transaction, + MetaWaylandTransaction **first_candidate) +{ + if (has_unapplied_dependencies (transaction)) + return; + + meta_wayland_transaction_apply (transaction, first_candidate); +} + +static void +meta_wayland_transaction_maybe_apply (MetaWaylandTransaction *transaction) +{ + MetaWaylandTransaction *first_candidate = META_WAYLAND_TRANSACTION_NONE; + + while (TRUE) + { + meta_wayland_transaction_maybe_apply_one (transaction, &first_candidate); + + if (first_candidate == META_WAYLAND_TRANSACTION_NONE) + return; + + transaction = first_candidate; + first_candidate = transaction->next_candidate; + transaction->next_candidate = NULL; + } +} + +void +meta_wayland_transaction_commit (MetaWaylandTransaction *transaction) +{ + static uint64_t committed_sequence; + GQueue *committed_queue; + gboolean maybe_apply = TRUE; + GHashTableIter iter; + MetaWaylandSurface *surface; + + transaction->committed_sequence = ++committed_sequence; + transaction->node.data = transaction; + + committed_queue = + meta_wayland_compositor_get_committed_transactions (transaction->compositor); + g_queue_push_tail_link (committed_queue, &transaction->node); + + g_hash_table_iter_init (&iter, transaction->entries); + while (g_hash_table_iter_next (&iter, (gpointer *) &surface, NULL)) + { + surface->transaction.last_committed = transaction; + + if (!surface->transaction.first_committed) + surface->transaction.first_committed = transaction; + else + maybe_apply = FALSE; + } + + if (maybe_apply) + meta_wayland_transaction_maybe_apply (transaction); +} + static MetaWaylandTransactionEntry * meta_wayland_transaction_ensure_entry (MetaWaylandTransaction *transaction, MetaWaylandSurface *surface) @@ -166,12 +304,13 @@ meta_wayland_transaction_entry_free (MetaWaylandTransactionEntry *entry) } MetaWaylandTransaction * -meta_wayland_transaction_new (void) +meta_wayland_transaction_new (MetaWaylandCompositor *compositor) { MetaWaylandTransaction *transaction; transaction = g_new0 (MetaWaylandTransaction, 1); + transaction->compositor = compositor; transaction->entries = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_wayland_transaction_entry_free); @@ -181,6 +320,35 @@ meta_wayland_transaction_new (void) void meta_wayland_transaction_free (MetaWaylandTransaction *transaction) { + if (transaction->node.data) + { + GQueue *committed_queue = + meta_wayland_compositor_get_committed_transactions (transaction->compositor); + + g_queue_unlink (committed_queue, &transaction->node); + } + g_hash_table_destroy (transaction->entries); g_free (transaction); } + +void +meta_wayland_transaction_finalize (MetaWaylandCompositor *compositor) +{ + GQueue *transactions; + MetaWaylandTransaction *transaction; + + transactions = meta_wayland_compositor_get_committed_transactions (compositor); + + while ((transaction = g_queue_pop_head (transactions))) + meta_wayland_transaction_free (transaction); +} + +void +meta_wayland_transaction_init (MetaWaylandCompositor *compositor) +{ + GQueue *transactions; + + transactions = meta_wayland_compositor_get_committed_transactions (compositor); + g_queue_init (transactions); +} diff --git a/src/wayland/meta-wayland-transaction.h b/src/wayland/meta-wayland-transaction.h index 62c0f388d..230548c19 100644 --- a/src/wayland/meta-wayland-transaction.h +++ b/src/wayland/meta-wayland-transaction.h @@ -28,8 +28,12 @@ void meta_wayland_transaction_add_state (MetaWaylandTransaction *transaction, MetaWaylandSurface *surface, MetaWaylandSurfaceState *state); -MetaWaylandTransaction *meta_wayland_transaction_new (void); +MetaWaylandTransaction *meta_wayland_transaction_new (MetaWaylandCompositor *compositor); void meta_wayland_transaction_free (MetaWaylandTransaction *transaction); +void meta_wayland_transaction_finalize (MetaWaylandCompositor *compositor); + +void meta_wayland_transaction_init (MetaWaylandCompositor *compositor); + #endif diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index 4227d546b..1db0838bb 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -46,6 +46,7 @@ #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-subsurface.h" #include "wayland/meta-wayland-tablet-manager.h" +#include "wayland/meta-wayland-transaction.h" #include "wayland/meta-wayland-xdg-foreign.h" #include "wayland/meta-xwayland-grab-keyboard.h" #include "wayland/meta-xwayland-private.h" @@ -375,6 +376,12 @@ meta_wayland_compositor_remove_presentation_feedback_surface (MetaWaylandComposi g_list_remove (compositor->presentation_time.feedback_surfaces, surface); } +GQueue * +meta_wayland_compositor_get_committed_transactions (MetaWaylandCompositor *compositor) +{ + return &compositor->committed_transactions; +} + static void set_gnome_env (const char *name, const char *value) @@ -462,6 +469,8 @@ meta_wayland_compositor_finalize (GObject *object) g_signal_handlers_disconnect_by_func (stage, on_after_update, compositor); g_signal_handlers_disconnect_by_func (stage, on_presented, compositor); + meta_wayland_transaction_finalize (compositor); + g_clear_object (&compositor->dma_buf_manager); g_clear_pointer (&compositor->seat, meta_wayland_seat_free); @@ -637,6 +646,7 @@ meta_wayland_compositor_new (MetaContext *context) meta_wayland_text_input_init (compositor); meta_wayland_init_presentation_time (compositor); meta_wayland_activation_init (compositor); + meta_wayland_transaction_init (compositor); /* Xwayland specific protocol, needs to be filtered out for all other clients */ if (meta_xwayland_grab_keyboard_init (compositor)) diff --git a/src/wayland/meta-wayland.h b/src/wayland/meta-wayland.h index 901faaed1..36b7f40ec 100644 --- a/src/wayland/meta-wayland.h +++ b/src/wayland/meta-wayland.h @@ -70,6 +70,8 @@ void meta_wayland_compositor_add_presentation_feedback_surfac void meta_wayland_compositor_remove_presentation_feedback_surface (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface); +GQueue *meta_wayland_compositor_get_committed_transactions (MetaWaylandCompositor *compositor); + META_EXPORT_TEST const char *meta_wayland_get_wayland_display_name (MetaWaylandCompositor *compositor);