From 845ff67301da767926f96c56026a8dd9c7964f01 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 17 Jun 2009 18:46:42 +0100 Subject: [PATCH] [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 | 667 ++++++++++++++++++++-------- 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, 1003 insertions(+), 296 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 5285f4d..ae66792 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 8aa1e8f..1b42358 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 786ff34..94ea5b2 100644 --- a/clutter/cogl/cogl-material.h +++ b/clutter/cogl/cogl-material.h @@ -674,6 +674,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 * Cogl texture @@ -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 adc3292..cbd70f0 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 3ef1b2b..9efa5c2 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 b6dfd75..7eb8561 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 e61ab97..6fe52be 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 _CoglMaterialFlushFlag +{ + 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 enum _CoglMaterialFlushOption +typedef struct _CoglMaterialFlushOptions { - COGL_MATERIAL_FLUSH_FALLBACK_MASK = 1, - COGL_MATERIAL_FLUSH_DISABLE_MASK, - COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE, -} CoglMaterialFlushOption; + 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 d39120b..a2ad30f 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 dafe92f..9e3ea89 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -40,9 +40,52 @@ #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, @@ -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); - - disable_mask = (1 << batch_start->n_layers) - 1; - disable_mask = ~disable_mask; + g_print ("stride = %d (%d bytes)\n", (int)stride, (int)stride * 4); - _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; - /* 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 (can_batch_callback (entry0, entry1)) + { + batch_len++; + continue; + } - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; + batch_callback (batch_start, batch_len, data); - enable_flags |= COGL_ENABLE_VERTEX_ARRAY; - cogl_enable (enable_flags); + batch_start = entry1; + batch_len = 1; + } - GE (glVertexPointer (2, GL_FLOAT, stride, vertex_pointer)); - _cogl_current_matrix_state_flush (); + /* The last batch... */ + batch_callback (batch_start, batch_len, data); +} + +static void +_cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, + int batch_len, + void *data) +{ + CoglJournalFlushState *state = data; + + 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: - * - * - 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? - */ + /* 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; - /* 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. + /* 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 will be able to do cooler stuff here when we extend the life of - * journals beyond _cogl_multitexture_multiple_rectangles. + * 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 */ - 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 268fba3..dd5a73c 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 a188520..da99aee 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 d06d8dd..944ff8d 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); } @@ -679,6 +705,12 @@ cogl_flush_gl_state (int flags) #endif void +_cogl_flush (void) +{ + _cogl_journal_flush (); +} + +void cogl_read_pixels (int x, int y, int width, @@ -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 1b6fab8..2266d95 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 9e1596e..fa1ccb6 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 db99e6a..f133409 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 6c106f9..30b5553 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 0a10cd4..75cf8e2 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 365a479..6a2ed7e 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 fb118c7..7721ea2 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 8444453..adaf3cb 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 d3c36d0..af06f97 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 c90809d..58c5e39 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 d9fef91..140038f 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 00bbcb4..962d4a7 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 70a1bc9..90c1167 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 6cc9155..f1531a0 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); -- 2.7.4