Queue clipped redraws work in terms of paint volumes

There is an internal _clutter_actor_queue_redraw_with_clip API that gets
used for texture-from-pixmap to minimize what we redraw in response to
Damage events. It was previously working in terms of a ClutterActorBox
but it has now been changed so an actor can queue a redraw of volume
instead.

The plan is that clutter_actor_queue_redraw will start to transparently
use _clutter_actor_queue_redraw_with_clip when it can determine a paint
volume for the actor.
This commit is contained in:
Robert Bragg 2010-09-07 19:31:27 +01:00
parent f3bffe5cab
commit 1ea7145efc
5 changed files with 114 additions and 81 deletions

View File

@ -457,7 +457,7 @@ struct _ClutterActorPrivate
* of the QUEUE_REDRAW signal. It's an out-of-band argument. * of the QUEUE_REDRAW signal. It's an out-of-band argument.
* See clutter_actor_queue_clipped_redraw() for details. * See clutter_actor_queue_clipped_redraw() for details.
*/ */
const ClutterActorBox *oob_queue_redraw_clip; const ClutterPaintVolume *oob_queue_redraw_clip;
ClutterMetaGroup *actions; ClutterMetaGroup *actions;
ClutterMetaGroup *constraints; ClutterMetaGroup *constraints;
@ -4907,7 +4907,7 @@ _clutter_actor_get_allocation_clip (ClutterActor *self,
ClutterActorBox allocation; ClutterActorBox allocation;
/* XXX: we don't care if we get an out of date allocation here /* XXX: we don't care if we get an out of date allocation here
* because clutter_actor_queue_redraw_with_origin knows to ignore * because clutter_actor_queue_redraw_with_clip knows to ignore
* the clip if the actor's allocation is invalid. * the clip if the actor's allocation is invalid.
* *
* This is noted because clutter_actor_get_allocation_box does some * This is noted because clutter_actor_get_allocation_box does some
@ -4917,7 +4917,7 @@ _clutter_actor_get_allocation_clip (ClutterActor *self,
*/ */
clutter_actor_get_allocation_box (self, &allocation); clutter_actor_get_allocation_box (self, &allocation);
/* NB: clutter_actor_queue_clipped_redraw expects a box in the /* NB: clutter_actor_queue_redraw_with_clip expects a box in the
* actor's own coordinate space but the allocation is in parent * actor's own coordinate space but the allocation is in parent
* coordinates */ * coordinates */
clip->x1 = 0; clip->x1 = 0;
@ -4931,7 +4931,7 @@ _clutter_actor_get_allocation_clip (ClutterActor *self,
* @self: A #ClutterActor * @self: A #ClutterActor
* @flags: A mask of #ClutterRedrawFlags controlling the behaviour of * @flags: A mask of #ClutterRedrawFlags controlling the behaviour of
* this queue redraw. * this queue redraw.
* @clip: A #ClutterActorBox describing the bounds of what needs to be * @volume: A #ClutterPaintVolume describing the bounds of what needs to be
* redrawn or %NULL if you are just using a @flag to state your * redrawn or %NULL if you are just using a @flag to state your
* desired clipping. * desired clipping.
* *
@ -4939,21 +4939,16 @@ _clutter_actor_get_allocation_clip (ClutterActor *self,
* occurs once the main loop becomes idle (after the current batch of * occurs once the main loop becomes idle (after the current batch of
* events has been processed, roughly). * events has been processed, roughly).
* *
* If the %CLUTTER_REDRAW_CLIPPED_TO_BOX @flag is used, the clip box is * If no flags are given the clip volume is defined by @volume
* specified in actor coordinates and tells Clutter that only content * specified in actor coordinates and tells Clutter that only content
* within this box has been changed so Clutter can optionally optimize * within this volume has been changed so Clutter can optionally
* the redraw. * optimize the redraw.
* *
* If you are queuing a clipped redraw it is assumed that the actor is * If the %CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION @flag is used, @volume
* flat, and once the clip rectangle is projected into stage * should be %NULL and this tells Clutter to use the actor's current
* coordinates it will cover the area of the stage that needs to be * allocation as a clip box. This flag can only be used for 2D actors,
* redrawn. This is not possible to determine for 3D actors since the * because any actor with depth may be projected outside its
* projection of such actors may escape the clip rectangle. * allocation.
*
* If the %CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION @flag is used, @clip
* should be NULL and this tells Clutter to use the actors current
* allocation as a clip box. As above this flag can only be used for
* 2D actors.
* *
* Applications rarely need to call this, as redraws are handled * Applications rarely need to call this, as redraws are handled
* automatically by modification functions. * automatically by modification functions.
@ -4963,16 +4958,25 @@ _clutter_actor_get_allocation_clip (ClutterActor *self,
* *
* Also be aware that painting is a NOP for actors with an opacity of * Also be aware that painting is a NOP for actors with an opacity of
* 0 * 0
*
* When you are implementing a custom actor you must queue a redraw
* whenever some private state changes that will affect painting or
* picking of your actor.
*/ */
void void
_clutter_actor_queue_redraw_with_clip (ClutterActor *self, _clutter_actor_queue_redraw_with_clip (ClutterActor *self,
ClutterRedrawFlags flags, ClutterRedrawFlags flags,
ClutterActorBox *clip) ClutterPaintVolume *volume)
{ {
ClutterActorBox allocation_clip; ClutterPaintVolume allocation_pv;
ClutterPaintVolume *pv;
gboolean should_free_pv;
/* If the actor doesn't have a valid allocation then we will queue a /* If the actor doesn't have a valid allocation then we will queue a
* full stage redraw */ * full stage redraw.
*
* XXX: Is this check redundant? Or should it maybe only be done
* when flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION? */
if (self->priv->needs_allocation) if (self->priv->needs_allocation)
{ {
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (self);
@ -4981,16 +4985,37 @@ _clutter_actor_queue_redraw_with_clip (ClutterActor *self,
if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION) if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION)
{ {
ClutterActorBox allocation_clip;
ClutterVertex origin;
_clutter_paint_volume_init_static (self, &allocation_pv);
pv = &allocation_pv;
_clutter_actor_get_allocation_clip (self, &allocation_clip); _clutter_actor_get_allocation_clip (self, &allocation_clip);
clip = &allocation_clip;
origin.x = allocation_clip.x1;
origin.y = allocation_clip.y1;
origin.z = 0;
clutter_paint_volume_set_origin (pv, &origin);
clutter_paint_volume_set_width (pv,
allocation_clip.x2 - allocation_clip.x1);
clutter_paint_volume_set_height (pv,
allocation_clip.y2 -
allocation_clip.y1);
should_free_pv = TRUE;
}
else
{
pv = volume;
should_free_pv = FALSE;
} }
/* XXX: Ideally the redraw signal would take a clip rectangle /* XXX: Ideally the redraw signal would take a clip volume
* argument, but that would be an ABI break. Until we can break the * argument, but that would be an ABI break. Until we can break the
* ABI we pass the argument out-of-band via an actor->priv member... * ABI we pass the argument out-of-band via an actor->priv member...
*/ */
_clutter_actor_set_queue_redraw_clip (self, clip); _clutter_actor_set_queue_redraw_clip (self, pv);
clutter_actor_queue_redraw_with_origin (self, self); clutter_actor_queue_redraw_with_origin (self, self);
@ -5001,6 +5026,9 @@ _clutter_actor_queue_redraw_with_clip (ClutterActor *self,
* Note: A NULL clip denotes a full-stage, un-clipped redraw * Note: A NULL clip denotes a full-stage, un-clipped redraw
*/ */
_clutter_actor_set_queue_redraw_clip (self, NULL); _clutter_actor_set_queue_redraw_clip (self, NULL);
if (should_free_pv)
clutter_paint_volume_free (pv);
} }
/** /**
@ -10895,7 +10923,7 @@ clutter_actor_has_pointer (ClutterActor *self)
* the QUEUE_REDRAW signal. It is an out-of-band argument. See * the QUEUE_REDRAW signal. It is an out-of-band argument. See
* clutter_actor_queue_clipped_redraw() for details. * clutter_actor_queue_clipped_redraw() for details.
*/ */
const ClutterActorBox * const ClutterPaintVolume *
_clutter_actor_get_queue_redraw_clip (ClutterActor *self) _clutter_actor_get_queue_redraw_clip (ClutterActor *self)
{ {
return self->priv->oob_queue_redraw_clip; return self->priv->oob_queue_redraw_clip;
@ -10903,7 +10931,7 @@ _clutter_actor_get_queue_redraw_clip (ClutterActor *self)
void void
_clutter_actor_set_queue_redraw_clip (ClutterActor *self, _clutter_actor_set_queue_redraw_clip (ClutterActor *self,
const ClutterActorBox *clip) const ClutterPaintVolume *clip)
{ {
self->priv->oob_queue_redraw_clip = clip; self->priv->oob_queue_redraw_clip = clip;
} }

View File

@ -136,25 +136,6 @@ typedef enum
CLUTTER_ABSOLUTE_ORIGIN_CHANGED = 1 << 1 CLUTTER_ABSOLUTE_ORIGIN_CHANGED = 1 << 1
} ClutterAllocationFlags; } ClutterAllocationFlags;
/**
* ClutterRedrawFlags:
* @CLUTTER_REDRAW_CLIPPED_TO_BOX: Tells clutter the redraw is clipped
* to a given clip box in actor coordinates.
* @CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION: Tells clutter the maximum
* extents of what needs to be redrawn lies within the actors
* current allocation.
*
* Flags passed to the clutter_actor_queue_redraw_with_clip ()
* function
*
* Since: 1.2
*/
typedef enum
{
CLUTTER_REDRAW_CLIPPED_TO_BOX = 0,
CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION = 1 << 1
} ClutterRedrawFlags;
/** /**
* ClutterActor: * ClutterActor:
* @flags: #ClutterActorFlags * @flags: #ClutterActorFlags

View File

@ -91,6 +91,23 @@ typedef enum {
CLUTTER_INTERNAL_CHILD = 1 << 6 CLUTTER_INTERNAL_CHILD = 1 << 6
} ClutterPrivateFlags; } ClutterPrivateFlags;
/*
* ClutterRedrawFlags:
* @CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION: Tells clutter the maximum
* extents of what needs to be redrawn lies within the actors
* current allocation. (Only use this for 2D actors though because
* any actor with depth may be projected outside of its allocation)
*
* Flags passed to the clutter_actor_queue_redraw_with_clip ()
* function
*
* Since: 1.6
*/
typedef enum
{
CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION = 1 << 0
} ClutterRedrawFlags;
struct _ClutterInputDevice struct _ClutterInputDevice
{ {
GObject parent_instance; GObject parent_instance;
@ -452,10 +469,10 @@ gboolean _clutter_actor_transform_and_project_box (ClutterActor *self,
void _clutter_actor_queue_redraw_with_clip (ClutterActor *self, void _clutter_actor_queue_redraw_with_clip (ClutterActor *self,
ClutterRedrawFlags flags, ClutterRedrawFlags flags,
ClutterActorBox *clip); ClutterPaintVolume *clip_volume);
const ClutterActorBox *_clutter_actor_get_queue_redraw_clip (ClutterActor *self); const ClutterPaintVolume *_clutter_actor_get_queue_redraw_clip (ClutterActor *self);
void _clutter_actor_set_queue_redraw_clip (ClutterActor *self, void _clutter_actor_set_queue_redraw_clip (ClutterActor *self,
const ClutterActorBox *clip); const ClutterPaintVolume *clip_volume);
void _clutter_run_repaint_functions (void); void _clutter_run_repaint_functions (void);

View File

@ -729,10 +729,10 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
ClutterStagePrivate *priv = stage->priv; ClutterStagePrivate *priv = stage->priv;
ClutterStageWindow *stage_window; ClutterStageWindow *stage_window;
ClutterGeometry stage_clip; ClutterGeometry stage_clip;
const ClutterActorBox *clip; const ClutterPaintVolume *redraw_clip;
ClutterActorBox bounds; ClutterPaintVolume projected_clip;
ClutterVertex v[4]; CoglMatrix modelview;
int i; ClutterActorBox bounding_box;
CLUTTER_NOTE (PAINT, "Redraw request number %lu", CLUTTER_NOTE (PAINT, "Redraw request number %lu",
CLUTTER_CONTEXT ()->redraw_count + 1); CLUTTER_CONTEXT ()->redraw_count + 1);
@ -761,47 +761,50 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
/* If the backend can't do anything with redraw clips (e.g. it already knows /* If the backend can't do anything with redraw clips (e.g. it already knows
* it needs to redraw everything anyway) then don't spend time transforming * it needs to redraw everything anyway) then don't spend time transforming
* any clip regions into stage coordinates... */ * any clip volume into stage coordinates... */
stage_window = _clutter_stage_get_window (stage); stage_window = _clutter_stage_get_window (stage);
if (_clutter_stage_window_ignoring_redraw_clips (stage_window)) if (_clutter_stage_window_ignoring_redraw_clips (stage_window))
return; return;
/* Convert the clip rectangle (which is in leaf actor coordinates) into stage /* Convert the clip volume (which is in leaf actor coordinates) into stage
* coordinates and then into an axis aligned stage coordinates bounding * coordinates and then into an axis aligned stage coordinates bounding
* box... * box...
*/ */
clip = _clutter_actor_get_queue_redraw_clip (leaf); if (!_clutter_actor_get_queue_redraw_clip (leaf))
if (!clip)
{ {
_clutter_stage_window_add_redraw_clip (stage_window, NULL); _clutter_stage_window_add_redraw_clip (stage_window, NULL);
return; return;
} }
_clutter_actor_transform_and_project_box (leaf, clip, v); redraw_clip = _clutter_actor_get_queue_redraw_clip (leaf);
bounds.x1 = v[0].x; bounds.y1 = v[0].y; _clutter_paint_volume_copy_static (redraw_clip, &projected_clip);
bounds.x2 = v[0].x; bounds.y2 = v[0].y;
for (i = 0; i < 4; i++) /* NB: _clutter_actor_apply_modelview_transform_recursive will never
{ * include the transformation between stage coordinates and OpenGL
if (v[i].x < bounds.x1) * window coordinates, we have to explicitly use the
bounds.x1 = v[i].x; * stage->apply_transform to get that... */
else if (v[i].x > bounds.x2) cogl_matrix_init_identity (&modelview);
bounds.x2 = v[i].x; _clutter_actor_apply_modelview_transform (CLUTTER_ACTOR (stage), &modelview);
_clutter_actor_apply_modelview_transform_recursive (leaf, NULL, &modelview);
if (v[i].y < bounds.y1) _clutter_paint_volume_project (&projected_clip,
bounds.y1 = v[i].y; &modelview,
else if (v[i].y > bounds.y2) &priv->projection,
bounds.y2 = v[i].y; priv->viewport);
}
_clutter_paint_volume_get_bounding_box (&projected_clip, &bounding_box);
clutter_paint_volume_free (&projected_clip);
clutter_actor_box_clamp_to_pixel (&bounding_box);
/* when converting to integer coordinates make sure we round the edges of the /* when converting to integer coordinates make sure we round the edges of the
* clip rectangle outwards... */ * clip rectangle outwards... */
stage_clip.x = bounds.x1; stage_clip.x = bounding_box.x1;
stage_clip.y = bounds.y1; stage_clip.y = bounding_box.y1;
stage_clip.width = ceilf (bounds.x2) - stage_clip.x; stage_clip.width = bounding_box.x2 - stage_clip.x;
stage_clip.height = ceilf (bounds.y2) - stage_clip.y; stage_clip.height = bounding_box.y2 - stage_clip.y;
_clutter_stage_window_add_redraw_clip (stage_window, &stage_clip); _clutter_stage_window_add_redraw_clip (stage_window, &stage_clip);
} }

View File

@ -317,7 +317,8 @@ clutter_x11_texture_pixmap_real_queue_damage_redraw (
ClutterActorBox allocation; ClutterActorBox allocation;
float scale_x; float scale_x;
float scale_y; float scale_y;
ClutterActorBox clip; ClutterVertex origin;
ClutterPaintVolume clip;
/* NB: clutter_actor_queue_clipped_redraw expects a box in the actor's /* NB: clutter_actor_queue_clipped_redraw expects a box in the actor's
* coordinate space so we need to convert from pixmap coordinates to * coordinate space so we need to convert from pixmap coordinates to
@ -344,14 +345,17 @@ clutter_x11_texture_pixmap_real_queue_damage_redraw (
scale_x = (allocation.x2 - allocation.x1) / priv->pixmap_width; scale_x = (allocation.x2 - allocation.x1) / priv->pixmap_width;
scale_y = (allocation.y2 - allocation.y1) / priv->pixmap_height; scale_y = (allocation.y2 - allocation.y1) / priv->pixmap_height;
clip.x1 = x * scale_x; _clutter_paint_volume_init_static (self, &clip);
clip.y1 = y * scale_y;
clip.x2 = clip.x1 + width * scale_x;
clip.y2 = clip.y1 + height * scale_y;
_clutter_actor_queue_redraw_with_clip (self, origin.x = x * scale_x;
CLUTTER_REDRAW_CLIPPED_TO_BOX, origin.y = y * scale_y;
&clip); origin.z = 0;
clutter_paint_volume_set_origin (&clip, &origin);
clutter_paint_volume_set_width (&clip, width * scale_x);
clutter_paint_volume_set_height (&clip, height * scale_y);
_clutter_actor_queue_redraw_with_clip (self, 0, &clip);
clutter_paint_volume_free (&clip);
} }
static void static void