Blame SOURCES/wayland-frame-callback-rework.patch

ebfe55
From 4232f2056cd31811d047104d1223f3e5ced4b107 Mon Sep 17 00:00:00 2001
ebfe55
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
ebfe55
Date: Thu, 16 Apr 2020 19:42:03 +0200
ebfe55
Subject: [PATCH 1/5] clutter/stage: Make clutter_stage_schedule_update()
ebfe55
 always schedule
ebfe55
ebfe55
We could call clutter_stage_schedule_update() and it wouldn't actually
ebfe55
schedule anything, as the master frame clock only tries to reschedule if
ebfe55
1) there is an active timeline, 2) there are pending relayouts, 3) there
ebfe55
are pending redraws, or 4) there are pending events. Thus, a call to
ebfe55
clutter_stage_schedule_update() didn't have any effect if it was called
ebfe55
at the wrong time.
ebfe55
ebfe55
Fix this by adding a boolean state "needs_update" to the stage, set on
ebfe55
clutter_stage_schedule_update() and cleared on
ebfe55
_clutter_stage_do_update(), that will make the master clock reschedule
ebfe55
an update if it is TRUE.
ebfe55
ebfe55
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1218
ebfe55
(cherry picked from commit b8003807b0772e97354302b5cc2825e0b22c6c83)
ebfe55
---
ebfe55
 clutter/clutter/clutter-stage.c | 10 +++++++++-
ebfe55
 1 file changed, 9 insertions(+), 1 deletion(-)
ebfe55
ebfe55
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
ebfe55
index aaa77d9ede..5a914a3d0b 100644
ebfe55
--- a/clutter/clutter/clutter-stage.c
ebfe55
+++ b/clutter/clutter/clutter-stage.c
ebfe55
@@ -152,6 +152,8 @@ struct _ClutterStagePrivate
ebfe55
 
ebfe55
   int update_freeze_count;
ebfe55
 
ebfe55
+  gboolean needs_update;
ebfe55
+
ebfe55
   guint relayout_pending       : 1;
ebfe55
   guint redraw_pending         : 1;
ebfe55
   guint is_fullscreen          : 1;
ebfe55
@@ -1074,7 +1076,9 @@ _clutter_stage_needs_update (ClutterStage *stage)
ebfe55
 
ebfe55
   priv = stage->priv;
ebfe55
 
ebfe55
-  return priv->relayout_pending || priv->redraw_pending;
ebfe55
+  return priv->relayout_pending ||
ebfe55
+         priv->needs_update ||
ebfe55
+         priv->redraw_pending;
ebfe55
 }
ebfe55
 
ebfe55
 void
ebfe55
@@ -1232,6 +1236,8 @@ _clutter_stage_do_update (ClutterStage *stage)
ebfe55
 
ebfe55
   priv->stage_was_relayout = FALSE;
ebfe55
 
ebfe55
+  priv->needs_update = FALSE;
ebfe55
+
ebfe55
   /* if the stage is being destroyed, or if the destruction already
ebfe55
    * happened and we don't have an StageWindow any more, then we
ebfe55
    * should bail out
ebfe55
@@ -4080,6 +4086,8 @@ _clutter_stage_schedule_update (ClutterStage *stage)
ebfe55
   if (stage_window == NULL)
ebfe55
     return;
ebfe55
 
ebfe55
+  stage->priv->needs_update = TRUE;
ebfe55
+
ebfe55
   return _clutter_stage_window_schedule_update (stage_window,
ebfe55
                                                 stage->priv->sync_delay);
ebfe55
 }
ebfe55
-- 
ebfe55
2.31.1
ebfe55
ebfe55
ebfe55
From 2d4453cfdcaf0c6a9f492d2c2395dbfc8cf833db Mon Sep 17 00:00:00 2001
ebfe55
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
ebfe55
Date: Thu, 16 Apr 2020 19:11:37 +0200
ebfe55
Subject: [PATCH 2/5] clutter/stage: Make clutter_stage_schedule_update()
ebfe55
 public API
ebfe55
ebfe55
It's effectively used by mutter by abusing a ClutterTimeline to scedule
ebfe55
updates.  Timelines are not really suited in places that is done, as it
ebfe55
is really just about getting a single new update scheduled whenever
ebfe55
suitable, so expose the API so we can use it directly.
ebfe55
ebfe55
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1218
ebfe55
(cherry picked from commit 99c9a14bc8058830232cd4e07c7bb897e84a8c9c)
ebfe55
---
ebfe55
 clutter/clutter/clutter-master-clock-default.c |  4 ++--
ebfe55
 clutter/clutter/clutter-stage-private.h        |  1 -
ebfe55
 clutter/clutter/clutter-stage.c                | 16 ++++++++--------
ebfe55
 clutter/clutter/clutter-stage.h                |  3 +++
ebfe55
 4 files changed, 13 insertions(+), 11 deletions(-)
ebfe55
ebfe55
diff --git a/clutter/clutter/clutter-master-clock-default.c b/clutter/clutter/clutter-master-clock-default.c
ebfe55
index 0647c3a7fd..de7a1e2f4d 100644
ebfe55
--- a/clutter/clutter/clutter-master-clock-default.c
ebfe55
+++ b/clutter/clutter/clutter-master-clock-default.c
ebfe55
@@ -206,7 +206,7 @@ master_clock_schedule_stage_updates (ClutterMasterClockDefault *master_clock)
ebfe55
   stages = clutter_stage_manager_peek_stages (stage_manager);
ebfe55
 
ebfe55
   for (l = stages; l != NULL; l = l->next)
ebfe55
-    _clutter_stage_schedule_update (l->data);
ebfe55
+    clutter_stage_schedule_update (l->data);
ebfe55
 }
ebfe55
 
ebfe55
 static GSList *
ebfe55
@@ -259,7 +259,7 @@ master_clock_reschedule_stage_updates (ClutterMasterClockDefault *master_clock,
ebfe55
       if (master_clock->timelines ||
ebfe55
           _clutter_stage_has_queued_events (l->data) ||
ebfe55
           _clutter_stage_needs_update (l->data))
ebfe55
-        _clutter_stage_schedule_update (l->data);
ebfe55
+        clutter_stage_schedule_update (l->data);
ebfe55
     }
ebfe55
 }
ebfe55
 
ebfe55
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
ebfe55
index 42474687ad..ea0ce5ec72 100644
ebfe55
--- a/clutter/clutter/clutter-stage-private.h
ebfe55
+++ b/clutter/clutter/clutter-stage-private.h
ebfe55
@@ -70,7 +70,6 @@ void     _clutter_stage_queue_event                       (ClutterStage *stage,
ebfe55
 gboolean _clutter_stage_has_queued_events                 (ClutterStage *stage);
ebfe55
 void     _clutter_stage_process_queued_events             (ClutterStage *stage);
ebfe55
 void     _clutter_stage_update_input_devices              (ClutterStage *stage);
ebfe55
-void     _clutter_stage_schedule_update                   (ClutterStage *stage);
ebfe55
 gint64    _clutter_stage_get_update_time                  (ClutterStage *stage);
ebfe55
 void     _clutter_stage_clear_update_time                 (ClutterStage *stage);
ebfe55
 gboolean _clutter_stage_has_full_redraw_queued            (ClutterStage *stage);
ebfe55
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
ebfe55
index 5a914a3d0b..74ff8b1337 100644
ebfe55
--- a/clutter/clutter/clutter-stage.c
ebfe55
+++ b/clutter/clutter/clutter-stage.c
ebfe55
@@ -921,7 +921,7 @@ _clutter_stage_queue_event (ClutterStage *stage,
ebfe55
     {
ebfe55
       ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
ebfe55
       _clutter_master_clock_start_running (master_clock);
ebfe55
-      _clutter_stage_schedule_update (stage);
ebfe55
+      clutter_stage_schedule_update (stage);
ebfe55
     }
ebfe55
 
ebfe55
   /* if needed, update the state of the input device of the event.
ebfe55
@@ -1295,7 +1295,7 @@ clutter_stage_real_queue_relayout (ClutterActor *self)
ebfe55
 
ebfe55
   if (!priv->relayout_pending)
ebfe55
     {
ebfe55
-      _clutter_stage_schedule_update (stage);
ebfe55
+      clutter_stage_schedule_update (stage);
ebfe55
       priv->relayout_pending = TRUE;
ebfe55
     }
ebfe55
 
ebfe55
@@ -3788,7 +3788,7 @@ clutter_stage_ensure_redraw (ClutterStage *stage)
ebfe55
   priv = stage->priv;
ebfe55
 
ebfe55
   if (!priv->relayout_pending && !priv->redraw_pending)
ebfe55
-    _clutter_stage_schedule_update (stage);
ebfe55
+    clutter_stage_schedule_update (stage);
ebfe55
 
ebfe55
   priv->relayout_pending = TRUE;
ebfe55
   priv->redraw_pending = TRUE;
ebfe55
@@ -4069,13 +4069,13 @@ clutter_stage_get_minimum_size (ClutterStage *stage,
ebfe55
 }
ebfe55
 
ebfe55
 /**
ebfe55
- * _clutter_stage_schedule_update:
ebfe55
- * @window: a #ClutterStage actor
ebfe55
+ * clutter_stage_schedule_update:
ebfe55
+ * @stage: a #ClutterStage actor
ebfe55
  *
ebfe55
  * Schedules a redraw of the #ClutterStage at the next optimal timestamp.
ebfe55
  */
