cogl-path: Optimise paths that are just a rectangle

Drawing and clipping to paths is generally quite expensive because the
geometry has to be tessellated into triangles in a single VBO which
breaks up the journal batching. If we can detect when the path
contains just a single rectangle then we can instead divert to calling
cogl_rectangle which will take advantage of the journal, or by pushing
a rectangle clip which usually ends up just using the scissor.

This patch adds a boolean to each path to mark when it is a
rectangle. It gets cleared whenever a node is added or gets set to
TRUE whenever cogl2_path_rectangle is called. This doesn't try to
catch cases where a rectangle is composed by cogl_path_line_to and
cogl_path_move_to commands.
This commit is contained in:
Neil Roberts 2011-03-09 17:46:23 +00:00
parent 9b7f362ea4
commit cbe9d758d5
3 changed files with 81 additions and 21 deletions

View File

@ -445,23 +445,35 @@ _cogl_clip_stack_push_from_path (CoglClipStack *stack,
CoglPath *path,
const CoglMatrix *modelview_matrix)
{
CoglClipStackPath *entry;
float x_1, y_1, x_2, y_2;
entry = _cogl_clip_stack_push_entry (stack,
sizeof (CoglClipStackPath),
COGL_CLIP_STACK_PATH);
entry->path = cogl_path_copy (path);
entry->matrix = *modelview_matrix;
_cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2);
_cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry,
x_1, y_1, x_2, y_2, modelview_matrix);
/* If the path is a simple rectangle then we can divert to pushing a
rectangle clip instead which usually won't involve the stencil
buffer */
if (_cogl_path_is_rectangle (path))
return _cogl_clip_stack_push_rectangle (stack,
x_1, y_1,
x_2, y_2,
modelview_matrix);
else
{
CoglClipStackPath *entry;
return (CoglClipStack *) entry;
entry = _cogl_clip_stack_push_entry (stack,
sizeof (CoglClipStackPath),
COGL_CLIP_STACK_PATH);
entry->path = cogl_path_copy (path);
entry->matrix = *modelview_matrix;
_cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry,
x_1, y_1, x_2, y_2, modelview_matrix);
return (CoglClipStack *) entry;
}
}
CoglClipStack *

View File

@ -87,6 +87,14 @@ struct _CoglPathData
CoglVertexArray *stroke_vbo;
CoglAttribute **stroke_vbo_attributes;
unsigned int stroke_vbo_n_attributes;
/* This is used as an optimisation for when the path contains a
single contour specified using cogl2_path_rectangle. Cogl is more
optimised to handle rectangles than paths so we can detect this
case and divert to the journal or a rectangle clip. If it is TRUE
then the entire path can be described by calling
_cogl_path_get_bounds */
gboolean is_rectangle;
};
void
@ -101,4 +109,7 @@ _cogl_path_get_bounds (CoglPath *path,
float *max_x,
float *max_y);
gboolean
_cogl_path_is_rectangle (CoglPath *path);
#endif /* __COGL_PATH_PRIVATE_H */

View File

@ -190,6 +190,11 @@ _cogl_path_add_node (CoglPath *path,
if (y > data->path_nodes_max.y)
data->path_nodes_max.y = y;
}
/* Once the path nodes have been modified then we'll assume it's no
longer a rectangle. cogl2_path_rectangle will set this back to
TRUE if this has been called from there */
data->is_rectangle = FALSE;
}
static void
@ -466,18 +471,32 @@ cogl2_path_fill (CoglPath *path)
if (path->data->path_nodes->len == 0)
return;
framebuffer = _cogl_get_draw_buffer ();
/* If the path is a simple rectangle then we can divert to using
cogl_rectangle which should be faster because it can go through
the journal instead of uploading the geometry just for two
triangles */
if (path->data->is_rectangle)
{
float x_1, y_1, x_2, y_2;
_cogl_framebuffer_flush_journal (framebuffer);
_cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2);
cogl_rectangle (x_1, y_1, x_2, y_2);
}
else
{
framebuffer = _cogl_get_draw_buffer ();
/* NB: _cogl_framebuffer_flush_state may disrupt various state (such
* as the pipeline state) when flushing the clip stack, so should
* always be done first when preparing to draw. */
_cogl_framebuffer_flush_state (framebuffer,
_cogl_get_read_buffer (),
0);
_cogl_framebuffer_flush_journal (framebuffer);
_cogl_path_fill_nodes (path);
/* NB: _cogl_framebuffer_flush_state may disrupt various state (such
* as the pipeline state) when flushing the clip stack, so should
* always be done first when preparing to draw. */
_cogl_framebuffer_flush_state (framebuffer,
_cogl_get_read_buffer (),
0);
_cogl_path_fill_nodes (path);
}
}
void
@ -612,11 +631,28 @@ cogl2_path_rectangle (CoglPath *path,
float x_2,
float y_2)
{
gboolean is_rectangle;
/* If the path was previously empty and the rectangle isn't mirrored
then we'll record that this is a simple rectangle path so that we
can optimise it */
is_rectangle = (path->data->path_nodes->len == 0 &&
x_2 >= x_1 &&
y_2 >= y_1);
cogl2_path_move_to (path, x_1, y_1);
cogl2_path_line_to (path, x_2, y_1);
cogl2_path_line_to (path, x_2, y_2);
cogl2_path_line_to (path, x_1, y_2);
cogl2_path_close (path);
path->data->is_rectangle = is_rectangle;
}
gboolean
_cogl_path_is_rectangle (CoglPath *path)
{
return path->data->is_rectangle;
}
static void
@ -975,6 +1011,7 @@ cogl2_path_new (void)
data->last_path = 0;
data->fill_vbo = COGL_INVALID_HANDLE;
data->stroke_vbo = NULL;
data->is_rectangle = FALSE;
return _cogl_path_object_new (path);
}