From 845ff67301da767926f96c56026a8dd9c7964f01 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 18:46:42 +0100 Subject: [PATCH 01/26] [cogl] Improving Cogl journal to minimize driver overheads + GPU state changes Previously the journal was always flushed at the end of _cogl_rectangles_with_multitexture_coords, (i.e. the end of any cogl_rectangle* calls) but now we have broadened the potential for batching geometry. In ideal circumstances we will only flush once per scene. In summary the journal works like this: When you use any of the cogl_rectangle* APIs then nothing is emitted to the GPU at this point, we just log one or more quads into the journal. A journal entry consists of the quad coordinates, an associated material reference, and a modelview matrix. Ideally the journal only gets flushed once at the end of a scene, but in fact there are things to consider that may cause unwanted flushing, including: - modifying materials mid-scene This is because each quad in the journal has an associated material reference (i.e. not copy), so if you try and modify a material that is already referenced in the journal we force a flush first) NOTE: For now this means you should avoid using cogl_set_source_color() since that currently uses a single shared material. Later we should change it to use a pool of materials that is recycled when the journal is flushed. - modifying any state that isn't currently logged, such as depth, fog and backface culling enables. The first thing that happens when flushing, is to upload all the vertex data associated with the journal into a single VBO. We then go through a process of splitting up the journal into batches that have compatible state so they can be emitted to the GPU together. This is currently broken up into 3 levels so we can stagger the state changes: 1) we break the journal up according to changes in the number of material layers associated with logged quads. The number of layers in a material determines the stride of the associated vertices, so we have to update our vertex array offsets at this level. (i.e. calling gl{Vertex,Color},Pointer etc) 2) we further split batches up according to material compatability. (e.g. materials with different textures) We flush material state at this level. 3) Finally we split batches up according to modelview changes. At this level we update the modelview matrix and actually emit the actual draw command. This commit is largely about putting the initial design in-place; this will be followed by other changes that take advantage of the extended batching. --- clutter/clutter-main.c | 3 + clutter/cogl/cogl-debug.h | 21 +- clutter/cogl/cogl-material.h | 19 +- clutter/cogl/cogl.h.in | 14 + clutter/cogl/common/cogl-clip-stack.c | 4 + clutter/cogl/common/cogl-debug.c | 3 +- clutter/cogl/common/cogl-material-private.h | 99 ++- clutter/cogl/common/cogl-material.c | 318 +++++++++- clutter/cogl/common/cogl-primitives.c | 669 ++++++++++++++------ clutter/cogl/common/cogl-primitives.h | 2 + clutter/cogl/common/cogl-vertex-buffer.c | 28 +- clutter/cogl/common/cogl.c | 38 +- clutter/cogl/gl/cogl-context.c | 4 + clutter/cogl/gl/cogl-context.h | 4 + clutter/cogl/gl/cogl-fbo.c | 2 + clutter/cogl/gl/cogl-primitives.c | 44 +- clutter/cogl/gl/cogl-program.c | 5 + clutter/cogl/gl/cogl-texture-private.h | 12 +- clutter/cogl/gl/cogl-texture.c | 3 +- clutter/eglnative/clutter-backend-egl.c | 1 + clutter/eglx/clutter-backend-egl.c | 1 + clutter/fruity/clutter-backend-fruity.c | 1 + clutter/glx/clutter-backend-glx.c | 1 + clutter/osx/clutter-stage-osx.c | 1 + clutter/sdl/clutter-backend-sdl.c | 3 +- clutter/win32/clutter-backend-win32.c | 1 + 26 files changed, 1004 insertions(+), 297 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 5285f4da5..ae667925c 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -409,6 +409,9 @@ _clutter_do_pick (ClutterStage *stage, /* Calls should work under both GL and GLES, note GLES needs RGBA */ glGetIntegerv(GL_VIEWPORT, viewport); + /* Make sure Cogl flushes any batched geometry to the GPU driver */ + _cogl_flush (); + /* Read the color of the screen co-ords pixel */ glReadPixels (x, viewport[3] - y -1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); diff --git a/clutter/cogl/cogl-debug.h b/clutter/cogl/cogl-debug.h index 8aa1e8f87..1b42358dc 100644 --- a/clutter/cogl/cogl-debug.h +++ b/clutter/cogl/cogl-debug.h @@ -29,16 +29,17 @@ G_BEGIN_DECLS typedef enum { - COGL_DEBUG_MISC = 1 << 0, - COGL_DEBUG_TEXTURE = 1 << 1, - COGL_DEBUG_MATERIAL = 1 << 2, - COGL_DEBUG_SHADER = 1 << 3, - COGL_DEBUG_OFFSCREEN = 1 << 4, - COGL_DEBUG_DRAW = 1 << 5, - COGL_DEBUG_PANGO = 1 << 6, - COGL_DEBUG_RECTANGLES = 1 << 7, - COGL_DEBUG_HANDLE = 1 << 8, - COGL_DEBUG_BLEND_STRINGS = 1 << 9 + COGL_DEBUG_MISC = 1 << 0, + COGL_DEBUG_TEXTURE = 1 << 1, + COGL_DEBUG_MATERIAL = 1 << 2, + COGL_DEBUG_SHADER = 1 << 3, + COGL_DEBUG_OFFSCREEN = 1 << 4, + COGL_DEBUG_DRAW = 1 << 5, + COGL_DEBUG_PANGO = 1 << 6, + COGL_DEBUG_RECTANGLES = 1 << 7, + COGL_DEBUG_HANDLE = 1 << 8, + COGL_DEBUG_BLEND_STRINGS = 1 << 9, + COGL_DEBUG_DISABLE_BATCHING = 1 << 10 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/clutter/cogl/cogl-material.h b/clutter/cogl/cogl-material.h index 786ff344a..94ea5b260 100644 --- a/clutter/cogl/cogl-material.h +++ b/clutter/cogl/cogl-material.h @@ -673,6 +673,16 @@ void cogl_material_set_layer_matrix (CoglHandle material, */ const GList *cogl_material_get_layers (CoglHandle material); +/** + * cogl_material_get_n_layers: + * @material: A CoglMaterial object + * + * Returns: The number of layers defined for the given material + * + * Since: 1.0 + */ +int cogl_material_get_n_layers (CoglHandle material); + /** * CoglMaterialLayerType: * @COGL_MATERIAL_LAYER_TYPE_TEXTURE: The layer represents a @@ -713,15 +723,6 @@ CoglMaterialLayerType cogl_material_layer_get_type (CoglHandle layer_handle); * likely return COGL_INVALID_HANDLE if you try to get the texture. * Considering this, you can call cogl_material_layer_get_type first, * to check it is of type COGL_MATERIAL_LAYER_TYPE_TEXTURE. - * - * Note: It is possible for a layer object of type - * COGL_MATERIAL_LAYER_TYPE_TEXTURE to be realized before a texture - * object has been associated with the layer. For example this happens - * if you setup layer combining for a given layer index before calling - * cogl_material_set_layer for that index. - * - * Returns: A CoglHandle to the layers texture object or COGL_INVALID_HANDLE - * if a texture has not been set yet. */ CoglHandle cogl_material_layer_get_texture (CoglHandle layer_handle); diff --git a/clutter/cogl/cogl.h.in b/clutter/cogl/cogl.h.in index adc329288..cbd70f0c7 100644 --- a/clutter/cogl/cogl.h.in +++ b/clutter/cogl/cogl.h.in @@ -760,6 +760,20 @@ void cogl_flush_gl_state (int flags); /* private */ void _cogl_set_indirect_context (gboolean indirect); +/* private + * + * cogl_flush: + * + * As an optimization Cogl drawing functions may batch up primitives + * internally, so you need to call _cogl_flush to ensure that the + * drawing operations you have submitted for the current frame get + * flushed through to the driver and GPU. + * + * This must be called before issuing a swap buffers. + */ +void _cogl_flush (void); + + G_END_DECLS #undef __COGL_H_INSIDE__ diff --git a/clutter/cogl/common/cogl-clip-stack.c b/clutter/cogl/common/cogl-clip-stack.c index 3ef1b2b16..9efa5c22d 100644 --- a/clutter/cogl/common/cogl-clip-stack.c +++ b/clutter/cogl/common/cogl-clip-stack.c @@ -324,6 +324,10 @@ _cogl_clip_stack_rebuild (void) _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* The current primitive journal does not support tracking changes to the + * clip stack... */ + _cogl_journal_flush (); + stack = (CoglClipStack *) ctx->clip.stacks->data; ctx->clip.stack_dirty = FALSE; diff --git a/clutter/cogl/common/cogl-debug.c b/clutter/cogl/common/cogl-debug.c index b6dfd7559..7eb8561c1 100644 --- a/clutter/cogl/common/cogl-debug.c +++ b/clutter/cogl/common/cogl-debug.c @@ -40,7 +40,8 @@ static const GDebugKey cogl_debug_keys[] = { { "pango", COGL_DEBUG_PANGO }, { "rectangles", COGL_DEBUG_RECTANGLES }, { "handle", COGL_DEBUG_HANDLE }, - { "blend-strings", COGL_DEBUG_BLEND_STRINGS } + { "blend-strings", COGL_DEBUG_BLEND_STRINGS }, + { "disable-batching", COGL_DEBUG_DISABLE_BATCHING } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/clutter/cogl/common/cogl-material-private.h b/clutter/cogl/common/cogl-material-private.h index e61ab97bd..6fe52be81 100644 --- a/clutter/cogl/common/cogl-material-private.h +++ b/clutter/cogl/common/cogl-material-private.h @@ -36,6 +36,16 @@ typedef struct _CoglMaterial CoglMaterial; typedef struct _CoglMaterialLayer CoglMaterialLayer; +typedef enum _CoglMaterialEqualFlags +{ + /* Return FALSE if any component of either material isn't set to its + * default value. (Note: if the materials have corresponding flush + * options indicating that e.g. the material color won't be flushed then + * this will not assert a default color value.) */ + COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS = 1L<<0, + +} CoglMaterialEqualFlags; + /* XXX: I don't think gtk-doc supports having private enums so these aren't * bundled in with CoglMaterialLayerFlags */ typedef enum _CoglMaterialLayerPrivFlags @@ -90,17 +100,19 @@ struct _CoglMaterialLayer typedef enum _CoglMaterialFlags { - COGL_MATERIAL_FLAG_ENABLE_BLEND = 1L<<0, - COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING = 1L<<1, - COGL_MATERIAL_FLAG_DEFAULT_COLOR = 1L<<2, - COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL = 1L<<3, - COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC = 1L<<4, - COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC = 1L<<5 + COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING = 1L<<0, + + COGL_MATERIAL_FLAG_DEFAULT_COLOR = 1L<<1, + COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL = 1L<<2, + COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC = 1L<<3, + COGL_MATERIAL_FLAG_ENABLE_BLEND = 1L<<4, + COGL_MATERIAL_FLAG_DEFAULT_BLEND = 1L<<5 } CoglMaterialFlags; struct _CoglMaterial { CoglHandleObject _parent; + gulong journal_ref_count; gulong flags; @@ -130,6 +142,7 @@ struct _CoglMaterial GLint blend_dst_factor_rgb; GList *layers; + guint n_layers; }; /* @@ -183,39 +196,69 @@ typedef enum _CoglMaterialLayerFlags gulong _cogl_material_layer_get_flags (CoglHandle layer_handle); /* - * CoglMaterialFlushOption: - * @COGL_MATERIAL_FLUSH_FALLBACK_MASK: Follow this by a guin32 mask - * of the layers that can't be supported with the user supplied texture - * and need to be replaced with fallback textures. (1 = fallback, and the - * least significant bit = layer 0) - * @COGL_MATERIAL_FLUSH_DISABLE_MASK: Follow this by a guint32 mask - * of the layers that you want to completly disable texturing for - * (1 = fallback, and the least significant bit = layer 0) - * @COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE: Follow this by a GLuint OpenGL texture - * name to override the texture used for layer 0 of the material. This is - * intended for dealing with sliced textures where you will need to point - * to each of the texture slices in turn when drawing your geometry. - * Passing a value of 0 is the same as not passing the option at all. + * CoglMaterialFlushFlag: + * @COGL_MATERIAL_FLUSH_FALLBACK_MASK: The fallback_layers member is set to + * a guint32 mask of the layers that can't be supported with the user + * supplied texture and need to be replaced with fallback textures. (1 = + * fallback, and the least significant bit = layer 0) + * @COGL_MATERIAL_FLUSH_DISABLE_MASK: The disable_layers member is set to + * a guint32 mask of the layers that you want to completly disable + * texturing for (1 = fallback, and the least significant bit = layer 0) + * @COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE: The layer0_override_texture member is + * set to a GLuint OpenGL texture name to override the texture used for + * layer 0 of the material. This is intended for dealing with sliced + * textures where you will need to point to each of the texture slices in + * turn when drawing your geometry. Passing a value of 0 is the same as + * not passing the option at all. + * @COGL_MATERIAL_FLUSH_SKIP_GL_COLOR: When flushing the GL state for the + * material don't call glColor. */ -typedef enum _CoglMaterialFlushOption +typedef enum _CoglMaterialFlushFlag { - COGL_MATERIAL_FLUSH_FALLBACK_MASK = 1, - COGL_MATERIAL_FLUSH_DISABLE_MASK, - COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE, -} CoglMaterialFlushOption; + COGL_MATERIAL_FLUSH_FALLBACK_MASK = 1L<<0, + COGL_MATERIAL_FLUSH_DISABLE_MASK = 1L<<1, + COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE = 1L<<2, + COGL_MATERIAL_FLUSH_SKIP_GL_COLOR = 1L<<3 +} CoglMaterialFlushFlag; + +/* + * CoglMaterialFlushOptions: + * + */ +typedef struct _CoglMaterialFlushOptions +{ + CoglMaterialFlushFlag flags; + + guint32 fallback_layers; + guint32 disable_layers; + GLuint layer0_override_texture; +} CoglMaterialFlushOptions; /* * cogl_material_flush_gl_state: * @material: A CoglMaterial object * @...: A NULL terminated list of (CoglMaterialFlushOption, data) pairs * - * This function commits the state of the specified CoglMaterial - including - * the texture state for all the layers - to the OpenGL[ES] driver. + * Note: It is possible for a layer object of type + * COGL_MATERIAL_LAYER_TYPE_TEXTURE to be realized before a texture + * object has been associated with the layer. For example this happens + * if you setup layer combining for a given layer index before calling + * cogl_material_set_layer for that index. * - * Since 1.0 + * Returns: A CoglHandle to the layers texture object or COGL_INVALID_HANDLE + * if a texture has not been set yet. */ void _cogl_material_flush_gl_state (CoglHandle material, - ...) G_GNUC_NULL_TERMINATED; + CoglMaterialFlushOptions *options); + +gboolean _cogl_material_equal (CoglHandle material0_handle, + CoglMaterialFlushOptions *material0_flush_options, + CoglHandle material1_handle, + CoglMaterialFlushOptions *material1_flush_options, + CoglMaterialEqualFlags flags); + +CoglHandle _cogl_material_journal_ref (CoglHandle material_handle); +void _cogl_material_journal_unref (CoglHandle material_handle); #endif /* __COGL_MATERIAL_PRIVATE_H */ diff --git a/clutter/cogl/common/cogl-material.c b/clutter/cogl/common/cogl-material.c index d39120be9..a2ad30fd9 100644 --- a/clutter/cogl/common/cogl-material.c +++ b/clutter/cogl/common/cogl-material.c @@ -111,9 +111,10 @@ cogl_material_new (void) #endif material->blend_src_factor_rgb = GL_ONE; material->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA; - material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC; + material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND; material->layers = NULL; + material->n_layers = 0; return _cogl_material_handle_new (material); } @@ -130,6 +131,7 @@ _cogl_material_free (CoglMaterial *material) g_free (material); } + static void handle_automatic_blend_enable (CoglMaterial *material) { @@ -139,6 +141,12 @@ handle_automatic_blend_enable (CoglMaterial *material) * a flag to know when it's user configured, so we don't trash it */ material->flags &= ~COGL_MATERIAL_FLAG_ENABLE_BLEND; + + /* XXX: Uncomment this to disable all blending */ +#if 0 + return; +#endif + for (tmp = material->layers; tmp != NULL; tmp = tmp->next) { CoglMaterialLayer *layer = tmp->data; @@ -156,6 +164,16 @@ handle_automatic_blend_enable (CoglMaterial *material) material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND; } +/* If primitives have been logged in the journal referencing the current + * state of this material we need to flush the journal before we can + * modify it... */ +static void +_cogl_material_pre_change_notify (CoglMaterial *material) +{ + if (material->journal_ref_count) + _cogl_journal_flush (); +} + void cogl_material_get_color (CoglHandle handle, CoglColor *color) @@ -191,6 +209,9 @@ cogl_material_set_color (CoglHandle handle, if (memcmp (unlit, material->unlit, sizeof (unlit)) == 0) return; + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + memcpy (material->unlit, unlit, sizeof (unlit)); material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_COLOR; @@ -255,6 +276,9 @@ cogl_material_set_ambient (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + ambient = material->ambient; ambient[0] = cogl_color_get_red_float (ambient_color); ambient[1] = cogl_color_get_green_float (ambient_color); @@ -292,6 +316,9 @@ cogl_material_set_diffuse (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + diffuse = material->diffuse; diffuse[0] = cogl_color_get_red_float (diffuse_color); diffuse[1] = cogl_color_get_green_float (diffuse_color); @@ -337,6 +364,9 @@ cogl_material_set_specular (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + specular = material->specular; specular[0] = cogl_color_get_red_float (specular_color); specular[1] = cogl_color_get_green_float (specular_color); @@ -372,6 +402,9 @@ cogl_material_set_shininess (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + material->shininess = (GLfloat)shininess * 128.0; material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL; @@ -405,6 +438,9 @@ cogl_material_set_emission (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + emission = material->emission; emission[0] = cogl_color_get_red_float (emission_color); emission[1] = cogl_color_get_green_float (emission_color); @@ -424,6 +460,10 @@ cogl_material_set_alpha_test_function (CoglHandle handle, g_return_if_fail (cogl_is_material (handle)); material = _cogl_material_pointer_from_handle (handle); + + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + material->alpha_func = alpha_func; material->alpha_func_reference = (GLfloat)alpha_reference; @@ -571,6 +611,9 @@ cogl_material_set_blend (CoglHandle handle, a = &statements[1]; } + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + #ifndef HAVE_COGL_GLES setup_blend_state (rgb, &material->blend_equation_rgb, @@ -587,7 +630,7 @@ cogl_material_set_blend (CoglHandle handle, &material->blend_dst_factor_rgb); #endif - material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC; + material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND; return TRUE; } @@ -604,13 +647,16 @@ cogl_material_set_blend_constant (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + constant = material->blend_constant; constant[0] = cogl_color_get_red_float (constant_color); constant[1] = cogl_color_get_green_float (constant_color); constant[2] = cogl_color_get_blue_float (constant_color); constant[3] = cogl_color_get_alpha_float (constant_color); - material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC; + material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND; #endif } @@ -682,20 +728,26 @@ cogl_material_set_layer (CoglHandle material_handle, { CoglMaterial *material; CoglMaterialLayer *layer; - int n_layers; g_return_if_fail (cogl_is_material (material_handle)); g_return_if_fail (texture_handle == COGL_INVALID_HANDLE || cogl_is_texture (texture_handle)); material = _cogl_material_pointer_from_handle (material_handle); + + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + layer = _cogl_material_get_layer (material_handle, layer_index, TRUE); if (texture_handle == layer->texture) return; - n_layers = g_list_length (material->layers); - if (n_layers >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS) + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + + material->n_layers = g_list_length (material->layers); + if (material->n_layers >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS) { if (!(material->flags & COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING)) { @@ -856,6 +908,9 @@ cogl_material_set_layer_combine (CoglHandle handle, a = &statements[1]; } + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + setup_texture_combine_state (rgb, &layer->texture_combine_rgb_func, layer->texture_combine_rgb_src, @@ -886,6 +941,9 @@ cogl_material_set_layer_combine_constant (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); layer = _cogl_material_get_layer (material, layer_index, TRUE); + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + constant = layer->texture_combine_constant; constant[0] = cogl_color_get_red_float (constant_color); constant[1] = cogl_color_get_green_float (constant_color); @@ -909,6 +967,9 @@ cogl_material_set_layer_matrix (CoglHandle material_handle, material = _cogl_material_pointer_from_handle (material_handle); layer = _cogl_material_get_layer (material, layer_index, TRUE); + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + layer->matrix = *matrix; layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY; @@ -935,6 +996,10 @@ cogl_material_remove_layer (CoglHandle material_handle, g_return_if_fail (cogl_is_material (material_handle)); material = _cogl_material_pointer_from_handle (material_handle); + + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + for (tmp = material->layers; tmp != NULL; tmp = tmp->next) { layer = tmp->data; @@ -943,6 +1008,7 @@ cogl_material_remove_layer (CoglHandle material_handle, CoglHandle handle = (CoglHandle) layer; cogl_handle_unref (handle); material->layers = g_list_remove (material->layers, layer); + material->n_layers--; break; } } @@ -992,6 +1058,18 @@ cogl_material_get_layers (CoglHandle material_handle) return material->layers; } +int +cogl_material_get_n_layers (CoglHandle material_handle) +{ + CoglMaterial *material; + + g_return_val_if_fail (cogl_is_material (material_handle), 0); + + material = _cogl_material_pointer_from_handle (material_handle); + + return material->n_layers; +} + CoglMaterialLayerType cogl_material_layer_get_type (CoglHandle layer_handle) { @@ -1318,7 +1396,10 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, !gl_layer_info->disabled)) #endif { + /* XXX: Debug: Comment this out to disable all texturing: */ +#if 1 GE (glEnable (gl_target)); +#endif } } else @@ -1369,18 +1450,30 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, } static void -_cogl_material_flush_base_gl_state (CoglMaterial *material) +_cogl_material_flush_base_gl_state (CoglMaterial *material, + gboolean skip_gl_color) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR - && material->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR)) + /* XXX: + * Currently we only don't update state when the flags indicate that the + * current material uses the defaults, and the new material also uses the + * defaults, but we could do deeper comparisons of state. */ + + if (!skip_gl_color) { - /* GLES doesn't have glColor4fv... */ - GE (glColor4f (material->unlit[0], - material->unlit[1], - material->unlit[2], - material->unlit[3])); + if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR + && material->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR) || + /* Assume if we were previously told to skip the color, then + * the current color needs updating... */ + ctx->current_material_flush_options.flags & + COGL_MATERIAL_FLUSH_SKIP_GL_COLOR) + { + GE (glColor4f (material->unlit[0], + material->unlit[1], + material->unlit[2], + material->unlit[3])); + } } if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL @@ -1401,8 +1494,8 @@ _cogl_material_flush_base_gl_state (CoglMaterial *material) GE (glAlphaFunc (material->alpha_func, material->alpha_func_reference)); } - if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC - && material->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC)) + if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND + && material->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND)) { #if defined (HAVE_COGL_GLES2) gboolean have_blend_equation_seperate = TRUE; @@ -1441,32 +1534,33 @@ _cogl_material_flush_base_gl_state (CoglMaterial *material) } void -_cogl_material_flush_gl_state (CoglHandle handle, ...) +_cogl_material_flush_gl_state (CoglHandle handle, + CoglMaterialFlushOptions *options) { - CoglMaterial *material; - va_list ap; - CoglMaterialFlushOption option; - guint32 fallback_layers = 0; - guint32 disable_layers = 0; - GLuint layer0_override_texture = 0; + CoglMaterial *material; + guint32 fallback_layers = 0; + guint32 disable_layers = 0; + GLuint layer0_override_texture = 0; + gboolean skip_gl_color = FALSE; _COGL_GET_CONTEXT (ctx, NO_RETVAL); material = _cogl_material_pointer_from_handle (handle); - _cogl_material_flush_base_gl_state (material); - - va_start (ap, handle); - while ((option = va_arg (ap, CoglMaterialFlushOption))) + if (options) { - if (option == COGL_MATERIAL_FLUSH_FALLBACK_MASK) - fallback_layers = va_arg (ap, guint32); - else if (option == COGL_MATERIAL_FLUSH_DISABLE_MASK) - disable_layers = va_arg (ap, guint32); - else if (option == COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE) - layer0_override_texture = va_arg (ap, GLuint); + if (options->flags & COGL_MATERIAL_FLUSH_FALLBACK_MASK) + fallback_layers = options->fallback_layers; + if (options->flags & COGL_MATERIAL_FLUSH_DISABLE_MASK) + disable_layers = options->disable_layers; + if (options->flags & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE) + layer0_override_texture = options->layer0_override_texture; + if (options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR) + skip_gl_color = TRUE; } - va_end (ap); + + _cogl_material_flush_base_gl_state (material, + skip_gl_color); _cogl_material_flush_layers_gl_state (material, fallback_layers, @@ -1483,6 +1577,158 @@ _cogl_material_flush_gl_state (CoglHandle handle, ...) ctx->current_material = handle; ctx->current_material_flags = material->flags; + if (options) + ctx->current_material_flush_options = *options; + else + memset (&ctx->current_material_flush_options, + 0, sizeof (CoglMaterialFlushOptions)); +} + +gboolean +_cogl_material_equal (CoglHandle material0_handle, + CoglMaterialFlushOptions *material0_flush_options, + CoglHandle material1_handle, + CoglMaterialFlushOptions *material1_flush_options, + CoglMaterialEqualFlags flags) +{ + CoglMaterial *material0; + CoglMaterial *material1; + GList *l0, *l1; + + if (!(flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS)) + { + g_critical ("FIXME: _cogl_material_equal doesn't yet support " + "deep comparisons of materials"); + return FALSE; + } + /* Note: the following code is written with the assumption this + * constraint will go away*/ + + material0 = _cogl_material_pointer_from_handle (material0_handle); + material1 = _cogl_material_pointer_from_handle (material1_handle); + + if (!((material0_flush_options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR && + material1_flush_options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR))) + { + if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR) != + (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR)) + return FALSE; + else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS && + !(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR)) + return FALSE; + else if (!memcmp (material0->unlit, material1->unlit, + sizeof (material0->unlit))) + return FALSE; + } + + if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL) != + (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL)) + return FALSE; + else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS && + !(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL)) + return FALSE; +#if 0 + else if (!_deep_are_gl_materials_equal ()) + return FALSE; +#endif + + if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC) != + (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC)) + return FALSE; + else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS && + !(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC)) + return FALSE; +#if 0 + else if (!_deep_are_alpha_funcs_equal ()) + return FALSE; +#endif + + if ((material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) != + (material1->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND)) + return FALSE; + /* XXX: potentially blending could be "enabled" but the blend mode + * could be equivalent to being disabled. */ + + if (material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) + { + if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND) != + (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND)) + return FALSE; + else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS && + !(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND)) + return FALSE; +#if 0 + else if (!_deep_is_blend_equal ()) + return FALSE; +#endif + } + + if (material0_flush_options->fallback_layers != + material1_flush_options->fallback_layers || + material0_flush_options->disable_layers != + material1_flush_options->disable_layers) + return FALSE; + + l0 = material0->layers; + l1 = material1->layers; + + while (l0 && l1) + { + CoglMaterialLayer *layer0; + CoglMaterialLayer *layer1; + + if ((l0 == NULL && l1 != NULL) || + (l1 == NULL && l0 != NULL)) + return FALSE; + + layer0 = l0->data; + layer1 = l1->data; + + if (layer0->texture != layer1->texture) + return FALSE; + + if ((layer0->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE) != + (layer1->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE)) + return FALSE; + else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS && + !(layer0->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE)) + return FALSE; +#if 0 + else if (!_deep_are_layer_combines_equal ()) + return FALSE; +#endif + + l0 = l0->next; + l1 = l1->next; + } + + if ((l0 == NULL && l1 != NULL) || + (l1 == NULL && l0 != NULL)) + return FALSE; + + return TRUE; +} + +/* While a material is referenced by the Cogl journal we can not allow + * modifications, so this gives us a mechanism to track journal + * references separately */ +CoglHandle +_cogl_material_journal_ref (CoglHandle material_handle) +{ + CoglMaterial *material = + material = _cogl_material_pointer_from_handle (material_handle); + material->journal_ref_count++; + cogl_handle_ref (material_handle); + return material_handle; +} + +void +_cogl_material_journal_unref (CoglHandle material_handle) +{ + CoglMaterial *material = + material = _cogl_material_pointer_from_handle (material_handle); + material->journal_ref_count--; + cogl_handle_unref (material_handle); } /* TODO: Should go in cogl.c, but that implies duplication which is also @@ -1556,6 +1802,10 @@ cogl_material_set_layer_filters (CoglHandle handle, g_return_if_fail (cogl_is_material (handle)); material = _cogl_material_pointer_from_handle (handle); + + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material); + layer = _cogl_material_get_layer (material, layer_index, TRUE); layer->min_filter = min_filter; diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index dafe92fa4..9e3ea89d6 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -40,10 +40,53 @@ #ifdef HAVE_COGL_GL +#define glGenBuffers ctx->pf_glGenBuffersARB +#define glBindBuffer ctx->pf_glBindBufferARB +#define glBufferData ctx->pf_glBufferDataARB +#define glBufferSubData ctx->pf_glBufferSubDataARB +#define glDeleteBuffers ctx->pf_glDeleteBuffersARB #define glClientActiveTexture ctx->pf_glClientActiveTexture +#elif defined (HAVE_COGL_GLES2) + +#include "../gles/cogl-gles2-wrapper.h" + #endif + +/* XXX NB: + * Our journal's vertex data is arranged as follows: + * 4 vertices per quad: + * 2 or GLfloats per position + * 4 RGBA GLubytes, + * 2 GLfloats per tex coord * n_layers + * + * So for a given number of layers this gets the stride in + * 32bit words: + */ +#define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \ + (2 + 1 + 2 * (N_LAYERS)) + + +typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start, + int n_entries, + void *data); +typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0, + CoglJournalEntry *entry1); + +typedef struct _CoglJournalFlushState +{ + /* Note: this is a pointer to handle fallbacks. It normally holds a VBO + * offset, but when the driver doesn't support VBOs then this points into + * our GArray of logged vertices. */ + char * vbo_offset; + GLuint vertex_offset; +#ifndef HAVE_COGL_GL + CoglJournalIndices *indices; + size_t indices_type_size; +#endif +} CoglJournalFlushState; + /* these are defined in the particular backend */ void _cogl_path_add_node (gboolean new_sub_path, float x, @@ -51,94 +94,107 @@ void _cogl_path_add_node (gboolean new_sub_path, void _cogl_path_fill_nodes (); void _cogl_path_stroke_nodes (); -static void -_cogl_journal_flush_quad_batch (CoglJournalEntry *batch_start, - gint batch_len, - GLfloat *vertex_pointer) +void +_cogl_journal_dump_quad_vertices (guint8 *data, int n_layers) { - gsize stride; - int i; - gulong enable_flags = 0; - guint32 disable_mask; - int prev_n_texcoord_arrays_enabled; + size_t stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers); + int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* XXX NB: - * Our vertex data is arranged as follows: - * 4 vertices per quad: 2 GLfloats per position, - * 2 GLfloats per tex coord * n_layers - */ - stride = 2 + 2 * batch_start->n_layers; - stride *= sizeof (GLfloat); + g_print ("stride = %d (%d bytes)\n", (int)stride, (int)stride * 4); - disable_mask = (1 << batch_start->n_layers) - 1; - disable_mask = ~disable_mask; - - _cogl_material_flush_gl_state (ctx->source_material, - COGL_MATERIAL_FLUSH_FALLBACK_MASK, - batch_start->fallback_mask, - COGL_MATERIAL_FLUSH_DISABLE_MASK, - disable_mask, - /* Redundant when dealing with unsliced - * textures but does no harm... */ - COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE, - batch_start->layer0_override_texture, - NULL); - - for (i = 0; i < batch_start->n_layers; i++) + for (i = 0; i < 4; i++) { - GE (glClientActiveTexture (GL_TEXTURE0 + i)); - GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY)); - GE (glTexCoordPointer (2, GL_FLOAT, stride, vertex_pointer + 2 + 2 * i)); + float *v = (float *)data + (i * stride); + guint8 *c = data + 8 + (i * stride * 4); + int j; + + g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X", + i, v[0], v[1], c[0], c[1], c[2], c[3]); + for (j = 0; j < n_layers; j++) + { + float *t = v + 3 + 2 * j; + g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]); + } + g_print ("\n"); } - prev_n_texcoord_arrays_enabled = - ctx->n_texcoord_arrays_enabled; - ctx->n_texcoord_arrays_enabled = batch_start->n_layers; - for (; i < prev_n_texcoord_arrays_enabled; i++) +} + +void +_cogl_journal_dump_quad_batch (guint8 *data, int n_layers, int n_quads) +{ + size_t byte_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4; + int i; + + g_print ("_cogl_journal_dump_quad_batch: n_layers = %d, n_quads = %d\n", + n_layers, n_quads); + for (i = 0; i < n_quads; i++) + _cogl_journal_dump_quad_vertices (data + byte_stride * 4 * i, n_layers); +} + +static void +batch_and_call (CoglJournalEntry *entries, + int n_entries, + CoglJournalBatchTest can_batch_callback, + CoglJournalBatchCallback batch_callback, + void *data) +{ + int i; + int batch_len = 1; + CoglJournalEntry *batch_start = entries; + + for (i = 1; i < n_entries; i++) { - GE (glClientActiveTexture (GL_TEXTURE0 + i)); - GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); + CoglJournalEntry *entry0 = &entries[i - 1]; + CoglJournalEntry *entry1 = entry0 + 1; + + if (can_batch_callback (entry0, entry1)) + { + batch_len++; + continue; + } + + batch_callback (batch_start, batch_len, data); + + batch_start = entry1; + batch_len = 1; } - /* FIXME: This api is a bit yukky, ideally it will be removed if we - * re-work the cogl_enable mechanism */ - enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); + /* The last batch... */ + batch_callback (batch_start, batch_len, data); +} - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; +static void +_cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, + int batch_len, + void *data) +{ + CoglJournalFlushState *state = data; - enable_flags |= COGL_ENABLE_VERTEX_ARRAY; - cogl_enable (enable_flags); - - GE (glVertexPointer (2, GL_FLOAT, stride, vertex_pointer)); - _cogl_current_matrix_state_flush (); + GE (glLoadMatrixf ((GLfloat *)&batch_start->model_view)); #ifdef HAVE_COGL_GL - GE( glDrawArrays (GL_QUADS, 0, batch_len * 4) ); + GE (glDrawArrays (GL_QUADS, state->vertex_offset, batch_len * 4)); #else /* HAVE_COGL_GL */ - /* GLES doesn't support GL_QUADS so we will use GL_TRIANGLES and - indices */ - { - int needed_indices = batch_len * 6; - CoglHandle indices_handle - = cogl_vertex_buffer_indices_get_for_quads (needed_indices); - CoglVertexBufferIndices *indices - = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle); - - GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, - GPOINTER_TO_UINT (indices->vbo_name))); - GE (glDrawElements (GL_TRIANGLES, - 6 * batch_len, - indices->type, - NULL)); - GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0)); - } - -#endif /* HAVE_COGL_GL */ + if (batch_len > 1) + { + int indices_offset = (state->vertex_offset / 4) * 6; + GE (glDrawElements (GL_TRIANGLES, + 6 * batch_len, + indices->type, + indices_offset * state->indices_type_size)); + } + else + { + GE (glDrawArrays (GL_TRIANGLE_FAN, + state->vertex_offset, /* first */ + 4)); /* n vertices */ + } +#endif /* DEBUGGING CODE XXX: * This path will cause all rectangles to be drawn with a red, green @@ -149,6 +205,7 @@ _cogl_journal_flush_quad_batch (CoglJournalEntry *batch_start, { static CoglHandle outline = COGL_INVALID_HANDLE; static int color = 0; + int i; if (outline == COGL_INVALID_HANDLE) outline = cogl_material_new (); @@ -161,97 +218,293 @@ _cogl_journal_flush_quad_batch (CoglJournalEntry *batch_start, color == 2 ? 0xff : 0x00, 0xff); _cogl_material_flush_gl_state (outline, NULL); - _cogl_current_matrix_state_flush (); GE( glDrawArrays (GL_LINE_LOOP, 4 * i, 4) ); } } + + state->vertex_offset += (4 * batch_len); } +static gboolean +compare_entry_modelviews (CoglJournalEntry *entry0, + CoglJournalEntry *entry1) +{ + /* Batch together quads with the same model view matrix */ + + /* FIXME: this is nasty, there are much nicer ways to track this + * (at the add_quad_vertices level) without resorting to a memcmp! + * + * E.g. If the cogl-current-matrix code maintained an "age" for + * the modelview matrix we could simply check in add_quad_vertices + * if the age has increased, and if so record the change as a + * boolean in the journal. + */ + if (memcmp (&entry0->model_view, &entry1->model_view, + sizeof (GLfloat) * 16) == 0) + return TRUE; + else + return FALSE; +} + +/* At this point we have a run of quads that we know have compatible + * materials, but they may not all have the same modelview matrix */ +static void +_cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start, + gint batch_len, + void *data) +{ + gulong enable_flags = 0; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + +#if 0 + if (batch_len != 1) + g_debug ("batch len = %d", batch_len); +#endif + + _cogl_material_flush_gl_state (batch_start->material, + &batch_start->flush_options); + + /* FIXME: This api is a bit yukky, ideally it will be removed if we + * re-work the cogl_enable mechanism */ + enable_flags |= _cogl_material_get_cogl_enable_flags (batch_start->material); + + if (ctx->enable_backface_culling) + enable_flags |= COGL_ENABLE_BACKFACE_CULLING; + + enable_flags |= COGL_ENABLE_VERTEX_ARRAY; + enable_flags |= COGL_ENABLE_COLOR_ARRAY; + cogl_enable (enable_flags); + + batch_and_call (batch_start, + batch_len, + compare_entry_modelviews, + _cogl_journal_flush_modelview_and_entries, + data); + +} + +static gboolean +compare_entry_materials (CoglJournalEntry *entry0, CoglJournalEntry *entry1) +{ + /* batch rectangles using compatible materials */ + + /* XXX: _cogl_material_equal may give false negatives since it avoids + * deep comparisons as an optimization. It aims to compare enough so + * that we that we are able to batch the 90% common cases, but may not + * look at less common differences. */ + if (_cogl_material_equal (entry0->material, + &entry0->flush_options, + entry1->material, + &entry1->flush_options, + COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS)) + return TRUE; + else + return FALSE; +} + +/* At this point we know the stride has changed from the previous batch + * of journal entries */ +static void +_cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, + gint batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + size_t stride; + int i; + int prev_n_texcoord_arrays_enabled; +#ifndef HAVE_COGL_GL + int needed_indices = batch_len * 6; + CoglHandle indices_handle; + CoglVertexBufferIndices *indices; +#endif + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* XXX NB: + * Our vertex data is arranged as follows: + * 4 vertices per quad: 2 GLfloats per position, + * 4 RGBA GLubytes, + * 2 GLfloats per tex coord * n_layers + */ + stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers); + stride *= sizeof (GLfloat); + + GE (glVertexPointer (2, GL_FLOAT, stride, (void *)state->vbo_offset)); + GE (glColorPointer (4, GL_UNSIGNED_BYTE, stride, + (void *)(state->vbo_offset + 8))); + + for (i = 0; i < batch_start->n_layers; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY)); + GE (glTexCoordPointer (2, GL_FLOAT, stride, + (void *)(state->vbo_offset + 12 + 8 * i))); + } + prev_n_texcoord_arrays_enabled = + ctx->n_texcoord_arrays_enabled; + ctx->n_texcoord_arrays_enabled = batch_start->n_layers; + for (; i < prev_n_texcoord_arrays_enabled; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); + } + +#ifndef HAVE_COGL_GL + indices_handle = cogl_vertex_buffer_indices_get_for_quads (needed_indices); + indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle); + state->indices = indices; + + if (indices->type == GL_UNSIGNED_BYTE) + state->indices_type_size = 1; + else if (indices->type == GL_UNSIGNED_SHORT) + state->indices_type_size = 2; + else + g_critical ("unknown indices type %d", indices->type); + + GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, + GPOINTER_TO_UINT (indices->vbo_name))); +#endif + + /* We only call gl{Vertex,Color,Texture}Pointer when the stride within + * the VBO changes. (due to a change in the number of material layers) + * While the stride remains constant we walk forward through the above + * VBO use a vertex offset passed to glDraw{Arrays,Elements} */ + state->vertex_offset = 0; + + /* XXX: Uncomment for debugging */ +#if 0 + g_assert (cogl_get_features () & COGL_FEATURE_VBOS); + _cogl_journal_dump_quad_batch (((guint8 *)ctx->logged_vertices->data) + + (size_t)state->vbo_offset, + batch_start->n_layers, + batch_len); +#endif + + batch_and_call (batch_start, + batch_len, + compare_entry_materials, + _cogl_journal_flush_material_and_entries, + data); + +#ifndef HAVE_COGL_GL + GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0)); +#endif + + /* progress forward through the VBO containing all our vertices */ + state->vbo_offset += (stride * 4 * batch_len); +} + +static gboolean +compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1) +{ + /* Currently the only thing that affects the stride for our vertex arrays + * is the number of material layers. We need to update our VBO offsets + * whenever the stride changes. */ + /* TODO: We should be padding the n_layers == 1 case as if it were + * n_layers == 2 so we can reduce the need to split batches. */ + if (entry0->n_layers == entry1->n_layers) + return TRUE; + else + return FALSE; +} + +static void +upload_vertices_to_vbo (GArray *vertices, CoglJournalFlushState *state) +{ + size_t needed_vbo_len; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + needed_vbo_len = vertices->len * sizeof (GLfloat); + if (ctx->journal_vbo_len < needed_vbo_len) + { + GE (glDeleteBuffers (1, &ctx->journal_vbo)); + + GE (glGenBuffers (1, &ctx->journal_vbo)); + GE (glBindBuffer (GL_ARRAY_BUFFER, ctx->journal_vbo)); + GE (glBufferData (GL_ARRAY_BUFFER, + needed_vbo_len, + vertices->data, + GL_STATIC_DRAW)); + ctx->journal_vbo_len = needed_vbo_len; + } + else + { + GE (glBindBuffer (GL_ARRAY_BUFFER, ctx->journal_vbo)); + GE (glBufferData (GL_ARRAY_BUFFER, + needed_vbo_len, + NULL, + GL_STATIC_DRAW)); + GE (glBufferSubData (GL_ARRAY_BUFFER, + 0, + needed_vbo_len, + vertices->data)); + } + + /* As we flush the journal entries in batches we walk forward through the + * above VBO starting at offset 0... */ + state->vbo_offset = 0; +} + +/* XXX NB: When _cogl_journal_flush() returns all state relating + * to materials, all glEnable flags and current matrix state + * is undefined. + */ void _cogl_journal_flush (void) { - GLfloat *current_vertex_pointer; - GLfloat *batch_vertex_pointer; - CoglJournalEntry *batch_start; - guint batch_len; - int i; + CoglJournalFlushState state; + int i; + gboolean vbo_fallback = + (cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (ctx->journal->len == 0) return; - /* Current non-variables / constraints: + /* Load all the vertex data we have accumulated so far into a single VBO + * to minimize memory management costs within the GL driver. */ + if (!vbo_fallback) + upload_vertices_to_vbo (ctx->logged_vertices, &state); + else + state.vbo_offset = (char *)ctx->logged_vertices->data; + + /* Since the journal deals with emitting the modelview matrices manually + * we need to dirty our client side matrix stack cache... */ + _cogl_current_matrix_state_dirty (); + + /* batch_and_call() batches a list of journal entries according to some + * given criteria and calls a callback once for each determined batch. * - * - We don't have to worry about much GL state changing between journal - * entries since currently the journal never out lasts a single call to - * _cogl_multitexture_multiple_rectangles. So the user doesn't get the - * chance to fiddle with anything. (XXX: later this will be extended at - * which point we can start logging certain state changes) - * - * - Implied from above: all entries will refer to the same material. - * - * - Although _cogl_multitexture_multiple_rectangles can cause the wrap mode - * of textures to be modified, the journal is flushed if a wrap mode is - * changed so we don't currently have to log wrap mode changes. - * - * - XXX - others? + * The process of flushing the journal is done by splitting the entries + * by three broad criteria: + * 1) We split the entries according the number of material layers. + * Each time the number of material layers changes, then the stride + * changes, so we need to call gl{Vertex,Color,Texture}Pointer to + * inform GL of new VO offsets. + * 2) We then split according to compatible Cogl materials. + * This is where we flush material state + * 3) Finally we split according to modelview matrix changes. + * This is when we finally tell GL to draw something. */ + batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */ + ctx->journal->len, /* max number of entries to consider */ + compare_entry_strides, + _cogl_journal_flush_vbo_offsets_and_entries, /* callback */ + &state); /* data */ - /* TODO: "compile" the journal to find ways of batching draw calls and vertex - * data. - * - * Simple E.g. given current constraints... - * pass 0 - load all data into a single CoglVertexBuffer - * pass 1 - batch gl draw calls according to entries that use the same - * textures. - * - * We will be able to do cooler stuff here when we extend the life of - * journals beyond _cogl_multitexture_multiple_rectangles. - */ - - batch_vertex_pointer = (GLfloat *)ctx->logged_vertices->data; - batch_start = (CoglJournalEntry *)ctx->journal->data; - batch_len = 1; - - current_vertex_pointer = batch_vertex_pointer; - - for (i = 1; i < ctx->journal->len; i++) + for (i = 0; i < ctx->journal->len; i++) { - CoglJournalEntry *prev_entry = - &g_array_index (ctx->journal, CoglJournalEntry, i - 1); - CoglJournalEntry *current_entry = prev_entry + 1; - gsize stride; - - /* Progress the vertex pointer to the next quad */ - stride = 2 + current_entry->n_layers * 2; - current_vertex_pointer += stride * 4; - - /* batch rectangles using the same textures */ - if (current_entry->material == prev_entry->material && - current_entry->n_layers == prev_entry->n_layers && - current_entry->fallback_mask == prev_entry->fallback_mask && - current_entry->layer0_override_texture - == prev_entry->layer0_override_texture) - { - batch_len++; - continue; - } - - _cogl_journal_flush_quad_batch (batch_start, - batch_len, - batch_vertex_pointer); - - batch_start = current_entry; - batch_len = 1; - batch_vertex_pointer = current_vertex_pointer; + CoglJournalEntry *entry = + &g_array_index (ctx->journal, CoglJournalEntry, i); + _cogl_material_journal_unref (entry->material); } - /* The last batch... */ - _cogl_journal_flush_quad_batch (batch_start, - batch_len, - batch_vertex_pointer); - + if (!vbo_fallback) + GE (glBindBuffer (GL_ARRAY_BUFFER, 0)); g_array_set_size (ctx->journal, 0); g_array_set_size (ctx->logged_vertices, 0); @@ -263,18 +516,23 @@ _cogl_journal_log_quad (float x_1, float x_2, float y_2, CoglHandle material, - gint n_layers, - guint32 fallback_mask, + int n_layers, + guint32 fallback_layers, GLuint layer0_override_texture, float *tex_coords, guint tex_coords_len) { - int stride; + size_t stride; + size_t byte_stride; int next_vert; GLfloat *v; + GLubyte *c; int i; int next_entry; + guint32 disable_layers; CoglJournalEntry *entry; + CoglColor color; + guint8 r, g, b, a; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -282,15 +540,16 @@ _cogl_journal_log_quad (float x_1, * directly passed to OpenGL */ - /* We pack the vertex data as 2 (x,y) GLfloats folowed by 2 (tx,ty) GLfloats - * for each texture being used, E.g.: - * [X, Y, TX0, TY0, TX1, TY1, X, Y, TX0, TY0, X, Y, ...] - */ - stride = 2 + n_layers * 2; + /* XXX: See definition of GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details + * about how we pack our vertex data */ + stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers); + /* NB: stride is in 32bit words */ + byte_stride = stride * 4; next_vert = ctx->logged_vertices->len; g_array_set_size (ctx->logged_vertices, next_vert + 4 * stride); v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); + c = (GLubyte *)(v + 2); /* XXX: All the jumping around to fill in this strided buffer doesn't * seem ideal. */ @@ -298,18 +557,32 @@ _cogl_journal_log_quad (float x_1, /* XXX: we could defer expanding the vertex data for GL until we come * to flushing the journal. */ + cogl_material_get_color (material, &color); + r = cogl_color_get_red_byte (&color); + g = cogl_color_get_green_byte (&color); + b = cogl_color_get_blue_byte (&color); + a = cogl_color_get_alpha_byte (&color); + v[0] = x_1; v[1] = y_1; + c[0] = r; c[1] = g; c[2] = b; c[3] = a; v += stride; + c += byte_stride; v[0] = x_1; v[1] = y_2; + c[0] = r; c[1] = g; c[2] = b; c[3] = a; v += stride; + c += byte_stride; v[0] = x_2; v[1] = y_2; + c[0] = r; c[1] = g; c[2] = b; c[3] = a; v += stride; + c += byte_stride; v[0] = x_2; v[1] = y_1; + c[0] = r; c[1] = g; c[2] = b; c[3] = a; for (i = 0; i < n_layers; i++) { - GLfloat *t = - &g_array_index (ctx->logged_vertices, GLfloat, next_vert + 2 + 2 * i); + /* NB: See note at top about vertex buffer layout: */ + GLfloat *t = &g_array_index (ctx->logged_vertices, + GLfloat, next_vert + 3 + 2 * i); t[0] = tex_coords[0]; t[1] = tex_coords[1]; t += stride; @@ -320,14 +593,34 @@ _cogl_journal_log_quad (float x_1, t[0] = tex_coords[2]; t[1] = tex_coords[1]; } + /* XXX: Uncomment for debugging */ +#if 0 + v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); + _cogl_journal_dump_quad_vertices ((guint8 *)v, n_layers); +#endif + next_entry = ctx->journal->len; g_array_set_size (ctx->journal, next_entry + 1); entry = &g_array_index (ctx->journal, CoglJournalEntry, next_entry); - entry->material = material; + disable_layers = (1 << n_layers) - 1; + disable_layers = ~disable_layers; + + entry->material = _cogl_material_journal_ref (material); entry->n_layers = n_layers; - entry->fallback_mask = fallback_mask; - entry->layer0_override_texture = layer0_override_texture; + entry->flush_options.flags = + COGL_MATERIAL_FLUSH_FALLBACK_MASK | + COGL_MATERIAL_FLUSH_DISABLE_MASK | + COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE | + COGL_MATERIAL_FLUSH_SKIP_GL_COLOR; + entry->flush_options.fallback_layers = fallback_layers; + entry->flush_options.disable_layers = disable_layers; + entry->flush_options.layer0_override_texture = layer0_override_texture; + cogl_get_modelview_matrix (&entry->model_view); + + if (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING + || cogl_debug_flags & COGL_DEBUG_RECTANGLES) + _cogl_journal_flush (); } static void @@ -511,12 +804,12 @@ _cogl_multitexture_unsliced_quad (float x_1, float x_2, float y_2, CoglHandle material, - gint n_layers, - guint32 fallback_mask, + guint32 fallback_layers, const float *user_tex_coords, gint user_tex_coords_len) { - float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); + int n_layers = cogl_material_get_n_layers (material); + float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); const GList *layers; GList *tmp; int i; @@ -601,7 +894,7 @@ _cogl_multitexture_unsliced_quad (float x_1, /* NB: marking for fallback will replace the layer with * a default transparent texture */ - fallback_mask |= (1 << i); + fallback_layers |= (1 << i); } } @@ -672,7 +965,7 @@ _cogl_multitexture_unsliced_quad (float x_1, y_2, material, n_layers, - fallback_mask, + fallback_layers, 0, /* don't replace the layer0 texture */ final_tex_coords, n_layers * 4); @@ -699,7 +992,7 @@ _cogl_rectangles_with_multitexture_coords ( const GList *layers; int n_layers; const GList *tmp; - guint32 fallback_mask = 0; + guint32 fallback_layers = 0; gboolean all_use_sliced_quad_fallback = FALSE; int i; @@ -710,7 +1003,7 @@ _cogl_rectangles_with_multitexture_coords ( material = ctx->source_material; layers = cogl_material_get_layers (material); - n_layers = g_list_length ((GList *)layers); + n_layers = cogl_material_get_n_layers (material); /* * Validate all the layers of the current source material... @@ -740,7 +1033,7 @@ _cogl_rectangles_with_multitexture_coords ( { if (i == 0) { - fallback_mask = ~1; /* fallback all except the first layer */ + fallback_layers = ~1; /* fallback all except the first layer */ all_use_sliced_quad_fallback = TRUE; if (tmp->next) { @@ -766,7 +1059,7 @@ _cogl_rectangles_with_multitexture_coords ( /* NB: marking for fallback will replace the layer with * a default transparent texture */ - fallback_mask |= (1 << i); + fallback_layers |= (1 << i); continue; } } @@ -788,7 +1081,7 @@ _cogl_rectangles_with_multitexture_coords ( /* NB: marking for fallback will replace the layer with * a default transparent texture */ - fallback_mask |= (1 << i); + fallback_layers |= (1 << i); continue; } } @@ -803,8 +1096,7 @@ _cogl_rectangles_with_multitexture_coords ( || !_cogl_multitexture_unsliced_quad (rects[i].x_1, rects[i].y_1, rects[i].x_2, rects[i].y_2, material, - n_layers, - fallback_mask, + fallback_layers, rects[i].tex_coords, rects[i].tex_coords_len)) { @@ -832,7 +1124,11 @@ _cogl_rectangles_with_multitexture_coords ( } } +#if 0 + /* XXX: The current journal doesn't handle changes to the model view matrix + * so for now we force a flush at the end of every primitive. */ _cogl_journal_flush (); +#endif } void @@ -947,6 +1243,7 @@ _cogl_texture_sliced_polygon (CoglTextureVertex *vertices, int x, y, tex_num, i; GLuint gl_handle; GLfloat *v; + CoglMaterialFlushOptions options; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1027,13 +1324,14 @@ _cogl_texture_sliced_polygon (CoglTextureVertex *vertices, v += stride; } - _cogl_material_flush_gl_state (ctx->source_material, - COGL_MATERIAL_FLUSH_DISABLE_MASK, - (guint32)~1, /* disable all except the - first layer */ - COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE, - gl_handle, - NULL); + options.flags = + COGL_MATERIAL_FLUSH_DISABLE_MASK | + COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE; + /* disable all except the first layer */ + options.disable_layers = (guint32)~1; + options.layer0_override_texture = gl_handle; + + _cogl_material_flush_gl_state (ctx->source_material, &options); _cogl_current_matrix_state_flush (); GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) ); @@ -1047,7 +1345,7 @@ _cogl_multitexture_unsliced_polygon (CoglTextureVertex *vertices, guint n_layers, guint stride, gboolean use_color, - guint32 fallback_mask) + guint32 fallback_layers) { CoglHandle material; const GList *layers; @@ -1055,6 +1353,7 @@ _cogl_multitexture_unsliced_polygon (CoglTextureVertex *vertices, GList *tmp; CoglTexSliceSpan *y_span, *x_span; GLfloat *v; + CoglMaterialFlushOptions options; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1125,10 +1424,11 @@ _cogl_multitexture_unsliced_polygon (CoglTextureVertex *vertices, } } - _cogl_material_flush_gl_state (ctx->source_material, - COGL_MATERIAL_FLUSH_FALLBACK_MASK, - fallback_mask, - NULL); + options.flags = COGL_MATERIAL_FLUSH_FALLBACK_MASK; + if (use_color) + options.flags |= COGL_MATERIAL_FLUSH_SKIP_GL_COLOR; + options.fallback_layers = fallback_layers; + _cogl_material_flush_gl_state (ctx->source_material, &options); _cogl_current_matrix_state_flush (); GE (glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices)); @@ -1144,7 +1444,7 @@ cogl_polygon (CoglTextureVertex *vertices, int n_layers; GList *tmp; gboolean use_sliced_polygon_fallback = FALSE; - guint32 fallback_mask = 0; + guint32 fallback_layers = 0; int i; gulong enable_flags; guint stride; @@ -1154,6 +1454,7 @@ cogl_polygon (CoglTextureVertex *vertices, _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); cogl_clip_ensure (); material = ctx->source_material; @@ -1228,7 +1529,7 @@ cogl_polygon (CoglTextureVertex *vertices, "textures with waste\n", i); warning_seen = TRUE; - fallback_mask |= (1 << i); + fallback_layers |= (1 << i); continue; } } @@ -1293,7 +1594,7 @@ cogl_polygon (CoglTextureVertex *vertices, n_layers, stride, use_color, - fallback_mask); + fallback_layers); /* Reset the size of the logged vertex array because rendering rectangles expects it to start at 0 */ @@ -1313,6 +1614,7 @@ cogl_path_fill_preserve (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); cogl_clip_ensure (); if (ctx->path_nodes->len == 0) @@ -1334,11 +1636,12 @@ cogl_path_stroke_preserve (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - cogl_clip_ensure (); - if (ctx->path_nodes->len == 0) return; + _cogl_journal_flush (); + cogl_clip_ensure (); + _cogl_path_stroke_nodes(); } diff --git a/clutter/cogl/common/cogl-primitives.h b/clutter/cogl/common/cogl-primitives.h index 268fba3cd..dd5a73c8b 100644 --- a/clutter/cogl/common/cogl-primitives.h +++ b/clutter/cogl/common/cogl-primitives.h @@ -57,4 +57,6 @@ struct _CoglBezCubic floatVec2 p4; }; +void _cogl_journal_flush (void); + #endif /* __COGL_PRIMITIVES_H */ diff --git a/clutter/cogl/common/cogl-vertex-buffer.c b/clutter/cogl/common/cogl-vertex-buffer.c index a18852078..da99aeea8 100644 --- a/clutter/cogl/common/cogl-vertex-buffer.c +++ b/clutter/cogl/common/cogl-vertex-buffer.c @@ -137,6 +137,7 @@ #include "cogl-vertex-buffer-private.h" #include "cogl-texture-private.h" #include "cogl-material-private.h" +#include "cogl-primitives.h" #define PAD_FOR_ALIGNMENT(VAR, TYPE_SIZE) \ (VAR = TYPE_SIZE + ((VAR - 1) & ~(TYPE_SIZE - 1))) @@ -1500,9 +1501,10 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) gulong enable_flags = 0; guint max_texcoord_attrib_unit = 0; const GList *layers; - guint32 fallback_mask = 0; - guint32 disable_mask = ~0; + guint32 fallback_layers = 0; + guint32 disable_layers = ~0; int i; + CoglMaterialFlushOptions options; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1573,7 +1575,7 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) pointer)); if (attribute->texture_unit > max_texcoord_attrib_unit) max_texcoord_attrib_unit = attribute->texture_unit; - disable_mask &= ~(1 << attribute->texture_unit); + disable_layers &= ~(1 << attribute->texture_unit); break; case COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY: enable_flags |= COGL_ENABLE_VERTEX_ARRAY; @@ -1638,17 +1640,17 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) * vertices once for each layer, each time with a fiddled texture * matrix. */ - fallback_mask |= (1 << i); + fallback_layers |= (1 << i); } } - _cogl_material_flush_gl_state (ctx->source_material, - COGL_MATERIAL_FLUSH_FALLBACK_MASK, - fallback_mask, - COGL_MATERIAL_FLUSH_DISABLE_MASK, - disable_mask, - NULL); + options.flags = + COGL_MATERIAL_FLUSH_FALLBACK_MASK | + COGL_MATERIAL_FLUSH_DISABLE_MASK; + options.fallback_layers = fallback_layers; + options.disable_layers = disable_layers; + _cogl_material_flush_gl_state (ctx->source_material, &options); enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); if (ctx->enable_backface_culling) @@ -1730,6 +1732,9 @@ cogl_vertex_buffer_draw (CoglHandle handle, if (!cogl_is_vertex_buffer (handle)) return; + _cogl_journal_flush (); + cogl_clip_ensure (); + buffer = _cogl_vertex_buffer_pointer_from_handle (handle); cogl_clip_ensure (); @@ -1859,6 +1864,9 @@ cogl_vertex_buffer_draw_elements (CoglHandle handle, if (!cogl_is_vertex_buffer (handle)) return; + _cogl_journal_flush (); + cogl_clip_ensure (); + buffer = _cogl_vertex_buffer_pointer_from_handle (handle); if (!cogl_is_vertex_buffer_indices (indices_handle)) diff --git a/clutter/cogl/common/cogl.c b/clutter/cogl/common/cogl.c index d06d8ddd9..944ff8d68 100644 --- a/clutter/cogl/common/cogl.c +++ b/clutter/cogl/common/cogl.c @@ -216,6 +216,9 @@ cogl_get_enable () void cogl_set_depth_test_enabled (gboolean setting) { + /* Currently the journal can't track changes to depth state... */ + _cogl_journal_flush (); + if (setting) { glEnable (GL_DEPTH_TEST); @@ -236,6 +239,9 @@ cogl_set_backface_culling_enabled (gboolean setting) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* Currently the journal can't track changes to backface culling state... */ + _cogl_journal_flush (); + ctx->enable_backface_culling = setting; } @@ -388,9 +394,15 @@ _cogl_add_stencil_clip (float x_offset, float height, gboolean first) { + CoglHandle current_source; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_material_flush_gl_state (ctx->stencil_material, NULL); + _cogl_journal_flush (); + + /* temporarily swap in our special stenciling material */ + current_source = cogl_handle_ref (ctx->source_material); + cogl_set_source (ctx->stencil_material); if (first) { @@ -443,9 +455,17 @@ _cogl_add_stencil_clip (float x_offset, _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); } + /* make sure our rectangles hit the stencil buffer before we restore + * the stencil function / operation */ + _cogl_journal_flush (); + /* Restore the stencil mode */ GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + + /* restore the original source material */ + cogl_set_source (current_source); + cogl_handle_unref (current_source); } void @@ -630,6 +650,9 @@ cogl_set_fog (const CoglColor *fog_color, GLfloat fogColor[4]; GLenum gl_mode = GL_LINEAR; + /* The cogl journal doesn't currently track fog state changes */ + _cogl_journal_flush (); + fogColor[0] = cogl_color_get_red_float (fog_color); fogColor[1] = cogl_color_get_green_float (fog_color); fogColor[2] = cogl_color_get_blue_float (fog_color); @@ -667,6 +690,9 @@ cogl_set_fog (const CoglColor *fog_color, void cogl_disable_fog (void) { + /* Currently the journal can't track changes to fog state... */ + _cogl_journal_flush (); + glDisable (GL_FOG); } @@ -678,6 +704,12 @@ cogl_flush_gl_state (int flags) } #endif +void +_cogl_flush (void) +{ + _cogl_journal_flush (); +} + void cogl_read_pixels (int x, int y, @@ -711,6 +743,10 @@ cogl_read_pixels (int x, glPixelStorei (GL_PACK_SKIP_ROWS, 0); #endif /* HAVE_COGL_GL */ + /* make sure any batched primitives get emitted to the GL driver before + * issuing our read pixels... */ + _cogl_flush (); + glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); /* TODO: consider using the GL_MESA_pack_invert extension in the future diff --git a/clutter/cogl/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c index 1b6fab8f6..2266d9564 100644 --- a/clutter/cogl/gl/cogl-context.c +++ b/clutter/cogl/gl/cogl-context.c @@ -69,9 +69,13 @@ cogl_create_context () _context->journal = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry)); _context->logged_vertices = g_array_new (FALSE, FALSE, sizeof (GLfloat)); + _context->journal_vbo = 0; + _context->journal_vbo_len = 0; _context->current_material = NULL; _context->current_material_flags = 0; + memset (&_context->current_material_flush_options, + 0, sizeof (CoglMaterialFlushOptions)); _context->current_layers = g_array_new (FALSE, FALSE, sizeof (CoglLayerInfo)); _context->n_texcoord_arrays_enabled = 0; diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index 9e1596e36..fa1ccb637 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -28,6 +28,7 @@ #include "cogl-clip-stack.h" #include "cogl-matrix-stack.h" #include "cogl-current-matrix.h" +#include "cogl-material-private.h" typedef struct { @@ -78,10 +79,13 @@ typedef struct * can batch things together. */ GArray *journal; GArray *logged_vertices; + GLuint journal_vbo; + size_t journal_vbo_len; /* Some simple caching, to minimize state changes... */ CoglHandle current_material; gulong current_material_flags; + CoglMaterialFlushOptions current_material_flush_options; GArray *current_layers; guint n_texcoord_arrays_enabled; diff --git a/clutter/cogl/gl/cogl-fbo.c b/clutter/cogl/gl/cogl-fbo.c index db99e6ace..f13340966 100644 --- a/clutter/cogl/gl/cogl-fbo.c +++ b/clutter/cogl/gl/cogl-fbo.c @@ -169,6 +169,8 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + g_assert (ctx->draw_buffer_stack != NULL); draw_buffer = ctx->draw_buffer_stack->data; diff --git a/clutter/cogl/gl/cogl-primitives.c b/clutter/cogl/gl/cogl-primitives.c index 6c106f9ec..30b5553f7 100644 --- a/clutter/cogl/gl/cogl-primitives.c +++ b/clutter/cogl/gl/cogl-primitives.c @@ -76,16 +76,18 @@ _cogl_path_stroke_nodes () { guint path_start = 0; gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + CoglMaterialFlushOptions options; _COGL_GET_CONTEXT (ctx, NO_RETVAL); enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); cogl_enable (enable_flags); - _cogl_material_flush_gl_state (ctx->source_material, - COGL_MATERIAL_FLUSH_DISABLE_MASK, - (guint32)~0, /* disable all texture layers */ - NULL); + options.flags = COGL_MATERIAL_FLUSH_DISABLE_MASK; + /* disable all texture layers */ + options.disable_layers = (guint32)~0; + + _cogl_material_flush_gl_state (ctx->source_material, &options); _cogl_current_matrix_state_flush (); while (path_start < ctx->path_nodes->len) @@ -123,18 +125,24 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, CoglPathNode *path, gboolean merge) { - guint path_start = 0; - guint sub_path_num = 0; - float bounds_x; - float bounds_y; - float bounds_w; - float bounds_h; - gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + guint path_start = 0; + guint sub_path_num = 0; + float bounds_x; + float bounds_y; + float bounds_w; + float bounds_h; + gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + CoglHandle prev_source; _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + /* Just setup a simple material that doesn't use texturing... */ - _cogl_material_flush_gl_state (ctx->stencil_material, NULL); + prev_source = cogl_handle_ref (ctx->source_material); + cogl_set_source (ctx->stencil_material); + + _cogl_material_flush_gl_state (ctx->source_material, NULL); enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); @@ -161,9 +169,12 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( glDepthMask (FALSE) ); - _cogl_current_matrix_state_flush (); while (path_start < path_size) { + /* NB: after calling _cogl_journal_flush the current matrix + * state is undefined */ + _cogl_current_matrix_state_flush (); + GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), (guchar *) path + G_STRUCT_OFFSET (CoglPathNode, x)) ); @@ -177,7 +188,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) ); cogl_rectangle (bounds_x, bounds_y, bounds_x + bounds_w, bounds_y + bounds_h); - + _cogl_journal_flush (); GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); } @@ -212,6 +223,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, cogl_rectangle (-1.0, -1.0, 1.0, 1.0); cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + _cogl_journal_flush (); _cogl_current_matrix_pop (); @@ -227,6 +239,10 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + + /* restore the original material */ + cogl_set_source (prev_source); + cogl_handle_unref (prev_source); } void diff --git a/clutter/cogl/gl/cogl-program.c b/clutter/cogl/gl/cogl-program.c index 0a10cd4c4..75cf8e2f8 100644 --- a/clutter/cogl/gl/cogl-program.c +++ b/clutter/cogl/gl/cogl-program.c @@ -128,6 +128,11 @@ cogl_program_use (CoglHandle handle) if (handle != COGL_INVALID_HANDLE && !cogl_is_program (handle)) return; + /* The Cogl journal doesn't currently cope with the use of + * shaders so we have to flush all priitives whenever the + * current shader changes... */ + _cogl_journal_flush (); + if (handle == COGL_INVALID_HANDLE) gl_handle = 0; else diff --git a/clutter/cogl/gl/cogl-texture-private.h b/clutter/cogl/gl/cogl-texture-private.h index 365a47921..6a2ed7e39 100644 --- a/clutter/cogl/gl/cogl-texture-private.h +++ b/clutter/cogl/gl/cogl-texture-private.h @@ -26,6 +26,7 @@ #include "cogl-bitmap-private.h" #include "cogl-handle.h" +#include "cogl-material-private.h" typedef struct _CoglTexture CoglTexture; typedef struct _CoglTexSliceSpan CoglTexSliceSpan; @@ -99,10 +100,13 @@ struct _CoglTexture * later flush the journal we aim to batch data, and gl draw calls. */ typedef struct _CoglJournalEntry { - CoglHandle material; - gint n_layers; - guint32 fallback_mask; - GLuint layer0_override_texture; + CoglHandle material; + int n_layers; + CoglMaterialFlushOptions flush_options; + CoglMatrix model_view; + /* XXX: These entries are pretty big now considering the padding in + * CoglMaterialFlushOptions and CoglMatrix, so we might need to optimize this + * later. */ } CoglJournalEntry; CoglTexture* diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index fb118c707..7721ea2a8 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -39,6 +39,7 @@ #include "cogl-material.h" #include "cogl-context.h" #include "cogl-handle.h" +#include "cogl-primitives.h" #include #include @@ -60,8 +61,6 @@ #endif -extern void _cogl_journal_flush (void); - static void _cogl_texture_free (CoglTexture *tex); COGL_HANDLE_DEFINE (Texture, texture); diff --git a/clutter/eglnative/clutter-backend-egl.c b/clutter/eglnative/clutter-backend-egl.c index 844445305..adaf3cba0 100644 --- a/clutter/eglnative/clutter-backend-egl.c +++ b/clutter/eglnative/clutter-backend-egl.c @@ -81,6 +81,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend, eglWaitNative (EGL_CORE_NATIVE_ENGINE); clutter_actor_paint (CLUTTER_ACTOR (stage)); + _cogl_flush (); eglWaitGL(); eglSwapBuffers (backend_egl->edpy, stage_egl->egl_surface); diff --git a/clutter/eglx/clutter-backend-egl.c b/clutter/eglx/clutter-backend-egl.c index d3c36d027..af06f97df 100644 --- a/clutter/eglx/clutter-backend-egl.c +++ b/clutter/eglx/clutter-backend-egl.c @@ -139,6 +139,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend, /* this will cause the stage implementation to be painted as well */ clutter_actor_paint (CLUTTER_ACTOR (stage)); + _cogl_flush (); /* Why this paint is done in backend as likely GL windowing system * specific calls, like swapping buffers. diff --git a/clutter/fruity/clutter-backend-fruity.c b/clutter/fruity/clutter-backend-fruity.c index c90809dae..58c5e395a 100644 --- a/clutter/fruity/clutter-backend-fruity.c +++ b/clutter/fruity/clutter-backend-fruity.c @@ -72,6 +72,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend, eglWaitNative (EGL_CORE_NATIVE_ENGINE); clutter_actor_paint (CLUTTER_ACTOR (stage)); + _cogl_flush (); eglWaitGL(); eglSwapBuffers (backend_egl->edpy, stage_egl->egl_surface); } diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index d9fef911d..140038fed 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -498,6 +498,7 @@ clutter_backend_glx_redraw (ClutterBackend *backend, /* this will cause the stage implementation to be painted */ clutter_actor_paint (CLUTTER_ACTOR (stage)); + _cogl_flush (); if (stage_x11->xwin != None) { diff --git a/clutter/osx/clutter-stage-osx.c b/clutter/osx/clutter-stage-osx.c index 00bbcb45e..962d4a707 100644 --- a/clutter/osx/clutter-stage-osx.c +++ b/clutter/osx/clutter-stage-osx.c @@ -128,6 +128,7 @@ clutter_stage_osx_state_update (ClutterStageOSX *self, - (void) drawRect: (NSRect) bounds { clutter_actor_paint (CLUTTER_ACTOR (self->stage_osx->wrapper)); + _cogl_flush (); [[self openGLContext] flushBuffer]; } diff --git a/clutter/sdl/clutter-backend-sdl.c b/clutter/sdl/clutter-backend-sdl.c index 70a1bc984..90c11674b 100644 --- a/clutter/sdl/clutter-backend-sdl.c +++ b/clutter/sdl/clutter-backend-sdl.c @@ -68,7 +68,8 @@ clutter_backend_sdl_redraw (ClutterBackend *backend, ClutterStage *stage) { clutter_actor_paint (CLUTTER_ACTOR (stage)); - + _cogl_flush (); + SDL_GL_SwapBuffers(); } diff --git a/clutter/win32/clutter-backend-win32.c b/clutter/win32/clutter-backend-win32.c index 6cc9155b6..f1531a009 100644 --- a/clutter/win32/clutter-backend-win32.c +++ b/clutter/win32/clutter-backend-win32.c @@ -297,6 +297,7 @@ clutter_backend_win32_redraw (ClutterBackend *backend, /* this will cause the stage implementation to be painted */ clutter_actor_paint (CLUTTER_ACTOR (stage)); + _cogl_flush (); if (stage_win32->client_dc) SwapBuffers (stage_win32->client_dc); From 40cfaeaffc9b744d99c21c1e6cd5b57e358ff37a Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 10 Jun 2009 13:59:45 +0100 Subject: [PATCH 02/26] [journal] Always pad our vertex data as if at least 2 layers are enabled The number of material layers enabled when logging a quad in the journal determines the stride of the corresponding vertex data (since we need a set of texture coordinates for each layer.) By padding data in the case where we have only one layer we can avoid a change in stride if we are mixing single and double layer primitives in a scene (e.g. relevent for a composite manager that may use 2 layers for all shaped windows) Avoiding stride changes means we can minimize calls to gl{Vertex,Color}Pointer when flushing the journal. Since we need to update the texcoord pointers when the actual number of layers changes, this adds another batch_and_call() stage to deal with glTexCoordPointer and enabling/disabling the client arrays. --- clutter/cogl/common/cogl-primitives.c | 120 ++++++++++++++++++-------- 1 file changed, 84 insertions(+), 36 deletions(-) diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index 9e3ea89d6..b0fbd976b 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -61,11 +61,16 @@ * 4 RGBA GLubytes, * 2 GLfloats per tex coord * n_layers * - * So for a given number of layers this gets the stride in - * 32bit words: + * Where n_layers corresponds to the number of material layers enabled + * + * To avoid frequent changes in the stride of our vertex data we always pad + * n_layers to be >= 2 + * + * So for a given number of layers this gets the stride in 32bit words: */ +#define MIN_LAYER_PADING 2 #define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \ - (2 + 1 + 2 * (N_LAYERS)) + (2 + 1 + 2 * (N_LAYERS < MIN_LAYER_PADING ? MIN_LAYER_PADING : N_LAYERS)) typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start, @@ -76,6 +81,7 @@ typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0, typedef struct _CoglJournalFlushState { + size_t stride; /* Note: this is a pointer to handle fallbacks. It normally holds a VBO * offset, but when the driver doesn't support VBOs then this points into * our GArray of logged vertices. */ @@ -303,43 +309,35 @@ compare_entry_materials (CoglJournalEntry *entry0, CoglJournalEntry *entry1) return FALSE; } -/* At this point we know the stride has changed from the previous batch - * of journal entries */ +/* Since the stride may not reflect the number of texture layers in use + * (due to padding) we deal with texture coordinate offsets separately + * from vertex and color offsets... */ static void -_cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, - gint batch_len, - void *data) +_cogl_journal_flush_texcoord_vbo_offsets_and_entries ( + CoglJournalEntry *batch_start, + gint batch_len, + void *data) { - CoglJournalFlushState *state = data; - size_t stride; - int i; - int prev_n_texcoord_arrays_enabled; -#ifndef HAVE_COGL_GL - int needed_indices = batch_len * 6; - CoglHandle indices_handle; - CoglVertexBufferIndices *indices; -#endif + CoglJournalFlushState *state = data; + int prev_n_texcoord_arrays_enabled; + int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* XXX NB: - * Our vertex data is arranged as follows: - * 4 vertices per quad: 2 GLfloats per position, - * 4 RGBA GLubytes, - * 2 GLfloats per tex coord * n_layers - */ - stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers); - stride *= sizeof (GLfloat); - - GE (glVertexPointer (2, GL_FLOAT, stride, (void *)state->vbo_offset)); - GE (glColorPointer (4, GL_UNSIGNED_BYTE, stride, - (void *)(state->vbo_offset + 8))); - for (i = 0; i < batch_start->n_layers; i++) { GE (glClientActiveTexture (GL_TEXTURE0 + i)); GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY)); - GE (glTexCoordPointer (2, GL_FLOAT, stride, + /* XXX NB: + * Our journal's vertex data is arranged as follows: + * 4 vertices per quad: + * 2 GLfloats per position + * 4 RGBA GLubytes, + * 2 GLfloats per tex coord * n_layers + * (though n_layers may be padded; see definition of + * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details) + */ + GE (glTexCoordPointer (2, GL_FLOAT, state->stride, (void *)(state->vbo_offset + 12 + 8 * i))); } prev_n_texcoord_arrays_enabled = @@ -351,6 +349,56 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); } + batch_and_call (batch_start, + batch_len, + compare_entry_materials, + _cogl_journal_flush_material_and_entries, + data); +} + +static gboolean +compare_entry_n_layers (CoglJournalEntry *entry0, CoglJournalEntry *entry1) +{ + if (entry0->n_layers == entry1->n_layers) + return TRUE; + else + return FALSE; +} + +/* At this point we know the stride has changed from the previous batch + * of journal entries */ +static void +_cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, + gint batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + size_t stride; +#ifndef HAVE_COGL_GL + int needed_indices = batch_len * 6; + CoglHandle indices_handle; + CoglVertexBufferIndices *indices; +#endif + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* XXX NB: + * Our journal's vertex data is arranged as follows: + * 4 vertices per quad: + * 2 GLfloats per position + * 4 RGBA GLubytes, + * 2 GLfloats per tex coord * n_layers + * (though n_layers may be padded; see definition of + * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details) + */ + stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers); + stride *= sizeof (GLfloat); + state->stride = stride; + + GE (glVertexPointer (2, GL_FLOAT, stride, (void *)state->vbo_offset)); + GE (glColorPointer (4, GL_UNSIGNED_BYTE, stride, + (void *)(state->vbo_offset + 8))); + #ifndef HAVE_COGL_GL indices_handle = cogl_vertex_buffer_indices_get_for_quads (needed_indices); indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle); @@ -384,8 +432,8 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, batch_and_call (batch_start, batch_len, - compare_entry_materials, - _cogl_journal_flush_material_and_entries, + compare_entry_n_layers, + _cogl_journal_flush_texcoord_vbo_offsets_and_entries, data); #ifndef HAVE_COGL_GL @@ -402,9 +450,9 @@ compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1) /* Currently the only thing that affects the stride for our vertex arrays * is the number of material layers. We need to update our VBO offsets * whenever the stride changes. */ - /* TODO: We should be padding the n_layers == 1 case as if it were - * n_layers == 2 so we can reduce the need to split batches. */ - if (entry0->n_layers == entry1->n_layers) + if (entry0->n_layers == entry1->n_layers || + (entry0->n_layers <= MIN_LAYER_PADING && + entry1->n_layers <= MIN_LAYER_PADING)) return TRUE; else return FALSE; From dc1ca79398e343d06918cf1c38b72771f476f0a6 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 18:36:27 +0100 Subject: [PATCH 03/26] [actor] Avoid modifying materials mid-scene to improve journal batching Currently cogl_set_source_color uses a single shared material which means each actor that uses it causes the journal to flush if the color changes. Until we improve cogl_set_source_color to use a pool of materials that can be recycled as the journal is flushed we avoid mid-scene material changes by giving all actors a private material instead. --- clutter/clutter-actor.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 7f8b1e997..76c574a20 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -332,6 +332,8 @@ struct _ClutterActorPrivate PangoContext *pango_context; ClutterActor *opacity_parent; + + CoglHandle pick_material; }; enum @@ -1374,11 +1376,13 @@ clutter_actor_real_pick (ClutterActor *self, width = box.x2 - box.x1; height = box.y2 - box.y1; - cogl_set_source_color4ub (color->red, - color->green, - color->blue, - color->alpha); + cogl_material_set_color4ub (self->priv->pick_material, + color->red, + color->green, + color->blue, + color->alpha); + cogl_set_source (self->priv->pick_material); cogl_rectangle (0, 0, width, height); } } @@ -4254,6 +4258,8 @@ clutter_actor_init (ClutterActor *self) priv->opacity_parent = NULL; priv->enable_model_view_transform = TRUE; + priv->pick_material = cogl_material_new (); + memset (priv->clip, 0, sizeof (gfloat) * 4); } From 938452f1b1109e7c8daff8f49c4f0a45a13be25f Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 5 Jun 2009 13:03:14 +0100 Subject: [PATCH 04/26] [rectangle] Avoid modifying materials mid scene To improve batching of geometry in the Cogl journal we need to avoid modifying materials midscene. Currently cogl_set_source_color and cogl_set_source_texture simply modify a single shared material. In the future we can improve this so they use a pool of materials that gets recycled as the journal is flushed, but for now we give all ClutterRectangles their own private materials for painting with. --- clutter/clutter-rectangle.c | 89 +++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/clutter/clutter-rectangle.c b/clutter/clutter-rectangle.c index fb975bed2..0eeb76648 100644 --- a/clutter/clutter-rectangle.c +++ b/clutter/clutter-rectangle.c @@ -60,8 +60,10 @@ enum struct _ClutterRectanglePrivate { - ClutterColor color; - ClutterColor border_color; + ClutterColor primary_color; + CoglHandle primary_material; + ClutterColor border_color; + CoglHandle border_material; guint border_width; @@ -78,6 +80,7 @@ clutter_rectangle_paint (ClutterActor *self) ClutterRectanglePrivate *priv; ClutterGeometry geom; guint8 tmp_alpha; + guint8 opacity; rectangle = CLUTTER_RECTANGLE(self); priv = rectangle->priv; @@ -88,6 +91,8 @@ clutter_rectangle_paint (ClutterActor *self) : "unknown"); clutter_actor_get_allocation_geometry (self, &geom); + opacity = clutter_actor_get_paint_opacity (self); + /* parent paint call will have translated us into position so * paint from 0, 0 */ @@ -96,15 +101,26 @@ clutter_rectangle_paint (ClutterActor *self) /* compute the composited opacity of the actor taking into * account the opacity of the color set by the user */ - tmp_alpha = clutter_actor_get_paint_opacity (self) - * priv->border_color.alpha - / 255; + tmp_alpha = opacity * priv->border_color.alpha / 255; /* paint the border */ - cogl_set_source_color4ub (priv->border_color.red, - priv->border_color.green, - priv->border_color.blue, - tmp_alpha); + + /* We are assuming this is a NOP when the color doesn't change. + * This is important since we should aim to never modify + * materials mid-scene. */ + cogl_material_set_color4ub (priv->border_material, + priv->border_color.red, + priv->border_color.green, + priv->border_color.blue, + tmp_alpha); + + cogl_set_source (priv->border_material); + + cogl_material_set_color4ub (priv->border_material, + priv->border_color.red, + priv->border_color.green, + priv->border_color.blue, + tmp_alpha); /* this sucks, but it's the only way to make a border */ cogl_rectangle (priv->border_width, 0, @@ -124,15 +140,16 @@ clutter_rectangle_paint (ClutterActor *self) priv->border_width, geom.height - priv->border_width); - tmp_alpha = clutter_actor_get_paint_opacity (self) - * priv->color.alpha - / 255; + tmp_alpha = opacity * priv->primary_color.alpha / 255; /* now paint the rectangle */ - cogl_set_source_color4ub (priv->color.red, - priv->color.green, - priv->color.blue, - tmp_alpha); + cogl_set_source (priv->primary_material); + + cogl_material_set_color4ub (priv->primary_material, + priv->primary_color.red, + priv->primary_color.green, + priv->primary_color.blue, + tmp_alpha); cogl_rectangle (priv->border_width, priv->border_width, geom.width - priv->border_width, @@ -143,14 +160,16 @@ clutter_rectangle_paint (ClutterActor *self) /* compute the composited opacity of the actor taking into * account the opacity of the color set by the user */ - tmp_alpha = clutter_actor_get_paint_opacity (self) - * priv->color.alpha - / 255; + tmp_alpha = opacity * priv->primary_color.alpha / 255; - cogl_set_source_color4ub (priv->color.red, - priv->color.green, - priv->color.blue, - tmp_alpha); + /* now paint the rectangle */ + cogl_set_source (priv->primary_material); + + cogl_material_set_color4ub (priv->primary_material, + priv->primary_color.red, + priv->primary_color.green, + priv->primary_color.blue, + tmp_alpha); cogl_rectangle (0, 0, geom.width, geom.height); } @@ -197,7 +216,7 @@ clutter_rectangle_get_property (GObject *object, switch (prop_id) { case PROP_COLOR: - clutter_value_set_color (value, &priv->color); + clutter_value_set_color (value, &priv->primary_color); break; case PROP_BORDER_COLOR: clutter_value_set_color (value, &priv->border_color); @@ -308,8 +327,10 @@ clutter_rectangle_init (ClutterRectangle *self) self->priv = priv = CLUTTER_RECTANGLE_GET_PRIVATE (self); - priv->color = default_color; + priv->primary_color = default_color; priv->border_color = default_border_color; + priv->primary_material = cogl_material_new (); + priv->border_material = cogl_material_new (); priv->border_width = 0; @@ -364,10 +385,10 @@ clutter_rectangle_get_color (ClutterRectangle *rectangle, priv = rectangle->priv; - color->red = priv->color.red; - color->green = priv->color.green; - color->blue = priv->color.blue; - color->alpha = priv->color.alpha; + color->red = priv->primary_color.red; + color->green = priv->primary_color.green; + color->blue = priv->primary_color.blue; + color->alpha = priv->primary_color.alpha; } /** @@ -390,10 +411,10 @@ clutter_rectangle_set_color (ClutterRectangle *rectangle, priv = rectangle->priv; - priv->color.red = color->red; - priv->color.green = color->green; - priv->color.blue = color->blue; - priv->color.alpha = color->alpha; + priv->primary_color.red = color->red; + priv->primary_color.green = color->green; + priv->primary_color.blue = color->blue; + priv->primary_color.alpha = color->alpha; #if 0 /* FIXME - appears to be causing border to always get drawn */ @@ -525,7 +546,7 @@ clutter_rectangle_set_border_color (ClutterRectangle *rectangle, priv->border_color.blue = color->blue; priv->border_color.alpha = color->alpha; - if (clutter_color_equal (&priv->color, &priv->border_color)) + if (clutter_color_equal (&priv->primary_color, &priv->border_color)) priv->has_border = FALSE; else priv->has_border = TRUE; From aca1bf4329481cedfd8ffa65591338662c6d626a Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 4 Jun 2009 14:23:16 +0100 Subject: [PATCH 05/26] [cogl material] optimize logging of material colors in the journal We now put the color of materials into the vertex array used by the journal instead of calling glColor() but the number of requests for the material color were quite expensive so we have changed the material color to internally be byte components instead of floats to avoid repeat conversions and added _cogl_material_get_colorubv as a fast-path for the journal to copy data into the vertex array. --- clutter/cogl/common/cogl-material-private.h | 21 +++------ clutter/cogl/common/cogl-material.c | 51 ++++++++++++--------- clutter/cogl/common/cogl-primitives.c | 26 +++++------ clutter/cogl/gles/cogl-gles2-wrapper.c | 7 +++ clutter/cogl/gles/cogl-gles2-wrapper.h | 2 + 5 files changed, 56 insertions(+), 51 deletions(-) diff --git a/clutter/cogl/common/cogl-material-private.h b/clutter/cogl/common/cogl-material-private.h index 6fe52be81..36763e73e 100644 --- a/clutter/cogl/common/cogl-material-private.h +++ b/clutter/cogl/common/cogl-material-private.h @@ -117,7 +117,7 @@ struct _CoglMaterial gulong flags; /* If no lighting is enabled; this is the basic material color */ - GLfloat unlit[4]; + GLubyte unlit[4]; /* Standard OpenGL lighting model attributes */ GLfloat ambient[4]; @@ -234,20 +234,11 @@ typedef struct _CoglMaterialFlushOptions GLuint layer0_override_texture; } CoglMaterialFlushOptions; -/* - * cogl_material_flush_gl_state: - * @material: A CoglMaterial object - * @...: A NULL terminated list of (CoglMaterialFlushOption, data) pairs - * - * Note: It is possible for a layer object of type - * COGL_MATERIAL_LAYER_TYPE_TEXTURE to be realized before a texture - * object has been associated with the layer. For example this happens - * if you setup layer combining for a given layer index before calling - * cogl_material_set_layer for that index. - * - * Returns: A CoglHandle to the layers texture object or COGL_INVALID_HANDLE - * if a texture has not been set yet. - */ + + +void _cogl_material_get_colorubv (CoglHandle handle, + guint8 *color); + void _cogl_material_flush_gl_state (CoglHandle material, CoglMaterialFlushOptions *options); diff --git a/clutter/cogl/common/cogl-material.c b/clutter/cogl/common/cogl-material.c index a2ad30fd9..bc56b8aa6 100644 --- a/clutter/cogl/common/cogl-material.c +++ b/clutter/cogl/common/cogl-material.c @@ -76,14 +76,14 @@ cogl_material_new (void) { /* Create new - blank - material */ CoglMaterial *material = g_new0 (CoglMaterial, 1); - GLfloat *unlit = material->unlit; + GLubyte *unlit = material->unlit; GLfloat *ambient = material->ambient; GLfloat *diffuse = material->diffuse; GLfloat *specular = material->specular; GLfloat *emission = material->emission; /* Use the same defaults as the GL spec... */ - unlit[0] = 1.0; unlit[1] = 1.0; unlit[2] = 1.0; unlit[3] = 1.0; + unlit[0] = 0xff; unlit[1] = 0xff; unlit[2] = 0xff; unlit[3] = 0xff; material->flags |= COGL_MATERIAL_FLAG_DEFAULT_COLOR; /* Use the same defaults as the GL spec... */ @@ -160,7 +160,7 @@ handle_automatic_blend_enable (CoglMaterial *material) material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND; } - if (material->unlit[3] != 1.0) + if (material->unlit[3] != 0xff) material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND; } @@ -184,11 +184,20 @@ cogl_material_get_color (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); - cogl_color_set_from_4f (color, - material->unlit[0], - material->unlit[1], - material->unlit[2], - material->unlit[3]); + cogl_color_set_from_4ub (color, + material->unlit[0], + material->unlit[1], + material->unlit[2], + material->unlit[3]); +} + +/* This is used heavily by the cogl journal when logging quads */ +void +_cogl_material_get_colorubv (CoglHandle handle, + guint8 *color) +{ + CoglMaterial *material = _cogl_material_pointer_from_handle (handle); + memcpy (color, material->unlit, 4); } void @@ -196,16 +205,16 @@ cogl_material_set_color (CoglHandle handle, const CoglColor *unlit_color) { CoglMaterial *material; - GLfloat unlit[4]; + GLubyte unlit[4]; g_return_if_fail (cogl_is_material (handle)); material = _cogl_material_pointer_from_handle (handle); - unlit[0] = cogl_color_get_red_float (unlit_color); - unlit[1] = cogl_color_get_green_float (unlit_color); - unlit[2] = cogl_color_get_blue_float (unlit_color); - unlit[3] = cogl_color_get_alpha_float (unlit_color); + unlit[0] = cogl_color_get_red_byte (unlit_color); + unlit[1] = cogl_color_get_green_byte (unlit_color); + unlit[2] = cogl_color_get_blue_byte (unlit_color); + unlit[3] = cogl_color_get_alpha_byte (unlit_color); if (memcmp (unlit, material->unlit, sizeof (unlit)) == 0) return; @@ -215,10 +224,10 @@ cogl_material_set_color (CoglHandle handle, memcpy (material->unlit, unlit, sizeof (unlit)); material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_COLOR; - if (unlit[0] == 1.0 && - unlit[1] == 1.0 && - unlit[2] == 1.0 && - unlit[3] == 1.0) + if (unlit[0] == 0xff && + unlit[1] == 0xff && + unlit[2] == 0xff && + unlit[3] == 0xff) material->flags |= COGL_MATERIAL_FLAG_DEFAULT_COLOR; handle_automatic_blend_enable (material); @@ -1469,10 +1478,10 @@ _cogl_material_flush_base_gl_state (CoglMaterial *material, ctx->current_material_flush_options.flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR) { - GE (glColor4f (material->unlit[0], - material->unlit[1], - material->unlit[2], - material->unlit[3])); + GE (glColor4ub (material->unlit[0], + material->unlit[1], + material->unlit[2], + material->unlit[3])); } } diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index b0fbd976b..87b58624e 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -575,16 +575,15 @@ _cogl_journal_log_quad (float x_1, int next_vert; GLfloat *v; GLubyte *c; + GLubyte *src_c; int i; int next_entry; guint32 disable_layers; CoglJournalEntry *entry; - CoglColor color; - guint8 r, g, b, a; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* The vertex data is logged into a seperate array in a layout that can be + /* The vertex data is logged into a separate array in a layout that can be * directly passed to OpenGL */ @@ -605,26 +604,23 @@ _cogl_journal_log_quad (float x_1, /* XXX: we could defer expanding the vertex data for GL until we come * to flushing the journal. */ - cogl_material_get_color (material, &color); - r = cogl_color_get_red_byte (&color); - g = cogl_color_get_green_byte (&color); - b = cogl_color_get_blue_byte (&color); - a = cogl_color_get_alpha_byte (&color); + /* FIXME: This is a hacky optimization, since it will break if we + * change the definition of CoglColor: */ + _cogl_material_get_colorubv (material, c); + src_c = c; + for (i = 0; i < 3; i++) + { + c += byte_stride; + memcpy (c, src_c, 4); + } v[0] = x_1; v[1] = y_1; - c[0] = r; c[1] = g; c[2] = b; c[3] = a; v += stride; - c += byte_stride; v[0] = x_1; v[1] = y_2; - c[0] = r; c[1] = g; c[2] = b; c[3] = a; v += stride; - c += byte_stride; v[0] = x_2; v[1] = y_2; - c[0] = r; c[1] = g; c[2] = b; c[3] = a; v += stride; - c += byte_stride; v[0] = x_2; v[1] = y_1; - c[0] = r; c[1] = g; c[2] = b; c[3] = a; for (i = 0; i < n_layers; i++) { diff --git a/clutter/cogl/gles/cogl-gles2-wrapper.c b/clutter/cogl/gles/cogl-gles2-wrapper.c index 979ade598..12716f009 100644 --- a/clutter/cogl/gles/cogl-gles2-wrapper.c +++ b/clutter/cogl/gles/cogl-gles2-wrapper.c @@ -1480,6 +1480,13 @@ cogl_wrap_glColor4f (GLclampf r, GLclampf g, GLclampf b, GLclampf a) glVertexAttrib4f (COGL_GLES2_WRAPPER_COLOR_ATTRIB, r, g, b, a); } +void +cogl_wrap_glColor4ub (GLubyte r, GLubyte g, GLubyte b, GLubyte a) +{ + glVertexAttrib4f (COGL_GLES2_WRAPPER_COLOR_ATTRIB, + r/255.0, g/255.0, b/255.0, a/255.0); +} + void cogl_wrap_glClipPlanef (GLenum plane, GLfloat *equation) { diff --git a/clutter/cogl/gles/cogl-gles2-wrapper.h b/clutter/cogl/gles/cogl-gles2-wrapper.h index 51bee2d5c..5d232f922 100644 --- a/clutter/cogl/gles/cogl-gles2-wrapper.h +++ b/clutter/cogl/gles/cogl-gles2-wrapper.h @@ -340,6 +340,7 @@ void cogl_wrap_glDisableClientState (GLenum array); void cogl_wrap_glAlphaFunc (GLenum func, GLclampf ref); void cogl_wrap_glColor4f (GLclampf r, GLclampf g, GLclampf b, GLclampf a); +void cogl_wrap_glColor4ub (GLubyte r, GLubyte g, GLubyte b, GLubyte a); void cogl_wrap_glClipPlanef (GLenum plane, GLfloat *equation); @@ -393,6 +394,7 @@ void _cogl_gles2_clear_cache_for_program (CoglHandle program); #define glDisableClientState cogl_wrap_glDisableClientState #define glAlphaFunc cogl_wrap_glAlphaFunc #define glColor4f cogl_wrap_glColor4f +#define glColor4ub cogl_wrap_glColor4ub #define glClipPlanef cogl_wrap_glClipPlanef #define glGetIntegerv cogl_wrap_glGetIntegerv #define glGetFloatv cogl_wrap_glGetFloatv From a8be68c83cb97a8bafc93f5304e177e1b265bca1 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 5 Jun 2009 12:58:31 +0100 Subject: [PATCH 06/26] [cogl-color] Adds a cogl_color_equal() function CoglColors shouldn't be compared using memcmp since they may contain uninitialized padding bytes. The prototype is also suitable for passing to g_hash_table_new as the key_equal_func. _cogl_pango_display_list_add_texture now uses this instead of memcmp. --- clutter/cogl/cogl-color.h | 16 ++++++++++++++++ clutter/cogl/common/cogl-color.c | 15 +++++++++++++++ clutter/pango/cogl-pango-display-list.c | 3 +-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/clutter/cogl/cogl-color.h b/clutter/cogl/cogl-color.h index 73de891de..6ade2594f 100644 --- a/clutter/cogl/cogl-color.h +++ b/clutter/cogl/cogl-color.h @@ -240,6 +240,22 @@ float cogl_color_get_alpha (const CoglColor *color); */ void cogl_color_premultiply (CoglColor *color); +/** + * cogl_color_equal: + * @v1: a #CoglColor + * @v2: a #CoglColor + * + * Compares two #CoglColors and checks if they are the same. + * + * This function can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using #CoglColors as keys in a #GHashTable. + * + * Return value: %TRUE if the two colors are the same. + * + * Since: 1.0 + */ +gboolean cogl_color_equal (gconstpointer v1, gconstpointer v2); + G_END_DECLS #endif /* __COGL_COLOR_H__ */ diff --git a/clutter/cogl/common/cogl-color.c b/clutter/cogl/common/cogl-color.c index df338fcc4..25a1bdaf2 100644 --- a/clutter/cogl/common/cogl-color.c +++ b/clutter/cogl/common/cogl-color.c @@ -25,6 +25,8 @@ #include "config.h" #endif +#include + #include "cogl.h" #include "cogl-color.h" #include "cogl-fixed.h" @@ -184,3 +186,16 @@ cogl_set_source_color4f (float red, cogl_color_set_from_4f (&c, red, green, blue, alpha); cogl_set_source_color (&c); } + +gboolean +cogl_color_equal (gconstpointer v1, gconstpointer v2) +{ + const guint32 *c1 = v1, *c2 = v2; + + g_return_val_if_fail (v1 != NULL, FALSE); + g_return_val_if_fail (v2 != NULL, FALSE); + + /* XXX: We don't compare the padding */ + return *c1 == *c2 ? TRUE : FALSE; +} + diff --git a/clutter/pango/cogl-pango-display-list.c b/clutter/pango/cogl-pango-display-list.c index 6a3b29c60..6790411bf 100644 --- a/clutter/pango/cogl-pango-display-list.c +++ b/clutter/pango/cogl-pango-display-list.c @@ -138,8 +138,7 @@ _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, && (node = dl->last_node->data)->type == COGL_PANGO_DISPLAY_LIST_TEXTURE && node->d.texture.texture == texture && (dl->color_override - ? (node->color_override && !memcmp (&dl->color, &node->color, - sizeof (CoglColor))) + ? (node->color_override && cogl_color_equal (&dl->color, &node->color)) : !node->color_override)) { /* Get rid of the vertex buffer so that it will be recreated */ From 713af6535de11f27e1da9c1e233e8402d2864ab9 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 5 Jun 2009 13:50:00 +0100 Subject: [PATCH 07/26] [cogl-debug] Adds a "client-side-matrices" Cogl debug option This allows us to force Cogl to use the client side matrix stack even when direct rendering. --- clutter/cogl/cogl-debug.h | 3 ++- clutter/cogl/common/cogl-current-matrix.c | 3 ++- clutter/cogl/common/cogl-debug.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/clutter/cogl/cogl-debug.h b/clutter/cogl/cogl-debug.h index 1b42358dc..c67f37568 100644 --- a/clutter/cogl/cogl-debug.h +++ b/clutter/cogl/cogl-debug.h @@ -39,7 +39,8 @@ typedef enum { COGL_DEBUG_RECTANGLES = 1 << 7, COGL_DEBUG_HANDLE = 1 << 8, COGL_DEBUG_BLEND_STRINGS = 1 << 9, - COGL_DEBUG_DISABLE_BATCHING = 1 << 10 + COGL_DEBUG_DISABLE_BATCHING = 1 << 10, + COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES = 1 << 11 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/clutter/cogl/common/cogl-current-matrix.c b/clutter/cogl/common/cogl-current-matrix.c index 7c3bc57a3..7c2372134 100644 --- a/clutter/cogl/common/cogl-current-matrix.c +++ b/clutter/cogl/common/cogl-current-matrix.c @@ -320,7 +320,8 @@ _cogl_current_matrix_state_init (void) ctx->matrix_mode = COGL_MATRIX_MODELVIEW; ctx->modelview_stack = NULL; - if (ctx->indirect) + if (ctx->indirect || + cogl_debug_flags & COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES) { ctx->modelview_stack = _cogl_matrix_stack_new (); diff --git a/clutter/cogl/common/cogl-debug.c b/clutter/cogl/common/cogl-debug.c index 7eb8561c1..53236cdbe 100644 --- a/clutter/cogl/common/cogl-debug.c +++ b/clutter/cogl/common/cogl-debug.c @@ -41,7 +41,8 @@ static const GDebugKey cogl_debug_keys[] = { { "rectangles", COGL_DEBUG_RECTANGLES }, { "handle", COGL_DEBUG_HANDLE }, { "blend-strings", COGL_DEBUG_BLEND_STRINGS }, - { "disable-batching", COGL_DEBUG_DISABLE_BATCHING } + { "disable-batching", COGL_DEBUG_DISABLE_BATCHING }, + { "client-side-matrices", COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); From 54159f5a1d029dbfff19711f5397fc9b99556ed4 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 01:37:39 +0100 Subject: [PATCH 08/26] [cogl] Force Cogl to always use the client side matrix stack Previously we only used the Cogl matrix stack API for indirect contexts, but it's too costly to keep on requesting modelview matrices from GL (for logging in the journal) even for direct rendering. I also experimented with a patch for mesa to improve performance and discussed this with upstream, but we agreed to consider the GL matrix API essentially deprecated. (For reference the GLES 2 and GL 3 specs have removed the matrix APIs) --- clutter/cogl/common/cogl-current-matrix.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clutter/cogl/common/cogl-current-matrix.c b/clutter/cogl/common/cogl-current-matrix.c index 7c2372134..6f70be880 100644 --- a/clutter/cogl/common/cogl-current-matrix.c +++ b/clutter/cogl/common/cogl-current-matrix.c @@ -320,8 +320,10 @@ _cogl_current_matrix_state_init (void) ctx->matrix_mode = COGL_MATRIX_MODELVIEW; ctx->modelview_stack = NULL; +#if 0 if (ctx->indirect || cogl_debug_flags & COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES) +#endif { ctx->modelview_stack = _cogl_matrix_stack_new (); From 3ea7816499996c3bb918b27c6f44c987b90d2e9c Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 01:46:06 +0100 Subject: [PATCH 09/26] [cogl] Adds a debug option for disabling use of VBOs --cogl-debug=disable-vbos For testing the VBO fallback paths it helps to be able to disable the COGL_FEATURE_VBOS feature flag. When VBOs aren't available Cogl should use client side malloc()'d buffers instead. --- clutter/cogl/cogl-debug.h | 3 ++- clutter/cogl/common/cogl-debug.c | 3 ++- clutter/cogl/common/cogl.c | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/clutter/cogl/cogl-debug.h b/clutter/cogl/cogl-debug.h index c67f37568..44fd1c5de 100644 --- a/clutter/cogl/cogl-debug.h +++ b/clutter/cogl/cogl-debug.h @@ -40,7 +40,8 @@ typedef enum { COGL_DEBUG_HANDLE = 1 << 8, COGL_DEBUG_BLEND_STRINGS = 1 << 9, COGL_DEBUG_DISABLE_BATCHING = 1 << 10, - COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES = 1 << 11 + COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES = 1 << 11, + COGL_DEBUG_DISABLE_VBOS = 1 << 12 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/clutter/cogl/common/cogl-debug.c b/clutter/cogl/common/cogl-debug.c index 53236cdbe..70f21d28b 100644 --- a/clutter/cogl/common/cogl-debug.c +++ b/clutter/cogl/common/cogl-debug.c @@ -42,7 +42,8 @@ static const GDebugKey cogl_debug_keys[] = { { "handle", COGL_DEBUG_HANDLE }, { "blend-strings", COGL_DEBUG_BLEND_STRINGS }, { "disable-batching", COGL_DEBUG_DISABLE_BATCHING }, - { "client-side-matrices", COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES } + { "client-side-matrices", COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES }, + { "disable-vbos", COGL_DEBUG_DISABLE_VBOS } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/clutter/cogl/common/cogl.c b/clutter/cogl/common/cogl.c index 944ff8d68..29366b335 100644 --- a/clutter/cogl/common/cogl.c +++ b/clutter/cogl/common/cogl.c @@ -574,6 +574,9 @@ cogl_get_features (void) if (!ctx->features_cached) _cogl_features_init (); + if (cogl_debug_flags & COGL_DEBUG_DISABLE_VBOS) + ctx->feature_flags &= ~COGL_FEATURE_VBOS; + return ctx->feature_flags; } From d03e6cfb2c1ea47fdcf8db7d471666bd380c93aa Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 01:59:28 +0100 Subject: [PATCH 10/26] [Cogl journal] Adds a --cogl-debug=journal option for tracing the journal When this option is used Cogl will print a trace of all quads that get logged into the journal, and a trace of quads as they get flushed. If you are seeing a bug with the geometry being drawn by Cogl this may give some clues by letting you sanity check the numbers being logged vs the numbers being emitted. --- clutter/cogl/cogl-debug.h | 3 ++- clutter/cogl/common/cogl-debug.c | 3 ++- clutter/cogl/common/cogl-primitives.c | 36 +++++++++++++++++---------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/clutter/cogl/cogl-debug.h b/clutter/cogl/cogl-debug.h index 44fd1c5de..1785114b1 100644 --- a/clutter/cogl/cogl-debug.h +++ b/clutter/cogl/cogl-debug.h @@ -41,7 +41,8 @@ typedef enum { COGL_DEBUG_BLEND_STRINGS = 1 << 9, COGL_DEBUG_DISABLE_BATCHING = 1 << 10, COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES = 1 << 11, - COGL_DEBUG_DISABLE_VBOS = 1 << 12 + COGL_DEBUG_DISABLE_VBOS = 1 << 12, + COGL_DEBUG_JOURNAL = 1 << 13 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/clutter/cogl/common/cogl-debug.c b/clutter/cogl/common/cogl-debug.c index 70f21d28b..82d5864a8 100644 --- a/clutter/cogl/common/cogl-debug.c +++ b/clutter/cogl/common/cogl-debug.c @@ -43,7 +43,8 @@ static const GDebugKey cogl_debug_keys[] = { { "blend-strings", COGL_DEBUG_BLEND_STRINGS }, { "disable-batching", COGL_DEBUG_DISABLE_BATCHING }, { "client-side-matrices", COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES }, - { "disable-vbos", COGL_DEBUG_DISABLE_VBOS } + { "disable-vbos", COGL_DEBUG_DISABLE_VBOS }, + { "journal", COGL_DEBUG_JOURNAL } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index 87b58624e..ed3561672 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -421,14 +421,19 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, * VBO use a vertex offset passed to glDraw{Arrays,Elements} */ state->vertex_offset = 0; - /* XXX: Uncomment for debugging */ -#if 0 - g_assert (cogl_get_features () & COGL_FEATURE_VBOS); - _cogl_journal_dump_quad_batch (((guint8 *)ctx->logged_vertices->data) + - (size_t)state->vbo_offset, - batch_start->n_layers, - batch_len); -#endif + if (cogl_debug_flags & COGL_DEBUG_JOURNAL) + { + guint8 *verts; + + if (cogl_get_features () & COGL_FEATURE_VBOS) + verts = ((guint8 *)ctx->logged_vertices->data) + + (size_t)state->vbo_offset; + else + verts = (guint8 *)state->vbo_offset; + _cogl_journal_dump_quad_batch (verts, + batch_start->n_layers, + batch_len); + } batch_and_call (batch_start, batch_len, @@ -442,6 +447,8 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, /* progress forward through the VBO containing all our vertices */ state->vbo_offset += (stride * 4 * batch_len); + if (cogl_debug_flags & COGL_DEBUG_JOURNAL) + g_print ("new vbo offset = %lu\n", (gulong)state->vbo_offset); } static gboolean @@ -450,6 +457,8 @@ compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1) /* Currently the only thing that affects the stride for our vertex arrays * is the number of material layers. We need to update our VBO offsets * whenever the stride changes. */ + /* TODO: We should be padding the n_layers == 1 case as if it were + * n_layers == 2 so we can reduce the need to split batches. */ if (entry0->n_layers == entry1->n_layers || (entry0->n_layers <= MIN_LAYER_PADING && entry1->n_layers <= MIN_LAYER_PADING)) @@ -637,11 +646,12 @@ _cogl_journal_log_quad (float x_1, t[0] = tex_coords[2]; t[1] = tex_coords[1]; } - /* XXX: Uncomment for debugging */ -#if 0 - v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); - _cogl_journal_dump_quad_vertices ((guint8 *)v, n_layers); -#endif + if (cogl_debug_flags & COGL_DEBUG_JOURNAL) + { + g_print ("Logged new quad:\n"); + v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); + _cogl_journal_dump_quad_vertices ((guint8 *)v, n_layers); + } next_entry = ctx->journal->len; g_array_set_size (ctx->journal, next_entry + 1); From 7d1876fd261f5039e25d468e0cc7dcbf63229912 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 11 Jun 2009 11:54:01 +0100 Subject: [PATCH 11/26] [cogl journal] Adds a --cogl-debug=batching option to trace batching Enabling this option makes Cogl trace how the journal is managing to batch your rectangles. The journal staggers how it emmits state to the GL driver and the batches will normally get smaller for each stage, but ideally you don't want to be in a situation where Cogl is only able to draw one quad per modelview change and draw call. E.g. this is a fairly ideal example: BATCHING: journal len = 101 BATCHING: vbo offset batch len = 101 BATCHING: material batch len = 101 BATCHING: modelview batch len = 101 This isn't: BATCHING: journal len = 1 BATCHING: vbo offset batch len = 1 BATCHING: material batch len = 1 BATCHING: modelview batch len = 1 BATCHING: journal len = 1 BATCHING: vbo offset batch len = 1 BATCHING: material batch len = 1 BATCHING: modelview batch len = 1 --- clutter/cogl/cogl-debug.h | 5 +++-- clutter/cogl/common/cogl-debug.c | 3 ++- clutter/cogl/common/cogl-primitives.c | 17 ++++++++++++----- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/clutter/cogl/cogl-debug.h b/clutter/cogl/cogl-debug.h index 1785114b1..c8c1f566e 100644 --- a/clutter/cogl/cogl-debug.h +++ b/clutter/cogl/cogl-debug.h @@ -41,8 +41,9 @@ typedef enum { COGL_DEBUG_BLEND_STRINGS = 1 << 9, COGL_DEBUG_DISABLE_BATCHING = 1 << 10, COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES = 1 << 11, - COGL_DEBUG_DISABLE_VBOS = 1 << 12, - COGL_DEBUG_JOURNAL = 1 << 13 + COGL_DEBUG_DISABLE_VBOS = 1 << 12, + COGL_DEBUG_JOURNAL = 1 << 13, + COGL_DEBUG_BATCHING = 1 << 14 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/clutter/cogl/common/cogl-debug.c b/clutter/cogl/common/cogl-debug.c index 82d5864a8..e093a344c 100644 --- a/clutter/cogl/common/cogl-debug.c +++ b/clutter/cogl/common/cogl-debug.c @@ -44,7 +44,8 @@ static const GDebugKey cogl_debug_keys[] = { { "disable-batching", COGL_DEBUG_DISABLE_BATCHING }, { "client-side-matrices", COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES }, { "disable-vbos", COGL_DEBUG_DISABLE_VBOS }, - { "journal", COGL_DEBUG_JOURNAL } + { "journal", COGL_DEBUG_JOURNAL }, + { "batching", COGL_DEBUG_BATCHING } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index ed3561672..3b9f7f5d4 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -178,6 +178,9 @@ _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, { CoglJournalFlushState *state = data; + if (cogl_debug_flags & COGL_DEBUG_BATCHING) + g_print ("BATCHING: modelview batch len = %d\n", batch_len); + GE (glLoadMatrixf ((GLfloat *)&batch_start->model_view)); #ifdef HAVE_COGL_GL @@ -263,10 +266,8 @@ _cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start, _COGL_GET_CONTEXT (ctx, NO_RETVAL); -#if 0 - if (batch_len != 1) - g_debug ("batch len = %d", batch_len); -#endif + if (cogl_debug_flags & COGL_DEBUG_BATCHING) + g_print ("BATCHING: material batch len = %d\n", batch_len); _cogl_material_flush_gl_state (batch_start->material, &batch_start->flush_options); @@ -382,6 +383,9 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, _COGL_GET_CONTEXT (ctx, NO_RETVAL); + if (cogl_debug_flags & COGL_DEBUG_BATCHING) + g_print ("BATCHING: vbo offset batch len = %d\n", batch_len); + /* XXX NB: * Our journal's vertex data is arranged as follows: * 4 vertices per quad: @@ -418,7 +422,7 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, /* We only call gl{Vertex,Color,Texture}Pointer when the stride within * the VBO changes. (due to a change in the number of material layers) * While the stride remains constant we walk forward through the above - * VBO use a vertex offset passed to glDraw{Arrays,Elements} */ + * VBO using a vertex offset passed to glDraw{Arrays,Elements} */ state->vertex_offset = 0; if (cogl_debug_flags & COGL_DEBUG_JOURNAL) @@ -522,6 +526,9 @@ _cogl_journal_flush (void) if (ctx->journal->len == 0) return; + if (cogl_debug_flags & COGL_DEBUG_BATCHING && ctx->journal->len != 1) + g_print ("BATCHING: journal len = %d\n", ctx->journal->len); + /* Load all the vertex data we have accumulated so far into a single VBO * to minimize memory management costs within the GL driver. */ if (!vbo_fallback) From 612a1e2dea5599a30e3c922dee288d72e890c415 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 01:30:24 +0100 Subject: [PATCH 12/26] [Cogl journal] use G_UNLIKLEY around runtime debugging conditions May as well improve the branch prediction around runtime debugging code. --- clutter/cogl/common/cogl-primitives.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index 3b9f7f5d4..9dec92132 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -178,7 +178,7 @@ _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, { CoglJournalFlushState *state = data; - if (cogl_debug_flags & COGL_DEBUG_BATCHING) + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING)) g_print ("BATCHING: modelview batch len = %d\n", batch_len); GE (glLoadMatrixf ((GLfloat *)&batch_start->model_view)); @@ -266,7 +266,7 @@ _cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start, _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (cogl_debug_flags & COGL_DEBUG_BATCHING) + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING)) g_print ("BATCHING: material batch len = %d\n", batch_len); _cogl_material_flush_gl_state (batch_start->material, @@ -383,7 +383,7 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (cogl_debug_flags & COGL_DEBUG_BATCHING) + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING)) g_print ("BATCHING: vbo offset batch len = %d\n", batch_len); /* XXX NB: @@ -451,7 +451,7 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, /* progress forward through the VBO containing all our vertices */ state->vbo_offset += (stride * 4 * batch_len); - if (cogl_debug_flags & COGL_DEBUG_JOURNAL) + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL)) g_print ("new vbo offset = %lu\n", (gulong)state->vbo_offset); } @@ -526,7 +526,7 @@ _cogl_journal_flush (void) if (ctx->journal->len == 0) return; - if (cogl_debug_flags & COGL_DEBUG_BATCHING && ctx->journal->len != 1) + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING)) g_print ("BATCHING: journal len = %d\n", ctx->journal->len); /* Load all the vertex data we have accumulated so far into a single VBO @@ -653,7 +653,7 @@ _cogl_journal_log_quad (float x_1, t[0] = tex_coords[2]; t[1] = tex_coords[1]; } - if (cogl_debug_flags & COGL_DEBUG_JOURNAL) + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL)) { g_print ("Logged new quad:\n"); v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); @@ -679,8 +679,8 @@ _cogl_journal_log_quad (float x_1, entry->flush_options.layer0_override_texture = layer0_override_texture; cogl_get_modelview_matrix (&entry->model_view); - if (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING - || cogl_debug_flags & COGL_DEBUG_RECTANGLES) + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING + || cogl_debug_flags & COGL_DEBUG_RECTANGLES)) _cogl_journal_flush (); } From efbf483d8cdd448b20ddcfedc518509f907b2ac6 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 01:31:36 +0100 Subject: [PATCH 13/26] [cogl journal] Perform software modelview transform on logged quads. Since most Clutter actors aren't much more than textured quads; flushing the journal typically involves lots of 'change modelview; draw quad' sequences. The amount of overhead involved in uploading a new modelview and queuing that primitive is huge in comparison to simply transforming 4 vertices by the current modelview when logging quads. (Note if your GPU supports HW vertex transform, then it still does the projective and viewport transforms) At the same time a --cogl-debug=disable-software-transform option has been added for comparison and debugging. This change allows typical pick scenes to be batched into a single draw call and I'm seeing test-pick run over 200% faster with this. (i965 + Mesa 7.6-devel) --- clutter/cogl/cogl-debug.h | 3 +- clutter/cogl/common/cogl-debug.c | 3 +- clutter/cogl/common/cogl-primitives.c | 158 +++++++++++++++++++------- 3 files changed, 120 insertions(+), 44 deletions(-) diff --git a/clutter/cogl/cogl-debug.h b/clutter/cogl/cogl-debug.h index c8c1f566e..0c602909a 100644 --- a/clutter/cogl/cogl-debug.h +++ b/clutter/cogl/cogl-debug.h @@ -43,7 +43,8 @@ typedef enum { COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES = 1 << 11, COGL_DEBUG_DISABLE_VBOS = 1 << 12, COGL_DEBUG_JOURNAL = 1 << 13, - COGL_DEBUG_BATCHING = 1 << 14 + COGL_DEBUG_BATCHING = 1 << 14, + COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 15 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/clutter/cogl/common/cogl-debug.c b/clutter/cogl/common/cogl-debug.c index e093a344c..28c475295 100644 --- a/clutter/cogl/common/cogl-debug.c +++ b/clutter/cogl/common/cogl-debug.c @@ -45,7 +45,8 @@ static const GDebugKey cogl_debug_keys[] = { { "client-side-matrices", COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES }, { "disable-vbos", COGL_DEBUG_DISABLE_VBOS }, { "journal", COGL_DEBUG_JOURNAL }, - { "batching", COGL_DEBUG_BATCHING } + { "batching", COGL_DEBUG_BATCHING }, + { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index 9dec92132..2bf358778 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -57,7 +57,7 @@ /* XXX NB: * Our journal's vertex data is arranged as follows: * 4 vertices per quad: - * 2 or GLfloats per position + * 2 or 3 GLfloats per position (3 when doing software transforms) * 4 RGBA GLubytes, * 2 GLfloats per tex coord * n_layers * @@ -66,11 +66,21 @@ * To avoid frequent changes in the stride of our vertex data we always pad * n_layers to be >= 2 * + * When we are transforming quads in software we need to also track the z + * coordinate of transformed vertices. + * * So for a given number of layers this gets the stride in 32bit words: */ -#define MIN_LAYER_PADING 2 +#define SW_TRANSFORM (!(cogl_debug_flags & \ + COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)) +#define POS_STRIDE (SW_TRANSFORM ? 3 : 2) /* number of 32bit words */ +#define N_POS_COMPONENTS POS_STRIDE +#define COLOR_STRIDE 1 /* number of 32bit words */ +#define TEX_STRIDE 2 /* number of 32bit words */ +#define MIN_LAYER_PADING 2 #define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \ - (2 + 1 + 2 * (N_LAYERS < MIN_LAYER_PADING ? MIN_LAYER_PADING : N_LAYERS)) + (POS_STRIDE + COLOR_STRIDE + \ + TEX_STRIDE * (N_LAYERS < MIN_LAYER_PADING ? MIN_LAYER_PADING : N_LAYERS)) typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start, @@ -85,8 +95,8 @@ typedef struct _CoglJournalFlushState /* Note: this is a pointer to handle fallbacks. It normally holds a VBO * offset, but when the driver doesn't support VBOs then this points into * our GArray of logged vertices. */ - char * vbo_offset; - GLuint vertex_offset; + char * vbo_offset; + GLuint vertex_offset; #ifndef HAVE_COGL_GL CoglJournalIndices *indices; size_t indices_type_size; @@ -108,19 +118,26 @@ _cogl_journal_dump_quad_vertices (guint8 *data, int n_layers) _COGL_GET_CONTEXT (ctx, NO_RETVAL); - g_print ("stride = %d (%d bytes)\n", (int)stride, (int)stride * 4); + g_print ("n_layers = %d; stride = %d; pos stride = %d; color stride = %d; " + "tex stride = %d; stride in bytes = %d\n", + n_layers, (int)stride, POS_STRIDE, COLOR_STRIDE, + TEX_STRIDE, (int)stride * 4); for (i = 0; i < 4; i++) { float *v = (float *)data + (i * stride); - guint8 *c = data + 8 + (i * stride * 4); + guint8 *c = data + (POS_STRIDE * 4) + (i * stride * 4); int j; - g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X", - i, v[0], v[1], c[0], c[1], c[2], c[3]); + if (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM) + g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X", + i, v[0], v[1], c[0], c[1], c[2], c[3]); + else + g_print ("v%d: x = %f, y = %f, z = %f, rgba=0x%02X%02X%02X%02X", + i, v[0], v[1], v[2], c[0], c[1], c[2], c[3]); for (j = 0; j < n_layers; j++) { - float *t = v + 3 + 2 * j; + float *t = v + POS_STRIDE + COLOR_STRIDE + TEX_STRIDE * j; g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]); } g_print ("\n"); @@ -181,7 +198,8 @@ _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING)) g_print ("BATCHING: modelview batch len = %d\n", batch_len); - GE (glLoadMatrixf ((GLfloat *)&batch_start->model_view)); + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)) + GE (glLoadMatrixf ((GLfloat *)&batch_start->model_view)); #ifdef HAVE_COGL_GL @@ -248,6 +266,7 @@ compare_entry_modelviews (CoglJournalEntry *entry0, * if the age has increased, and if so record the change as a * boolean in the journal. */ + if (memcmp (&entry0->model_view, &entry1->model_view, sizeof (GLfloat) * 16) == 0) return TRUE; @@ -283,12 +302,18 @@ _cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start, enable_flags |= COGL_ENABLE_COLOR_ARRAY; cogl_enable (enable_flags); - batch_and_call (batch_start, - batch_len, - compare_entry_modelviews, - _cogl_journal_flush_modelview_and_entries, - data); - + /* If we haven't transformed the quads in software then we need to also break + * up batches according to changes in the modelview matrix... */ + if (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM) + { + batch_and_call (batch_start, + batch_len, + compare_entry_modelviews, + _cogl_journal_flush_modelview_and_entries, + data); + } + else + _cogl_journal_flush_modelview_and_entries (batch_start, batch_len, data); } static gboolean @@ -332,14 +357,16 @@ _cogl_journal_flush_texcoord_vbo_offsets_and_entries ( /* XXX NB: * Our journal's vertex data is arranged as follows: * 4 vertices per quad: - * 2 GLfloats per position + * 2 or 3 GLfloats per position (3 when doing software transforms) * 4 RGBA GLubytes, * 2 GLfloats per tex coord * n_layers * (though n_layers may be padded; see definition of * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details) */ GE (glTexCoordPointer (2, GL_FLOAT, state->stride, - (void *)(state->vbo_offset + 12 + 8 * i))); + (void *)(state->vbo_offset + + (POS_STRIDE + COLOR_STRIDE) * 4 + + TEX_STRIDE * 4 * i))); } prev_n_texcoord_arrays_enabled = ctx->n_texcoord_arrays_enabled; @@ -389,7 +416,7 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, /* XXX NB: * Our journal's vertex data is arranged as follows: * 4 vertices per quad: - * 2 GLfloats per position + * 2 or 3 GLfloats per position (3 when doing software transforms) * 4 RGBA GLubytes, * 2 GLfloats per tex coord * n_layers * (though n_layers may be padded; see definition of @@ -399,9 +426,10 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, stride *= sizeof (GLfloat); state->stride = stride; - GE (glVertexPointer (2, GL_FLOAT, stride, (void *)state->vbo_offset)); + GE (glVertexPointer (N_POS_COMPONENTS, GL_FLOAT, stride, + (void *)state->vbo_offset)); GE (glColorPointer (4, GL_UNSIGNED_BYTE, stride, - (void *)(state->vbo_offset + 8))); + (void *)(state->vbo_offset + (POS_STRIDE * 4)))); #ifndef HAVE_COGL_GL indices_handle = cogl_vertex_buffer_indices_get_for_quads (needed_indices); @@ -540,19 +568,36 @@ _cogl_journal_flush (void) * we need to dirty our client side matrix stack cache... */ _cogl_current_matrix_state_dirty (); + /* If we have transformed all our quads at log time then the whole journal + * then we ensure no further model transform is applied by loading the + * identity matrix here...*/ + if (!(cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)) + { + GE (glMatrixMode (GL_MODELVIEW)); + glLoadIdentity (); + } + /* batch_and_call() batches a list of journal entries according to some * given criteria and calls a callback once for each determined batch. * - * The process of flushing the journal is done by splitting the entries - * by three broad criteria: - * 1) We split the entries according the number of material layers. - * Each time the number of material layers changes, then the stride - * changes, so we need to call gl{Vertex,Color,Texture}Pointer to - * inform GL of new VO offsets. - * 2) We then split according to compatible Cogl materials. + * The process of flushing the journal is staggered to reduce the amount + * of driver/GPU state changes necessary: + * 1) We split the entries according to the stride of the vertices: + * Each time the stride of our vertex data changes we need to call + * gl{Vertex,Color}Pointer to inform GL of new VBO offsets. + * Currently the only thing that affects the stride of our vertex data + * is the number of material layers. + * 2) We split the entries explicitly by the number of material layers: + * We pad our vertex data when the number of layers is < 2 so that we + * can minimize changes in stride. Each time the number of layers + * changes we need to call glTexCoordPointer to inform GL of new VBO + * offsets. + * 3) We then split according to compatible Cogl materials: * This is where we flush material state - * 3) Finally we split according to modelview matrix changes. + * 4) Finally we split according to modelview matrix changes: * This is when we finally tell GL to draw something. + * Note: Splitting by modelview changes is skipped when are doing the + * vertex transformation in software at log time. */ batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */ ctx->journal->len, /* max number of entries to consider */ @@ -612,7 +657,7 @@ _cogl_journal_log_quad (float x_1, next_vert = ctx->logged_vertices->len; g_array_set_size (ctx->logged_vertices, next_vert + 4 * stride); v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); - c = (GLubyte *)(v + 2); + c = (GLubyte *)(v + POS_STRIDE); /* XXX: All the jumping around to fill in this strided buffer doesn't * seem ideal. */ @@ -630,19 +675,47 @@ _cogl_journal_log_quad (float x_1, memcpy (c, src_c, 4); } - v[0] = x_1; v[1] = y_1; - v += stride; - v[0] = x_1; v[1] = y_2; - v += stride; - v[0] = x_2; v[1] = y_2; - v += stride; - v[0] = x_2; v[1] = y_1; + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)) + { + v[0] = x_1; v[1] = y_1; + v += stride; + v[0] = x_1; v[1] = y_2; + v += stride; + v[0] = x_2; v[1] = y_2; + v += stride; + v[0] = x_2; v[1] = y_1; + } + else + { + CoglMatrix mv; + float x, y, z, w; + + cogl_get_modelview_matrix (&mv); + + x = x_1, y = y_1, z = 0; w = 1; + cogl_matrix_transform_point (&mv, &x, &y, &z, &w); + v[0] = x; v[1] = y; v[2] = z; + v += stride; + x = x_1, y = y_2, z = 0; w = 1; + cogl_matrix_transform_point (&mv, &x, &y, &z, &w); + v[0] = x; v[1] = y; v[2] = z; + v += stride; + x = x_2, y = y_2, z = 0; w = 1; + cogl_matrix_transform_point (&mv, &x, &y, &z, &w); + v[0] = x; v[1] = y; v[2] = z; + v += stride; + x = x_2, y = y_1, z = 0; w = 1; + cogl_matrix_transform_point (&mv, &x, &y, &z, &w); + v[0] = x; v[1] = y; v[2] = z; + } for (i = 0; i < n_layers; i++) { - /* NB: See note at top about vertex buffer layout: */ - GLfloat *t = &g_array_index (ctx->logged_vertices, - GLfloat, next_vert + 3 + 2 * i); + /* XXX: See definition of GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details + * about how we pack our vertex data */ + GLfloat *t = &g_array_index (ctx->logged_vertices, GLfloat, + next_vert + POS_STRIDE + + COLOR_STRIDE + TEX_STRIDE * i); t[0] = tex_coords[0]; t[1] = tex_coords[1]; t += stride; @@ -677,7 +750,8 @@ _cogl_journal_log_quad (float x_1, entry->flush_options.fallback_layers = fallback_layers; entry->flush_options.disable_layers = disable_layers; entry->flush_options.layer0_override_texture = layer0_override_texture; - cogl_get_modelview_matrix (&entry->model_view); + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)) + cogl_get_modelview_matrix (&entry->model_view); if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING || cogl_debug_flags & COGL_DEBUG_RECTANGLES)) From 9afa52f056b58bbd35274fec3fe21ff483949321 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 23:31:11 +0100 Subject: [PATCH 14/26] [material] avoid flushing the journal when just changing the color Whenever we modify a material we call _cogl_material_pre_change_notify which checks to see if the material is referenced by the journal and if so flushes if before we modify the material. Since the journal logs material colors directly into a vertex array (to avoid us repeatedly calling glColor) then we know we never need to flush the journal when material colors change. --- clutter/cogl/common/cogl-material.c | 80 +++++++++++++++++++---------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/clutter/cogl/common/cogl-material.c b/clutter/cogl/common/cogl-material.c index bc56b8aa6..71c02b6c7 100644 --- a/clutter/cogl/common/cogl-material.c +++ b/clutter/cogl/common/cogl-material.c @@ -131,22 +131,24 @@ _cogl_material_free (CoglMaterial *material) g_free (material); } - -static void -handle_automatic_blend_enable (CoglMaterial *material) +static gboolean +_cogl_material_needs_blending_enabled (CoglMaterial *material, + GLubyte *override_color) { GList *tmp; /* XXX: If we expose manual control over ENABLE_BLEND, we'll add * a flag to know when it's user configured, so we don't trash it */ - material->flags &= ~COGL_MATERIAL_FLAG_ENABLE_BLEND; - /* XXX: Uncomment this to disable all blending */ #if 0 return; #endif + if ((override_color && override_color[3] != 0xff) || + material->unlit[3] != 0xff) + return TRUE; + for (tmp = material->layers; tmp != NULL; tmp = tmp->next) { CoglMaterialLayer *layer = tmp->data; @@ -157,10 +159,18 @@ handle_automatic_blend_enable (CoglMaterial *material) continue; if (cogl_texture_get_format (layer->texture) & COGL_A_BIT) - material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND; + return TRUE; } - if (material->unlit[3] != 0xff) + return FALSE; +} + +static void +handle_automatic_blend_enable (CoglMaterial *material) +{ + material->flags &= ~COGL_MATERIAL_FLAG_ENABLE_BLEND; + + if (_cogl_material_needs_blending_enabled (material, NULL)) material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND; } @@ -168,8 +178,23 @@ handle_automatic_blend_enable (CoglMaterial *material) * state of this material we need to flush the journal before we can * modify it... */ static void -_cogl_material_pre_change_notify (CoglMaterial *material) +_cogl_material_pre_change_notify (CoglMaterial *material, + gboolean only_color_change, + GLubyte *new_color) { + /* XXX: We don't usually need to flush the journal just due to color changes + * since material colors are logged in the journals vertex buffer. The + * exception is when the change in color enables or disables the need for + * blending. */ + if (only_color_change) + { + gboolean will_need_blending = + _cogl_material_needs_blending_enabled (material, new_color); + if (will_need_blending == + (material->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) ? TRUE : FALSE) + return; + } + if (material->journal_ref_count) _cogl_journal_flush (); } @@ -219,7 +244,7 @@ cogl_material_set_color (CoglHandle handle, return; /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, TRUE, unlit); memcpy (material->unlit, unlit, sizeof (unlit)); @@ -286,7 +311,7 @@ cogl_material_set_ambient (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); ambient = material->ambient; ambient[0] = cogl_color_get_red_float (ambient_color); @@ -326,7 +351,7 @@ cogl_material_set_diffuse (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); diffuse = material->diffuse; diffuse[0] = cogl_color_get_red_float (diffuse_color); @@ -374,7 +399,7 @@ cogl_material_set_specular (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); specular = material->specular; specular[0] = cogl_color_get_red_float (specular_color); @@ -412,7 +437,7 @@ cogl_material_set_shininess (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); material->shininess = (GLfloat)shininess * 128.0; @@ -448,7 +473,7 @@ cogl_material_set_emission (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); emission = material->emission; emission[0] = cogl_color_get_red_float (emission_color); @@ -471,7 +496,7 @@ cogl_material_set_alpha_test_function (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); material->alpha_func = alpha_func; material->alpha_func_reference = (GLfloat)alpha_reference; @@ -621,7 +646,7 @@ cogl_material_set_blend (CoglHandle handle, } /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); #ifndef HAVE_COGL_GLES setup_blend_state (rgb, @@ -657,7 +682,7 @@ cogl_material_set_blend_constant (CoglHandle handle, material = _cogl_material_pointer_from_handle (handle); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); constant = material->blend_constant; constant[0] = cogl_color_get_red_float (constant_color); @@ -699,6 +724,9 @@ _cogl_material_get_layer (CoglMaterial *material, if (!create_if_not_found) return NULL; + /* possibly flush primitives referencing the current state... */ + _cogl_material_pre_change_notify (material, FALSE, NULL); + layer = g_new0 (CoglMaterialLayer, 1); layer_handle = _cogl_material_layer_handle_new (layer); @@ -744,16 +772,13 @@ cogl_material_set_layer (CoglHandle material_handle, material = _cogl_material_pointer_from_handle (material_handle); - /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); - layer = _cogl_material_get_layer (material_handle, layer_index, TRUE); if (texture_handle == layer->texture) return; /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); material->n_layers = g_list_length (material->layers); if (material->n_layers >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS) @@ -918,7 +943,7 @@ cogl_material_set_layer_combine (CoglHandle handle, } /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); setup_texture_combine_state (rgb, &layer->texture_combine_rgb_func, @@ -951,7 +976,7 @@ cogl_material_set_layer_combine_constant (CoglHandle handle, layer = _cogl_material_get_layer (material, layer_index, TRUE); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); constant = layer->texture_combine_constant; constant[0] = cogl_color_get_red_float (constant_color); @@ -977,7 +1002,7 @@ cogl_material_set_layer_matrix (CoglHandle material_handle, layer = _cogl_material_get_layer (material, layer_index, TRUE); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); layer->matrix = *matrix; @@ -1007,7 +1032,7 @@ cogl_material_remove_layer (CoglHandle material_handle, material = _cogl_material_pointer_from_handle (material_handle); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); + _cogl_material_pre_change_notify (material, FALSE, NULL); for (tmp = material->layers; tmp != NULL; tmp = tmp->next) { @@ -1811,11 +1836,10 @@ cogl_material_set_layer_filters (CoglHandle handle, g_return_if_fail (cogl_is_material (handle)); material = _cogl_material_pointer_from_handle (handle); + layer = _cogl_material_get_layer (material, layer_index, TRUE); /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material); - - layer = _cogl_material_get_layer (material, layer_index, TRUE); + _cogl_material_pre_change_notify (material, FALSE, NULL); layer->min_filter = min_filter; layer->mag_filter = mag_filter; From 6ac3b5a564608862fdf4e9b8887b882ebe497f4b Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 23:31:40 +0100 Subject: [PATCH 15/26] [material] _cogl_material_equal: catch the simplest case of matching handles We were missing the simplest test of all: are the two CoglHandles equal and are the flush option flags for each material equal? This should improve batching for some common cases. --- clutter/cogl/common/cogl-material.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clutter/cogl/common/cogl-material.c b/clutter/cogl/common/cogl-material.c index 71c02b6c7..07a3236cd 100644 --- a/clutter/cogl/common/cogl-material.c +++ b/clutter/cogl/common/cogl-material.c @@ -1629,6 +1629,10 @@ _cogl_material_equal (CoglHandle material0_handle, CoglMaterial *material1; GList *l0, *l1; + if (material0_handle == material1_handle && + material0_flush_options->flags == material1_flush_options->flags) + return TRUE; + if (!(flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS)) { g_critical ("FIXME: _cogl_material_equal doesn't yet support " From 6562f3224a558b224b9076fe794547bfc9bb2af5 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 23:35:49 +0100 Subject: [PATCH 16/26] [pango-display-list] Use the Cogl journal for short runs of text For small runs of text like icon labels, we can get better performance going through the Cogl journal since text may then be batched together with other geometry. For larger runs of text though we still use VBOs since the cost of logging the quads becomes too expensive, including the software transform which isn't at all optimized at this point. VBOs also have the further advantage of avoiding repeated validation of vertices by the driver and repeated mapping of data into the GPU so long as the text doesn't change. Currently the threshold is 100 vertices/25 quads. This number was plucked out of thin air and should be tuned later. With this change I see ~180% fps improvment for test-text. (x61s + i965 + Mesa 7.6-devel) --- clutter/pango/cogl-pango-display-list.c | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/clutter/pango/cogl-pango-display-list.c b/clutter/pango/cogl-pango-display-list.c index 6790411bf..b353aa6fd 100644 --- a/clutter/pango/cogl-pango-display-list.c +++ b/clutter/pango/cogl-pango-display-list.c @@ -244,6 +244,39 @@ _cogl_pango_display_list_render_texture (CoglHandle material, cogl_material_set_color (material, &premult_color); cogl_set_source (material); + /* For small runs of text like icon labels, we can get better performance + * going through the Cogl journal since text may then be batched together + * with other geometry. */ + /* FIXME: 100 is a number I plucked out of thin air; it would be good + * to determine this empirically! */ + if (node->d.texture.verts->len < 100) + { + int i; + + for (i = 0; i < node->d.texture.verts->len; i += 4) + { + CoglPangoDisplayListVertex *v0 = + &g_array_index (node->d.texture.verts, + CoglPangoDisplayListVertex, i); + CoglPangoDisplayListVertex *v1 = + &g_array_index (node->d.texture.verts, + CoglPangoDisplayListVertex, i + 2); + cogl_rectangle_with_texture_coords (v0->x, v0->y, v1->x, v1->y, + v0->t_x, v0->t_y, + v1->t_x, v1->t_y); + } + + return; + } + + /* It's expensive to go through the Cogl journal for large runs + * of text in part because the journal transforms the quads in software + * to avoid changing the modelview matrix. So for larger runs of text + * we load the vertices into a VBO, and this has the added advantage + * that if the text doesn't change from frame to frame the VBO can + * be re-used avoiding the repeated cost of validating the data and + * mapping it into the GPU... */ + if (node->d.texture.vertex_buffer == COGL_INVALID_HANDLE) { CoglHandle vb = cogl_vertex_buffer_new (node->d.texture.verts->len); From 7b7787b050a1dd245dea396335ddf96662c6a570 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 22 Jun 2009 00:05:02 +0100 Subject: [PATCH 17/26] [cogl] avoid using the journal in _cogl_add_path_to_stencil_buffer Using cogl_rectangle (and thus the journal) in _cogl_add_path_to_stencil_buffer means we have to consider all the state that the journal may change in case it may interfer with the direct GL calls used. This has proven to be error prone and in this case the journal is an unnecissary overhead. We now simply call glRectf instead of using cogl_rectangle. --- clutter/cogl/gl/cogl-primitives.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/clutter/cogl/gl/cogl-primitives.c b/clutter/cogl/gl/cogl-primitives.c index 30b5553f7..35de5b684 100644 --- a/clutter/cogl/gl/cogl-primitives.c +++ b/clutter/cogl/gl/cogl-primitives.c @@ -169,12 +169,10 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( glDepthMask (FALSE) ); + _cogl_current_matrix_state_flush (); + while (path_start < path_size) { - /* NB: after calling _cogl_journal_flush the current matrix - * state is undefined */ - _cogl_current_matrix_state_flush (); - GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), (guchar *) path + G_STRUCT_OFFSET (CoglPathNode, x)) ); @@ -186,9 +184,8 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, significant bit */ GE( glStencilMask (merge ? 6 : 3) ); GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) ); - cogl_rectangle (bounds_x, bounds_y, - bounds_x + bounds_w, bounds_y + bounds_h); - _cogl_journal_flush (); + glRectf (bounds_x, bounds_y, + bounds_x + bounds_w, bounds_y + bounds_h); GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); } @@ -221,9 +218,8 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, _cogl_current_matrix_push (); _cogl_current_matrix_identity (); - cogl_rectangle (-1.0, -1.0, 1.0, 1.0); - cogl_rectangle (-1.0, -1.0, 1.0, 1.0); - _cogl_journal_flush (); + glRectf (-1.0, -1.0, 1.0, 1.0); + glRectf (-1.0, -1.0, 1.0, 1.0); _cogl_current_matrix_pop (); From f386b1f002f111210e87e6f72eb1795ad2befc1e Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 22 Jun 2009 00:11:41 +0100 Subject: [PATCH 18/26] [journal] Don't resize a singlton VBO; create and destroy a VBO each flush This simplifies the vertex data uploading in the journal, and could improve performance. Modifying a VBO mid-scene could reqire synchronizing with the GPU or some form of shadowing/copying to avoid modifying data that the GPU is currently processing; the buffer was also being marked as GL_STATIC_DRAW which could have made things worse. Now we simply create a GL_STATIC_DRAW VBO for each flush and and delete it when we are finished. --- clutter/cogl/common/cogl-primitives.c | 40 ++++++++------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index 2bf358778..dcef4547f 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -473,10 +473,6 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, _cogl_journal_flush_texcoord_vbo_offsets_and_entries, data); -#ifndef HAVE_COGL_GL - GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0)); -#endif - /* progress forward through the VBO containing all our vertices */ state->vbo_offset += (stride * 4 * batch_len); if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL)) @@ -507,30 +503,15 @@ upload_vertices_to_vbo (GArray *vertices, CoglJournalFlushState *state) _COGL_GET_CONTEXT (ctx, NO_RETVAL); needed_vbo_len = vertices->len * sizeof (GLfloat); - if (ctx->journal_vbo_len < needed_vbo_len) - { - GE (glDeleteBuffers (1, &ctx->journal_vbo)); - GE (glGenBuffers (1, &ctx->journal_vbo)); - GE (glBindBuffer (GL_ARRAY_BUFFER, ctx->journal_vbo)); - GE (glBufferData (GL_ARRAY_BUFFER, - needed_vbo_len, - vertices->data, - GL_STATIC_DRAW)); - ctx->journal_vbo_len = needed_vbo_len; - } - else - { - GE (glBindBuffer (GL_ARRAY_BUFFER, ctx->journal_vbo)); - GE (glBufferData (GL_ARRAY_BUFFER, - needed_vbo_len, - NULL, - GL_STATIC_DRAW)); - GE (glBufferSubData (GL_ARRAY_BUFFER, - 0, - needed_vbo_len, - vertices->data)); - } + g_assert (ctx->journal_vbo == 0); + g_assert (needed_vbo_len); + GE (glGenBuffers (1, &ctx->journal_vbo)); + GE (glBindBuffer (GL_ARRAY_BUFFER, ctx->journal_vbo)); + GE (glBufferData (GL_ARRAY_BUFFER, + needed_vbo_len, + vertices->data, + GL_STATIC_DRAW)); /* As we flush the journal entries in batches we walk forward through the * above VBO starting at offset 0... */ @@ -613,7 +594,10 @@ _cogl_journal_flush (void) } if (!vbo_fallback) - GE (glBindBuffer (GL_ARRAY_BUFFER, 0)); + { + GE (glDeleteBuffers (1, &ctx->journal_vbo)); + ctx->journal_vbo = 0; + } g_array_set_size (ctx->journal, 0); g_array_set_size (ctx->logged_vertices, 0); From ca0a0e9dac23219709a1c365a070ea9d4d517909 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 22 Jun 2009 00:49:20 +0100 Subject: [PATCH 19/26] [cogl] flush matrices in _cogl_add_path_to_stencil_buffer Before calling glRectf we need to ensure we flush the modelview and projection matrices. --- clutter/cogl/gl/cogl-primitives.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clutter/cogl/gl/cogl-primitives.c b/clutter/cogl/gl/cogl-primitives.c index 35de5b684..d4ab7de0c 100644 --- a/clutter/cogl/gl/cogl-primitives.c +++ b/clutter/cogl/gl/cogl-primitives.c @@ -218,6 +218,8 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, _cogl_current_matrix_push (); _cogl_current_matrix_identity (); + _cogl_current_matrix_state_flush (); + glRectf (-1.0, -1.0, 1.0, 1.0); glRectf (-1.0, -1.0, 1.0, 1.0); From 8873c6a11a4668e737e4dc75d1d2a2c14904482f Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 22 Jun 2009 01:29:39 +0100 Subject: [PATCH 20/26] [cogl] disable all client tex coord arrays in _cogl_add_path_to_stencil_buffer After flushing the journal an unknown number of client side texture arrays may be left enabled. Disable them all before using glDrawArrays. --- clutter/cogl/gl/cogl-primitives.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clutter/cogl/gl/cogl-primitives.c b/clutter/cogl/gl/cogl-primitives.c index d4ab7de0c..cb6671c08 100644 --- a/clutter/cogl/gl/cogl-primitives.c +++ b/clutter/cogl/gl/cogl-primitives.c @@ -133,6 +133,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, float bounds_h; gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; CoglHandle prev_source; + int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -169,6 +170,13 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( glDepthMask (FALSE) ); + for (i = 0; i < ctx->n_texcoord_arrays_enabled; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); + } + ctx->n_texcoord_arrays_enabled = 0; + _cogl_current_matrix_state_flush (); while (path_start < path_size) From 8b67916cc1e2aed7861214205fc0d68c0c6d67f1 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 24 Jun 2009 18:10:50 +0100 Subject: [PATCH 21/26] [cogl-vertex-buffer] Disable unused client tex coord arrays Before any cogl vertex buffer drawing we call enable_state_for_drawing_buffer which sets up the GL state, but we weren't disabling unsed client texture coord arrays. --- clutter/cogl/common/cogl-vertex-buffer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clutter/cogl/common/cogl-vertex-buffer.c b/clutter/cogl/common/cogl-vertex-buffer.c index da99aeea8..2892bfe71 100644 --- a/clutter/cogl/common/cogl-vertex-buffer.c +++ b/clutter/cogl/common/cogl-vertex-buffer.c @@ -1644,6 +1644,13 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) } } + for (i = max_texcoord_attrib_unit + 1; i < ctx->n_texcoord_arrays_enabled; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); + } + ctx->n_texcoord_arrays_enabled = max_texcoord_attrib_unit + 1; + options.flags = COGL_MATERIAL_FLUSH_FALLBACK_MASK | COGL_MATERIAL_FLUSH_DISABLE_MASK; From 5ffbe052480cb06e44f1a317ac729553ddd2dc96 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 24 Jun 2009 18:20:45 +0100 Subject: [PATCH 22/26] Revert "[actor] Avoid modifying materials mid-scene to improve journal batching" This reverts commit 85243da382025bd516937c76a61b8381f6e74689. Since the journal puts material colors in the vertex array accumulated for drawing we don't need to flush the journal simply due to color changes which means using cogl_set_source_color4ub is no longer a concern. --- clutter/clutter-actor.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 76c574a20..7f8b1e997 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -332,8 +332,6 @@ struct _ClutterActorPrivate PangoContext *pango_context; ClutterActor *opacity_parent; - - CoglHandle pick_material; }; enum @@ -1376,13 +1374,11 @@ clutter_actor_real_pick (ClutterActor *self, width = box.x2 - box.x1; height = box.y2 - box.y1; - cogl_material_set_color4ub (self->priv->pick_material, - color->red, - color->green, - color->blue, - color->alpha); + cogl_set_source_color4ub (color->red, + color->green, + color->blue, + color->alpha); - cogl_set_source (self->priv->pick_material); cogl_rectangle (0, 0, width, height); } } @@ -4258,8 +4254,6 @@ clutter_actor_init (ClutterActor *self) priv->opacity_parent = NULL; priv->enable_model_view_transform = TRUE; - priv->pick_material = cogl_material_new (); - memset (priv->clip, 0, sizeof (gfloat) * 4); } From 6ee8e1565475d75d343682e4d4eed2f68e0c26eb Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 24 Jun 2009 18:24:58 +0100 Subject: [PATCH 23/26] Revert "[rectangle] Avoid modifying materials mid scene" This reverts commit 8cf42ea8ac5c05f6b443c453f9c6c2a3cd75acfa. Since the journal puts material colors in the vertex array accumulated for drawing we don't need to flush the journal simply due to color changes which means using cogl_set_source_color4ub is no longer a concern. --- clutter/clutter-rectangle.c | 89 ++++++++++++++----------------------- 1 file changed, 34 insertions(+), 55 deletions(-) diff --git a/clutter/clutter-rectangle.c b/clutter/clutter-rectangle.c index 0eeb76648..fb975bed2 100644 --- a/clutter/clutter-rectangle.c +++ b/clutter/clutter-rectangle.c @@ -60,10 +60,8 @@ enum struct _ClutterRectanglePrivate { - ClutterColor primary_color; - CoglHandle primary_material; - ClutterColor border_color; - CoglHandle border_material; + ClutterColor color; + ClutterColor border_color; guint border_width; @@ -80,7 +78,6 @@ clutter_rectangle_paint (ClutterActor *self) ClutterRectanglePrivate *priv; ClutterGeometry geom; guint8 tmp_alpha; - guint8 opacity; rectangle = CLUTTER_RECTANGLE(self); priv = rectangle->priv; @@ -91,8 +88,6 @@ clutter_rectangle_paint (ClutterActor *self) : "unknown"); clutter_actor_get_allocation_geometry (self, &geom); - opacity = clutter_actor_get_paint_opacity (self); - /* parent paint call will have translated us into position so * paint from 0, 0 */ @@ -101,26 +96,15 @@ clutter_rectangle_paint (ClutterActor *self) /* compute the composited opacity of the actor taking into * account the opacity of the color set by the user */ - tmp_alpha = opacity * priv->border_color.alpha / 255; + tmp_alpha = clutter_actor_get_paint_opacity (self) + * priv->border_color.alpha + / 255; /* paint the border */ - - /* We are assuming this is a NOP when the color doesn't change. - * This is important since we should aim to never modify - * materials mid-scene. */ - cogl_material_set_color4ub (priv->border_material, - priv->border_color.red, - priv->border_color.green, - priv->border_color.blue, - tmp_alpha); - - cogl_set_source (priv->border_material); - - cogl_material_set_color4ub (priv->border_material, - priv->border_color.red, - priv->border_color.green, - priv->border_color.blue, - tmp_alpha); + cogl_set_source_color4ub (priv->border_color.red, + priv->border_color.green, + priv->border_color.blue, + tmp_alpha); /* this sucks, but it's the only way to make a border */ cogl_rectangle (priv->border_width, 0, @@ -140,16 +124,15 @@ clutter_rectangle_paint (ClutterActor *self) priv->border_width, geom.height - priv->border_width); - tmp_alpha = opacity * priv->primary_color.alpha / 255; + tmp_alpha = clutter_actor_get_paint_opacity (self) + * priv->color.alpha + / 255; /* now paint the rectangle */ - cogl_set_source (priv->primary_material); - - cogl_material_set_color4ub (priv->primary_material, - priv->primary_color.red, - priv->primary_color.green, - priv->primary_color.blue, - tmp_alpha); + cogl_set_source_color4ub (priv->color.red, + priv->color.green, + priv->color.blue, + tmp_alpha); cogl_rectangle (priv->border_width, priv->border_width, geom.width - priv->border_width, @@ -160,16 +143,14 @@ clutter_rectangle_paint (ClutterActor *self) /* compute the composited opacity of the actor taking into * account the opacity of the color set by the user */ - tmp_alpha = opacity * priv->primary_color.alpha / 255; + tmp_alpha = clutter_actor_get_paint_opacity (self) + * priv->color.alpha + / 255; - /* now paint the rectangle */ - cogl_set_source (priv->primary_material); - - cogl_material_set_color4ub (priv->primary_material, - priv->primary_color.red, - priv->primary_color.green, - priv->primary_color.blue, - tmp_alpha); + cogl_set_source_color4ub (priv->color.red, + priv->color.green, + priv->color.blue, + tmp_alpha); cogl_rectangle (0, 0, geom.width, geom.height); } @@ -216,7 +197,7 @@ clutter_rectangle_get_property (GObject *object, switch (prop_id) { case PROP_COLOR: - clutter_value_set_color (value, &priv->primary_color); + clutter_value_set_color (value, &priv->color); break; case PROP_BORDER_COLOR: clutter_value_set_color (value, &priv->border_color); @@ -327,10 +308,8 @@ clutter_rectangle_init (ClutterRectangle *self) self->priv = priv = CLUTTER_RECTANGLE_GET_PRIVATE (self); - priv->primary_color = default_color; + priv->color = default_color; priv->border_color = default_border_color; - priv->primary_material = cogl_material_new (); - priv->border_material = cogl_material_new (); priv->border_width = 0; @@ -385,10 +364,10 @@ clutter_rectangle_get_color (ClutterRectangle *rectangle, priv = rectangle->priv; - color->red = priv->primary_color.red; - color->green = priv->primary_color.green; - color->blue = priv->primary_color.blue; - color->alpha = priv->primary_color.alpha; + color->red = priv->color.red; + color->green = priv->color.green; + color->blue = priv->color.blue; + color->alpha = priv->color.alpha; } /** @@ -411,10 +390,10 @@ clutter_rectangle_set_color (ClutterRectangle *rectangle, priv = rectangle->priv; - priv->primary_color.red = color->red; - priv->primary_color.green = color->green; - priv->primary_color.blue = color->blue; - priv->primary_color.alpha = color->alpha; + priv->color.red = color->red; + priv->color.green = color->green; + priv->color.blue = color->blue; + priv->color.alpha = color->alpha; #if 0 /* FIXME - appears to be causing border to always get drawn */ @@ -546,7 +525,7 @@ clutter_rectangle_set_border_color (ClutterRectangle *rectangle, priv->border_color.blue = color->blue; priv->border_color.alpha = color->alpha; - if (clutter_color_equal (&priv->primary_color, &priv->border_color)) + if (clutter_color_equal (&priv->color, &priv->border_color)) priv->has_border = FALSE; else priv->has_border = TRUE; From 87f99e214a6f60c06cc34f844e02ecb0f63c953f Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 24 Jun 2009 18:34:06 +0100 Subject: [PATCH 24/26] [cogl-material] Be more carefull about flushing in cogl_material_remove_layer Previously we would call _cogl_material_pre_change_notify unconditionally, but now we wait until we really know we are removing a layer before notifying the change, which will require a journal flush. Since the convenience functions cogl_set_source_color4ub and cogl_set_source_texture share a single material, cogl_set_source_color4ub always calls cogl_material_remove_layer. Often this is a NOP though and shouldn't require a journal flush. This gets performance back to where it was before reverting the per-actor material commits. --- clutter/cogl/common/cogl-material.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/clutter/cogl/common/cogl-material.c b/clutter/cogl/common/cogl-material.c index 07a3236cd..453fd23cf 100644 --- a/clutter/cogl/common/cogl-material.c +++ b/clutter/cogl/common/cogl-material.c @@ -1026,20 +1026,26 @@ cogl_material_remove_layer (CoglHandle material_handle, CoglMaterial *material; CoglMaterialLayer *layer; GList *tmp; + gboolean notified_change = FALSE; g_return_if_fail (cogl_is_material (material_handle)); material = _cogl_material_pointer_from_handle (material_handle); - /* possibly flush primitives referencing the current state... */ - _cogl_material_pre_change_notify (material, FALSE, NULL); - for (tmp = material->layers; tmp != NULL; tmp = tmp->next) { layer = tmp->data; if (layer->index == layer_index) { CoglHandle handle = (CoglHandle) layer; + + /* possibly flush primitives referencing the current state... */ + if (!notified_change) + { + _cogl_material_pre_change_notify (material, FALSE, NULL); + notified_change = TRUE; + } + cogl_handle_unref (handle); material->layers = g_list_remove (material->layers, layer); material->n_layers--; From 6d9498da7cbe180ddc4b68ae3a48985631db46a0 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 29 Jun 2009 17:10:34 +0100 Subject: [PATCH 25/26] Adds a cogl_flush() to give developers breaking into raw GL a fighting chance This function should only need to be called in exceptional circumstances since Cogl can normally determine internally when a flush is necessary. As an optimization Cogl drawing functions may batch up primitives internally, so if you are trying to use raw GL outside of Cogl you stand a better chance of being successful if you ask Cogl to flush any batched geometry before making your state changes. cogl_flush() ensures that the underlying driver is issued all the commands necessary to draw the batched primitives. It provides no guarantees about when the driver will complete the rendering. This provides no guarantees about the GL state upon returning and to avoid confusing Cogl you should aim to restore any changes you make before resuming use of Cogl. If you are making state changes with the intention of affecting Cogl drawing primitives you are 100% on your own since you stand a good chance of conflicting with Cogl internals. For example clutter-gst which currently uses direct GL calls to bind ARBfp programs will very likely break when Cogl starts to use ARBfb programs internally for the material API, but for now it can use cogl_flush() to at least ensure that the ARBfp program isn't applied to additional primitives. This does not provide a robust generalized solution supporting safe use of raw GL, its use is very much discouraged. --- clutter/clutter-main.c | 2 +- clutter/cogl/cogl.h.in | 42 ++++++++++++++++--------- clutter/cogl/common/cogl.c | 4 +-- clutter/eglnative/clutter-backend-egl.c | 2 +- clutter/eglx/clutter-backend-egl.c | 2 +- clutter/fruity/clutter-backend-fruity.c | 2 +- clutter/glx/clutter-backend-glx.c | 2 +- clutter/osx/clutter-stage-osx.c | 2 +- clutter/sdl/clutter-backend-sdl.c | 2 +- clutter/win32/clutter-backend-win32.c | 2 +- doc/reference/cogl/cogl-sections.txt | 3 ++ 11 files changed, 41 insertions(+), 24 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index ae667925c..84602f1b2 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -410,7 +410,7 @@ _clutter_do_pick (ClutterStage *stage, glGetIntegerv(GL_VIEWPORT, viewport); /* Make sure Cogl flushes any batched geometry to the GPU driver */ - _cogl_flush (); + cogl_flush (); /* Read the color of the screen co-ords pixel */ glReadPixels (x, viewport[3] - y -1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); diff --git a/clutter/cogl/cogl.h.in b/clutter/cogl/cogl.h.in index cbd70f0c7..295cf0d4b 100644 --- a/clutter/cogl/cogl.h.in +++ b/clutter/cogl/cogl.h.in @@ -724,6 +724,34 @@ void cogl_read_pixels (int x, CoglPixelFormat format, guint8 *pixels); +/** + * cogl_flush: + * + * This function should only need to be called in exceptional circumstances. + * + * As an optimization Cogl drawing functions may batch up primitives + * internally, so if you are trying to use raw GL outside of Cogl you stand a + * better chance of being successful if you ask Cogl to flush any batched + * geometry before making your state changes. + * + * It only ensure that the underlying driver is issued all the commands + * necessary to draw the batched primitives. It provides no guarantees about + * when the driver will complete the rendering. + * + * This provides no guarantees about the GL state upon returning and to avoid + * confusing Cogl you should aim to restore any changes you make before + * resuming use of Cogl. + * + * If you are making state changes with the intention of affecting Cogl drawing + * primitives you are 100% on your own since you stand a good chance of + * conflicting with Cogl internals. For example clutter-gst which currently + * uses direct GL calls to bind ARBfp programs will very likely break when Cogl + * starts to use ARBfb programs itself for the material API. + * + * Since: 1.0 + */ +void cogl_flush (void); + /* * Internal API available only to Clutter. @@ -760,20 +788,6 @@ void cogl_flush_gl_state (int flags); /* private */ void _cogl_set_indirect_context (gboolean indirect); -/* private - * - * cogl_flush: - * - * As an optimization Cogl drawing functions may batch up primitives - * internally, so you need to call _cogl_flush to ensure that the - * drawing operations you have submitted for the current frame get - * flushed through to the driver and GPU. - * - * This must be called before issuing a swap buffers. - */ -void _cogl_flush (void); - - G_END_DECLS #undef __COGL_H_INSIDE__ diff --git a/clutter/cogl/common/cogl.c b/clutter/cogl/common/cogl.c index 29366b335..81a0312ba 100644 --- a/clutter/cogl/common/cogl.c +++ b/clutter/cogl/common/cogl.c @@ -708,7 +708,7 @@ cogl_flush_gl_state (int flags) #endif void -_cogl_flush (void) +cogl_flush (void) { _cogl_journal_flush (); } @@ -748,7 +748,7 @@ cogl_read_pixels (int x, /* make sure any batched primitives get emitted to the GL driver before * issuing our read pixels... */ - _cogl_flush (); + cogl_flush (); glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); diff --git a/clutter/eglnative/clutter-backend-egl.c b/clutter/eglnative/clutter-backend-egl.c index adaf3cba0..e5f6c48d3 100644 --- a/clutter/eglnative/clutter-backend-egl.c +++ b/clutter/eglnative/clutter-backend-egl.c @@ -81,7 +81,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend, eglWaitNative (EGL_CORE_NATIVE_ENGINE); clutter_actor_paint (CLUTTER_ACTOR (stage)); - _cogl_flush (); + cogl_flush (); eglWaitGL(); eglSwapBuffers (backend_egl->edpy, stage_egl->egl_surface); diff --git a/clutter/eglx/clutter-backend-egl.c b/clutter/eglx/clutter-backend-egl.c index af06f97df..5a4057d91 100644 --- a/clutter/eglx/clutter-backend-egl.c +++ b/clutter/eglx/clutter-backend-egl.c @@ -139,7 +139,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend, /* this will cause the stage implementation to be painted as well */ clutter_actor_paint (CLUTTER_ACTOR (stage)); - _cogl_flush (); + cogl_flush (); /* Why this paint is done in backend as likely GL windowing system * specific calls, like swapping buffers. diff --git a/clutter/fruity/clutter-backend-fruity.c b/clutter/fruity/clutter-backend-fruity.c index 58c5e395a..ecc005c9f 100644 --- a/clutter/fruity/clutter-backend-fruity.c +++ b/clutter/fruity/clutter-backend-fruity.c @@ -72,7 +72,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend, eglWaitNative (EGL_CORE_NATIVE_ENGINE); clutter_actor_paint (CLUTTER_ACTOR (stage)); - _cogl_flush (); + cogl_flush (); eglWaitGL(); eglSwapBuffers (backend_egl->edpy, stage_egl->egl_surface); } diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 140038fed..51672be8a 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -498,7 +498,7 @@ clutter_backend_glx_redraw (ClutterBackend *backend, /* this will cause the stage implementation to be painted */ clutter_actor_paint (CLUTTER_ACTOR (stage)); - _cogl_flush (); + cogl_flush (); if (stage_x11->xwin != None) { diff --git a/clutter/osx/clutter-stage-osx.c b/clutter/osx/clutter-stage-osx.c index 962d4a707..ad3e3c5fe 100644 --- a/clutter/osx/clutter-stage-osx.c +++ b/clutter/osx/clutter-stage-osx.c @@ -128,7 +128,7 @@ clutter_stage_osx_state_update (ClutterStageOSX *self, - (void) drawRect: (NSRect) bounds { clutter_actor_paint (CLUTTER_ACTOR (self->stage_osx->wrapper)); - _cogl_flush (); + cogl_flush (); [[self openGLContext] flushBuffer]; } diff --git a/clutter/sdl/clutter-backend-sdl.c b/clutter/sdl/clutter-backend-sdl.c index 90c11674b..146fdd05f 100644 --- a/clutter/sdl/clutter-backend-sdl.c +++ b/clutter/sdl/clutter-backend-sdl.c @@ -68,7 +68,7 @@ clutter_backend_sdl_redraw (ClutterBackend *backend, ClutterStage *stage) { clutter_actor_paint (CLUTTER_ACTOR (stage)); - _cogl_flush (); + cogl_flush (); SDL_GL_SwapBuffers(); } diff --git a/clutter/win32/clutter-backend-win32.c b/clutter/win32/clutter-backend-win32.c index f1531a009..959bc53cf 100644 --- a/clutter/win32/clutter-backend-win32.c +++ b/clutter/win32/clutter-backend-win32.c @@ -297,7 +297,7 @@ clutter_backend_win32_redraw (ClutterBackend *backend, /* this will cause the stage implementation to be painted */ clutter_actor_paint (CLUTTER_ACTOR (stage)); - _cogl_flush (); + cogl_flush (); if (stage_win32->client_dc) SwapBuffers (stage_win32->client_dc); diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index c28ab52f6..f383e2620 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -72,6 +72,9 @@ cogl_set_source_texture CoglReadPixelsFlags cogl_read_pixels + +cogl_flush + COGL_TYPE_ATTRIBUTE_TYPE COGL_TYPE_BUFFER_BIT From 33400c0aae58468107be8a9e4572a37dd890ec81 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 29 Jun 2009 22:32:05 +0100 Subject: [PATCH 26/26] [cogl] Improve ability to break out into raw OpenGL via begin/end mechanism Although we wouldn't recommend developers try and interleve OpenGL drawing with Cogl drawing - we would prefer patches that improve Cogl to avoid this if possible - we are providing a simple mechanism that will at least give developers a fighting chance if they find it necissary. Note: we aren't helping developers change OpenGL state to modify the behaviour of Cogl drawing functions - it's unlikley that can ever be reliably supported - but if they are trying to do something like: - setup some OpenGL state. - draw using OpenGL (e.g. glDrawArrays() ) - reset modified OpenGL state. - continue using Cogl to draw They should surround their blocks of raw OpenGL with cogl_begin_gl() and cogl_end_gl(): cogl_begin_gl (); - setup some OpenGL state. - draw using OpenGL (e.g. glDrawArrays() ) - reset modified OpenGL state. cogl_end_gl (); - continue using Cogl to draw Again; we aren't supporting code like this: - setup some OpenGL state. - use Cogl to draw - reset modified OpenGL state. When the internals of Cogl evolves, this is very liable to break. cogl_begin_gl() will flush all internally batched Cogl primitives, and emit all internal Cogl state to OpenGL as if it were going to draw something itself. The result is that the OpenGL modelview matrix will be setup; the state corresponding to the current source material will be setup and other world state such as backface culling, depth and fogging enabledness will be also be sent to OpenGL. Note: no special material state is flushed, so if developers want Cogl to setup a simplified material state it is the their responsibility to set a simple source material before calling cogl_begin_gl. E.g. by calling cogl_set_source_color4ub(). Note: It is the developers responsibility to restore any OpenGL state that they modify to how it was after calling cogl_begin_gl() if they don't do this then the result of further Cogl calls is undefined. --- clutter/cogl/cogl.h.in | 79 ++++++++++++++++++++++++++++ clutter/cogl/common/cogl.c | 75 ++++++++++++++++++++++++++ clutter/cogl/gl/cogl-context.c | 2 + clutter/cogl/gl/cogl-context.h | 2 + doc/reference/cogl/cogl-sections.txt | 2 + 5 files changed, 160 insertions(+) diff --git a/clutter/cogl/cogl.h.in b/clutter/cogl/cogl.h.in index 295cf0d4b..ffa5cce0b 100644 --- a/clutter/cogl/cogl.h.in +++ b/clutter/cogl/cogl.h.in @@ -752,6 +752,85 @@ void cogl_read_pixels (int x, */ void cogl_flush (void); +/** + * cogl_begin_gl: + * + * We do not advise nor reliably support the interleaving of raw GL drawing and + * Cogl drawing functions, but if you insist, cogl_begin_gl() and cogl_end_gl() + * provide a simple mechanism that may at least give you a fighting chance of + * succeeding. + * + * Note: this doesn't help you modify the behaviour of Cogl drawing functions + * through the modification of GL state; that will never be reliably supported, + * but if you are trying to do something like: + * + * { + * - setup some OpenGL state. + * - draw using OpenGL (e.g. glDrawArrays() ) + * - reset modified OpenGL state. + * - continue using Cogl to draw + * } + * + * You should surround blocks of drawing using raw GL with cogl_begin_gl() + * and cogl_end_gl(): + * + * { + * cogl_begin_gl (); + * - setup some OpenGL state. + * - draw using OpenGL (e.g. glDrawArrays() ) + * - reset modified OpenGL state. + * cogl_end_gl (); + * - continue using Cogl to draw + * } + * + * + * Don't ever try and do: + * + * { + * - setup some OpenGL state. + * - use Cogl to draw + * - reset modified OpenGL state. + * } + * + * When the internals of Cogl evolves, this is very liable to break. + * + * This function will flush all batched primitives, and subsequently flush + * all internal Cogl state to OpenGL as if it were going to draw something + * itself. + * + * The result is that the OpenGL modelview matrix will be setup; the state + * corresponding to the current source material will be set up and other world + * state such as backface culling, depth and fogging enabledness will be sent + * to OpenGL. + * + * Note: no special material state is flushed, so if you want Cogl to setup a + * simplified material state it is your responsibility to set a simple source + * material before calling cogl_begin_gl. E.g. by calling + * cogl_set_source_color4ub(). + * + * Note: It is your responsibility to restore any OpenGL state that you modify + * to how it was after calling cogl_begin_gl() if you don't do this then the + * result of further Cogl calls is undefined. + * + * Note: You can not nest begin/end blocks. + * + * Again we would like to stress, we do not advise the use of this API and if + * possible we would prefer to improve Cogl than have developers require raw + * OpenGL. + * + * Since: 1.0 + */ +void cogl_begin_gl (void); + +/** + * cogl_end_gl: + * + * This is the counterpart to cogl_begin_gl() used to delimit blocks of drawing + * code using raw OpenGL. Please refer to cogl_begin_gl() for full details. + * + * Since: 1.0 + */ +void cogl_end_gl (void); /* * Internal API available only to Clutter. diff --git a/clutter/cogl/common/cogl.c b/clutter/cogl/common/cogl.c index 81a0312ba..309e744d5 100644 --- a/clutter/cogl/common/cogl.c +++ b/clutter/cogl/common/cogl.c @@ -771,3 +771,78 @@ cogl_read_pixels (int x, } } +void +cogl_begin_gl (void) +{ + CoglMaterialFlushOptions options; + gulong enable_flags; + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->in_begin_gl_block) + { + static gboolean shown = FALSE; + if (!shown) + g_warning ("You should not nest cogl_begin_gl/cogl_end_gl blocks"); + shown = TRUE; + return; + } + ctx->in_begin_gl_block = TRUE; + + /* Flush all batched primitives */ + cogl_flush (); + + /* Flush our clipping state to GL */ + cogl_clip_ensure (); + + /* Flush any client side matrix state */ + _cogl_current_matrix_state_flush (); + + + /* Setup the state for the current material */ + + /* We considered flushing a specific, minimal material here to try and + * simplify the GL state, but decided to avoid special cases and second + * guessing what would be actually helpful. + * + * A user should instead call cogl_set_source_color4ub() before + * cogl_begin_gl() to simplify the state flushed. + */ + options.flags = 0; + _cogl_material_flush_gl_state (ctx->source_material, &options); + + /* FIXME: This api is a bit yukky, ideally it will be removed if we + * re-work the cogl_enable mechanism */ + enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); + + if (ctx->enable_backface_culling) + enable_flags |= COGL_ENABLE_BACKFACE_CULLING; + + cogl_enable (enable_flags); + + /* Disable all client texture coordinate arrays */ + for (i = 0; i < ctx->n_texcoord_arrays_enabled; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); + } + ctx->n_texcoord_arrays_enabled = 0; +} + +void +cogl_end_gl (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!ctx->in_begin_gl_block) + { + static gboolean shown = FALSE; + if (!shown) + g_warning ("cogl_end_gl is being called before cogl_begin_gl"); + shown = TRUE; + return; + } + ctx->in_begin_gl_block = FALSE; +} + diff --git a/clutter/cogl/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c index 2266d9564..f587f8cd1 100644 --- a/clutter/cogl/gl/cogl-context.c +++ b/clutter/cogl/gl/cogl-context.c @@ -90,6 +90,8 @@ cogl_create_context () _context->last_path = 0; _context->stencil_material = cogl_material_new (); + _context->in_begin_gl_block = FALSE; + _context->pf_glGenRenderbuffersEXT = NULL; _context->pf_glBindRenderbufferEXT = NULL; _context->pf_glRenderbufferStorageEXT = NULL; diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index fa1ccb637..81d17c4fa 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -110,6 +110,8 @@ typedef struct guint quad_indices_short_len; CoglHandle quad_indices_short; + gboolean in_begin_gl_block; + /* Relying on glext.h to define these */ COGL_PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT; COGL_PFNGLDELETERENDERBUFFERSEXTPROC pf_glDeleteRenderbuffersEXT; diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index f383e2620..5a80895f2 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -74,6 +74,8 @@ cogl_read_pixels cogl_flush +cogl_begin_gl +cogl_end_gl COGL_TYPE_ATTRIBUTE_TYPE