From 675a97d58e3d4602a7905d49fe06c7b2ca60feea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 10 Apr 2020 00:34:49 +0200 Subject: [PATCH] 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 --- clutter/clutter/clutter-actor-private.h | 2 + clutter/clutter/clutter-actor.c | 114 +++++++++++++++++++++++- clutter/clutter/clutter-actor.h | 3 + clutter/clutter/clutter-stage.c | 12 +++ 4 files changed, 129 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h index 3b6a4153b..ed398bf21 100644 --- a/clutter/clutter/clutter-actor-private.h +++ b/clutter/clutter/clutter-actor-private.h @@ -321,6 +321,8 @@ gboolean _clutter_actor_get_real_resource_scale ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self, CoglTexture *texture); +void clutter_actor_update_stage_views (ClutterActor *self); + G_END_DECLS #endif /* __CLUTTER_ACTOR_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 5ea4cc1c9..1d3f98d41 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -813,6 +813,8 @@ struct _ClutterActorPrivate gulong font_changed_id; gulong layout_changed_id; + GList *stage_views; + /* bitfields: KEEP AT THE END */ /* fixed position and sizes */ @@ -855,6 +857,7 @@ struct _ClutterActorPrivate guint had_effects_on_last_paint_volume_update : 1; guint needs_compute_resource_scale : 1; guint absolute_origin_changed : 1; + guint needs_update_stage_views : 1; }; enum @@ -2565,6 +2568,7 @@ static void absolute_allocation_changed (ClutterActor *actor) { actor->priv->needs_compute_resource_scale = TRUE; + actor->priv->needs_update_stage_views = TRUE; } static ClutterActorTraverseVisitFlags @@ -4282,6 +4286,7 @@ typedef enum 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, /* default flags for public API */ REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | @@ -4290,14 +4295,16 @@ typedef enum REMOVE_CHILD_EMIT_ACTOR_REMOVED | REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_FLUSH_QUEUE | - REMOVE_CHILD_NOTIFY_FIRST_LAST, + REMOVE_CHILD_NOTIFY_FIRST_LAST | + REMOVE_CHILD_CLEAR_STAGE_VIEWS, /* flags for legacy/deprecated API */ REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_FLUSH_QUEUE | REMOVE_CHILD_EMIT_PARENT_SET | - REMOVE_CHILD_NOTIFY_FIRST_LAST + REMOVE_CHILD_NOTIFY_FIRST_LAST | + REMOVE_CHILD_CLEAR_STAGE_VIEWS } ClutterActorRemoveChildFlags; /*< private > @@ -4319,6 +4326,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, gboolean notify_first_last; gboolean was_mapped; gboolean stop_transitions; + gboolean clear_stage_views; GObject *obj; if (self == child) @@ -4335,6 +4343,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, 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; obj = G_OBJECT (self); g_object_freeze_notify (obj); @@ -4408,6 +4417,13 @@ clutter_actor_remove_child_internal (ClutterActor *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)) { child->priv->needs_compute_resource_scale = TRUE; @@ -6080,6 +6096,8 @@ clutter_actor_dispose (GObject *object) priv->clones = NULL; } + g_clear_pointer (&priv->stage_views, g_list_free); + G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); } @@ -8634,6 +8652,7 @@ clutter_actor_init (ClutterActor *self) priv->needs_allocation = TRUE; priv->needs_paint_volume_update = TRUE; priv->needs_compute_resource_scale = TRUE; + priv->needs_update_stage_views = TRUE; priv->cached_width_age = 1; priv->cached_height_age = 1; @@ -17521,7 +17540,11 @@ clear_stage_views_cb (ClutterActor *actor, int depth, gpointer user_data) { + actor->priv->needs_update_stage_views = TRUE; actor->priv->needs_compute_resource_scale = TRUE; + + g_clear_pointer (&actor->priv->stage_views, g_list_free); + return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } @@ -17622,6 +17645,93 @@ clutter_actor_get_resource_scale (ClutterActor *self, 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 #ClutterStageViews 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 + * #ClutterStageViews 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: * @self: A #ClutterActor diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h index b5d4b7845..616e80169 100644 --- a/clutter/clutter/clutter-actor.h +++ b/clutter/clutter/clutter-actor.h @@ -919,6 +919,9 @@ void clutter_actor_pick_box (ClutterActor *self, ClutterPickContext *pick_context, const ClutterActorBox *box); +CLUTTER_EXPORT +GList * clutter_actor_peek_stage_views (ClutterActor *self); + G_END_DECLS #endif /* __CLUTTER_ACTOR_H__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 5326b1cc2..ff079c1ad 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -1469,6 +1469,14 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage) 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: * @stage: A #ClutterStage @@ -1516,6 +1524,10 @@ _clutter_stage_do_update (ClutterStage *stage) if (stage_was_relayout) 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"); clutter_stage_maybe_finish_queue_redraws (stage);