* clutter/cogl/gles/cogl-gles2-wrapper.h:

* clutter/cogl/gles/cogl-gles2-wrapper.c: All of the settings and
	uniforms are now proxied into COGL variables instead of setting
	the GL uniforms directly. Just before glDrawArrays is executed a
	shader is generated using the given settings to avoid using 'if'
	statements. The shaders are cached.

	* clutter/cogl/gles/cogl-fixed-vertex-shader.glsl: 
	* clutter/cogl/gles/cogl-fixed-fragment-shader.glsl: The shaders
	are now split into parts using comments instead of 'if' statements
	so that the simplest shader can be generated on the fly.

	* clutter/cogl/gles/stringify.sh: Now splits up the shader sources
	into separate C strings where deliminated by special comments.

	* clutter/cogl/gles/cogl-program.h: 
	* clutter/cogl/gles/cogl-program.c: A custom shader can no longer
	be directly linked with the fixed-functionality replacement
	because the replacement changes depending on the settings. Instead
	the bound shader is linked with the appropriate replacement shader
	just before glDrawArrays is executed. The custom uniform variables
	must also be proxied through COGL variables because their location
	can change when relinked.
This commit is contained in:
Neil Roberts 2008-06-24 16:21:40 +00:00
parent d12cfb39ba
commit cbd77008ff
7 changed files with 718 additions and 331 deletions

View File

@ -1,3 +1,5 @@
/*** cogl_fixed_fragment_shader_start ***/
/* There is no default precision for floats in fragment shaders in
GLES 2 so we need to define one */
precision mediump float;
@ -8,86 +10,64 @@ varying vec2 tex_coord;
varying float fog_amount;
/* Texturing options */
uniform bool texture_2d_enabled;
uniform sampler2D texture_unit;
uniform bool alpha_only;
/* Fogging options */
uniform bool fog_enabled;
uniform vec4 fog_color;
/* Alpha test options */
uniform bool alpha_test_enabled;
uniform int alpha_test_func;
uniform float alpha_test_ref;
/* Alpha test functions */
const int GL_NEVER = 0x0200;
const int GL_LESS = 0x0201;
const int GL_EQUAL = 0x0202;
const int GL_LEQUAL = 0x0203;
const int GL_GREATER = 0x0204;
const int GL_NOTEQUAL = 0x0205;
const int GL_GEQUAL = 0x0206;
void
main (void)
{
if (texture_2d_enabled)
{
if (alpha_only)
{
/* If the texture only has an alpha channel (eg, with the
textures from the pango renderer) then the RGB components
will be black. We want to use the RGB from the current
color in that case */
gl_FragColor = frag_color;
gl_FragColor.a *= texture2D (texture_unit, tex_coord).a;
}
else
gl_FragColor = frag_color * texture2D (texture_unit, tex_coord);
}
else
gl_FragColor = frag_color;
/*** cogl_fixed_fragment_shader_texture_alpha_only ***/
if (fog_enabled)
/* Mix the calculated color with the fog color */
gl_FragColor.rgb = mix (fog_color.rgb, gl_FragColor.rgb, fog_amount);
/* If the texture only has an alpha channel (eg, with the textures
from the pango renderer) then the RGB components will be
black. We want to use the RGB from the current color in that
case */
gl_FragColor = frag_color;
gl_FragColor.a *= texture2D (texture_unit, tex_coord).a;
/*** cogl_fixed_fragment_shader_texture ***/
/* This pointless extra variable is needed to work around an
apparent bug in the PowerVR drivers. Without it the alpha
blending seems to stop working */
vec4 frag_color_copy = frag_color;
gl_FragColor = frag_color_copy * texture2D (texture_unit, tex_coord);
/*** cogl_fixed_fragment_shader_solid_color ***/
gl_FragColor = frag_color;
/*** cogl_fixed_fragment_shader_fog ***/
/* Mix the calculated color with the fog color */
gl_FragColor.rgb = mix (fog_color.rgb, gl_FragColor.rgb, fog_amount);
/* Alpha testing */
if (alpha_test_enabled)
{
if (alpha_test_func == GL_NEVER)
discard;
else if (alpha_test_func == GL_LESS)
{
if (gl_FragColor.a >= alpha_test_ref)
discard;
}
else if (alpha_test_func == GL_EQUAL)
{
if (gl_FragColor.a != alpha_test_ref)
discard;
}
else if (alpha_test_func == GL_LEQUAL)
{
if (gl_FragColor.a > alpha_test_ref)
discard;
}
else if (alpha_test_func == GL_GREATER)
{
if (gl_FragColor.a <= alpha_test_ref)
discard;
}
else if (alpha_test_func == GL_NOTEQUAL)
{
if (gl_FragColor.a == alpha_test_ref)
discard;
}
else if (alpha_test_func == GL_GEQUAL)
{
if (gl_FragColor.a < alpha_test_ref)
discard;
}
}
/*** cogl_fixed_fragment_shader_alpha_never ***/
discard;
/*** cogl_fixed_fragment_shader_alpha_less ***/
if (gl_FragColor.a >= alpha_test_ref)
discard;
/*** cogl_fixed_fragment_shader_alpha_equal ***/
if (gl_FragColor.a != alpha_test_ref)
discard;
/*** cogl_fixed_fragment_shader_alpha_lequal ***/
if (gl_FragColor.a > alpha_test_ref)
discard;
/*** cogl_fixed_fragment_shader_alpha_greater ***/
if (gl_FragColor.a <= alpha_test_ref)
discard;
/*** cogl_fixed_fragment_shader_alpha_notequal ***/
if (gl_FragColor.a == alpha_test_ref)
discard;
/*** cogl_fixed_fragment_shader_alpha_gequal ***/
if (gl_FragColor.a < alpha_test_ref)
discard;
/*** cogl_fixed_fragment_shader_end ***/
}

View File