ebfe55
 void
ebfe55
-_clutter_stage_schedule_update (ClutterStage *stage)
ebfe55
+clutter_stage_schedule_update (ClutterStage *stage)
ebfe55
 {
ebfe55
   ClutterStageWindow *stage_window;
ebfe55
 
ebfe55
@@ -4097,7 +4097,7 @@ _clutter_stage_schedule_update (ClutterStage *stage)
ebfe55
  * @stage: a #ClutterStage actor
ebfe55
  *
ebfe55
  * Returns the earliest time in which the stage is ready to update. The update
ebfe55
- * time is set when _clutter_stage_schedule_update() is called. This can then
ebfe55
+ * time is set when clutter_stage_schedule_update() is called. This can then
ebfe55
  * be used by e.g. the #ClutterMasterClock to know when the stage needs to be
ebfe55
  * redrawn.
ebfe55
  *
ebfe55
@@ -4267,7 +4267,7 @@ _clutter_stage_queue_actor_redraw (ClutterStage                 *stage,
ebfe55
 
ebfe55
       CLUTTER_NOTE (PAINT, "First redraw request");
ebfe55
 
ebfe55
-      _clutter_stage_schedule_update (stage);
ebfe55
+      clutter_stage_schedule_update (stage);
ebfe55
       priv->redraw_pending = TRUE;
ebfe55
 
ebfe55
       master_clock = _clutter_master_clock_get_default ();
ebfe55
diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h
ebfe55
index 9da63d211d..53e37ae3bc 100644
ebfe55
--- a/clutter/clutter/clutter-stage.h
ebfe55
+++ b/clutter/clutter/clutter-stage.h
ebfe55
@@ -265,6 +265,9 @@ CLUTTER_EXPORT
ebfe55
 void            clutter_stage_skip_sync_delay                   (ClutterStage          *stage);
ebfe55
 #endif
ebfe55
 
ebfe55
+CLUTTER_EXPORT
ebfe55
+void clutter_stage_schedule_update (ClutterStage *stage);
ebfe55
+
ebfe55
 CLUTTER_EXPORT
ebfe55
 gboolean clutter_stage_get_capture_final_size (ClutterStage          *stage,
ebfe55
                                                cairo_rectangle_int_t *rect,
ebfe55
-- 
ebfe55
2.31.1
ebfe55
ebfe55
ebfe55
From 1c4db591b6bb2ae9d649e8157eae24b875e7a22b Mon Sep 17 00:00:00 2001
ebfe55
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
ebfe55
Date: Mon, 28 Oct 2019 18:20:31 +0100
ebfe55
Subject: [PATCH 3/5] wayland/actor-surface: Always store away frame callbacks
ebfe55
 on commit
ebfe55
ebfe55
We're expected by MetaWaylandSurface to always pick the frame callbacks
ebfe55
out from the pending state when committing (applying) so that no frame
ebfe55
callbacks are unaccounted for. We failed to do this if our actor for
ebfe55
some reason (e.g. associated window was unmanaged) was destroyed. To
ebfe55
handle this situation better, store away the frame callbacks until we
ebfe55
some later point in time need to pass them on forward.
ebfe55
ebfe55
https://gitlab.gnome.org/GNOME/mutter/merge_requests/893
ebfe55
---
ebfe55
 src/wayland/meta-wayland-actor-surface.c | 25 +++++++++++++++++++++++-
ebfe55
 1 file changed, 24 insertions(+), 1 deletion(-)
ebfe55
ebfe55
diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c
ebfe55
index 2471de0a92..264565c575 100644
ebfe55
--- a/src/wayland/meta-wayland-actor-surface.c
ebfe55
+++ b/src/wayland/meta-wayland-actor-surface.c
ebfe55
@@ -35,6 +35,8 @@ typedef struct _MetaWaylandActorSurfacePrivate MetaWaylandActorSurfacePrivate;
ebfe55
 struct _MetaWaylandActorSurfacePrivate
ebfe55
 {
ebfe55
   MetaSurfaceActor *actor;
ebfe55
+
ebfe55
+  struct wl_list frame_callback_list;
ebfe55
 };
ebfe55
 
ebfe55
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaWaylandActorSurface,
ebfe55
@@ -56,6 +58,7 @@ meta_wayland_actor_surface_dispose (GObject *object)
ebfe55
     meta_wayland_actor_surface_get_instance_private (META_WAYLAND_ACTOR_SURFACE (object));
ebfe55
   MetaWaylandSurface *surface =
ebfe55
     meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (object));
ebfe55
+  MetaWaylandFrameCallback *cb, *next;
ebfe55
 
ebfe55
   if (priv->actor)
ebfe55
     {
ebfe55
@@ -66,6 +69,9 @@ meta_wayland_actor_surface_dispose (GObject *object)
ebfe55
       g_clear_object (&priv->actor);
ebfe55
     }
ebfe55
 
ebfe55
+  wl_list_for_each_safe (cb, next, &priv->frame_callback_list, link)
ebfe55
+    wl_resource_destroy (cb->resource);
ebfe55
+
ebfe55
   G_OBJECT_CLASS (meta_wayland_actor_surface_parent_class)->dispose (object);
ebfe55
 }
ebfe55
 
ebfe55
@@ -99,6 +105,9 @@ meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor
ebfe55
   MetaSurfaceActorWayland *surface_actor_wayland =
ebfe55
     META_SURFACE_ACTOR_WAYLAND (priv->actor);
ebfe55
 
ebfe55
+  meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland,
ebfe55
+                                                  &priv->frame_callback_list);
ebfe55
+  wl_list_init (&priv->frame_callback_list);
ebfe55
   meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland,
