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);