mirror of
https://github.com/brl/mutter.git
synced 2024-12-28 05:42:14 +00:00
72f4ddf532
Since using addresses that might change is something that finally the FSF acknowledge as a plausible scenario (after changing address twice), the license blurb in the source files should use the URI for getting the license in case the library did not come with it. Not that URIs cannot possibly change, but at least it's easier to set up a redirection at the same place. As a side note: this commit closes the oldes bug in Clutter's bug report tool. http://bugzilla.openedhand.com/show_bug.cgi?id=521
1720 lines
49 KiB
C
1720 lines
49 KiB
C
/*
|
|
* Cogl
|
|
*
|
|
* An object oriented GL/GLES Abstraction/Utility Layer
|
|
*
|
|
* Copyright (C) 2008,2009 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <clutter/clutter-fixed.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
/* We don't want to get the remaps from the gl* functions to the
|
|
cogl_wrap_gl* functions in this file because we need to be able to
|
|
call the base version */
|
|
#define COGL_GLES2_WRAPPER_NO_REMAP 1
|
|
|
|
#include "cogl.h"
|
|
#include "cogl-gles2-wrapper.h"
|
|
#include "cogl-fixed-vertex-shader.h"
|
|
#include "cogl-fixed-fragment-shader.h"
|
|
#include "cogl-context.h"
|
|
#include "cogl-shader-private.h"
|
|
#include "cogl-program.h"
|
|
#include "cogl-internal.h"
|
|
|
|
#define _COGL_GET_GLES2_WRAPPER(wvar, retval) \
|
|
CoglGles2Wrapper *wvar; \
|
|
{ \
|
|
CoglContext *__ctxvar = _cogl_context_get_default (); \
|
|
if (__ctxvar == NULL) return retval; \
|
|
wvar = &__ctxvar->drv.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_COLOR_ATTRIB 1
|
|
#define COGL_GLES2_WRAPPER_NORMAL_ATTRIB 2
|
|
|
|
|
|
static GLuint
|
|
cogl_gles2_wrapper_create_shader (GLenum type, const char *source)
|
|
{
|
|
GLuint shader;
|
|
GLint source_len = strlen (source);
|
|
GLint status;
|
|
|
|
shader = glCreateShader (type);
|
|
glShaderSource (shader, 1, &source, &source_len);
|
|
glCompileShader (shader);
|
|
|
|
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
|
|
|
|
if (!status)
|
|
{
|
|
char shader_log[1024];
|
|
GLint len;
|
|
|
|
glGetShaderInfoLog (shader, sizeof (shader_log) - 1, &len, shader_log);
|
|
shader_log[len] = '\0';
|
|
|
|
g_critical ("%s", shader_log);
|
|
|
|
glDeleteShader (shader);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
static void
|
|
initialize_texture_units (CoglGles2Wrapper *w)
|
|
{
|
|
/* We save the active texture unit since we may need to temporarily
|
|
* change this to initialise each new texture unit and we want to
|
|
* restore the active unit afterwards */
|
|
int initial_active_unit = w->active_texture_unit;
|
|
GLint prev_mode;
|
|
int i;
|
|
|
|
/* We will need to set the matrix mode to GL_TEXTURE to
|
|
* initialise any new texture units, so we save the current
|
|
* mode for restoring afterwards */
|
|
GE( cogl_wrap_glGetIntegerv (GL_MATRIX_MODE, &prev_mode));
|
|
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
{
|
|
CoglGles2WrapperTextureUnit *new_unit;
|
|
|
|
new_unit = w->texture_units + i;
|
|
memset (new_unit, 0, sizeof (CoglGles2WrapperTextureUnit));
|
|
|
|
w->active_texture_unit = i;
|
|
GE( cogl_wrap_glMatrixMode (GL_TEXTURE));
|
|
GE( cogl_wrap_glLoadIdentity ());
|
|
|
|
/* The real GL default is GL_MODULATE but the shader only
|
|
supports GL_COMBINE so let's default to that instead */
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
|
|
GL_COMBINE) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB,
|
|
GL_MODULATE) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB,
|
|
GL_PREVIOUS) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB,
|
|
GL_TEXTURE) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB,
|
|
GL_SRC_COLOR) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB,
|
|
GL_SRC_COLOR) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA,
|
|
GL_MODULATE) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA,
|
|
GL_PREVIOUS) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA,
|
|
GL_TEXTURE) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA,
|
|
GL_SRC_COLOR) );
|
|
GE( cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA,
|
|
GL_SRC_COLOR) );
|
|
}
|
|
|
|
GE( cogl_wrap_glMatrixMode ((GLenum) prev_mode));
|
|
|
|
w->settings.texture_units = 0;
|
|
|
|
w->active_texture_unit = initial_active_unit;
|
|
}
|
|
|
|
void
|
|
cogl_gles2_wrapper_init (CoglGles2Wrapper *wrapper)
|
|
{
|
|
GLfloat default_fog_color[4] = { 0, 0, 0, 0 };
|
|
|
|
memset (wrapper, 0, sizeof (CoglGles2Wrapper));
|
|
|
|
/* Initialize the stacks */
|
|
cogl_wrap_glMatrixMode (GL_PROJECTION);
|
|
cogl_wrap_glLoadIdentity ();
|
|
cogl_wrap_glMatrixMode (GL_MODELVIEW);
|
|
cogl_wrap_glLoadIdentity ();
|
|
|
|
/* The gl*ActiveTexture wrappers will initialise the texture
|
|
* stack for the texture unit when it's first activated */
|
|
cogl_wrap_glActiveTexture (GL_TEXTURE0);
|
|
cogl_wrap_glClientActiveTexture (GL_TEXTURE0);
|
|
|
|
/* Initialize the fogging options */
|
|
cogl_wrap_glDisable (GL_FOG);
|
|
cogl_wrap_glFogf (GL_FOG_MODE, GL_LINEAR);
|
|
cogl_wrap_glFogf (GL_FOG_DENSITY, 1.0);
|
|
cogl_wrap_glFogf (GL_FOG_START, 0);
|
|
cogl_wrap_glFogf (GL_FOG_END, 1);
|
|
cogl_wrap_glFogfv (GL_FOG_COLOR, default_fog_color);
|
|
|
|
/* Initialize alpha testing */
|
|
cogl_wrap_glDisable (GL_ALPHA_TEST);
|
|
cogl_wrap_glAlphaFunc (GL_ALWAYS, 0.0f);
|
|
|
|
initialize_texture_units (wrapper);
|
|
}
|
|
|
|
static int
|
|
cogl_gles2_get_n_args_for_combine_func (GLenum func)
|
|
{
|
|
switch (func)
|
|
{
|
|
case GL_REPLACE:
|
|
return 1;
|
|
case GL_MODULATE:
|
|
case GL_ADD:
|
|
case GL_ADD_SIGNED:
|
|
case GL_SUBTRACT:
|
|
case GL_DOT3_RGB:
|
|
case GL_DOT3_RGBA:
|
|
return 2;
|
|
case GL_INTERPOLATE:
|
|
return 3;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
cogl_gles2_settings_equal (const CoglGles2WrapperSettings *a,
|
|
const CoglGles2WrapperSettings *b,
|
|
gboolean vertex_tests,
|
|
gboolean fragment_tests)
|
|
{
|
|
int i;
|
|
|
|
if (a->texture_units != b->texture_units)
|
|
return FALSE;
|
|
|
|
if (fragment_tests)
|
|
{
|
|
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;
|
|
|
|
/* Compare layer combine operation for each active unit */
|
|
if (fragment_tests)
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (a->texture_units, i))
|
|
{
|
|
const CoglGles2WrapperTexEnv *tex_env_a = a->tex_env + i;
|
|
const CoglGles2WrapperTexEnv *tex_env_b = b->tex_env + i;
|
|
int arg, n_args;
|
|
GLenum func;
|
|
|
|
func = tex_env_a->texture_combine_rgb_func;
|
|
|
|
if (func != tex_env_b->texture_combine_rgb_func)
|
|
return FALSE;
|
|
|
|
n_args = cogl_gles2_get_n_args_for_combine_func (func);
|
|
|
|
for (arg = 0; arg < n_args; arg++)
|
|
{
|
|
if (tex_env_a->texture_combine_rgb_src[arg] !=
|
|
tex_env_b->texture_combine_rgb_src[arg])
|
|
return FALSE;
|
|
if (tex_env_a->texture_combine_rgb_op[arg] !=
|
|
tex_env_b->texture_combine_rgb_op[arg])
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static CoglGles2WrapperShader *
|
|
cogl_gles2_get_vertex_shader (const CoglGles2WrapperSettings *settings)
|
|
{
|
|
GString *shader_source;
|
|
GLuint shader_obj;
|
|
CoglGles2WrapperShader *shader;
|
|
GSList *node;
|
|
int i;
|
|
int n_texture_units = 0;
|
|
|
|
_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_per_vertex_attribs);
|
|
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i))
|
|
g_string_append_printf (shader_source,
|
|
"attribute vec4 multi_tex_coord_attrib%d;\n",
|
|
i);
|
|
|
|
/* Find the biggest enabled texture unit index */
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i))
|
|
n_texture_units = i + 1;
|
|
|
|
g_string_append (shader_source, cogl_fixed_vertex_shader_transform_matrices);
|
|
g_string_append (shader_source, cogl_fixed_vertex_shader_output_variables);
|
|
|
|
if (n_texture_units > 0)
|
|
{
|
|
g_string_append_printf (shader_source,
|
|
"uniform mat4 texture_matrix[%d];\n",
|
|
n_texture_units);
|
|
|
|
g_string_append_printf (shader_source,
|
|
"varying vec2 tex_coord[%d];",
|
|
n_texture_units);
|
|
}
|
|
|
|
g_string_append (shader_source, cogl_fixed_vertex_shader_fogging_options);
|
|
g_string_append (shader_source, cogl_fixed_vertex_shader_main_start);
|
|
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i))
|
|
{
|
|
g_string_append_printf (shader_source,
|
|
"transformed_tex_coord = "
|
|
"texture_matrix[%d] "
|
|
" * multi_tex_coord_attrib%d;\n",
|
|
i, i);
|
|
g_string_append_printf (shader_source,
|
|
"tex_coord[%d] = transformed_tex_coord.st "
|
|
" / transformed_tex_coord.q;\n",
|
|
i);
|
|
}
|
|
|
|
g_string_append (shader_source, cogl_fixed_vertex_shader_frag_color_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 void
|
|
cogl_gles2_add_arg (int unit,
|
|
GLenum src,
|
|
GLenum operand,
|
|
const char *swizzle,
|
|
GString *shader_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:
|
|
g_string_append_printf (shader_source,
|
|
"texture2D (texture_unit[%d], tex_coord[%d]).%s",
|
|
unit, unit, swizzle);
|
|
break;
|
|
|
|
case GL_CONSTANT:
|
|
g_string_append_printf (shader_source, "combine_constant[%d].%s",
|
|
unit, swizzle);
|
|
break;
|
|
|
|
case GL_PREVIOUS:
|
|
if (unit > 0)
|
|
{
|
|
g_string_append_printf (shader_source, "gl_FragColor.%s", swizzle);
|
|
break;
|
|
}
|
|
/* flow through */
|
|
case GL_PRIMARY_COLOR:
|
|
g_string_append_printf (shader_source, "frag_color.%s", swizzle);
|
|
break;
|
|
|
|
default:
|
|
if (src >= GL_TEXTURE0 &&
|
|
src < GL_TEXTURE0 + COGL_GLES2_MAX_TEXTURE_UNITS)
|
|
g_string_append_printf (shader_source,
|
|
"texture2D (texture_unit[%d], "
|
|
"tex_coord[%d]).%s",
|
|
src - GL_TEXTURE0,
|
|
src - GL_TEXTURE0,
|
|
swizzle);
|
|
break;
|
|
}
|
|
|
|
g_string_append_c (shader_source, ')');
|
|
}
|
|
|
|
static void
|
|
cogl_gles2_add_operation (int unit,
|
|
GLenum combine_func,
|
|
const GLenum *sources,
|
|
const GLenum *operands,
|
|
const char *swizzle,
|
|
GString *shader_source)
|
|
{
|
|
switch (combine_func)
|
|
{
|
|
case GL_REPLACE:
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
swizzle, shader_source);
|
|
break;
|
|
|
|
case GL_MODULATE:
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
swizzle, shader_source);
|
|
g_string_append (shader_source, " * ");
|
|
cogl_gles2_add_arg (unit, sources[1], operands[1],
|
|
swizzle, shader_source);
|
|
break;
|
|
|
|
case GL_ADD:
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
swizzle, shader_source);
|
|
g_string_append (shader_source, " + ");
|
|
cogl_gles2_add_arg (unit, sources[1], operands[1],
|
|
swizzle, shader_source);
|
|
break;
|
|
|
|
case GL_ADD_SIGNED:
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
swizzle, shader_source);
|
|
g_string_append (shader_source, " + ");
|
|
cogl_gles2_add_arg (unit, sources[1], operands[1],
|
|
swizzle, shader_source);
|
|
g_string_append_printf (shader_source,
|
|
" - vec4(0.5, 0.5, 0.5, 0.5).%s",
|
|
swizzle);
|
|
break;
|
|
|
|
case GL_SUBTRACT:
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
swizzle, shader_source);
|
|
g_string_append (shader_source, " - ");
|
|
cogl_gles2_add_arg (unit, sources[1], operands[1],
|
|
swizzle, shader_source);
|
|
break;
|
|
|
|
case GL_INTERPOLATE:
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
swizzle, shader_source);
|
|
g_string_append (shader_source, " * ");
|
|
cogl_gles2_add_arg (unit, sources[2], operands[2],
|
|
swizzle, shader_source);
|
|
g_string_append (shader_source, " + ");
|
|
cogl_gles2_add_arg (unit, sources[1], operands[1],
|
|
swizzle, shader_source);
|
|
g_string_append_printf (shader_source,
|
|
" * (vec4(1.0, 1.0, 1.0, 1.0).%s - ",
|
|
swizzle);
|
|
cogl_gles2_add_arg (unit, sources[2], operands[2],
|
|
swizzle, shader_source);
|
|
g_string_append_c (shader_source, ')');
|
|
break;
|
|
|
|
case GL_DOT3_RGB:
|
|
case GL_DOT3_RGBA:
|
|
g_string_append (shader_source, "vec4(4 * ((");
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
"r", shader_source);
|
|
g_string_append (shader_source, " - 0.5) * (");
|
|
cogl_gles2_add_arg (unit, sources[1], operands[1],
|
|
"r", shader_source);
|
|
g_string_append (shader_source, " - 0.5) + (");
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
"g", shader_source);
|
|
g_string_append (shader_source, " - 0.5) * (");
|
|
cogl_gles2_add_arg (unit, sources[1], operands[1],
|
|
"g", shader_source);
|
|
g_string_append (shader_source, " - 0.5) + (");
|
|
cogl_gles2_add_arg (unit, sources[0], operands[0],
|
|
"b", shader_source);
|
|
g_string_append (shader_source, " - 0.5) * (");
|
|
cogl_gles2_add_arg (unit, sources[1], operands[1],
|
|
"b", shader_source);
|
|
g_string_append_printf (shader_source, " - 0.5))).%s", swizzle);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
cogl_gles2_rgb_and_alpha_equal (const CoglGles2WrapperTexEnv *tex_env)
|
|
{
|
|
int arg, n_args;
|
|
|
|
if (tex_env->texture_combine_rgb_func != tex_env->texture_combine_alpha_func)
|
|
return FALSE;
|
|
|
|
n_args =
|
|
cogl_gles2_get_n_args_for_combine_func (tex_env->texture_combine_rgb_func);
|
|
|
|
for (arg = 0; arg < n_args; arg++)
|
|
{
|
|
if (tex_env->texture_combine_rgb_src[arg] !=
|
|
tex_env->texture_combine_alpha_src[arg])
|
|
return FALSE;
|
|
|
|
if (tex_env->texture_combine_rgb_op[arg] != GL_SRC_COLOR ||
|
|
tex_env->texture_combine_alpha_op[arg] != GL_SRC_ALPHA)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static CoglGles2WrapperShader *
|
|
cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings)
|
|
{
|
|
GString *shader_source;
|
|
GLuint shader_obj;
|
|
CoglGles2WrapperShader *shader;
|
|
GSList *node;
|
|
int i;
|
|
int n_texture_units = 0;
|
|
|
|
_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_variables_start);
|
|
|
|
/* Find the biggest enabled texture unit index */
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i))
|
|
n_texture_units = i + 1;
|
|
|
|
g_string_append (shader_source, cogl_fixed_fragment_shader_inputs);
|
|
|
|
if (n_texture_units > 0)
|
|
{
|
|
g_string_append_printf (shader_source,
|
|
"varying vec2 tex_coord[%d];\n",
|
|
n_texture_units);
|
|
|
|
g_string_append (shader_source, cogl_fixed_fragment_shader_texturing_options);
|
|
g_string_append_printf (shader_source,
|
|
"uniform sampler2D texture_unit[%d];\n",
|
|
n_texture_units);
|
|
}
|
|
|
|
g_string_append (shader_source, cogl_fixed_fragment_shader_fogging_options);
|
|
|
|
g_string_append (shader_source, cogl_fixed_fragment_shader_main_declare);
|
|
|
|
g_string_append (shader_source, cogl_fixed_fragment_shader_main_start);
|
|
|
|
/* 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 */
|
|
g_string_append (shader_source,
|
|
"vec4 frag_color_copy = frag_color;\n");
|
|
|
|
/* If there are no textures units enabled then we can just directly
|
|
use the color from the vertex shader */
|
|
if (n_texture_units == 0)
|
|
g_string_append (shader_source, "gl_FragColor = frag_color;\n");
|
|
else
|
|
/* Otherwise we need to calculate the value based on the layer
|
|
combine settings */
|
|
for (i = 0; i < n_texture_units; i++)
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i))
|
|
{
|
|
const CoglGles2WrapperTexEnv *tex_env = settings->tex_env + i;
|
|
|
|
/* If the rgb and alpha combine functions are the same then
|
|
we can do both with a single statement, otherwise we need
|
|
to do them separately */
|
|
if (cogl_gles2_rgb_and_alpha_equal (tex_env))
|
|
{
|
|
g_string_append (shader_source, "gl_FragColor.rgba = ");
|
|
cogl_gles2_add_operation (i,
|
|
tex_env->texture_combine_rgb_func,
|
|
tex_env->texture_combine_rgb_src,
|
|
tex_env->texture_combine_rgb_op,
|
|
"rgba",
|
|
shader_source);
|
|
g_string_append (shader_source, ";\n");
|
|
}
|
|
else
|
|
{
|
|
g_string_append (shader_source, "gl_FragColor.rgb = ");
|
|
cogl_gles2_add_operation (i,
|
|
tex_env->texture_combine_rgb_func,
|
|
tex_env->texture_combine_rgb_src,
|
|
tex_env->texture_combine_rgb_op,
|
|
"rgb",
|
|
shader_source);
|
|
g_string_append (shader_source,
|
|
";\n"
|
|
"gl_FragColor.a = ");
|
|
cogl_gles2_add_operation (i,
|
|
tex_env->texture_combine_alpha_func,
|
|
tex_env->texture_combine_alpha_src,
|
|
tex_env->texture_combine_alpha_op,
|
|
"a",
|
|
shader_source);
|
|
g_string_append (shader_source, ";\n");
|
|
}
|
|
}
|
|
|
|
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 void
|
|
cogl_gles2_wrapper_get_locations (GLuint program,
|
|
CoglGles2WrapperSettings *settings,
|
|
CoglGles2WrapperUniforms *uniforms,
|
|
CoglGles2WrapperAttributes *attribs)
|
|
{
|
|
int i;
|
|
|
|
uniforms->mvp_matrix_uniform
|
|
= glGetUniformLocation (program, "mvp_matrix");
|
|
uniforms->modelview_matrix_uniform
|
|
= glGetUniformLocation (program, "modelview_matrix");
|
|
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i))
|
|
{
|
|
char *matrix_var_name = g_strdup_printf ("texture_matrix[%d]", i);
|
|
char *sampler_var_name = g_strdup_printf ("texture_unit[%d]", i);
|
|
char *tex_coord_var_name =
|
|
g_strdup_printf ("multi_tex_coord_attrib%d", i);
|
|
|
|
uniforms->texture_matrix_uniforms[i]
|
|
= glGetUniformLocation (program, matrix_var_name);
|
|
uniforms->texture_sampler_uniforms[i]
|
|
= glGetUniformLocation (program, sampler_var_name);
|
|
attribs->multi_texture_coords[i]
|
|
= glGetAttribLocation (program, tex_coord_var_name);
|
|
|
|
g_free (tex_coord_var_name);
|
|
g_free (sampler_var_name);
|
|
g_free (matrix_var_name);
|
|
}
|
|
else
|
|
{
|
|
uniforms->texture_matrix_uniforms[i] = -1;
|
|
uniforms->texture_sampler_uniforms[i] = -1;
|
|
attribs->multi_texture_coords[i] = -1;
|
|
}
|
|
|
|
uniforms->fog_density_uniform
|
|
= glGetUniformLocation (program, "fog_density");
|
|
uniforms->fog_start_uniform
|
|
= glGetUniformLocation (program, "fog_start");
|
|
uniforms->fog_end_uniform
|
|
= glGetUniformLocation (program, "fog_end");
|
|
uniforms->fog_color_uniform
|
|
= glGetUniformLocation (program, "fog_color");
|
|
|
|
uniforms->alpha_test_ref_uniform
|
|
= glGetUniformLocation (program, "alpha_test_ref");
|
|
}
|
|
|
|
static void
|
|
cogl_gles2_wrapper_bind_attributes (GLuint program)
|
|
{
|
|
glBindAttribLocation (program, COGL_GLES2_WRAPPER_VERTEX_ATTRIB,
|
|
"vertex_attrib");
|
|
glBindAttribLocation (program, COGL_GLES2_WRAPPER_COLOR_ATTRIB,
|
|
"color_attrib");
|
|
glBindAttribLocation (program, COGL_GLES2_WRAPPER_NORMAL_ATTRIB,
|
|
"normal_attrib");
|
|
}
|
|
|
|
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 == COGL_SHADER_TYPE_VERTEX)
|
|
custom_vertex_shader = TRUE;
|
|
else if (shader->type == COGL_SHADER_TYPE_FRAGMENT)
|
|
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 shader_log[1024];
|
|
GLint len;
|
|
|
|
glGetProgramInfoLog (program->program, sizeof (shader_log) - 1, &len, shader_log);
|
|
shader_log[len] = '\0';
|
|
|
|
g_critical ("%s", shader_log);
|
|
|
|
glDeleteProgram (program->program);
|
|
g_slice_free (CoglGles2WrapperProgram, program);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
program->settings = *settings;
|
|
|
|
cogl_gles2_wrapper_get_locations (program->program,
|
|
&program->settings,
|
|
&program->uniforms,
|
|
&program->attributes);
|
|
|
|
/* 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_deinit (CoglGles2Wrapper *wrapper)
|
|
{
|
|
GSList *node, *next;
|
|
int i;
|
|
|
|
for (node = wrapper->compiled_programs; node; node = next)
|
|
{
|
|
next = node->next;
|
|
glDeleteProgram (((CoglGles2WrapperProgram *) node->data)->program);
|
|
g_slist_free1 (node);
|
|
}
|
|
wrapper->compiled_programs = NULL;
|
|
|
|
for (node = wrapper->compiled_vertex_shaders; node; node = next)
|
|
{
|
|
next = node->next;
|
|
glDeleteShader (((CoglGles2WrapperShader *) node->data)->shader);
|
|
g_slist_free1 (node);
|
|
}
|
|
wrapper->compiled_vertex_shaders = NULL;
|
|
|
|
for (node = wrapper->compiled_fragment_shaders; node; node = next)
|
|
{
|
|
next = node->next;
|
|
glDeleteShader (((CoglGles2WrapperShader *) node->data)->shader);
|
|
g_slist_free1 (node);
|
|
}
|
|
wrapper->compiled_fragment_shaders = NULL;
|
|
|
|
for (i = 0; i < COGL_GLES2_NUM_CUSTOM_UNIFORMS; i++)
|
|
if (wrapper->custom_uniforms[i].count > 1)
|
|
g_free (wrapper->custom_uniforms[i].v.array);
|
|
}
|
|
|
|
static void
|
|
cogl_gles2_wrapper_notify_matrix_changed (CoglGles2Wrapper *wrapper,
|
|
GLenum mode)
|
|
{
|
|
CoglGles2WrapperTextureUnit *texture_unit;
|
|
|
|
switch (mode)
|
|
{
|
|
case GL_MODELVIEW:
|
|
wrapper->dirty_uniforms |= COGL_GLES2_DIRTY_MVP_MATRIX
|
|
| COGL_GLES2_DIRTY_MODELVIEW_MATRIX;
|
|
break;
|
|
|
|
case GL_PROJECTION:
|
|
wrapper->dirty_uniforms |= COGL_GLES2_DIRTY_MVP_MATRIX;
|
|
break;
|
|
|
|
case GL_TEXTURE:
|
|
wrapper->dirty_uniforms |= COGL_GLES2_DIRTY_TEXTURE_MATRICES;
|
|
texture_unit = wrapper->texture_units + wrapper->active_texture_unit;
|
|
texture_unit->dirty_matrix = 1;
|
|
break;
|
|
|
|
default:
|
|
g_critical ("%s: Unexpected matrix mode %d\n", G_STRFUNC, mode);
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glMatrixMode (GLenum mode)
|
|
{
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
w->matrix_mode = mode;
|
|
}
|
|
|
|
static CoglMatrix *
|
|
cogl_gles2_get_current_matrix (CoglGles2Wrapper *wrapper)
|
|
{
|
|
CoglGles2WrapperTextureUnit *texture_unit;
|
|
|
|
switch (wrapper->matrix_mode)
|
|
{
|
|
default:
|
|
g_critical ("%s: Unexpected matrix mode %d\n",
|
|
G_STRFUNC, wrapper->matrix_mode);
|
|
/* flow through */
|
|
|
|
case GL_MODELVIEW:
|
|
return &wrapper->modelview_matrix;
|
|
|
|
case GL_PROJECTION:
|
|
return &wrapper->projection_matrix;
|
|
|
|
case GL_TEXTURE:
|
|
texture_unit = wrapper->texture_units + wrapper->active_texture_unit;
|
|
return &texture_unit->texture_matrix;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glLoadIdentity (void)
|
|
{
|
|
CoglMatrix *matrix;
|
|
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
matrix = cogl_gles2_get_current_matrix (w);
|
|
|
|
cogl_matrix_init_identity (matrix);
|
|
|
|
cogl_gles2_wrapper_notify_matrix_changed (w, w->matrix_mode);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glLoadMatrixf (const GLfloat *m)
|
|
{
|
|
CoglMatrix *matrix;
|
|
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
matrix = cogl_gles2_get_current_matrix (w);
|
|
|
|
cogl_matrix_init_from_array (matrix, m);
|
|
|
|
cogl_gles2_wrapper_notify_matrix_changed (w, w->matrix_mode);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glVertexPointer (GLint size, GLenum type, GLsizei stride,
|
|
const GLvoid *pointer)
|
|
{
|
|
glVertexAttribPointer (COGL_GLES2_WRAPPER_VERTEX_ATTRIB, size, type,
|
|
GL_FALSE, stride, pointer);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glTexCoordPointer (GLint size, GLenum type, GLsizei stride,
|
|
const GLvoid *pointer)
|
|
{
|
|
int active_unit;
|
|
CoglGles2WrapperTextureUnit *texture_unit;
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
active_unit = w->active_client_texture_unit;
|
|
|
|
texture_unit = w->texture_units + active_unit;
|
|
texture_unit->texture_coords_size = size;
|
|
texture_unit->texture_coords_type = type;
|
|
texture_unit->texture_coords_stride = stride;
|
|
texture_unit->texture_coords_pointer = pointer;
|
|
|
|
w->dirty_attribute_pointers
|
|
|= COGL_GLES2_DIRTY_TEX_COORD_VERTEX_ATTRIB;
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glColorPointer (GLint size, GLenum type, GLsizei stride,
|
|
const GLvoid *pointer)
|
|
{
|
|
glVertexAttribPointer (COGL_GLES2_WRAPPER_COLOR_ATTRIB, size, type,
|
|
GL_TRUE, stride, pointer);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer)
|
|
{
|
|
glVertexAttribPointer (COGL_GLES2_WRAPPER_NORMAL_ATTRIB, 1, type,
|
|
GL_FALSE, stride, pointer);
|
|
}
|
|
|
|
static void
|
|
cogl_gles2_do_set_uniform (GLint location, CoglBoxedValue *value)
|
|
{
|
|
switch (value->type)
|
|
{
|
|
case COGL_BOXED_NONE:
|
|
break;
|
|
|
|
case COGL_BOXED_INT:
|
|
{
|
|
int *ptr;
|
|
|
|
if (value->count == 1)
|
|
ptr = value->v.int_value;
|
|
else
|
|
ptr = value->v.int_array;
|
|
|
|
switch (value->size)
|
|
{
|
|
case 1: glUniform1iv (location, value->count, ptr); break;
|
|
case 2: glUniform2iv (location, value->count, ptr); break;
|
|
case 3: glUniform3iv (location, value->count, ptr); break;
|
|
case 4: glUniform4iv (location, value->count, ptr); break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case COGL_BOXED_FLOAT:
|
|
{
|
|
float *ptr;
|
|
|
|
if (value->count == 1)
|
|
ptr = value->v.float_value;
|
|
else
|
|
ptr = value->v.float_array;
|
|
|
|
switch (value->size)
|
|
{
|
|
case 1: glUniform1fv (location, value->count, ptr); break;
|
|
case 2: glUniform2fv (location, value->count, ptr); break;
|
|
case 3: glUniform3fv (location, value->count, ptr); break;
|
|
case 4: glUniform4fv (location, value->count, ptr); break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case COGL_BOXED_MATRIX:
|
|
{
|
|
float *ptr;
|
|
|
|
if (value->count == 1)
|
|
ptr = value->v.matrix;
|
|
else
|
|
ptr = value->v.float_array;
|
|
|
|
switch (value->size)
|
|
{
|
|
case 2:
|
|
glUniformMatrix2fv (location, value->count, value->transpose, ptr);
|
|
break;
|
|
case 3:
|
|
glUniformMatrix3fv (location, value->count, value->transpose, ptr);
|
|
break;
|
|
case 4:
|
|
glUniformMatrix4fv (location, value->count, value->transpose, ptr);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
cogl_wrap_prepare_for_draw (void)
|
|
{
|
|
CoglGles2WrapperProgram *program;
|
|
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
/* Check if we need to switch programs */
|
|
if (w->settings_dirty)
|
|
{
|
|
/* Find or create a program for the current settings */
|
|
program = cogl_gles2_wrapper_get_program (&w->settings);
|
|
|
|
if (program == NULL)
|
|
/* Can't compile a shader so there is nothing we can do */
|
|
return;
|
|
|
|
/* 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;
|
|
|
|
/* 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)))
|
|
{
|
|
CoglMatrix mvp_matrix;
|
|
CoglMatrix *modelview_matrix = &w->modelview_matrix;
|
|
CoglMatrix *projection_matrix = &w->projection_matrix;
|
|
|
|
/* FIXME: we should have a cogl_matrix_copy () function */
|
|
memcpy (&mvp_matrix, projection_matrix, sizeof (CoglMatrix));
|
|
|
|
cogl_matrix_multiply (&mvp_matrix, &mvp_matrix, modelview_matrix);
|
|
|
|
if (program->uniforms.mvp_matrix_uniform != -1)
|
|
glUniformMatrix4fv (program->uniforms.mvp_matrix_uniform, 1,
|
|
GL_FALSE, (float *) &mvp_matrix);
|
|
if (program->uniforms.modelview_matrix_uniform != -1)
|
|
glUniformMatrix4fv (program->uniforms.modelview_matrix_uniform, 1,
|
|
GL_FALSE, (float *) &modelview_matrix);
|
|
}
|
|
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_TEXTURE_MATRICES))
|
|
{
|
|
int i;
|
|
|
|
/* TODO - we should probably have a per unit dirty flag too */
|
|
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
{
|
|
CoglGles2WrapperTextureUnit *texture_unit;
|
|
GLint uniform = program->uniforms.texture_matrix_uniforms[i];
|
|
|
|
texture_unit = w->texture_units + i;
|
|
if (uniform != -1)
|
|
glUniformMatrix4fv (uniform, 1, GL_FALSE,
|
|
(float *) &texture_unit->texture_matrix);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_TEXTURE_UNITS))
|
|
{
|
|
int i;
|
|
|
|
/* TODO - we should probably have a per unit dirty flag too */
|
|
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
{
|
|
GLint uniform = program->uniforms.texture_sampler_uniforms[i];
|
|
|
|
if (uniform != -1)
|
|
glUniform1i (uniform, i);
|
|
}
|
|
}
|
|
|
|
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)
|
|
cogl_gles2_do_set_uniform (program->custom_uniforms[i],
|
|
&w->custom_uniforms[i]);
|
|
}
|
|
}
|
|
|
|
w->dirty_custom_uniforms = 0;
|
|
}
|
|
|
|
if (w->dirty_attribute_pointers
|
|
& COGL_GLES2_DIRTY_TEX_COORD_VERTEX_ATTRIB)
|
|
{
|
|
int i;
|
|
|
|
/* TODO - coverage test */
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (w->settings.texture_units, i))
|
|
{
|
|
GLint tex_coord_var_index;
|
|
CoglGles2WrapperTextureUnit *texture_unit;
|
|
|
|
texture_unit = w->texture_units + w->active_texture_unit;
|
|
if (!texture_unit->texture_coords_enabled)
|
|
continue;
|
|
|
|
/* TODO - we should probably have a per unit dirty flag too */
|
|
|
|
/* TODO - coverage test */
|
|
tex_coord_var_index = program->attributes.multi_texture_coords[i];
|
|
glVertexAttribPointer (tex_coord_var_index,
|
|
texture_unit->texture_coords_size,
|
|
texture_unit->texture_coords_type,
|
|
GL_FALSE,
|
|
texture_unit->texture_coords_stride,
|
|
texture_unit->texture_coords_pointer);
|
|
}
|
|
}
|
|
|
|
if (w->dirty_vertex_attrib_enables)
|
|
{
|
|
int i;
|
|
|
|
/* TODO - coverage test */
|
|
|
|
/* TODO - we should probably have a per unit dirty flag too */
|
|
|
|
for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++)
|
|
{
|
|
CoglGles2WrapperTextureUnit *texture_unit
|
|
= w->texture_units + w->active_texture_unit;
|
|
GLint attrib = program->attributes.multi_texture_coords[i];
|
|
|
|
if (attrib != -1)
|
|
{
|
|
if (texture_unit->texture_coords_enabled)
|
|
glEnableVertexAttribArray (attrib);
|
|
else
|
|
glDisableVertexAttribArray (attrib);
|
|
}
|
|
}
|
|
|
|
w->dirty_vertex_attrib_enables = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glDrawArrays (GLenum mode, GLint first, GLsizei count)
|
|
{
|
|
cogl_wrap_prepare_for_draw ();
|
|
|
|
glDrawArrays (mode, first, count);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glDrawElements (GLenum mode, GLsizei count, GLenum type,
|
|
const GLvoid *indices)
|
|
{
|
|
cogl_wrap_prepare_for_draw ();
|
|
|
|
glDrawElements (mode, count, type, indices);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLint param)
|
|
{
|
|
if (target == GL_TEXTURE_ENV)
|
|
{
|
|
CoglGles2WrapperTexEnv *tex_env;
|
|
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
tex_env = w->settings.tex_env + w->active_texture_unit;
|
|
|
|
switch (pname)
|
|
{
|
|
case GL_COMBINE_RGB:
|
|
tex_env->texture_combine_rgb_func = param;
|
|
break;
|
|
case GL_COMBINE_ALPHA:
|
|
tex_env->texture_combine_alpha_func = param;
|
|
break;
|
|
case GL_SRC0_RGB:
|
|
case GL_SRC1_RGB:
|
|
case GL_SRC2_RGB:
|
|
tex_env->texture_combine_rgb_src[pname - GL_SRC0_RGB] = param;
|
|
break;
|
|
case GL_SRC0_ALPHA:
|
|
case GL_SRC1_ALPHA:
|
|
case GL_SRC2_ALPHA:
|
|
tex_env->texture_combine_alpha_src[pname - GL_SRC0_ALPHA] = param;
|
|
break;
|
|
case GL_OPERAND0_RGB:
|
|
case GL_OPERAND1_RGB:
|
|
case GL_OPERAND2_RGB:
|
|
tex_env->texture_combine_rgb_op[pname - GL_OPERAND0_RGB] = param;
|
|
break;
|
|
case GL_OPERAND0_ALPHA:
|
|
case GL_OPERAND1_ALPHA:
|
|
case GL_OPERAND2_ALPHA:
|
|
tex_env->texture_combine_alpha_op[pname - GL_OPERAND0_ALPHA] = param;
|
|
break;
|
|
}
|
|
|
|
w->settings_dirty = TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params)
|
|
{
|
|
if (target == GL_TEXTURE_ENV && pname == GL_TEXTURE_ENV_COLOR)
|
|
{
|
|
CoglGles2WrapperTexEnv *tex_env;
|
|
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
tex_env = w->settings.tex_env + w->active_texture_unit;
|
|
|
|
memcpy (tex_env->texture_combine_constant, params, sizeof (GLfloat) * 4);
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glClientActiveTexture (GLenum texture)
|
|
{
|
|
int texture_unit_index = texture - GL_TEXTURE0;
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
if (texture_unit_index < COGL_GLES2_MAX_TEXTURE_UNITS)
|
|
w->active_client_texture_unit = texture_unit_index;
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glActiveTexture (GLenum texture)
|
|
{
|
|
int texture_unit_index = texture - GL_TEXTURE0;
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
glActiveTexture (texture);
|
|
|
|
if (texture_unit_index < COGL_GLES2_MAX_TEXTURE_UNITS)
|
|
w->active_texture_unit = texture_unit_index;
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glEnable (GLenum cap)
|
|
{
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
switch (cap)
|
|
{
|
|
case GL_TEXTURE_2D:
|
|
if (!COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (w->settings.texture_units,
|
|
w->active_texture_unit))
|
|
{
|
|
COGL_GLES2_TEXTURE_UNIT_SET_ENABLED (w->settings.texture_units,
|
|
w->active_texture_unit,
|
|
TRUE);
|
|
w->settings_dirty = TRUE;
|
|
}
|
|
break;
|
|
|
|
case GL_FOG:
|
|
_COGL_GLES2_CHANGE_SETTING (w, fog_enabled, TRUE);
|
|
break;
|
|
|
|
case GL_ALPHA_TEST:
|
|
_COGL_GLES2_CHANGE_SETTING (w, alpha_test_enabled, TRUE);
|
|
break;
|
|
|
|
default:
|
|
glEnable (cap);
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glDisable (GLenum cap)
|
|
{
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
switch (cap)
|
|
{
|
|
case GL_TEXTURE_2D:
|
|
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (w->settings.texture_units,
|
|
w->active_texture_unit))
|
|
{
|
|
COGL_GLES2_TEXTURE_UNIT_SET_ENABLED (w->settings.texture_units,
|
|
w->active_texture_unit,
|
|
FALSE);
|
|
w->settings_dirty = TRUE;
|
|
}
|
|
break;
|
|
|
|
case GL_FOG:
|
|
_COGL_GLES2_CHANGE_SETTING (w, fog_enabled, FALSE);
|
|
break;
|
|
|
|
case GL_ALPHA_TEST:
|
|
_COGL_GLES2_CHANGE_SETTING (w, alpha_test_enabled, FALSE);
|
|
break;
|
|
|
|
default:
|
|
glDisable (cap);
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glEnableClientState (GLenum array)
|
|
{
|
|
CoglGles2WrapperTextureUnit *texture_unit;
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
switch (array)
|
|
{
|
|
case GL_VERTEX_ARRAY:
|
|
glEnableVertexAttribArray (COGL_GLES2_WRAPPER_VERTEX_ATTRIB);
|
|
break;
|
|
case GL_TEXTURE_COORD_ARRAY:
|
|
/* TODO - review if this should be in w->settings? */
|
|
|
|
texture_unit = w->texture_units + w->active_texture_unit;
|
|
if (texture_unit->texture_coords_enabled != 1)
|
|
{
|
|
texture_unit->texture_coords_enabled = 1;
|
|
w->dirty_vertex_attrib_enables
|
|
|= COGL_GLES2_DIRTY_TEX_COORD_ATTRIB_ENABLES;
|
|
}
|
|
break;
|
|
case GL_COLOR_ARRAY:
|
|
glEnableVertexAttribArray (COGL_GLES2_WRAPPER_COLOR_ATTRIB);
|
|
break;
|
|
case GL_NORMAL_ARRAY:
|
|
glEnableVertexAttribArray (COGL_GLES2_WRAPPER_NORMAL_ATTRIB);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glDisableClientState (GLenum array)
|
|
{
|
|
CoglGles2WrapperTextureUnit *texture_unit;
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
switch (array)
|
|
{
|
|
case GL_VERTEX_ARRAY:
|
|
glDisableVertexAttribArray (COGL_GLES2_WRAPPER_VERTEX_ATTRIB);
|
|
break;
|
|
case GL_TEXTURE_COORD_ARRAY:
|
|
|
|
texture_unit = w->texture_units + w->active_texture_unit;
|
|
/* TODO - review if this should be in w->settings? */
|
|
if (texture_unit->texture_coords_enabled != 0)
|
|
{
|
|
texture_unit->texture_coords_enabled = 0;
|
|
w->dirty_vertex_attrib_enables
|
|
|= COGL_GLES2_DIRTY_TEX_COORD_ATTRIB_ENABLES;
|
|
}
|
|
break;
|
|
case GL_COLOR_ARRAY:
|
|
glDisableVertexAttribArray (COGL_GLES2_WRAPPER_COLOR_ATTRIB);
|
|
break;
|
|
case GL_NORMAL_ARRAY:
|
|
glDisableVertexAttribArray (COGL_GLES2_WRAPPER_NORMAL_ATTRIB);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glAlphaFunc (GLenum func, GLclampf ref)
|
|
{
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
if (ref < 0.0f)
|
|
ref = 0.0f;
|
|
else if (ref > 1.0f)
|
|
ref = 1.0f;
|
|
|
|
_COGL_GLES2_CHANGE_SETTING (w, alpha_test_func, func);
|
|
_COGL_GLES2_CHANGE_UNIFORM (w, ALPHA_TEST_REF, alpha_test_ref, ref);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glColor4f (GLclampf r, GLclampf g, GLclampf b, GLclampf a)
|
|
{
|
|
glVertexAttrib4f (COGL_GLES2_WRAPPER_COLOR_ATTRIB, r, g, b, a);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glColor4ub (GLubyte r, GLubyte g, GLubyte b, GLubyte a)
|
|
{
|
|
glVertexAttrib4f (COGL_GLES2_WRAPPER_COLOR_ATTRIB,
|
|
r/255.0, g/255.0, b/255.0, a/255.0);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glClipPlanef (GLenum plane, GLfloat *equation)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glGetIntegerv (GLenum pname, GLint *params)
|
|
{
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
switch (pname)
|
|
{
|
|
case GL_MAX_CLIP_PLANES:
|
|
*params = 0;
|
|
break;
|
|
|
|
case GL_MATRIX_MODE:
|
|
*params = w->matrix_mode;
|
|
break;
|
|
|
|
case GL_MAX_TEXTURE_UNITS:
|
|
glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, params);
|
|
if (*params > COGL_GLES2_MAX_TEXTURE_UNITS)
|
|
*params = COGL_GLES2_MAX_TEXTURE_UNITS;
|
|
break;
|
|
|
|
default:
|
|
glGetIntegerv (pname, params);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glGetFloatv (GLenum pname, GLfloat *params)
|
|
{
|
|
CoglGles2WrapperTextureUnit *texture_unit;
|
|
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
switch (pname)
|
|
{
|
|
case GL_MODELVIEW_MATRIX:
|
|
memcpy (params, &w->modelview_matrix, sizeof (GLfloat) * 16);
|
|
break;
|
|
|
|
case GL_PROJECTION_MATRIX:
|
|
memcpy (params, &w->projection_matrix, sizeof (GLfloat) * 16);
|
|
break;
|
|
|
|
case GL_TEXTURE_MATRIX:
|
|
texture_unit = w->texture_units + w->active_texture_unit;
|
|
memcpy (params, &texture_unit->texture_matrix, sizeof (GLfloat) * 16);
|
|
break;
|
|
|
|
case GL_VIEWPORT:
|
|
glGetFloatv (GL_VIEWPORT, params);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glFogf (GLenum pname, GLfloat param)
|
|
{
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
switch (pname)
|
|
{
|
|
case GL_FOG_MODE:
|
|
_COGL_GLES2_CHANGE_SETTING (w, fog_mode, param);
|
|
break;
|
|
|
|
case GL_FOG_DENSITY:
|
|
_COGL_GLES2_CHANGE_UNIFORM (w, FOG_DENSITY, fog_density,
|
|
(param));
|
|
break;
|
|
|
|
case GL_FOG_START:
|
|
_COGL_GLES2_CHANGE_UNIFORM (w, FOG_START, fog_start,
|
|
(param));
|
|
break;
|
|
|
|
case GL_FOG_END:
|
|
_COGL_GLES2_CHANGE_UNIFORM (w, FOG_END, fog_end,
|
|
(param));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glFogfv (GLenum pname, const GLfloat *params)
|
|
{
|
|
int i;
|
|
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
|
|
|
|
if (pname == GL_FOG_COLOR)
|
|
{
|
|
for (i = 0; i < 4; i++)
|
|
w->fog_color[i] = (params[i]);
|
|
|
|
w->dirty_uniforms |= COGL_GLES2_DIRTY_FOG_COLOR;
|
|
}
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glTexParameteri (GLenum target, GLenum pname, GLfloat param)
|
|
{
|
|
if (pname != GL_GENERATE_MIPMAP)
|
|
glTexParameteri (target, pname, param);
|
|
}
|
|
|
|
void
|
|
cogl_wrap_glMaterialfv (GLenum face, GLenum pname, const GLfloat *params)
|
|
{
|
|
/* FIXME: the GLES 2 backend doesn't yet support lighting so this
|
|
function can't do anything */
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|