@ -1,3 +1,5 @@
/*** cogl_fixed_vertex_shader_start ***/
/* Per vertex attributes */
attribute vec4 vertex_attrib;
attribute vec4 tex_coord_attrib;
@ -14,17 +16,10 @@ varying vec2 tex_coord;
varying float fog_amount;
/* Fogging options */
uniform bool fog_enabled;
uniform int fog_mode;
uniform float fog_density;
uniform float fog_start;
uniform float fog_end;
/* Fogging modes */
const int GL_LINEAR = 0x2601;
const int GL_EXP = 0x0800;
const int GL_EXP2 = 0x0801;
void
main (void)
{
@ -38,23 +33,26 @@ main (void)
/* Pass the interpolated vertex color on to the fragment shader */
frag_color = color_attrib;
if (fog_enabled)
{
/* Estimate the distance from the eye using just the
z-coordinate to use as the fog coord */
vec4 eye_coord = modelview_matrix * vertex_attrib;
float fog_coord = abs (eye_coord.z / eye_coord.w);
/*** cogl_fixed_vertex_shader_fog_start ***/
/* Calculate the fog amount per-vertex and interpolate it for
the fragment shader */
if (fog_mode == GL_EXP)
fog_amount = exp (-fog_density * fog_coord);
else if (fog_mode == GL_EXP2)
fog_amount = exp (-fog_density * fog_coord
* fog_density * fog_coord);
else
fog_amount = (fog_end - fog_coord) / (fog_end - fog_start);
/* Estimate the distance from the eye using just the z-coordinate to
use as the fog coord */
vec4 eye_coord = modelview_matrix * vertex_attrib;
float fog_coord = abs (eye_coord.z / eye_coord.w);
fog_amount = clamp (fog_amount, 0.0, 1.0);
}
/* Calculate the fog amount per-vertex and interpolate it for the
fragment shader */
/*** cogl_fixed_vertex_shader_fog_exp ***/
fog_amount = exp (-fog_density * fog_coord);
/*** cogl_fixed_vertex_shader_fog_exp2 ***/
fog_amount = exp (-fog_density * fog_coord
* fog_density * fog_coord);
/*** cogl_fixed_vertex_shader_fog_linear ***/
fog_amount = (fog_end - fog_coord) / (fog_end - fog_start);
/*** cogl_fixed_vertex_shader_fog_end ***/
fog_amount = clamp (fog_amount, 0.0, 1.0);
/*** cogl_fixed_vertex_shader_end ***/
}

View File

