Use the same number for n_tex_coord_attribs in all linked shaders

On GLES2, we need to specify an array size for the texture coord
varying array. Previously this size would be decided in one of the
following ways:

- For generated vertex shaders it is always the number of layers in
  the pipeline.

- For generated fragment shaders it is the highest sampled texture
  unit in the pipeline or the number of attributes supplied by the
  primitive, whichever is higher.

- For user shaders it is usually the number of attributes supplied by
  the primitive. However, if the application tries to compile the
  shader and query the result before using it, it will always be at
  least 4.

These shaders can quite easily end up with different values for the
declaration which makes it fail to link. This patch changes it so that
all of the shaders are generated with the maximum of the number of
texture attributes supplied by the primitive and the number of layers
in the pipeline. If this value changes then the shaders are
regenerated, including user shaders. That way all of the shaders will
always have the same value.

https://bugzilla.gnome.org/show_bug.cgi?id=662184

Reviewed-by: Robert Bragg <robert@linux.intel.com>
This commit is contained in:
Neil Roberts 2011-10-19 12:47:22 +01:00
parent 884647071c
commit 00f0a66ec9
7 changed files with 64 additions and 50 deletions

View File

@ -103,6 +103,11 @@ typedef struct
program changes then we may need to redecide whether to generate
a shader at all */
unsigned int user_program_age;
/* The number of tex coord attributes that the shader was generated
for. If this changes on GLES2 then we need to regenerate the
shader */
int n_tex_coord_attribs;
} CoglPipelineShaderState;
static CoglUserDataKey shader_state_key;
@ -268,9 +273,14 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
{
/* If we already have a valid GLSL shader then we don't need to
generate a new one. However if there's a user program and it
has changed since the last link then we do need a new shader */
if (user_program == NULL ||
shader_state->user_program_age == user_program->age)
has changed since the last link then we do need a new
shader. If the number of tex coord attribs changes on GLES2
then we need to regenerate the shader with a different boiler
plate */
if ((user_program == NULL ||
shader_state->user_program_age == user_program->age)
&& (ctx->driver != COGL_DRIVER_GLES2 ||
shader_state->n_tex_coord_attribs == n_tex_coord_attribs))
return TRUE;
/* We need to recreate the shader so destroy the existing one */
@ -285,6 +295,8 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
if (user_program)
shader_state->user_program_age = user_program->age;
shader_state->n_tex_coord_attribs = n_tex_coord_attribs;
/* If the user program contains a fragment shader then we don't need
to generate one */
if (user_program &&
@ -873,8 +885,6 @@ _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline,
GLint lengths[2];
GLint compile_status;
GLuint shader;
int n_tex_coord_attribs = 0;
int i, n_layers;
COGL_STATIC_COUNTER (fragend_glsl_compile_counter,
"glsl fragment compile counter",
@ -921,15 +931,9 @@ _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline,
lengths[1] = shader_state->source->len;
source_strings[1] = shader_state->source->str;
/* Find the highest texture unit that is sampled to pass as the
number of texture coordinate attributes */
n_layers = cogl_pipeline_get_n_layers (pipeline);
for (i = 0; i < n_layers; i++)
if (shader_state->unit_state[i].sampled)
n_tex_coord_attribs = i + 1;
_cogl_shader_set_source_with_boilerplate (shader, GL_FRAGMENT_SHADER,
n_tex_coord_attribs,
shader_state
->n_tex_coord_attribs,
2, /* count */
source_strings, lengths);

View File

@ -1200,6 +1200,16 @@ _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline,
else
layer_differences = NULL;
/* Make sure we generate the texture coordinate array to be at least
the number of layers. This is important because the vertend will
try to pass along the corresponding varying for each layer
regardless of whether the fragment shader is actually using
it. Also it is possible that the application is assuming that if
the attribute isn't passed then it will default to 0,0. This is
what test-cogl-primitive does */
if (n_layers > n_tex_coord_attribs)
n_tex_coord_attribs = n_layers;
/* First flush everything that's the same regardless of which
* pipeline backend is being used...
*
@ -1300,7 +1310,8 @@ _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline,
* scratch buffers here... */
if (G_UNLIKELY (!vertend->start (pipeline,
n_layers,
pipelines_difference)))
pipelines_difference,
n_tex_coord_attribs)))
continue;
state.vertend = vertend;

View File

@ -542,7 +542,8 @@ typedef struct _CoglPipelineVertend
{
gboolean (*start) (CoglPipeline *pipeline,
int n_layers,
unsigned long pipelines_difference);
unsigned long pipelines_difference,
int n_tex_coord_attribs);
gboolean (*add_layer) (CoglPipeline *pipeline,
CoglPipelineLayer *layer,
unsigned long layers_difference);

View File

