From ef8be9e25ebe77fc63055191cc48af53d731c108 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 7 Sep 2010 22:11:28 +0100 Subject: [PATCH] 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. --- clutter/clutter-actor.c | 75 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 7300f516d..8d2ef6030 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -468,6 +468,9 @@ struct _ClutterActorPrivate gboolean paint_volume_valid; ClutterPaintVolume paint_volume; + gboolean last_paint_box_valid; + ClutterActorBox last_paint_box; + gboolean paint_volume_disabled; }; @@ -2606,6 +2609,23 @@ clutter_actor_paint (ClutterActor *self) { priv->paint_volume_disabled = TRUE; 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) @@ -4894,9 +4914,64 @@ clutter_actor_destroy (ClutterActor *self) void clutter_actor_queue_redraw (ClutterActor *self) { + ClutterActorPrivate *priv; + const ClutterPaintVolume *pv; + gboolean clipped; + 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); + + /* 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