clutter/actor: Add API to get the stage-views an actor is painted on

There are certain rendering techniques and optimizations, for example
the unredirection of non-fullscreen windows, where information about the
output/stage-view an actor is on is needed to determine whether the
optimization can be enabled.

So add a new method to ClutterActor that allows listing the stage-views
the actor is being painted on: clutter_actor_peek_stage_views()

With the way Clutter works, the only point where we can reliably get
this information is during or right before the paint phase, when the
layout phase of the stage has been completed and no more changes to the
actors transformation matrices happen. So to get the stage views the
actor is on, introduce a new step that's done on every master clock tick
between layout and paint cycle: Traversing through the actor tree and
updating the stage-views the mapped actors are going to be painted on.

We're doing this in a separate step instead of inside
clutter_actor_paint() itself for a few reasons: It keeps the code
separate from the painting code, making profiling easier and issues
easier to track down (hopefully), it allows for a new
"stage-views-changed" signal that doesn't interfere with painting, and
finally, it will make it very easy to update the resource scales in the
same step in the future.

Currently, this list is only invalidated on allocation changes of
actors, but not on changes to the transformation matrices. That's
because there's no proper API to invalidate the transformation matrices
ClutterActor implementations can apply through the apply_transform()
vfunc.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196
This commit is contained in:
Jonas Dreßler 2020-04-10 00:34:49 +02:00 committed by Carlos Garnacho
parent 8127494e52
commit 675a97d58e
4 changed files with 129 additions and 2 deletions

View File

@ -321,6 +321,8 @@ gboolean _clutter_actor_get_real_resource_scale
ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self, ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self,
CoglTexture *texture); CoglTexture *texture);
void clutter_actor_update_stage_views (ClutterActor *self);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_ACTOR_PRIVATE_H__ */ #endif /* __CLUTTER_ACTOR_PRIVATE_H__ */

View File

