mesa: Add custom get function for SCISSOR_TEST to _mesa_IsEnabledi
[mesa.git] / src / mesa / main / uniform_query.cpp
index 496f60416bfd654cbeea6819a9acc148142d6ec0..82d7628e8d8aa89abd3524b593a8ffc8a337ddb4 100644 (file)
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
  */
+
 #include <stdlib.h>
+
 #include "main/core.h"
 #include "main/context.h"
 #include "ir.h"
 #include "program/hash_table.h"
 #include "../glsl/program.h"
 #include "../glsl/ir_uniform.h"
-
-extern "C" {
+#include "../glsl/glsl_parser_extras.h"
 #include "main/shaderapi.h"
 #include "main/shaderobj.h"
 #include "uniforms.h"
-}
+
 
 extern "C" void GLAPIENTRY
-_mesa_GetActiveUniformARB(GLhandleARB program, GLuint index,
+_mesa_GetActiveUniform(GLhandleARB program, GLuint index,
                           GLsizei maxLength, GLsizei *length, GLint *size,
                           GLenum *type, GLcharARB *nameOut)
 {
@@ -57,7 +59,7 @@ _mesa_GetActiveUniformARB(GLhandleARB program, GLuint index,
    const struct gl_uniform_storage *const uni = &shProg->UniformStorage[index];
 
    if (nameOut) {
-      _mesa_copy_string(nameOut, maxLength, length, uni->name);
+      _mesa_get_uniform_name(uni, maxLength, length, nameOut);
    }
 
    if (size) {
@@ -72,265 +74,101 @@ _mesa_GetActiveUniformARB(GLhandleARB program, GLuint index,
    }
 }
 
-static GLenum
-base_uniform_type(GLenum type)
+extern "C" void GLAPIENTRY
+_mesa_GetActiveUniformsiv(GLuint program,
+                         GLsizei uniformCount,
+                         const GLuint *uniformIndices,
+                         GLenum pname,
+                         GLint *params)
 {
-   switch (type) {
-   case GL_BOOL:
-   case GL_BOOL_VEC2:
-   case GL_BOOL_VEC3:
-   case GL_BOOL_VEC4:
-      return GL_BOOL;
-   case GL_FLOAT:
-   case GL_FLOAT_VEC2:
-   case GL_FLOAT_VEC3:
-   case GL_FLOAT_VEC4:
-   case GL_FLOAT_MAT2:
-   case GL_FLOAT_MAT2x3:
-   case GL_FLOAT_MAT2x4:
-   case GL_FLOAT_MAT3x2:
-   case GL_FLOAT_MAT3:
-   case GL_FLOAT_MAT3x4:
-   case GL_FLOAT_MAT4x2:
-   case GL_FLOAT_MAT4x3:
-   case GL_FLOAT_MAT4:
-      return GL_FLOAT;
-   case GL_UNSIGNED_INT:
-   case GL_UNSIGNED_INT_VEC2:
-   case GL_UNSIGNED_INT_VEC3:
-   case GL_UNSIGNED_INT_VEC4:
-      return GL_UNSIGNED_INT;
-   case GL_INT:
-   case GL_INT_VEC2:
-   case GL_INT_VEC3:
-   case GL_INT_VEC4:
-      return GL_INT;
-   default:
-      _mesa_problem(NULL, "Invalid type in base_uniform_type()");
-      return GL_FLOAT;
-   }
-}
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_shader_program *shProg;
+   GLsizei i;
 
-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;
-   }
-}
+   shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform");
+   if (!shProg)
+      return;
 
-static GLboolean
-is_sampler_type(GLenum type)
-{
-   switch (type) {
-   case GL_SAMPLER_1D:
-   case GL_INT_SAMPLER_1D:
-   case GL_UNSIGNED_INT_SAMPLER_1D:
-   case GL_SAMPLER_2D:
-   case GL_INT_SAMPLER_2D:
-   case GL_UNSIGNED_INT_SAMPLER_2D:
-   case GL_SAMPLER_3D:
-   case GL_INT_SAMPLER_3D:
-   case GL_UNSIGNED_INT_SAMPLER_3D:
-   case GL_SAMPLER_CUBE:
-   case GL_INT_SAMPLER_CUBE:
-   case GL_UNSIGNED_INT_SAMPLER_CUBE:
-   case GL_SAMPLER_1D_SHADOW:
-   case GL_SAMPLER_2D_SHADOW:
-   case GL_SAMPLER_CUBE_SHADOW:
-   case GL_SAMPLER_2D_RECT_ARB:
-   case GL_INT_SAMPLER_2D_RECT:
-   case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
-   case GL_SAMPLER_2D_RECT_SHADOW_ARB:
-   case GL_SAMPLER_1D_ARRAY_EXT:
-   case GL_INT_SAMPLER_1D_ARRAY:
-   case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
-   case GL_SAMPLER_2D_ARRAY_EXT:
-   case GL_INT_SAMPLER_2D_ARRAY:
-   case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
-   case GL_SAMPLER_1D_ARRAY_SHADOW_EXT:
-   case GL_SAMPLER_2D_ARRAY_SHADOW_EXT:
-   case GL_SAMPLER_CUBE_MAP_ARRAY:
-   case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
-   case GL_SAMPLER_BUFFER:
-   case GL_INT_SAMPLER_BUFFER:
-   case GL_UNSIGNED_INT_SAMPLER_BUFFER:
-   case GL_SAMPLER_2D_MULTISAMPLE:
-   case GL_INT_SAMPLER_2D_MULTISAMPLE:
-   case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
-   case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
-   case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
-   case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
-   case GL_SAMPLER_EXTERNAL_OES:
-      return GL_TRUE;
-   default:
-      return GL_FALSE;
+   if (uniformCount < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                 "glGetUniformIndices(uniformCount < 0)");
+      return;
    }
-}
 
-/**
- * Given a uniform index, return the vertex/geometry/fragment program
- * that has that parameter, plus the position of the parameter in the
- * parameter/constant buffer.
- * \param shProg  the shader program
- * \param index  the uniform index in [0, NumUniforms-1]
- * \param progOut  returns containing program
- * \param posOut  returns position of the uniform in the param/const buffer
- * \return GL_TRUE for success, GL_FALSE for invalid index
- */
-static GLboolean
-find_uniform_parameter_pos(struct gl_shader_program *shProg, GLint index,
-                           struct gl_program **progOut, GLint *posOut)
-{
-   struct gl_program *prog = NULL;
-   GLint pos;
+   for (i = 0; i < uniformCount; i++) {
+      GLuint index = uniformIndices[i];
 
-   if (!shProg->Uniforms ||
-       index < 0 ||
-       index >= (GLint) shProg->Uniforms->NumUniforms) {
-      return GL_FALSE;
-   }
-
-   pos = shProg->Uniforms->Uniforms[index].VertPos;
-   if (pos >= 0) {
-      prog = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->Program;
-   }
-   else {
-      pos = shProg->Uniforms->Uniforms[index].FragPos;
-      if (pos >= 0) {
-         prog = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->Program;
-      }
-      else {
-         pos = shProg->Uniforms->Uniforms[index].GeomPos;
-         if (pos >= 0) {
-            prog = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->Program;
-         }
+      if (index >= shProg->NumUserUniformStorage) {
+        _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniformsiv(index)");
+        return;
       }
    }
 
-   if (!prog || pos < 0)
-      return GL_FALSE; /* should really never happen */
+   for (i = 0; i < uniformCount; i++) {
+      GLuint index = uniformIndices[i];
+      const struct gl_uniform_storage *uni = &shProg->UniformStorage[index];
 
-   *progOut = prog;
-   *posOut = pos;
+      switch (pname) {
+      case GL_UNIFORM_TYPE:
+        params[i] = uni->type->gl_type;
+        break;
 
-   return GL_TRUE;
-}
+      case GL_UNIFORM_SIZE:
+        /* array_elements is zero for non-arrays, but the API requires that 1 be
+         * returned.
+         */
+        params[i] = MAX2(1, uni->array_elements);
+        break;
 
-/**
- * Return pointer to a gl_program_parameter which corresponds to a uniform.
- * \param shProg  the shader program
- * \param index  the uniform index in [0, NumUniforms-1]
- * \return gl_program_parameter point or NULL if index is invalid
- */
-const struct gl_program_parameter *
-get_uniform_parameter(struct gl_shader_program *shProg, GLint index)
-{
-   struct gl_program *prog;
-   GLint progPos;
+      case GL_UNIFORM_NAME_LENGTH:
+        params[i] = strlen(uni->name) + 1;
 
-   if (find_uniform_parameter_pos(shProg, index, &prog, &progPos))
-      return &prog->Parameters->Parameters[progPos];
-   else
-      return NULL;
-}
+         /* Page 61 (page 73 of the PDF) in section 2.11 of the OpenGL ES 3.0
+          * spec says:
+          *
+          *     "If the active uniform is an array, the uniform name returned
+          *     in name will always be the name of the uniform array appended
+          *     with "[0]"."
+          */
+         if (uni->array_elements != 0)
+            params[i] += 3;
+        break;
 
-static unsigned
-get_vector_elements(GLenum type)
-{
-   switch (type) {
-   case GL_FLOAT:
-   case GL_INT:
-   case GL_BOOL:
-   case GL_UNSIGNED_INT:
-   default: /* Catch all the various sampler types. */
-      return 1;
+      case GL_UNIFORM_BLOCK_INDEX:
+        params[i] = uni->block_index;
+        break;
 
-   case GL_FLOAT_VEC2:
-   case GL_INT_VEC2:
-   case GL_BOOL_VEC2:
-   case GL_UNSIGNED_INT_VEC2:
-      return 2;
+      case GL_UNIFORM_OFFSET:
+        params[i] = uni->offset;
+        break;
 
-   case GL_FLOAT_VEC3:
-   case GL_INT_VEC3:
-   case GL_BOOL_VEC3:
-   case GL_UNSIGNED_INT_VEC3:
-      return 3;
+      case GL_UNIFORM_ARRAY_STRIDE:
+        params[i] = uni->array_stride;
+        break;
 
-   case GL_FLOAT_VEC4:
-   case GL_INT_VEC4:
-   case GL_BOOL_VEC4:
-   case GL_UNSIGNED_INT_VEC4:
-      return 4;
-   }
-}
+      case GL_UNIFORM_MATRIX_STRIDE:
+        params[i] = uni->matrix_stride;
+        break;
 
-static void
-get_matrix_dims(GLenum type, GLint *rows, GLint *cols)
-{
-   switch (type) {
-   case GL_FLOAT_MAT2:
-      *rows = *cols = 2;
-      break;
-   case GL_FLOAT_MAT2x3:
-      *rows = 3;
-      *cols = 2;
-      break;
-   case GL_FLOAT_MAT2x4:
-      *rows = 4;
-      *cols = 2;
-      break;
-   case GL_FLOAT_MAT3:
-      *rows = 3;
-      *cols = 3;
-      break;
-   case GL_FLOAT_MAT3x2:
-      *rows = 2;
-      *cols = 3;
-      break;
-   case GL_FLOAT_MAT3x4:
-      *rows = 4;
-      *cols = 3;
-      break;
-   case GL_FLOAT_MAT4:
-      *rows = 4;
-      *cols = 4;
-      break;
-   case GL_FLOAT_MAT4x2:
-      *rows = 2;
-      *cols = 4;
-      break;
-   case GL_FLOAT_MAT4x3:
-      *rows = 3;
-      *cols = 4;
-      break;
-   default:
-      *rows = *cols = 0;
-   }
-}
+      case GL_UNIFORM_IS_ROW_MAJOR:
+        params[i] = uni->row_major;
+        break;
 
-/**
- * Determine the number of rows and columns occupied by a uniform
- * according to its datatype.  For non-matrix types (such as GL_FLOAT_VEC4),
- * the number of rows = 1 and cols = number of elements in the vector.
- */
-static void
-get_uniform_rows_cols(const struct gl_program_parameter *p,
-                      GLint *rows, GLint *cols)
-{
-   get_matrix_dims(p->DataType, rows, cols);
-   if (*rows == 0 && *cols == 0) {
-      /* not a matrix type, probably a float or vector */
-      *rows = 1;
-      *cols = get_vector_elements(p->DataType);
+      case GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX:
+         if (!ctx->Extensions.ARB_shader_atomic_counters)
+            goto invalid_enum;
+         params[i] = uni->atomic_buffer_index;
+         break;
+
+      default:
+         goto invalid_enum;
+      }
    }
+
+   return;
+
+ invalid_enum:
+   _mesa_error(ctx, GL_INVALID_ENUM, "glGetActiveUniformsiv(pname)");
 }
 
 static bool
@@ -399,6 +237,8 @@ validate_uniform_parameters(struct gl_context *ctx,
     *
     *         - if no variable with a location of location exists in the
     *           program object currently in use and location is not -1,
+    *         - if count is greater than one, and the uniform declared in the
+    *           shader is not an array variable,
     */
    if (location < -1) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
@@ -406,7 +246,7 @@ validate_uniform_parameters(struct gl_context *ctx,
       return false;
    }
 
-   _mesa_uniform_split_location_offset(location, loc, array_index);
+   _mesa_uniform_split_location_offset(shProg, location, loc, array_index);
 
    if (*loc >= shProg->NumUserUniformStorage) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
@@ -414,11 +254,21 @@ validate_uniform_parameters(struct gl_context *ctx,
       return false;
    }
 
-   /* This case should be impossible.  The implication is that a call like
-    * glGetUniformLocation(prog, "foo[8]") was successful but "foo" is not an
-    * array.
+   if (shProg->UniformStorage[*loc].array_elements == 0 && count > 1) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                 "%s(count > 1 for non-array, location=%d)",
+                 caller, location);
+      return false;
+   }
+
+   /* If the uniform is an array, check that array_index is in bounds.
+    * If not an array, check that array_index is zero.
+    * array_index is unsigned so no need to check for less than zero.
     */
-   if (*array_index != 0 && shProg->UniformStorage[*loc].array_elements == 0) {
+   unsigned limit = shProg->UniformStorage[*loc].array_elements;
+   if (limit == 0)
+      limit = 1;
+   if (*array_index >= limit) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
                  caller, location);
       return false;
@@ -455,10 +305,18 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location,
       const union gl_constant_value *const src =
         &uni->storage[offset * elements];
 
-      unsigned bytes = sizeof(uni->storage[0]) * elements;
-      if (bytes > bufSize) {
-        elements = bufSize / sizeof(uni->storage[0]);
-        bytes = bufSize;
+      assert(returnType == GLSL_TYPE_FLOAT || returnType == GLSL_TYPE_INT ||
+             returnType == GLSL_TYPE_UINT);
+      /* The three (currently) supported types all have the same size,
+       * which is of course the same as their union. That'll change
+       * with glGetUniformdv()...
+       */
+      unsigned bytes = sizeof(src[0]) * elements;
+      if (bufSize < 0 || bytes > (unsigned) bufSize) {
+        _mesa_error( ctx, GL_INVALID_OPERATION,
+                    "glGetnUniform*vARB(out of bounds: bufSize is %d,"
+                    " but %u bytes are required)", bufSize, bytes );
+        return;
       }
 
       /* If the return type and the uniform's native type are "compatible,"
@@ -587,20 +445,14 @@ log_uniform(const void *values, enum glsl_base_type basicType,
 static void
 log_program_parameters(const struct gl_shader_program *shProg)
 {
-   static const char *stages[] = {
-      "vertex", "fragment", "geometry"
-   };
-
-   assert(Elements(stages) == MESA_SHADER_TYPES);
-
-   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+   for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
       if (shProg->_LinkedShaders[i] == NULL)
         continue;
 
       const struct gl_program *const prog = shProg->_LinkedShaders[i]->Program;
 
       printf("Program %d %s shader parameters:\n",
-            shProg->Name, stages[i]);
+             shProg->Name, _mesa_shader_stage_to_string(i));
       for (unsigned j = 0; j < prog->Parameters->NumParameters; j++) {
         printf("%s: %p %f %f %f %f\n",
                prog->Parameters->Parameters[j].Name,
@@ -615,207 +467,6 @@ log_program_parameters(const struct gl_shader_program *shProg)
 }
 #endif
 
-/**
- * Check if the type given by userType is allowed to set a uniform of the
- * target type.  Generally, equivalence is required, but setting Boolean
- * uniforms can be done with glUniformiv or glUniformfv.
- */
-static GLboolean
-compatible_types(GLenum userType, GLenum targetType)
-{
-   if (userType == targetType)
-      return GL_TRUE;
-
-   if (targetType == GL_BOOL && (userType == GL_FLOAT ||
-                                 userType == GL_UNSIGNED_INT ||
-                                 userType == GL_INT))
-      return GL_TRUE;
-
-   if (targetType == GL_BOOL_VEC2 && (userType == GL_FLOAT_VEC2 ||
-                                      userType == GL_UNSIGNED_INT_VEC2 ||
-                                      userType == GL_INT_VEC2))
-      return GL_TRUE;
-
-   if (targetType == GL_BOOL_VEC3 && (userType == GL_FLOAT_VEC3 ||
-                                      userType == GL_UNSIGNED_INT_VEC3 ||
-                                      userType == GL_INT_VEC3))
-      return GL_TRUE;
-
-   if (targetType == GL_BOOL_VEC4 && (userType == GL_FLOAT_VEC4 ||
-                                      userType == GL_UNSIGNED_INT_VEC4 ||
-                                      userType == GL_INT_VEC4))
-      return GL_TRUE;
-
-   if (is_sampler_type(targetType) && userType == GL_INT)
-      return GL_TRUE;
-
-   return GL_FALSE;
-}
-
-/**
- * Set the value of a program's uniform variable.
- * \param program  the program whose uniform to update
- * \param index  the index of the program parameter for the uniform
- * \param offset  additional parameter slot offset (for arrays)
- * \param type  the incoming datatype of 'values'
- * \param count  the number of uniforms to set
- * \param elems  number of elements per uniform (1, 2, 3 or 4)
- * \param values  the new values, of datatype 'type'
- */
-static void
-set_program_uniform(struct gl_context *ctx, struct gl_program *program,
-                    GLint index, GLint offset,
-                    GLenum type, GLsizei count, GLint elems,
-                    const void *values)
-{
-   const struct gl_program_parameter *param =
-      &program->Parameters->Parameters[index];
-
-   assert(offset >= 0);
-   assert(elems >= 1);
-   assert(elems <= 4);
-
-   if (!compatible_types(type, param->DataType)) {
-      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)");
-      return;
-   }
-
-   if (index + offset > (GLint) program->Parameters->Size) {
-      /* out of bounds! */
-      return;
-   }
-
-   if (param->Type == PROGRAM_SAMPLER) {
-      /* This controls which texture unit which is used by a sampler */
-      GLboolean changed = GL_FALSE;
-      GLint i;
-
-      /* this should have been caught by the compatible_types() check */
-      ASSERT(type == GL_INT);
-
-      /* loop over number of samplers to change */
-      for (i = 0; i < count; i++) {
-         GLuint sampler = (GLuint)
-            program->Parameters->ParameterValues[index+offset + i][0].f;
-         GLuint texUnit = ((GLuint *) values)[i];
-
-         /* check that the sampler (tex unit index) is legal */
-         if (texUnit >= ctx->Const.MaxCombinedTextureImageUnits) {
-            _mesa_error(ctx, GL_INVALID_VALUE,
-                        "glUniform1(invalid sampler/tex unit index for '%s')",
-                        param->Name);
-            return;
-         }
-
-         /* This maps a sampler to a texture unit: */
-         if (sampler < MAX_SAMPLERS) {
-#if 0
-            printf("Set program %p sampler %d '%s' to unit %u\n",
-                  program, sampler, param->Name, texUnit);
-#endif
-            if (program->SamplerUnits[sampler] != texUnit) {
-               program->SamplerUnits[sampler] = texUnit;
-               changed = GL_TRUE;
-            }
-         }
-      }
-
-      if (changed) {
-         /* When a sampler's value changes it usually requires rewriting
-          * a GPU program's TEX instructions since there may not be a
-          * sampler->texture lookup table.  We signal this with the
-          * ProgramStringNotify() callback.
-          */
-         FLUSH_VERTICES(ctx, _NEW_TEXTURE | _NEW_PROGRAM);
-         _mesa_update_shader_textures_used(program);
-         /* Do we need to care about the return value here?
-          * This should not be the first time the driver was notified of
-          * this program.
-          */
-         (void) ctx->Driver.ProgramStringNotify(ctx, program->Target, program);
-      }
-   }
-   else {
-      /* ordinary uniform variable */
-      const GLboolean isUniformBool = is_boolean_type(param->DataType);
-      const GLenum basicType = base_uniform_type(type);
-      const GLint slots = (param->Size + 3) / 4;
-      const GLint typeSize = _mesa_sizeof_glsl_type(param->DataType);
-      GLsizei k, i;
-
-      if ((GLint) param->Size > typeSize) {
-         /* an array */
-         /* we'll ignore extra data below */
-      }
-      else {
-         /* non-array: count must be at most one; count == 0 is handled
-          * by the loop below
-          */
-         if (count > 1) {
-            _mesa_error(ctx, GL_INVALID_OPERATION,
-                        "glUniform(uniform '%s' is not an array)",
-                        param->Name);
-            return;
-         }
-      }
-
-      /* loop over number of array elements */
-      for (k = 0; k < count; k++) {
-         gl_constant_value *uniformVal;
-
-         if (offset + k >= slots) {
-            /* Extra array data is ignored */
-            break;
-         }
-
-         /* uniformVal (the destination) is always gl_constant_value[4] */
-         uniformVal = program->Parameters->ParameterValues[index + offset + k];
-
-         if (basicType == GL_INT) {
-            const GLint *iValues = ((const GLint *) values) + k * elems;
-            for (i = 0; i < elems; i++) {
-               if (!ctx->Const.NativeIntegers)
-                  uniformVal[i].f = (GLfloat) iValues[i];
-               else
-                  uniformVal[i].i = iValues[i];
-            }
-         }
-         else if (basicType == GL_UNSIGNED_INT) {
-            const GLuint *iValues = ((const GLuint *) values) + k * elems;
-            for (i = 0; i < elems; i++) {
-               if (!ctx->Const.NativeIntegers)
-                  uniformVal[i].f = (GLfloat)(GLuint) iValues[i];
-               else
-                  uniformVal[i].u = iValues[i];
-            }
-         }
-         else {
-            const GLfloat *fValues = ((const GLfloat *) values) + k * elems;
-            assert(basicType == GL_FLOAT);
-            for (i = 0; i < elems; i++) {
-               uniformVal[i].f = fValues[i];
-            }
-         }
-
-         /* if the uniform is bool-valued, convert to 1 or 0 */
-         if (isUniformBool) {
-            for (i = 0; i < elems; i++) {
-               if (basicType == GL_FLOAT)
-                  uniformVal[i].b = uniformVal[i].f != 0.0f ? 1 : 0;
-               else
-                  uniformVal[i].b = uniformVal[i].u ? 1 : 0;
-
-               if (ctx->Const.NativeIntegers)
-                  uniformVal[i].u =
-                        uniformVal[i].b ? ctx->Const.UniformBooleanTrue : 0;
-               else
-                  uniformVal[i].f = uniformVal[i].b ? 1.0f : 0.0f;
-            }
-         }
-      }
-   }
-}
-
 /**
  * Propagate some values from uniform backing storage to driver storage
  *
@@ -943,12 +594,9 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
    unsigned loc, offset;
    unsigned components;
    unsigned src_components;
-   unsigned i;
    enum glsl_base_type basicType;
    struct gl_uniform_storage *uni;
 
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
-
    if (!validate_uniform_parameters(ctx, shProg, location, count,
                                    &loc, &offset, "glUniform", false))
       return;
@@ -1071,6 +719,8 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
     * GL_INVALID_VALUE error and ignore the command.
     */
    if (uni->type->is_sampler()) {
+      int i;
+
       for (i = 0; i < count; i++) {
         const unsigned texUnit = ((unsigned *) values)[i];
 
@@ -1097,10 +747,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
     * will have already generated an error.
     */
    if (uni->array_elements != 0) {
-      if (offset >= uni->array_elements)
-        return;
-
-      count = MIN2(count, (uni->array_elements - offset));
+      count = MIN2(count, (int) (uni->array_elements - offset));
    }
 
    FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS);
@@ -1115,6 +762,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
         (const union gl_constant_value *) values;
       union gl_constant_value *dst = &uni->storage[components * offset];
       const unsigned elems = components * count;
+      unsigned i;
 
       for (i = 0; i < elems; i++) {
         if (basicType == GLSL_TYPE_FLOAT) {
@@ -1133,117 +781,64 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
     * the changes through.
     */
    if (uni->type->is_sampler()) {
-      for (i = 0; i < count; i++) {
-        shProg->SamplerUnits[uni->sampler + offset + i] =
-           ((unsigned *) values)[i];
-      }
+      int i;
 
       bool flushed = false;
-      for (i = 0; i < MESA_SHADER_TYPES; i++) {
-        struct gl_program *prog;
+      for (i = 0; i < MESA_SHADER_STAGES; i++) {
+        struct gl_shader *const sh = shProg->_LinkedShaders[i];
+         int j;
 
-        if (shProg->_LinkedShaders[i] == NULL)
+        /* If the shader stage doesn't use the sampler uniform, skip this.
+         */
+        if (sh == NULL || !uni->sampler[i].active)
            continue;
 
-        prog = shProg->_LinkedShaders[i]->Program;
+         for (j = 0; j < count; j++) {
+            sh->SamplerUnits[uni->sampler[i].index + offset + j] =
+               ((unsigned *) values)[j];
+         }
+
+        struct gl_program *const prog = sh->Program;
 
-        assert(sizeof(prog->SamplerUnits) == sizeof(shProg->SamplerUnits));
+        assert(sizeof(prog->SamplerUnits) == sizeof(sh->SamplerUnits));
 
-        if (memcmp(prog->SamplerUnits,
-                   shProg->SamplerUnits,
-                   sizeof(shProg->SamplerUnits)) != 0) {
+        /* Determine if any of the samplers used by this shader stage have
+         * been modified.
+         */
+        bool changed = false;
+        for (unsigned j = 0; j < Elements(prog->SamplerUnits); j++) {
+           if ((sh->active_samplers & (1U << j)) != 0
+               && (prog->SamplerUnits[j] != sh->SamplerUnits[j])) {
+              changed = true;
+              break;
+           }
+        }
+
+        if (changed) {
            if (!flushed) {
               FLUSH_VERTICES(ctx, _NEW_TEXTURE | _NEW_PROGRAM);
               flushed = true;
            }
 
            memcpy(prog->SamplerUnits,
-                  shProg->SamplerUnits,
-                  sizeof(shProg->SamplerUnits));
+                  sh->SamplerUnits,
+                  sizeof(sh->SamplerUnits));
 
-           _mesa_update_shader_textures_used(prog);
-           (void) ctx->Driver.ProgramStringNotify(ctx, prog->Target, prog);
+           _mesa_update_shader_textures_used(shProg, prog);
+            if (ctx->Driver.SamplerUniformChange)
+              ctx->Driver.SamplerUniformChange(ctx, prog->Target, prog);
         }
       }
    }
 }
 
-/**
- * Set a matrix-valued program parameter.
- */
-static void
-set_program_uniform_matrix(struct gl_context *ctx, struct gl_program *program,
-                           GLuint index, GLuint offset,
-                           GLuint count, GLuint rows, GLuint cols,
-                           GLboolean transpose, const GLfloat *values)
-{
-   GLuint mat, row, col;
-   GLuint src = 0;
-   const struct gl_program_parameter *param =
-      &program->Parameters->Parameters[index];
-   const GLuint slots = (param->Size + 3) / 4;
-   const GLint typeSize = _mesa_sizeof_glsl_type(param->DataType);
-   GLint nr, nc;
-
-   /* check that the number of rows, columns is correct */
-   get_matrix_dims(param->DataType, &nr, &nc);
-   if (rows != nr || cols != nc) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "glUniformMatrix(matrix size mismatch)");
-      return;
-   }
-
-   if ((GLint) param->Size <= typeSize) {
-      /* non-array: count must be at most one; count == 0 is handled
-       * by the loop below
-       */
-      if (count > 1) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glUniformMatrix(uniform is not an array)");
-         return;
-      }
-   }
-
-   /*
-    * Note: the _columns_ of a matrix are stored in program registers, not
-    * the rows.  So, the loops below look a little funny.
-    * XXX could optimize this a bit...
-    */
-
-   /* loop over matrices */
-   for (mat = 0; mat < count; mat++) {
-
-      /* each matrix: */
-      for (col = 0; col < cols; col++) {
-         GLfloat *v;
-         if (offset >= slots) {
-            /* Ignore writes beyond the end of (the used part of) an array */
-            return;
-         }
-         v = (GLfloat *) program->Parameters->ParameterValues[index + offset];
-         for (row = 0; row < rows; row++) {
-            if (transpose) {
-               v[row] = values[src + row * cols + col];
-            }
-            else {
-               v[row] = values[src + col * rows + row];
-            }
-         }
-
-         offset++;
-      }
-
-      src += rows * cols;  /* next matrix */
-   }
-}
-
 /**
  * Called by glUniformMatrix*() functions.
  * Note: cols=2, rows=4  ==>  array[2] of vec4
  */
 extern "C" void
 _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
-                    GLint cols, GLint rows,
+                    GLuint cols, GLuint rows,
                      GLint location, GLsizei count,
                      GLboolean transpose, const GLfloat *values)
 {
@@ -1253,8 +848,6 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
    unsigned elements;
    struct gl_uniform_storage *uni;
 
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
-
    if (!validate_uniform_parameters(ctx, shProg, location, count,
                                    &loc, &offset, "glUniformMatrix", false))
       return;
@@ -1279,6 +872,17 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       return;
    }
 
+   /* GL_INVALID_VALUE is generated if `transpose' is not GL_FALSE.
+    * http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml */
+   if (ctx->API == API_OPENGLES
+       || (ctx->API == API_OPENGLES2 && ctx->Version < 30)) {
+      if (transpose) {
+        _mesa_error(ctx, GL_INVALID_VALUE,
+                    "glUniformMatrix(matrix transpose is not GL_FALSE)");
+        return;
+      }
+   }
+
    if (ctx->Shader.Flags & GLSL_UNIFORMS) {
       log_uniform(values, GLSL_TYPE_FLOAT, components, vectors, count,
                  bool(transpose), shProg, location, uni);
@@ -1296,10 +900,7 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
     * will have already generated an error.
     */
    if (uni->array_elements != 0) {
-      if (offset >= uni->array_elements)
-        return;
-
-      count = MIN2(count, (uni->array_elements - offset));
+      count = MIN2(count, (int) (uni->array_elements - offset));
    }
 
    FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS);
@@ -1317,7 +918,7 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       const float *src = values;
       float *dst = &uni->storage[elements * offset].f;
 
-      for (unsigned i = 0; i < count; i++) {
+      for (int i = 0; i < count; i++) {
         for (unsigned r = 0; r < rows; r++) {
            for (unsigned c = 0; c < cols; c++) {
               dst[(c * components) + r] = src[c + (r * vectors)];
@@ -1334,85 +935,54 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
    _mesa_propagate_uniforms_to_driver_storage(uni, offset, count);
 }
 
+
 /**
  * Called via glGetUniformLocation().
  *
- * The return value will encode two values, the uniform location and an
- * offset (used for arrays, structs).
+ * Returns the uniform index into UniformStorage (also the
+ * glGetActiveUniformsiv uniform index), and stores the referenced
+ * array offset in *offset, or GL_INVALID_INDEX (-1).  Those two
+ * return values can be encoded into a uniform location for
+ * glUniform* using _mesa_uniform_merge_location_offset(index, offset).
  */
-extern "C" GLint
+extern "C" unsigned
 _mesa_get_uniform_location(struct gl_context *ctx,
                            struct gl_shader_program *shProg,
-                          const GLchar *name)
+                           const GLchar *name,
+                           unsigned *out_offset)
 {
-   const size_t len = strlen(name);
-   long offset;
-   bool array_lookup;
-   char *name_copy;
-
-   /* If the name ends with a ']', assume that it refers to some element of an
-    * array.  Malformed array references will fail the hash table look up
-    * below, so it doesn't matter that they are not caught here.  This code
-    * only wants to catch the "leaf" array references so that arrays of
-    * structures containing arrays will be handled correctly.
+   /* Page 80 (page 94 of the PDF) of the OpenGL 2.1 spec says:
+    *
+    *     "The first element of a uniform array is identified using the
+    *     name of the uniform array appended with "[0]". Except if the last
+    *     part of the string name indicates a uniform array, then the
+    *     location of the first element of that array can be retrieved by
+    *     either using the name of the uniform array, or the name of the
+    *     uniform array appended with "[0]"."
+    *
+    * Note: since uniform names are not allowed to use whitespace, and array
+    * indices within uniform names are not allowed to use "+", "-", or leading
+    * zeros, it follows that each uniform has a unique name up to the possible
+    * ambiguity with "[0]" noted above.  Therefore we don't need to worry
+    * about mal-formed inputs--they will properly fail when we try to look up
+    * the uniform name in shProg->UniformHash.
     */
-   if (name[len-1] == ']') {
-      unsigned i;
-
-      /* Walk backwards over the string looking for a non-digit character.
-       * This had better be the opening bracket for an array index.
-       *
-       * Initially, i specifies the location of the ']'.  Since the string may
-       * contain only the ']' charcater, walk backwards very carefully.
-       */
-      for (i = len - 1; (i > 0) && isdigit(name[i-1]); --i)
-        /* empty */ ;
-
-      /* Page 80 (page 94 of the PDF) of the OpenGL 2.1 spec says:
-       *
-       *     "The first element of a uniform array is identified using the
-       *     name of the uniform array appended with "[0]". Except if the last
-       *     part of the string name indicates a uniform array, then the
-       *     location of the first element of that array can be retrieved by
-       *     either using the name of the uniform array, or the name of the
-       *     uniform array appended with "[0]"."
-       *
-       * Page 79 (page 93 of the PDF) of the OpenGL 2.1 spec says:
-       *
-       *     "name must be a null terminated string, without white space."
-       *
-       * Return an error if there is no opening '[' to match the closing ']'.
-       * An error will also be returned if there is intervening white space
-       * (or other non-digit characters) before the opening '['.
-       */
-      if ((i == 0) || name[i-1] != '[')
-        return -1;
-
-      /* Return an error if there are no digits between the opening '[' to
-       * match the closing ']'.
-       */
-      if (i == (len - 1))
-        return -1;
 
-      /* Make a new string that is a copy of the old string up to (but not
-       * including) the '[' character.
-       */
-      name_copy = (char *) malloc(i);
-      memcpy(name_copy, name, i - 1);
-      name_copy[i-1] = '\0';
-
-      offset = strtol(&name[i], NULL, 10);
-      if (offset < 0)
-        return -1;
+   const GLchar *base_name_end;
+   long offset = parse_program_resource_name(name, &base_name_end);
+   bool array_lookup = offset >= 0;
+   char *name_copy;
 
-      array_lookup = true;
+   if (array_lookup) {
+      name_copy = (char *) malloc(base_name_end - name + 1);
+      memcpy(name_copy, name, base_name_end - name);
+      name_copy[base_name_end - name] = '\0';
    } else {
       name_copy = (char *) name;
       offset = 0;
-      array_lookup = false;
    }
 
-   unsigned location;
+   unsigned location = 0;
    const bool found = shProg->UniformHash->get(location, name_copy);
 
    assert(!found
@@ -1424,14 +994,61 @@ _mesa_get_uniform_location(struct gl_context *ctx,
       free(name_copy);
 
    if (!found)
-      return -1;
+      return GL_INVALID_INDEX;
 
-   /* Since array_elements is 0 for non-arrays, this causes look-ups of 'a[0]'
-    * to (correctly) fail if 'a' is not an array.
+   /* If the uniform is an array, fail if the index is out of bounds.
+    * (A negative index is caught above.)  This also fails if the uniform
+    * is not an array, but the user is trying to index it, because
+    * array_elements is zero and offset >= 0.
     */
-   if (array_lookup && shProg->UniformStorage[location].array_elements == 0) {
-      return -1;
+   if (array_lookup
+       && offset >= (long) shProg->UniformStorage[location].array_elements) {
+      return GL_INVALID_INDEX;
    }
 
-   return _mesa_uniform_merge_location_offset(location, offset);
+   *out_offset = offset;
+   return location;
+}
+
+extern "C" bool
+_mesa_sampler_uniforms_are_valid(const struct gl_shader_program *shProg,
+                                char *errMsg, size_t errMsgLength)
+{
+   const glsl_type *unit_types[MAX_COMBINED_TEXTURE_IMAGE_UNITS];
+
+   memset(unit_types, 0, sizeof(unit_types));
+
+   for (unsigned i = 0; i < shProg->NumUserUniformStorage; i++) {
+      const struct gl_uniform_storage *const storage =
+        &shProg->UniformStorage[i];
+      const glsl_type *const t = (storage->type->is_array())
+        ? storage->type->fields.array : storage->type;
+
+      if (!t->is_sampler())
+        continue;
+
+      const unsigned count = MAX2(1, storage->type->array_size());
+      for (unsigned j = 0; j < count; j++) {
+        const unsigned unit = storage->storage[j].i;
+
+        /* The types of the samplers associated with a particular texture
+         * unit must be an exact match.  Page 74 (page 89 of the PDF) of the
+         * OpenGL 3.3 core spec says:
+         *
+         *     "It is not allowed to have variables of different sampler
+         *     types pointing to the same texture image unit within a program
+         *     object."
+         */
+        if (unit_types[unit] == NULL) {
+           unit_types[unit] = t;
+        } else if (unit_types[unit] != t) {
+           _mesa_snprintf(errMsg, errMsgLength,
+                          "Texture unit %d is accessed both as %s and %s",
+                          unit, unit_types[unit]->name, t->name);
+           return false;
+        }
+      }
+   }
+
+   return true;
 }