ebfe55
                                                   &pending->frame_callback_list);
ebfe55
   wl_list_init (&pending->frame_callback_list);
ebfe55
@@ -253,10 +262,20 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
 {
ebfe55
   MetaWaylandActorSurface *actor_surface =
ebfe55
     META_WAYLAND_ACTOR_SURFACE (surface_role);
ebfe55
+  MetaWaylandActorSurfacePrivate *priv =
ebfe55
+    meta_wayland_actor_surface_get_instance_private (actor_surface);
ebfe55
   MetaWaylandSurface *surface =
ebfe55
     meta_wayland_surface_role_get_surface (surface_role);
ebfe55
   MetaWaylandSurface *toplevel_surface;
ebfe55
 
ebfe55
+  if (!priv->actor)
ebfe55
+    {
ebfe55
+      wl_list_insert_list (&priv->frame_callback_list,
ebfe55
+                           &pending->frame_callback_list);
ebfe55
+      wl_list_init (&pending->frame_callback_list);
ebfe55
+      return;
ebfe55
+    }
ebfe55
+
ebfe55
   meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
ebfe55
 
ebfe55
   toplevel_surface = meta_wayland_surface_get_toplevel (surface);
ebfe55
@@ -307,8 +326,12 @@ meta_wayland_actor_surface_is_on_logical_monitor (MetaWaylandSurfaceRole *surfac
ebfe55
 }
ebfe55
 
ebfe55
 static void
ebfe55
-meta_wayland_actor_surface_init (MetaWaylandActorSurface *role)
ebfe55
+meta_wayland_actor_surface_init (MetaWaylandActorSurface *actor_surface)
ebfe55
 {
ebfe55
+  MetaWaylandActorSurfacePrivate *priv =
ebfe55
+    meta_wayland_actor_surface_get_instance_private (actor_surface);
ebfe55
+
ebfe55
+  wl_list_init (&priv->frame_callback_list);
ebfe55
 }
ebfe55
 
ebfe55
 static void
ebfe55
-- 
ebfe55
2.31.1
ebfe55
ebfe55
ebfe55
From 54e5cee1a8d1a94fb19b438a3c80fe72179a3c80 Mon Sep 17 00:00:00 2001
ebfe55
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
ebfe55
Date: Fri, 6 Aug 2021 19:09:24 +0200
ebfe55
Subject: [PATCH 4/5] wayland/actor-surface: Always store away frame callbacks
ebfe55
 on commit
ebfe55
ebfe55
We're expected by MetaWaylandSurface to always pick the frame callbacks
ebfe55
out from the pending state when committing (applying) so that no frame
ebfe55
callbacks are unaccounted for. We failed to do this if our actor for
ebfe55
some reason (e.g. associated window was unmanaged) was destroyed. To
ebfe55
handle this situation better, store away the frame callbacks until we
ebfe55
some later point in time need to pass them on forward.
ebfe55
ebfe55
https://gitlab.gnome.org/GNOME/mutter/merge_requests/893
ebfe55
---
ebfe55
 src/compositor/meta-surface-actor-wayland.c | 29 ---------
ebfe55
 src/wayland/meta-wayland-actor-surface.c    | 65 ++++++++++++++++-----
ebfe55
 src/wayland/meta-wayland-actor-surface.h    |  3 +
ebfe55
 src/wayland/meta-wayland-cursor-surface.c   |  4 +-
ebfe55
 src/wayland/meta-wayland-dnd-surface.c      | 11 +++-
ebfe55
 src/wayland/meta-wayland-legacy-xdg-shell.c |  8 +--
ebfe55
 src/wayland/meta-wayland-private.h          |  2 +-
ebfe55
 src/wayland/meta-wayland-subsurface.c       |  2 -
ebfe55
 src/wayland/meta-wayland-surface.c          | 38 +++---------
ebfe55
 src/wayland/meta-wayland-surface.h          |  9 +--
ebfe55
 src/wayland/meta-wayland-wl-shell.c         |  3 -
ebfe55
 src/wayland/meta-wayland-xdg-shell.c        | 15 ++---
ebfe55
 src/wayland/meta-wayland.c                  | 56 +++++++++++++-----
ebfe55
 src/wayland/meta-wayland.h                  |  8 ++-
ebfe55
 src/wayland/meta-xwayland.c                 | 45 --------------
ebfe55
 15 files changed, 127 insertions(+), 171 deletions(-)
ebfe55
ebfe55
diff --git a/src/compositor/meta-surface-actor-wayland.c b/src/compositor/meta-surface-actor-wayland.c
ebfe55
index a75c4dd096..480b51c61a 100644
ebfe55
--- a/src/compositor/meta-surface-actor-wayland.c
ebfe55
+++ b/src/compositor/meta-surface-actor-wayland.c
ebfe55
@@ -42,7 +42,6 @@ struct _MetaSurfaceActorWayland
ebfe55
   MetaSurfaceActor parent;
ebfe55
 
ebfe55
   MetaWaylandSurface *surface;
ebfe55
-  struct wl_list frame_callback_list;
ebfe55
 };
ebfe55
 
ebfe55
 G_DEFINE_TYPE (MetaSurfaceActorWayland,
ebfe55
@@ -91,13 +90,6 @@ meta_surface_actor_wayland_is_unredirected (MetaSurfaceActor *actor)
ebfe55
   return FALSE;
ebfe55
 }
ebfe55
 
ebfe55
-void
ebfe55
-meta_surface_actor_wayland_add_frame_callbacks (MetaSurfaceActorWayland *self,
ebfe55
-                                                struct wl_list *frame_callbacks)
ebfe55
-{
ebfe55
-  wl_list_insert_list (&self->frame_callback_list, frame_callbacks);
ebfe55
-}
ebfe55
-
ebfe55
 static MetaWindow *
ebfe55
 meta_surface_actor_wayland_get_window (MetaSurfaceActor *actor)
ebfe55
 {
ebfe55
@@ -158,22 +150,6 @@ meta_surface_actor_wayland_get_preferred_height  (ClutterActor *actor,
ebfe55
     *natural_height_p *= scale;
ebfe55
 }
ebfe55
 
ebfe55
-static void
ebfe55
-meta_surface_actor_wayland_paint (ClutterActor *actor)
ebfe55
-{
ebfe55
-  MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor);
ebfe55
-
ebfe55
-  if (self->surface)
ebfe55
-    {
ebfe55
-      MetaWaylandCompositor *compositor = self->surface->compositor;
ebfe55
-
ebfe55
-      wl_list_insert_list (&compositor->frame_callbacks, &self->frame_callback_list);
ebfe55
-      wl_list_init (&self->frame_callback_list);
ebfe55
-    }
ebfe55
-
ebfe55
-  CLUTTER_ACTOR_CLASS (meta_surface_actor_wayland_parent_class)->paint (actor);
ebfe55
-}
ebfe55
-
ebfe55
 static void
