diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index 1b5d85854..fddd4c60e 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/cogl/Makefile.am @@ -240,6 +240,8 @@ cogl_sources_c = \ $(srcdir)/cogl-pipeline-fragend-arbfp-private.h \ $(srcdir)/cogl-pipeline-fragend-fixed.c \ $(srcdir)/cogl-pipeline-fragend-fixed-private.h \ + $(srcdir)/cogl-pipeline-vertend-glsl.c \ + $(srcdir)/cogl-pipeline-vertend-glsl-private.h \ $(srcdir)/cogl-pipeline-vertend-fixed.c \ $(srcdir)/cogl-pipeline-vertend-fixed-private.h \ $(srcdir)/cogl-pipeline-progend-glsl.c \ diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index 976b988ba..fc6000e2b 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/cogl/cogl-context.c @@ -193,7 +193,8 @@ cogl_create_context (void) _context->current_program = COGL_INVALID_HANDLE; - _context->current_use_program_type = COGL_PIPELINE_PROGRAM_TYPE_FIXED; + _context->current_fragment_program_type = COGL_PIPELINE_PROGRAM_TYPE_FIXED; + _context->current_vertex_program_type = COGL_PIPELINE_PROGRAM_TYPE_FIXED; _context->current_gl_program = 0; _context->gl_blend_enable_cache = FALSE; diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index b1720230e..3e553b408 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/cogl/cogl-context.h @@ -183,7 +183,8 @@ typedef struct /* Fragment processing programs */ CoglHandle current_program; - CoglPipelineProgramType current_use_program_type; + CoglPipelineProgramType current_fragment_program_type; + CoglPipelineProgramType current_vertex_program_type; GLuint current_gl_program; /* List of types that will be considered a subclass of CoglTexture in diff --git a/clutter/cogl/cogl/cogl-pipeline-fragend-arbfp.c b/clutter/cogl/cogl/cogl-pipeline-fragend-arbfp.c index 48eef5ec6..ffd45f2cf 100644 --- a/clutter/cogl/cogl/cogl-pipeline-fragend-arbfp.c +++ b/clutter/cogl/cogl/cogl-pipeline-fragend-arbfp.c @@ -205,6 +205,7 @@ _cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline, user_program = cogl_pipeline_get_user_program (pipeline); if (user_program != COGL_INVALID_HANDLE && + _cogl_program_has_fragment_shader (user_program) && _cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_ARBFP) return FALSE; @@ -969,7 +970,7 @@ _cogl_pipeline_fragend_arbfp_end (CoglPipeline *pipeline, gl_program = arbfp_program_state->gl_program; GE (glBindProgram (GL_FRAGMENT_PROGRAM_ARB, gl_program)); - _cogl_use_program (0, COGL_PIPELINE_PROGRAM_TYPE_ARBFP); + _cogl_use_fragment_program (0, COGL_PIPELINE_PROGRAM_TYPE_ARBFP); if (arbfp_program_state->user_program == COGL_INVALID_HANDLE) { diff --git a/clutter/cogl/cogl/cogl-pipeline-fragend-fixed.c b/clutter/cogl/cogl/cogl-pipeline-fragend-fixed.c index a9ab6ca98..031c42416 100644 --- a/clutter/cogl/cogl/cogl-pipeline-fragend-fixed.c +++ b/clutter/cogl/cogl/cogl-pipeline-fragend-fixed.c @@ -42,6 +42,7 @@ #include "cogl-texture-private.h" #include "cogl-blend-string.h" #include "cogl-profile.h" +#include "cogl-program-private.h" #include #include @@ -94,11 +95,16 @@ _cogl_pipeline_fragend_fixed_start (CoglPipeline *pipeline, if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_FIXED)) 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) + if (user_program != COGL_INVALID_HANDLE && + _cogl_program_has_fragment_shader (user_program)) return FALSE; - _cogl_use_program (0, COGL_PIPELINE_PROGRAM_TYPE_FIXED); + _cogl_use_fragment_program (0, COGL_PIPELINE_PROGRAM_TYPE_FIXED); return TRUE; } diff --git a/clutter/cogl/cogl/cogl-pipeline-fragend-glsl.c b/clutter/cogl/cogl/cogl-pipeline-fragend-glsl.c index 07c65e9e7..3622cea2a 100644 --- a/clutter/cogl/cogl/cogl-pipeline-fragend-glsl.c +++ b/clutter/cogl/cogl/cogl-pipeline-fragend-glsl.c @@ -214,9 +214,10 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline, user_program = cogl_pipeline_get_user_program (pipeline); - /* If the program isn't GLSL then we should let another backend - handle it */ + /* If the user fragment shader isn't GLSL then we should let + another backend handle it */ if (user_program && + _cogl_program_has_fragment_shader (user_program) && _cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_GLSL) return FALSE; @@ -609,7 +610,7 @@ append_masked_combine (CoglPipeline *pipeline, case GL_DOT3_RGB: case GL_DOT3_RGBA: - g_string_append (shader_source, "vec4(4 * (("); + g_string_append (shader_source, "vec4(4.0 * (("); add_arg (glsl_shader_state, pipeline, layer, src[0], op[0], "r"); g_string_append (shader_source, " - 0.5) * ("); @@ -777,9 +778,9 @@ _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline, int i, n_layers; COGL_STATIC_COUNTER (fragend_glsl_compile_counter, - "glsl compile counter", + "glsl fragment compile counter", "Increments each time a new GLSL " - "program is compiled", + "fragment shader is compiled", 0 /* no application private data */); COGL_COUNTER_INC (_cogl_uprof_context, fragend_glsl_compile_counter); @@ -790,7 +791,7 @@ _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline, g_string_append (glsl_shader_state->source, "}\n"); if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_SHOW_SOURCE)) - g_message ("pipeline program:\n%s%s", + g_message ("pipeline fragment shader:\n%s%s", glsl_shader_state->header->str, glsl_shader_state->source->str); diff --git a/clutter/cogl/cogl/cogl-pipeline-opengl.c b/clutter/cogl/cogl/cogl-pipeline-opengl.c index f417ba195..83570c4e7 100644 --- a/clutter/cogl/cogl/cogl-pipeline-opengl.c +++ b/clutter/cogl/cogl/cogl-pipeline-opengl.c @@ -251,21 +251,45 @@ _cogl_pipeline_texture_storage_change_notify (CoglHandle texture) * we continue to check the rest */ } } +static void +set_glsl_program (GLuint gl_program) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->current_gl_program != gl_program) + { + GLenum gl_error; + + while ((gl_error = glGetError ()) != GL_NO_ERROR) + ; + glUseProgram (gl_program); + if (glGetError () == GL_NO_ERROR) + ctx->current_gl_program = gl_program; + else + { + GE( glUseProgram (0) ); + ctx->current_gl_program = 0; + } + } +} void -_cogl_use_program (GLuint gl_program, CoglPipelineProgramType type) +_cogl_use_fragment_program (GLuint gl_program, CoglPipelineProgramType type) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* If we're changing program type... */ - if (type != ctx->current_use_program_type) + if (type != ctx->current_fragment_program_type) { /* ... disable the old type */ - switch (ctx->current_use_program_type) + switch (ctx->current_fragment_program_type) { case COGL_PIPELINE_PROGRAM_TYPE_GLSL: - GE( glUseProgram (0) ); - ctx->current_gl_program = 0; + /* If the program contains a vertex shader then we shouldn't + disable it */ + if (ctx->current_vertex_program_type != + COGL_PIPELINE_PROGRAM_TYPE_GLSL) + set_glsl_program (0); break; case COGL_PIPELINE_PROGRAM_TYPE_ARBFP: @@ -298,22 +322,7 @@ _cogl_use_program (GLuint gl_program, CoglPipelineProgramType type) if (type == COGL_PIPELINE_PROGRAM_TYPE_GLSL) { #ifdef COGL_PIPELINE_FRAGEND_GLSL - - if (ctx->current_gl_program != gl_program) - { - GLenum gl_error; - - while ((gl_error = glGetError ()) != GL_NO_ERROR) - ; - glUseProgram (gl_program); - if (glGetError () == GL_NO_ERROR) - ctx->current_gl_program = gl_program; - else - { - GE( glUseProgram (0) ); - ctx->current_gl_program = 0; - } - } + set_glsl_program (gl_program); #else @@ -323,10 +332,73 @@ _cogl_use_program (GLuint gl_program, CoglPipelineProgramType type) } #ifndef COGL_PIPELINE_FRAGEND_ARBFP else if (type == COGL_PIPELINE_PROGRAM_TYPE_ARBFP) - g_warning ("Unexpected use of ARBFP fragend!"); + g_warning ("Unexpected use of ARBFP fragend!"); #endif /* COGL_PIPELINE_FRAGEND_ARBFP */ - ctx->current_use_program_type = type; + ctx->current_fragment_program_type = type; +} + +void +_cogl_use_vertex_program (GLuint gl_program, CoglPipelineProgramType type) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* If we're changing program type... */ + if (type != ctx->current_vertex_program_type) + { + /* ... disable the old type */ + switch (ctx->current_vertex_program_type) + { + case COGL_PIPELINE_PROGRAM_TYPE_GLSL: + /* If the program contains a fragment shader then we shouldn't + disable it */ + if (ctx->current_fragment_program_type != + COGL_PIPELINE_PROGRAM_TYPE_GLSL) + set_glsl_program (0); + break; + + case COGL_PIPELINE_PROGRAM_TYPE_ARBFP: + /* It doesn't make sense to enable ARBfp for the vertex program */ + g_assert_not_reached (); + break; + + case COGL_PIPELINE_PROGRAM_TYPE_FIXED: + /* don't need to to anything */ + break; + } + + /* ... and enable the new type */ + switch (type) + { + case COGL_PIPELINE_PROGRAM_TYPE_ARBFP: + /* It doesn't make sense to enable ARBfp for the vertex program */ + g_assert_not_reached (); + break; + + case COGL_PIPELINE_PROGRAM_TYPE_GLSL: + case COGL_PIPELINE_PROGRAM_TYPE_FIXED: + /* don't need to to anything */ + break; + } + } + + if (type == COGL_PIPELINE_PROGRAM_TYPE_GLSL) + { +#ifdef COGL_PIPELINE_VERTEND_GLSL + set_glsl_program (gl_program); + +#else + + g_warning ("Unexpected use of GLSL vertend!"); + +#endif /* COGL_PIPELINE_VERTEND_GLSL */ + } +#ifndef COGL_PIPELINE_VERTEND_ARBFP + else if (type == COGL_PIPELINE_PROGRAM_TYPE_ARBFP) + g_warning ("Unexpected use of ARBFP vertend!"); +#endif /* COGL_PIPELINE_VERTEND_ARBFP */ + + ctx->current_vertex_program_type = type; } #if defined (COGL_PIPELINE_FRAGEND_GLSL) || \ @@ -519,18 +591,6 @@ _cogl_pipeline_flush_color_blend_alpha_depth_state ( } } - 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( glPointSize (authority->big_state->point_size) ); - ctx->point_size_cache = authority->big_state->point_size; - } - } - if (pipeline->real_blend_enable != ctx->gl_blend_enable_cache) { if (pipeline->real_blend_enable) diff --git a/clutter/cogl/cogl/cogl-pipeline-private.h b/clutter/cogl/cogl/cogl-pipeline-private.h index bd7738294..6caf6a6ff 100644 --- a/clutter/cogl/cogl/cogl-pipeline-private.h +++ b/clutter/cogl/cogl/cogl-pipeline-private.h @@ -71,10 +71,27 @@ typedef struct _CoglPipelineLayer CoglPipelineLayer; #define COGL_PIPELINE_FRAGEND_DEFAULT 0 #define COGL_PIPELINE_FRAGEND_UNDEFINED 3 +#if defined (HAVE_COGL_GL) + +#define COGL_PIPELINE_VERTEND_FIXED 0 +#define COGL_PIPELINE_VERTEND_GLSL 1 + +#define COGL_PIPELINE_N_VERTENDS 2 + +#elif defined (HAVE_COGL_GLES2) + +#define COGL_PIPELINE_VERTEND_GLSL 0 + +#define COGL_PIPELINE_N_VERTENDS 1 + +#else /* HAVE_COGL_GLES */ + #define COGL_PIPELINE_VERTEND_FIXED 0 #define COGL_PIPELINE_N_VERTENDS 1 +#endif + #define COGL_PIPELINE_VERTEND_DEFAULT 0 #define COGL_PIPELINE_VERTEND_UNDEFINED 3 @@ -158,7 +175,9 @@ typedef enum #ifdef HAVE_COGL_GLES2 COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS | #endif - COGL_PIPELINE_LAYER_STATE_UNIT + COGL_PIPELINE_LAYER_STATE_UNIT, + + COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN = 0 } CoglPipelineLayerState; @@ -405,6 +424,10 @@ typedef enum _CoglPipelineState so we can't share programs there */ COGL_PIPELINE_STATE_ALPHA_FUNC | #endif + COGL_PIPELINE_STATE_USER_SHADER, + + COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN = + COGL_PIPELINE_STATE_LAYERS | COGL_PIPELINE_STATE_USER_SHADER } CoglPipelineState; @@ -831,7 +854,10 @@ _cogl_get_max_texture_image_units (void); void -_cogl_use_program (GLuint gl_program, CoglPipelineProgramType type); +_cogl_use_fragment_program (GLuint gl_program, CoglPipelineProgramType type); + +void +_cogl_use_vertex_program (GLuint gl_program, CoglPipelineProgramType type); unsigned int _cogl_get_n_args_for_combine_func (GLint func); diff --git a/clutter/cogl/cogl/cogl-pipeline-progend-glsl.c b/clutter/cogl/cogl/cogl-pipeline-progend-glsl.c index 64424967d..e0216b1f6 100644 --- a/clutter/cogl/cogl/cogl-pipeline-progend-glsl.c +++ b/clutter/cogl/cogl/cogl-pipeline-progend-glsl.c @@ -40,6 +40,7 @@ #include "cogl-handle.h" #include "cogl-program-private.h" #include "cogl-pipeline-fragend-glsl-private.h" +#include "cogl-pipeline-vertend-glsl-private.h" #ifndef HAVE_COGL_GLES2 @@ -86,17 +87,13 @@ typedef struct int n_tex_coord_attribs; #ifdef HAVE_COGL_GLES2 - /* The GLES2 generated program that was generated from the user - program. This is used to detect when the GLES2 backend generates - a different program which would mean we need to flush all of the - custom uniforms. This is a massive hack but it can go away once - this GLSL backend starts generating its own shaders */ - GLuint gles2_program; - /* Under GLES2 the alpha test is implemented in the shader. We need a uniform for the reference value */ gboolean dirty_alpha_test_reference; GLint alpha_test_reference_uniform; + + gboolean dirty_point_size; + GLint point_size_uniform; #endif /* We need to track the last pipeline that the program was used with @@ -297,6 +294,21 @@ update_alpha_test_reference (CoglPipeline *pipeline, } } +static void +update_point_size (CoglPipeline *pipeline, + GLuint gl_program, + CoglPipelineProgendPrivate *priv) +{ + if (priv->dirty_point_size && priv->point_size_uniform != -1) + { + float point_size = cogl_pipeline_get_point_size (pipeline); + + GE( glUniform1f (priv->point_size_uniform, point_size) ); + + priv->dirty_point_size = FALSE; + } +} + #endif /* HAVE_COGL_GLES2 */ static void @@ -314,7 +326,8 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline, /* If neither of the glsl fragend or vertends are used then we don't need to do anything */ - if (pipeline->fragend != COGL_PIPELINE_FRAGEND_GLSL) + if (pipeline->fragend != COGL_PIPELINE_FRAGEND_GLSL && + pipeline->vertend != COGL_PIPELINE_VERTEND_GLSL) return; priv = get_glsl_priv (pipeline); @@ -330,9 +343,11 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline, state */ authority = _cogl_pipeline_find_equivalent_parent (pipeline, - COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN & + (COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN | + COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN) & ~COGL_PIPELINE_STATE_LAYERS, - COGL_PIPELINE_LAYER_STATE_AFFECTS_FRAGMENT_CODEGEN, + COGL_PIPELINE_LAYER_STATE_AFFECTS_FRAGMENT_CODEGEN | + COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN, COGL_PIPELINE_FIND_EQUIVALENT_COMPARE_TEXTURE_TARGET); priv = get_glsl_priv (authority); @@ -345,9 +360,6 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline, priv->n_tex_coord_attribs = 0; priv->unit_state = g_new (UnitState, cogl_pipeline_get_n_layers (pipeline)); -#ifdef HAVE_COGL_GLES2 - priv->gles2_program = 0; -#endif set_glsl_priv (authority, priv); } @@ -417,6 +429,9 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline, if (pipeline->fragend == COGL_PIPELINE_FRAGEND_GLSL && (backend_shader = _cogl_pipeline_fragend_glsl_get_shader (pipeline))) GE( glAttachShader (priv->program, backend_shader) ); + if (pipeline->vertend == COGL_PIPELINE_VERTEND_GLSL && + (backend_shader = _cogl_pipeline_vertend_glsl_get_shader (pipeline))) + GE( glAttachShader (priv->program, backend_shader) ); link_program (priv->program); @@ -432,16 +447,12 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline, work. It should only be neccessary until we move the GLSL vertex shader generation into a vertend instead of the GLES2 driver backend */ - gl_program = _cogl_gles2_use_program (gl_program); - /* We need to detect when the GLES2 backend gives us a different - program from last time */ - if (gl_program != priv->gles2_program) - { - priv->gles2_program = gl_program; - program_changed = TRUE; - } + _cogl_gles2_use_program (gl_program); #else - _cogl_use_program (gl_program, COGL_PIPELINE_PROGRAM_TYPE_GLSL); + if (pipeline->fragend == COGL_PIPELINE_FRAGEND_GLSL) + _cogl_use_fragment_program (gl_program, COGL_PIPELINE_PROGRAM_TYPE_GLSL); + if (pipeline->vertend == COGL_PIPELINE_VERTEND_GLSL) + _cogl_use_vertex_program (gl_program, COGL_PIPELINE_PROGRAM_TYPE_GLSL); #endif state.unit = 0; @@ -463,14 +474,24 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline, #ifdef HAVE_COGL_GLES2 if (program_changed) - GE_RET( priv->alpha_test_reference_uniform, - glGetUniformLocation (gl_program, - "_cogl_alpha_test_ref") ); + { + GE_RET( priv->alpha_test_reference_uniform, + glGetUniformLocation (gl_program, + "_cogl_alpha_test_ref") ); + + GE_RET( priv->point_size_uniform, + glGetUniformLocation (gl_program, + "cogl_point_size_in") ); + } if (program_changed || priv->last_used_for_pipeline != pipeline) - priv->dirty_alpha_test_reference = TRUE; + { + priv->dirty_alpha_test_reference = TRUE; + priv->dirty_point_size = TRUE; + } update_alpha_test_reference (pipeline, gl_program, priv); + update_point_size (pipeline, gl_program, priv); #endif if (user_program) @@ -490,14 +511,20 @@ _cogl_pipeline_progend_glsl_pre_change_notify (CoglPipeline *pipeline, { if ((change & COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN)) dirty_glsl_program_state (pipeline); -#ifdef COGL_HAS_GLES2 +#ifdef HAVE_COGL_GLES2 else if ((change & COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE)) { CoglPipelineProgendPrivate *priv = get_glsl_priv (pipeline); if (priv) priv->dirty_alpha_test_reference = TRUE; } -#endif + else if ((change & COGL_PIPELINE_STATE_POINT_SIZE)) + { + CoglPipelineProgendPrivate *priv = get_glsl_priv (pipeline); + if (priv) + priv->dirty_point_size = TRUE; + } +#endif /* HAVE_COGL_GLES2 */ } /* NB: layers are considered immutable once they have any dependants diff --git a/clutter/cogl/cogl/cogl-pipeline-vertend-fixed.c b/clutter/cogl/cogl/cogl-pipeline-vertend-fixed.c index 8e5a85b9a..8c286ecc3 100644 --- a/clutter/cogl/cogl/cogl-pipeline-vertend-fixed.c +++ b/clutter/cogl/cogl/cogl-pipeline-vertend-fixed.c @@ -38,6 +38,7 @@ #include "cogl-internal.h" #include "cogl-context.h" #include "cogl-handle.h" +#include "cogl-program-private.h" const CoglPipelineVertend _cogl_pipeline_fixed_vertend; @@ -46,9 +47,22 @@ _cogl_pipeline_vertend_fixed_start (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference) { + CoglProgram *user_program; + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_FIXED)) return FALSE; + /* If there is a user program with a vertex shader then the + appropriate backend for that language should handle it. We can + still use the fixed vertex backend if the program only contains + a fragment shader */ + user_program = cogl_pipeline_get_user_program (pipeline); + if (user_program != COGL_INVALID_HANDLE && + _cogl_program_has_vertex_shader (user_program)) + return FALSE; + + _cogl_use_vertex_program (0, COGL_PIPELINE_PROGRAM_TYPE_FIXED); + return TRUE; } @@ -79,6 +93,8 @@ static gboolean _cogl_pipeline_vertend_fixed_end (CoglPipeline *pipeline, unsigned long pipelines_difference) { + _COGL_GET_CONTEXT (ctx, FALSE); + if (pipelines_difference & COGL_PIPELINE_STATE_LIGHTING) { CoglPipeline *authority = @@ -98,6 +114,18 @@ _cogl_pipeline_vertend_fixed_end (CoglPipeline *pipeline, &lighting_state->shininess)); } + 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( glPointSize (authority->big_state->point_size) ); + ctx->point_size_cache = authority->big_state->point_size; + } + } + return TRUE; } diff --git a/clutter/cogl/cogl/cogl-pipeline-vertend-glsl-private.h b/clutter/cogl/cogl/cogl-pipeline-vertend-glsl-private.h new file mode 100644 index 000000000..52a078c82 --- /dev/null +++ b/clutter/cogl/cogl/cogl-pipeline-vertend-glsl-private.h @@ -0,0 +1,39 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2010 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H +#define __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineVertend _cogl_pipeline_glsl_vertend; + +GLuint +_cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline); + +#endif /* __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H */ + diff --git a/clutter/cogl/cogl/cogl-pipeline-vertend-glsl.c b/clutter/cogl/cogl/cogl-pipeline-vertend-glsl.c new file mode 100644 index 000000000..7e5ae291f --- /dev/null +++ b/clutter/cogl/cogl/cogl-pipeline-vertend-glsl.c @@ -0,0 +1,396 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2010 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" + +#ifdef COGL_PIPELINE_VERTEND_GLSL + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-program-private.h" +#include "cogl-pipeline-vertend-glsl-private.h" + +#ifndef HAVE_COGL_GLES2 + +#define glCreateShader ctx->drv.pf_glCreateShader +#define glGetShaderiv ctx->drv.pf_glGetShaderiv +#define glGetShaderInfoLog ctx->drv.pf_glGetShaderInfoLog +#define glCompileShader ctx->drv.pf_glCompileShader +#define glShaderSource ctx->drv.pf_glShaderSource +#define glDeleteShader ctx->drv.pf_glDeleteShader + +#endif /* HAVE_COGL_GLES2 */ + +const CoglPipelineVertend _cogl_pipeline_glsl_vertend; + +typedef struct +{ + unsigned int ref_count; + + GLuint gl_shader; + GString *header, *source; + + /* Age of the user program that was current when the shader was + generated. We need to keep track of this because if the user + program changes then we may need to redecide whether to generate + a shader at all */ + unsigned int user_program_age; +} CoglPipelineVertendPrivate; + +static CoglUserDataKey glsl_priv_key; + +static CoglPipelineVertendPrivate * +get_glsl_priv (CoglPipeline *pipeline) +{ + return cogl_object_get_user_data (COGL_OBJECT (pipeline), &glsl_priv_key); +} + +static void +destroy_glsl_priv (void *user_data) +{ + CoglPipelineVertendPrivate *priv = user_data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (--priv->ref_count == 0) + { + if (priv->gl_shader) + GE( glDeleteShader (priv->gl_shader) ); + + g_slice_free (CoglPipelineVertendPrivate, priv); + } +} + +static void +set_glsl_priv (CoglPipeline *pipeline, CoglPipelineVertendPrivate *priv) +{ + cogl_object_set_user_data (COGL_OBJECT (pipeline), + &glsl_priv_key, + priv, + destroy_glsl_priv); +} + +static void +dirty_glsl_shader_state (CoglPipeline *pipeline) +{ + cogl_object_set_user_data (COGL_OBJECT (pipeline), + &glsl_priv_key, + NULL, + destroy_glsl_priv); +} + +GLuint +_cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline) +{ + CoglPipelineVertendPrivate *priv = get_glsl_priv (pipeline); + + if (priv) + return priv->gl_shader; + else + return 0; +} + +static gboolean +_cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference) +{ + CoglPipelineVertendPrivate *priv; + 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) */ + priv = get_glsl_priv (pipeline); + + if (priv == 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, + 0); + + priv = get_glsl_priv (authority); + + if (priv == NULL) + { + priv = g_slice_new0 (CoglPipelineVertendPrivate); + priv->ref_count = 1; + set_glsl_priv (authority, priv); + } + + if (authority != pipeline) + { + priv->ref_count++; + set_glsl_priv (pipeline, priv); + } + } + + if (priv->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 || + priv->user_program_age == user_program->age) + return TRUE; + + /* We need to recreate the shader so destroy the existing one */ + GE( glDeleteShader (priv->gl_shader) ); + priv->gl_shader = 0; + } + + /* If we make it here then we have a priv 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) + priv->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); + priv->header = ctx->codegen_header_buffer; + priv->source = ctx->codegen_source_buffer; + + g_string_append (priv->source, + "void\n" + "main ()\n" + "{\n"); + + return TRUE; +} + +static gboolean +_cogl_pipeline_vertend_glsl_add_layer (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference) +{ + CoglPipelineVertendPrivate *priv; + int unit_index; + + priv = get_glsl_priv (pipeline); + + unit_index = _cogl_pipeline_layer_get_unit_index (layer); + + /* We are using the fixed function uniforms for the user matrices + and the only way to set them is with the fixed function API so we + still need to flush them here */ + if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX) + { + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, state); + CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); + + _cogl_matrix_stack_set (unit->matrix_stack, + &authority->big_state->matrix); + + _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE); + } + + if (priv->source == NULL) + return TRUE; + + /* Transform the texture coordinates by the layer's user matrix. + * + * FIXME: this should avoid doing the transform if there is no user + * matrix set. This might need a separate layer state flag for + * whether there is a user matrix + * + * FIXME: we could be more clever here and try to detect if the + * fragment program is going to use the texture coordinates and + * avoid setting them if not + */ + + g_string_append_printf (priv->source, + " cogl_tex_coord_out[%i] = " + "cogl_texture_matrix[%i] * cogl_tex_coord%i_in;\n", + unit_index, unit_index, unit_index); + + return TRUE; +} + +static gboolean +_cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, + unsigned long pipelines_difference) +{ + CoglPipelineVertendPrivate *priv; + + _COGL_GET_CONTEXT (ctx, FALSE); + + priv = get_glsl_priv (pipeline); + + if (priv->source) + { + const char *source_strings[2]; + GLint lengths[2]; + GLint compile_status; + GLuint shader; + int n_layers; + + COGL_STATIC_COUNTER (vertend_glsl_compile_counter, + "glsl vertex compile counter", + "Increments each time a new GLSL " + "vertex shader is compiled", + 0 /* no application private data */); + COGL_COUNTER_INC (_cogl_uprof_context, vertend_glsl_compile_counter); + + g_string_append (priv->source, + " cogl_position_out = " + "cogl_modelview_projection_matrix * " + "cogl_position_in;\n" + " cogl_color_out = cogl_color_in;\n" + "}\n"); + + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_SHOW_SOURCE)) + g_message ("pipeline vertex shader:\n%s%s", + priv->header->str, + priv->source->str); + + GE_RET( shader, glCreateShader (GL_VERTEX_SHADER) ); + + lengths[0] = priv->header->len; + source_strings[0] = priv->header->str; + lengths[1] = priv->source->len; + source_strings[1] = priv->source->str; + + n_layers = cogl_pipeline_get_n_layers (pipeline); + + _cogl_shader_set_source_with_boilerplate (shader, GL_VERTEX_SHADER, + n_layers, + 2, /* count */ + source_strings, lengths); + + GE( glCompileShader (shader) ); + GE( glGetShaderiv (shader, GL_COMPILE_STATUS, &compile_status) ); + + if (!compile_status) + { + GLint len = 0; + char *shader_log; + + GE( glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &len) ); + shader_log = g_alloca (len); + GE( glGetShaderInfoLog (shader, len, &len, shader_log) ); + g_warning ("Shader compilation failed:\n%s", shader_log); + } + + priv->header = NULL; + priv->source = NULL; + priv->gl_shader = shader; + } + + return TRUE; +} + +static void +_cogl_pipeline_vertend_glsl_pre_change_notify (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color) +{ + if ((change & COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN)) + dirty_glsl_shader_state (pipeline); +} + +/* NB: layers are considered immutable once they have any dependants + * so although multiple pipelines can end up depending on a single + * static layer, we can guarantee that if a layer is being *changed* + * then it can only have one pipeline depending on it. + * + * XXX: Don't forget this is *pre* change, we can't read the new value + * yet! + */ +static void +_cogl_pipeline_vertend_glsl_layer_pre_change_notify ( + CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change) +{ + CoglPipelineVertendPrivate *priv; + + priv = get_glsl_priv (owner); + if (!priv) + return; + + if ((change & COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN)) + { + dirty_glsl_shader_state (owner); + return; + } + + /* TODO: we could be saving snippets of texture combine code along + * with each layer and then when a layer changes we would just free + * the snippet. */ +} + +const CoglPipelineVertend _cogl_pipeline_glsl_vertend = + { + _cogl_pipeline_vertend_glsl_start, + _cogl_pipeline_vertend_glsl_add_layer, + _cogl_pipeline_vertend_glsl_end, + _cogl_pipeline_vertend_glsl_pre_change_notify, + _cogl_pipeline_vertend_glsl_layer_pre_change_notify + }; + +#endif /* COGL_PIPELINE_VERTEND_GLSL */ diff --git a/clutter/cogl/cogl/cogl-pipeline.c b/clutter/cogl/cogl/cogl-pipeline.c index c2e490251..3c139ac81 100644 --- a/clutter/cogl/cogl/cogl-pipeline.c +++ b/clutter/cogl/cogl/cogl-pipeline.c @@ -83,6 +83,9 @@ _cogl_pipeline_progends[MAX (COGL_PIPELINE_N_PROGENDS, 1)]; #include "cogl-pipeline-progend-glsl-private.h" #endif +#ifdef COGL_PIPELINE_VERTEND_GLSL +#include "cogl-pipeline-vertend-glsl-private.h" +#endif #ifdef COGL_PIPELINE_VERTEND_FIXED #include "cogl-pipeline-vertend-fixed-private.h" #endif @@ -229,6 +232,10 @@ _cogl_pipeline_init_default_pipeline (void) &_cogl_pipeline_glsl_progend; #endif +#ifdef COGL_PIPELINE_VERTEND_GLSL + _cogl_pipeline_vertends[COGL_PIPELINE_VERTEND_GLSL] = + &_cogl_pipeline_glsl_vertend; +#endif #ifdef COGL_PIPELINE_VERTEND_FIXED _cogl_pipeline_vertends[COGL_PIPELINE_VERTEND_FIXED] = &_cogl_pipeline_fixed_vertend; diff --git a/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.c b/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.c index 69540075a..437702fed 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.c +++ b/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.c @@ -345,9 +345,10 @@ cogl_gles2_wrapper_get_program (const CoglGles2WrapperSettings *settings) } } - /* We should always have a custom fragment shader because the - pipeline backend should create one for us */ + /* We should always have a custom shaders because the pipeline + backend should create them for us */ g_assert (custom_fragment_shader); + g_assert (custom_vertex_shader); /* Get or create the fixed functionality shaders for these settings if there is no custom replacement */ @@ -360,12 +361,9 @@ cogl_gles2_wrapper_get_program (const CoglGles2WrapperSettings *settings) program = g_slice_new (CoglGles2WrapperProgram); - program->program = glCreateProgram (); + program->program = settings->user_program; if (!custom_vertex_shader) glAttachShader (program->program, vertex_shader->shader); - /* Attach all the shaders stolen from the user program */ - for (i = 0; i < n_shaders; i++) - glAttachShader (program->program, shaders[i]); cogl_gles2_wrapper_bind_attributes (program->program); glLinkProgram (program->program); @@ -381,7 +379,6 @@ cogl_gles2_wrapper_get_program (const CoglGles2WrapperSettings *settings) g_critical ("%s", shader_log); - glDeleteProgram (program->program); g_slice_free (CoglGles2WrapperProgram, program); return NULL; @@ -407,7 +404,6 @@ _cogl_gles2_wrapper_deinit (CoglGles2Wrapper *wrapper) for (node = wrapper->compiled_programs; node; node = next) { next = node->next; - glDeleteProgram (((CoglGles2WrapperProgram *) node->data)->program); g_slist_free1 (node); } wrapper->compiled_programs = NULL; @@ -586,7 +582,10 @@ _cogl_wrap_prepare_for_draw (void) again in the _start function. This should go away once the GLSL code is generated in the GLSL material backend so it's probably not worth worrying about now */ - _cogl_use_program (program->program, COGL_PIPELINE_PROGRAM_TYPE_GLSL); + _cogl_use_fragment_program (w->settings.user_program, + COGL_PIPELINE_PROGRAM_TYPE_GLSL); + _cogl_use_vertex_program (w->settings.user_program, + COGL_PIPELINE_PROGRAM_TYPE_GLSL); /* Make sure all of the uniforms are up to date */ if (w->dirty_uniforms) @@ -979,8 +978,6 @@ _cogl_gles2_clear_cache_for_program (GLuint gl_program) if (program->settings.user_program == gl_program) { - glDeleteProgram (program->program); - if (last) last->next = next; else