clutter/actor: Properly invalidate cached paint volumes of actors

The priv->paint_volume field of ClutterActor stores the cached paint
volume in the actors local coordinate system. It consist of the actors
paint volume itself and the union of all children paint volumes.

We want to invalidate those cached paint volumes according to the
following rules:

- If an actors transformation matrix changes, all paint volumes of the
parent-tree need to be invalidated (that's because the parent-volumes
have unioned the actors paint volume). Our own paint volume does not
need invalidation since the transformation matrix is not applied to it.

- If an actors allocation-size changes, its own paint volume and all the
volumes of the parent-tree need to be invalidated. That's because the
allocation-size is used as the size of the paint volume.

- If a clip gets set or clip_to_allocation gets enabled for an actor,
its own paint volume and all the volumes of the parent-tree need to be
invalidated. That's because the clip is factored in when creating the
paint volume.

So far we did this invalidation in various places and the invalidation
up the parent-tree happened inside clutter_actor_real_queue_relayout().
We did not invalidate on changes to the actors transformation matrices
and the invalidation in clutter_actor_real_queue_relayout() was more
like a "big hammer" that probably invalidated unnecessarily a few times.

So introduce proper infrastructure to invalidate those cached paint
volumes of actors only in the cases where they actually need to be
invalidated. To do that, we reuse the transform_changed() function and
introduce a new function queue_update_paint_volume() that invalidates
the paint volumes up the actor tree.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1773>
This commit is contained in:
Jonas Dreßler 2020-10-12 10:58:38 +02:00 committed by Marge Bot
parent 7aa147829d
commit 3d17e8d590

View File

