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)
*/
priv->queue_redraw_entry = NULL;
_clutter_actor_propagate_queue_redraw (self, self);
}
void
@ -8041,63 +8039,26 @@ _clutter_actor_queue_redraw_full (ClutterActor *self,
* wrapper for this function. Additionally, an effect can queue a
* 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
* stage which is a list of actors that queued a redraw while
* updating the timelines, performing layouting and processing other
* 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
* we iterate this list and actually emit the "queue-redraw" signals
* for each of the listed actors which will bubble up to the stage
* for each actor and at that point we will transform the actors
* paint volume into screen coordinates to determine the clip region
* for what needs to be redrawn in the next paint.
* we iterate this list and build the redraw clip of the stage by
* either using the clip that was supplied to
* _clutter_actor_queue_redraw_full() or by asking the actor for its
* redraw clip using clutter_actor_get_redraw_clip().
*
* Besides minimizing redundant work another reason for this
* deferred design is that it's more likely we will be able to
* determine the paint volume of an actor once we've finished
* updating the scenegraph because its allocation should be up to
* 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.
* Doing this later during the stage update instead of now is an
* important optimization, because later it's more likely we will be
* able to determine the paint volume of an actor (its allocation
* should be up to date).
*/
/* ignore queueing a redraw for actors being destroyed */
@ -8179,6 +8140,9 @@ _clutter_actor_queue_redraw_full (ClutterActor *self,
}
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)
{
ClutterStagePrivate *priv = stage->priv;
GList *l;
COGL_TRACE_BEGIN_SCOPED (ClutterStageFinishQueueRedraws, "FinishQueueRedraws");
@ -2890,68 +2891,50 @@ clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
priv->pending_finish_queue_redraws = FALSE;
/* Note: we have to repeat until the pending_queue_redraws list is
* 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)
for (l = priv->pending_queue_redraws; l; l = l->next)
{
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;
ClutterStageQueueRedrawEntry *entry = l->data;
for (l = stolen_list; l; l = l->next)
/* NB: Entries may be invalidated if the actor gets destroyed */
if (G_LIKELY (entry->actor != NULL))
{
ClutterStageQueueRedrawEntry *entry = l->data;
ClutterPaintVolume old_actor_pv, new_actor_pv;
/* NB: Entries may be invalidated if the actor gets destroyed */
if (G_LIKELY (entry->actor != NULL))
_clutter_paint_volume_init_static (&old_actor_pv, NULL);
_clutter_paint_volume_init_static (&new_actor_pv, NULL);
if (entry->has_clip)
{
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 (&new_actor_pv, NULL);
if (entry->has_clip)
{
add_to_stage_clip (stage, &entry->clip);
}
else if (clutter_actor_get_redraw_clip (entry->actor,
&old_actor_pv,
&new_actor_pv))
{
/* Add both the old paint volume of the actor (which is
* currently visible on the screen) and the new paint volume
* (which will be visible on the screen after this redraw)
* to the redraw clip.
* The former we do to ensure the old texture on the screen
* will be fully painted over in case the actor was moved.
*/
add_to_stage_clip (stage, &old_actor_pv);
add_to_stage_clip (stage, &new_actor_pv);
}
else
{
/* If there's no clip we can use, we have to trigger an
* unclipped full stage redraw.
*/
add_to_stage_clip (stage, NULL);
}
add_to_stage_clip (stage, &entry->clip);
}
else if (clutter_actor_get_redraw_clip (entry->actor,
&old_actor_pv,
&new_actor_pv))
{
/* Add both the old paint volume of the actor (which is
* currently visible on the screen) and the new paint volume
* (which will be visible on the screen after this redraw)
* to the redraw clip.
* The former we do to ensure the old texture on the screen
* will be fully painted over in case the actor was moved.
*/
add_to_stage_clip (stage, &old_actor_pv);
add_to_stage_clip (stage, &new_actor_pv);
}
else
{
/* If there's no clip we can use, we have to trigger an
* unclipped full stage redraw.
*/
add_to_stage_clip (stage, NULL);
}
free_queue_redraw_entry (entry);
}
g_list_free (stolen_list);
free_queue_redraw_entry (entry);
}
g_list_free (priv->pending_queue_redraws);
priv->pending_queue_redraws = NULL;
}
/**