From a86a1df3725522dfd5133181c7063f9712cbefbe Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 22 Nov 2010 18:33:49 +0000 Subject: [PATCH] cogl-pipeline-glsl: Generate the alpha test snippet under GLES2 GLES2 has no glAlphaFunc function so we need to simulate the behaviour in the fragment shader. The alpha test function is simulated with an if-statement and a discard statement. The reference value is stored as a uniform. --- cogl/cogl-pipeline-glsl.c | 144 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 4 deletions(-) diff --git a/cogl/cogl-pipeline-glsl.c b/cogl/cogl-pipeline-glsl.c index 6db16446e..f187dd8ff 100644 --- a/cogl/cogl-pipeline-glsl.c +++ b/cogl/cogl-pipeline-glsl.c @@ -61,6 +61,7 @@ #define glDeleteShader ctx->drv.pf_glDeleteShader #define glGetUniformLocation ctx->drv.pf_glGetUniformLocation #define glUniform1i ctx->drv.pf_glUniform1i +#define glUniform1f ctx->drv.pf_glUniform1f #define glUniform4fv ctx->drv.pf_glUniform4fv #endif /* HAVE_COGL_GLES2 */ @@ -117,6 +118,12 @@ typedef struct _GlslProgramState custom uniforms. This is a massive hack but it can go away once this GLSL backend starts generating its own shaders */ GLuint gles2_program; + + /* Under GLES2 the alpha test is implemented in the shader. We need + a uniform for the reference value */ + gboolean alpha_test_reference_used; + gboolean dirty_alpha_test_reference; + GLint alpha_test_reference_uniform; #endif /* We need to track the last pipeline that the program was used with @@ -416,6 +423,12 @@ _cogl_pipeline_backend_glsl_start (CoglPipeline *pipeline, "main ()\n" "{\n"); +#ifdef HAVE_COGL_GLES2 + priv->glsl_program_state->alpha_test_reference_uniform = -1; + priv->glsl_program_state->alpha_test_reference_used = FALSE; + priv->glsl_program_state->dirty_alpha_test_reference = FALSE; +#endif + for (i = 0; i < n_layers; i++) { priv->glsl_program_state->unit_state[i].sampled = FALSE; @@ -878,6 +891,103 @@ update_constants_cb (CoglPipeline *pipeline, return TRUE; } +/* GLES2 doesn't have alpha testing so we need to implement it in the + shader */ + +#ifdef HAVE_COGL_GLES2 + +static void +add_alpha_test_snippet (CoglPipeline *pipeline, + GlslProgramState *glsl_program_state) +{ + CoglPipelineAlphaFunc alpha_func; + + alpha_func = cogl_pipeline_get_alpha_test_function (pipeline); + + if (alpha_func == COGL_PIPELINE_ALPHA_FUNC_ALWAYS) + /* Do nothing */ + return; + + if (alpha_func == COGL_PIPELINE_ALPHA_FUNC_NEVER) + { + /* Always discard the fragment */ + g_string_append (glsl_program_state->source, + " discard;\n"); + return; + } + + /* For all of the other alpha functions we need a uniform for the + reference */ + + glsl_program_state->alpha_test_reference_used = TRUE; + glsl_program_state->dirty_alpha_test_reference = TRUE; + + g_string_append (glsl_program_state->header, + "uniform float _cogl_alpha_test_ref;\n"); + + g_string_append (glsl_program_state->source, + " if (cogl_color_out.a "); + + switch (alpha_func) + { + case COGL_PIPELINE_ALPHA_FUNC_LESS: + g_string_append (glsl_program_state->source, ">="); + break; + case COGL_PIPELINE_ALPHA_FUNC_EQUAL: + g_string_append (glsl_program_state->source, "!="); + break; + case COGL_PIPELINE_ALPHA_FUNC_LEQUAL: + g_string_append (glsl_program_state->source, ">"); + break; + case COGL_PIPELINE_ALPHA_FUNC_GREATER: + g_string_append (glsl_program_state->source, "<="); + break; + case COGL_PIPELINE_ALPHA_FUNC_NOTEQUAL: + g_string_append (glsl_program_state->source, "=="); + break; + case COGL_PIPELINE_ALPHA_FUNC_GEQUAL: + g_string_append (glsl_program_state->source, "< "); + break; + + case COGL_PIPELINE_ALPHA_FUNC_ALWAYS: + case COGL_PIPELINE_ALPHA_FUNC_NEVER: + g_assert_not_reached (); + break; + } + + g_string_append (glsl_program_state->source, + " _cogl_alpha_test_ref)\n discard;\n"); +} + +static void +update_alpha_test_reference (CoglPipeline *pipeline, + GLuint gl_program, + GlslProgramState *glsl_program_state) +{ + float alpha_reference; + + if (glsl_program_state->dirty_alpha_test_reference) + { + if (glsl_program_state->alpha_test_reference_uniform == -1) + { + GE_RET( glsl_program_state->alpha_test_reference_uniform, + glGetUniformLocation (gl_program, + "_cogl_alpha_test_ref") ); + g_return_if_fail (glsl_program_state-> + alpha_test_reference_uniform != -1); + } + + alpha_reference = cogl_pipeline_get_alpha_test_reference (pipeline); + + GE( glUniform1f (glsl_program_state->alpha_test_reference_uniform, + alpha_reference) ); + + glsl_program_state->dirty_alpha_test_reference = FALSE; + } +} + +#endif /* HAVE_COGL_GLES2 */ + gboolean _cogl_pipeline_backend_glsl_end (CoglPipeline *pipeline, unsigned long pipelines_difference) @@ -934,6 +1044,10 @@ _cogl_pipeline_backend_glsl_end (CoglPipeline *pipeline, 0 /* no application private data */); COGL_COUNTER_INC (_cogl_uprof_context, backend_glsl_compile_counter); +#ifdef HAVE_COGL_GLES2 + add_alpha_test_snippet (pipeline, glsl_program_state); +#endif + g_string_append (glsl_program_state->source, "}\n"); if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_SHOW_SOURCE)) @@ -1016,6 +1130,19 @@ _cogl_pipeline_backend_glsl_end (CoglPipeline *pipeline, update_constants_cb, &state); +#ifdef HAVE_COGL_GLES2 + if (glsl_program_state->alpha_test_reference_used) + { + if (gl_program_changed) + glsl_program_state->alpha_test_reference_uniform = -1; + if (gl_program_changed || + glsl_program_state->last_used_for_pipeline != pipeline) + glsl_program_state->dirty_alpha_test_reference = TRUE; + + update_alpha_test_reference (pipeline, gl_program, glsl_program_state); + } +#endif + if (user_program) _cogl_program_flush_uniforms (user_program, gl_program, @@ -1035,13 +1162,22 @@ _cogl_pipeline_backend_glsl_pre_change_notify (CoglPipeline *pipeline, { static const unsigned long fragment_op_changes = COGL_PIPELINE_STATE_LAYERS | +#ifdef COGL_HAS_GLES2 + COGL_PIPELINE_STATE_ALPHA_FUNC | +#endif COGL_PIPELINE_STATE_USER_SHADER; /* TODO: COGL_PIPELINE_STATE_FOG */ - if (!(change & fragment_op_changes)) - return; - - dirty_glsl_program_state (pipeline); + if ((change & fragment_op_changes)) + dirty_glsl_program_state (pipeline); +#ifdef COGL_HAS_GLES2 + else if ((change & COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE)) + { + GlslProgramState *glsl_program_state = + get_glsl_program_state (pipeline); + glsl_program_state->dirty_alpha_test_reference = TRUE; + } +#endif } /* NB: layers are considered immutable once they have any dependants