@ -106,7 +106,7 @@ typedef struct
* array of texture coordinate varyings, but to know how to emit the
* declaration we need to know how many texture coordinate
* attributes are in use. The boilerplate also needs to be changed
* if this increases. */
* if this changes. */
int n_tex_coord_attribs;
#ifdef HAVE_COGL_GLES2
@ -620,11 +620,12 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
* need to relink
*
* Also if the number of texture coordinate attributes in use has
* increased, then delete the program so we can prepend a new
* changed, then delete the program so we can prepend a new
* _cogl_tex_coord[] varying array declaration. */
if (program_state->program && user_program &&
(user_program->age != program_state->user_program_age ||
n_tex_coord_attribs > program_state->n_tex_coord_attribs))
if ((program_state->program && user_program &&
user_program->age != program_state->user_program_age) ||
(ctx->driver == COGL_DRIVER_GLES2 &&
n_tex_coord_attribs != program_state->n_tex_coord_attribs))
{
GE( ctx, glDeleteProgram (program_state->program) );
program_state->program = 0;
@ -640,23 +641,6 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
/* Attach all of the shader from the user program */
if (user_program)
{
if (program_state->n_tex_coord_attribs > n_tex_coord_attribs)
n_tex_coord_attribs = program_state->n_tex_coord_attribs;
#ifdef HAVE_COGL_GLES2
/* Find the largest count of texture coordinate attributes
* associated with each of the shaders so we can ensure a consistent
* _cogl_tex_coord[] array declaration across all of the shaders.*/
if (ctx->driver == COGL_DRIVER_GLES2 &&
user_program)
for (l = user_program->attached_shaders; l; l = l->next)
{
CoglShader *shader = l->data;
n_tex_coord_attribs = MAX (shader->n_tex_coord_attribs,
n_tex_coord_attribs);
}
#endif
for (l = user_program->attached_shaders; l; l = l->next)
{
CoglShader *shader = l->data;

View File

@ -46,7 +46,8 @@ const CoglPipelineVertend _cogl_pipeline_fixed_vertend;
static gboolean
_cogl_pipeline_vertend_fixed_start (CoglPipeline *pipeline,
int n_layers,
unsigned long pipelines_difference)
unsigned long pipelines_difference,
int n_tex_coord_attribs)
{
CoglProgram *user_program;

View File

@ -56,6 +56,11 @@ typedef struct
program changes then we may need to redecide whether to generate
a shader at all */
unsigned int user_program_age;
/* The number of tex coord attributes that the shader was generated
for. If this changes on GLES2 then we need to regenerate the
shader */
int n_tex_coord_attribs;
} CoglPipelineShaderState;
static CoglUserDataKey shader_state_key;
@ -126,7 +131,8 @@ _cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline)
static gboolean
_cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
int n_layers,
unsigned long pipelines_difference)
unsigned long pipelines_difference,
int n_tex_coord_attribs)
{
CoglPipelineShaderState *shader_state;
CoglPipeline *template_pipeline = NULL;
@ -203,9 +209,14 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
{
/* If we already have a valid GLSL shader then we don't need to
generate a new one. However if there's a user program and it
has changed since the last link then we do need a new shader */
if (user_program == NULL ||
shader_state->user_program_age == user_program->age)
has changed since the last link then we do need a new
shader. If the number of tex coord attribs changes on GLES2
then we need to regenerate the shader with a different boiler
plate */
if ((user_program == NULL ||
shader_state->user_program_age == user_program->age)
&& (ctx->driver != COGL_DRIVER_GLES2 ||
shader_state->n_tex_coord_attribs == n_tex_coord_attribs))
return TRUE;
/* We need to recreate the shader so destroy the existing one */
@ -220,6 +231,8 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
if (user_program)
shader_state->user_program_age = user_program->age;
shader_state->n_tex_coord_attribs = n_tex_coord_attribs;
/* If the user program contains a vertex shader then we don't need
to generate one */
if (user_program &&
@ -336,7 +349,6 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline,
GLint lengths[2];
GLint compile_status;
GLuint shader;
int n_layers;
COGL_STATIC_COUNTER (vertend_glsl_compile_counter,
"glsl vertex compile counter",
@ -359,10 +371,9 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline,
lengths[1] = shader_state->source->len;
source_strings[1] = shader_state->source->str;
n_layers = cogl_pipeline_get_n_layers (pipeline);
_cogl_shader_set_source_with_boilerplate (shader, GL_VERTEX_SHADER,
n_layers,
shader_state
->n_tex_coord_attribs,
2, /* count */
source_strings, lengths);

View File

@ -342,7 +342,7 @@ _cogl_shader_compile_real (CoglHandle handle,
#ifdef HAVE_COGL_GLES2
&&
(ctx->driver != COGL_DRIVER_GLES2 ||
shader->n_tex_coord_attribs >= n_tex_coord_attribs)
shader->n_tex_coord_attribs == n_tex_coord_attribs)
#endif
)
return;
@ -424,9 +424,11 @@ cogl_shader_get_info_log (CoglHandle handle)
* Here we force an early compile if the user is interested in
* log information to increase the chance that the log will be
* useful! We have to guess the number of texture coordinate
* attributes that may be used (normally less than 4) since that
* affects the boilerplate.
*/
* attributes that may be used since that affects the
* boilerplate. We use four so that the shader will still
* compile if the user is using more than one
* layer. Unfortunately this is likely to end up causing it to
* be compiled again when we know the actual number of layers */
if (!shader->gl_handle)
_cogl_shader_compile_real (handle, 4);