wayland: Add timed transactions

Allow a transaction to have a timing constraint.

Any transaction with a timing constraint will be deferred at its initial
commit, without testing the target time. The timing constraints are cleared
later in an on_before handler immediately before repaint.

The new frame clock api to schedule later ticks is use to ensure we get an
appropraitely timed tick to clear the constraint.

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3355>
This commit is contained in:
Derek Foreman 2024-10-07 06:15:32 -05:00
parent 5b214dc2b7
commit a645659440
6 changed files with 138 additions and 0 deletions

View File

@ -115,6 +115,9 @@ struct _MetaWaylandCompositor
* order they were committed. * order they were committed.
*/ */
GQueue committed_transactions; GQueue committed_transactions;
/* Transactions with time constraints. */
GQueue *timed_transactions;
}; };
gboolean meta_wayland_compositor_is_egl_display_bound (MetaWaylandCompositor *compositor); gboolean meta_wayland_compositor_is_egl_display_bound (MetaWaylandCompositor *compositor);

View File

@ -138,6 +138,9 @@ struct _MetaWaylandSurfaceState
gboolean has_new_color_state; gboolean has_new_color_state;
ClutterColorState *color_state; ClutterColorState *color_state;
gboolean has_target_time;
int64_t target_time_us;
}; };
struct _MetaWaylandDragDestFuncs struct _MetaWaylandDragDestFuncs

View File

@ -45,6 +45,8 @@ struct _MetaWaylandTransaction
/* Sources for buffers which are not ready yet */ /* Sources for buffers which are not ready yet */
GHashTable *buf_sources; GHashTable *buf_sources;
int64_t target_presentation_time_us;
}; };
struct _MetaWaylandTransactionEntry struct _MetaWaylandTransactionEntry
@ -60,6 +62,12 @@ struct _MetaWaylandTransactionEntry
int y; int y;
}; };
int64_t
meta_wayland_transaction_get_target_presentation_time_us (const MetaWaylandTransaction *transaction)
{
return transaction->target_presentation_time_us;
}
static MetaWaylandTransactionEntry * static MetaWaylandTransactionEntry *
meta_wayland_transaction_get_entry (MetaWaylandTransaction *transaction, meta_wayland_transaction_get_entry (MetaWaylandTransaction *transaction,
MetaWaylandSurface *surface) MetaWaylandSurface *surface)
@ -241,6 +249,9 @@ has_dependencies (MetaWaylandTransaction *transaction)
GHashTableIter iter; GHashTableIter iter;
MetaWaylandSurface *surface; MetaWaylandSurface *surface;
if (transaction->target_presentation_time_us)
return TRUE;
if (transaction->buf_sources && if (transaction->buf_sources &&
g_hash_table_size (transaction->buf_sources) > 0) g_hash_table_size (transaction->buf_sources) > 0)
return TRUE; return TRUE;
@ -283,6 +294,20 @@ meta_wayland_transaction_maybe_apply (MetaWaylandTransaction *transaction)
} }
} }
gboolean
meta_wayland_transaction_unblock_timed (MetaWaylandTransaction *transaction,
int64_t target_time_us)
{
if (target_time_us < transaction->target_presentation_time_us)
return FALSE;
transaction->target_presentation_time_us = 0;
meta_wayland_transaction_maybe_apply (transaction);
return TRUE;
}
static void static void
meta_wayland_transaction_dma_buf_dispatch (MetaWaylandBuffer *buffer, meta_wayland_transaction_dma_buf_dispatch (MetaWaylandBuffer *buffer,
gpointer user_data) gpointer user_data)
@ -389,6 +414,8 @@ meta_wayland_transaction_commit (MetaWaylandTransaction *transaction)
g_autoptr (GPtrArray) placement_states = NULL; g_autoptr (GPtrArray) placement_states = NULL;
unsigned int num_placement_states = 0; unsigned int num_placement_states = 0;
int i; int i;
gint64 max_time_us = 0;
MetaWaylandSurface *max_time_surface = NULL;
g_hash_table_iter_init (&iter, transaction->entries); g_hash_table_iter_init (&iter, transaction->entries);
while (g_hash_table_iter_next (&iter, while (g_hash_table_iter_next (&iter,
@ -413,6 +440,13 @@ meta_wayland_transaction_commit (MetaWaylandTransaction *transaction)
g_ptr_array_add (placement_states, entry->state); g_ptr_array_add (placement_states, entry->state);
num_placement_states++; num_placement_states++;
} }
if (entry->state->has_target_time &&
entry->state->target_time_us > max_time_us)
{
max_time_us = entry->state->target_time_us;
max_time_surface = surface;
}
} }
} }
@ -425,6 +459,23 @@ meta_wayland_transaction_commit (MetaWaylandTransaction *transaction)
placement_state); placement_state);
} }
/* If we have a time constraint, we always defer application until just before the
* appropriate frame clock tick.
*/
if (max_time_us)
{
MetaSurfaceActor *actor = meta_wayland_surface_get_actor (max_time_surface);
ClutterFrameClock *frame_clock =
clutter_actor_pick_frame_clock (CLUTTER_ACTOR (actor), NULL);
if (frame_clock)
{
maybe_apply = FALSE;
transaction->target_presentation_time_us = max_time_us;
meta_wayland_compositor_add_timed_transaction (transaction->compositor, transaction);
clutter_frame_clock_add_future_time (frame_clock, max_time_us);
}
}
transaction->committed_sequence = ++committed_sequence; transaction->committed_sequence = ++committed_sequence;
transaction->node.data = transaction; transaction->node.data = transaction;

