From eda291e316601d2ebf9834c290686854ea857aec Mon Sep 17 00:00:00 2001 From: Brian Paul Date: Wed, 6 Aug 2008 16:26:47 -0600 Subject: [PATCH] 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. --- src/mesa/shader/shader_api.c | 230 ++++++++++++++++++++++++++++++----- 1 file changed, 199 insertions(+), 31 deletions(-) diff --git a/src/mesa/shader/shader_api.c b/src/mesa/shader/shader_api.c index 6e13c72f7ca..c05d052f43e 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(). */ @@ -1096,12 +1150,42 @@ _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); } } } -- 2.30.2