mesa: glsl: fix a number of glUniform issues
authorBrian Paul <brian.paul@tungstengraphics.com>
Wed, 6 Aug 2008 22:26:47 +0000 (16:26 -0600)
committerBrian Paul <brian.paul@tungstengraphics.com>
Wed, 6 Aug 2008 22:26:47 +0000 (16:26 -0600)
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

index 3a85f476740d8e1897c8b3cee2210fab03338966..c2c58fffde3527f859f4452812099e1b6640c61e 100644 (file)
@@ -752,6 +752,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,
@@ -778,6 +808,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().
  */
@@ -1147,12 +1201,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");
 
@@ -1168,7 +1252,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;
 }
 
 
@@ -1341,23 +1472,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;
 
@@ -1369,7 +1510,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 */
@@ -1388,18 +1529,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];
@@ -1411,6 +1553,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;
+            }
+         }
       }
    }
 }
@@ -1424,7 +1573,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)");
@@ -1434,6 +1583,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;
@@ -1472,38 +1623,48 @@ _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
 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;
    }
 
@@ -1544,6 +1705,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) {
@@ -1555,6 +1717,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;
@@ -1567,18 +1731,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);
       }
    }
 }