ebfe55
 meta_surface_actor_wayland_dispose (GObject *object)
ebfe55
 {
ebfe55
@@ -190,9 +166,6 @@ meta_surface_actor_wayland_dispose (GObject *object)
ebfe55
       self->surface = NULL;
ebfe55
     }
ebfe55
 
ebfe55
-  wl_list_for_each_safe (cb, next, &self->frame_callback_list, link)
ebfe55
-    wl_resource_destroy (cb->resource);
ebfe55
-
ebfe55
   G_OBJECT_CLASS (meta_surface_actor_wayland_parent_class)->dispose (object);
ebfe55
 }
ebfe55
 
ebfe55
@@ -205,7 +178,6 @@ meta_surface_actor_wayland_class_init (MetaSurfaceActorWaylandClass *klass)
ebfe55
 
ebfe55
   actor_class->get_preferred_width = meta_surface_actor_wayland_get_preferred_width;
ebfe55
   actor_class->get_preferred_height = meta_surface_actor_wayland_get_preferred_height;
ebfe55
-  actor_class->paint = meta_surface_actor_wayland_paint;
ebfe55
 
ebfe55
   surface_actor_class->process_damage = meta_surface_actor_wayland_process_damage;
ebfe55
   surface_actor_class->pre_paint = meta_surface_actor_wayland_pre_paint;
ebfe55
@@ -232,7 +204,6 @@ meta_surface_actor_wayland_new (MetaWaylandSurface *surface)
ebfe55
 
ebfe55
   g_assert (meta_is_wayland_compositor ());
ebfe55
 
ebfe55
-  wl_list_init (&self->frame_callback_list);
ebfe55
   self->surface = surface;
ebfe55
   g_object_add_weak_pointer (G_OBJECT (self->surface),
ebfe55
                              (gpointer *) &self->surface);
ebfe55
diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c
ebfe55
index 264565c575..037dd901ab 100644
ebfe55
--- a/src/wayland/meta-wayland-actor-surface.c
ebfe55
+++ b/src/wayland/meta-wayland-actor-surface.c
ebfe55
@@ -84,16 +84,22 @@ meta_wayland_actor_surface_assigned (MetaWaylandSurfaceRole *surface_role)
ebfe55
     meta_wayland_surface_role_get_surface (surface_role);
ebfe55
   GList *l;
ebfe55
 
ebfe55
-  meta_surface_actor_wayland_add_frame_callbacks (META_SURFACE_ACTOR_WAYLAND (priv->actor),
ebfe55
-                                                  &surface->pending_frame_callback_list);
ebfe55
-  wl_list_init (&surface->pending_frame_callback_list);
ebfe55
-
ebfe55
   for (l = surface->subsurfaces; l; l = l->next)
ebfe55
     {
ebfe55
       ClutterActor *subsurface_actor =
ebfe55
         CLUTTER_ACTOR (meta_wayland_surface_get_actor (l->data));
ebfe55
       clutter_actor_add_child (CLUTTER_ACTOR (priv->actor), subsurface_actor);
ebfe55
     }
ebfe55
+
ebfe55
+  if (wl_list_empty (&surface->unassigned.pending_frame_callback_list))
ebfe55
+    return;
ebfe55
+
ebfe55
+  wl_list_insert_list (priv->frame_callback_list.prev,
ebfe55
+                       &surface->unassigned.pending_frame_callback_list);
ebfe55
+  wl_list_init (&surface->unassigned.pending_frame_callback_list);
ebfe55
+
ebfe55
+  meta_wayland_compositor_add_frame_callback_surface (surface->compositor,
ebfe55
+                                                      surface);
ebfe55
 }
ebfe55
 
ebfe55
 void
ebfe55
@@ -102,15 +108,40 @@ meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor
ebfe55
 {
ebfe55
   MetaWaylandActorSurfacePrivate *priv =
ebfe55
     meta_wayland_actor_surface_get_instance_private (actor_surface);
ebfe55
-  MetaSurfaceActorWayland *surface_actor_wayland =
ebfe55
-    META_SURFACE_ACTOR_WAYLAND (priv->actor);
ebfe55
+  MetaWaylandSurfaceRole *surface_role =
ebfe55
+    META_WAYLAND_SURFACE_ROLE (actor_surface);
ebfe55
+  MetaWaylandSurface *surface =
ebfe55
+    meta_wayland_surface_role_get_surface (surface_role);
ebfe55
 
ebfe55
-  meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland,
ebfe55
-                                                  &priv->frame_callback_list);
ebfe55
-  wl_list_init (&priv->frame_callback_list);
ebfe55
-  meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland,
ebfe55
-                                                  &pending->frame_callback_list);
ebfe55
+  if (!priv->actor)
ebfe55
+    return;
ebfe55
+
ebfe55
+  if (wl_list_empty (&pending->frame_callback_list))
ebfe55
+    return;
ebfe55
+
ebfe55
+  wl_list_insert_list (priv->frame_callback_list.prev,
ebfe55
+                       &pending->frame_callback_list);
ebfe55
   wl_list_init (&pending->frame_callback_list);
ebfe55
+
ebfe55
+  meta_wayland_compositor_add_frame_callback_surface (surface->compositor,
ebfe55
+                                                      surface);
ebfe55
+}
ebfe55
+
ebfe55
+void
ebfe55
+meta_wayland_actor_surface_emit_frame_callbacks (MetaWaylandActorSurface *actor_surface,
ebfe55
+                                                 uint32_t                 timestamp_ms)
ebfe55
+{
ebfe55
+  MetaWaylandActorSurfacePrivate *priv =
ebfe55
+    meta_wayland_actor_surface_get_instance_private (actor_surface);
ebfe55
+
ebfe55
+  while (!wl_list_empty (&priv->frame_callback_list))
ebfe55
+    {
ebfe55
+      MetaWaylandFrameCallback *callback =
ebfe55
+        wl_container_of (priv->frame_callback_list.next, callback, link);
ebfe55
+
ebfe55
+      wl_callback_send_done (callback->resource, timestamp_ms);
ebfe55
+      wl_resource_destroy (callback->resource);
ebfe55
+    }
ebfe55
 }
ebfe55
 
ebfe55
 static double
ebfe55
@@ -268,12 +299,14 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
     meta_wayland_surface_role_get_surface (surface_role);
ebfe55
   MetaWaylandSurface *toplevel_surface;
ebfe55
 
ebfe55
-  if (!priv->actor)
ebfe55
+  if (!wl_list_empty (&pending->frame_callback_list) &&
ebfe55
+      priv->actor &&
ebfe55
+      !meta_surface_actor_is_obscured (priv->actor))
ebfe55
     {
ebfe55
-      wl_list_insert_list (&priv->frame_callback_list,
ebfe55
-                           &pending->frame_callback_list);
ebfe55
-      wl_list_init (&pending->frame_callback_list);
ebfe55
-      return;
ebfe55
+      MetaBackend *backend = meta_get_backend ();
ebfe55
+      ClutterActor *stage = meta_backend_get_stage (backend);
ebfe55
+
ebfe55
+      clutter_stage_schedule_update (CLUTTER_STAGE (stage));
ebfe55
     }
ebfe55
 
ebfe55
   meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