@ -36,6 +36,8 @@
#include "cogl-fixed-vertex-shader.h"
#include "cogl-fixed-fragment-shader.h"
#include "cogl-context.h"
#include "cogl-shader.h"
#include "cogl-program.h"
#define _COGL_GET_GLES2_WRAPPER(wvar, retval) \
CoglGles2Wrapper *wvar; \
@ -45,6 +47,24 @@
wvar = &__ctxvar->gles2; \
}
#define _COGL_GLES2_CHANGE_SETTING(w, var, val) \
do \
if ((w)->settings.var != (val)) \
{ \
(w)->settings.var = (val); \
(w)->settings_dirty = TRUE; \
} \
while (0)
#define _COGL_GLES2_CHANGE_UNIFORM(w, flag, var, val) \
do \
if ((w)->var != (val)) \
{ \
(w)->var = (val); \
(w)->dirty_uniforms |= COGL_GLES2_DIRTY_ ## flag; \
} \
while (0)
#define COGL_GLES2_WRAPPER_VERTEX_ATTRIB 0
#define COGL_GLES2_WRAPPER_TEX_COORD_ATTRIB 1
#define COGL_GLES2_WRAPPER_COLOR_ATTRIB 2
@ -83,62 +103,10 @@ cogl_gles2_wrapper_create_shader (GLenum type, const char *source)
void
cogl_gles2_wrapper_init (CoglGles2Wrapper *wrapper)
{
GLint status;
GLfixed default_fog_color[4] = { 0, 0, 0, 0 };
memset (wrapper, 0, sizeof (CoglGles2Wrapper));
/* Create the shader program */
wrapper->vertex_shader
= cogl_gles2_wrapper_create_shader (GL_VERTEX_SHADER,
cogl_fixed_vertex_shader);
if (wrapper->vertex_shader == 0)
return;
wrapper->fragment_shader
= cogl_gles2_wrapper_create_shader (GL_FRAGMENT_SHADER,
cogl_fixed_fragment_shader);
if (wrapper->fragment_shader == 0)
{
glDeleteShader (wrapper->vertex_shader);
return;
}
wrapper->program = glCreateProgram ();
glAttachShader (wrapper->program, wrapper->fragment_shader);
glAttachShader (wrapper->program, wrapper->vertex_shader);
cogl_gles2_wrapper_bind_attributes (wrapper->program);
glLinkProgram (wrapper->program);
glGetProgramiv (wrapper->program, GL_LINK_STATUS, &status);
if (!status)
{
char log[1024];
GLint len;
glGetProgramInfoLog (wrapper->program, sizeof (log) - 1, &len, log);
log[len] = '\0';
g_critical ("%s", log);
glDeleteProgram (wrapper->program);
glDeleteShader (wrapper->vertex_shader);
glDeleteShader (wrapper->fragment_shader);
return;
}
glUseProgram (wrapper->program);
wrapper->uniforms = &wrapper->fixed_uniforms;
cogl_gles2_wrapper_get_uniforms (wrapper->program, wrapper->uniforms);
/* Always use the first texture unit */
glUniform1i (wrapper->uniforms->bound_texture_uniform, 0);
/* Initialize the stacks */
cogl_wrap_glMatrixMode (GL_TEXTURE);
cogl_wrap_glLoadIdentity ();
@ -147,8 +115,6 @@ cogl_gles2_wrapper_init (CoglGles2Wrapper *wrapper)
cogl_wrap_glMatrixMode (GL_MODELVIEW);
cogl_wrap_glLoadIdentity ();
wrapper->mvp_uptodate = GL_FALSE;
/* Initialize the fogging options */
cogl_wrap_glDisable (GL_FOG);
cogl_wrap_glFogx (GL_FOG_MODE, GL_LINEAR);
@ -162,6 +128,293 @@ cogl_gles2_wrapper_init (CoglGles2Wrapper *wrapper)
cogl_wrap_glAlphaFunc (GL_ALWAYS, 0.0f);
}
static gboolean
cogl_gles2_settings_equal (const CoglGles2WrapperSettings *a,
const CoglGles2WrapperSettings *b,
gboolean vertex_tests,
gboolean fragment_tests)
{
if (fragment_tests)
{
if (a->texture_2d_enabled != b->texture_2d_enabled)
return FALSE;
if (a->texture_2d_enabled && a->alpha_only != b->alpha_only)
return FALSE;
if (a->alpha_test_enabled != b->alpha_test_enabled)
return FALSE;
if (a->alpha_test_enabled && a->alpha_test_func != b->alpha_test_func)
return FALSE;
}
if (a->fog_enabled != b->fog_enabled)
return FALSE;
if (vertex_tests && a->fog_enabled && a->fog_mode != b->fog_mode)
return FALSE;
return TRUE;
}
static CoglGles2WrapperShader *
cogl_gles2_get_vertex_shader (const CoglGles2WrapperSettings *settings)
{
GString *shader_source;
GLuint shader_obj;
CoglGles2WrapperShader *shader;
GSList *node;
_COGL_GET_GLES2_WRAPPER (w, NULL);
/* Check if we already have a vertex shader for these settings */
for (node = w->compiled_vertex_shaders; node; node = node->next)
if (cogl_gles2_settings_equal (settings,
&((CoglGles2WrapperShader *)
node->data)->settings,
TRUE, FALSE))
return (CoglGles2WrapperShader *) node->data;
/* Otherwise create a new shader */
shader_source = g_string_new (cogl_fixed_vertex_shader_start);
if (settings->fog_enabled)
{
g_string_append (shader_source, cogl_fixed_vertex_shader_fog_start);
switch (settings->fog_mode)
{
case GL_EXP:
g_string_append (shader_source, cogl_fixed_vertex_shader_fog_exp);
break;
case GL_EXP2:
g_string_append (shader_source, cogl_fixed_vertex_shader_fog_exp2);
break;
default:
g_string_append (shader_source, cogl_fixed_vertex_shader_fog_linear);
break;
}
g_string_append (shader_source, cogl_fixed_vertex_shader_fog_end);
}
g_string_append (shader_source, cogl_fixed_vertex_shader_end);
shader_obj = cogl_gles2_wrapper_create_shader (GL_VERTEX_SHADER,
shader_source->str);
g_string_free (shader_source, TRUE);
if (shader_obj == 0)
return NULL;
shader = g_slice_new (CoglGles2WrapperShader);
shader->shader = shader_obj;
shader->settings = *settings;
w->compiled_vertex_shaders = g_slist_prepend (w->compiled_vertex_shaders,
shader);
return shader;
}
static CoglGles2WrapperShader *
cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings)
{
GString *shader_source;
GLuint shader_obj;
CoglGles2WrapperShader *shader;
GSList *node;
_COGL_GET_GLES2_WRAPPER (w, NULL);
/* Check if we already have a fragment shader for these settings */
for (node = w->compiled_fragment_shaders; node; node = node->next)
if (cogl_gles2_settings_equal (settings,
&((CoglGles2WrapperShader *)
node->data)->settings,
FALSE, TRUE))
return (CoglGles2WrapperShader *) node->data;
/* Otherwise create a new shader */
shader_source = g_string_new (cogl_fixed_fragment_shader_start);
if (settings->texture_2d_enabled)
{
if (settings->alpha_only)
g_string_append (shader_source,
cogl_fixed_fragment_shader_texture_alpha_only);
else
g_string_append (shader_source,
cogl_fixed_fragment_shader_texture);
}
else
g_string_append (shader_source, cogl_fixed_fragment_shader_solid_color);
if (settings->fog_enabled)
g_string_append (shader_source, cogl_fixed_fragment_shader_fog);
if (settings->alpha_test_enabled)
switch (settings->alpha_test_func)
{
case GL_NEVER:
g_string_append (shader_source,
cogl_fixed_fragment_shader_alpha_never);
break;
case GL_LESS:
g_string_append (shader_source,
cogl_fixed_fragment_shader_alpha_less);
break;
case GL_EQUAL:
g_string_append (shader_source,
cogl_fixed_fragment_shader_alpha_equal);
break;
case GL_LEQUAL:
g_string_append (shader_source,
cogl_fixed_fragment_shader_alpha_lequal);
break;
case GL_GREATER:
g_string_append (shader_source,
cogl_fixed_fragment_shader_alpha_greater);
break;
case GL_NOTEQUAL:
g_string_append (shader_source,
cogl_fixed_fragment_shader_alpha_notequal);
break;
case GL_GEQUAL:
g_string_append (shader_source,
cogl_fixed_fragment_shader_alpha_gequal);
}
g_string_append (shader_source, cogl_fixed_fragment_shader_end);
shader_obj = cogl_gles2_wrapper_create_shader (GL_FRAGMENT_SHADER,
shader_source->str);
g_string_free (shader_source, TRUE);
if (shader_obj == 0)
return NULL;
shader = g_slice_new (CoglGles2WrapperShader);
shader->shader = shader_obj;
shader->settings = *settings;
w->compiled_fragment_shaders = g_slist_prepend (w->compiled_fragment_shaders,
shader);
return shader;
}
static CoglGles2WrapperProgram *
cogl_gles2_wrapper_get_program (const CoglGles2WrapperSettings *settings)
{
GSList *node;
CoglGles2WrapperProgram *program;
CoglGles2WrapperShader *vertex_shader, *fragment_shader;
GLint status;
gboolean custom_vertex_shader = FALSE, custom_fragment_shader = FALSE;
CoglProgram *user_program = NULL;
int i;
_COGL_GET_GLES2_WRAPPER (w, NULL);
/* Check if we've already got a program for these settings */
for (node = w->compiled_programs; node; node = node->next)
{
program = (CoglGles2WrapperProgram *) node->data;
if (cogl_gles2_settings_equal (settings, &program->settings, TRUE, TRUE)
&& program->settings.user_program == settings->user_program)
return (CoglGles2WrapperProgram *) node->data;
}
/* Otherwise create a new program */
/* Check whether the currently used custom program has vertex and
fragment shaders */
if (w->settings.user_program != COGL_INVALID_HANDLE)
{
user_program
= _cogl_program_pointer_from_handle (w->settings.user_program);
for (node = user_program->attached_shaders; node; node = node->next)
{
CoglShader *shader
= _cogl_shader_pointer_from_handle ((CoglHandle) node->data);
if (shader->type == CGL_VERTEX_SHADER)
custom_vertex_shader = TRUE;
else if (shader->type == CGL_FRAGMENT_SHADER)
custom_fragment_shader = TRUE;
}
}
/* Get or create the fixed functionality shaders for these settings
if there is no custom replacement */
if (!custom_vertex_shader)
{
vertex_shader = cogl_gles2_get_vertex_shader (settings);
if (vertex_shader == NULL)
return NULL;
}
if (!custom_fragment_shader)
{
fragment_shader = cogl_gles2_get_fragment_shader (settings);
if (fragment_shader == NULL)
return NULL;
}
program = g_slice_new (CoglGles2WrapperProgram);
program->program = glCreateProgram ();
if (!custom_vertex_shader)
glAttachShader (program->program, vertex_shader->shader);
if (!custom_fragment_shader)
glAttachShader (program->program, fragment_shader->shader);
if (user_program)
for (node = user_program->attached_shaders; node; node = node->next)
{
CoglShader *shader
= _cogl_shader_pointer_from_handle ((CoglHandle) node->data);
glAttachShader (program->program, shader->gl_handle);
}
cogl_gles2_wrapper_bind_attributes (program->program);
glLinkProgram (program->program);
glGetProgramiv (program->program, GL_LINK_STATUS, &status);
if (!status)
{
char log[1024];
GLint len;
glGetProgramInfoLog (program->program, sizeof (log) - 1, &len, log);
log[len] = '\0';
g_critical ("%s", log);
glDeleteProgram (program->program);
g_slice_free (CoglGles2WrapperProgram, program);
return NULL;
}
program->settings = *settings;
cogl_gles2_wrapper_get_uniforms (program->program, &program->uniforms);
/* We haven't tried to get a location for any of the custom uniforms
yet */
for (i = 0; i < COGL_GLES2_NUM_CUSTOM_UNIFORMS; i++)
program->custom_uniforms[i] = COGL_GLES2_UNBOUND_CUSTOM_UNIFORM;
w->compiled_programs = g_slist_append (w->compiled_programs, program);
return program;
}
void
cogl_gles2_wrapper_bind_attributes (GLuint program)
{
@ -183,17 +436,9 @@ cogl_gles2_wrapper_get_uniforms (GLuint program,
= glGetUniformLocation (program, "modelview_matrix");
uniforms->texture_matrix_uniform
= glGetUniformLocation (program, "texture_matrix");
uniforms->texture_2d_enabled_uniform
= glGetUniformLocation (program, "texture_2d_enabled");
uniforms->bound_texture_uniform
= glGetUniformLocation (program, "texture_unit");
uniforms->alpha_only_uniform
= glGetUniformLocation (program, "alpha_only");
uniforms->fog_enabled_uniform
= glGetUniformLocation (program, "fog_enabled");
uniforms->fog_mode_uniform
= glGetUniformLocation (program, "fog_mode");
uniforms->fog_density_uniform
= glGetUniformLocation (program, "fog_density");
uniforms->fog_start_uniform
@ -203,10 +448,6 @@ cogl_gles2_wrapper_get_uniforms (GLuint program,
uniforms->fog_color_uniform
= glGetUniformLocation (program, "fog_color");
uniforms->alpha_test_enabled_uniform
= glGetUniformLocation (program, "alpha_test_enabled");
uniforms->alpha_test_func_uniform
= glGetUniformLocation (program, "alpha_test_func");
uniforms->alpha_test_ref_uniform
= glGetUniformLocation (program, "alpha_test_ref");
}
@ -214,42 +455,50 @@ cogl_gles2_wrapper_get_uniforms (GLuint program,
void
cogl_gles2_wrapper_deinit (CoglGles2Wrapper *wrapper)
{
if (wrapper->program)
GSList *node, *next;
for (node = wrapper->compiled_programs; node; node = next)
{
glDeleteProgram (wrapper->program);
wrapper->program = 0;
next = node->next;
glDeleteProgram (((CoglGles2WrapperProgram *) node->data)->program);
g_slist_free1 (node);
}
if (wrapper->vertex_shader)
wrapper->compiled_programs = NULL;
for (node = wrapper->compiled_vertex_shaders; node; node = next)
{
glDeleteShader (wrapper->vertex_shader);
wrapper->vertex_shader = 0;
next = node->next;
glDeleteShader (((CoglGles2WrapperShader *) node->data)->shader);
g_slist_free1 (node);
}
if (wrapper->fragment_shader)
wrapper->compiled_vertex_shaders = NULL;
for (node = wrapper->compiled_fragment_shaders; node; node = next)
{
glDeleteShader (wrapper->fragment_shader);
wrapper->fragment_shader = 0;
next = node->next;
glDeleteShader (((CoglGles2WrapperShader *) node->data)->shader);
g_slist_free1 (node);
}
wrapper->compiled_fragment_shaders = NULL;
}
void
cogl_gles2_wrapper_update_matrix (CoglGles2Wrapper *wrapper, GLenum matrix_num)
{
const float *matrix;
switch (matrix_num)
{
default:
case GL_MODELVIEW:
wrapper->dirty_uniforms |= COGL_GLES2_DIRTY_MVP_MATRIX
| COGL_GLES2_DIRTY_MODELVIEW_MATRIX;
break;
case GL_PROJECTION:
/* Queue a recalculation of the combined modelview and
projection matrix at the next draw */
wrapper->mvp_uptodate = GL_FALSE;
wrapper->dirty_uniforms |= COGL_GLES2_DIRTY_MVP_MATRIX;
break;
case GL_TEXTURE:
matrix = wrapper->texture_stack + wrapper->texture_stack_pos * 16;
glUniformMatrix4fv (wrapper->uniforms->texture_matrix_uniform,
1, GL_FALSE, matrix);
wrapper->dirty_uniforms |= COGL_GLES2_DIRTY_TEXTURE_MATRIX;
break;
}
}
@ -530,26 +779,105 @@ cogl_wrap_glColorPointer (GLint size, GLenum type, GLsizei stride,
void
cogl_wrap_glDrawArrays (GLenum mode, GLint first, GLsizei count)
{
CoglGles2WrapperProgram *program;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
/* Make sure the modelview+projection matrix is up to date */
if (!w->mvp_uptodate)
/* Check if we need to switch programs */
if (w->settings_dirty)
{
float mvp_matrix[16];
const float *modelview_matrix = w->modelview_stack
+ w->modelview_stack_pos * 16;
/* Find or create a program for the current settings */
program = cogl_gles2_wrapper_get_program (&w->settings);
cogl_gles2_wrapper_mult_matrix (mvp_matrix,
w->projection_stack
+ w->projection_stack_pos * 16,
modelview_matrix);
if (program == NULL)
/* Can't compile a shader so there is nothing we can do */
return;
glUniformMatrix4fv (w->uniforms->mvp_matrix_uniform, 1,
GL_FALSE, mvp_matrix);
glUniformMatrix4fv (w->uniforms->modelview_matrix_uniform, 1, GL_FALSE,
modelview_matrix);
/* Start using it if we aren't already */
if (w->current_program != program)
{
glUseProgram (program->program);
w->current_program = program;
/* All of the uniforms are probably now out of date */
w->dirty_uniforms = COGL_GLES2_DIRTY_ALL;
w->dirty_custom_uniforms = (1 << COGL_GLES2_NUM_CUSTOM_UNIFORMS) - 1;
}
w->settings_dirty = FALSE;
}
else
program = w->current_program;
w->mvp_uptodate = GL_TRUE;
/* Make sure all of the uniforms are up to date */
if (w->dirty_uniforms)
{
if ((w->dirty_uniforms & (COGL_GLES2_DIRTY_MVP_MATRIX
| COGL_GLES2_DIRTY_MODELVIEW_MATRIX)))
{
float mvp_matrix[16];
const float *modelview_matrix = w->modelview_stack
+ w->modelview_stack_pos * 16;
cogl_gles2_wrapper_mult_matrix (mvp_matrix,
w->projection_stack
+ w->projection_stack_pos * 16,
modelview_matrix);
if (program->uniforms.mvp_matrix_uniform != -1)
glUniformMatrix4fv (program->uniforms.mvp_matrix_uniform, 1,
GL_FALSE, mvp_matrix);
if (program->uniforms.modelview_matrix_uniform != -1)
glUniformMatrix4fv (program->uniforms.modelview_matrix_uniform, 1,
GL_FALSE, modelview_matrix);
}
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_TEXTURE_MATRIX)
&& program->uniforms.texture_matrix_uniform != -1)
glUniformMatrix4fv (program->uniforms.texture_matrix_uniform, 1,
GL_FALSE,
w->texture_stack + w->texture_stack_pos * 16);
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_FOG_DENSITY)
&& program->uniforms.fog_density_uniform != -1)
glUniform1f (program->uniforms.fog_density_uniform, w->fog_density);
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_FOG_START)
&& program->uniforms.fog_start_uniform != -1)
glUniform1f (program->uniforms.fog_start_uniform, w->fog_start);
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_FOG_END)
&& program->uniforms.fog_end_uniform != -1)
glUniform1f (program->uniforms.fog_end_uniform, w->fog_end);
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_ALPHA_TEST_REF)
&& program->uniforms.alpha_test_ref_uniform != -1)
glUniform1f (program->uniforms.alpha_test_ref_uniform,
w->alpha_test_ref);
w->dirty_uniforms = 0;
}
if (w->dirty_custom_uniforms)
{
int i;
if (w->settings.user_program != COGL_INVALID_HANDLE)
{
CoglProgram *user_program
= _cogl_program_pointer_from_handle (w->settings.user_program);
const char *uniform_name;
for (i = 0; i < COGL_GLES2_NUM_CUSTOM_UNIFORMS; i++)
if ((w->dirty_custom_uniforms & (1 << i))
&& (uniform_name = user_program->custom_uniform_names[i]))
{
if (program->custom_uniforms[i]
== COGL_GLES2_UNBOUND_CUSTOM_UNIFORM)
program->custom_uniforms[i]
= glGetUniformLocation (program->program, uniform_name);
if (program->custom_uniforms[i] >= 0)
glUniform1f (program->custom_uniforms[i],
w->custom_uniforms[i]);
}
}
w->dirty_custom_uniforms = 0;
}
glDrawArrays (mode, first, count);
@ -566,8 +894,7 @@ cogl_gles2_wrapper_bind_texture (GLenum target, GLuint texture,
/* We need to keep track of whether the texture is alpha-only
because the emulation of GL_MODULATE needs to work differently in
that case */
glUniform1i (w->uniforms->alpha_only_uniform,
internal_format == GL_ALPHA ? GL_TRUE : GL_FALSE);
_COGL_GLES2_CHANGE_SETTING (w, alpha_only, internal_format == GL_ALPHA);
}
void
@ -586,15 +913,15 @@ cogl_wrap_glEnable (GLenum cap)
switch (cap)
{
case GL_TEXTURE_2D:
glUniform1i (w->uniforms->texture_2d_enabled_uniform, GL_TRUE);
_COGL_GLES2_CHANGE_SETTING (w, texture_2d_enabled, TRUE);
break;
case GL_FOG:
glUniform1i (w->uniforms->fog_enabled_uniform, GL_TRUE);
_COGL_GLES2_CHANGE_SETTING (w, fog_enabled, TRUE);
break;
case GL_ALPHA_TEST:
glUniform1i (w->uniforms->alpha_test_enabled_uniform, GL_TRUE);
_COGL_GLES2_CHANGE_SETTING (w, alpha_test_enabled, TRUE);
break;
default:
@ -610,15 +937,15 @@ cogl_wrap_glDisable (GLenum cap)
switch (cap)
{
case GL_TEXTURE_2D:
glUniform1i (w->uniforms->texture_2d_enabled_uniform, GL_FALSE);
_COGL_GLES2_CHANGE_SETTING (w, texture_2d_enabled, FALSE);
break;
case GL_FOG:
glUniform1i (w->uniforms->fog_enabled_uniform, GL_FALSE);
_COGL_GLES2_CHANGE_SETTING (w, fog_enabled, FALSE);
break;
case GL_ALPHA_TEST:
glUniform1i (w->uniforms->alpha_test_enabled_uniform, GL_FALSE);
_COGL_GLES2_CHANGE_SETTING (w, alpha_test_enabled, FALSE);
break;
default:
@ -670,8 +997,8 @@ cogl_wrap_glAlphaFunc (GLenum func, GLclampf ref)
else if (ref > 1.0f)
ref = 1.0f;
glUniform1i (w->uniforms->alpha_test_func_uniform, func);
glUniform1f (w->uniforms->alpha_test_ref_uniform, ref);
_COGL_GLES2_CHANGE_SETTING (w, alpha_test_func, func);
_COGL_GLES2_CHANGE_UNIFORM (w, ALPHA_TEST_REF, alpha_test_ref, ref);
}
void
@ -753,22 +1080,22 @@ cogl_wrap_glFogx (GLenum pname, GLfixed param)
switch (pname)
{
case GL_FOG_MODE:
glUniform1i (w->uniforms->fog_mode_uniform, param);
_COGL_GLES2_CHANGE_SETTING (w, fog_mode, param);
break;
case GL_FOG_DENSITY:
glUniform1f (w->uniforms->fog_density_uniform,
CLUTTER_FIXED_TO_FLOAT (param));
_COGL_GLES2_CHANGE_UNIFORM (w, FOG_DENSITY, fog_density,
CLUTTER_FIXED_TO_FLOAT (param));
break;
case GL_FOG_START:
glUniform1f (w->uniforms->fog_start_uniform,
CLUTTER_FIXED_TO_FLOAT (param));
_COGL_GLES2_CHANGE_UNIFORM (w, FOG_START, fog_start,
CLUTTER_FIXED_TO_FLOAT (param));
break;
case GL_FOG_END:
glUniform1f (w->uniforms->fog_end_uniform,
CLUTTER_FIXED_TO_FLOAT (param));
_COGL_GLES2_CHANGE_UNIFORM (w, FOG_END, fog_end,
CLUTTER_FIXED_TO_FLOAT (param));
break;
}
}
@ -776,14 +1103,15 @@ cogl_wrap_glFogx (GLenum pname, GLfixed param)
void
cogl_wrap_glFogxv (GLenum pname, const GLfixed *params)
{
int i;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
if (pname == GL_FOG_COLOR)
glUniform4f (w->uniforms->fog_color_uniform,
CLUTTER_FIXED_TO_FLOAT (params[0]),
CLUTTER_FIXED_TO_FLOAT (params[1]),
CLUTTER_FIXED_TO_FLOAT (params[2]),
CLUTTER_FIXED_TO_FLOAT (params[3]));
{
for (i = 0; i < 4; i++)
w->fog_color[i] = CLUTTER_FIXED_TO_FLOAT (params[i]);
w->dirty_uniforms |= COGL_GLES2_DIRTY_FOG_COLOR;
}
}
void
@ -792,3 +1120,33 @@ cogl_wrap_glTexParameteri (GLenum target, GLenum pname, GLfloat param)
if (pname != GL_GENERATE_MIPMAP)
glTexParameteri (target, pname, param);
}
void
_cogl_gles2_clear_cache_for_program (CoglHandle user_program)
{
GSList *node, *next, *last = NULL;
CoglGles2WrapperProgram *program;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
/* Remove any cached programs that link against this custom program */
for (node = w->compiled_programs; node; node = next)
{
next = node->next;
program = (CoglGles2WrapperProgram *) node->data;
if (program->settings.user_program == user_program)
{
glDeleteProgram (program->program);
if (last)
last->next = next;
else
w->compiled_programs = next;
g_slist_free1 (node);
}
else
last = node;
}
}