@ -1501,6 +1501,16 @@ queue_update_stage_views (ClutterActor *actor)
} }
} }
static void
queue_update_paint_volume (ClutterActor *actor)
{
while (actor)
{
actor->priv->needs_paint_volume_update = TRUE;
actor = actor->priv->parent;
}
}
static void static void
clutter_actor_real_map (ClutterActor *self) clutter_actor_real_map (ClutterActor *self)
{ {
@ -1516,8 +1526,6 @@ clutter_actor_real_map (ClutterActor *self)
if (priv->unmapped_paint_branch_counter == 0) if (priv->unmapped_paint_branch_counter == 0)
{ {
priv->needs_paint_volume_update = TRUE;
/* We skip unmapped actors when updating the stage-views list, so if /* We skip unmapped actors when updating the stage-views list, so if
* an actors list got invalidated while it was unmapped make sure to * an actors list got invalidated while it was unmapped make sure to
* set priv->needs_update_stage_views to TRUE for all actors up the * set priv->needs_update_stage_views to TRUE for all actors up the
@ -2463,6 +2471,9 @@ transform_changed (ClutterActor *actor)
{ {
actor->priv->transform_valid = FALSE; actor->priv->transform_valid = FALSE;
if (actor->priv->parent)
queue_update_paint_volume (actor->priv->parent);
_clutter_actor_traverse (actor, _clutter_actor_traverse (actor,
CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
absolute_geometry_changed_cb, absolute_geometry_changed_cb,
@ -2492,7 +2503,7 @@ clutter_actor_set_allocation_internal (ClutterActor *self,
{ {
ClutterActorPrivate *priv = self->priv; ClutterActorPrivate *priv = self->priv;
GObject *obj; GObject *obj;
gboolean x1_changed, y1_changed, x2_changed, y2_changed; gboolean origin_changed, size_changed;
ClutterActorBox old_alloc = { 0, }; ClutterActorBox old_alloc = { 0, };
g_return_if_fail (!isnan (box->x1) && !isnan (box->x2) && g_return_if_fail (!isnan (box->x1) && !isnan (box->x2) &&
@ -2504,10 +2515,11 @@ clutter_actor_set_allocation_internal (ClutterActor *self,
clutter_actor_store_old_geometry (self, &old_alloc); clutter_actor_store_old_geometry (self, &old_alloc);
x1_changed = priv->allocation.x1 != box->x1; origin_changed =
y1_changed = priv->allocation.y1 != box->y1; priv->allocation.x1 != box->x1 || priv->allocation.y1 != box->y1;
x2_changed = priv->allocation.x2 != box->x2; size_changed =
y2_changed = priv->allocation.y2 != box->y2; priv->allocation.x2 - priv->allocation.x1 != box->x2 - box->x1 ||
priv->allocation.y2 - priv->allocation.y1 != box->y2 - box->y1;
priv->allocation = *box; priv->allocation = *box;
@ -2516,10 +2528,7 @@ clutter_actor_set_allocation_internal (ClutterActor *self,
priv->needs_height_request = FALSE; priv->needs_height_request = FALSE;
priv->needs_allocation = FALSE; priv->needs_allocation = FALSE;
if (x1_changed || if (origin_changed || size_changed)
y1_changed ||
x2_changed ||
y2_changed)
{ {
CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed", CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed",
_clutter_actor_get_debug_name (self)); _clutter_actor_get_debug_name (self));
@ -2527,6 +2536,9 @@ clutter_actor_set_allocation_internal (ClutterActor *self,
/* This will also call absolute_geometry_changed() on the subtree */ /* This will also call absolute_geometry_changed() on the subtree */
transform_changed (self); transform_changed (self);
if (size_changed)
queue_update_paint_volume (self);
g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]); g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]);
/* if the allocation changes, so does the content box */ /* if the allocation changes, so does the content box */
@ -2663,7 +2675,6 @@ clutter_actor_real_queue_relayout (ClutterActor *self)
priv->needs_width_request = TRUE; priv->needs_width_request = TRUE;
priv->needs_height_request = TRUE; priv->needs_height_request = TRUE;
priv->needs_allocation = TRUE; priv->needs_allocation = TRUE;
priv->needs_paint_volume_update = TRUE;
/* reset the cached size requests */ /* reset the cached size requests */
memset (priv->width_requests, 0, memset (priv->width_requests, 0,
@ -2675,18 +2686,9 @@ clutter_actor_real_queue_relayout (ClutterActor *self)
if (priv->parent != NULL) if (priv->parent != NULL)
{ {
if (priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT) if (priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)
{ clutter_actor_queue_shallow_relayout (self);
clutter_actor_queue_shallow_relayout (self);
/* The above might have invalidated the parent's paint volume if self
* has moved or resized. DnD seems to require this...
*/
priv->parent->priv->needs_paint_volume_update = TRUE;
}
else else
{ _clutter_actor_queue_only_relayout (priv->parent);
_clutter_actor_queue_only_relayout (priv->parent);
}
} }
} }
@ -4757,6 +4759,7 @@ clutter_actor_set_clip_rect (ClutterActor *self,
else else
priv->has_clip = FALSE; priv->has_clip = FALSE;
queue_update_paint_volume (self);
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (self);
g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]); g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
@ -9267,9 +9270,6 @@ clutter_actor_allocate (ClutterActor *self,
return; return;
} }
if (CLUTTER_ACTOR_IS_MAPPED (self))
self->priv->needs_paint_volume_update = TRUE;
if (!origin_changed && !size_changed) if (!origin_changed && !size_changed)
{ {
/* If the actor didn't move but needs_allocation is set, we just /* If the actor didn't move but needs_allocation is set, we just
@ -11142,6 +11142,7 @@ clutter_actor_set_clip (ClutterActor *self,
priv->has_clip = TRUE; priv->has_clip = TRUE;
queue_update_paint_volume (self);
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (self);
g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]); g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
@ -11164,6 +11165,7 @@ clutter_actor_remove_clip (ClutterActor *self)
self->priv->has_clip = FALSE; self->priv->has_clip = FALSE;
queue_update_paint_volume (self);
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (self);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
@ -15132,6 +15134,7 @@ clutter_actor_set_clip_to_allocation (ClutterActor *self,
{ {
priv->clip_to_allocation = clip_set; priv->clip_to_allocation = clip_set;
queue_update_paint_volume (self);
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (self);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP_TO_ALLOCATION]); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP_TO_ALLOCATION]);
@ -19547,7 +19550,7 @@ clutter_actor_invalidate_paint_volume (ClutterActor *self)
{ {
g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (self));
self->priv->needs_paint_volume_update = TRUE; queue_update_paint_volume (self);
} }
gboolean gboolean