ebfe55
diff --git a/src/wayland/meta-wayland-actor-surface.h b/src/wayland/meta-wayland-actor-surface.h
ebfe55
index 444b3b1785..e79f1caff5 100644
ebfe55
--- a/src/wayland/meta-wayland-actor-surface.h
ebfe55
+++ b/src/wayland/meta-wayland-actor-surface.h
ebfe55
@@ -46,4 +46,7 @@ void meta_wayland_actor_surface_reset_actor (MetaWaylandActorSurface *actor_surf
ebfe55
 void meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor_surface,
ebfe55
                                                        MetaWaylandPendingState *pending);
ebfe55
 
ebfe55
+void meta_wayland_actor_surface_emit_frame_callbacks (MetaWaylandActorSurface *actor_surface,
ebfe55
+                                                      uint32_t                 timestamp_ms);
ebfe55
+
ebfe55
 #endif /* META_WAYLAND_ACTOR_SURFACE_H */
ebfe55
diff --git a/src/wayland/meta-wayland-cursor-surface.c b/src/wayland/meta-wayland-cursor-surface.c
ebfe55
index d46b3511fa..6b791eb378 100644
ebfe55
--- a/src/wayland/meta-wayland-cursor-surface.c
ebfe55
+++ b/src/wayland/meta-wayland-cursor-surface.c
ebfe55
@@ -124,8 +124,8 @@ meta_wayland_cursor_surface_assigned (MetaWaylandSurfaceRole *surface_role)
ebfe55
     meta_wayland_cursor_surface_get_instance_private (cursor_surface);
ebfe55
 
ebfe55
   wl_list_insert_list (&priv->frame_callbacks,
ebfe55
-                       &surface->pending_frame_callback_list);
ebfe55
-  wl_list_init (&surface->pending_frame_callback_list);
ebfe55
+                       &surface->unassigned.pending_frame_callback_list);
ebfe55
+  wl_list_init (&surface->unassigned.pending_frame_callback_list);
ebfe55
 }
ebfe55
 
ebfe55
 static void
ebfe55
diff --git a/src/wayland/meta-wayland-dnd-surface.c b/src/wayland/meta-wayland-dnd-surface.c
ebfe55
index 2aad6dcd5d..8ddeb2a7bd 100644
ebfe55
--- a/src/wayland/meta-wayland-dnd-surface.c
ebfe55
+++ b/src/wayland/meta-wayland-dnd-surface.c
ebfe55
@@ -21,6 +21,8 @@
ebfe55
 
ebfe55
 #include "wayland/meta-wayland-dnd-surface.h"
ebfe55
 
ebfe55
+#include "wayland/meta-wayland.h"
ebfe55
+
ebfe55
 struct _MetaWaylandSurfaceRoleDND
ebfe55
 {
ebfe55
   MetaWaylandActorSurface parent;
ebfe55
@@ -36,7 +38,11 @@ dnd_surface_assigned (MetaWaylandSurfaceRole *surface_role)
ebfe55
   MetaWaylandSurface *surface =
ebfe55
     meta_wayland_surface_role_get_surface (surface_role);
ebfe55
 
ebfe55
-  meta_wayland_surface_queue_pending_frame_callbacks (surface);
ebfe55
+  if (wl_list_empty (&surface->unassigned.pending_frame_callback_list))
ebfe55
+    return;
ebfe55
+
ebfe55
+  meta_wayland_compositor_add_frame_callback_surface (surface->compositor,
ebfe55
+                                                      surface);
ebfe55
 }
ebfe55
 
ebfe55
 static void
ebfe55
@@ -46,7 +52,8 @@ dnd_surface_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
   MetaWaylandSurface *surface =
ebfe55
     meta_wayland_surface_role_get_surface (surface_role);
ebfe55
 
ebfe55
-  meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending);
ebfe55
+  meta_wayland_compositor_add_frame_callback_surface (surface->compositor,
ebfe55
+                                                      surface);
ebfe55
 }
ebfe55
 
ebfe55
 static void
ebfe55
diff --git a/src/wayland/meta-wayland-legacy-xdg-shell.c b/src/wayland/meta-wayland-legacy-xdg-shell.c
ebfe55
index 8230641770..b78552f31b 100644
ebfe55
--- a/src/wayland/meta-wayland-legacy-xdg-shell.c
ebfe55
+++ b/src/wayland/meta-wayland-legacy-xdg-shell.c
ebfe55
@@ -659,6 +659,8 @@ meta_wayland_zxdg_toplevel_v6_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
     META_WAYLAND_ZXDG_SURFACE_V6 (xdg_toplevel);
ebfe55
   MetaWaylandZxdgSurfaceV6Private *xdg_surface_priv =
ebfe55
     meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface);
ebfe55
+  MetaWaylandActorSurface *actor_surface =
ebfe55
+    META_WAYLAND_ACTOR_SURFACE (xdg_surface);
ebfe55
   MetaWaylandSurfaceRoleClass *surface_role_class;
ebfe55
   MetaWaylandSurface *surface =
ebfe55
     meta_wayland_surface_role_get_surface (surface_role);
ebfe55
@@ -670,7 +672,7 @@ meta_wayland_zxdg_toplevel_v6_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
   window = surface->window;
ebfe55
   if (!window)
ebfe55
     {
ebfe55
-      meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
ebfe55
+      meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
ebfe55
       return;
ebfe55
     }
ebfe55
 
ebfe55
@@ -1220,14 +1222,10 @@ meta_wayland_zxdg_surface_v6_send_configure (MetaWaylandZxdgSurfaceV6 *xdg_surfa
ebfe55
 static void
ebfe55
 zxdg_surface_v6_destructor (struct wl_resource *resource)
ebfe55
 {
ebfe55
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
ebfe55
   MetaWaylandZxdgSurfaceV6 *xdg_surface = wl_resource_get_user_data (resource);
ebfe55
   MetaWaylandZxdgSurfaceV6Private *priv =
ebfe55
     meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface);
ebfe55
 
ebfe55
-  meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
ebfe55
-                                                   surface);
ebfe55
-
ebfe55
   priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces,
ebfe55
                                                 xdg_surface);
ebfe55
 
ebfe55
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
ebfe55
index 5bcb0ea4f9..215d0967f6 100644
ebfe55
--- a/src/wayland/meta-wayland-private.h
ebfe55
+++ b/src/wayland/meta-wayland-private.h
ebfe55
@@ -67,7 +67,7 @@ struct _MetaWaylandCompositor
ebfe55
   struct wl_display *wayland_display;
ebfe55
   char *display_name;
ebfe55
   GHashTable *outputs;
ebfe55
-  struct wl_list frame_callbacks;
ebfe55
+  GList *frame_callback_surfaces;
ebfe55
 
ebfe55
   MetaXWaylandManager xwayland_manager;
ebfe55
 
ebfe55
diff --git a/src/wayland/meta-wayland-subsurface.c b/src/wayland/meta-wayland-subsurface.c
ebfe55
index e0fa0a48b2..c7059b99a2 100644
ebfe55
--- a/src/wayland/meta-wayland-subsurface.c
ebfe55
+++ b/src/wayland/meta-wayland-subsurface.c
ebfe55
@@ -239,8 +239,6 @@ wl_subsurface_destructor (struct wl_resource *resource)
ebfe55
 {
ebfe55
   MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
ebfe55
 
ebfe55
-  meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
ebfe55
-                                                   surface);
ebfe55
   if (surface->sub.parent)
