From 80ccf06b84d8d469e44947bc36ec7c35005be676 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sun, 19 Feb 2012 22:50:57 +0000 Subject: [PATCH] debug: support wireframe mode with vertex shaders Previously for the wireframe debug mode we identified the users "cogl_position_in" attribute, mapped that, created a replacement attribute with a LINE_LIST topology and then drew the attribute with a simple pipeline with a green colour. This meant we completely discarded the users original pipeline which may have involved vertex processing that would be useful to visualize in the wireframe. The new approach instead keeps the users attributes and instead generates CoglIndices that can be used to refererence the original attributes in LINE_LIST topology and instead of scrapping the user's pipeline we now create a weak copy of the original pipeline and just replace the fragment processing with a snippet to force the output color to be green. Reviewed-by: Neil Roberts --- cogl/cogl-framebuffer.c | 332 ++++++++++++++++++++++------------------ 1 file changed, 187 insertions(+), 145 deletions(-) diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c index 8c058d91f..8f72e9709 100644 --- a/cogl/cogl-framebuffer.c +++ b/cogl/cogl-framebuffer.c @@ -113,6 +113,8 @@ typedef struct _CoglFramebufferStackEntry extern CoglObjectClass _cogl_onscreen_class; +static CoglUserDataKey wire_pipeline_key; + static void _cogl_offscreen_free (CoglOffscreen *offscreen); COGL_OBJECT_DEFINE_WITH_CODE (Offscreen, offscreen, @@ -2442,59 +2444,75 @@ get_index (void *indices, } static void -add_line (void *vertices, - void *indices, - CoglIndicesType indices_type, - CoglAttribute *attribute, - int start, - int end, - CoglVertexP3 *lines, - int *n_line_vertices) +add_line (guint32 *line_indices, + int base, + void *user_indices, + CoglIndicesType user_indices_type, + int index0, + int index1, + int *pos) { - int start_index = get_index (indices, indices_type, start); - int end_index = get_index (indices, indices_type, end); - float *v0 = (float *)((guint8 *)vertices + start_index * attribute->stride); - float *v1 = (float *)((guint8 *)vertices + end_index * attribute->stride); - float *o = (float *)(&lines[*n_line_vertices]); - int i; + index0 = get_index (user_indices, user_indices_type, index0); + index1 = get_index (user_indices, user_indices_type, index1); - for (i = 0; i < attribute->n_components; i++) - *(o++) = *(v0++); - for (;i < 3; i++) - *(o++) = 0; - - for (i = 0; i < attribute->n_components; i++) - *(o++) = *(v1++); - for (;i < 3; i++) - *(o++) = 0; - - *n_line_vertices += 2; + line_indices[(*pos)++] = base + index0; + line_indices[(*pos)++] = base + index1; } -static CoglVertexP3 * -get_wire_lines (CoglAttribute *attribute, - CoglVerticesMode mode, - int n_vertices_in, - int *n_vertices_out, - CoglIndices *_indices) +static int +get_line_count (CoglVerticesMode mode, int n_vertices) { - CoglAttributeBuffer *attribute_buffer = cogl_attribute_get_buffer (attribute); - void *vertices; + if (mode == COGL_VERTICES_MODE_TRIANGLES && + (n_vertices % 3) == 0) + { + return n_vertices; + } + else if (mode == COGL_VERTICES_MODE_TRIANGLE_FAN && + n_vertices >= 3) + { + return 2 * n_vertices - 3; + } + else if (mode == COGL_VERTICES_MODE_TRIANGLE_STRIP && + n_vertices >= 3) + { + return 2 * n_vertices - 3; + } + /* In the journal we are a bit sneaky and actually use GL_QUADS + * which isn't actually a valid CoglVerticesMode! */ +#ifdef HAVE_COGL_GL + else if (mode == GL_QUADS && (n_vertices % 4) == 0) + { + return n_vertices; + } +#endif + + g_return_val_if_reached (0); +} + +static CoglIndices * +get_wire_line_indices (CoglContext *ctx, + CoglVerticesMode mode, + int first_vertex, + int n_vertices_in, + CoglIndices *user_indices, + int *n_indices) +{ + int n_lines; + guint32 *line_indices; CoglIndexBuffer *index_buffer; void *indices; CoglIndicesType indices_type; + int base = first_vertex; + int pos; int i; - int n_lines; - CoglVertexP3 *out = NULL; + CoglIndices *ret; - vertices = cogl_buffer_map (COGL_BUFFER (attribute_buffer), - COGL_BUFFER_ACCESS_READ, 0); - if (_indices) + if (user_indices) { - index_buffer = cogl_indices_get_buffer (_indices); + index_buffer = cogl_indices_get_buffer (user_indices); indices = cogl_buffer_map (COGL_BUFFER (index_buffer), COGL_BUFFER_ACCESS_READ, 0); - indices_type = cogl_indices_get_type (_indices); + indices_type = cogl_indices_get_type (user_indices); } else { @@ -2503,63 +2521,47 @@ get_wire_lines (CoglAttribute *attribute, indices_type = COGL_INDICES_TYPE_UNSIGNED_BYTE; } - *n_vertices_out = 0; + n_lines = get_line_count (mode, n_vertices_in); + + /* Note: we are using COGL_INDICES_TYPE_UNSIGNED_INT so 4 bytes per index. */ + line_indices = g_malloc (4 * n_lines * 2); + + pos = 0; if (mode == COGL_VERTICES_MODE_TRIANGLES && (n_vertices_in % 3) == 0) { - n_lines = n_vertices_in; - out = g_new (CoglVertexP3, n_lines * 2); for (i = 0; i < n_vertices_in; i += 3) { - add_line (vertices, indices, indices_type, attribute, - i, i+1, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - i+1, i+2, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - i+2, i, out, n_vertices_out); + add_line (line_indices, base, indices, indices_type, i, i+1, &pos); + add_line (line_indices, base, indices, indices_type, i+1, i+2, &pos); + add_line (line_indices, base, indices, indices_type, i+2, i, &pos); } } else if (mode == COGL_VERTICES_MODE_TRIANGLE_FAN && n_vertices_in >= 3) { - n_lines = 2 * n_vertices_in - 3; - out = g_new (CoglVertexP3, n_lines * 2); - - add_line (vertices, indices, indices_type, attribute, - 0, 1, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - 1, 2, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - 0, 2, out, n_vertices_out); + add_line (line_indices, base, indices, indices_type, 0, 1, &pos); + add_line (line_indices, base, indices, indices_type, 1, 2, &pos); + add_line (line_indices, base, indices, indices_type, 0, 2, &pos); for (i = 3; i < n_vertices_in; i++) { - add_line (vertices, indices, indices_type, attribute, - i - 1, i, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - 0, i, out, n_vertices_out); + add_line (line_indices, base, indices, indices_type, i - 1, i, &pos); + add_line (line_indices, base, indices, indices_type, 0, i, &pos); } } else if (mode == COGL_VERTICES_MODE_TRIANGLE_STRIP && n_vertices_in >= 3) { - n_lines = 2 * n_vertices_in - 3; - out = g_new (CoglVertexP3, n_lines * 2); - - add_line (vertices, indices, indices_type, attribute, - 0, 1, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - 1, 2, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - 0, 2, out, n_vertices_out); + add_line (line_indices, base, indices, indices_type, 0, 1, &pos); + add_line (line_indices, base, indices, indices_type, 1, 2, &pos); + add_line (line_indices, base, indices, indices_type, 0, 2, &pos); for (i = 3; i < n_vertices_in; i++) { - add_line (vertices, indices, indices_type, attribute, - i - 1, i, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - i - 2, i, out, n_vertices_out); + add_line (line_indices, base, indices, indices_type, i - 1, i, &pos); + add_line (line_indices, base, indices, indices_type, i - 2, i, &pos); } } /* In the journal we are a bit sneaky and actually use GL_QUADS @@ -2567,34 +2569,70 @@ get_wire_lines (CoglAttribute *attribute, #ifdef HAVE_COGL_GL else if (mode == GL_QUADS && (n_vertices_in % 4) == 0) { - n_lines = n_vertices_in; - out = g_new (CoglVertexP3, n_lines * 2); - for (i = 0; i < n_vertices_in; i += 4) { - add_line (vertices, indices, indices_type, attribute, - i, i + 1, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - i + 1, i + 2, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - i + 2, i + 3, out, n_vertices_out); - add_line (vertices, indices, indices_type, attribute, - i + 3, i, out, n_vertices_out); + add_line (line_indices, + base, indices, indices_type, i, i + 1, &pos); + add_line (line_indices, + base, indices, indices_type, i + 1, i + 2, &pos); + add_line (line_indices, + base, indices, indices_type, i + 2, i + 3, &pos); + add_line (line_indices, + base, indices, indices_type, i + 3, i, &pos); } } #endif - if (vertices != NULL) - cogl_buffer_unmap (COGL_BUFFER (attribute_buffer)); - - if (indices != NULL) + if (user_indices) cogl_buffer_unmap (COGL_BUFFER (index_buffer)); - return out; + *n_indices = n_lines * 2; + + ret = cogl_indices_new (ctx, + COGL_INDICES_TYPE_UNSIGNED_INT, + line_indices, + *n_indices); + + g_free (line_indices); + + return ret; +} + +static gboolean +remove_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + cogl_pipeline_remove_layer (pipeline, layer_index); + return TRUE; } static void -draw_wireframe (CoglFramebuffer *framebuffer, +pipeline_destroyed_cb (CoglPipeline *weak_pipeline, void *user_data) +{ + CoglPipeline *original_pipeline = user_data; + + /* XXX: I think we probably need to provide a custom unref function for + * CoglPipeline because it's possible that we will reach this callback + * because original_pipeline is being freed which means cogl_object_unref + * will have already freed any associated user data. + * + * Setting more user data here will *probably* succeed but that may allocate + * a new user-data array which could be leaked. + * + * Potentially we could have a _cogl_object_free_user_data function so + * that a custom unref function could be written that can destroy weak + * pipeline children before removing user data. + */ + cogl_object_set_user_data (COGL_OBJECT (original_pipeline), + &wire_pipeline_key, NULL, NULL); + + cogl_object_unref (weak_pipeline); +} + +static void +draw_wireframe (CoglContext *ctx, + CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, @@ -2603,68 +2641,70 @@ draw_wireframe (CoglFramebuffer *framebuffer, int n_attributes, CoglIndices *indices) { - CoglAttribute *position = NULL; - int i; - int n_line_vertices; - static CoglPipeline *wire_pipeline; - CoglAttribute *wire_attribute[1]; - CoglVertexP3 *lines; - CoglAttributeBuffer *attribute_buffer; + CoglIndices *wire_indices; + CoglPipeline *wire_pipeline; + int n_indices; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + wire_indices = get_wire_line_indices (ctx, + mode, + first_vertex, + n_vertices, + indices, + &n_indices); - for (i = 0; i < n_attributes; i++) - { - if (attributes[i]->name_state->name_id == - COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY) - { - position = attributes[i]; - break; - } - } - if (!position) - return; - - lines = get_wire_lines (position, - mode, - n_vertices, - &n_line_vertices, - indices); - attribute_buffer = - cogl_attribute_buffer_new (ctx, sizeof (CoglVertexP3) * n_line_vertices, - lines); - wire_attribute[0] = - cogl_attribute_new (attribute_buffer, "cogl_position_in", - sizeof (CoglVertexP3), - 0, - 3, - COGL_ATTRIBUTE_TYPE_FLOAT); - cogl_object_unref (attribute_buffer); + wire_pipeline = cogl_object_get_user_data (COGL_OBJECT (pipeline), + &wire_pipeline_key); if (!wire_pipeline) { - wire_pipeline = cogl_pipeline_new (); - cogl_pipeline_set_color4ub (wire_pipeline, - 0x00, 0xff, 0x00, 0xff); + wire_pipeline = + _cogl_pipeline_weak_copy (pipeline, pipeline_destroyed_cb, NULL); + + cogl_object_set_user_data (COGL_OBJECT (pipeline), + &wire_pipeline_key, wire_pipeline, + NULL); + + /* If we have glsl then the pipeline may have an associated + * vertex program and since we'd like to see the results of the + * vertex program in the wireframe we just add a final clobber + * of the wire color leaving the rest of the state untouched. */ + if (cogl_has_feature (framebuffer->context, COGL_FEATURE_ID_GLSL)) + { + CoglSnippet *snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, + NULL, + NULL); + cogl_snippet_set_replace (snippet, + "cogl_color_out = " + "vec4 (0.0, 1.0, 0.0, 1.0);\n"); + + cogl_pipeline_add_snippet (wire_pipeline, snippet); + cogl_object_unref (snippet); + } + else + { + cogl_pipeline_foreach_layer (wire_pipeline, remove_layer_cb, NULL); + cogl_pipeline_set_color4f (wire_pipeline, 0, 1, 0, 1); + } } /* temporarily disable the wireframe to avoid recursion! */ COGL_DEBUG_CLEAR_FLAG (COGL_DEBUG_WIREFRAME); - _cogl_framebuffer_draw_attributes (framebuffer, - wire_pipeline, - COGL_VERTICES_MODE_LINES, - 0, - n_line_vertices, - wire_attribute, - 1, - COGL_DRAW_SKIP_JOURNAL_FLUSH | - COGL_DRAW_SKIP_PIPELINE_VALIDATION | - COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH | - COGL_DRAW_SKIP_LEGACY_STATE); - + _cogl_framebuffer_draw_indexed_attributes ( + framebuffer, + wire_pipeline, + COGL_VERTICES_MODE_LINES, + 0, + n_indices, + wire_indices, + attributes, + n_attributes, + COGL_DRAW_SKIP_JOURNAL_FLUSH | + COGL_DRAW_SKIP_PIPELINE_VALIDATION | + COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH | + COGL_DRAW_SKIP_LEGACY_STATE); COGL_DEBUG_SET_FLAG (COGL_DEBUG_WIREFRAME); - cogl_object_unref (wire_attribute[0]); + cogl_object_unref (wire_indices); } #endif @@ -2683,7 +2723,8 @@ _cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer, { #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME))) - draw_wireframe (framebuffer, pipeline, + draw_wireframe (framebuffer->context, + framebuffer, pipeline, mode, first_vertex, n_vertices, attributes, n_attributes, NULL); else @@ -2776,7 +2817,8 @@ _cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer, { #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME))) - draw_wireframe (framebuffer, pipeline, + draw_wireframe (framebuffer->context, + framebuffer, pipeline, mode, first_vertex, n_vertices, attributes, n_attributes, indices); else