Generate GLSL in the CoglPipeline GLSL backend

The GLSL pipeline backend can now generate code to represent the
pipeline state in a similar way to the ARBfp backend. Most of the code
for this is taken from the GLES 2 wrapper.
This commit is contained in:
Neil Roberts 2010-11-19 10:44:27 +00:00
parent 6246c2bd6c
commit 3fa73735c0
3 changed files with 710 additions and 80 deletions

View File

@ -132,6 +132,7 @@ cogl_create_context (void)
_context->opaque_color_pipeline = cogl_pipeline_new (); _context->opaque_color_pipeline = cogl_pipeline_new ();
_context->blended_color_pipeline = cogl_pipeline_new (); _context->blended_color_pipeline = cogl_pipeline_new ();
_context->texture_pipeline = cogl_pipeline_new (); _context->texture_pipeline = cogl_pipeline_new ();
_context->fragment_header_buffer = g_string_new ("");
_context->fragment_source_buffer = g_string_new (""); _context->fragment_source_buffer = g_string_new ("");
_context->source_stack = NULL; _context->source_stack = NULL;

View File

@ -86,6 +86,7 @@ typedef struct
CoglPipeline *opaque_color_pipeline; /* used for set_source_color */ CoglPipeline *opaque_color_pipeline; /* used for set_source_color */
CoglPipeline *blended_color_pipeline; /* used for set_source_color */ CoglPipeline *blended_color_pipeline; /* used for set_source_color */
CoglPipeline *texture_pipeline; /* used for set_source_texture */ CoglPipeline *texture_pipeline; /* used for set_source_texture */
GString *fragment_header_buffer;
GString *fragment_source_buffer; GString *fragment_source_buffer;
GList *source_stack; GList *source_stack;

View File