ebfe55
     {
ebfe55
       wl_list_remove (&surface->sub.parent_destroy_listener.link);
ebfe55
diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
ebfe55
index 6ffcd6a7fb..a76ab28c24 100644
ebfe55
--- a/src/wayland/meta-wayland-surface.c
ebfe55
+++ b/src/wayland/meta-wayland-surface.c
ebfe55
@@ -358,15 +358,6 @@ surface_process_damage (MetaWaylandSurface *surface,
ebfe55
   cairo_region_destroy (transformed_region);
ebfe55
 }
ebfe55
 
ebfe55
-void
ebfe55
-meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface      *surface,
ebfe55
-                                                          MetaWaylandPendingState *pending)
ebfe55
-{
ebfe55
-  wl_list_insert_list (&surface->compositor->frame_callbacks,
ebfe55
-                       &pending->frame_callback_list);
ebfe55
-  wl_list_init (&pending->frame_callback_list);
ebfe55
-}
ebfe55
-
ebfe55
 void
ebfe55
 meta_wayland_surface_destroy_window (MetaWaylandSurface *surface)
ebfe55
 {
ebfe55
@@ -656,15 +647,6 @@ parent_surface_state_applied (gpointer data,
ebfe55
   meta_wayland_subsurface_parent_state_applied (subsurface);
ebfe55
 }
ebfe55
 
ebfe55
-void
ebfe55
-meta_wayland_surface_cache_pending_frame_callbacks (MetaWaylandSurface      *surface,
ebfe55
-                                                    MetaWaylandPendingState *pending)
ebfe55
-{
ebfe55
-  wl_list_insert_list (&surface->pending_frame_callback_list,
ebfe55
-                       &pending->frame_callback_list);
ebfe55
-  wl_list_init (&pending->frame_callback_list);
ebfe55
-}
ebfe55
-
ebfe55
 void
ebfe55
 meta_wayland_surface_apply_pending_state (MetaWaylandSurface      *surface,
ebfe55
                                           MetaWaylandPendingState *pending)
ebfe55
@@ -810,7 +792,9 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface      *surface,
ebfe55
     }
ebfe55
   else
ebfe55
     {
ebfe55
-      meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
ebfe55
+      wl_list_insert_list (surface->unassigned.pending_frame_callback_list.prev,
ebfe55
+                           &pending->frame_callback_list);
ebfe55
+      wl_list_init (&pending->frame_callback_list);
ebfe55
 
ebfe55
       if (pending->newly_attached)
ebfe55
         {
ebfe55
@@ -1352,12 +1336,14 @@ wl_surface_destructor (struct wl_resource *resource)
ebfe55
   if (surface->input_region)
ebfe55
     cairo_region_destroy (surface->input_region);
ebfe55
 
ebfe55
-  meta_wayland_compositor_destroy_frame_callbacks (compositor, surface);
ebfe55
+  meta_wayland_compositor_remove_frame_callback_surface (compositor, surface);
ebfe55
 
ebfe55
   g_hash_table_foreach (surface->outputs_to_destroy_notify_id, surface_output_disconnect_signal, surface);
ebfe55
   g_hash_table_unref (surface->outputs_to_destroy_notify_id);
ebfe55
 
ebfe55
-  wl_list_for_each_safe (cb, next, &surface->pending_frame_callback_list, link)
ebfe55
+  wl_list_for_each_safe (cb, next,
ebfe55
+                         &surface->unassigned.pending_frame_callback_list,
ebfe55
+                         link)
ebfe55
     wl_resource_destroy (cb->resource);
ebfe55
 
ebfe55
   if (surface->resource)
ebfe55
@@ -1401,7 +1387,7 @@ meta_wayland_surface_create (MetaWaylandCompositor *compositor,
ebfe55
   surface->resource = wl_resource_create (client, &wl_surface_interface, wl_resource_get_version (compositor_resource), id);
ebfe55
   wl_resource_set_implementation (surface->resource, &meta_wayland_wl_surface_interface, surface, wl_surface_destructor);
ebfe55
 
ebfe55
-  wl_list_init (&surface->pending_frame_callback_list);
ebfe55
+  wl_list_init (&surface->unassigned.pending_frame_callback_list);
ebfe55
 
ebfe55
   sync_drag_dest_funcs (surface);
ebfe55
 
ebfe55
@@ -1809,14 +1795,6 @@ meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role)
ebfe55
   return priv->surface;
ebfe55
 }
ebfe55
 
ebfe55
-void
ebfe55
-meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface)
ebfe55
-{
ebfe55
-  wl_list_insert_list (&surface->compositor->frame_callbacks,
ebfe55
-                       &surface->pending_frame_callback_list);
ebfe55
-  wl_list_init (&surface->pending_frame_callback_list);
ebfe55
-}
ebfe55
-
ebfe55
 cairo_region_t *
ebfe55
 meta_wayland_surface_calculate_input_region (MetaWaylandSurface *surface)
ebfe55
 {
ebfe55
diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h
ebfe55
index e244a3fdf7..776431fca2 100644
ebfe55
--- a/src/wayland/meta-wayland-surface.h
ebfe55
+++ b/src/wayland/meta-wayland-surface.h
ebfe55
@@ -160,13 +160,9 @@ struct _MetaWaylandSurface
ebfe55
   /* Buffer renderer state. */
ebfe55
   gboolean buffer_held;
ebfe55
 
ebfe55
-  /* List of pending frame callbacks that needs to stay queued longer than one
ebfe55
-   * commit sequence, such as when it has not yet been assigned a role.
ebfe55
-   */
ebfe55
-  struct wl_list pending_frame_callback_list;
ebfe55
-
ebfe55
   /* Intermediate state for when no role has been assigned. */
ebfe55
   struct {
ebfe55
+    struct wl_list pending_frame_callback_list;
ebfe55
     MetaWaylandBuffer *buffer;
ebfe55
   } unassigned;
ebfe55
 
ebfe55
@@ -274,9 +270,6 @@ MetaWaylandSurface *meta_wayland_surface_get_toplevel (MetaWaylandSurface *surfa
ebfe55
 
ebfe55
 MetaWindow *        meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface);
ebfe55
 
ebfe55
-void                meta_wayland_surface_cache_pending_frame_callbacks (MetaWaylandSurface      *surface,
ebfe55
-                                                                        MetaWaylandPendingState *pending);
ebfe55
-
ebfe55
 void                meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface);
ebfe55
 
ebfe55
 void                meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface      *surface,
ebfe55
diff --git a/src/wayland/meta-wayland-wl-shell.c b/src/wayland/meta-wayland-wl-shell.c
ebfe55
index 539fb9858e..e80db17e78 100644
ebfe55
--- a/src/wayland/meta-wayland-wl-shell.c
ebfe55
+++ b/src/wayland/meta-wayland-wl-shell.c
ebfe55
@@ -100,9 +100,6 @@ wl_shell_surface_destructor (struct wl_resource *resource)
ebfe55
     surface_from_wl_shell_surface_resource (resource);
ebfe55
   GList *l;
ebfe55
 
ebfe55
-  meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
ebfe55
-                                                   surface);
ebfe55
-
ebfe55
   if (wl_shell_surface->popup)
ebfe55
     meta_wayland_popup_dismiss (wl_shell_surface->popup);
