From fd39ca9f2096aea7d59fd9e4a774a05d6c8cb7d4 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 24 Apr 2024 11:51:45 -0500 Subject: [PATCH] wayland: Add support for FIFO commits New protocol that allows a client to require that a previously flagged content update must be presented before a content update can be applied. Signed-off-by: Derek Foreman Part-of: --- src/meson.build | 2 + src/wayland/meta-wayland-actor-surface.c | 3 +- src/wayland/meta-wayland-fifo.c | 191 +++++++++++++++++++++ src/wayland/meta-wayland-fifo.h | 25 +++ src/wayland/meta-wayland-private.h | 3 + src/wayland/meta-wayland-surface-private.h | 5 + src/wayland/meta-wayland-surface.c | 23 ++- src/wayland/meta-wayland-transaction.c | 41 ++++- src/wayland/meta-wayland-transaction.h | 4 + src/wayland/meta-wayland-versions.h | 1 + src/wayland/meta-wayland.c | 54 ++++++ src/wayland/meta-wayland.h | 5 + src/wayland/meta-window-wayland.c | 1 + 13 files changed, 355 insertions(+), 3 deletions(-) create mode 100644 src/wayland/meta-wayland-fifo.c create mode 100644 src/wayland/meta-wayland-fifo.h diff --git a/src/meson.build b/src/meson.build index 9d187b294..1702433f1 100644 --- a/src/meson.build +++ b/src/meson.build @@ -607,6 +607,7 @@ if have_wayland 'wayland/meta-wayland-color-management.c', 'wayland/meta-wayland-color-management.h', 'wayland/meta-wayland-commit-timing.c', + 'wayland/meta-wayland-fifo.c', 'wayland/meta-wayland-cursor-surface.c', 'wayland/meta-wayland-cursor-surface.h', 'wayland/meta-wayland-data-device.c', @@ -1130,6 +1131,7 @@ if have_wayland ['color-management-v1', 'private', ], ['session-management-v1', 'private', ], ['commit-timing', 'staging', 'v1', ], + ['fifo', 'staging', 'v1', ], ] if have_wayland_eglstream wayland_eglstream_protocols_dir = wayland_eglstream_protocols_dep.get_variable('pkgdatadir') diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c index 9a4502411..d9b7beae0 100644 --- a/src/wayland/meta-wayland-actor-surface.c +++ b/src/wayland/meta-wayland-actor-surface.c @@ -316,7 +316,8 @@ meta_wayland_actor_surface_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); - if (priv->actor && !wl_list_empty (&pending->frame_callback_list)) + if (priv->actor && + (!wl_list_empty (&pending->frame_callback_list) || pending->fifo_wait)) meta_surface_actor_schedule_update (priv->actor); meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); diff --git a/src/wayland/meta-wayland-fifo.c b/src/wayland/meta-wayland-fifo.c new file mode 100644 index 000000000..63b663e5a --- /dev/null +++ b/src/wayland/meta-wayland-fifo.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2023 Valve Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "config.h" + +#include + +#include "fifo-v1-server-protocol.h" +#include "wayland/meta-wayland-fifo.h" +#include "wayland/meta-wayland-private.h" +#include "wayland/meta-wayland-versions.h" + +typedef struct _MetaWaylandFifoSurface +{ + MetaWaylandSurface *surface; + gulong destroy_handler_id; +} MetaWaylandFifoSurface; + +static void +fifo_destructor (struct wl_resource *resource) +{ + MetaWaylandFifoSurface *fifo = wl_resource_get_user_data (resource); + + if (fifo->surface) + { + g_object_set_data (G_OBJECT (fifo->surface), "-meta-wayland-fifo", NULL); + + g_clear_signal_handler (&fifo->destroy_handler_id, fifo->surface); + } + + g_free (fifo); +} + +static void +fifo_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +set_barrier (struct wl_client *client, + struct wl_resource *resource) +{ + MetaWaylandFifoSurface *fifo = wl_resource_get_user_data (resource); + MetaWaylandSurface *surface = fifo->surface; + MetaWaylandSurfaceState *pending; + + if (!surface) + { + wl_resource_post_error (resource, + WP_FIFO_V1_ERROR_SURFACE_DESTROYED, + "surface destroyed"); + return; + } + + pending = meta_wayland_surface_get_pending_state (surface); + pending->fifo_barrier = TRUE; +} + +static void +wait_barrier (struct wl_client *client, + struct wl_resource *resource) +{ + MetaWaylandFifoSurface *fifo = wl_resource_get_user_data (resource); + MetaWaylandSurface *surface = fifo->surface; + MetaWaylandSurfaceState *pending; + + if (!surface) + { + wl_resource_post_error (resource, + WP_FIFO_V1_ERROR_SURFACE_DESTROYED, + "surface destroyed"); + return; + } + + pending = meta_wayland_surface_get_pending_state (surface); + pending->fifo_wait = TRUE; +} + +static const struct wp_fifo_v1_interface meta_wayland_fifo_interface = +{ + set_barrier, + wait_barrier, + fifo_destroy, +}; + +static void +fifo_manager_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +on_surface_destroyed (MetaWaylandSurface *surface, + MetaWaylandFifoSurface *fifo) +{ + fifo->surface = NULL; +} + +static void +fifo_manager_get_queue (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource) +{ + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + MetaWaylandFifoSurface *fifo; + struct wl_resource *fifo_resource; + + fifo = g_object_get_data (G_OBJECT (surface), "-meta-wayland-fifo"); + + if (fifo) + { + wl_resource_post_error (resource, + WP_FIFO_MANAGER_V1_ERROR_ALREADY_EXISTS, + "Fifo resource already exists on surface"); + return; + } + + fifo_resource = wl_resource_create (client, + &wp_fifo_v1_interface, + wl_resource_get_version (resource), + id); + + fifo = g_new0 (MetaWaylandFifoSurface, 1); + fifo->surface = surface; + + fifo->destroy_handler_id = + g_signal_connect (surface, + "destroy", + G_CALLBACK (on_surface_destroyed), + fifo); + + g_object_set_data (G_OBJECT (surface), "-meta-wayland-fifo", fifo); + + wl_resource_set_implementation (fifo_resource, + &meta_wayland_fifo_interface, + fifo, + fifo_destructor); +} + +static const struct wp_fifo_manager_v1_interface meta_wayland_fifo_manager_interface = +{ + fifo_manager_destroy, + fifo_manager_get_queue, +}; + +static void +bind_fifo (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, + &wp_fifo_manager_v1_interface, + version, id); + + wl_resource_set_implementation (resource, + &meta_wayland_fifo_manager_interface, + NULL, NULL); +} + +void +meta_wayland_fifo_init (MetaWaylandCompositor *compositor) +{ + if (wl_global_create (compositor->wayland_display, + &wp_fifo_manager_v1_interface, + META_WP_FIFO_V1_VERSION, + NULL, + bind_fifo) == NULL) + g_error ("Failed to register a global fifo object"); +} diff --git a/src/wayland/meta-wayland-fifo.h b/src/wayland/meta-wayland-fifo.h new file mode 100644 index 000000000..10e41f86b --- /dev/null +++ b/src/wayland/meta-wayland-fifo.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 Valve Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#pragma once + +#include + +#include "wayland/meta-wayland-types.h" + +void meta_wayland_fifo_init (MetaWaylandCompositor *compositor); diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h index 7eed4b55a..3025728c3 100644 --- a/src/wayland/meta-wayland-private.h +++ b/src/wayland/meta-wayland-private.h @@ -118,6 +118,9 @@ struct _MetaWaylandCompositor /* Transactions with time constraints. */ GQueue *timed_transactions; + + /* Surfaces with fifo barriers. */ + GList *barrier_surfaces; }; gboolean meta_wayland_compositor_is_egl_display_bound (MetaWaylandCompositor *compositor); diff --git a/src/wayland/meta-wayland-surface-private.h b/src/wayland/meta-wayland-surface-private.h index be0a38bf5..f42423164 100644 --- a/src/wayland/meta-wayland-surface-private.h +++ b/src/wayland/meta-wayland-surface-private.h @@ -142,6 +142,9 @@ struct _MetaWaylandSurfaceState gboolean has_target_time; int64_t target_time_us; + + gboolean fifo_barrier; + gboolean fifo_wait; }; struct _MetaWaylandDragDestFuncs @@ -285,6 +288,8 @@ struct _MetaWaylandSurface /* color-management */ ClutterColorState *color_state; + + gboolean fifo_barrier; }; void meta_wayland_shell_init (MetaWaylandCompositor *compositor); diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index cba0cdf08..e2c3d3193 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -28,6 +28,7 @@ #include "backends/meta-cursor-tracker-private.h" #include "clutter/clutter.h" #include "cogl/cogl.h" +#include "fifo-v1-server-protocol.h" #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-surface-actor.h" #include "compositor/meta-window-actor-private.h" @@ -456,6 +457,9 @@ meta_wayland_surface_state_set_default (MetaWaylandSurfaceState *state) state->has_new_color_state = FALSE; state->color_state = NULL; + + state->fifo_wait = FALSE; + state->fifo_barrier = FALSE; } static void @@ -629,6 +633,12 @@ meta_wayland_surface_state_merge_into (MetaWaylandSurfaceState *from, from->subsurface_placement_ops = NULL; } + if (from->fifo_wait) + to->fifo_wait = TRUE; + + if (from->fifo_barrier) + to->fifo_barrier = TRUE; + /* * A new commit indicates a new content update, so any previous * content update did not go on screen and needs to be discarded. @@ -802,6 +812,13 @@ meta_wayland_surface_apply_state (MetaWaylandSurface *surface, state->buffer->type != META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL)); } + if (state->fifo_barrier) + { + surface->fifo_barrier = TRUE; + meta_wayland_compositor_add_barrier_surface (surface->compositor, + surface); + } + if (state->has_new_buffer_transform) surface->buffer_transform = state->buffer_transform; @@ -1056,7 +1073,10 @@ meta_wayland_surface_commit (MetaWaylandSurface *surface) } if (meta_wayland_surface_is_synchronized (surface)) - transaction = meta_wayland_surface_ensure_transaction (surface); + { + pending->fifo_wait = FALSE; + transaction = meta_wayland_surface_ensure_transaction (surface); + } else transaction = meta_wayland_transaction_new (surface->compositor); @@ -1605,6 +1625,7 @@ meta_wayland_surface_dispose (GObject *object) meta_wayland_compositor_remove_frame_callback_surface (compositor, surface); meta_wayland_compositor_remove_presentation_feedback_surface (compositor, surface); + meta_wayland_compositor_remove_barrier_surface (compositor, surface); if (surface->outputs) { diff --git a/src/wayland/meta-wayland-transaction.c b/src/wayland/meta-wayland-transaction.c index 39f9c1803..d0ca29ec2 100644 --- a/src/wayland/meta-wayland-transaction.c +++ b/src/wayland/meta-wayland-transaction.c @@ -248,6 +248,7 @@ has_dependencies (MetaWaylandTransaction *transaction) { GHashTableIter iter; MetaWaylandSurface *surface; + MetaWaylandTransactionEntry *entry; if (transaction->target_presentation_time_us) return TRUE; @@ -257,10 +258,24 @@ has_dependencies (MetaWaylandTransaction *transaction) return TRUE; g_hash_table_iter_init (&iter, transaction->entries); - while (g_hash_table_iter_next (&iter, (gpointer *) &surface, NULL)) + while (g_hash_table_iter_next (&iter, (gpointer *) &surface, + (gpointer *) &entry)) { + MetaSurfaceActor *actor; + if (surface->transaction.first_committed != transaction) return TRUE; + + if (!entry || !entry->state) + continue; + + actor = meta_wayland_surface_get_actor (surface); + if (!actor || meta_surface_actor_is_effectively_obscured (actor) || + !clutter_actor_is_mapped (CLUTTER_ACTOR (actor))) + continue; + + if (entry->state->fifo_wait && surface->fifo_barrier) + return TRUE; } return FALSE; @@ -308,6 +323,30 @@ meta_wayland_transaction_unblock_timed (MetaWaylandTransaction *transaction, return TRUE; } +void +meta_wayland_transaction_consider_surface (MetaWaylandSurface *surface) +{ + MetaWaylandTransaction *transaction; + + transaction = surface->transaction.first_committed; + if (transaction) + meta_wayland_transaction_maybe_apply (transaction); +} + +void +meta_wayland_transaction_unblock_surface (MetaWaylandSurface *surface) +{ + if (!surface->fifo_barrier) + { + g_warning ("Attempting to unblock a surface with no fifo_barrier"); + return; + } + + surface->fifo_barrier = FALSE; + + meta_wayland_transaction_consider_surface (surface); +} + static void meta_wayland_transaction_dma_buf_dispatch (MetaWaylandBuffer *buffer, gpointer user_data) diff --git a/src/wayland/meta-wayland-transaction.h b/src/wayland/meta-wayland-transaction.h index b45c0e5ad..2951bcb45 100644 --- a/src/wayland/meta-wayland-transaction.h +++ b/src/wayland/meta-wayland-transaction.h @@ -57,3 +57,7 @@ int64_t meta_wayland_transaction_get_target_presentation_time_us (const MetaWayl gboolean meta_wayland_transaction_unblock_timed (MetaWaylandTransaction *transaction, int64_t target_time_us); + +void meta_wayland_transaction_unblock_surface (MetaWaylandSurface *surface); + +void meta_wayland_transaction_consider_surface (MetaWaylandSurface *surface); diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index 771352a37..e5360b5ba 100644 --- a/src/wayland/meta-wayland-versions.h +++ b/src/wayland/meta-wayland-versions.h @@ -64,3 +64,4 @@ #define META_WP_SYSTEM_BELL_V1_VERSION 1 #define META_XDG_TOPLEVEL_DRAG_VERSION 1 #define META_WP_COMMIT_TIMING_V1_VERSION 1 +#define META_WP_FIFO_V1_VERSION 1 diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index b3d179836..9016346c7 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -41,6 +41,7 @@ #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-color-management.h" #include "wayland/meta-wayland-commit-timing.h" +#include "wayland/meta-wayland-fifo.h" #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-wayland-dma-buf.h" #include "wayland/meta-wayland-egl-stream.h" @@ -374,6 +375,36 @@ clear_time_constraints_for_stage_view_transactions (MetaWaylandCompositor *compo } } +static void +clear_barrier_for_stage_view_surfaces (MetaWaylandCompositor *compositor, + ClutterStageView *stage_view) +{ + GList *l; + + l = compositor->barrier_surfaces; + while (l) + { + GList *l_cur = l; + MetaWaylandSurface *surface = l->data; + MetaSurfaceActor *actor; + + l = l->next; + + /* If the surface is not visible, the fifo condition should be + * unblocked, even if it wasn't on this stage view */ + actor = meta_wayland_surface_get_actor (surface); + if (actor && !meta_surface_actor_is_effectively_obscured (actor) && + clutter_actor_is_mapped (CLUTTER_ACTOR (actor)) && + !meta_surface_actor_wayland_is_view_primary (actor, stage_view)) + continue; + + compositor->barrier_surfaces = + g_list_delete_link (compositor->barrier_surfaces, l_cur); + + meta_wayland_transaction_unblock_surface (surface); + } +} + static void on_before_update (ClutterStage *stage, ClutterStageView *stage_view, @@ -400,6 +431,8 @@ on_after_update (ClutterStage *stage, GSource *source; int64_t frame_deadline_us; + clear_barrier_for_stage_view_surfaces (compositor, stage_view); + if (!META_IS_BACKEND_NATIVE (backend)) { emit_frame_callbacks_for_stage_view (compositor, stage_view); @@ -430,6 +463,7 @@ on_after_update (ClutterStage *stage, g_source_set_ready_time (source, frame_deadline_us); #else + clear_barrier_for_stage_view_surfaces (compositor, stage_view); emit_frame_callbacks_for_stage_view (compositor, stage_view); #endif } @@ -671,6 +705,25 @@ meta_wayland_compositor_remove_timed_transaction (MetaWaylandCompositor *compos g_queue_remove (compositor->timed_transactions, transaction); } +void +meta_wayland_compositor_add_barrier_surface (MetaWaylandCompositor *compositor, + MetaWaylandSurface *surface) +{ + if (g_list_find (compositor->barrier_surfaces, surface)) + return; + + compositor->barrier_surfaces = + g_list_prepend (compositor->barrier_surfaces, surface); +} + +void +meta_wayland_compositor_remove_barrier_surface (MetaWaylandCompositor *compositor, + MetaWaylandSurface *surface) +{ + compositor->barrier_surfaces = + g_list_remove (compositor->barrier_surfaces, surface); +} + GQueue * meta_wayland_compositor_get_committed_transactions (MetaWaylandCompositor *compositor) { @@ -973,6 +1026,7 @@ meta_wayland_compositor_new (MetaContext *context) meta_wayland_drm_lease_manager_init (compositor); #endif meta_wayland_commit_timing_init (compositor); + meta_wayland_fifo_init (compositor); #ifdef HAVE_WAYLAND_EGLSTREAM { diff --git a/src/wayland/meta-wayland.h b/src/wayland/meta-wayland.h index a8a35e224..b45080bb6 100644 --- a/src/wayland/meta-wayland.h +++ b/src/wayland/meta-wayland.h @@ -68,6 +68,11 @@ void meta_wayland_compositor_add_timed_transaction (MetaWayla void meta_wayland_compositor_remove_timed_transaction (MetaWaylandCompositor *compositor, MetaWaylandTransaction *transaction); +void meta_wayland_compositor_add_barrier_surface (MetaWaylandCompositor *compositor, + MetaWaylandSurface *surface); + +void meta_wayland_compositor_remove_barrier_surface (MetaWaylandCompositor *compositor, + MetaWaylandSurface *surface); GQueue *meta_wayland_compositor_get_committed_transactions (MetaWaylandCompositor *compositor); diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 27bc74337..1e9724068 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -40,6 +40,7 @@ #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-surface-private.h" #include "wayland/meta-wayland-toplevel-drag.h" +#include "wayland/meta-wayland-transaction.h" #include "wayland/meta-wayland-window-configuration.h" #include "wayland/meta-wayland-xdg-shell.h"