[mutter] clutter/stage: Don't pass QueueRedrawEntries to actors



commit 906124b09fe712a25907ac444f173f93ad3e7951
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Sat Oct 17 22:29:05 2020 +0200

    clutter/stage: Don't pass QueueRedrawEntries to actors
    
    We currently pass actors a reference to their associated
    ClutterStageQueueRedrawEntry when queueing a redraw. This "splitting" of
    the ownership of the entry has introduced quite a few bugs in the past
    and is hard to follow.
    
    So give up the "splitting" of the ownership and exclusively handle those
    entries inside ClutterStage. To still allow removing the entry when an
    actor gets unrealized introduce clutter_stage_dequeue_actor_redraw()
    similar to what we already have for relayouts.
    
    To be able to efficiently find entries when actors queue redraws, make
    pending_queue_redraws a GHashTable, which fits quite nicely and also
    allows removing the QueueRedrawEntries actor pointer in favour of the
    key of the hashtable.
    
    Since the struct is now private to ClutterStage, we can also rename it
    to QueueRedrawEntry.
    
    While at it, also sneak in the removal of the leading underscore from
    clutter_stage_queue_actor_redraw().
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1511>

 clutter/clutter/clutter-actor.c         |  75 +++--------------
 clutter/clutter/clutter-stage-private.h |  13 ++-
 clutter/clutter/clutter-stage.c         | 139 ++++++++++++++------------------
 3 files changed, 75 insertions(+), 152 deletions(-)
---
diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
index 03e49a2d59..c061f722f9 100644
--- a/clutter/clutter/clutter-actor.c
+++ b/clutter/clutter/clutter-actor.c
@@ -775,8 +775,6 @@ struct _ClutterActorPrivate
    */
   ClutterPaintVolume last_paint_volume;
 
-  ClutterStageQueueRedrawEntry *queue_redraw_entry;
-
   ClutterColor bg_color;
 
 #ifdef CLUTTER_ENABLE_DEBUG
@@ -2117,6 +2115,9 @@ unrealize_actor_after_children_cb (ClutterActor *self,
       priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)
     clutter_stage_dequeue_actor_relayout (CLUTTER_STAGE (stage), self);
 
+  if (stage != NULL)
+    clutter_stage_dequeue_actor_redraw (CLUTTER_STAGE (stage), self);
+
   if (priv->unmapped_paint_branch_counter == 0)
     priv->allocation = (ClutterActorBox) CLUTTER_ACTOR_BOX_UNINITIALIZED;
 
@@ -4059,22 +4060,6 @@ _clutter_actor_stop_transitions (ClutterActor *self)
     }
 }
 
-static ClutterActorTraverseVisitFlags
-invalidate_queue_redraw_entry (ClutterActor *self,
-                               int           depth,
-                               gpointer      user_data)
-{
-  ClutterActorPrivate *priv = self->priv;
-
-  if (priv->queue_redraw_entry != NULL)
-    {
-      _clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry);
-      priv->queue_redraw_entry = NULL;
-    }
-
-  return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
-}
-
 static inline void
 remove_child (ClutterActor *self,
               ClutterActor *child)
@@ -4107,10 +4092,9 @@ typedef enum
   REMOVE_CHILD_EMIT_PARENT_SET    = 1 << 1,
   REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
   REMOVE_CHILD_CHECK_STATE        = 1 << 3,
-  REMOVE_CHILD_FLUSH_QUEUE        = 1 << 4,
-  REMOVE_CHILD_NOTIFY_FIRST_LAST  = 1 << 5,
-  REMOVE_CHILD_STOP_TRANSITIONS   = 1 << 6,
-  REMOVE_CHILD_CLEAR_STAGE_VIEWS  = 1 << 7,
+  REMOVE_CHILD_NOTIFY_FIRST_LAST  = 1 << 4,
+  REMOVE_CHILD_STOP_TRANSITIONS   = 1 << 5,
+  REMOVE_CHILD_CLEAR_STAGE_VIEWS  = 1 << 6,
 
   /* default flags for public API */
   REMOVE_CHILD_DEFAULT_FLAGS      = REMOVE_CHILD_STOP_TRANSITIONS |
@@ -4118,7 +4102,6 @@ typedef enum
                                     REMOVE_CHILD_EMIT_PARENT_SET |
                                     REMOVE_CHILD_EMIT_ACTOR_REMOVED |
                                     REMOVE_CHILD_CHECK_STATE |