ebfe55
 
ebfe55
diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c
ebfe55
index fa0207a03c..4a4995c425 100644
ebfe55
--- a/src/wayland/meta-wayland-xdg-shell.c
ebfe55
+++ b/src/wayland/meta-wayland-xdg-shell.c
ebfe55
@@ -684,6 +684,8 @@ meta_wayland_xdg_toplevel_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
   MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel);
ebfe55
   MetaWaylandXdgSurfacePrivate *xdg_surface_priv =
ebfe55
     meta_wayland_xdg_surface_get_instance_private (xdg_surface);
ebfe55
+  MetaWaylandActorSurface *actor_surface =
ebfe55
+    META_WAYLAND_ACTOR_SURFACE (xdg_toplevel);
ebfe55
   MetaWaylandSurfaceRoleClass *surface_role_class;
ebfe55
   MetaWaylandSurface *surface =
ebfe55
     meta_wayland_surface_role_get_surface (surface_role);
ebfe55
@@ -695,15 +697,12 @@ meta_wayland_xdg_toplevel_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
   window = surface->window;
ebfe55
   if (!window)
ebfe55
     {
ebfe55
-      meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
ebfe55
+      meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
ebfe55
       return;
ebfe55
     }
ebfe55
 
ebfe55
   if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached)
ebfe55
     {
ebfe55
-      MetaWaylandActorSurface *actor_surface =
ebfe55
-        META_WAYLAND_ACTOR_SURFACE (xdg_toplevel);
ebfe55
-
ebfe55
       meta_wayland_xdg_surface_reset (xdg_surface);
ebfe55
       meta_wayland_actor_surface_queue_frame_callbacks (actor_surface,
ebfe55
                                                         pending);
ebfe55
@@ -1037,6 +1036,8 @@ meta_wayland_xdg_popup_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
   MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role);
ebfe55
   MetaWaylandXdgSurfacePrivate *xdg_surface_priv =
ebfe55
     meta_wayland_xdg_surface_get_instance_private (xdg_surface);
ebfe55
+  MetaWaylandActorSurface *actor_surface =
ebfe55
+    META_WAYLAND_ACTOR_SURFACE (xdg_popup);
ebfe55
   MetaWaylandSurfaceRoleClass *surface_role_class;
ebfe55
   MetaWaylandSurface *surface =
ebfe55
     meta_wayland_surface_role_get_surface (surface_role);
ebfe55
@@ -1048,7 +1049,7 @@ meta_wayland_xdg_popup_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
   if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached)
ebfe55
     {
ebfe55
       meta_wayland_xdg_surface_reset (xdg_surface);
ebfe55
-      meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
ebfe55
+      meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
ebfe55
       return;
ebfe55
     }
ebfe55
 
ebfe55
@@ -1313,14 +1314,10 @@ meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_surface)
ebfe55
 static void
ebfe55
 xdg_surface_destructor (struct wl_resource *resource)
ebfe55
 {
ebfe55
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
ebfe55
   MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
ebfe55
   MetaWaylandXdgSurfacePrivate *priv =
ebfe55
     meta_wayland_xdg_surface_get_instance_private (xdg_surface);
ebfe55
 
ebfe55
-  meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
ebfe55
-                                                   surface);
ebfe55
-
ebfe55
   priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces,
ebfe55
                                                 xdg_surface);
ebfe55
 
ebfe55
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
ebfe55
index 129da8e20d..4cb9ca650d 100644
ebfe55
--- a/src/wayland/meta-wayland.c
ebfe55
+++ b/src/wayland/meta-wayland.c
ebfe55
@@ -194,15 +194,35 @@ meta_wayland_compositor_update (MetaWaylandCompositor *compositor,
ebfe55
 void
ebfe55
 meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor)
ebfe55
 {
ebfe55
-  gint64 current_time = g_get_monotonic_time ();
ebfe55
+  GList *l;
ebfe55
+  int64_t now_us;
ebfe55
 
ebfe55
-  while (!wl_list_empty (&compositor->frame_callbacks))
ebfe55
+  now_us = g_get_monotonic_time ();
ebfe55
+
ebfe55
+  l = compositor->frame_callback_surfaces;
ebfe55
+  while (l)
ebfe55
     {
ebfe55
-      MetaWaylandFrameCallback *callback =
ebfe55
-        wl_container_of (compositor->frame_callbacks.next, callback, link);
ebfe55
+      GList *l_cur = l;
ebfe55
+      MetaWaylandSurface *surface = l->data;
ebfe55
+      MetaSurfaceActor *actor;
ebfe55
+      MetaWaylandActorSurface *actor_surface;
ebfe55
+
ebfe55
+      l = l->next;
ebfe55
+
ebfe55
+      actor = meta_wayland_surface_get_actor (surface);
ebfe55
+      if (!actor)
ebfe55
+        continue;
ebfe55
+
ebfe55
+      if (!clutter_actor_has_mapped_clones (CLUTTER_ACTOR (actor)) &&
ebfe55
+          meta_surface_actor_is_obscured (actor))
ebfe55
+        continue;
ebfe55
 
ebfe55
-      wl_callback_send_done (callback->resource, current_time / 1000);
ebfe55
-      wl_resource_destroy (callback->resource);
ebfe55
+      actor_surface = META_WAYLAND_ACTOR_SURFACE (surface->role);
ebfe55
+      meta_wayland_actor_surface_emit_frame_callbacks (actor_surface,
ebfe55
+                                                       now_us / 1000);
ebfe55
+
ebfe55
+      compositor->frame_callback_surfaces =
ebfe55
+        g_list_delete_link (compositor->frame_callback_surfaces, l_cur);
ebfe55
     }
ebfe55
 }
ebfe55
 
ebfe55
@@ -249,16 +269,22 @@ meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor,
ebfe55
 }
ebfe55
 
ebfe55
 void
ebfe55
-meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor,
ebfe55
-                                                 MetaWaylandSurface    *surface)
ebfe55
+meta_wayland_compositor_add_frame_callback_surface (MetaWaylandCompositor *compositor,
ebfe55
+                                                    MetaWaylandSurface    *surface)
ebfe55
 {
ebfe55
-  MetaWaylandFrameCallback *callback, *next;
ebfe55
+  if (g_list_find (compositor->frame_callback_surfaces, surface))
ebfe55
+    return;
ebfe55
 
ebfe55
-  wl_list_for_each_safe (callback, next, &compositor->frame_callbacks, link)
ebfe55
-    {
ebfe55
-      if (callback->surface == surface)
ebfe55
-        wl_resource_destroy (callback->resource);
ebfe55
-    }
ebfe55
+  compositor->frame_callback_surfaces =
ebfe55
+    g_list_prepend (compositor->frame_callback_surfaces, surface);
ebfe55
+}
ebfe55
+
ebfe55
+void
ebfe55
+meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor,
ebfe55
+                                                       MetaWaylandSurface    *surface)
ebfe55
+{
ebfe55
+  compositor->frame_callback_surfaces =
ebfe55
+    g_list_remove (compositor->frame_callback_surfaces, surface);
ebfe55
 }
ebfe55
 
ebfe55
 static void
