[mutter] clutter/actor: Always update last_paint_volumes during painting



commit 0320649a1c50993f308344e0dd296abaad0ee6d4
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Sat Nov 21 10:07:06 2020 +0100

    clutter/actor: Always update last_paint_volumes during painting
    
    It's currently possible that some last_paint_volumes don't get updated
    during a paint cycle, this can happen when a ClutterOffscreenEffect is
    used: The offscreen effect might skip painting the content and the
    children of an actor because it uses its own offscreened texture
    instead. This means the offscreen effect doesn't call
    clutter_actor_continue_paint(), and thus the the last_paint_volumes of
    the children won't be updated.
    
    Now one might think that isn't a problem, because as soon as a child
    changes it's size or position, the offscreened texture would get
    invalidated and clutter_actor_continue_paint() would get called. It's
    not that easy though: Because the last_paint_volume includes all the
    transformation matrices up to eye-coordinates, it has to be updated on
    any changes to matrices, which includes position/transformation changes
    to any actor up the hierarchy.
    
    Now that's where get into problems with the offscreen effect: In case of
    transformation changes to the offscreened actor or an actor up the
    hierarchy, the offscreened texture won't get invalidated (that makes
    sense, we can simply paint it transformed) and the last_paint_volumes
    won't get updated even though they should.
    
    This leaves us around with outdated last_paint_volumes where
    last_paint_volume_valid is still set to TRUE. It can cause issues with
    culling and clipped redraws.
    
    So fix that by ensuring that all children that would get painted by
    Clutter get their last_paint_volumes updated in case a ClutterEffect
    decided not to call clutter_actor_continue_paint().
    
    This ignores the case where a paint() vfunc override does the same and
    doesn't call clutter_actor_paint() on children. Let's ignore this case
    for now, there shouldn't be any implementation which does that and
    ideally in a world that's painted solely by ClutterContent, we can get
    rid of that vfunc in the future.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1591>

 clutter/clutter/clutter-actor.c | 47 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
---
diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
index c061f722f9..f00ce35b13 100644
--- a/clutter/clutter/clutter-actor.c
+++ b/clutter/clutter/clutter-actor.c
@@ -844,6 +844,7 @@ struct _ClutterActorPrivate
   guint needs_paint_volume_update   : 1;
   guint had_effects_on_last_paint_volume_update : 1;
   guint needs_update_stage_views    : 1;
+  guint children_painted            : 1;
 };
 
 enum
@@ -3586,6 +3587,30 @@ clutter_actor_paint_node (ClutterActor        *actor,
   return TRUE;
 }
 
+static void
+ensure_last_paint_volumes_updated (ClutterActor *self)
+{
+  ClutterActorPrivate *priv = self->priv;
+  ClutterActor *child;
+
+  /* Same entry checks as in clutter_actor_paint() */
+  if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
+    return;
+
+  if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
+      ((priv->opacity_override >= 0) ?
+       priv->opacity_override : priv->opacity) == 0)
+    return;
+
+  if (!CLUTTER_ACTOR_IS_MAPPED (self))
+    return;
+
+  _clutter_actor_update_last_paint_volume (self);
+
+  for (child = priv->first_child; child; child = child->priv->next_sibling)
+    ensure_last_paint_volumes_updated (child);
+}
+
 /**
  * clutter_actor_paint:
  * @self: A #ClutterActor
@@ -3791,8 +3816,28 @@ clutter_actor_paint (ClutterActor        *self,
   if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES))
     _clutter_actor_draw_paint_volume (self, actor_node);
 
+  priv->children_painted = FALSE;
+
   clutter_paint_node_paint (root_node, paint_context);
 
+  /* If an effect choose to not call clutter_actor_continue_paint()
+   * (for example offscreen effects might just paint their cached
+   * texture instead), the last_paint_volumes of the whole subtree
+   * still need to be updated to adjust for any changes to their
+   * eye-coordinates transformation matrices.
+   */
+  if (!priv->children_painted)
+    {
+      if (!culling_inhibited &&
+          !in_clone_paint () &&
+          G_LIKELY ((clutter_paint_debug_flags &
+                     (CLUTTER_DEBUG_DISABLE_CULLING |
+                      CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
+                    (CLUTTER_DEBUG_DISABLE_CULLING |
+                     CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
+        ensure_last_paint_volumes_updated (self);
+    }
+
   /* If we make it here then the actor has run through a complete
      paint run including all the effects so it's no longer dirty */
   priv->is_dirty = FALSE;
@@ -3835,6 +3880,8 @@ clutter_actor_continue_paint (ClutterActor        *self,
       CoglFramebuffer *framebuffer;
       ClutterPaintNode *dummy;
 
+      priv->children_painted = TRUE;
+
       /* XXX - this will go away in 2.0, when we can get rid of this
        * stuff and switch to a pure retained render tree of PaintNodes
        * for the entire frame, starting from the Stage; the paint()


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