diff options
| author | Brian Paul <brian.paul@tungstengraphics.com> | 2008-08-06 16:26:47 -0600 | 
|---|---|---|
| committer | Keith Whitwell <keith@tungstengraphics.com> | 2008-09-23 17:33:49 -0700 | 
| commit | eda291e316601d2ebf9834c290686854ea857aec (patch) | |
| tree | 54b703904e1f5dc35aebc4b91944abf6dd21696c | |
| parent | bda6ad273d17efbe69a2a0599595cd4d50617cc3 (diff) | |
mesa: glsl: fix a number of glUniform issues
Additional error checking.
Allow setting elements of uniform arrays.  This involves encoding both
a uniform location and a parameter offset in the value returned by
glGetUniformLocation().
Limit glUniform[if]v()'s count to the size of the uniform array.
When setting bool-valued uniforms, convert all float/int values to 0 or 1.
| -rw-r--r-- | src/mesa/shader/shader_api.c | 230 | 
1 files changed, 199 insertions, 31 deletions
| diff --git a/src/mesa/shader/shader_api.c b/src/mesa/shader/shader_api.c index 6e13c72f7c..c05d052f43 100644 --- a/src/mesa/shader/shader_api.c +++ b/src/mesa/shader/shader_api.c @@ -765,6 +765,36 @@ sizeof_glsl_type(GLenum type)  } +static GLboolean +is_boolean_type(GLenum type) +{ +   switch (type) { +   case GL_BOOL: +   case GL_BOOL_VEC2: +   case GL_BOOL_VEC3: +   case GL_BOOL_VEC4: +      return GL_TRUE; +   default: +      return GL_FALSE; +   } +} + + +static GLboolean +is_integer_type(GLenum type) +{ +   switch (type) { +   case GL_INT: +   case GL_INT_VEC2: +   case GL_INT_VEC3: +   case GL_INT_VEC4: +      return GL_TRUE; +   default: +      return GL_FALSE; +   } +} + +  static void  _mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index,                          GLsizei maxLength, GLsizei *length, GLint *size, @@ -796,6 +826,30 @@ _mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index,  } +static struct gl_program_parameter * +get_uniform_parameter(const struct gl_shader_program *shProg, GLuint index) +{ +   const struct gl_program *prog; +   GLint progPos; + +   progPos = shProg->Uniforms->Uniforms[index].VertPos; +   if (progPos >= 0) { +      prog = &shProg->VertexProgram->Base; +   } +   else { +      progPos = shProg->Uniforms->Uniforms[index].FragPos; +      if (progPos >= 0) { +         prog = &shProg->FragmentProgram->Base; +      } +   } + +   if (!prog || progPos < 0) +      return NULL; /* should never happen */ + +   return &prog->Parameters->Parameters[progPos]; +} + +  /**   * Called via ctx->Driver.GetActiveUniform().   */ @@ -1097,11 +1151,41 @@ _mesa_get_uniformiv(GLcontext *ctx, GLuint program, GLint location,  /** + * The value returned by GetUniformLocation actually encodes two things: + * 1. the index into the prog->Uniforms[] array for the uniform + * 2. an offset in the prog->ParameterValues[] array for specifying array + *    elements or structure fields. + * This function merges those two values. + */ +static void +merge_location_offset(GLint *location, GLint offset) +{ +   *location = *location | (offset << 16); +} + + +/** + * Seperate the uniform location and parameter offset.  See above. + */ +static void +split_location_offset(GLint *location, GLint *offset) +{ +   *offset = (*location >> 16); +   *location = *location & 0xffff; +} + + +/**   * Called via ctx->Driver.GetUniformLocation(). + * + * The return value will encode two values, the uniform location and an + * offset (used for arrays, structs).   */  static GLint  _mesa_get_uniform_location(GLcontext *ctx, GLuint program, const GLchar *name)  { +   GLint offset = 0, location = -1; +     struct gl_shader_program *shProg =        _mesa_lookup_shader_program_err(ctx, program, "glGetUniformLocation"); @@ -1117,7 +1201,54 @@ _mesa_get_uniform_location(GLcontext *ctx, GLuint program, const GLchar *name)      * actually used.      */ -   return _mesa_lookup_uniform(shProg->Uniforms, name); +   /* XXX we need to be able to parse uniform names for structs and arrays +    * such as: +    *   mymatrix[1] +    *   mystruct.field1 +    */ + +   { +      /* handle 1-dimension arrays here... */ +      char *c = strchr(name, '['); +      if (c) { +         /* truncate name at [ */ +         const GLint len = c - name; +         GLchar *newName = _mesa_malloc(len + 1); +         if (!newName) +            return -1; /* out of mem */ +         _mesa_memcpy(newName, name, len); +         newName[len] = 0; + +         location = _mesa_lookup_uniform(shProg->Uniforms, newName); +         if (location >= 0) { +            const GLint element = _mesa_atoi(c + 1); +            if (element > 0) { +               /* get type of the uniform array element */ +               struct gl_program_parameter *p; +               p = get_uniform_parameter(shProg, location); +               if (p) { +                  GLint rows, cols; +                  get_matrix_dims(p->DataType, &rows, &cols); +                  if (rows < 1) +                     rows = 1; +                  offset = element * rows; +               } +            } +         } + +         _mesa_free(newName); +      } +   } + +   if (location < 0) { +      location = _mesa_lookup_uniform(shProg->Uniforms, name); +   } + +   if (location >= 0) { +      merge_location_offset(&location, offset); +   } + +   return location;  } @@ -1290,23 +1421,33 @@ compatible_types(GLenum userType, GLenum targetType)  /**   * Set the value of a program's uniform variable.   * \param program  the program whose uniform to update - * \param location  the location/index of the uniform + * \param index  the index of the program parameter for the uniform + * \param offset  additional parameter slot offset (for arrays)   * \param type  the datatype of the uniform   * \param count  the number of uniforms to set   * \param elems  number of elements per uniform   * \param values  the new values   */  static void -set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location, -                    GLenum type, GLsizei count, GLint elems, const void *values) +set_program_uniform(GLcontext *ctx, struct gl_program *program, +                    GLint index, GLint offset, +                    GLenum type, GLsizei count, GLint elems, +                    const void *values)  { +   assert(offset >= 0); +     if (!compatible_types(type, -                         program->Parameters->Parameters[location].DataType)) { +                         program->Parameters->Parameters[index].DataType)) {        _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)");        return;     } -   if (program->Parameters->Parameters[location].Type == PROGRAM_SAMPLER) { +   if (index + offset > program->Parameters->Size) { +      /* out of bounds! */ +      return; +   } + +   if (program->Parameters->Parameters[index].Type == PROGRAM_SAMPLER) {        /* This controls which texture unit which is used by a sampler */        GLuint texUnit, sampler; @@ -1318,7 +1459,7 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location,           return;        } -      sampler = (GLuint) program->Parameters->ParameterValues[location][0]; +      sampler = (GLuint) program->Parameters->ParameterValues[index][0];        texUnit = ((GLuint *) values)[0];        /* check that the sampler (tex unit index) is legal */ @@ -1337,18 +1478,19 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location,     else {        /* ordinary uniform variable */        GLsizei k, i; +      GLint slots = (program->Parameters->Parameters[index].Size + 3) / 4; -      if (count * elems > (GLint) program->Parameters->Parameters[location].Size) { +      if (count * elems > (GLint) program->Parameters->Parameters[index].Size) {           _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(count too large)");           return;        } +      if (count > slots) +         count = slots; +        for (k = 0; k < count; k++) { -         GLfloat *uniformVal = program->Parameters->ParameterValues[location + k]; -         if (type == GL_INT || -             type == GL_INT_VEC2 || -             type == GL_INT_VEC3 || -             type == GL_INT_VEC4) { +         GLfloat *uniformVal = program->Parameters->ParameterValues[index + offset + k]; +         if (is_integer_type(type)) {              const GLint *iValues = ((const GLint *) values) + k * elems;              for (i = 0; i < elems; i++) {                 uniformVal[i] = (GLfloat) iValues[i]; @@ -1360,6 +1502,13 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location,                 uniformVal[i] = fValues[i];              }           } + +         /* if the uniform is bool-valued, convert to 1.0 or 0.0 */ +         if (is_boolean_type(program->Parameters->Parameters[index].DataType)) { +            for (i = 0; i < elems; i++) { +               uniformVal[i] = uniformVal[i] ? 1.0 : 0.0; +            } +         }        }     }  } @@ -1373,7 +1522,7 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count,                const GLvoid *values, GLenum type)  {     struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; -   GLint elems; +   GLint elems, offset;     if (!shProg || !shProg->LinkStatus) {        _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(program not linked)"); @@ -1383,6 +1532,8 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count,     if (location == -1)        return;   /* The standard specifies this as a no-op */ +   split_location_offset(&location, &offset); +     if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) {        _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(location)");        return; @@ -1421,23 +1572,28 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count,      * shader.  We may need to update one or both shader's uniform here:      */     if (shProg->VertexProgram) { -      GLint loc = shProg->Uniforms->Uniforms[location].VertPos; -      if (loc >= 0) { +      /* convert uniform location to program parameter index */ +      GLint index = shProg->Uniforms->Uniforms[location].VertPos; +      if (index >= 0) {           set_program_uniform(ctx, &shProg->VertexProgram->Base, -                             loc, type, count, elems, values); +                             index, offset, type, count, elems, values);        }     }     if (shProg->FragmentProgram) { -      GLint loc = shProg->Uniforms->Uniforms[location].FragPos; -      if (loc >= 0) { +      /* convert uniform location to program parameter index */ +      GLint index = shProg->Uniforms->Uniforms[location].FragPos; +      if (index >= 0) {           set_program_uniform(ctx, &shProg->FragmentProgram->Base, -                             loc, type, count, elems, values); +                             index, offset, type, count, elems, values);        }     }  } +/** + * Set a matrix-valued program parameter. + */  static void  get_matrix_dims(GLenum type, GLint *rows, GLint *cols)  { @@ -1485,19 +1641,24 @@ get_matrix_dims(GLenum type, GLint *rows, GLint *cols)  static void  set_program_uniform_matrix(GLcontext *ctx, struct gl_program *program, -                           GLuint location, GLuint count, -                           GLuint rows, GLuint cols, +                           GLuint index, GLuint offset, +                           GLuint count, GLuint rows, GLuint cols,                             GLboolean transpose, const GLfloat *values)  {     GLuint mat, row, col; -   GLuint dst = location, src = 0; +   GLuint dst = index + offset, src = 0;     GLint nr, nc;     /* check that the number of rows, columns is correct */ -   get_matrix_dims(program->Parameters->Parameters[location].DataType, &nr, &nc); +   get_matrix_dims(program->Parameters->Parameters[index].DataType, &nr, &nc);     if (rows != nr || cols != nc) {        _mesa_error(ctx, GL_INVALID_OPERATION, -                  "glUniformMatrix(matrix size mismatch"); +                  "glUniformMatrix(matrix size mismatch)"); +      return; +   } + +   if (index + offset > program->Parameters->Size) { +      /* out of bounds! */        return;     } @@ -1538,6 +1699,7 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows,                       GLenum matrixType, GLint location, GLsizei count,                       GLboolean transpose, const GLfloat *values)  { +   GLint offset;     struct gl_shader_program *shProg = ctx->Shader.CurrentProgram;     if (!shProg || !shProg->LinkStatus) { @@ -1549,6 +1711,8 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows,     if (location == -1)        return;   /* The standard specifies this as a no-op */ +   split_location_offset(&location, &offset); +     if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) {        _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(location)");        return; @@ -1561,18 +1725,22 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows,     FLUSH_VERTICES(ctx, _NEW_PROGRAM);     if (shProg->VertexProgram) { -      GLint loc = shProg->Uniforms->Uniforms[location].VertPos; -      if (loc >= 0) { +      /* convert uniform location to program parameter index */ +      GLint index = shProg->Uniforms->Uniforms[location].VertPos; +      if (index >= 0) {           set_program_uniform_matrix(ctx, &shProg->VertexProgram->Base, -                                    loc, count, rows, cols, transpose, values); +                                    index, offset, +                                    count, rows, cols, transpose, values);        }     }     if (shProg->FragmentProgram) { -      GLint loc = shProg->Uniforms->Uniforms[location].FragPos; -      if (loc >= 0) { +      /* convert uniform location to program parameter index */ +      GLint index = shProg->Uniforms->Uniforms[location].FragPos; +      if (index >= 0) {           set_program_uniform_matrix(ctx, &shProg->FragmentProgram->Base, -                                    loc, count, rows, cols, transpose, values); +                                    index, offset, +                                    count, rows, cols, transpose, values);        }     }  } | 