-                                    REMOVE_CHILD_FLUSH_QUEUE |
                                     REMOVE_CHILD_NOTIFY_FIRST_LAST |
                                     REMOVE_CHILD_CLEAR_STAGE_VIEWS,
 } ClutterActorRemoveChildFlags;
@@ -4138,7 +4121,6 @@ clutter_actor_remove_child_internal (ClutterActor                 *self,
 {
   ClutterActor *old_first, *old_last;
   gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
-  gboolean flush_queue;
   gboolean notify_first_last;
   gboolean stop_transitions;
   gboolean clear_stage_views;
@@ -4155,7 +4137,6 @@ clutter_actor_remove_child_internal (ClutterActor                 *self,
   emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
   emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
   check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
-  flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
   notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
   stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
   clear_stage_views = (flags & REMOVE_CHILD_CLEAR_STAGE_VIEWS) != 0;
@@ -4181,28 +4162,6 @@ clutter_actor_remove_child_internal (ClutterActor                 *self,
       clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
     }
 
-  if (flush_queue)
-    {
-      /* We take this opportunity to invalidate any queue redraw entry
-       * associated with the actor and descendants since we won't be able to
-       * determine the appropriate stage after this.
-       *
-       * we do this after we updated the mapped state because actors might
-       * end up queueing redraws inside their mapped/unmapped virtual
-       * functions, and if we invalidate the redraw entry we could end up
-       * with an inconsistent state and weird memory corruption. see
-       * bugs:
-       *
-       *   http://bugzilla.clutter-project.org/show_bug.cgi?id=2621
-       *   https://bugzilla.gnome.org/show_bug.cgi?id=652036
-       */
-      _clutter_actor_traverse (child,
-                               0,
-                               invalidate_queue_redraw_entry,
-                               NULL,
-                               NULL);
-    }
-
   old_first = self->priv->first_child;
   old_last = self->priv->last_child;
 
@@ -7901,20 +7860,6 @@ clutter_actor_destroy (ClutterActor *self)
   g_object_unref (self);
 }
 
-void
-_clutter_actor_finish_queue_redraw (ClutterActor *self)
-{
-  ClutterActorPrivate *priv = self->priv;
-
-  /* Remove queue entry early in the process, otherwise a new
-     queue_redraw() during signal handling could put back this
-     object in the stage redraw list (but the entry is freed as
-     soon as we return from this function, causing a segfault
-     later)
-  */
-  priv->queue_redraw_entry = NULL;
-}
-
 void
 _clutter_actor_queue_redraw_full (ClutterActor             *self,
                                   const ClutterPaintVolume *volume,
@@ -7980,11 +7925,9 @@ _clutter_actor_queue_redraw_full (ClutterActor             *self,
   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
     return;
 
-  self->priv->queue_redraw_entry =
-    _clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
-                                       priv->queue_redraw_entry,
-                                       self,
-                                       volume);
+  clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
+                                    self,
+                                    volume);
 
   /* If this is the first redraw queued then we can directly use the
      effect parameter */
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
index 85f54e4976..6bfdeec79a 100644
--- a/clutter/clutter/clutter-stage-private.h
+++ b/clutter/clutter/clutter-stage-private.h
@@ -31,8 +31,6 @@
 
 G_BEGIN_DECLS
 
-typedef struct _ClutterStageQueueRedrawEntry ClutterStageQueueRedrawEntry;
-
 /* stage */
 ClutterStageWindow *_clutter_stage_get_default_window    (void);
 
@@ -89,11 +87,12 @@ ClutterActor *_clutter_stage_do_pick (ClutterStage    *stage,
 ClutterPaintVolume *_clutter_stage_paint_volume_stack_allocate (ClutterStage *stage);
 void                _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage);
 
-ClutterStageQueueRedrawEntry *_clutter_stage_queue_actor_redraw            (ClutterStage                 
*stage,
-                                                                            ClutterStageQueueRedrawEntry 
*entry,
-                                                                            ClutterActor                 
*actor,
-                                                                            const ClutterPaintVolume     
*clip);
-void                          _clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry 
*entry);
+void clutter_stage_queue_actor_redraw (ClutterStage             *stage,
+                                       ClutterActor             *actor,
+                                       const ClutterPaintVolume *clip);
+
+void clutter_stage_dequeue_actor_redraw (ClutterStage *stage,
+                                         ClutterActor *actor);
 
 void            _clutter_stage_add_pointer_drag_actor    (ClutterStage       *stage,
                                                           ClutterInputDevice *device,
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
index 6afc40ea5c..d9191c0a25 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -79,12 +79,11 @@
 
 #define MAX_FRUSTA 64
 
-struct _ClutterStageQueueRedrawEntry
+typedef struct _QueueRedrawEntry
 {
-  ClutterActor *actor;
   gboolean has_clip;
   ClutterPaintVolume clip;
-};
+} QueueRedrawEntry;
 
 typedef struct _PickRecord
 {
@@ -118,7 +117,7 @@ struct _ClutterStagePrivate
   GArray *paint_volume_stack;
 
   GSList *pending_relayouts;
-  GList *pending_queue_redraws;
+  GHashTable *pending_queue_redraws;
 
   gint sync_delay;
 
@@ -173,7 +172,7 @@ static guint stage_signals[LAST_SIGNAL] = { 0, };
 
 static const ClutterColor default_stage_color = { 255, 255, 255, 255 };
 
-static void free_queue_redraw_entry (ClutterStageQueueRedrawEntry *entry);
+static void free_queue_redraw_entry (QueueRedrawEntry *entry);
 static void capture_view_into (ClutterStage          *stage,
                                gboolean               paint,
                                ClutterStageView      *view,
@@ -1309,9 +1308,7 @@ clutter_stage_dispose (GObject *object)
 
   clutter_actor_destroy_all_children (CLUTTER_ACTOR (object));
 
-  g_list_free_full (priv->pending_queue_redraws,
-                    (GDestroyNotify) free_queue_redraw_entry);
-  priv->pending_queue_redraws = NULL;
+  g_hash_table_remove_all (priv->pending_queue_redraws);
 
   g_slist_free_full (priv->pending_relayouts,
                      (GDestroyNotify) g_object_unref);
@@ -1649,6 +1646,11 @@ clutter_stage_init (ClutterStage *self)
 
   clutter_stage_set_viewport (self, geom.width, geom.height);
 
+  priv->pending_queue_redraws =
+    g_hash_table_new_full (NULL, NULL,
+                           g_object_unref,
+                           (GDestroyNotify) free_queue_redraw_entry);
+
   priv->paint_volume_stack =
     g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
 
@@ -2714,13 +2716,13 @@ _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage)
  * paint volume so we can clip the redraw request even if the user
  * didn't explicitly do so.
  */
-ClutterStageQueueRedrawEntry *
-_clutter_stage_queue_actor_redraw (ClutterStage                 *stage,
-                                   ClutterStageQueueRedrawEntry *entry,
-                                   ClutterActor                 *actor,
-                                   const ClutterPaintVolume     *clip)
+void
+clutter_stage_queue_actor_redraw (ClutterStage             *stage,
+                                  ClutterActor             *actor,
+                                  const ClutterPaintVolume *clip)
 {
   ClutterStagePrivate *priv = stage->priv;
+  QueueRedrawEntry *entry = NULL;
 
   CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
                 _clutter_actor_get_debug_name (actor), clip);
@@ -2745,6 +2747,8 @@ _clutter_stage_queue_actor_redraw (ClutterStage                 *stage,
       priv->pending_finish_queue_redraws = TRUE;
     }
 
+  entry = g_hash_table_lookup (priv->pending_queue_redraws, actor);
+
   if (entry)
     {
       /* Ignore all requests to queue a redraw for an actor if a full
@@ -2754,7 +2758,7 @@ _clutter_stage_queue_actor_redraw (ClutterStage                 *stage,
           CLUTTER_NOTE (CLIPPING, "Bail from stage_queue_actor_redraw (%s): "
                         "Unclipped redraw of actor already queued",
                         _clutter_actor_get_debug_name (actor));
-          return entry;
+          return;
         }
 
       /* If queuing a clipped redraw and a clipped redraw has
@@ -2767,12 +2771,10 @@ _clutter_stage_queue_actor_redraw (ClutterStage                 *stage,
           clutter_paint_volume_free (&entry->clip);
           entry->has_clip = FALSE;
         }
-      return entry;
     }
   else
     {
-      entry = g_slice_new (ClutterStageQueueRedrawEntry);
-      entry->actor = g_object_ref (actor);
+      entry = g_slice_new (QueueRedrawEntry);
 
       if (clip)
         {
@@ -2783,40 +2785,24 @@ _clutter_stage_queue_actor_redraw (ClutterStage                 *stage,
       else
         entry->has_clip = FALSE;
 
-      stage->priv->pending_queue_redraws =
-        g_list_prepend (stage->priv->pending_queue_redraws, entry);
-
-      return entry;
+      g_hash_table_insert (priv->pending_queue_redraws,
+                           g_object_ref (actor), entry);
     }
 }
 
 static void
-free_queue_redraw_entry (ClutterStageQueueRedrawEntry *entry)
+free_queue_redraw_entry (QueueRedrawEntry *entry)
 {
-  if (entry->actor)
-    g_object_unref (entry->actor);
   if (entry->has_clip)
     clutter_paint_volume_free (&entry->clip);
-  g_slice_free (ClutterStageQueueRedrawEntry, entry);
+  g_slice_free (QueueRedrawEntry, entry);
 }
 
 void
-_clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry *entry)
+clutter_stage_dequeue_actor_redraw (ClutterStage *self,
+                                    ClutterActor *actor)
 {
-  if (entry == NULL)
-    return;
-
-  if (entry->actor != NULL)
-    {
-      g_object_unref (entry->actor);
-      entry->actor = NULL;
-    }
-
-  if (entry->has_clip)
-    {
-      clutter_paint_volume_free (&entry->clip);
-      entry->has_clip = FALSE;
-    }
+  g_hash_table_remove (self->priv->pending_queue_redraws, actor);
 }
 
 static void
@@ -2882,7 +2868,8 @@ void
 clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
 {
   ClutterStagePrivate *priv = stage->priv;
-  GList *l;
+  GHashTableIter iter;
+  gpointer key, value;
 
   COGL_TRACE_BEGIN_SCOPED (ClutterStageFinishQueueRedraws, "FinishQueueRedraws");
 
@@ -2891,50 +2878,44 @@ clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
 
   priv->pending_finish_queue_redraws = FALSE;
 
-  for (l = priv->pending_queue_redraws; l; l = l->next)
+  g_hash_table_iter_init (&iter, priv->pending_queue_redraws);
+  while (g_hash_table_iter_next (&iter, &key, &value))
     {
-      ClutterStageQueueRedrawEntry *entry = l->data;
+      ClutterActor *redraw_actor = key;
+      QueueRedrawEntry *entry = value;
+      ClutterPaintVolume old_actor_pv, new_actor_pv;
 
-      /* NB: Entries may be invalidated if the actor gets destroyed */
-      if (G_LIKELY (entry->actor != NULL))
-        {
-          ClutterPaintVolume old_actor_pv, new_actor_pv;
-
-          _clutter_paint_volume_init_static (&old_actor_pv, NULL);
-          _clutter_paint_volume_init_static (&new_actor_pv, NULL);
+      _clutter_paint_volume_init_static (&old_actor_pv, NULL);
+      _clutter_paint_volume_init_static (&new_actor_pv, NULL);
 
-          if (entry->has_clip)
-            {
-              add_to_stage_clip (stage, &entry->clip);
-            }
-          else if (clutter_actor_get_redraw_clip (entry->actor,
-                                                  &old_actor_pv,
-                                                  &new_actor_pv))
-            {
-              /* Add both the old paint volume of the actor (which is
-               * currently visible on the screen) and the new paint volume
-               * (which will be visible on the screen after this redraw)
-               * to the redraw clip.
-               * The former we do to ensure the old texture on the screen
-               * will be fully painted over in case the actor was moved.
-               */
-              add_to_stage_clip (stage, &old_actor_pv);
-              add_to_stage_clip (stage, &new_actor_pv);
-            }
-          else
-            {
-              /* If there's no clip we can use, we have to trigger an
-               * unclipped full stage redraw.
-               */
-              add_to_stage_clip (stage, NULL);
-            }
+      if (entry->has_clip)
+        {
+          add_to_stage_clip (stage, &entry->clip);
+        }
+      else if (clutter_actor_get_redraw_clip (redraw_actor,
+                                              &old_actor_pv,
+                                              &new_actor_pv))
+        {
+          /* Add both the old paint volume of the actor (which is
+           * currently visible on the screen) and the new paint volume
+           * (which will be visible on the screen after this redraw)
+           * to the redraw clip.
+           * The former we do to ensure the old texture on the screen
+           * will be fully painted over in case the actor was moved.
+           */
+          add_to_stage_clip (stage, &old_actor_pv);
+          add_to_stage_clip (stage, &new_actor_pv);
+        }
+      else
+        {
+          /* If there's no clip we can use, we have to trigger an
+           * unclipped full stage redraw.
+           */
+          add_to_stage_clip (stage, NULL);
         }
 
-      free_queue_redraw_entry (entry);
+      g_hash_table_iter_remove (&iter);
     }
-
-  g_list_free (priv->pending_queue_redraws);
-  priv->pending_queue_redraws = NULL;
 }
 
 /**


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]