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>
This commit is contained in:
Jonas Dreßler 2020-11-21 10:07:06 +01:00
parent 0da8a49719
commit 0320649a1c

View File

@ -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()