diff options
author | Robert Bragg <robert@linux.intel.com> | 2010-07-23 17:46:41 +0100 |
---|---|---|
committer | Robert Bragg <robert@linux.intel.com> | 2010-11-10 14:24:52 +0000 |
commit | 353ea5299b40df57e1b20dccba68a065fa1a47d3 (patch) | |
tree | 80b2239dbaf4733b53c50ac3321e55ec8a103abc /cogl/cogl-shader.c | |
parent | 7a4c7b41c213de3eca8895084d59a16b567722c9 (diff) | |
download | cogl-353ea5299b40df57e1b20dccba68a065fa1a47d3.tar.gz |
cogl-shader: Prepend boilerplate for portable shaders
We now prepend a set of defines to any given GLSL shader so that we can
define builtin uniforms/attributes within the "cogl" namespace that we
can use to provide compatibility across a range of the earlier versions
of GLSL.
This updates test-cogl-shader-glsl.c and test-shader.c so they no longer
needs to special case GLES vs GL when splicing together its shaders as
well as the blur, colorize and desaturate effects.
To get a feel for the new, portable uniform/attribute names here are the
defines for OpenGL vertex shaders:
#define cogl_position_in gl_Vertex
#define cogl_color_in gl_Color
#define cogl_tex_coord_in gl_MultiTexCoord0
#define cogl_tex_coord0_in gl_MultiTexCoord0
#define cogl_tex_coord1_in gl_MultiTexCoord1
#define cogl_tex_coord2_in gl_MultiTexCoord2
#define cogl_tex_coord3_in gl_MultiTexCoord3
#define cogl_tex_coord4_in gl_MultiTexCoord4
#define cogl_tex_coord5_in gl_MultiTexCoord5
#define cogl_tex_coord6_in gl_MultiTexCoord6
#define cogl_tex_coord7_in gl_MultiTexCoord7
#define cogl_normal_in gl_Normal
#define cogl_position_out gl_Position
#define cogl_point_size_out gl_PointSize
#define cogl_color_out gl_FrontColor
#define cogl_tex_coord_out gl_TexCoord
#define cogl_modelview_matrix gl_ModelViewMatrix
#define cogl_modelview_projection_matrix gl_ModelViewProjectionMatrix
#define cogl_projection_matrix gl_ProjectionMatrix
#define cogl_texture_matrix gl_TextureMatrix
And for fragment shaders we have:
#define cogl_color_in gl_Color
#define cogl_tex_coord_in gl_TexCoord
#define cogl_color_out gl_FragColor
#define cogl_depth_out gl_FragDepth
#define cogl_front_facing gl_FrontFacing
Diffstat (limited to 'cogl/cogl-shader.c')
-rw-r--r-- | cogl/cogl-shader.c | 206 |
1 files changed, 158 insertions, 48 deletions
diff --git a/cogl/cogl-shader.c b/cogl/cogl-shader.c index 6391ea52..b58cbe68 100644 --- a/cogl/cogl-shader.c +++ b/cogl/cogl-shader.c @@ -27,6 +27,7 @@ #include "cogl.h" #include "cogl-shader-private.h" +#include "cogl-shader-boilerplate.h" #include "cogl-internal.h" #include "cogl-context.h" #include "cogl-handle.h" @@ -100,11 +101,35 @@ cogl_create_shader (CoglShaderType type) shader = g_slice_new (CoglShader); shader->language = COGL_SHADER_LANGUAGE_GLSL; shader->gl_handle = 0; +#ifdef HAVE_COGL_GLES2 + shader->n_tex_coord_attribs = 0; +#endif shader->type = type; return _cogl_shader_handle_new (shader); } +static void +delete_shader (CoglShader *shader) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + +#ifdef HAVE_COGL_GL + if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) + { + if (shader->gl_handle) + GE (glDeletePrograms (1, &shader->gl_handle)); + } + else +#endif + { + if (shader->gl_handle) + GE (glDeleteShader (shader->gl_handle)); + } + + shader->gl_handle = 0; +} + void cogl_shader_source (CoglHandle handle, const char *source) @@ -127,29 +152,55 @@ cogl_shader_source (CoglHandle handle, language = COGL_SHADER_LANGUAGE_GLSL; /* Delete the old object if the language is changing... */ - if (G_UNLIKELY (language != shader->language)) - { + if (G_UNLIKELY (language != shader->language) && + shader->gl_handle) + delete_shader (shader); + + shader->source = g_strdup (source); + + shader->language = language; +} + +void +cogl_shader_compile (CoglHandle handle) +{ + CoglShader *shader = handle; + + if (!cogl_is_shader (handle)) + return; + #ifdef HAVE_COGL_GL - if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) - { - if (shader->gl_handle) - GE (glDeletePrograms (1, &shader->gl_handle)); - } - else + _cogl_shader_compile_real (shader, 0 /* ignored */); #endif - { - if (shader->gl_handle) - GE (glDeleteShader (shader->gl_handle)); - } - } + + /* XXX: For GLES2 we don't actually compile anything until the + * shader gets used so we have an opportunity to add some + * boilerplate to the shader. + * + * At the end of the day this is obviously a badly designed API + * given that we are having to lie to the user. It was a mistake to + * so thinly wrap the OpenGL shader API and the current plan is to + * replace it with a pipeline snippets API. */ +} + +void +_cogl_shader_compile_real (CoglHandle handle, + int n_tex_coord_attribs) +{ + CoglShader *shader = handle; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); #ifdef HAVE_COGL_GL - if (language == COGL_SHADER_LANGUAGE_ARBFP) + if (shader->language == COGL_SHADER_LANGUAGE_ARBFP) { #ifdef COGL_GL_DEBUG GLenum gl_error; #endif + if (shader->gl_handle) + return; + GE (glGenPrograms (1, &shader->gl_handle)); GE (glBindProgram (GL_FRAGMENT_PROGRAM_ARB, shader->gl_handle)); @@ -160,8 +211,8 @@ cogl_shader_source (CoglHandle handle, #endif glProgramString (GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, - strlen (source), - source); + strlen (shader->source), + shader->source); #ifdef COGL_GL_DEBUG gl_error = glGetError (); if (gl_error != GL_NO_ERROR) @@ -169,7 +220,7 @@ cogl_shader_source (CoglHandle handle, g_warning ("%s: GL error (%d): Failed to compile ARBfp:\n%s\n%s", G_STRLOC, gl_error, - source, + shader->source, glGetString (GL_PROGRAM_ERROR_STRING_ARB)); } #endif @@ -177,44 +228,70 @@ cogl_shader_source (CoglHandle handle, else #endif { - if (!shader->gl_handle) + char *sourcev[4]; + int count = 0; + GLenum gl_type; + + if (shader->gl_handle +#ifdef HAVE_COGL_GLES2 + && shader->n_tex_coord_attribs >= n_tex_coord_attribs +#endif + ) + return; + + if (shader->gl_handle) + delete_shader (shader); + + switch (shader->type) { - GLenum gl_type; - - 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 = glCreateShader (gl_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; } - glShaderSource (shader->gl_handle, 1, &source, NULL); - } - shader->language = language; -} + shader->gl_handle = glCreateShader (gl_type); -void -cogl_shader_compile (CoglHandle handle) -{ - CoglShader *shader; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + sourcev[count++] = _COGL_COMMON_SHADER_BOILERPLATE; + if (shader->type == COGL_SHADER_TYPE_VERTEX) + sourcev[count++] = _COGL_VERTEX_SHADER_BOILERPLATE; + else + sourcev[count++] = _COGL_FRAGMENT_SHADER_BOILERPLATE; + +#ifdef HAVE_COGL_GLES2 + if (n_tex_coord_attribs) + sourcev[count++] = + g_strdup_printf ("varying vec2 _cogl_tex_coord[%d];\n", + n_tex_coord_attribs); + shader->n_tex_coord_attribs = n_tex_coord_attribs; +#endif - if (!cogl_is_shader (handle)) - return; + sourcev[count++] = shader->source; - shader = _cogl_shader_pointer_from_handle (handle); + glShaderSource (shader->gl_handle, count, (const char **)sourcev, NULL); + +#ifdef HAVE_COGL_GLES2 + if (count == 4) + g_free (sourcev[2]); +#endif + + GE (glCompileShader (shader->gl_handle)); - if (shader->language == COGL_SHADER_LANGUAGE_GLSL) - GE (glCompileShader (shader->gl_handle)); +#ifdef COGL_GL_DEBUG + if (!cogl_shader_is_compiled (handle)) + { + char *log = cogl_shader_get_info_log (handle); + g_warning ("Failed to compile GLSL program:\nsrc:\n%s\nerror:\n%s\n", + shader->source, + log); + } +#endif + } } char * @@ -242,6 +319,21 @@ cogl_shader_get_info_log (CoglHandle handle) { char buffer[512]; int len = 0; + + /* We don't normally compile the shader when the user calls + * cogl_shader_compile() because we want to be able to add + * boilerplate code that depends on how it ends up finally being + * used. + * + * Here we force an early compile if the user is interested in + * log information to increase the chance that the log will be + * useful! We have to guess the number of texture coordinate + * attributes that may be used (normally less than 4) since that + * affects the boilerplate. + */ + if (!shader->gl_handle) + _cogl_shader_compile_real (handle, 4); + glGetShaderInfoLog (shader->gl_handle, 511, &len, buffer); buffer[len] = '\0'; return g_strdup (buffer); @@ -284,6 +376,24 @@ cogl_shader_is_compiled (CoglHandle handle) else #endif { + /* FIXME: We currently have an arbitrary limit of 4 texture + * coordinate attributes since our API means we have to add + * some boilerplate to the users GLSL program (for GLES2) + * before we actually know how many attributes are in use. + * + * 4 will probably be enough (or at least that limitation should + * be enough until we can replace this API with the pipeline + * snippets API) but if it isn't then the shader won't compile, + * through no fault of the user. + * + * To some extent this is just a symptom of bad API design; it + * was a mistake for Cogl to so thinly wrap the OpenGL shader + * API. Eventually we plan for this whole API will be deprecated + * by the pipeline snippets framework. + */ + if (!shader->gl_handle) + _cogl_shader_compile_real (handle, 4); + GE (glGetShaderiv (shader->gl_handle, GL_COMPILE_STATUS, &status)); if (status == GL_TRUE) return TRUE; |