From 8dd77de00967ac332e89a7e4264c6b44f93db19e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 18 Apr 2012 15:57:33 +0100 Subject: [PATCH] Replace cogl_path_{stroke,fill} with framebuffer API The existing functions for stroking and filling a path depend on the global framebuffer and source stacks. These are now replaced with cogl_framebuffer_{stroke,fill}_path which get explicitly passed the framebuffer and pipeline. Reviewed-by: Robert Bragg (cherry picked from commit 713a8f8160bc5884b091c69eb7a84b069e0950e6) --- cogl-pango/cogl-pango-display-list.c | 3 +- cogl/cogl-clip-stack.c | 20 ++- cogl/cogl-framebuffer.c | 25 ++++ cogl/cogl-framebuffer.h | 41 +++++++ cogl/cogl-path-private.h | 10 +- cogl/cogl2-path.c | 115 +++++++++++------- .../cogl-2.0-experimental-sections.txt | 4 +- tests/conform/test-path.c | 64 +++++----- 8 files changed, 199 insertions(+), 83 deletions(-) diff --git a/cogl-pango/cogl-pango-display-list.c b/cogl-pango/cogl-pango-display-list.c index f4f130f8a..eda3c0722 100644 --- a/cogl-pango/cogl-pango-display-list.c +++ b/cogl-pango/cogl-pango-display-list.c @@ -437,6 +437,7 @@ _cogl_pango_display_list_render (CoglPangoDisplayList *dl, case COGL_PANGO_DISPLAY_LIST_TRAPEZOID: { + CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); float points[8]; CoglPath *path; @@ -453,7 +454,7 @@ _cogl_pango_display_list_render (CoglPangoDisplayList *dl, path = cogl_path_new (); cogl_path_polygon (path, points, 4); - cogl_path_fill (path); + cogl_framebuffer_fill_path (framebuffer, node->pipeline, path); cogl_object_unref (path); } break; diff --git a/cogl/cogl-clip-stack.c b/cogl/cogl-clip-stack.c index 1bab67d7d..e5703c251 100644 --- a/cogl/cogl-clip-stack.c +++ b/cogl/cogl-clip-stack.c @@ -278,7 +278,9 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer, GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); } -typedef void (*SilhouettePaintCallback) (void *user_data); +typedef void (*SilhouettePaintCallback) (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + void *user_data); static void add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, @@ -346,7 +348,7 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT)); - silhouette_callback (user_data); + silhouette_callback (framebuffer, ctx->stencil_pipeline, user_data); if (merge) { @@ -382,11 +384,15 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, } static void -paint_path_silhouette (void *user_data) +paint_path_silhouette (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + void *user_data) { CoglPath *path = user_data; if (path->data->path_nodes->len >= 3) _cogl_path_fill_nodes (path, + framebuffer, + pipeline, COGL_DRAW_SKIP_JOURNAL_FLUSH | COGL_DRAW_SKIP_PIPELINE_VALIDATION | COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH); @@ -411,10 +417,12 @@ add_stencil_clip_path (CoglFramebuffer *framebuffer, } static void -paint_primitive_silhouette (void *user_data) +paint_primitive_silhouette (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + void *user_data) { - _cogl_framebuffer_draw_primitive (cogl_get_draw_framebuffer (), - cogl_get_source (), + _cogl_framebuffer_draw_primitive (framebuffer, + pipeline, user_data, COGL_DRAW_SKIP_JOURNAL_FLUSH | COGL_DRAW_SKIP_PIPELINE_VALIDATION | diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c index af6ecffca..4e24e0387 100644 --- a/cogl/cogl-framebuffer.c +++ b/cogl/cogl-framebuffer.c @@ -47,6 +47,7 @@ #include "cogl1-context.h" #include "cogl-private.h" #include "cogl-primitives-private.h" +#include "cogl-path-private.h" #ifndef GL_FRAMEBUFFER #define GL_FRAMEBUFFER 0x8D40 @@ -3522,3 +3523,27 @@ cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer, n_rectangles, TRUE); } + +void +cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPath *path) +{ + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer)); + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + _cogl_path_fill_nodes (path, framebuffer, pipeline, 0 /* flags */); +} + +void +cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPath *path) +{ + _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer)); + _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); + _COGL_RETURN_IF_FAIL (cogl_is_path (path)); + + _cogl_path_stroke_nodes (path, framebuffer, pipeline); +} diff --git a/cogl/cogl-framebuffer.h b/cogl/cogl-framebuffer.h index 3750ca1ab..76ce811af 100644 --- a/cogl/cogl-framebuffer.h +++ b/cogl/cogl-framebuffer.h @@ -1491,6 +1491,47 @@ cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer, const float *coordinates, unsigned int n_rectangles); +/** + * cogl_framebuffer_fill_path: + * @framebuffer: A #CoglFramebuffer + * @pipeline: A #CoglPipeline to render with + * @path: The #CoglPath to fill + * + * Fills the interior of the path using the fragment operations + * defined by the pipeline. + * + * The interior of the shape is determined using the fill rule of the + * path. See %CoglPathFillRule for details. + * + * The result of referencing sliced textures in your current + * pipeline when filling a path are undefined. You should pass + * the %COGL_TEXTURE_NO_SLICING flag when loading any texture you will + * use while filling a path. + * + * Since: 2.0 + */ +void +cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPath *path); + +/** + * cogl_framebuffer_stroke_path: + * @framebuffer: A #CoglFramebuffer + * @pipeline: A #CoglPipeline to render with + * @path: The #CoglPath to stroke + * + * Strokes the edge of the path using the fragment operations defined + * by the pipeline. The stroke line will have a width of 1 pixel + * regardless of the current transformation matrix. + * + * Since: 2.0 + */ +void +cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglPath *path); + /* XXX: Should we take an n_buffers + buffer id array instead of using * the CoglBufferBits type which doesn't seem future proof? */ /** diff --git a/cogl/cogl-path-private.h b/cogl/cogl-path-private.h index c7e94a8d7..cc0b65c34 100644 --- a/cogl/cogl-path-private.h +++ b/cogl/cogl-path-private.h @@ -116,6 +116,14 @@ CoglBool _cogl_path_is_rectangle (CoglPath *path); void -_cogl_path_fill_nodes (CoglPath *path, CoglDrawFlags flags); +_cogl_path_stroke_nodes (CoglPath *path, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline); + +void +_cogl_path_fill_nodes (CoglPath *path, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglDrawFlags flags); #endif /* __COGL_PATH_PRIVATE_H */ diff --git a/cogl/cogl2-path.c b/cogl/cogl2-path.c index 3934015f3..f7327aebe 100644 --- a/cogl/cogl2-path.c +++ b/cogl/cogl2-path.c @@ -202,37 +202,37 @@ _cogl_path_add_node (CoglPath *path, data->is_rectangle = FALSE; } -static void -_cogl_path_stroke_nodes (CoglPath *path) +void +_cogl_path_stroke_nodes (CoglPath *path, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline) { CoglPathData *data = path->data; CoglPipeline *copy = NULL; - CoglPipeline *source; unsigned int path_start; int path_num = 0; CoglPathNode *node; - source = cogl_get_source (); + if (data->path_nodes->len == 0) + return; - if (cogl_pipeline_get_n_layers (source) != 0) + if (cogl_pipeline_get_n_layers (pipeline) != 0) { - copy = cogl_pipeline_copy (source); + copy = cogl_pipeline_copy (pipeline); _cogl_pipeline_prune_to_n_layers (copy, 0); - source = copy; + pipeline = copy; } _cogl_path_build_stroke_attribute_buffer (path); - cogl_push_source (source); - for (path_start = 0; path_start < data->path_nodes->len; path_start += node->path_size) { node = &g_array_index (data->path_nodes, CoglPathNode, path_start); - cogl_framebuffer_vdraw_attributes (cogl_get_draw_framebuffer (), - source, + cogl_framebuffer_vdraw_attributes (framebuffer, + pipeline, COGL_VERTICES_MODE_LINE_STRIP, 0, node->path_size, data->stroke_attributes[path_num], @@ -241,8 +241,6 @@ _cogl_path_stroke_nodes (CoglPath *path) path_num++; } - cogl_pop_source (); - if (copy) cogl_object_unref (copy); } @@ -273,10 +271,10 @@ _cogl_path_get_bounds (CoglPath *path, } static void -_cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path) +_cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline) { - CoglFramebuffer *fb; - if (!(path->data->context->private_feature_flags & COGL_PRIVATE_FEATURE_STENCIL_BUFFER)) { @@ -291,13 +289,14 @@ _cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path) } } - fb = cogl_get_draw_framebuffer (); - cogl_framebuffer_push_path_clip (fb, path); - cogl_rectangle (path->data->path_nodes_min.x, - path->data->path_nodes_min.y, - path->data->path_nodes_max.x, - path->data->path_nodes_max.y); - cogl_framebuffer_pop_clip (fb); + cogl_framebuffer_push_path_clip (framebuffer, path); + cogl_framebuffer_draw_rectangle (framebuffer, + pipeline, + path->data->path_nodes_min.x, + path->data->path_nodes_min.y, + path->data->path_nodes_max.x, + path->data->path_nodes_max.y); + cogl_framebuffer_pop_clip (framebuffer); } static CoglBool @@ -321,30 +320,55 @@ validate_layer_cb (CoglPipelineLayer *layer, void *user_data) } void -_cogl_path_fill_nodes (CoglPath *path, CoglDrawFlags flags) +_cogl_path_fill_nodes (CoglPath *path, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglDrawFlags flags) { - gboolean needs_fallback = FALSE; - CoglPipeline *pipeline = cogl_get_source (); + if (path->data->path_nodes->len == 0) + return; - _cogl_pipeline_foreach_layer_internal (pipeline, - validate_layer_cb, &needs_fallback); - if (needs_fallback) + /* If the path is a simple rectangle then we can divert to using + cogl_framebuffer_draw_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 && flags == 0) { - _cogl_path_fill_nodes_with_clipped_rectangle (path); - return; + float x_1, y_1, x_2, y_2; + + _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2); + cogl_framebuffer_draw_rectangle (framebuffer, + pipeline, + x_1, y_1, + x_2, y_2); } + else + { + CoglBool needs_fallback = FALSE; - _cogl_path_build_fill_attribute_buffer (path); + _cogl_pipeline_foreach_layer_internal (pipeline, + validate_layer_cb, + &needs_fallback); + if (needs_fallback) + { + _cogl_path_fill_nodes_with_clipped_rectangle (path, + framebuffer, + pipeline); + return; + } - _cogl_framebuffer_draw_indexed_attributes (cogl_get_draw_framebuffer (), - pipeline, - COGL_VERTICES_MODE_TRIANGLES, - 0, /* first_vertex */ - path->data->fill_vbo_n_indices, - path->data->fill_vbo_indices, - path->data->fill_attributes, - COGL_PATH_N_ATTRIBUTES, - flags); + _cogl_path_build_fill_attribute_buffer (path); + + _cogl_framebuffer_draw_indexed_attributes (framebuffer, + pipeline, + COGL_VERTICES_MODE_TRIANGLES, + 0, /* first_vertex */ + path->data->fill_vbo_n_indices, + path->data->fill_vbo_indices, + path->data->fill_attributes, + COGL_PATH_N_ATTRIBUTES, + flags); + } } void @@ -367,7 +391,10 @@ cogl2_path_fill (CoglPath *path) cogl_rectangle (x_1, y_1, x_2, y_2); } else - _cogl_path_fill_nodes (path, 0); + _cogl_path_fill_nodes (path, + cogl_get_draw_framebuffer (), + cogl_get_source (), + 0); } void @@ -378,7 +405,9 @@ cogl2_path_stroke (CoglPath *path) if (path->data->path_nodes->len == 0) return; - _cogl_path_stroke_nodes (path); + _cogl_path_stroke_nodes (path, + cogl_get_draw_framebuffer (), + cogl_get_source ()); } void diff --git a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt index ade861d62..da3d4315b 100644 --- a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt +++ b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt @@ -286,8 +286,6 @@ cogl_path_ellipse CoglPathFillRule cogl_path_set_fill_rule cogl_path_get_fill_rule -cogl_path_fill -cogl_path_stroke
@@ -425,6 +423,8 @@ cogl_framebuffer_draw_textured_rectangle cogl_framebuffer_draw_multitextured_rectangle cogl_framebuffer_draw_rectangles cogl_framebuffer_draw_textured_rectangles +cogl_framebuffer_stroke_path +cogl_framebuffer_fill_path cogl_framebuffer_swap_buffers diff --git a/tests/conform/test-path.c b/tests/conform/test-path.c index fbe1fcddb..286d6d1e4 100644 --- a/tests/conform/test-path.c +++ b/tests/conform/test-path.c @@ -15,11 +15,13 @@ typedef struct _TestState } TestState; static void -draw_path_at (int x, int y) +draw_path_at (CoglPath *path, CoglPipeline *pipeline, int x, int y) { cogl_framebuffer_push_matrix (fb); cogl_framebuffer_translate (fb, x * BLOCK_SIZE, y * BLOCK_SIZE, 0.0f); - cogl_path_fill (); + + cogl_framebuffer_fill_path (fb, pipeline, path); + cogl_framebuffer_pop_matrix (fb); } @@ -64,9 +66,10 @@ check_block (int block_x, int block_y, int block_mask) static void paint (TestState *state) { - CoglHandle path_a, path_b, path_c; + CoglPath *path_a, *path_b, *path_c; + CoglPipeline *white = cogl_pipeline_new (ctx); - cogl_set_source_color4ub (255, 255, 255, 255); + cogl_pipeline_set_color4f (white, 1, 1, 1, 1); /* Create a path filling just a quarter of a block. It will use two rectangles so that we have a sub path in the path */ @@ -75,22 +78,21 @@ paint (TestState *state) BLOCK_SIZE, BLOCK_SIZE); cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE * 3 / 4, BLOCK_SIZE); - path_a = cogl_handle_ref (cogl_get_path ()); - draw_path_at (0, 0); + path_a = cogl_object_ref (cogl_get_path ()); + draw_path_at (path_a, white, 0, 0); /* Create another path filling the whole block */ + cogl_path_new (); cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE); - path_b = cogl_handle_ref (cogl_get_path ()); - draw_path_at (1, 0); + path_b = cogl_object_ref (cogl_get_path ()); + draw_path_at (path_b, white, 1, 0); /* Draw the first path again */ - cogl_set_path (path_a); - draw_path_at (2, 0); + draw_path_at (path_a, white, 2, 0); /* Draw a copy of path a */ path_c = cogl_path_copy (path_a); - cogl_set_path (path_c); - draw_path_at (3, 0); + draw_path_at (path_c, white, 3, 0); /* Add another rectangle to path a. We'll use line_to's instead of cogl_rectangle so that we don't create another sub-path because @@ -100,11 +102,10 @@ paint (TestState *state) cogl_path_line_to (0, 0); cogl_path_line_to (BLOCK_SIZE / 2, 0); cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2); - draw_path_at (4, 0); + draw_path_at (path_a, white, 4, 0); /* Draw the copy again. It should not have changed */ - cogl_set_path (path_c); - draw_path_at (5, 0); + draw_path_at (path_c, white, 5, 0); /* Add another rectangle to path c. It will be added in two halves, one as an extension of the previous path and the other as a new @@ -115,32 +116,39 @@ paint (TestState *state) cogl_path_line_to (BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2); cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_rectangle (BLOCK_SIZE * 3 / 4, 0, BLOCK_SIZE, BLOCK_SIZE / 2); - draw_path_at (6, 0); + draw_path_at (path_c, white, 6, 0); /* Draw the original path again. It should not have changed */ cogl_set_path (path_a); - draw_path_at (7, 0); + draw_path_at (path_a, white, 7, 0); - cogl_handle_unref (path_a); - cogl_handle_unref (path_b); - cogl_handle_unref (path_c); + cogl_object_unref (path_a); + cogl_object_unref (path_b); + cogl_object_unref (path_c); /* Draw a self-intersecting path. The part that intersects should be inverted */ + cogl_path_new (); cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE); cogl_path_line_to (0, BLOCK_SIZE / 2); cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_line_to (BLOCK_SIZE / 2, 0); cogl_path_close (); - draw_path_at (8, 0); + path_a = cogl_object_ref (cogl_get_path ()); + draw_path_at (path_a, white, 8, 0); + cogl_object_unref (path_a); /* Draw two sub paths. Where the paths intersect it should be inverted */ + cogl_path_new (); cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE); cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE); - draw_path_at (9, 0); + path_a = cogl_object_ref (cogl_get_path ()); + draw_path_at (path_a, white, 9, 0); + cogl_object_unref (path_a); /* Draw a clockwise outer path */ + cogl_path_new (); cogl_path_move_to (0, 0); cogl_path_line_to (BLOCK_SIZE, 0); cogl_path_line_to (BLOCK_SIZE, BLOCK_SIZE); @@ -159,15 +167,15 @@ paint (TestState *state) cogl_path_line_to (BLOCK_SIZE, 0); cogl_path_close (); /* Retain the path for the next test */ - path_a = cogl_handle_ref (cogl_get_path ()); - draw_path_at (10, 0); + path_a = cogl_object_ref (cogl_get_path ()); + draw_path_at (path_a, white, 10, 0); /* Draw the same path again with the other fill rule */ cogl_set_path (path_a); cogl_path_set_fill_rule (COGL_PATH_FILL_RULE_NON_ZERO); - draw_path_at (11, 0); + draw_path_at (path_a, white, 11, 0); - cogl_handle_unref (path_a); + cogl_object_unref (path_a); } static void @@ -199,11 +207,7 @@ test_path (void) -1, 100); - /* XXX: we have to push/pop a framebuffer since this test currently - * uses the legacy cogl_rectangle() api. */ - cogl_push_framebuffer (fb); paint (&state); - cogl_pop_framebuffer (); validate_result (); if (cogl_test_verbose ())