From 9cba5b83bde0857d606d9668ea420b702378d6a8 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 9 Mar 2011 17:46:23 +0000 Subject: [PATCH] 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. --- clutter/cogl/cogl/cogl-clip-stack.c | 36 ++++++++++++------ clutter/cogl/cogl/cogl-path-private.h | 11 ++++++ clutter/cogl/cogl/cogl2-path.c | 55 ++++++++++++++++++++++----- 3 files changed, 81 insertions(+), 21 deletions(-) diff --git a/clutter/cogl/cogl/cogl-clip-stack.c b/clutter/cogl/cogl/cogl-clip-stack.c index c8685d98b..fc8edcaa2 100644 --- a/clutter/cogl/cogl/cogl-clip-stack.c +++ b/clutter/cogl/cogl/cogl-clip-stack.c @@ -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 * diff --git a/clutter/cogl/cogl/cogl-path-private.h b/clutter/cogl/cogl/cogl-path-private.h index f3ea62299..dbd23f407 100644 --- a/clutter/cogl/cogl/cogl-path-private.h +++ b/clutter/cogl/cogl/cogl-path-private.h @@ -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 */ diff --git a/clutter/cogl/cogl/cogl2-path.c b/clutter/cogl/cogl/cogl2-path.c index f1f0e4532..6b79f75cc 100644 --- a/clutter/cogl/cogl/cogl2-path.c +++ b/clutter/cogl/cogl/cogl2-path.c @@ -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); }