View File

@ -32,39 +32,64 @@ G_BEGIN_DECLS
typedef struct _CoglGles2Wrapper CoglGles2Wrapper;
typedef struct _CoglGles2WrapperUniforms CoglGles2WrapperUniforms;
typedef struct _CoglGles2WrapperSettings CoglGles2WrapperSettings;
typedef struct _CoglGles2WrapperProgram CoglGles2WrapperProgram;
typedef struct _CoglGles2WrapperShader CoglGles2WrapperShader;
#define COGL_GLES2_NUM_CUSTOM_UNIFORMS 16
#define COGL_GLES2_UNBOUND_CUSTOM_UNIFORM -2
/* Must be a power of two */
#define COGL_GLES2_MODELVIEW_STACK_SIZE 32
#define COGL_GLES2_PROJECTION_STACK_SIZE 2
#define COGL_GLES2_TEXTURE_STACK_SIZE 2
#define COGL_GLES2_MODELVIEW_STACK_SIZE 32
#define COGL_GLES2_PROJECTION_STACK_SIZE 2
#define COGL_GLES2_TEXTURE_STACK_SIZE 2
enum
{
COGL_GLES2_DIRTY_MVP_MATRIX = 1 << 0,
COGL_GLES2_DIRTY_MODELVIEW_MATRIX = 1 << 1,
COGL_GLES2_DIRTY_TEXTURE_MATRIX = 1 << 2,
COGL_GLES2_DIRTY_FOG_DENSITY = 1 << 3,
COGL_GLES2_DIRTY_FOG_START = 1 << 4,
COGL_GLES2_DIRTY_FOG_END = 1 << 5,
COGL_GLES2_DIRTY_FOG_COLOR = 1 << 6,
COGL_GLES2_DIRTY_ALPHA_TEST_REF = 1 << 7,
COGL_GLES2_DIRTY_ALL = (1 << 8) - 1
};
struct _CoglGles2WrapperUniforms
{
GLint mvp_matrix_uniform;
GLint modelview_matrix_uniform;
GLint texture_matrix_uniform;
GLint texture_2d_enabled_uniform;
GLint bound_texture_uniform;
GLint alpha_only_uniform;
GLint fog_enabled_uniform;
GLint fog_mode_uniform;
GLint fog_density_uniform;
GLint fog_start_uniform;
GLint fog_end_uniform;
GLint fog_color_uniform;
GLint alpha_test_enabled_uniform;
GLint alpha_test_func_uniform;
GLint alpha_test_ref_uniform;
};
struct _CoglGles2WrapperSettings
{
gboolean texture_2d_enabled;
gboolean alpha_only;
gboolean alpha_test_enabled;
GLint alpha_test_func;
gboolean fog_enabled;
GLint fog_mode;
/* The current in-use user program */
CoglHandle user_program;
};
struct _CoglGles2Wrapper
{
GLuint program;
GLuint vertex_shader;
GLuint fragment_shader;
GLuint matrix_mode;
GLfloat modelview_stack[COGL_GLES2_MODELVIEW_STACK_SIZE * 16];
GLuint modelview_stack_pos;
@ -73,19 +98,61 @@ struct _CoglGles2Wrapper
GLfloat texture_stack[COGL_GLES2_TEXTURE_STACK_SIZE * 16];
GLuint texture_stack_pos;
/* The uniforms for the fixed-functionality emulation program */
CoglGles2WrapperUniforms fixed_uniforms;
/* The uniforms for the currently bound program */
CoglGles2WrapperUniforms *uniforms;
/* The combined modelview and projection matrix is only updated at
the last minute in glDrawArrays to avoid recalculating it for
every change to the modelview matrix */
GLboolean mvp_uptodate;
/* The currently bound program */
CoglGles2WrapperProgram *current_program;
/* The current settings */
CoglGles2WrapperSettings settings;
/* Whether the settings have changed since the last draw */
gboolean settings_dirty;
/* Uniforms that have changed since the last draw */
int dirty_uniforms, dirty_custom_uniforms;
/* List of all compiled program combinations */
GSList *compiled_programs;
/* List of all compiled vertex shaders */
GSList *compiled_vertex_shaders;
/* List of all compiled fragment shaders */
GSList *compiled_fragment_shaders;
/* Values for the uniforms */
GLfloat alpha_test_ref;
GLfloat fog_density;
GLfloat fog_start;
GLfloat fog_end;
GLfloat fog_color[4];
GLfloat custom_uniforms[COGL_GLES2_NUM_CUSTOM_UNIFORMS];
};
struct _CoglGles2WrapperProgram
{
GLuint program;
/* The settings that were used to generate this combination */
CoglGles2WrapperSettings settings;
/* The uniforms for this program */
CoglGles2WrapperUniforms uniforms;
GLint custom_uniforms[COGL_GLES2_NUM_CUSTOM_UNIFORMS];
};
struct _CoglGles2WrapperShader
{
GLuint shader;
/* The settings that were used to generate this shader */
CoglGles2WrapperSettings settings;
};
/* These defines are missing from GL ES 2 but we can still use them
with the wrapper funcions */
with the wrapper functions */
#ifndef GL_MODELVIEW
@ -122,6 +189,9 @@ struct _CoglGles2Wrapper
#define GL_TEXTURE_ENV_MODE 0x2200
#define GL_MODULATE 0x2100
#define GL_EXP 0x8000
#define GL_EXP2 0x8001
#endif /* GL_MODELVIEW */
void cogl_gles2_wrapper_init (CoglGles2Wrapper *wrapper);
@ -184,6 +254,8 @@ void cogl_gles2_wrapper_get_uniforms (GLuint program,
void cogl_gles2_wrapper_update_matrix (CoglGles2Wrapper *wrapper,
GLenum matrix_num);
void _cogl_gles2_clear_cache_for_program (CoglHandle program);
#else /* HAVE_COGL_GLES2 */
/* If we're not using GL ES 2 then just use the GL functions

View File

@ -35,6 +35,8 @@
#ifdef HAVE_COGL_GLES2
#include <string.h>
#include "cogl-shader.h"
#include "cogl-program.h"
@ -45,26 +47,38 @@ COGL_HANDLE_DEFINE (Program, program, program_handles);
static void
_cogl_program_free (CoglProgram *program)
{
/* Frees program resources but its handle is not
released! Do that separately before this! */
int i;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
glDeleteProgram (program->gl_handle);
/* Unref all of the attached shaders */
g_slist_foreach (program->attached_shaders, (GFunc) cogl_shader_unref, NULL);
/* Destroy the list */
g_slist_free (program->attached_shaders);
_cogl_gles2_clear_cache_for_program ((CoglHandle) program);
if (ctx->gles2.settings.user_program == (CoglHandle) program)
{
ctx->gles2.settings.user_program = COGL_INVALID_HANDLE;
ctx->gles2.settings_dirty = TRUE;
}
for (i = 0; i < COGL_GLES2_NUM_CUSTOM_UNIFORMS; i++)
if (program->custom_uniform_names[i])
g_free (program->custom_uniform_names[i]);
}
CoglHandle
cogl_create_program (void)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, 0);
program = g_slice_new (CoglProgram);
program->ref_count = 1;
program->gl_handle = glCreateProgram ();
program->attached_vertex_shader = FALSE;
program->attached_fragment_shader = FALSE;
program->attached_fixed_vertex_shader = FALSE;
program->attached_fixed_fragment_shader = FALSE;
program->attached_shaders = NULL;
memset (program->custom_uniform_names, 0,
COGL_GLES2_NUM_CUSTOM_UNIFORMS * sizeof (char *));
COGL_HANDLE_DEBUG_NEW (program, program);
@ -76,7 +90,6 @@ cogl_program_attach_shader (CoglHandle program_handle,
CoglHandle shader_handle)
{
CoglProgram *program;
CoglShader *shader;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -84,110 +97,66 @@ cogl_program_attach_shader (CoglHandle program_handle,
return;
program = _cogl_program_pointer_from_handle (program_handle);
shader = _cogl_shader_pointer_from_handle (shader_handle);
program->attached_shaders
= g_slist_prepend (program->attached_shaders,
cogl_shader_ref (shader_handle));
if (shader->type == GL_VERTEX_SHADER)
{
if (program->attached_fixed_vertex_shader)
{
glDetachShader (program->gl_handle, ctx->gles2.vertex_shader);
program->attached_fixed_vertex_shader = FALSE;
}
program->attached_vertex_shader = TRUE;
}
else if (shader->type == GL_FRAGMENT_SHADER)
{
if (program->attached_fixed_fragment_shader)
{
glDetachShader (program->gl_handle, ctx->gles2.fragment_shader);
program->attached_fixed_fragment_shader = FALSE;
}
program->attached_fragment_shader = TRUE;
}
glAttachShader (program->gl_handle, shader->gl_handle);
/* Whenever the shader changes we will need to relink the program
with the fixed functionality shaders so we should forget the
cached programs */
_cogl_gles2_clear_cache_for_program (program);
}
void
cogl_program_link (CoglHandle handle)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (!cogl_is_program (handle))
return;
program = _cogl_program_pointer_from_handle (handle);
if (!program->attached_vertex_shader
&& !program->attached_fixed_vertex_shader)
{
glAttachShader (program->gl_handle, ctx->gles2.vertex_shader);
program->attached_fixed_vertex_shader = TRUE;
}
if (!program->attached_fragment_shader
&& !program->attached_fixed_fragment_shader)
{
glAttachShader (program->gl_handle, ctx->gles2.fragment_shader);
program->attached_fixed_fragment_shader = TRUE;
}
/* Set the attributes so that the wrapper functions will still work */
cogl_gles2_wrapper_bind_attributes (program->gl_handle);
glLinkProgram (program->gl_handle);
/* Retrieve the uniforms */
cogl_gles2_wrapper_get_uniforms (program->gl_handle,
&program->uniforms);
/* There's no point in linking the program here because it will have
to be relinked with a different fixed functionality shader
whenever the settings change */
}
void
cogl_program_use (CoglHandle handle)
{
CoglProgram *program;
GLuint gl_handle;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (handle != COGL_INVALID_HANDLE && !cogl_is_program (handle))
return;
if (handle == COGL_INVALID_HANDLE)
{
/* Go back to the fixed-functionality emulator program */
gl_handle = ctx->gles2.program;
ctx->gles2.uniforms = &ctx->gles2.fixed_uniforms;
}
else
{
program = _cogl_program_pointer_from_handle (handle);
gl_handle = program->gl_handle;
/* Use the uniforms in the program */
ctx->gles2.uniforms = &program->uniforms;
}
glUseProgram (gl_handle);
/* Update all of the matrix attributes */
cogl_gles2_wrapper_update_matrix (&ctx->gles2, GL_MODELVIEW);
cogl_gles2_wrapper_update_matrix (&ctx->gles2, GL_PROJECTION);
cogl_gles2_wrapper_update_matrix (&ctx->gles2, GL_TEXTURE);
ctx->gles2.settings.user_program = handle;
ctx->gles2.settings_dirty = TRUE;
}
COGLint
cogl_program_get_uniform_location (CoglHandle handle,
const gchar *uniform_name)
{
int i;
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, 0);
if (!cogl_is_program (handle))
return 0;
return -1;
program = _cogl_program_pointer_from_handle (handle);
return glGetUniformLocation (program->gl_handle, uniform_name);
/* We can't just ask the GL program object for the uniform location
directly because it will change every time the program is linked
with a new fixed functionality shader. Instead we make our own
mapping of uniform numbers and cache the names */
for (i = 0; program->custom_uniform_names[i]
&& i < COGL_GLES2_NUM_CUSTOM_UNIFORMS; i++)
if (!strcmp (program->custom_uniform_names[i], uniform_name))
return i;
if (i < COGL_GLES2_NUM_CUSTOM_UNIFORMS)
{
program->custom_uniform_names[i] = g_strdup (uniform_name);
return i;
}
else
/* We've run out of space for new uniform names so just pretend it
isn't there */
return -1;
}
void
@ -195,7 +164,12 @@ cogl_program_uniform_1f (COGLint uniform_no,
gfloat value)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
glUniform1f (uniform_no, value);
if (uniform_no >= 0 && uniform_no < COGL_GLES2_NUM_CUSTOM_UNIFORMS)
{
ctx->gles2.custom_uniforms[uniform_no] = value;
ctx->gles2.dirty_custom_uniforms |= 1 << uniform_no;
}
}
#else /* HAVE_COGL_GLES2 */

