static void _cogl_init_feature_overrides (CoglContext *ctx) { if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_VBOS))) COGL_FLAGS_SET (ctx->private_features, COGL_PRIVATE_FEATURE_VBOS, FALSE); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PBOS))) COGL_FLAGS_SET (ctx->private_features, COGL_PRIVATE_FEATURE_PBOS, FALSE); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_GLSL))) { ctx->feature_flags &= ~COGL_FEATURE_SHADERS_GLSL; COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GLSL, FALSE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, FALSE); } if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_NPOT_TEXTURES))) { ctx->feature_flags &= ~(COGL_FEATURE_TEXTURE_NPOT | COGL_FEATURE_TEXTURE_NPOT_BASIC | COGL_FEATURE_TEXTURE_NPOT_MIPMAP | COGL_FEATURE_TEXTURE_NPOT_REPEAT); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT, FALSE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, FALSE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, FALSE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, FALSE); } }
static CoglBool _cogl_pipeline_progend_fixed_start (CoglPipeline *pipeline) { _COGL_GET_CONTEXT (ctx, FALSE); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FIXED))) return FALSE; if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)) return FALSE; /* Vertex snippets are only supported in the GLSL fragend */ if (_cogl_pipeline_has_vertex_snippets (pipeline)) return FALSE; /* Fragment snippets are only supported in the GLSL fragend */ if (_cogl_pipeline_has_fragment_snippets (pipeline)) return FALSE; /* If there is a user program then the appropriate backend for that * language should handle it. */ if (cogl_pipeline_get_user_program (pipeline)) return FALSE; /* The fixed progend can't handle the per-vertex point size * attribute */ if (cogl_pipeline_get_per_vertex_point_size (pipeline)) return FALSE; return TRUE; }
static gboolean _cogl_pipeline_fragend_fixed_start (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference, int n_tex_coord_attribs) { CoglHandle user_program; _COGL_GET_CONTEXT (ctx, FALSE); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FIXED))) return FALSE; if (ctx->driver == COGL_DRIVER_GLES2) return FALSE; /* Fragment snippets are only supported in the GLSL fragend */ if (_cogl_pipeline_has_fragment_snippets (pipeline)) return FALSE; /* If there is a user program with a fragment shader then the appropriate backend for that language should handle it. We can still use the fixed fragment backend if the program only contains a vertex shader */ user_program = cogl_pipeline_get_user_program (pipeline); if (user_program != COGL_INVALID_HANDLE && _cogl_program_has_fragment_shader (user_program)) return FALSE; _cogl_use_fragment_program (0, COGL_PIPELINE_PROGRAM_TYPE_FIXED); return TRUE; }
static CoglBool _cogl_pipeline_progend_fixed_arbfp_start (CoglPipeline *pipeline) { _COGL_GET_CONTEXT (ctx, FALSE); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FIXED))) return FALSE; if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_FIXED)) return FALSE; /* Vertex snippets are only supported in the GLSL fragend */ if (_cogl_pipeline_has_vertex_snippets (pipeline)) return FALSE; /* Validate that we can handle the fragment state using ARBfp */ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ARBFP)) return FALSE; /* Fragment snippets are only supported in the GLSL fragend */ if (_cogl_pipeline_has_fragment_snippets (pipeline)) return FALSE; /* The ARBfp progend can't handle the per-vertex point size * attribute */ if (cogl_pipeline_get_per_vertex_point_size (pipeline)) return FALSE; return TRUE; }
static void setup_texture_source (CoglPipelineShaderState *shader_state, int unit_index, CoglTextureType texture_type) { if (!shader_state->unit_state[unit_index].sampled) { if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING))) g_string_append_printf (shader_state->source, "TEMP texel%d;\n" "MOV texel%d, one;\n", unit_index, unit_index); else g_string_append_printf (shader_state->source, "TEMP texel%d;\n" "TEX texel%d,fragment.texcoord[%d]," "texture[%d],%s;\n", unit_index, unit_index, unit_index, unit_index, texture_type_to_arbfp_string (texture_type)); shader_state->unit_state[unit_index].sampled = TRUE; } }
static gboolean cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache, PangoFont *font, PangoGlyph glyph, CoglPangoGlyphCacheValue *value) { CoglAtlasTexture *texture; CoglError *ignore_error = NULL; if (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SHARED_ATLAS)) return FALSE; /* If the cache is using mipmapping then we can't use the global atlas because it would just get migrated back out */ if (cache->use_mipmapping) return FALSE; texture = cogl_atlas_texture_new_with_size (cache->ctx, value->draw_width, value->draw_height); if (!cogl_texture_allocate (COGL_TEXTURE (texture), &ignore_error)) { cogl_error_free (ignore_error); return FALSE; } value->texture = COGL_TEXTURE (texture); value->tx1 = 0; value->ty1 = 0; value->tx2 = 1; value->ty2 = 1; value->tx_pixel = 0; value->ty_pixel = 0; /* The first time we store a texture in the global atlas we'll register for notifications when the global atlas is reorganized so we can forward the notification on as a glyph reorganization */ if (!cache->using_global_atlas) { _cogl_atlas_texture_add_reorganize_callback (cache->ctx, cogl_pango_glyph_cache_reorganize_cb, cache); cache->using_global_atlas = TRUE; } return TRUE; }
static CoglBool validate_statements_for_context (CoglBlendStringStatement *statements, int n_statements, CoglBlendStringContext context, CoglError **error) { const char *error_string; if (n_statements == 1) { if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) { error_string = "You need to also give a blend statement for the RGB" "channels"; goto error; } else if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) { error_string = "You need to also give a blend statement for the " "Alpha channel"; goto error; } } if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) return validate_blend_statements (statements, n_statements, error); else return validate_tex_combine_statements (statements, n_statements, error); error: _cogl_set_error (error, COGL_BLEND_STRING_ERROR, COGL_BLEND_STRING_ERROR_INVALID_ERROR, "Invalid %s string: %s", context == COGL_BLEND_STRING_CONTEXT_BLENDING ? "blend" : "texture combine", error_string); if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { g_debug ("Invalid %s string: %s", context == COGL_BLEND_STRING_CONTEXT_BLENDING ? "blend" : "texture combine", error_string); } return FALSE; }
static CoglBool validate_tex_combine_statements (CoglBlendStringStatement *statements, int n_statements, CoglError **error) { int i, j; const char *error_string; CoglBlendStringError detail = COGL_BLEND_STRING_ERROR_INVALID_ERROR; for (i = 0; i < n_statements; i++) { for (j = 0; j < statements[i].function->argc; j++) { CoglBlendStringArgument *arg = &statements[i].args[j]; if (arg->source.is_zero) { error_string = "You can't use the constant '0' as a texture " "combine argument"; goto error; } if (!arg->factor.is_one) { error_string = "Argument factors are only relevant to blending " "not texture combining"; goto error; } } } return TRUE; error: _cogl_set_error (error, COGL_BLEND_STRING_ERROR, detail, "Invalid texture combine string: %s", error_string); if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { g_debug ("Invalid texture combine string: %s", error_string); } return FALSE; }
static CoglBool cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache, PangoFont *font, PangoGlyph glyph, CoglPangoGlyphCacheValue *value) { CoglAtlasTexture *texture; if (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SHARED_ATLAS)) return FALSE; /* If the cache is using mipmapping then we can't use the global atlas because it would just get migrated back out */ if (cache->use_mipmapping) return FALSE; texture = _cogl_atlas_texture_new_with_size (value->draw_width, value->draw_height, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_RGBA_8888_PRE); if (texture == NULL) return FALSE; value->texture = COGL_TEXTURE (texture); value->tx1 = 0; value->ty1 = 0; value->tx2 = 1; value->ty2 = 1; value->tx_pixel = 0; value->ty_pixel = 0; /* The first time we store a texture in the global atlas we'll register for notifications when the global atlas is reorganized so we can forward the notification on as a glyph reorganization */ if (!cache->using_global_atlas) { _cogl_atlas_texture_add_reorganize_callback (cogl_pango_glyph_cache_reorganize_cb, cache); cache->using_global_atlas = TRUE; } return TRUE; }
void _cogl_glsl_shader_set_source_with_boilerplate (CoglContext *ctx, const char *version_string, GLuint shader_gl_handle, GLenum shader_gl_type, GLsizei count_in, const char **strings_in, const GLint *lengths_in) { const char *vertex_boilerplate; const char *fragment_boilerplate; const char **strings = g_alloca (sizeof (char *) * (count_in + 4)); GLint *lengths = g_alloca (sizeof (GLint) * (count_in + 4)); int count = 0; char *tex_coord_declarations = NULL; vertex_boilerplate = _COGL_VERTEX_SHADER_BOILERPLATE; fragment_boilerplate = _COGL_FRAGMENT_SHADER_BOILERPLATE; if (version_string) { strings[count] = version_string; lengths[count++] = -1; } if (ctx->driver == COGL_DRIVER_GLES2 && cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_3D)) { static const char texture_3d_extension[] = "#extension GL_OES_texture_3D : enable\n"; strings[count] = texture_3d_extension; lengths[count++] = sizeof (texture_3d_extension) - 1; } if (shader_gl_type == GL_VERTEX_SHADER) { strings[count] = vertex_boilerplate; lengths[count++] = strlen (vertex_boilerplate); } else if (shader_gl_type == GL_FRAGMENT_SHADER) { strings[count] = fragment_boilerplate; lengths[count++] = strlen (fragment_boilerplate); } memcpy (strings + count, strings_in, sizeof (char *) * count_in); if (lengths_in) memcpy (lengths + count, lengths_in, sizeof (GLint) * count_in); else { int i; for (i = 0; i < count_in; i++) lengths[count + i] = -1; /* null terminated */ } count += count_in; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) { GString *buf = g_string_new (NULL); int i; g_string_append_printf (buf, "%s shader:\n", shader_gl_type == GL_VERTEX_SHADER ? "vertex" : "fragment"); for (i = 0; i < count; i++) if (lengths[i] != -1) g_string_append_len (buf, strings[i], lengths[i]); else g_string_append (buf, strings[i]); g_message ("%s", buf->str); g_string_free (buf, TRUE); } GE( ctx, glShaderSource (shader_gl_handle, count, (const char **) strings, lengths) ); g_free (tex_coord_declarations); }
int _cogl_blend_string_compile (const char *string, CoglBlendStringContext context, CoglBlendStringStatement *statements, CoglError **error) { const char *p = string; const char *mark = NULL; const char *error_string; ParserState state = PARSER_STATE_EXPECT_DEST_CHANNELS; CoglBlendStringStatement *statement = statements; int current_statement = 0; int current_arg = 0; int remaining_argc = 0; #if 0 COGL_DEBUG_SET_FLAG (COGL_DEBUG_BLEND_STRINGS); #endif if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { COGL_NOTE (BLEND_STRINGS, "Compiling %s string:\n%s\n", context == COGL_BLEND_STRING_CONTEXT_BLENDING ? "blend" : "texture combine", string); } do { if (g_ascii_isspace (*p)) continue; if (*p == '\0') { switch (state) { case PARSER_STATE_EXPECT_DEST_CHANNELS: if (current_statement != 0) goto finished; error_string = "Empty statement"; goto error; case PARSER_STATE_SCRAPING_DEST_CHANNELS: error_string = "Expected an '=' following the destination " "channel mask"; goto error; case PARSER_STATE_EXPECT_FUNCTION_NAME: error_string = "Expected a function name"; goto error; case PARSER_STATE_SCRAPING_FUNCTION_NAME: error_string = "Expected parenthesis after the function name"; goto error; case PARSER_STATE_EXPECT_ARG_START: error_string = "Expected to find the start of an argument"; goto error; case PARSER_STATE_EXPECT_STATEMENT_END: error_string = "Expected closing parenthesis for statement"; goto error; } } switch (state) { case PARSER_STATE_EXPECT_DEST_CHANNELS: mark = p; state = PARSER_STATE_SCRAPING_DEST_CHANNELS; /* fall through */ case PARSER_STATE_SCRAPING_DEST_CHANNELS: if (*p != '=') continue; if (strncmp (mark, "RGBA", 4) == 0) statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA; else if (strncmp (mark, "RGB", 3) == 0) statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; else if (strncmp (mark, "A", 1) == 0) statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; else { error_string = "Unknown destination channel mask; " "expected RGBA=, RGB= or A="; goto error; } state = PARSER_STATE_EXPECT_FUNCTION_NAME; continue; case PARSER_STATE_EXPECT_FUNCTION_NAME: mark = p; state = PARSER_STATE_SCRAPING_FUNCTION_NAME; /* fall through */ case PARSER_STATE_SCRAPING_FUNCTION_NAME: if (*p != '(') { if (!is_alphanum_char (*p)) { error_string = "non alpha numeric character in function" "name"; goto error; } continue; } statement->function = get_function_info (mark, p, context); if (!statement->function) { error_string = "Unknown function name"; goto error; } remaining_argc = statement->function->argc; current_arg = 0; state = PARSER_STATE_EXPECT_ARG_START; /* fall through */ case PARSER_STATE_EXPECT_ARG_START: if (*p != '(' && *p != ',') continue; if (remaining_argc) { p++; /* parse_argument expects to see the first char of the arg */ if (!parse_argument (string, &p, statement, current_arg, &statement->args[current_arg], context, error)) return 0; current_arg++; remaining_argc--; } if (!remaining_argc) state = PARSER_STATE_EXPECT_STATEMENT_END; continue; case PARSER_STATE_EXPECT_STATEMENT_END: if (*p != ')') { error_string = "Expected end of statement"; goto error; } state = PARSER_STATE_EXPECT_DEST_CHANNELS; if (current_statement++ == 1) goto finished; statement = &statements[current_statement]; } } while (p++); finished: if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { if (current_statement > 0) print_statement (0, &statements[0]); if (current_statement > 1) print_statement (1, &statements[1]); } if (!validate_statements_for_context (statements, current_statement, context, error)) return 0; return current_statement; error: { int offset = p - string; _cogl_set_error (error, COGL_BLEND_STRING_ERROR, COGL_BLEND_STRING_ERROR_PARSE_ERROR, "Syntax error at offset %d: %s", offset, error_string); if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { g_debug ("Syntax error at offset %d: %s", offset, error_string); } return 0; } }
static CoglBool parse_argument (const char *string, /* original user string */ const char **ret_p, /* start of argument IN:OUT */ const CoglBlendStringStatement *statement, int current_arg, CoglBlendStringArgument *arg, /* OUT */ CoglBlendStringContext context, CoglError **error) { const char *p = *ret_p; const char *mark = NULL; const char *error_string = NULL; ParserArgState state = PARSER_ARG_STATE_START; CoglBool parsing_factor = FALSE; CoglBool implicit_factor_brace = FALSE; arg->source.is_zero = FALSE; arg->source.info = NULL; arg->source.texture = 0; arg->source.one_minus = FALSE; arg->source.mask = statement->mask; arg->factor.is_one = FALSE; arg->factor.is_color = FALSE; arg->factor.is_src_alpha_saturate = FALSE; arg->factor.source.is_zero = FALSE; arg->factor.source.info = NULL; arg->factor.source.texture = 0; arg->factor.source.one_minus = FALSE; arg->factor.source.mask = statement->mask; do { if (g_ascii_isspace (*p)) continue; if (*p == '\0') { error_string = "Unexpected end of string while parsing argument"; goto error; } switch (state) { case PARSER_ARG_STATE_START: if (*p == '1') state = PARSER_ARG_STATE_EXPECT_MINUS; else if (*p == '0') { arg->source.is_zero = TRUE; state = PARSER_ARG_STATE_EXPECT_END; } else { p--; /* backtrack */ state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; } continue; case PARSER_ARG_STATE_EXPECT_MINUS: if (*p != '-') { error_string = "expected a '-' following the 1"; goto error; } arg->source.one_minus = TRUE; state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; continue; case PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME: if (!is_symbol_char (*p)) { error_string = "expected a color source name"; goto error; } state = PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME; mark = p; if (parsing_factor) arg->factor.is_color = TRUE; /* fall through */ case PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME: if (!is_symbol_char (*p)) { CoglBlendStringColorSource *source = parsing_factor ? &arg->factor.source : &arg->source; source->info = get_color_src_info (mark, p, context); if (!source->info) { error_string = "Unknown color source name"; goto error; } if (source->info->type == COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N) { char *endp; source->texture = strtoul (&mark[strlen ("TEXTURE_")], &endp, 10); if (mark == endp) { error_string = "invalid texture number given with " "TEXTURE_N color source"; goto error; } p = endp; } state = PARSER_ARG_STATE_MAYBE_COLOR_MASK; } else continue; /* fall through */ case PARSER_ARG_STATE_MAYBE_COLOR_MASK: if (*p != '[') { p--; /* backtrack */ if (!parsing_factor) state = PARSER_ARG_STATE_MAYBE_MULT; else state = PARSER_ARG_STATE_EXPECT_END; continue; } state = PARSER_ARG_STATE_SCRAPING_MASK; mark = p; /* fall through */ case PARSER_ARG_STATE_SCRAPING_MASK: if (*p == ']') { size_t len = p - mark; CoglBlendStringColorSource *source = parsing_factor ? &arg->factor.source : &arg->source; if (len == 5 && strncmp (mark, "[RGBA", len) == 0) { if (statement->mask != COGL_BLEND_STRING_CHANNEL_MASK_RGBA) { error_string = "You can't use an RGBA color mask if the " "statement hasn't also got an RGBA= mask"; goto error; } source->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA; } else if (len == 4 && strncmp (mark, "[RGB", len) == 0) source->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; else if (len == 2 && strncmp (mark, "[A", len) == 0) source->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; else { error_string = "Expected a channel mask of [RGBA]" "[RGB] or [A]"; goto error; } if (parsing_factor) state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; else state = PARSER_ARG_STATE_MAYBE_MULT; } continue; case PARSER_ARG_STATE_EXPECT_OPEN_PAREN: if (*p != '(') { if (is_alphanum_char (*p)) { p--; /* compensate for implicit brace and ensure this * char gets considered part of the blend factor */ implicit_factor_brace = TRUE; } else { error_string = "Expected '(' around blend factor or alpha " "numeric character for blend factor name"; goto error; } } else implicit_factor_brace = FALSE; parsing_factor = TRUE; state = PARSER_ARG_STATE_EXPECT_FACTOR; continue; case PARSER_ARG_STATE_EXPECT_FACTOR: if (*p == '1') state = PARSER_ARG_STATE_MAYBE_MINUS; else if (*p == '0') { arg->source.is_zero = TRUE; state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; } else { state = PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE; mark = p; } continue; case PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE: if (!is_symbol_char (*p)) { size_t len = p - mark; if (len >= strlen ("SRC_ALPHA_SATURATE") && strncmp (mark, "SRC_ALPHA_SATURATE", len) == 0) { arg->factor.is_src_alpha_saturate = TRUE; state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; } else { state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; p = mark - 1; /* backtrack */ } } continue; case PARSER_ARG_STATE_MAYBE_MINUS: if (*p == '-') { if (implicit_factor_brace) { error_string = "Expected ( ) braces around blend factor with " "a subtraction"; goto error; } arg->factor.source.one_minus = TRUE; state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; } else { arg->factor.is_one = TRUE; state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; } continue; case PARSER_ARG_STATE_EXPECT_CLOSE_PAREN: if (implicit_factor_brace) { p--; state = PARSER_ARG_STATE_EXPECT_END; continue; } if (*p != ')') { error_string = "Expected closing parenthesis after blend factor"; goto error; } state = PARSER_ARG_STATE_EXPECT_END; continue; case PARSER_ARG_STATE_MAYBE_MULT: if (*p == '*') { state = PARSER_ARG_STATE_EXPECT_OPEN_PAREN; continue; } arg->factor.is_one = TRUE; state = PARSER_ARG_STATE_EXPECT_END; /* fall through */ case PARSER_ARG_STATE_EXPECT_END: if (*p != ',' && *p != ')') { error_string = "expected , or )"; goto error; } *ret_p = p - 1; return TRUE; } } while (p++); error: { int offset = p - string; _cogl_set_error (error, COGL_BLEND_STRING_ERROR, COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR, "Syntax error for argument %d at offset %d: %s", current_arg, offset, error_string); if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { g_debug ("Syntax error for argument %d at offset %d: %s", current_arg, offset, error_string); } return FALSE; } }
void _cogl_glsl_shader_set_source_with_boilerplate (CoglContext *ctx, GLuint shader_gl_handle, GLenum shader_gl_type, CoglPipeline *pipeline, GLsizei count_in, const char **strings_in, const GLint *lengths_in) { const char *vertex_boilerplate; const char *fragment_boilerplate; const char **strings = g_alloca (sizeof (char *) * (count_in + 4)); GLint *lengths = g_alloca (sizeof (GLint) * (count_in + 4)); char *version_string; int count = 0; int n_layers; vertex_boilerplate = _COGL_VERTEX_SHADER_BOILERPLATE; fragment_boilerplate = _COGL_FRAGMENT_SHADER_BOILERPLATE; version_string = g_strdup_printf ("#version %i\n\n", ctx->glsl_version_to_use); strings[count] = version_string; lengths[count++] = -1; if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_EMBEDDED) && cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_3D)) { static const char texture_3d_extension[] = "#extension GL_OES_texture_3D : enable\n"; strings[count] = texture_3d_extension; lengths[count++] = sizeof (texture_3d_extension) - 1; } if (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL)) { static const char texture_3d_extension[] = "#extension GL_OES_EGL_image_external : require\n"; strings[count] = texture_3d_extension; lengths[count++] = sizeof (texture_3d_extension) - 1; } if (shader_gl_type == GL_VERTEX_SHADER) { strings[count] = vertex_boilerplate; lengths[count++] = strlen (vertex_boilerplate); } else if (shader_gl_type == GL_FRAGMENT_SHADER) { strings[count] = fragment_boilerplate; lengths[count++] = strlen (fragment_boilerplate); } n_layers = cogl_pipeline_get_n_layers (pipeline); if (n_layers) { GString *layer_declarations = ctx->codegen_boilerplate_buffer; g_string_set_size (layer_declarations, 0); g_string_append_printf (layer_declarations, "varying vec4 _cogl_tex_coord[%d];\n", n_layers); if (shader_gl_type == GL_VERTEX_SHADER) { g_string_append_printf (layer_declarations, "uniform mat4 cogl_texture_matrix[%d];\n", n_layers); _cogl_pipeline_foreach_layer_internal (pipeline, add_layer_vertex_boilerplate_cb, layer_declarations); } else if (shader_gl_type == GL_FRAGMENT_SHADER) { _cogl_pipeline_foreach_layer_internal (pipeline, add_layer_fragment_boilerplate_cb, layer_declarations); } strings[count] = layer_declarations->str; lengths[count++] = -1; /* null terminated */ } memcpy (strings + count, strings_in, sizeof (char *) * count_in); if (lengths_in) memcpy (lengths + count, lengths_in, sizeof (GLint) * count_in); else { int i; for (i = 0; i < count_in; i++) lengths[count + i] = -1; /* null terminated */ } count += count_in; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) { GString *buf = g_string_new (NULL); int i; g_string_append_printf (buf, "%s shader:\n", shader_gl_type == GL_VERTEX_SHADER ? "vertex" : "fragment"); for (i = 0; i < count; i++) if (lengths[i] != -1) g_string_append_len (buf, strings[i], lengths[i]); else g_string_append (buf, strings[i]); g_message ("%s", buf->str); g_string_free (buf, TRUE); } GE( ctx, glShaderSource (shader_gl_handle, count, (const char **) strings, lengths) ); g_free (version_string); }
static CoglTexture * _cogl_texture_new_from_bitmap (CoglBitmap *bitmap, CoglTextureFlags flags, CoglPixelFormat internal_format, CoglBool can_convert_in_place, CoglError **error) { CoglContext *ctx = _cogl_bitmap_get_context (bitmap); CoglTexture *tex; CoglError *internal_error = NULL; if (!flags && !COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_ATLAS)) { /* First try putting the texture in the atlas */ CoglAtlasTexture *atlas_tex = _cogl_atlas_texture_new_from_bitmap (bitmap, can_convert_in_place); _cogl_texture_set_internal_format (COGL_TEXTURE (atlas_tex), internal_format); if (cogl_texture_allocate (COGL_TEXTURE (atlas_tex), &internal_error)) return COGL_TEXTURE (atlas_tex); cogl_error_free (internal_error); internal_error = NULL; cogl_object_unref (atlas_tex); } /* If that doesn't work try a fast path 2D texture */ if ((_cogl_util_is_pot (bitmap->width) && _cogl_util_is_pot (bitmap->height)) || (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP))) { tex = COGL_TEXTURE (_cogl_texture_2d_new_from_bitmap (bitmap, can_convert_in_place)); _cogl_texture_set_internal_format (tex, internal_format); if (!cogl_texture_allocate (tex, &internal_error)) { cogl_error_free (internal_error); internal_error = NULL; cogl_object_unref (tex); tex = NULL; } } else tex = NULL; if (!tex) { /* Otherwise create a sliced texture */ int max_waste = flags & COGL_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE; tex = COGL_TEXTURE (_cogl_texture_2d_sliced_new_from_bitmap (bitmap, max_waste, can_convert_in_place)); _cogl_texture_set_internal_format (tex, internal_format); if (!cogl_texture_allocate (tex, error)) { cogl_object_unref (tex); tex = NULL; } } if (tex && flags & COGL_TEXTURE_NO_AUTO_MIPMAP) { cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), 0, 0, 1, 1, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, set_auto_mipmap_cb, NULL); } return tex; }
void _cogl_shader_compile_real (CoglHandle handle, CoglPipeline *pipeline) { CoglShader *shader = handle; _COGL_GET_CONTEXT (ctx, NO_RETVAL); #ifdef HAVE_COGL_GL if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) { #ifdef COGL_GL_DEBUG GLenum gl_error; #endif if (shader->gl_handle) return; GE (ctx, glGenPrograms (1, &shader->gl_handle)); GE (ctx, glBindProgram (GL_FRAGMENT_PROGRAM_ARB, shader->gl_handle)); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) g_message ("user ARBfp program:\n%s", shader->source); #ifdef COGL_GL_DEBUG _cogl_gl_util_clear_gl_errors (ctx); #endif ctx->glProgramString (GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen (shader->source), shader->source); #ifdef COGL_GL_DEBUG gl_error = _cogl_gl_util_get_error (ctx); if (gl_error != GL_NO_ERROR) { g_warning ("%s: GL error (%d): Failed to compile ARBfp:\n%s\n%s", G_STRLOC, gl_error, shader->source, ctx->glGetString (GL_PROGRAM_ERROR_STRING_ARB)); } #endif } else #endif { GLenum gl_type; GLint status; if (shader->gl_handle) { CoglPipeline *prev = shader->compilation_pipeline; /* XXX: currently the only things that will affect the * boilerplate for user shaders, apart from driver features, * are the pipeline layer-indices and texture-unit-indices */ if (pipeline == prev || _cogl_pipeline_layer_and_unit_numbers_equal (prev, pipeline)) return; } if (shader->gl_handle) delete_shader (shader); switch (shader->type) { case COGL_SHADER_TYPE_VERTEX: gl_type = GL_VERTEX_SHADER; break; case COGL_SHADER_TYPE_FRAGMENT: gl_type = GL_FRAGMENT_SHADER; break; default: g_assert_not_reached (); break; } shader->gl_handle = ctx->glCreateShader (gl_type); _cogl_glsl_shader_set_source_with_boilerplate (ctx, shader->gl_handle, gl_type, pipeline, 1, (const char **) &shader->source, NULL); GE (ctx, glCompileShader (shader->gl_handle)); shader->compilation_pipeline = cogl_object_ref (pipeline); GE (ctx, glGetShaderiv (shader->gl_handle, GL_COMPILE_STATUS, &status)); if (!status) { char buffer[512]; int len = 0; ctx->glGetShaderInfoLog (shader->gl_handle, 511, &len, buffer); buffer[len] = '\0'; g_warning ("Failed to compile GLSL program:\n" "src:\n%s\n" "error:\n%s\n", shader->source, buffer); } } }
static void _cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state; CoglPipeline *authority; CoglPipelineCacheEntry *cache_entry = NULL; CoglProgram *user_program = cogl_pipeline_get_user_program (pipeline); _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Now lookup our ARBfp backend private state */ shader_state = get_shader_state (pipeline); /* If we have a valid shader_state then we are all set and don't * need to generate a new program. */ if (shader_state) return; /* If we don't have an associated arbfp program yet then find the * arbfp-authority (the oldest ancestor whose state will result in * the same program being generated as for this pipeline). * * We always make sure to associate new programs with the * arbfp-authority to maximize the chance that other pipelines can * share it. */ authority = _cogl_pipeline_find_equivalent_parent (pipeline, _cogl_pipeline_get_state_for_fragment_codegen (ctx) & ~COGL_PIPELINE_STATE_LAYERS, _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx)); shader_state = get_shader_state (authority); if (shader_state) { /* If we are going to share our program state with an arbfp-authority * then add a reference to the program state associated with that * arbfp-authority... */ set_shader_state (pipeline, shader_state); return; } /* If we haven't yet found an existing program then before we resort to * generating a new arbfp program we see if we can find a suitable * program in the pipeline_cache. */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) { cache_entry = _cogl_pipeline_cache_get_fragment_template (ctx->pipeline_cache, authority); shader_state = get_shader_state (cache_entry->pipeline); if (shader_state) shader_state->ref_count++; } /* If we still haven't got a shader state then we'll have to create a new one */ if (shader_state == NULL) { shader_state = shader_state_new (n_layers, cache_entry); shader_state->user_program = user_program; if (user_program == COGL_INVALID_HANDLE) { /* We reuse a single grow-only GString for code-gen */ g_string_set_size (ctx->codegen_source_buffer, 0); shader_state->source = ctx->codegen_source_buffer; g_string_append (shader_state->source, "!!ARBfp1.0\n" "TEMP output;\n" "TEMP tmp0, tmp1, tmp2, tmp3, tmp4;\n" "PARAM half = {.5, .5, .5, .5};\n" "PARAM one = {1, 1, 1, 1};\n" "PARAM two = {2, 2, 2, 2};\n" "PARAM minus_one = {-1, -1, -1, -1};\n"); } } set_shader_state (pipeline, shader_state); shader_state->ref_count--; /* Since we have already resolved the arbfp-authority at this point * we might as well also associate any program we find from the cache * with the authority too... */ if (authority != pipeline) set_shader_state (authority, shader_state); /* If we found a template then we'll attach it to that too so that next time a similar pipeline is used it can use the same state */ if (cache_entry) set_shader_state (cache_entry->pipeline, shader_state); }
static CoglBool _cogl_driver_update_features (CoglContext *ctx, CoglError **error) { CoglPrivateFeatureFlags private_flags = 0; char **gl_extensions; int gl_major = 0, gl_minor = 0; /* We have to special case getting the pointer to the glGetString* functions because we need to use them to determine what functions we can expect */ ctx->glGetString = (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, "glGetString", TRUE); ctx->glGetStringi = (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, "glGetStringi", TRUE); ctx->glGetIntegerv = (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, "glGetIntegerv", TRUE); gl_extensions = _cogl_context_get_gl_extensions (ctx); if (!check_gl_version (ctx, gl_extensions, error)) return FALSE; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WINSYS))) { char *all_extensions = g_strjoinv (" ", gl_extensions); COGL_NOTE (WINSYS, "Checking features\n" " GL_VENDOR: %s\n" " GL_RENDERER: %s\n" " GL_VERSION: %s\n" " GL_EXTENSIONS: %s", ctx->glGetString (GL_VENDOR), ctx->glGetString (GL_RENDERER), _cogl_context_get_gl_version (ctx), all_extensions); g_free (all_extensions); } _cogl_get_gl_version (ctx, &gl_major, &gl_minor); _cogl_gpu_info_init (ctx, &ctx->gpu); ctx->glsl_major = 1; ctx->glsl_minor = 1; if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0)) { const char *glsl_version = (char *)ctx->glGetString (GL_SHADING_LANGUAGE_VERSION); parse_gl_version (glsl_version, &ctx->glsl_major, &ctx->glsl_minor); } COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_UNSIGNED_INT_INDICES, TRUE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_DEPTH_RANGE, TRUE); if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 1, 4)) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_MIRRORED_REPEAT, TRUE); _cogl_feature_check_ext_functions (ctx, gl_major, gl_minor, gl_extensions); if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0) || _cogl_check_extension ("GL_ARB_texture_non_power_of_two", gl_extensions)) { COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT, TRUE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, TRUE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, TRUE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, TRUE); } if (_cogl_check_extension ("GL_MESA_pack_invert", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_MESA_PACK_INVERT; if (ctx->glGenRenderbuffers) { COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_OFFSCREEN, TRUE); private_flags |= COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS; } if (ctx->glBlitFramebuffer) private_flags |= COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT; if (ctx->glRenderbufferStorageMultisampleIMG) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_OFFSCREEN_MULTISAMPLE, TRUE); if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 0) || _cogl_check_extension ("GL_ARB_depth_texture", gl_extensions)) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_DEPTH_TEXTURE, TRUE); if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 1) || _cogl_check_extension ("GL_EXT_pixel_buffer_object", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_PBOS; if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 1, 4) || _cogl_check_extension ("GL_EXT_blend_color", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_BLEND_CONSTANT; if (ctx->glGenPrograms) private_flags |= COGL_PRIVATE_FEATURE_ARBFP; if (ctx->glCreateProgram) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GLSL, TRUE); else { /* If all of the old GLSL extensions are available then we can fake * the GL 2.0 GLSL support by diverting to the old function names */ if (ctx->glCreateProgramObject && /* GL_ARB_shader_objects */ ctx->glVertexAttribPointer && /* GL_ARB_vertex_shader */ _cogl_check_extension ("GL_ARB_fragment_shader", gl_extensions)) { ctx->glCreateShader = ctx->glCreateShaderObject; ctx->glCreateProgram = ctx->glCreateProgramObject; ctx->glDeleteShader = ctx->glDeleteObject; ctx->glDeleteProgram = ctx->glDeleteObject; ctx->glAttachShader = ctx->glAttachObject; ctx->glUseProgram = ctx->glUseProgramObject; ctx->glGetProgramInfoLog = ctx->glGetInfoLog; ctx->glGetShaderInfoLog = ctx->glGetInfoLog; ctx->glGetShaderiv = ctx->glGetObjectParameteriv; ctx->glGetProgramiv = ctx->glGetObjectParameteriv; ctx->glDetachShader = ctx->glDetachObject; ctx->glGetAttachedShaders = ctx->glGetAttachedObjects; /* FIXME: there doesn't seem to be an equivalent for glIsShader * and glIsProgram. This doesn't matter for now because Cogl * doesn't use these but if we add support for simulating a * GLES2 context on top of regular GL then we'll need to do * something here */ COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GLSL, TRUE); } } if ((COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0) || _cogl_check_extension ("GL_ARB_point_sprite", gl_extensions)) && /* If GLSL is supported then we only enable point sprite support * too if we have glsl >= 1.2 otherwise we don't have the * gl_PointCoord builtin which we depend on in the glsl backend. */ (!COGL_FLAGS_GET (ctx->features, COGL_FEATURE_ID_GLSL) || COGL_CHECK_GL_VERSION (ctx->glsl_major, ctx->glsl_minor, 1, 2))) { COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_POINT_SPRITE, TRUE); } if (ctx->glGenBuffers) { private_flags |= COGL_PRIVATE_FEATURE_VBOS; COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ, TRUE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, TRUE); } if (_cogl_check_extension ("GL_ARB_texture_rectangle", gl_extensions)) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_RECTANGLE, TRUE); if (ctx->glTexImage3D) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_3D, TRUE); if (ctx->glEGLImageTargetTexture2D) private_flags |= COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE; if (_cogl_check_extension ("GL_EXT_packed_depth_stencil", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL; if (ctx->glGenSamplers) private_flags |= COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS; if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 3) || _cogl_check_extension ("GL_ARB_texture_swizzle", gl_extensions) || _cogl_check_extension ("GL_EXT_texture_swizzle", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE; /* The per-vertex point size is only available via GLSL with the * gl_PointSize builtin. This is only available in GL 2.0 (not the * GLSL extensions) */ if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0)) { COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, TRUE); private_flags |= COGL_PRIVATE_FEATURE_ENABLE_PROGRAM_POINT_SIZE; } if (ctx->driver == COGL_DRIVER_GL) { int max_clip_planes = 0; /* Features which are not available in GL 3 */ private_flags |= (COGL_PRIVATE_FEATURE_GL_FIXED | COGL_PRIVATE_FEATURE_ALPHA_TEST | COGL_PRIVATE_FEATURE_QUADS | COGL_PRIVATE_FEATURE_ALPHA_TEXTURES); GE( ctx, glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); if (max_clip_planes >= 4) private_flags |= COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES; } private_flags |= (COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT | COGL_PRIVATE_FEATURE_ANY_GL | COGL_PRIVATE_FEATURE_FORMAT_CONVERSION | COGL_PRIVATE_FEATURE_BLEND_CONSTANT | COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM | COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS | COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL); if (ctx->glFenceSync) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_FENCE, TRUE); /* Cache features */ ctx->private_feature_flags |= private_flags; g_strfreev (gl_extensions); if ((private_flags & (COGL_PRIVATE_FEATURE_ALPHA_TEXTURES | COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE)) == 0) { _cogl_set_error (error, COGL_DRIVER_ERROR, COGL_DRIVER_ERROR_NO_SUITABLE_DRIVER_FOUND, "The GL_ARB_texture_swizzle extension is required " "to use the GL3 driver"); return FALSE; } return TRUE; }
static gboolean _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state; CoglPipeline *template_pipeline = NULL; CoglProgram *user_program; _COGL_GET_CONTEXT (ctx, FALSE); if (!cogl_features_available (COGL_FEATURE_SHADERS_GLSL)) return FALSE; user_program = cogl_pipeline_get_user_program (pipeline); /* If the user program has a vertex shader that isn't GLSL then the appropriate vertend for that language should handle it */ if (user_program && _cogl_program_has_vertex_shader (user_program) && _cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_GLSL) return FALSE; /* Now lookup our glsl backend private state (allocating if * necessary) */ shader_state = get_shader_state (pipeline); if (shader_state == NULL) { CoglPipeline *authority; /* Get the authority for anything affecting vertex shader state */ authority = _cogl_pipeline_find_equivalent_parent (pipeline, COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN & ~COGL_PIPELINE_STATE_LAYERS, COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN); shader_state = get_shader_state (authority); if (shader_state == NULL) { /* Check if there is already a similar cached pipeline whose shader state we can share */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) { template_pipeline = _cogl_pipeline_cache_get_vertex_template (ctx->pipeline_cache, authority); shader_state = get_shader_state (template_pipeline); } if (shader_state) shader_state->ref_count++; else shader_state = shader_state_new (); set_shader_state (authority, shader_state); if (template_pipeline) { shader_state->ref_count++; set_shader_state (template_pipeline, shader_state); } } if (authority != pipeline) { shader_state->ref_count++; set_shader_state (pipeline, shader_state); } } if (shader_state->gl_shader) { /* If we already have a valid GLSL shader then we don't need to generate a new one. However if there's a user program and it has changed since the last link then we do need a new shader */ if (user_program == NULL || shader_state->user_program_age == user_program->age) return TRUE; /* We need to recreate the shader so destroy the existing one */ GE( ctx, glDeleteShader (shader_state->gl_shader) ); shader_state->gl_shader = 0; } /* If we make it here then we have a shader_state struct without a gl_shader either because this is the first time we've encountered it or because the user program has changed */ if (user_program) shader_state->user_program_age = user_program->age; /* If the user program contains a vertex shader then we don't need to generate one */ if (user_program && _cogl_program_has_vertex_shader (user_program)) return TRUE; /* We reuse two grow-only GStrings for code-gen. One string contains the uniform and attribute declarations while the other contains the main function. We need two strings because we need to dynamically declare attributes as the add_layer callback is invoked */ g_string_set_size (ctx->codegen_header_buffer, 0); g_string_set_size (ctx->codegen_source_buffer, 0); shader_state->header = ctx->codegen_header_buffer; shader_state->source = ctx->codegen_source_buffer; g_string_append (shader_state->source, "void\n" "main ()\n" "{\n"); if (ctx->driver == COGL_DRIVER_GLES2) /* There is no builtin uniform for the pointsize on GLES2 so we need to copy it from the custom uniform in the vertex shader */ g_string_append (shader_state->source, " cogl_point_size_out = cogl_point_size_in;\n"); /* On regular OpenGL we'll just flush the point size builtin */ else if (pipelines_difference & COGL_PIPELINE_STATE_POINT_SIZE) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_POINT_SIZE); if (ctx->point_size_cache != authority->big_state->point_size) { GE( ctx, glPointSize (authority->big_state->point_size) ); ctx->point_size_cache = authority->big_state->point_size; } } return TRUE; }
static void _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state; CoglPipeline *template_pipeline = NULL; _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Now lookup our glsl backend private state (allocating if * necessary) */ shader_state = get_shader_state (pipeline); if (shader_state == NULL) { CoglPipeline *authority; /* Get the authority for anything affecting vertex shader state */ authority = _cogl_pipeline_find_equivalent_parent (pipeline, COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN & ~COGL_PIPELINE_STATE_LAYERS, COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN); shader_state = get_shader_state (authority); if (shader_state == NULL) { /* Check if there is already a similar cached pipeline whose shader state we can share */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) { template_pipeline = _cogl_pipeline_cache_get_vertex_template (ctx->pipeline_cache, authority); shader_state = get_shader_state (template_pipeline); } if (shader_state) shader_state->ref_count++; else shader_state = shader_state_new (); set_shader_state (authority, shader_state); if (template_pipeline) { shader_state->ref_count++; set_shader_state (template_pipeline, shader_state); } } if (authority != pipeline) { shader_state->ref_count++; set_shader_state (pipeline, shader_state); } } if (shader_state->gl_shader) return; /* If we make it here then we have a shader_state struct without a gl_shader because this is the first time we've encountered it */ /* We reuse two grow-only GStrings for code-gen. One string contains the uniform and attribute declarations while the other contains the main function. We need two strings because we need to dynamically declare attributes as the add_layer callback is invoked */ g_string_set_size (ctx->codegen_header_buffer, 0); g_string_set_size (ctx->codegen_source_buffer, 0); shader_state->header = ctx->codegen_header_buffer; shader_state->source = ctx->codegen_source_buffer; add_layer_declarations (pipeline, shader_state); add_global_declarations (pipeline, shader_state); g_string_append (shader_state->source, "void\n" "cogl_generated_source ()\n" "{\n"); if (!(ctx->private_feature_flags & COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM)) /* There is no builtin uniform for the pointsize on GLES2 so we need to copy it from the custom uniform in the vertex shader */ g_string_append (shader_state->source, " cogl_point_size_out = cogl_point_size_in;\n"); }
static CoglBool _cogl_driver_update_features (CoglContext *context, CoglError **error) { CoglPrivateFeatureFlags private_flags = 0; char **gl_extensions; /* We have to special case getting the pointer to the glGetString function because we need to use it to determine what functions we can expect */ context->glGetString = (void *) _cogl_renderer_get_proc_address (context->display->renderer, "glGetString", TRUE); gl_extensions = _cogl_context_get_gl_extensions (context); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WINSYS))) { char *all_extensions = g_strjoinv (" ", gl_extensions); COGL_NOTE (WINSYS, "Checking features\n" " GL_VENDOR: %s\n" " GL_RENDERER: %s\n" " GL_VERSION: %s\n" " GL_EXTENSIONS: %s", context->glGetString (GL_VENDOR), context->glGetString (GL_RENDERER), _cogl_context_get_gl_version (context), all_extensions); g_free (all_extensions); } context->glsl_major = 1; context->glsl_minor = 0; _cogl_gpu_info_init (context, &context->gpu); _cogl_feature_check_ext_functions (context, -1 /* GL major version */, -1 /* GL minor version */, gl_extensions); #ifdef HAVE_COGL_GLES if (context->driver == COGL_DRIVER_GLES1) { int max_clip_planes; GE( context, glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); if (max_clip_planes >= 4) private_flags |= COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES; } #endif if (context->driver == COGL_DRIVER_GLES2) { /* Note GLES 2 core doesn't support mipmaps for npot textures or * repeat modes other than CLAMP_TO_EDGE. */ COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_GLSL, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_OFFSCREEN, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_DEPTH_RANGE, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_MIRRORED_REPEAT, TRUE); private_flags |= COGL_PRIVATE_FEATURE_BLEND_CONSTANT; } else if (context->driver == COGL_DRIVER_GLES1) private_flags |= (COGL_PRIVATE_FEATURE_FIXED_FUNCTION | COGL_PRIVATE_FEATURE_ALPHA_TEST | COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM); private_flags |= (COGL_PRIVATE_FEATURE_VBOS | COGL_PRIVATE_FEATURE_ANY_GL | COGL_PRIVATE_FEATURE_ALPHA_TEXTURES); /* Both GLES 1.1 and GLES 2.0 support point sprites in core */ COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_POINT_SPRITE, TRUE); if (context->glGenRenderbuffers) COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_OFFSCREEN, TRUE); if (context->glBlitFramebuffer) private_flags |= COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT; if (_cogl_check_extension ("GL_OES_element_index_uint", gl_extensions)) COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_UNSIGNED_INT_INDICES, TRUE); if (_cogl_check_extension ("GL_OES_depth_texture", gl_extensions)) COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_DEPTH_TEXTURE, TRUE); if (_cogl_check_extension ("GL_OES_texture_npot", gl_extensions)) { COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_NPOT, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, TRUE); } else if (_cogl_check_extension ("GL_IMG_texture_npot", gl_extensions)) { COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, TRUE); } if (context->glTexImage3D) COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_3D, TRUE); if (context->glMapBuffer) /* The GL_OES_mapbuffer extension doesn't support mapping for read */ COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, TRUE); if (context->glEGLImageTargetTexture2D) private_flags |= COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE; if (_cogl_check_extension ("GL_OES_packed_depth_stencil", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL; if (_cogl_check_extension ("GL_EXT_texture_format_BGRA8888", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_BGRA8888; if (_cogl_check_extension ("GL_EXT_unpack_subimage", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE; /* Cache features */ context->private_feature_flags |= private_flags; g_strfreev (gl_extensions); return TRUE; }
static CoglBool _cogl_pipeline_fragend_arbfp_end (CoglPipeline *pipeline, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state = get_shader_state (pipeline); GLuint gl_program; UpdateConstantsState state; _COGL_GET_CONTEXT (ctx, FALSE); if (shader_state->source) { GLenum gl_error; COGL_STATIC_COUNTER (fragend_arbfp_compile_counter, "arbfp compile counter", "Increments each time a new ARBfp " "program is compiled", 0 /* no application private data */); COGL_COUNTER_INC (_cogl_uprof_context, fragend_arbfp_compile_counter); g_string_append (shader_state->source, "MOV result.color,output;\n"); g_string_append (shader_state->source, "END\n"); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) g_message ("pipeline program:\n%s", shader_state->source->str); GE (ctx, glGenPrograms (1, &shader_state->gl_program)); GE (ctx, glBindProgram (GL_FRAGMENT_PROGRAM_ARB, shader_state->gl_program)); while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) ; ctx->glProgramString (GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, shader_state->source->len, shader_state->source->str); if (ctx->glGetError () != GL_NO_ERROR) { g_warning ("\n%s\n%s", shader_state->source->str, ctx->glGetString (GL_PROGRAM_ERROR_STRING_ARB)); } shader_state->source = NULL; } gl_program = shader_state->gl_program; GE (ctx, glBindProgram (GL_FRAGMENT_PROGRAM_ARB, gl_program)); _cogl_use_fragment_program (0, COGL_PIPELINE_PROGRAM_TYPE_ARBFP); state.unit = 0; state.shader_state = shader_state; /* If this arbfp program was last used with a different pipeline * then we need to ensure we update all program.local params */ state.update_all = pipeline != shader_state->last_used_for_pipeline; cogl_pipeline_foreach_layer (pipeline, update_constants_cb, &state); /* We need to track what pipeline used this arbfp program last since * we will need to update program.local params when switching * between different pipelines. */ shader_state->last_used_for_pipeline = pipeline; return TRUE; }
static CoglBool _cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference, int n_tex_coord_attribs) { CoglPipelineShaderState *shader_state; CoglPipeline *authority; CoglPipeline *template_pipeline = NULL; _COGL_GET_CONTEXT (ctx, FALSE); /* First validate that we can handle the current state using ARBfp */ if (!cogl_has_feature (ctx, COGL_FEATURE_ID_ARBFP)) return FALSE; /* TODO: support fog */ if (_cogl_pipeline_get_fog_enabled (pipeline)) return FALSE; /* Fragment snippets are only supported in the GLSL fragend */ if (_cogl_pipeline_has_fragment_snippets (pipeline)) return FALSE; /* Now lookup our ARBfp backend private state */ shader_state = get_shader_state (pipeline); /* If we have a valid shader_state then we are all set and don't * need to generate a new program. */ if (shader_state) return TRUE; /* If we don't have an associated arbfp program yet then find the * arbfp-authority (the oldest ancestor whose state will result in * the same program being generated as for this pipeline). * * We always make sure to associate new programs with the * arbfp-authority to maximize the chance that other pipelines can * share it. */ authority = _cogl_pipeline_find_equivalent_parent (pipeline, _cogl_pipeline_get_state_for_fragment_codegen (ctx) & ~COGL_PIPELINE_STATE_LAYERS, _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx)); shader_state = get_shader_state (authority); if (shader_state) { /* If we are going to share our program state with an arbfp-authority * then add a reference to the program state associated with that * arbfp-authority... */ shader_state->ref_count++; set_shader_state (pipeline, shader_state); return TRUE; } /* If we haven't yet found an existing program then before we resort to * generating a new arbfp program we see if we can find a suitable * program in the pipeline_cache. */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) { template_pipeline = _cogl_pipeline_cache_get_fragment_template (ctx->pipeline_cache, authority); shader_state = get_shader_state (template_pipeline); if (shader_state) shader_state->ref_count++; } /* If we still haven't got a shader state then we'll have to create a new one */ if (shader_state == NULL) { shader_state = shader_state_new (n_layers); /* We reuse a single grow-only GString for code-gen */ g_string_set_size (ctx->codegen_source_buffer, 0); shader_state->source = ctx->codegen_source_buffer; g_string_append (shader_state->source, "!!ARBfp1.0\n" "TEMP output;\n" "TEMP tmp0, tmp1, tmp2, tmp3, tmp4;\n" "PARAM half = {.5, .5, .5, .5};\n" "PARAM one = {1, 1, 1, 1};\n" "PARAM two = {2, 2, 2, 2};\n" "PARAM minus_one = {-1, -1, -1, -1};\n"); } set_shader_state (pipeline, shader_state); /* Since we have already resolved the arbfp-authority at this point * we might as well also associate any program we find from the cache * with the authority too... */ if (authority != pipeline) { shader_state->ref_count++; set_shader_state (authority, shader_state); } /* If we found a template then we'll attach it to that too so that next time a similar pipeline is used it can use the same state */ if (template_pipeline) { shader_state->ref_count++; set_shader_state (template_pipeline, shader_state); } return TRUE; }
static CoglBool _cogl_pipeline_fragend_fixed_add_layer (CoglPipeline *pipeline, CoglPipelineLayer *layer, unsigned long layers_difference) { CoglTextureUnit *unit = _cogl_get_texture_unit (_cogl_pipeline_layer_get_unit_index (layer)); int unit_index = unit->index; int n_rgb_func_args; int n_alpha_func_args; _COGL_GET_CONTEXT (ctx, FALSE); /* XXX: Beware that since we are changing the active texture unit we * must make sure we don't call into other Cogl components that may * temporarily bind texture objects to query/modify parameters since * they will end up binding texture unit 1. See * _cogl_bind_gl_texture_transient for more details. */ _cogl_set_active_texture_unit (unit_index); if (G_UNLIKELY (unit_index >= get_max_texture_units ())) { _cogl_disable_texture_unit (unit_index); /* TODO: although this isn't considered an error that * warrants falling back to a different backend we * should print a warning here. */ return TRUE; } /* Handle enabling or disabling the right texture type */ if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE) { CoglTextureType texture_type = _cogl_pipeline_layer_get_texture_type (layer); GLenum gl_target; switch (texture_type) { case COGL_TEXTURE_TYPE_2D: gl_target = GL_TEXTURE_2D; break; case COGL_TEXTURE_TYPE_3D: gl_target = GL_TEXTURE_3D; break; case COGL_TEXTURE_TYPE_RECTANGLE: gl_target = GL_TEXTURE_RECTANGLE_ARB; break; } _cogl_set_active_texture_unit (unit_index); /* The common GL code handles binding the right texture so we just need to handle enabling and disabling it */ if (unit->enabled_gl_target != gl_target) { /* Disable the previous target if it's still enabled */ if (unit->enabled_gl_target) GE (ctx, glDisable (unit->enabled_gl_target)); /* Enable the new target */ if (!G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING))) { GE (ctx, glEnable (gl_target)); unit->enabled_gl_target = gl_target; } } } else { /* Even though there may be no difference between the last flushed * texture state and the current layers texture state it may be that the * texture unit has been disabled for some time so we need to assert that * it's enabled now. */ if (!G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING)) && unit->enabled_gl_target == 0) { _cogl_set_active_texture_unit (unit_index); GE (ctx, glEnable (unit->gl_target)); unit->enabled_gl_target = unit->gl_target; } } if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE) { CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_COMBINE); CoglPipelineLayerBigState *big_state = authority->big_state; GLenum sources[3]; GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE)); /* Set the combiner functions... */ GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, big_state->texture_combine_rgb_func)); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, big_state->texture_combine_alpha_func)); /* * Setup the function arguments... */ /* For the RGB components... */ n_rgb_func_args = _cogl_get_n_args_for_combine_func (big_state->texture_combine_rgb_func); translate_sources (pipeline, n_rgb_func_args, big_state->texture_combine_rgb_src, sources); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, sources[0])); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, big_state->texture_combine_rgb_op[0])); if (n_rgb_func_args > 1) { GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, sources[1])); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, big_state->texture_combine_rgb_op[1])); } if (n_rgb_func_args > 2) { GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_RGB, sources[2])); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB, big_state->texture_combine_rgb_op[2])); } /* For the Alpha component */ n_alpha_func_args = _cogl_get_n_args_for_combine_func (big_state->texture_combine_alpha_func); translate_sources (pipeline, n_alpha_func_args, big_state->texture_combine_alpha_src, sources); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, sources[0])); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, big_state->texture_combine_alpha_op[0])); if (n_alpha_func_args > 1) { GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, sources[1])); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, big_state->texture_combine_alpha_op[1])); } if (n_alpha_func_args > 2) { GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_ALPHA, sources[2])); GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, big_state->texture_combine_alpha_op[2])); } } if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT) { CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT); CoglPipelineLayerBigState *big_state = authority->big_state; GE (ctx, glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, big_state->texture_combine_constant)); } return TRUE; }
/* In addition to writing the stack matrix into the give @matrix * argument this function *may* sometimes also return a pointer * to a matrix too so if we are querying the inverse matrix we * should query from the return matrix so that the result can * be cached within the stack. */ CoglMatrix * cogl_matrix_entry_get (CoglMatrixEntry *entry, CoglMatrix *matrix) { int depth; CoglMatrixEntry *current; CoglMatrixEntry **children; int i; for (depth = 0, current = entry; current; current = current->parent, depth++) { switch (current->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: cogl_matrix_init_identity (matrix); goto initialized; case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)current; *matrix = *load->matrix; goto initialized; } case COGL_MATRIX_OP_SAVE: { CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)current; if (!save->cache_valid) { CoglMagazine *matrices_magazine = cogl_matrix_stack_matrices_magazine; save->cache = _cogl_magazine_chunk_alloc (matrices_magazine); cogl_matrix_entry_get (current->parent, save->cache); save->cache_valid = TRUE; } *matrix = *save->cache; goto initialized; } default: continue; } } initialized: if (depth == 0) { switch (entry->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: case COGL_MATRIX_OP_TRANSLATE: case COGL_MATRIX_OP_ROTATE: case COGL_MATRIX_OP_ROTATE_QUATERNION: case COGL_MATRIX_OP_ROTATE_EULER: case COGL_MATRIX_OP_SCALE: case COGL_MATRIX_OP_MULTIPLY: return NULL; case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; return load->matrix; } case COGL_MATRIX_OP_SAVE: { CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry; return save->cache; } } g_warn_if_reached (); return NULL; } #ifdef COGL_ENABLE_DEBUG if (!current) { g_warning ("Inconsistent matrix stack"); return NULL; } entry->composite_gets++; #endif children = g_alloca (sizeof (CoglMatrixEntry) * depth); /* We need walk the list of entries from the init/load/save entry * back towards the leaf node but the nodes don't link to their * children so we need to re-walk them here to add to a separate * array. */ for (i = depth - 1, current = entry; i >= 0 && current; i--, current = current->parent) { children[i] = current; } #ifdef COGL_ENABLE_DEBUG if (COGL_DEBUG_ENABLED (COGL_DEBUG_PERFORMANCE) && entry->composite_gets >= 2) { COGL_NOTE (PERFORMANCE, "Re-composing a matrix stack entry multiple times"); } #endif for (i = 0; i < depth; i++) { switch (children[i]->op) { case COGL_MATRIX_OP_TRANSLATE: { CoglMatrixEntryTranslate *translate = (CoglMatrixEntryTranslate *)children[i]; cogl_matrix_translate (matrix, translate->x, translate->y, translate->z); continue; } case COGL_MATRIX_OP_ROTATE: { CoglMatrixEntryRotate *rotate= (CoglMatrixEntryRotate *)children[i]; cogl_matrix_rotate (matrix, rotate->angle, rotate->x, rotate->y, rotate->z); continue; } case COGL_MATRIX_OP_ROTATE_EULER: { CoglMatrixEntryRotateEuler *rotate = (CoglMatrixEntryRotateEuler *)children[i]; CoglEuler euler; cogl_euler_init (&euler, rotate->heading, rotate->pitch, rotate->roll); cogl_matrix_rotate_euler (matrix, &euler); continue; } case COGL_MATRIX_OP_ROTATE_QUATERNION: { CoglMatrixEntryRotateQuaternion *rotate = (CoglMatrixEntryRotateQuaternion *)children[i]; CoglQuaternion quaternion; cogl_quaternion_init_from_array (&quaternion, rotate->values); cogl_matrix_rotate_quaternion (matrix, &quaternion); continue; } case COGL_MATRIX_OP_SCALE: { CoglMatrixEntryScale *scale = (CoglMatrixEntryScale *)children[i]; cogl_matrix_scale (matrix, scale->x, scale->y, scale->z); continue; } case COGL_MATRIX_OP_MULTIPLY: { CoglMatrixEntryMultiply *multiply = (CoglMatrixEntryMultiply *)children[i]; cogl_matrix_multiply (matrix, matrix, multiply->matrix); continue; } case COGL_MATRIX_OP_LOAD_IDENTITY: case COGL_MATRIX_OP_LOAD: case COGL_MATRIX_OP_SAVE: g_warn_if_reached (); continue; } } return NULL; }