clutter/actor: Emit the queue-redraw signal right away

Since we now decoupled the "queue-redraw" signal from creating the stage
clip, we can move signal emission into
_clutter_actor_queue_redraw_full() and emit the signal right away when
queueing a redraw on an actor. With that we now no longer have to
accommodate for the stage pending_queue_redraws list changing while
iterating over it.

To ensure we don't emit the signal too often when multiple redraws are
queued on one actor, use the propagated_one_redraw flag to limit the
number of emissions to a single one for every update cycle.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1511>
This commit is contained in:
Jonas Dreßler 2020-10-19 16:48:53 +02:00 committed by Marge Bot
parent 9b16eff784
commit ce4c297cea
2 changed files with 53 additions and 106 deletions

View File

@ -8023,8 +8023,6 @@ _clutter_actor_finish_queue_redraw (ClutterActor *self)
later) later)
*/ */
priv->queue_redraw_entry = NULL; priv->queue_redraw_entry = NULL;
_clutter_actor_propagate_queue_redraw (self, self);
} }
void void
@ -8041,63 +8039,26 @@ _clutter_actor_queue_redraw_full (ClutterActor *self,
* wrapper for this function. Additionally, an effect can queue a * wrapper for this function. Additionally, an effect can queue a
* redraw by wrapping this function in clutter_effect_queue_repaint(). * redraw by wrapping this function in clutter_effect_queue_repaint().
* *
* This function will emit the "queue-redraw" signal for each actor
* up the actor-tree, allowing to track redraws queued by children
* or to queue a redraw of a different actor (like a clone) in
* response to this one.
*
* This functions queues an entry in a list associated with the * This functions queues an entry in a list associated with the
* stage which is a list of actors that queued a redraw while * stage which is a list of actors that queued a redraw while
* updating the timelines, performing layouting and processing other * updating the timelines, performing layouting and processing other
* mainloop sources before the next paint starts. * mainloop sources before the next paint starts.
* *
* We aim to minimize the processing done at this point because
* there is a good chance other events will happen while updating
* the scenegraph that would invalidate any expensive work we might
* otherwise try to do here. For example we don't try and resolve
* the screen space bounding box of an actor at this stage so as to
* minimize how much of the screen redraw because it's possible
* something else will happen which will force a full redraw anyway.
*
* When all updates are complete and we come to paint the stage then * When all updates are complete and we come to paint the stage then
* we iterate this list and actually emit the "queue-redraw" signals * we iterate this list and build the redraw clip of the stage by
* for each of the listed actors which will bubble up to the stage * either using the clip that was supplied to
* for each actor and at that point we will transform the actors * _clutter_actor_queue_redraw_full() or by asking the actor for its
* paint volume into screen coordinates to determine the clip region * redraw clip using clutter_actor_get_redraw_clip().
* for what needs to be redrawn in the next paint.
* *
* Besides minimizing redundant work another reason for this * Doing this later during the stage update instead of now is an
* deferred design is that it's more likely we will be able to * important optimization, because later it's more likely we will be
* determine the paint volume of an actor once we've finished * able to determine the paint volume of an actor (its allocation
* updating the scenegraph because its allocation should be up to * should be up to date).
* date. NB: If we can't determine an actors paint volume then we
* can't automatically queue a clipped redraw which can make a big
* difference to performance.
*
* So the control flow goes like this:
* One of clutter_actor_queue_redraw(),
* or clutter_effect_queue_repaint()
*
* then control moves to:
* _clutter_stage_queue_actor_redraw()
*
* later during _clutter_stage_do_update(), once relayouting is done
* and the scenegraph has been updated we will call:
* clutter_stage_maybe_finish_queue_redraws().
*
* clutter_stage_maybe_finish_queue_redraws() will call
* _clutter_actor_finish_queue_redraw() for each listed actor.
*
* Note: actors *are* allowed to queue further redraws during this
* process (considering clone actors or texture_new_from_actor which
* respond to their source queueing a redraw by queuing a redraw
* themselves). We repeat the process until the list is empty.
*
* This will result in the "queue-redraw" signal being fired for
* each actor which will pass control to the default signal handler:
* clutter_actor_real_queue_redraw()
*
* This will bubble up to the stages handler:
* clutter_stage_real_queue_redraw()
*
* clutter_stage_real_queue_redraw() will transform the actors paint
* volume into screen space and add it as a clip region for the next
* paint.
*/ */
/* ignore queueing a redraw for actors being destroyed */ /* ignore queueing a redraw for actors being destroyed */
@ -8179,6 +8140,9 @@ _clutter_actor_queue_redraw_full (ClutterActor *self,
} }
priv->is_dirty = TRUE; priv->is_dirty = TRUE;
if (!priv->propagated_one_redraw)
_clutter_actor_propagate_queue_redraw (self, self);
} }
/** /**

View File

@ -2882,6 +2882,7 @@ void
clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage) clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
{ {
ClutterStagePrivate *priv = stage->priv; ClutterStagePrivate *priv = stage->priv;
GList *l;
COGL_TRACE_BEGIN_SCOPED (ClutterStageFinishQueueRedraws, "FinishQueueRedraws"); COGL_TRACE_BEGIN_SCOPED (ClutterStageFinishQueueRedraws, "FinishQueueRedraws");
@ -2890,23 +2891,7 @@ clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
priv->pending_finish_queue_redraws = FALSE; priv->pending_finish_queue_redraws = FALSE;
/* Note: we have to repeat until the pending_queue_redraws list is for (l = priv->pending_queue_redraws; l; l = l->next)
* empty because actors are allowed to queue redraws in response to
* the queue-redraw signal. For example Clone actors or
* texture_new_from_actor actors will have to queue a redraw if
* their source queues a redraw.
*/
while (stage->priv->pending_queue_redraws)
{
GList *l;
/* XXX: we need to allow stage->priv->pending_queue_redraws to
* be updated while we process the current entries in the list
* so we steal the list pointer and then reset it to an empty
* list before processing... */
GList *stolen_list = stage->priv->pending_queue_redraws;
stage->priv->pending_queue_redraws = NULL;
for (l = stolen_list; l; l = l->next)
{ {
ClutterStageQueueRedrawEntry *entry = l->data; ClutterStageQueueRedrawEntry *entry = l->data;
@ -2915,8 +2900,6 @@ clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
{ {
ClutterPaintVolume old_actor_pv, new_actor_pv; ClutterPaintVolume old_actor_pv, new_actor_pv;
_clutter_actor_finish_queue_redraw (entry->actor);
_clutter_paint_volume_init_static (&old_actor_pv, NULL); _clutter_paint_volume_init_static (&old_actor_pv, NULL);
_clutter_paint_volume_init_static (&new_actor_pv, NULL); _clutter_paint_volume_init_static (&new_actor_pv, NULL);
@ -2945,13 +2928,13 @@ clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
*/ */
add_to_stage_clip (stage, NULL); add_to_stage_clip (stage, NULL);
} }
} }
free_queue_redraw_entry (entry); free_queue_redraw_entry (entry);
} }
g_list_free (stolen_list);
} g_list_free (priv->pending_queue_redraws);
priv->pending_queue_redraws = NULL;
} }
/** /**