actor: Use paint volumes to always queue clipped redraws

This makes clutter_actor_queue_redraw transparently use an actor's paint
volume to queue a clipped redraw.

We save the actors paint box each time it is painted so that when
clutter_actor_queue_redraw is called we can determine the old and new
location of the actor so we know the full bounds of what must be redrawn
to clear its old view and show the new.
This commit is contained in:
Robert Bragg 2010-09-07 22:11:28 +01:00
parent 13c4d7b95d
commit ef8be9e25e

View File

@ -468,6 +468,9 @@ struct _ClutterActorPrivate
gboolean paint_volume_valid; gboolean paint_volume_valid;
ClutterPaintVolume paint_volume; ClutterPaintVolume paint_volume;
gboolean last_paint_box_valid;
ClutterActorBox last_paint_box;
gboolean paint_volume_disabled; gboolean paint_volume_disabled;
}; };
@ -2606,6 +2609,23 @@ clutter_actor_paint (ClutterActor *self)
{ {
priv->paint_volume_disabled = TRUE; priv->paint_volume_disabled = TRUE;
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (self);
priv->last_paint_box_valid = FALSE;
}
if (G_LIKELY (!priv->paint_volume_disabled))
{
/* We save the current paint box so that the next time the
* actor queues a redraw we can constrain the redraw to just
* cover the union of the new bounding box and the old.
*
* XXX: We are starting to do a lot of vertex transforms on
* the CPU in a typical paint, so at some point we should
* audit these and consider caching some things.
*/
if (clutter_actor_get_paint_box (self, &priv->last_paint_box))
priv->last_paint_box_valid = TRUE;
else
priv->last_paint_box_valid = FALSE;
} }
if (priv->effects != NULL) if (priv->effects != NULL)
@ -4894,9 +4914,64 @@ clutter_actor_destroy (ClutterActor *self)
void void
clutter_actor_queue_redraw (ClutterActor *self) clutter_actor_queue_redraw (ClutterActor *self)
{ {
ClutterActorPrivate *priv;
const ClutterPaintVolume *pv;
gboolean clipped;
g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (self));
priv = self->priv;
/* Don't clip this redraw if we don't know what position we had for
* the previous redraw since we don't know where to set the clip so
* it will clear the actor as it is currently. */
if (G_LIKELY (priv->last_paint_box_valid))
{
pv = clutter_actor_get_paint_volume (self);
if (pv)
{
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
ClutterPaintVolume stage_pv;
_clutter_paint_volume_init_static (stage, &stage_pv);
ClutterActorBox *box = &priv->last_paint_box;
ClutterVertex origin;
origin.x = box->x1;
origin.y = box->y1;
origin.z = 0;
clutter_paint_volume_set_origin (&stage_pv, &origin);
clutter_paint_volume_set_width (&stage_pv, box->x2 - box->x1);
clutter_paint_volume_set_height (&stage_pv, box->y2 - box->y1);
/* The idea is that if we know the paint volume for where
* the actor was last drawn and we also have the paint
* volume for where it will be drawn next then if we queue
* a redraw for both these regions that will cover
* everything that needs to be redrawn to clear the old
* view and show the latest view of the actor.
*/
_clutter_actor_queue_redraw_with_clip (stage, 0, &stage_pv);
clutter_paint_volume_free (&stage_pv);
_clutter_actor_set_queue_redraw_clip (self, pv);
clipped = TRUE;
}
else
clipped = FALSE;
}
else
clipped = FALSE;
clutter_actor_queue_redraw_with_origin (self, self); clutter_actor_queue_redraw_with_origin (self, self);
/* Just in case anyone is manually firing redraw signals without
* using the public queue_redraw() API we are careful to ensure that
* our out-of-band clip member is cleared before returning...
*
* Note: A NULL clip denotes a full-stage, un-clipped redraw
*/
if (G_LIKELY (clipped))
_clutter_actor_set_queue_redraw_clip (self, NULL);
} }
static void static void