cogl-gles2-wrapper: Add support for the layer combine operations

The texture layer combine functions are now hard coded to GL_COMBINE
instead of GL_MODULATE. The combine function can be customized with
all the parameters of GL_COMBINE. A shader is generated to implement
the given parameters.

Currently it will try to generate code for the constant color but it
will use a uniform which does not exist.
This commit is contained in:
Neil Roberts 2010-02-24 16:50:32 +00:00
parent 02b952394a
commit eba07020c5
3 changed files with 371 additions and 36 deletions

View File

@ -172,15 +172,6 @@ validate_tex_combine_statements (CoglBlendStringStatement *statements,
for (i = 0; i < n_statements; i++) for (i = 0; i < n_statements; i++)
{ {
#ifdef HAVE_COGL_GLES2
if (statements[i].function->type != COGL_BLEND_STRING_FUNCTION_MODULATE)
{
error_string = "Using anything but MODULATE() for texture combining"
" under GLES 2 is currently unsupported";
detail = COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR;
goto error;
}
#endif
for (j = 0; j < statements[i].function->argc; j++) for (j = 0; j < statements[i].function->argc; j++)
{ {
CoglBlendStringArgument *arg = &statements[i].args[j]; CoglBlendStringArgument *arg = &statements[i].args[j];

View File

@ -130,6 +130,31 @@ initialize_texture_units (CoglGles2Wrapper *w)
w->active_texture_unit = i; w->active_texture_unit = i;
GE( cogl_wrap_glMatrixMode (GL_TEXTURE)); GE( cogl_wrap_glMatrixMode (GL_TEXTURE));
GE( cogl_wrap_glLoadIdentity ()); 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)); GE( cogl_wrap_glMatrixMode ((GLenum) prev_mode));
@ -172,12 +197,35 @@ cogl_gles2_wrapper_init (CoglGles2Wrapper *wrapper)
initialize_texture_units (wrapper); 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 static gboolean
cogl_gles2_settings_equal (const CoglGles2WrapperSettings *a, cogl_gles2_settings_equal (const CoglGles2WrapperSettings *a,
const CoglGles2WrapperSettings *b, const CoglGles2WrapperSettings *b,
gboolean vertex_tests, gboolean vertex_tests,
gboolean fragment_tests) gboolean fragment_tests)
{ {
int i;
if (a->texture_units != b->texture_units) if (a->texture_units != b->texture_units)
return FALSE; return FALSE;
@ -195,6 +243,34 @@ cogl_gles2_settings_equal (const CoglGles2WrapperSettings *a,
if (vertex_tests && a->fog_enabled && a->fog_mode != b->fog_mode) if (vertex_tests && a->fog_enabled && a->fog_mode != b->fog_mode)
return FALSE; 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; return TRUE;
} }
@ -307,6 +383,187 @@ cogl_gles2_get_vertex_shader (const CoglGles2WrapperSettings *settings)
return 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 * static CoglGles2WrapperShader *
cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings) cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings)
{ {
@ -360,27 +617,53 @@ cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings)
blending seems to stop working */ blending seems to stop working */
g_string_append (shader_source, g_string_append (shader_source,
"vec4 frag_color_copy = frag_color;\n"); "vec4 frag_color_copy = frag_color;\n");
g_string_append (shader_source, "gl_FragColor = 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++) for (i = 0; i < n_texture_units; i++)
if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i)) if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i))
{ {
if (COGL_GLES2_TEXTURE_UNIT_IS_ALPHA_ONLY (settings->texture_units, i)) const CoglGles2WrapperTexEnv *tex_env = settings->tex_env + i;
/* If the texture only has an alpha channel (eg, with the textures
from the pango renderer) then the RGB components will be /* If the rgb and alpha combine functions are the same then
black. We want to use the RGB from the current color in that we can do both with a single statement, otherwise we need
case */ to do them separately */
g_string_append_printf (shader_source, if (cogl_gles2_rgb_and_alpha_equal (tex_env))
"gl_FragColor.a *= " {
"texture2D (texture_unit[%d], " g_string_append (shader_source, "gl_FragColor.rgba = ");
"tex_coord[%d]).a;\n", cogl_gles2_add_operation (i,
i, 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 else
g_string_append_printf (shader_source, {
"gl_FragColor *= " g_string_append (shader_source, "gl_FragColor.rgb = ");
"texture2D (texture_unit[%d], " cogl_gles2_add_operation (i,
"tex_coord[%d]);\n", tex_env->texture_combine_rgb_func,
i, i); 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) if (settings->fog_enabled)
@ -1079,16 +1362,61 @@ cogl_gles2_wrapper_bind_texture (GLenum target, GLuint texture,
void void
cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLint param) cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLint param)
{ {
/* This function is only used to set the texture mode once to if (target == GL_TEXTURE_ENV)
GL_MODULATE. The shader is hard-coded to modulate the texture so {
nothing needs to be done here. */ 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 void
cogl_wrap_glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params) cogl_wrap_glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params)
{ {
/* FIXME: Currently needed to support texture combining using if (target == GL_TEXTURE_ENV && pname == GL_TEXTURE_ENV_COLOR)
* COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT */ {
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 void

View File

@ -37,6 +37,7 @@ typedef struct _CoglGles2WrapperTextureUnit
typedef struct _CoglGles2WrapperAttributes CoglGles2WrapperAttributes; typedef struct _CoglGles2WrapperAttributes CoglGles2WrapperAttributes;
typedef struct _CoglGles2WrapperUniforms CoglGles2WrapperUniforms; typedef struct _CoglGles2WrapperUniforms CoglGles2WrapperUniforms;
typedef struct _CoglGles2WrapperTexEnv CoglGles2WrapperTexEnv;
typedef struct _CoglGles2WrapperTextureUnitSettings typedef struct _CoglGles2WrapperTextureUnitSettings
CoglGles2WrapperTextureUnitSettings; CoglGles2WrapperTextureUnitSettings;
typedef struct _CoglGles2WrapperSettings CoglGles2WrapperSettings; typedef struct _CoglGles2WrapperSettings CoglGles2WrapperSettings;
@ -115,6 +116,19 @@ struct _CoglGles2WrapperUniforms
GLint texture_unit_uniform; GLint texture_unit_uniform;
}; };
struct _CoglGles2WrapperTexEnv
{
GLenum texture_combine_rgb_func;
GLenum texture_combine_rgb_src[3];
GLenum texture_combine_rgb_op[3];
GLenum texture_combine_alpha_func;
GLenum texture_combine_alpha_src[3];
GLenum texture_combine_alpha_op[3];
GLfloat texture_combine_constant[4];
};
/* NB: We get a copy of this for each fragment/vertex /* NB: We get a copy of this for each fragment/vertex
* program varient we generate so we try to keep it * program varient we generate so we try to keep it
* fairly lean */ * fairly lean */
@ -130,6 +144,8 @@ struct _CoglGles2WrapperSettings
unsigned int alpha_test_enabled:1; unsigned int alpha_test_enabled:1;
unsigned int fog_enabled:1; unsigned int fog_enabled:1;
CoglGles2WrapperTexEnv tex_env[COGL_GLES2_MAX_TEXTURE_UNITS];
}; };
struct _CoglGles2WrapperTextureUnit struct _CoglGles2WrapperTextureUnit