@ -29,8 +29,11 @@
#include "config.h" #include "config.h"
#endif #endif
#include <string.h>
#include "cogl-pipeline-private.h" #include "cogl-pipeline-private.h"
#include "cogl-shader-private.h" #include "cogl-shader-private.h"
#include "cogl-blend-string.h"
#ifdef COGL_PIPELINE_BACKEND_GLSL #ifdef COGL_PIPELINE_BACKEND_GLSL
@ -50,6 +53,15 @@
#define glDeleteProgram ctx->drv.pf_glDeleteProgram #define glDeleteProgram ctx->drv.pf_glDeleteProgram
#define glGetProgramInfoLog ctx->drv.pf_glGetProgramInfoLog #define glGetProgramInfoLog ctx->drv.pf_glGetProgramInfoLog
#define glGetProgramiv ctx->drv.pf_glGetProgramiv #define glGetProgramiv ctx->drv.pf_glGetProgramiv
#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
#define glGetUniformLocation ctx->drv.pf_glGetUniformLocation
#define glUniform1i ctx->drv.pf_glUniform1i
#define glUniform4fv ctx->drv.pf_glUniform4fv
#endif /* HAVE_COGL_GLES2 */ #endif /* HAVE_COGL_GLES2 */
@ -63,6 +75,20 @@
#include "../gles/cogl-gles2-wrapper.h" #include "../gles/cogl-gles2-wrapper.h"
#endif #endif
/* This might not be defined on GLES */
#ifndef GL_TEXTURE_3D
#define GL_TEXTURE_3D 0x806F
#endif
typedef struct _UnitState
{
unsigned int sampled:1;
unsigned int combine_constant_used:1;
unsigned int dirty_combine_constant:1;
GLint combine_constant_uniform;
} UnitState;
typedef struct _GlslProgramState typedef struct _GlslProgramState
{ {
int ref_count; int ref_count;
@ -72,8 +98,9 @@ typedef struct _GlslProgramState
program */ program */
unsigned int user_program_age; unsigned int user_program_age;
GLuint gl_program; GLuint gl_program;
GString *header, *source;
UnitState *unit_state;
#ifdef HAVE_COGL_GLES2
/* To allow writing shaders that are portable between GLES 2 and /* To allow writing shaders that are portable between GLES 2 and
* OpenGL Cogl prepends a number of boilerplate #defines and * OpenGL Cogl prepends a number of boilerplate #defines and
* declarations to user shaders. One of those declarations is an * declarations to user shaders. One of those declarations is an
@ -82,11 +109,6 @@ typedef struct _GlslProgramState
* attributes are in use. The boilerplate also needs to be changed * attributes are in use. The boilerplate also needs to be changed
* if this increases. */ * if this increases. */
int n_tex_coord_attribs; int n_tex_coord_attribs;
#endif
/* This is set to TRUE if the program has changed since we last
flushed the uniforms */
gboolean gl_program_changed;
#ifdef HAVE_COGL_GLES2 #ifdef HAVE_COGL_GLES2
/* The GLES2 generated program that was generated from the user /* The GLES2 generated program that was generated from the user
@ -96,6 +118,10 @@ typedef struct _GlslProgramState
this GLSL backend starts generating its own shaders */ this GLSL backend starts generating its own shaders */
GLuint gles2_program; GLuint gles2_program;
#endif #endif
/* We need to track the last pipeline that the program was used with
* so know if we need to update all of the uniforms */
CoglPipeline *last_used_for_pipeline;
} GlslProgramState; } GlslProgramState;
typedef struct _CoglPipelineBackendGlslPrivate typedef struct _CoglPipelineBackendGlslPrivate
@ -117,6 +143,7 @@ glsl_program_state_new (int n_layers)
GlslProgramState *state = g_slice_new0 (GlslProgramState); GlslProgramState *state = g_slice_new0 (GlslProgramState);
state->ref_count = 1; state->ref_count = 1;
state->unit_state = g_new0 (UnitState, n_layers);
return state; return state;
} }
@ -158,23 +185,12 @@ glsl_program_state_unref (GlslProgramState *state)
state->gl_program = 0; state->gl_program = 0;
} }
g_free (state->unit_state);
g_slice_free (GlslProgramState, state); g_slice_free (GlslProgramState, state);
} }
} }
/* This tries to find the oldest ancestor whos state would generate
* the same glsl program as the current pipeline. This is a simple
* mechanism for reducing the number of glsl programs we have to
* generate.
*/
static CoglPipeline *
find_glsl_authority (CoglPipeline *pipeline, CoglHandle user_program)
{
/* Find the first pipeline that modifies the user shader */
return _cogl_pipeline_get_authority (pipeline,
COGL_PIPELINE_STATE_USER_SHADER);
}
static CoglPipelineBackendGlslPrivate * static CoglPipelineBackendGlslPrivate *
get_glsl_priv (CoglPipeline *pipeline) get_glsl_priv (CoglPipeline *pipeline)
{ {
@ -270,8 +286,8 @@ _cogl_pipeline_backend_glsl_start (CoglPipeline *pipeline,
CoglPipeline *authority; CoglPipeline *authority;
CoglPipelineBackendGlslPrivate *authority_priv; CoglPipelineBackendGlslPrivate *authority_priv;
CoglProgram *user_program; CoglProgram *user_program;
GLuint gl_program;
GSList *l; GSList *l;
int i;
_COGL_GET_CONTEXT (ctx, FALSE); _COGL_GET_CONTEXT (ctx, FALSE);
@ -279,9 +295,9 @@ _cogl_pipeline_backend_glsl_start (CoglPipeline *pipeline,
return FALSE; return FALSE;
user_program = cogl_pipeline_get_user_program (pipeline); user_program = cogl_pipeline_get_user_program (pipeline);
if (user_program == COGL_INVALID_HANDLE || if (user_program &&
_cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_GLSL) _cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_GLSL)
return FALSE; /* XXX: change me when we support code generation here */ return FALSE;
/* Now lookup our glsl backend private state (allocating if /* Now lookup our glsl backend private state (allocating if
* necessary) */ * necessary) */
@ -302,12 +318,13 @@ _cogl_pipeline_backend_glsl_start (CoglPipeline *pipeline,
* Also if the number of texture coordinate attributes in use has * Also if the number of texture coordinate attributes in use has
* increased, then delete the program so we can prepend a new * increased, then delete the program so we can prepend a new
* _cogl_tex_coord[] varying array declaration. */ * _cogl_tex_coord[] varying array declaration. */
if (priv->glsl_program_state->user_program_age == user_program->age if (user_program == NULL ||
(priv->glsl_program_state->user_program_age == user_program->age
#ifdef HAVE_COGL_GLES2 #ifdef HAVE_COGL_GLES2
&& priv->glsl_program_state->n_tex_coord_attribs >= && (priv->glsl_program_state->n_tex_coord_attribs >=
n_tex_coord_attribs n_tex_coord_attribs)
#endif #endif
) ))
return TRUE; return TRUE;
/* Destroy the existing program. We can't just dirty the whole /* Destroy the existing program. We can't just dirty the whole
@ -326,7 +343,8 @@ _cogl_pipeline_backend_glsl_start (CoglPipeline *pipeline,
* glsl-authority to maximize the chance that other pipelines can * glsl-authority to maximize the chance that other pipelines can
* share it. * share it.
*/ */
authority = find_glsl_authority (pipeline, user_program); authority =
_cogl_pipeline_find_codegen_authority (pipeline, user_program);
authority_priv = get_glsl_priv (authority); authority_priv = get_glsl_priv (authority);
if (!authority_priv) if (!authority_priv)
{ {
@ -342,6 +360,7 @@ _cogl_pipeline_backend_glsl_start (CoglPipeline *pipeline,
GlslProgramState *glsl_program_state = GlslProgramState *glsl_program_state =
glsl_program_state_new (n_layers); glsl_program_state_new (n_layers);
authority_priv->glsl_program_state = glsl_program_state; authority_priv->glsl_program_state = glsl_program_state;
}
/* If the pipeline isn't actually its own glsl-authority /* If the pipeline isn't actually its own glsl-authority
* then take a reference to the program state associated * then take a reference to the program state associated
@ -350,21 +369,17 @@ _cogl_pipeline_backend_glsl_start (CoglPipeline *pipeline,
priv->glsl_program_state = priv->glsl_program_state =
glsl_program_state_ref (authority_priv->glsl_program_state); glsl_program_state_ref (authority_priv->glsl_program_state);
} }
}
/* If we make it here then we have a glsl_program_state struct /* If we make it here then we have a glsl_program_state struct
without a gl_program either because this is the first time we've without a gl_program either because this is the first time we've
encountered it or because the user program has changed since it encountered it or because the user program has changed since it
was last linked */ was last linked */
priv->glsl_program_state->gl_program_changed = TRUE;
GE_RET( gl_program, glCreateProgram () );
#ifdef HAVE_COGL_GLES2 #ifdef HAVE_COGL_GLES2
/* Find the largest count of texture coordinate attributes /* Find the largest count of texture coordinate attributes
* associated with each of the shaders so we can ensure a consistent * associated with each of the shaders so we can ensure a consistent
* _cogl_tex_coord[] array declaration across all of the shaders.*/ * _cogl_tex_coord[] array declaration across all of the shaders.*/
if (user_program)
for (l = user_program->attached_shaders; l; l = l->next) for (l = user_program->attached_shaders; l; l = l->next)
{ {
CoglShader *shader = l->data; CoglShader *shader = l->data;
@ -373,40 +388,493 @@ _cogl_pipeline_backend_glsl_start (CoglPipeline *pipeline,
} }
#endif #endif
/* Add all of the shaders from the user program */ priv->glsl_program_state->n_tex_coord_attribs = n_tex_coord_attribs;
/* Check whether the user program contains a fragment
shader. Otherwise we need to generate one */
if (user_program)
for (l = user_program->attached_shaders; l; l = l->next) for (l = user_program->attached_shaders; l; l = l->next)
{ {
CoglShader *shader = l->data; CoglShader *shader = l->data;
g_assert (shader->language == COGL_SHADER_LANGUAGE_GLSL); if (shader->type == COGL_SHADER_TYPE_FRAGMENT)
goto no_fragment_shader_needed;
_cogl_shader_compile_real (shader, n_tex_coord_attribs);
GE( glAttachShader (gl_program, shader->gl_handle) );
} }
priv->glsl_program_state->gl_program = gl_program; /* We reuse two grow-only GStrings for code-gen. One string
priv->glsl_program_state->user_program_age = user_program->age; contains the uniform and attribute declarations while the
#ifdef HAVE_COGL_GLES2 other contains the main function. We need two strings
priv->glsl_program_state->n_tex_coord_attribs = n_tex_coord_attribs; because we need to dynamically declare attributes as the
#endif add_layer callback is invoked */
g_string_set_size (ctx->fragment_header_buffer, 0);
g_string_set_size (ctx->fragment_source_buffer, 0);
priv->glsl_program_state->header = ctx->fragment_header_buffer;
priv->glsl_program_state->source = ctx->fragment_source_buffer;
link_program (gl_program); g_string_append (priv->glsl_program_state->source,
"void\n"
"main ()\n"
"{\n");
for (i = 0; i < n_layers; i++)
{
priv->glsl_program_state->unit_state[i].sampled = FALSE;
priv->glsl_program_state->unit_state[i].combine_constant_used = FALSE;
priv->glsl_program_state->unit_state[i].dirty_combine_constant = FALSE;
}
no_fragment_shader_needed:
return TRUE; return TRUE;
} }
gboolean static void
add_constant_lookup (GlslProgramState *glsl_program_state,
CoglPipeline *pipeline,
CoglPipelineLayer *layer,
const char *swizzle)
{
int unit_index = _cogl_pipeline_layer_get_unit_index (layer);
/* Create a sampler uniform for this layer if we haven't already */
if (!glsl_program_state->unit_state[unit_index].combine_constant_used)
{
g_string_append_printf (glsl_program_state->header,
"uniform vec4 _cogl_layer_constant_%i;\n",
unit_index);
glsl_program_state->unit_state[unit_index].combine_constant_used = TRUE;
glsl_program_state->unit_state[unit_index].dirty_combine_constant = TRUE;
}
g_string_append_printf (glsl_program_state->source,
"_cogl_layer_constant_%i.%s",
unit_index, swizzle);
}
static void
add_texture_lookup (GlslProgramState *glsl_program_state,
CoglPipeline *pipeline,
CoglPipelineLayer *layer,
const char *swizzle)
{
CoglHandle texture;
int unit_index = _cogl_pipeline_layer_get_unit_index (layer);
const char *target_string, *tex_coord_swizzle;
texture = _cogl_pipeline_layer_get_texture (layer);
if (texture == COGL_INVALID_HANDLE)
{
target_string = "2D";
tex_coord_swizzle = "st";
}
else
{
GLenum gl_target;
cogl_texture_get_gl_texture (texture, NULL, &gl_target);
switch (gl_target)
{
#ifndef HAVE_COGL_GLES2
case GL_TEXTURE_1D:
target_string = "1D";
tex_coord_swizzle = "s";
break;
#endif
case GL_TEXTURE_2D:
target_string = "2D";
tex_coord_swizzle = "st";
break;
#ifdef GL_ARB_texture_rectangle
case GL_TEXTURE_RECTANGLE_ARB:
target_string = "2DRect";
tex_coord_swizzle = "st";
break;
#endif
case GL_TEXTURE_3D:
target_string = "3D";
tex_coord_swizzle = "stp";
break;
default:
g_assert_not_reached ();
}
}
/* Create a sampler uniform for this layer if we haven't already */
if (!glsl_program_state->unit_state[unit_index].sampled)
{
g_string_append_printf (glsl_program_state->header,
"uniform sampler%s _cogl_sampler_%i;\n",
target_string,
unit_index);
glsl_program_state->unit_state[unit_index].sampled = TRUE;
}
g_string_append_printf (glsl_program_state->source,
"texture%s (_cogl_sampler_%i, ",
target_string, unit_index);
/* If point sprite coord generation is being used then divert to the
built-in varying var for that instead of the texture
coordinates */
if (cogl_pipeline_get_layer_point_sprite_coords_enabled (pipeline,
layer->index))
g_string_append_printf (glsl_program_state->source,
"gl_PointCoord.%s",
tex_coord_swizzle);
else
g_string_append_printf (glsl_program_state->source,
"cogl_tex_coord_in[%d].%s",
unit_index, tex_coord_swizzle);
g_string_append_printf (glsl_program_state->source, ").%s", swizzle);
}
typedef struct
{
int unit_index;
CoglPipelineLayer *layer;
} FindPipelineLayerData;
static gboolean
find_pipeline_layer_cb (CoglPipelineLayer *layer,
void *user_data)
{
FindPipelineLayerData *data = user_data;
int unit_index;
unit_index = _cogl_pipeline_layer_get_unit_index (layer);
if (unit_index == data->unit_index)
{
data->layer = layer;
return FALSE;
}
return TRUE;
}
static void
add_arg (GlslProgramState *glsl_program_state,
CoglPipeline *pipeline,
CoglPipelineLayer *layer,
GLint src,
GLenum operand,
const char *swizzle)
{
GString *shader_source = glsl_program_state->source;
char alpha_swizzle[5] = "aaaa";
g_string_append_c (shader_source, '(');
if (operand == GL_ONE_MINUS_SRC_COLOR || operand == GL_ONE_MINUS_SRC_ALPHA)
g_string_append_printf (shader_source,
"vec4(1.0, 1.0, 1.0, 1.0).%s - ",
swizzle);
/* If the operand is reading from the alpha then replace the swizzle
with the same number of copies of the alpha */
if (operand == GL_SRC_ALPHA || operand == GL_ONE_MINUS_SRC_ALPHA)
{
alpha_swizzle[strlen (swizzle)] = '\0';
swizzle = alpha_swizzle;
}
switch (src)
{
case GL_TEXTURE:
add_texture_lookup (glsl_program_state,
pipeline,
layer,
swizzle);
break;
case GL_CONSTANT:
add_constant_lookup (glsl_program_state,
pipeline,
layer,
swizzle);
break;
case GL_PREVIOUS:
if (_cogl_pipeline_layer_get_unit_index (layer) > 0)
{
g_string_append_printf (shader_source, "cogl_color_out.%s", swizzle);
break;
}
/* flow through */
case GL_PRIMARY_COLOR:
g_string_append_printf (shader_source, "cogl_color_in.%s", swizzle);
break;
default:
if (src >= GL_TEXTURE0 && src < GL_TEXTURE0 + 32)
{
FindPipelineLayerData data;
data.unit_index = src - GL_TEXTURE0;
data.layer = layer;
_cogl_pipeline_foreach_layer_internal (pipeline,
find_pipeline_layer_cb,
&data);
add_texture_lookup (glsl_program_state,
pipeline,
data.layer,
swizzle);
}
break;
}
g_string_append_c (shader_source, ')');
}
static void
append_masked_combine (CoglPipeline *pipeline,
CoglPipelineLayer *layer,
const char *swizzle,
GLint function,
GLint *src,
GLint *op)
{
GlslProgramState *glsl_program_state = get_glsl_program_state (pipeline);
GString *shader_source = glsl_program_state->source;
g_string_append_printf (glsl_program_state->source,
" cogl_color_out.%s = ", swizzle);
switch (function)
{
case GL_REPLACE:
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], swizzle);
break;
case GL_MODULATE:
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], swizzle);
g_string_append (shader_source, " * ");
add_arg (glsl_program_state, pipeline, layer,
src[1], op[1], swizzle);
break;
case GL_ADD:
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], swizzle);
g_string_append (shader_source, " + ");
add_arg (glsl_program_state, pipeline, layer,
src[1], op[1], swizzle);
break;
case GL_ADD_SIGNED:
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], swizzle);
g_string_append (shader_source, " + ");
add_arg (glsl_program_state, pipeline, layer,
src[1], op[1], swizzle);
g_string_append_printf (shader_source,
" - vec4(0.5, 0.5, 0.5, 0.5).%s",
swizzle);
break;
case GL_SUBTRACT:
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], swizzle);
g_string_append (shader_source, " - ");
add_arg (glsl_program_state, pipeline, layer,
src[1], op[1], swizzle);
break;
case GL_INTERPOLATE:
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], swizzle);
g_string_append (shader_source, " * ");
add_arg (glsl_program_state, pipeline, layer,
src[2], op[2], swizzle);
g_string_append (shader_source, " + ");
add_arg (glsl_program_state, pipeline, layer,
src[1], op[1], swizzle);
g_string_append_printf (shader_source,
" * (vec4(1.0, 1.0, 1.0, 1.0).%s - ",
swizzle);
add_arg (glsl_program_state, pipeline, layer,
src[2], op[2], swizzle);
g_string_append_c (shader_source, ')');
break;
case GL_DOT3_RGB:
case GL_DOT3_RGBA:
g_string_append (shader_source, "vec4(4 * ((");
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], "r");
g_string_append (shader_source, " - 0.5) * (");
add_arg (glsl_program_state, pipeline, layer,
src[1], op[1], "r");
g_string_append (shader_source, " - 0.5) + (");
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], "g");
g_string_append (shader_source, " - 0.5) * (");
add_arg (glsl_program_state, pipeline, layer,
src[1], op[1], "g");
g_string_append (shader_source, " - 0.5) + (");
add_arg (glsl_program_state, pipeline, layer,
src[0], op[0], "b");
g_string_append (shader_source, " - 0.5) * (");
add_arg (glsl_program_state, pipeline, layer,
src[1], op[1], "b");
g_string_append_printf (shader_source, " - 0.5))).%s", swizzle);
break;
}
g_string_append_printf (shader_source, ";\n");
}
static gboolean
_cogl_pipeline_backend_glsl_add_layer (CoglPipeline *pipeline, _cogl_pipeline_backend_glsl_add_layer (CoglPipeline *pipeline,
CoglPipelineLayer *layer, CoglPipelineLayer *layer,
unsigned long layers_difference) unsigned long layers_difference)
{ {
GlslProgramState *glsl_program_state = get_glsl_program_state (pipeline);
CoglPipelineLayer *combine_authority =
_cogl_pipeline_layer_get_authority (layer,
COGL_PIPELINE_LAYER_STATE_COMBINE);
CoglPipelineLayerBigState *big_state = combine_authority->big_state;
if (!glsl_program_state->source)
return TRUE;
if (!_cogl_pipeline_need_texture_combine_separate (combine_authority) ||
/* GL_DOT3_RGBA Is a bit weird as a GL_COMBINE_RGB function
* since if you use it, it overrides your ALPHA function...
*/
big_state->texture_combine_rgb_func == GL_DOT3_RGBA)
append_masked_combine (pipeline,
layer,
"rgba",
big_state->texture_combine_rgb_func,
big_state->texture_combine_rgb_src,
big_state->texture_combine_rgb_op);
else
{
append_masked_combine (pipeline,
layer,
"rgb",
big_state->texture_combine_rgb_func,
big_state->texture_combine_rgb_src,
big_state->texture_combine_rgb_op);
append_masked_combine (pipeline,
layer,
"a",
big_state->texture_combine_alpha_func,
big_state->texture_combine_alpha_src,
big_state->texture_combine_alpha_op);
}
return TRUE; return TRUE;
} }
gboolean gboolean
_cogl_pipeline_backend_glsl_passthrough (CoglPipeline *pipeline) _cogl_pipeline_backend_glsl_passthrough (CoglPipeline *pipeline)
{ {
GlslProgramState *glsl_program_state = get_glsl_program_state (pipeline);
if (!glsl_program_state->source)
return TRUE;
g_string_append (glsl_program_state->source,
" cogl_color_out = cogl_color_in;\n");
return TRUE;
}
typedef struct
{
int unit;
GLuint gl_program;
gboolean update_all;
GlslProgramState *glsl_program_state;
} UpdateUniformsState;
static gboolean
get_uniform_cb (CoglPipeline *pipeline,
int layer_index,
void *user_data)
{
UpdateUniformsState *state = user_data;
GlslProgramState *glsl_program_state = state->glsl_program_state;
UnitState *unit_state = &glsl_program_state->unit_state[state->unit];
GLint uniform_location;
_COGL_GET_CONTEXT (ctx, FALSE);
if (unit_state->sampled)
{
/* We can reuse the source buffer to create the uniform name because
the program has now been linked */
g_string_set_size (ctx->fragment_source_buffer, 0);
g_string_append_printf (ctx->fragment_source_buffer,
"_cogl_sampler_%i", state->unit);
GE_RET( uniform_location,
glGetUniformLocation (state->gl_program,
ctx->fragment_source_buffer->str) );
g_return_val_if_fail (uniform_location != -1, TRUE);
/* We can set the uniform immediately because the samplers are
the unit index not the texture object number so it will never
change. Unfortunately GL won't let us use a constant instead
of a uniform */
GE( glUniform1i (uniform_location, state->unit) );
}
if (unit_state->combine_constant_used)
{
g_string_set_size (ctx->fragment_source_buffer, 0);
g_string_append_printf (ctx->fragment_source_buffer,
"_cogl_layer_constant_%i", state->unit);
GE_RET( uniform_location,
glGetUniformLocation (state->gl_program,
ctx->fragment_source_buffer->str) );
g_return_val_if_fail (uniform_location != -1, TRUE);
unit_state->combine_constant_uniform = uniform_location;
}
state->unit++;
return TRUE;
}
static gboolean
update_constants_cb (CoglPipeline *pipeline,
int layer_index,
void *user_data)
{
UpdateUniformsState *state = user_data;
GlslProgramState *glsl_program_state = state->glsl_program_state;
UnitState *unit_state = &glsl_program_state->unit_state[state->unit++];
_COGL_GET_CONTEXT (ctx, FALSE);
if (unit_state->combine_constant_used &&
(state->update_all || unit_state->dirty_combine_constant))
{
float constant[4];
_cogl_pipeline_get_layer_combine_constant (pipeline,
layer_index,
constant);
GE (glUniform4fv (unit_state->combine_constant_uniform,
1, constant));
unit_state->dirty_combine_constant = FALSE;
}
return TRUE; return TRUE;
} }
@ -415,11 +883,105 @@ _cogl_pipeline_backend_glsl_end (CoglPipeline *pipeline,
unsigned long pipelines_difference) unsigned long pipelines_difference)
{ {
GlslProgramState *glsl_program_state = get_glsl_program_state (pipeline); GlslProgramState *glsl_program_state = get_glsl_program_state (pipeline);
CoglProgram *user_program;
GLuint gl_program; GLuint gl_program;
gboolean gl_program_changed; gboolean gl_program_changed = FALSE;
UpdateUniformsState state;
_COGL_GET_CONTEXT (ctx, FALSE);
gl_program = glsl_program_state->gl_program; gl_program = glsl_program_state->gl_program;
gl_program_changed = glsl_program_state->gl_program_changed; user_program = cogl_pipeline_get_user_program (pipeline);
if (gl_program == 0)
{
gl_program_changed = TRUE;
GE_RET( gl_program, glCreateProgram () );
if (user_program)
{
GSList *l;
/* Add all of the shaders from the user program */
for (l = user_program->attached_shaders; l; l = l->next)
{
CoglShader *shader = l->data;
g_assert (shader->language == COGL_SHADER_LANGUAGE_GLSL);
_cogl_shader_compile_real (shader,
glsl_program_state->
n_tex_coord_attribs);
GE( glAttachShader (gl_program, shader->gl_handle) );
}
glsl_program_state->user_program_age = user_program->age;
}
if (glsl_program_state->source)
{
const GLchar *source_strings[2];
GLint lengths[2];
GLint compile_status;
GLuint shader;
COGL_STATIC_COUNTER (backend_glsl_compile_counter,
"glsl compile counter",
"Increments each time a new GLSL "
"program is compiled",
0 /* no application private data */);
COGL_COUNTER_INC (_cogl_uprof_context, backend_glsl_compile_counter);
g_string_append (glsl_program_state->source, "}\n");
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_SHOW_SOURCE))
g_message ("pipeline program:\n%s%s",
glsl_program_state->header->str,
glsl_program_state->source->str);
GE_RET( shader, glCreateShader (GL_FRAGMENT_SHADER) );
lengths[0] = glsl_program_state->header->len;
source_strings[0] = glsl_program_state->header->str;
lengths[1] = glsl_program_state->source->len;
source_strings[1] = glsl_program_state->source->str;
_cogl_shader_set_source_with_boilerplate (shader, GL_FRAGMENT_SHADER,
glsl_program_state->
n_tex_coord_attribs,
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);
}
GE( glAttachShader (gl_program, shader) );
/* We can delete the shader now, but it won't actually be
destroyed until the program is also desroyed */
GE( glDeleteShader (shader) );
glsl_program_state->header = NULL;
glsl_program_state->source = NULL;
}
link_program (gl_program);
glsl_program_state->gl_program = gl_program;
}
#ifdef HAVE_COGL_GLES2 #ifdef HAVE_COGL_GLES2
/* This function is a massive hack to get the GLES2 backend to /* This function is a massive hack to get the GLES2 backend to
@ -437,10 +999,31 @@ _cogl_pipeline_backend_glsl_end (CoglPipeline *pipeline,
_cogl_use_program (gl_program, COGL_PIPELINE_PROGRAM_TYPE_GLSL); _cogl_use_program (gl_program, COGL_PIPELINE_PROGRAM_TYPE_GLSL);
#endif #endif
_cogl_program_flush_uniforms (cogl_pipeline_get_user_program (pipeline), state.unit = 0;
gl_program, gl_program_changed); state.gl_program = gl_program;
state.glsl_program_state = glsl_program_state;
glsl_program_state->gl_program_changed = FALSE; if (gl_program_changed)
cogl_pipeline_foreach_layer (pipeline,
get_uniform_cb,
&state);
state.unit = 0;
state.update_all = (gl_program_changed ||
glsl_program_state->last_used_for_pipeline != pipeline);
cogl_pipeline_foreach_layer (pipeline,
update_constants_cb,
&state);
if (user_program)
_cogl_program_flush_uniforms (user_program,
gl_program,
gl_program_changed);
/* We need to track the last pipeline that the program was used with
* so know if we need to update all of the uniforms */
glsl_program_state->last_used_for_pipeline = pipeline;
return TRUE; return TRUE;
} }
@ -450,15 +1033,60 @@ _cogl_pipeline_backend_glsl_pre_change_notify (CoglPipeline *pipeline,
CoglPipelineState change, CoglPipelineState change,
const CoglColor *new_color) const CoglColor *new_color)
{ {
static const unsigned long glsl_op_changes = static const unsigned long fragment_op_changes =
COGL_PIPELINE_STATE_LAYERS |
COGL_PIPELINE_STATE_USER_SHADER; COGL_PIPELINE_STATE_USER_SHADER;
/* TODO: COGL_PIPELINE_STATE_FOG */
if (!(change & glsl_op_changes)) if (!(change & fragment_op_changes))
return; return;
dirty_glsl_program_state (pipeline); dirty_glsl_program_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_backend_glsl_layer_pre_change_notify (
CoglPipeline *owner,
CoglPipelineLayer *layer,
CoglPipelineLayerState change)
{
CoglPipelineBackendGlslPrivate *priv;
static const unsigned long not_fragment_op_changes =
COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT |
COGL_PIPELINE_LAYER_STATE_TEXTURE;
priv = get_glsl_priv (owner);
if (!priv)
return;
if (!(change & not_fragment_op_changes))
{
dirty_glsl_program_state (owner);
return;
}
if (change & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT)
{
GlslProgramState *glsl_program_state =
get_glsl_program_state (owner);
int unit_index = _cogl_pipeline_layer_get_unit_index (layer);
glsl_program_state->unit_state[unit_index].dirty_combine_constant = TRUE;
}
/* 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. */
return;
}
static void static void
_cogl_pipeline_backend_glsl_free_priv (CoglPipeline *pipeline) _cogl_pipeline_backend_glsl_free_priv (CoglPipeline *pipeline)
{ {
@ -481,7 +1109,7 @@ const CoglPipelineBackend _cogl_pipeline_glsl_backend =
_cogl_pipeline_backend_glsl_end, _cogl_pipeline_backend_glsl_end,
_cogl_pipeline_backend_glsl_pre_change_notify, _cogl_pipeline_backend_glsl_pre_change_notify,
NULL, /* pipeline_set_parent_notify */ NULL, /* pipeline_set_parent_notify */
NULL, /* layer_pre_change_notify */ _cogl_pipeline_backend_glsl_layer_pre_change_notify,
_cogl_pipeline_backend_glsl_free_priv, _cogl_pipeline_backend_glsl_free_priv,
NULL /* free_layer_priv */ NULL /* free_layer_priv */
}; };