View File

@ -52,3 +52,8 @@ void meta_wayland_transaction_free (MetaWaylandTransaction *transaction);
void meta_wayland_transaction_finalize (MetaWaylandCompositor *compositor); void meta_wayland_transaction_finalize (MetaWaylandCompositor *compositor);
void meta_wayland_transaction_init (MetaWaylandCompositor *compositor); void meta_wayland_transaction_init (MetaWaylandCompositor *compositor);
int64_t meta_wayland_transaction_get_target_presentation_time_us (const MetaWaylandTransaction *transaction);
gboolean meta_wayland_transaction_unblock_timed (MetaWaylandTransaction *transaction,
int64_t target_time_us);

View File

@ -357,6 +357,36 @@ ensure_source_for_stage_view (MetaWaylandCompositor *compositor,
} }
#endif /* HAVE_NATIVE_BACKEND */ #endif /* HAVE_NATIVE_BACKEND */
static void
clear_time_constraints_for_stage_view_transactions (MetaWaylandCompositor *compositor,
ClutterStageView *stage_view,
int64_t target_time_us)
{
MetaWaylandTransaction *transaction;
while ((transaction = g_queue_peek_head (compositor->timed_transactions)))
{
if (meta_wayland_transaction_unblock_timed (transaction, target_time_us))
g_queue_pop_head (compositor->timed_transactions);
else
break;
}
}
static void
on_before_update (ClutterStage *stage,
ClutterStageView *stage_view,
ClutterFrame *frame,
MetaWaylandCompositor *compositor)
{
int64_t target_time_us;
if (!clutter_frame_get_target_presentation_time (frame, &target_time_us))
target_time_us = g_get_monotonic_time ();
clear_time_constraints_for_stage_view_transactions (compositor, stage_view, target_time_us);
}
static void static void
on_after_update (ClutterStage *stage, on_after_update (ClutterStage *stage,
ClutterStageView *stage_view, ClutterStageView *stage_view,
@ -605,6 +635,41 @@ meta_wayland_compositor_remove_presentation_feedback_surface (MetaWaylandComposi
g_list_remove (compositor->presentation_time.feedback_surfaces, surface); g_list_remove (compositor->presentation_time.feedback_surfaces, surface);
} }
static int
compare_transaction_times (const MetaWaylandTransaction *a,
const MetaWaylandTransaction *b,
void *data)
{
int64_t target_time_us_a = meta_wayland_transaction_get_target_presentation_time_us (a);
int64_t target_time_us_b = meta_wayland_transaction_get_target_presentation_time_us (b);
if (target_time_us_a > target_time_us_b)
return 1;
if (target_time_us_a < target_time_us_b)
return -1;
return 0;
}
void
meta_wayland_compositor_add_timed_transaction (MetaWaylandCompositor *compositor,
MetaWaylandTransaction *transaction)
{
if (g_queue_find (compositor->timed_transactions, transaction))
return;
g_queue_insert_sorted (compositor->timed_transactions, transaction,
(GCompareDataFunc)compare_transaction_times, NULL);
}
void
meta_wayland_compositor_remove_timed_transaction (MetaWaylandCompositor *compositor,
MetaWaylandTransaction *transaction)
{
g_queue_remove (compositor->timed_transactions, transaction);
}
GQueue * GQueue *
meta_wayland_compositor_get_committed_transactions (MetaWaylandCompositor *compositor) meta_wayland_compositor_get_committed_transactions (MetaWaylandCompositor *compositor)
{ {
@ -716,6 +781,8 @@ meta_wayland_compositor_finalize (GObject *object)
g_clear_pointer (&compositor->wayland_display, wl_display_destroy); g_clear_pointer (&compositor->wayland_display, wl_display_destroy);
g_clear_pointer (&compositor->source, g_source_destroy); g_clear_pointer (&compositor->source, g_source_destroy);
g_queue_free (compositor->timed_transactions);
G_OBJECT_CLASS (meta_wayland_compositor_parent_class)->finalize (object); G_OBJECT_CLASS (meta_wayland_compositor_parent_class)->finalize (object);
} }
@ -741,6 +808,7 @@ meta_wayland_compositor_init (MetaWaylandCompositor *compositor)
priv->frame_callback_sources = priv->frame_callback_sources =
g_hash_table_new_full (NULL, NULL, NULL, g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) g_source_destroy); (GDestroyNotify) g_source_destroy);
compositor->timed_transactions = g_queue_new ();
} }
static void static void
@ -854,6 +922,8 @@ meta_wayland_compositor_new (MetaContext *context)
compositor->source = wayland_event_source; compositor->source = wayland_event_source;
g_source_unref (wayland_event_source); g_source_unref (wayland_event_source);
g_signal_connect (stage, "before-update",
G_CALLBACK (on_before_update), compositor);
g_signal_connect (stage, "after-update", g_signal_connect (stage, "after-update",
G_CALLBACK (on_after_update), compositor); G_CALLBACK (on_after_update), compositor);
g_signal_connect (stage, "presented", g_signal_connect (stage, "presented",

View File

@ -63,6 +63,12 @@ void meta_wayland_compositor_add_presentation_feedback_surfac
void meta_wayland_compositor_remove_presentation_feedback_surface (MetaWaylandCompositor *compositor, void meta_wayland_compositor_remove_presentation_feedback_surface (MetaWaylandCompositor *compositor,
MetaWaylandSurface *surface); MetaWaylandSurface *surface);
void meta_wayland_compositor_add_timed_transaction (MetaWaylandCompositor *compositor,
MetaWaylandTransaction *transaction);
void meta_wayland_compositor_remove_timed_transaction (MetaWaylandCompositor *compositor,
MetaWaylandTransaction *transaction);
GQueue *meta_wayland_compositor_get_committed_transactions (MetaWaylandCompositor *compositor); GQueue *meta_wayland_compositor_get_committed_transactions (MetaWaylandCompositor *compositor);
META_EXPORT_TEST META_EXPORT_TEST