@ -813,6 +813,8 @@ struct _ClutterActorPrivate
gulong font_changed_id; gulong font_changed_id;
gulong layout_changed_id; gulong layout_changed_id;
GList *stage_views;
/* bitfields: KEEP AT THE END */ /* bitfields: KEEP AT THE END */
/* fixed position and sizes */ /* fixed position and sizes */
@ -855,6 +857,7 @@ struct _ClutterActorPrivate
guint had_effects_on_last_paint_volume_update : 1; guint had_effects_on_last_paint_volume_update : 1;
guint needs_compute_resource_scale : 1; guint needs_compute_resource_scale : 1;
guint absolute_origin_changed : 1; guint absolute_origin_changed : 1;
guint needs_update_stage_views : 1;
}; };
enum enum
@ -2565,6 +2568,7 @@ static void
absolute_allocation_changed (ClutterActor *actor) absolute_allocation_changed (ClutterActor *actor)
{ {
actor->priv->needs_compute_resource_scale = TRUE; actor->priv->needs_compute_resource_scale = TRUE;
actor->priv->needs_update_stage_views = TRUE;
} }
static ClutterActorTraverseVisitFlags static ClutterActorTraverseVisitFlags
@ -4282,6 +4286,7 @@ typedef enum
REMOVE_CHILD_FLUSH_QUEUE = 1 << 4, REMOVE_CHILD_FLUSH_QUEUE = 1 << 4,
REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5, REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5,
REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6, REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6,
REMOVE_CHILD_CLEAR_STAGE_VIEWS = 1 << 7,
/* default flags for public API */ /* default flags for public API */
REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
@ -4290,14 +4295,16 @@ typedef enum
REMOVE_CHILD_EMIT_ACTOR_REMOVED | REMOVE_CHILD_EMIT_ACTOR_REMOVED |
REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_CHECK_STATE |
REMOVE_CHILD_FLUSH_QUEUE | REMOVE_CHILD_FLUSH_QUEUE |
REMOVE_CHILD_NOTIFY_FIRST_LAST, REMOVE_CHILD_NOTIFY_FIRST_LAST |
REMOVE_CHILD_CLEAR_STAGE_VIEWS,
/* flags for legacy/deprecated API */ /* flags for legacy/deprecated API */
REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_CHECK_STATE |
REMOVE_CHILD_FLUSH_QUEUE | REMOVE_CHILD_FLUSH_QUEUE |
REMOVE_CHILD_EMIT_PARENT_SET | REMOVE_CHILD_EMIT_PARENT_SET |
REMOVE_CHILD_NOTIFY_FIRST_LAST REMOVE_CHILD_NOTIFY_FIRST_LAST |
REMOVE_CHILD_CLEAR_STAGE_VIEWS
} ClutterActorRemoveChildFlags; } ClutterActorRemoveChildFlags;
/*< private > /*< private >
@ -4319,6 +4326,7 @@ clutter_actor_remove_child_internal (ClutterActor *self,
gboolean notify_first_last; gboolean notify_first_last;
gboolean was_mapped; gboolean was_mapped;
gboolean stop_transitions; gboolean stop_transitions;
gboolean clear_stage_views;
GObject *obj; GObject *obj;
if (self == child) if (self == child)
@ -4335,6 +4343,7 @@ clutter_actor_remove_child_internal (ClutterActor *self,
flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0; flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0; notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0; stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
clear_stage_views = (flags & REMOVE_CHILD_CLEAR_STAGE_VIEWS) != 0;
obj = G_OBJECT (self); obj = G_OBJECT (self);
g_object_freeze_notify (obj); g_object_freeze_notify (obj);
@ -4408,6 +4417,13 @@ clutter_actor_remove_child_internal (ClutterActor *self,
clutter_actor_queue_compute_expand (self); clutter_actor_queue_compute_expand (self);
} }
/* Only actors which are attached to a stage get notified about changes
* to the stage views, so make sure all the stage-views lists are
* cleared as the child and its children leave the actor tree.
*/
if (clear_stage_views)
clutter_actor_clear_stage_views_recursive (child);
if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child))
{ {
child->priv->needs_compute_resource_scale = TRUE; child->priv->needs_compute_resource_scale = TRUE;
@ -6080,6 +6096,8 @@ clutter_actor_dispose (GObject *object)
priv->clones = NULL; priv->clones = NULL;
} }
g_clear_pointer (&priv->stage_views, g_list_free);
G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
} }
@ -8634,6 +8652,7 @@ clutter_actor_init (ClutterActor *self)
priv->needs_allocation = TRUE; priv->needs_allocation = TRUE;
priv->needs_paint_volume_update = TRUE; priv->needs_paint_volume_update = TRUE;
priv->needs_compute_resource_scale = TRUE; priv->needs_compute_resource_scale = TRUE;
priv->needs_update_stage_views = TRUE;
priv->cached_width_age = 1; priv->cached_width_age = 1;
priv->cached_height_age = 1; priv->cached_height_age = 1;
@ -17521,7 +17540,11 @@ clear_stage_views_cb (ClutterActor *actor,
int depth, int depth,
gpointer user_data) gpointer user_data)
{ {
actor->priv->needs_update_stage_views = TRUE;
actor->priv->needs_compute_resource_scale = TRUE; actor->priv->needs_compute_resource_scale = TRUE;
g_clear_pointer (&actor->priv->stage_views, g_list_free);
return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
} }
@ -17622,6 +17645,93 @@ clutter_actor_get_resource_scale (ClutterActor *self,
return FALSE; return FALSE;
} }
static void
update_stage_views (ClutterActor *self)
{
ClutterActorPrivate *priv = self->priv;
ClutterStage *stage;
graphene_rect_t bounding_rect;
g_clear_pointer (&priv->stage_views, g_list_free);
if (priv->needs_allocation)
{
g_warning ("Can't update stage views actor %s is on because it needs an "
"allocation.", _clutter_actor_get_debug_name (self));
return;
}
stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
g_return_if_fail (stage);
clutter_actor_get_transformed_position (self,
&bounding_rect.origin.x,
&bounding_rect.origin.y);
clutter_actor_get_transformed_size (self,
&bounding_rect.size.width,
&bounding_rect.size.height);
if (bounding_rect.size.width == 0.0 ||
bounding_rect.size.height == 0.0)
return;
priv->stage_views = clutter_stage_get_views_for_rect (stage,
&bounding_rect);
}
void
clutter_actor_update_stage_views (ClutterActor *self)
{
ClutterActorPrivate *priv = self->priv;
ClutterActor *child;
if (!CLUTTER_ACTOR_IS_MAPPED (self) ||
CLUTTER_ACTOR_IN_DESTRUCTION (self))
return;
if (priv->needs_update_stage_views)
{
update_stage_views (self);
priv->needs_update_stage_views = FALSE;
}
for (child = priv->first_child; child; child = child->priv->next_sibling)
clutter_actor_update_stage_views (child);
}
/**
* clutter_actor_peek_stage_views:
* @self: A #ClutterActor
*
* Retrieves the list of #ClutterStageView<!-- -->s the actor is being
* painted on.
*
* If this function is called during the paint cycle, the list is guaranteed
* to be up-to-date, if called outside the paint cycle, the list will
* contain the views the actor was painted on last.
*
* The list returned by this function is not updated when the actors
* visibility changes: If an actor gets hidden and is not being painted
* anymore, this function will return the list of views the actor was
* painted on last.
*
* If an actor is not attached to a stage (realized), this function will
* always return an empty list.
*
* Returns: (transfer none) (element-type Clutter.StageView): The list of
* #ClutterStageView<!-- -->s the actor is being painted on. The list and
* its contents are owned by the #ClutterActor and the list may not be
* freed or modified.
*/
GList *
clutter_actor_peek_stage_views (ClutterActor *self)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
return self->priv->stage_views;
}
/** /**
* clutter_actor_has_overlaps: * clutter_actor_has_overlaps:
* @self: A #ClutterActor * @self: A #ClutterActor

View File

@ -919,6 +919,9 @@ void clutter_actor_pick_box (ClutterActor *self,
ClutterPickContext *pick_context, ClutterPickContext *pick_context,
const ClutterActorBox *box); const ClutterActorBox *box);
CLUTTER_EXPORT
GList * clutter_actor_peek_stage_views (ClutterActor *self);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_ACTOR_H__ */ #endif /* __CLUTTER_ACTOR_H__ */

View File

@ -1469,6 +1469,14 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage)
return updating; return updating;
} }
static void
update_actor_stage_views (ClutterStage *stage)
{
ClutterActor *actor = CLUTTER_ACTOR (stage);
clutter_actor_update_stage_views (actor);
}
/** /**
* _clutter_stage_do_update: * _clutter_stage_do_update:
* @stage: A #ClutterStage * @stage: A #ClutterStage
@ -1516,6 +1524,10 @@ _clutter_stage_do_update (ClutterStage *stage)
if (stage_was_relayout) if (stage_was_relayout)
pointers = _clutter_stage_check_updated_pointers (stage); pointers = _clutter_stage_check_updated_pointers (stage);
COGL_TRACE_BEGIN (ClutterStageUpdateActorStageViews, "Actor stage-views");
update_actor_stage_views (stage);
COGL_TRACE_END (ClutterStageUpdateActorStageViews);
COGL_TRACE_BEGIN (ClutterStagePaint, "Paint"); COGL_TRACE_BEGIN (ClutterStagePaint, "Paint");
clutter_stage_maybe_finish_queue_redraws (stage); clutter_stage_maybe_finish_queue_redraws (stage);