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);