ebfe55
@@ -309,8 +335,6 @@ meta_wayland_log_func (const char *fmt,
ebfe55
 static void
ebfe55
 meta_wayland_compositor_init (MetaWaylandCompositor *compositor)
ebfe55
 {
ebfe55
-  wl_list_init (&compositor->frame_callbacks);
ebfe55
-
ebfe55
   compositor->scheduled_surface_associations = g_hash_table_new (NULL, NULL);
ebfe55
 
ebfe55
   wl_log_set_handler_server (meta_wayland_log_func);
ebfe55
diff --git a/src/wayland/meta-wayland.h b/src/wayland/meta-wayland.h
ebfe55
index 2a0aa11400..c5e5924891 100644
ebfe55
--- a/src/wayland/meta-wayland.h
ebfe55
+++ b/src/wayland/meta-wayland.h
ebfe55
@@ -64,9 +64,11 @@ void                    meta_wayland_compositor_set_input_focus (MetaWaylandComp
ebfe55
 META_EXPORT_TEST
ebfe55
 void                    meta_wayland_compositor_paint_finished  (MetaWaylandCompositor *compositor);
ebfe55
 
ebfe55
-META_EXPORT_TEST
ebfe55
-void                    meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor,
ebfe55
-                                                                         MetaWaylandSurface    *surface);
ebfe55
+void                    meta_wayland_compositor_add_frame_callback_surface (MetaWaylandCompositor *compositor,
ebfe55
+                                                                            MetaWaylandSurface    *surface);
ebfe55
+
ebfe55
+void                    meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor,
ebfe55
+                                                                               MetaWaylandSurface    *surface);
ebfe55
 
ebfe55
 META_EXPORT_TEST
ebfe55
 const char             *meta_wayland_get_wayland_display_name   (MetaWaylandCompositor *compositor);
ebfe55
diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c
ebfe55
index 275aeb78cb..6e4b9a8ffd 100644
ebfe55
--- a/src/wayland/meta-xwayland.c
ebfe55
+++ b/src/wayland/meta-xwayland.c
ebfe55
@@ -788,49 +788,6 @@ meta_xwayland_stop (MetaXWaylandManager *manager)
ebfe55
     }
ebfe55
 }
ebfe55
 
ebfe55
-static void
ebfe55
-xwayland_surface_assigned (MetaWaylandSurfaceRole *surface_role)
ebfe55
-{
ebfe55
-  MetaWaylandSurface *surface =
ebfe55
-    meta_wayland_surface_role_get_surface (surface_role);
ebfe55
-  MetaWaylandSurfaceRoleClass *surface_role_class =
ebfe55
-    META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_xwayland_parent_class);
ebfe55
-
ebfe55
-  /* See comment in xwayland_surface_commit for why we reply even though the
ebfe55
-   * surface may not be drawn the next frame.
ebfe55
-   */
ebfe55
-  wl_list_insert_list (&surface->compositor->frame_callbacks,
ebfe55
-                       &surface->pending_frame_callback_list);
ebfe55
-  wl_list_init (&surface->pending_frame_callback_list);
ebfe55
-
ebfe55
-  surface_role_class->assigned (surface_role);
ebfe55
-}
ebfe55
-
ebfe55
-static void
ebfe55
-xwayland_surface_commit (MetaWaylandSurfaceRole  *surface_role,
ebfe55
-                         MetaWaylandPendingState *pending)
ebfe55
-{
ebfe55
-  MetaWaylandSurface *surface =
ebfe55
-    meta_wayland_surface_role_get_surface (surface_role);
ebfe55
-  MetaWaylandSurfaceRoleClass *surface_role_class =
ebfe55
-    META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_xwayland_parent_class);
ebfe55
-
ebfe55
-  /* For Xwayland windows, throttling frames when the window isn't actually
ebfe55
-   * drawn is less useful, because Xwayland still has to do the drawing sent
ebfe55
-   * from the application - the throttling would only be of sending us damage
ebfe55
-   * messages, so we simplify and send frame callbacks after the next paint of
ebfe55
-   * the screen, whether the window was drawn or not.
ebfe55
-   *
ebfe55
-   * Currently it may take a few frames before we draw the window, for not
ebfe55
-   * completely understood reasons, and in that case, not thottling frame
ebfe55
-   * callbacks to drawing has the happy side effect that we avoid showing the
ebfe55
-   * user the initial black frame from when the window is mapped empty.
ebfe55
-   */
ebfe55
-  meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending);
ebfe55
-
ebfe55
-  surface_role_class->commit (surface_role, pending);
ebfe55
-}
ebfe55
-
ebfe55
 static MetaWaylandSurface *
ebfe55
 xwayland_surface_get_toplevel (MetaWaylandSurfaceRole *surface_role)
ebfe55
 {
ebfe55
@@ -848,8 +805,6 @@ meta_wayland_surface_role_xwayland_class_init (MetaWaylandSurfaceRoleXWaylandCla
ebfe55
   MetaWaylandSurfaceRoleClass *surface_role_class =
ebfe55
     META_WAYLAND_SURFACE_ROLE_CLASS (klass);
ebfe55
 
ebfe55
-  surface_role_class->assigned = xwayland_surface_assigned;
ebfe55
-  surface_role_class->commit = xwayland_surface_commit;
ebfe55
   surface_role_class->get_toplevel = xwayland_surface_get_toplevel;
ebfe55
 
ebfe55
   xwayland_surface_signals[XWAYLAND_SURFACE_WINDOW_ASSOCIATED] =
ebfe55
-- 
ebfe55
2.31.1
ebfe55
ebfe55
ebfe55
From 076ac20d34db128aea8ffe0dc3c2791918667c43 Mon Sep 17 00:00:00 2001
ebfe55
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
ebfe55
Date: Fri, 6 Aug 2021 19:46:06 +0200
ebfe55
Subject: [PATCH 5/5] wayland: Respond to frame callbacks even if the paint was
ebfe55
 empty
ebfe55
ebfe55
---
ebfe55
 src/compositor/compositor.c | 10 +++++-----
ebfe55
 1 file changed, 5 insertions(+), 5 deletions(-)
ebfe55
ebfe55
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
ebfe55
index ce2c1b8a3b..8331737d1a 100644
ebfe55
--- a/src/compositor/compositor.c
ebfe55
+++ b/src/compositor/compositor.c
ebfe55
@@ -467,11 +467,6 @@ after_stage_paint (ClutterStage *stage,
ebfe55
 
ebfe55
   for (l = compositor->windows; l; l = l->next)
ebfe55
     meta_window_actor_post_paint (l->data);
ebfe55
-
ebfe55
-#ifdef HAVE_WAYLAND
ebfe55
-  if (meta_is_wayland_compositor ())
ebfe55
-    meta_wayland_compositor_paint_finished (meta_wayland_compositor_get_default ());
ebfe55
-#endif
ebfe55
 }
ebfe55
 
ebfe55
 static void
ebfe55
@@ -1404,6 +1399,11 @@ meta_post_paint_func (gpointer data)
ebfe55
       break;
ebfe55
     }
ebfe55
 
ebfe55
+#ifdef HAVE_WAYLAND
ebfe55
+  if (meta_is_wayland_compositor ())
ebfe55
+    meta_wayland_compositor_paint_finished (meta_wayland_compositor_get_default ());
ebfe55
+#endif
ebfe55
+
ebfe55
   return TRUE;
ebfe55
 }
ebfe55
 
ebfe55
-- 
ebfe55
2.31.1
ebfe55