View File

@ -33,16 +33,10 @@ typedef struct _CoglProgram CoglProgram;
struct _CoglProgram
{
guint ref_count;
GLuint gl_handle;
/* Keep track of which types of shader we've attached so we can link
in our replacement fixed functionality shader */
gboolean attached_vertex_shader;
gboolean attached_fragment_shader;
gboolean attached_fixed_vertex_shader;
gboolean attached_fixed_fragment_shader;
GSList *attached_shaders;
CoglGles2WrapperUniforms uniforms;
char *custom_uniform_names[COGL_GLES2_NUM_CUSTOM_UNIFORMS];
};
CoglProgram *_cogl_program_pointer_from_handle (CoglHandle handle);

View File

@ -44,7 +44,11 @@ if test "$#" = 2; then
echo "#ifndef ${guardname}" >> "${headername}";
echo "#define ${guardname}" >> "${headername}";
echo >> "${headername}";
echo "extern const char ${varname}[];" >> "${headername}";
sed -n \
-e 's/^ *\/\*\*\* \([a-zA-Z0-9_]*\) \*\*\*\//extern const char \1[];/p' \
< "$2" >> "${headername}";
echo >> "${headername}";
echo "#endif /* ${guardname} */" >> "${headername}";
@ -57,9 +61,16 @@ else
output_copyright "${cname}";
echo >> "${cname}";
echo "const char ${varname}[] =" >> "${cname}";
sed -e 's/"/\\"/' -e 's/^/ \"/' -e 's/$/\\n"/' \
sed -n \
-e h \
-e 's/^ *\/\*\*\* \([a-zA-Z0-9_]*\) \*\*\*\// ;\nconst char \1[] =/' \
-e 't got' \
-e g \
-e 's/"/\\"/' \
-e 's/^/ "/' \
-e 's/$/\\n"/' \
-e ': got' \
-e p \
< "$1" >> "${cname}";
echo " ;" >> "${cname}";
fi;