Optimize culling by doing culling in eye-coordinates

This implements a variation of frustum culling whereby we convert screen
space clip rectangles into eye space mini-frustums so that we don't have
to repeatedly transform actor paint-volumes all the way into screen
coordinates to perform culling, we just have to apply the modelview
transform and then determine each points distance from the planes that
make up the clip frustum.

By avoiding the projective transform, perspective divide and viewport
scale for each point culled this makes culling much cheaper.
This commit is contained in:
Robert Bragg
2011-02-01 18:32:08 +00:00
parent eef9078f89
commit 19b8622983
12 changed files with 519 additions and 212 deletions

View File

@ -133,7 +133,7 @@ struct _ClutterStagePrivate
GArray *paint_volume_stack;
const ClutterGeometry *current_paint_clip;
ClutterPlane current_clip_planes[4];
GList *pending_queue_redraws;
@ -380,6 +380,110 @@ clutter_stage_allocate (ClutterActor *self,
}
}
typedef struct _Vector4
{
float x, y, z, w;
} Vector4;
static void
_cogl_util_get_eye_planes_for_screen_poly (float *polygon,
int n_vertices,
float *viewport,
const CoglMatrix *projection,
const CoglMatrix *inverse_project,
ClutterPlane *planes)
{
float Wc;
Vector4 *tmp_poly;
ClutterPlane *plane;
int i;
CoglVector3 b;
CoglVector3 c;
int count;
tmp_poly = g_alloca (sizeof (Vector4) * n_vertices * 2);
#define DEPTH -50
/* Determine W in clip-space (Wc) for a point (0, 0, DEPTH, 1)
*
* Note: the depth could be anything except 0.
*
* We will transform the polygon into clip coordinates using this
* depth and then into eye coordinates. Our clip planes will be
* defined by triangles that extend between points of the polygon at
* DEPTH and corresponding points of the same polygon at DEPTH * 2.
*
* NB: Wc defines the position of the clip planes in clip
* coordinates. Given a screen aligned cross section through the
* frustum; coordinates range from [-Wc,Wc] left to right on the
* x-axis and [Wc,-Wc] top to bottom on the y-axis.
*/
Wc = DEPTH * projection->wz + projection->ww;
#define CLIP_X(X) ((((float)X - viewport[0]) * (2.0 / viewport[2])) - 1) * Wc
#define CLIP_Y(Y) ((((float)Y - viewport[1]) * (2.0 / viewport[3])) - 1) * -Wc
for (i = 0; i < n_vertices; i++)
{
tmp_poly[i].x = CLIP_X (polygon[i * 2]);
tmp_poly[i].y = CLIP_Y (polygon[i * 2 + 1]);
tmp_poly[i].z = DEPTH;
tmp_poly[i].w = Wc;
}
Wc = DEPTH * 2 * projection->wz + projection->ww;
/* FIXME: technically we don't need to project all of the points
* twice, it would be enough project every other point since
* we can share points in this set to define the plane vectors. */
for (i = 0; i < n_vertices; i++)
{
tmp_poly[n_vertices + i].x = CLIP_X (polygon[i * 2]);
tmp_poly[n_vertices + i].y = CLIP_Y (polygon[i * 2 + 1]);
tmp_poly[n_vertices + i].z = DEPTH * 2;
tmp_poly[n_vertices + i].w = Wc;
}
#undef CLIP_X
#undef CLIP_Y
cogl_matrix_project_points (inverse_project,
4,
sizeof (Vector4),
tmp_poly,
sizeof (Vector4),
tmp_poly,
n_vertices * 2);
/* XXX: It's quite ugly that we end up with these casts between
* Vector4 types and CoglVector3s, it might be better if the
* cogl_vector APIs just took pointers to floats.
*/
count = n_vertices - 1;
for (i = 0; i < count; i++)
{
plane = &planes[i];
plane->v0 = *(CoglVector3 *)&tmp_poly[i];
b = *(CoglVector3 *)&tmp_poly[n_vertices + i];
c = *(CoglVector3 *)&tmp_poly[n_vertices + i + 1];
cogl_vector3_subtract (&b, &b, &plane->v0);
cogl_vector3_subtract (&c, &c, &plane->v0);
cogl_vector3_cross_product (&plane->n, &b, &c);
cogl_vector3_normalize (&plane->n);
}
plane = &planes[n_vertices - 1];
plane->v0 = *(CoglVector3 *)&tmp_poly[0];
b = *(CoglVector3 *)&tmp_poly[2 * n_vertices - 1];
c = *(CoglVector3 *)&tmp_poly[n_vertices];
cogl_vector3_subtract (&b, &b, &plane->v0);
cogl_vector3_subtract (&c, &c, &plane->v0);
cogl_vector3_cross_product (&plane->n, &b, &c);
cogl_vector3_normalize (&plane->n);
}
/* This provides a common point of entry for painting the scenegraph
* for picking or painting...
*
@ -392,10 +496,44 @@ void
_clutter_stage_do_paint (ClutterStage *stage, const ClutterGeometry *clip)
{
ClutterStagePrivate *priv = stage->priv;
priv->current_paint_clip = clip;
float clip_poly[8];
if (clip)
{
clip_poly[0] = clip->x;
clip_poly[1] = clip->y;
clip_poly[2] = clip->x + clip->width;
clip_poly[3] = clip->y;
clip_poly[4] = clip->x + clip->width;
clip_poly[5] = clip->y + clip->height;
clip_poly[6] = clip->x;
clip_poly[7] = clip->y + clip->height;
}
else
{
ClutterGeometry geom;
_clutter_stage_window_get_geometry (priv->impl, &geom);
clip_poly[0] = 0;
clip_poly[1] = 0;
clip_poly[2] = geom.width;
clip_poly[3] = 0;
clip_poly[4] = geom.width;
clip_poly[5] = geom.height;
clip_poly[6] = 0;
clip_poly[7] = geom.height;
}
_cogl_util_get_eye_planes_for_screen_poly (clip_poly,
4,
priv->viewport,
&priv->projection,
&priv->inverse_projection,
priv->current_clip_planes);
_clutter_stage_paint_volume_stack_free_all (stage);
clutter_actor_paint (CLUTTER_ACTOR (stage));
priv->current_paint_clip = NULL;
}
static void
@ -924,12 +1062,9 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
ClutterActor *leaf)
{
ClutterStage *stage = CLUTTER_STAGE (actor);
ClutterStagePrivate *priv = stage->priv;
ClutterStageWindow *stage_window;
ClutterGeometry stage_clip;
const ClutterPaintVolume *redraw_clip;
ClutterPaintVolume projected_clip;
CoglMatrix modelview;
ClutterPaintVolume *redraw_clip;
ClutterActorBox bounding_box;
if (CLUTTER_ACTOR_IN_DESTRUCTION (actor))
@ -948,9 +1083,8 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
return;
}
/* Convert the clip volume (which is in leaf actor coordinates) into stage
* coordinates and then into an axis aligned stage coordinates bounding
* box...
/* Convert the clip volume into stage coordinates and then into an
* axis aligned stage coordinates bounding box...
*/
if (!_clutter_actor_get_queue_redraw_clip (leaf))
@ -961,25 +1095,9 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
redraw_clip = _clutter_actor_get_queue_redraw_clip (leaf);
_clutter_paint_volume_copy_static (redraw_clip, &projected_clip);
/* NB: _clutter_actor_apply_modelview_transform_recursive will never
* include the transformation between stage coordinates and OpenGL
* window coordinates, we have to explicitly use the
* stage->apply_transform to get that... */
cogl_matrix_init_identity (&modelview);
_clutter_actor_apply_modelview_transform (CLUTTER_ACTOR (stage), &modelview);
_clutter_actor_apply_modelview_transform_recursive (leaf, NULL, &modelview);
_clutter_paint_volume_project (&projected_clip,
&modelview,
&priv->projection,
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);
_clutter_paint_volume_get_stage_paint_box (redraw_clip,
stage,
&bounding_box);
/* when converting to integer coordinates make sure we round the edges of the
* clip rectangle outwards... */
@ -3195,10 +3313,10 @@ _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage)
/* The is an out-of-band paramater available while painting that
* can be used to cull actors. */
const ClutterGeometry *
const ClutterPlane *
_clutter_stage_get_clip (ClutterStage *stage)
{
return stage->priv->current_paint_clip;
return stage->priv->current_clip_planes;
}
/* When an actor queues a redraw we add it to a list on the stage that
@ -3220,6 +3338,9 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage,
{
ClutterStagePrivate *priv = stage->priv;
CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
G_OBJECT_TYPE_NAME (actor), clip);
if (!priv->redraw_pending)
{
ClutterMasterClock *master_clock;
@ -3256,7 +3377,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage,
/* Ignore all requests to queue a redraw for an actor if a full
* (non-clipped) redraw of the actor has already been queued. */
if (!entry->has_clip)
return entry;
{
CLUTTER_NOTE (CLIPPING, "Bail from stage_queue_actor_redraw (%s): "
"Unclipped redraw of actor already queued",
G_OBJECT_CLASS_NAME (actor));
return entry;
}
/* If queuing a clipped redraw and a clipped redraw has
* previously been queued for this actor then combine the latest
@ -3278,7 +3404,7 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage,
if (clip)
{
entry->has_clip = TRUE;
_clutter_paint_volume_init_static (actor, &entry->clip);
_clutter_paint_volume_init_static (&entry->clip, actor);
_clutter_paint_volume_set_from_volume (&entry->clip, clip);
}
else