mesa: update the shader programs->TexturesUsed array at link time
[mesa.git] / src / mesa / shader / shader_api.c
index 99bcc0aeb42e6cb14084d801701ebf015f5269d0..7af502a84cb98f0775bf7b8237a59f25787bc4ac 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Mesa 3-D graphics library
- * Version:  7.0
+ * Version:  7.2
  *
- * Copyright (C) 2004-2007  Brian Paul   All Rights Reserved.
+ * Copyright (C) 2004-2008  Brian Paul   All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  */
 
 
-#include "glheader.h"
-#include "context.h"
-#include "hash.h"
-#include "macros.h"
+#include "main/glheader.h"
+#include "main/context.h"
+#include "main/hash.h"
+#include "main/macros.h"
 #include "program.h"
 #include "prog_parameter.h"
 #include "prog_print.h"
 
 
 
+#ifndef GL_PROGRAM_BINARY_LENGTH_OES
+#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741
+#endif
+
+
 /**
  * Allocate a new gl_shader_program object, initialize it.
  */
@@ -262,15 +267,11 @@ _mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type)
 void
 _mesa_free_shader(GLcontext *ctx, struct gl_shader *sh)
 {
-   GLuint i;
    if (sh->Source)
       _mesa_free((void *) sh->Source);
    if (sh->InfoLog)
       _mesa_free(sh->InfoLog);
-   for (i = 0; i < sh->NumPrograms; i++)
-      _mesa_reference_program(ctx, &sh->Programs[i], NULL);
-   if (sh->Programs)
-      _mesa_free(sh->Programs);
+   _mesa_reference_program(ctx, &sh->Program, NULL);
    _mesa_free(sh);
 }
 
@@ -496,10 +497,14 @@ _mesa_get_attrib_location(GLcontext *ctx, GLuint program,
    if (!name)
       return -1;
 
-   if (shProg->Attributes) {
-      GLint i = _mesa_lookup_parameter_index(shProg->Attributes, -1, name);
-      if (i >= 0) {
-         return shProg->Attributes->Parameters[i].StateIndexes[0];
+   if (shProg->VertexProgram) {
+      const struct gl_program_parameter_list *attribs =
+         shProg->VertexProgram->Base.Attributes;
+      if (attribs) {
+         GLint i = _mesa_lookup_parameter_index(attribs, -1, name);
+         if (i >= 0) {
+            return attribs->Parameters[i].StateIndexes[0];
+         }
       }
    }
    return -1;
@@ -512,8 +517,8 @@ _mesa_bind_attrib_location(GLcontext *ctx, GLuint program, GLuint index,
 {
    struct gl_shader_program *shProg;
    const GLint size = -1; /* unknown size */
-   GLint i, oldIndex;
-   GLenum datatype;
+   GLint i;
+   GLenum datatype = GL_FLOAT_VEC4;
 
    shProg = _mesa_lookup_shader_program_err(ctx, program,
                                             "glBindAttribLocation");
@@ -535,27 +540,17 @@ _mesa_bind_attrib_location(GLcontext *ctx, GLuint program, GLuint index,
       return;
    }
 
-   if (shProg->LinkStatus) {
-      /* get current index/location for the attribute */
-      oldIndex = _mesa_get_attrib_location(ctx, program, name);
-      assert(0);
-   }
-   else {
-      oldIndex = -1;
-   }
-
    /* this will replace the current value if it's already in the list */
    i = _mesa_add_attribute(shProg->Attributes, name, size, datatype, index);
    if (i < 0) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindAttribLocation");
+      return;
    }
 
-   if (shProg->VertexProgram && oldIndex >= 0 && oldIndex != index) {
-      /* If the index changed, need to search/replace references to that attribute
-       * in the vertex program.
-       */
-      _slang_remap_attribute(&shProg->VertexProgram->Base, oldIndex, index);
-   }
+   /*
+    * Note that this attribute binding won't go into effect until
+    * glLinkProgram is called again.
+    */
 }
 
 
@@ -719,6 +714,17 @@ sizeof_glsl_type(GLenum type)
    case GL_FLOAT:
    case GL_INT:
    case GL_BOOL:
+   case GL_SAMPLER_1D:
+   case GL_SAMPLER_2D:
+   case GL_SAMPLER_3D:
+   case GL_SAMPLER_CUBE:
+   case GL_SAMPLER_1D_SHADOW:
+   case GL_SAMPLER_2D_SHADOW:
+   case GL_SAMPLER_2D_RECT_ARB:
+   case GL_SAMPLER_2D_RECT_SHADOW_ARB:
+   case GL_SAMPLER_1D_ARRAY_SHADOW_EXT:
+   case GL_SAMPLER_2D_ARRAY_SHADOW_EXT:
+   case GL_SAMPLER_CUBE_SHADOW_EXT:
       return 1;
    case GL_FLOAT_VEC2:
    case GL_INT_VEC2:
@@ -751,29 +757,88 @@ 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,
                         GLenum *type, GLchar *nameOut)
 {
+   const struct gl_program_parameter_list *attribs = NULL;
    struct gl_shader_program *shProg;
 
    shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveAttrib");
    if (!shProg)
       return;
 
-   if (!shProg->Attributes || index >= shProg->Attributes->NumParameters) {
+   if (shProg->VertexProgram)
+      attribs = shProg->VertexProgram->Base.Attributes;
+
+   if (!attribs || index >= attribs->NumParameters) {
       _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(index)");
       return;
    }
 
-   copy_string(nameOut, maxLength, length,
-               shProg->Attributes->Parameters[index].Name);
+   copy_string(nameOut, maxLength, length, attribs->Parameters[index].Name);
+
    if (size)
-      *size = shProg->Attributes->Parameters[index].Size
-         / sizeof_glsl_type(shProg->Attributes->Parameters[index].DataType);
+      *size = attribs->Parameters[index].Size
+         / sizeof_glsl_type(attribs->Parameters[index].DataType);
+
    if (type)
-      *type = shProg->Attributes->Parameters[index].DataType;
+      *type = attribs->Parameters[index].DataType;
+}
+
+
+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];
 }
 
 
@@ -871,6 +936,7 @@ static void
 _mesa_get_programiv(GLcontext *ctx, GLuint program,
                     GLenum pname, GLint *params)
 {
+   const struct gl_program_parameter_list *attribs;
    struct gl_shader_program *shProg
       = _mesa_lookup_shader_program(ctx, program);
 
@@ -879,6 +945,11 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program,
       return;
    }
 
+   if (shProg->VertexProgram)
+      attribs = shProg->VertexProgram->Base.Attributes;
+   else
+      attribs = NULL;
+
    switch (pname) {
    case GL_DELETE_STATUS:
       *params = shProg->DeletePending;
@@ -896,11 +967,10 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program,
       *params = shProg->NumShaders;
       break;
    case GL_ACTIVE_ATTRIBUTES:
-      *params = shProg->Attributes ? shProg->Attributes->NumParameters : 0;
+      *params = attribs ? attribs->NumParameters : 0;
       break;
    case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
-      *params = _mesa_longest_parameter_name(shProg->Attributes,
-                                             PROGRAM_INPUT) + 1;
+      *params = _mesa_longest_parameter_name(attribs, PROGRAM_INPUT) + 1;
       break;
    case GL_ACTIVE_UNIFORMS:
       *params = shProg->Uniforms ? shProg->Uniforms->NumUniforms : 0;
@@ -910,6 +980,9 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program,
       if (*params > 0)
          (*params)++;  /* add one for terminating zero */
       break;
+   case GL_PROGRAM_BINARY_LENGTH_OES:
+      *params = 0;
+      break;
    default:
       _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramiv(pname)");
       return;
@@ -992,6 +1065,78 @@ _mesa_get_shader_source(GLcontext *ctx, GLuint shader, GLsizei maxLength,
 }
 
 
+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;
+   }
+}
+
+
+/**
+ * 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 */
+      if (p->Size <= 4) {
+         *rows = 1;
+         *cols = p->Size;
+      }
+      else {
+         *rows = p->Size / 4 + 1;
+         if (p->Size % 4 == 0)
+            *cols = 4;
+         else
+            *cols = p->Size % 4;
+      }
+   }
+}
+
+
 #define MAX_UNIFORM_ELEMENTS 16
 
 /**
@@ -1003,12 +1148,11 @@ get_uniformfv(GLcontext *ctx, GLuint program, GLint location,
               GLfloat *params)
 {
    struct gl_shader_program *shProg
-      = _mesa_lookup_shader_program(ctx, program);
+      = _mesa_lookup_shader_program_err(ctx, program, "glGetUniform[if]v");
    if (shProg) {
       if (shProg->Uniforms &&
           location >= 0 && location < (GLint) shProg->Uniforms->NumUniforms) {
          GLint progPos;
-         GLuint i;
          const struct gl_program *prog = NULL;
 
          progPos = shProg->Uniforms->Uniforms[location].VertPos;
@@ -1024,22 +1168,29 @@ get_uniformfv(GLcontext *ctx, GLuint program, GLint location,
 
          ASSERT(prog);
          if (prog) {
+            const struct gl_program_parameter *p =
+               &prog->Parameters->Parameters[progPos];
+            GLint rows, cols, i, j, k;
+
             /* See uniformiv() below */                    
-            assert(prog->Parameters->Parameters[progPos].Size <= MAX_UNIFORM_ELEMENTS);
+            assert(p->Size <= MAX_UNIFORM_ELEMENTS);
 
-            for (i = 0; i < prog->Parameters->Parameters[progPos].Size; i++) {
-               params[i] = prog->Parameters->ParameterValues[progPos][i];
+            get_uniform_rows_cols(p, &rows, &cols);
+
+            k = 0;
+            for (i = 0; i < rows; i++) {
+               for (j = 0; j < cols; j++ ) {
+                  params[k++] = prog->Parameters->ParameterValues[progPos+i][j];
+               }
             }
-            return prog->Parameters->Parameters[progPos].Size;
+
+            return p->Size;
          }
       }
       else {
          _mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(location)");
       }
    }
-   else {
-      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(program)");
-   }
    return 0;
 }
 
@@ -1072,12 +1223,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");
 
@@ -1093,7 +1274,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;
 }
 
 
@@ -1189,10 +1417,22 @@ _mesa_use_program(GLcontext *ctx, GLuint program)
 
 
 /**
- * Update the vertex and fragment program's TexturesUsed arrays.
+ * Update the vertex/fragment program's TexturesUsed array.
+ *
+ * This needs to be called after glUniform(set sampler var) is called.
+ * A call to glUniform(samplerVar, value) causes a sampler to point to a
+ * particular texture unit.  We know the sampler's texture target
+ * (1D/2D/3D/etc) from compile time but the sampler's texture unit is
+ * set by glUniform() calls.
+ *
+ * So, scan the program->SamplerUnits[] and program->SamplerTargets[]
+ * information to update the prog->TexturesUsed[] values.
+ * Each value of TexturesUsed[unit] is one of zero, TEXTURE_1D_INDEX,
+ * TEXTURE_2D_INDEX, TEXTURE_3D_INDEX, etc.
+ * We'll use that info for state validation before rendering.
  */
-static void
-update_textures_used(struct gl_program *prog)
+void
+_mesa_update_shader_textures_used(struct gl_program *prog)
 {
    GLuint s;
 
@@ -1266,23 +1506,35 @@ 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)
 {
-   if (!compatible_types(type,
-                         program->Parameters->Parameters[location].DataType)) {
+   struct gl_program_parameter *param =
+      &program->Parameters->Parameters[index];
+
+   assert(offset >= 0);
+
+   if (!compatible_types(type, param->DataType)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)");
       return;
    }
 
-   if (program->Parameters->Parameters[location].Type == PROGRAM_SAMPLER) {
+   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 */
       GLuint texUnit, sampler;
 
@@ -1294,7 +1546,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 */
@@ -1306,25 +1558,27 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location,
 
       /* This maps a sampler to a texture unit: */
       program->SamplerUnits[sampler] = texUnit;
-      update_textures_used(program);
+      _mesa_update_shader_textures_used(program);
 
       FLUSH_VERTICES(ctx, _NEW_TEXTURE);
    }
    else {
       /* ordinary uniform variable */
       GLsizei k, i;
+      GLint slots = (param->Size + 3) / 4;
 
-      if (count * elems > (GLint) program->Parameters->Parameters[location].Size) {
+      if (count * elems > (GLint) param->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];
@@ -1336,6 +1590,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(param->DataType)) {
+            for (i = 0; i < elems; i++) {
+               uniformVal[i] = uniformVal[i] ? 1.0f : 0.0f;
+            }
+         }
       }
    }
 }
@@ -1349,7 +1610,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)");
@@ -1359,6 +1620,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;
@@ -1397,83 +1660,50 @@ _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);
       }
    }
-}
 
-
-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;
-   }
+   shProg->Uniforms->Uniforms[location].Initialized = GL_TRUE;
 }
 
 
+/**
+ * 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;
    }
 
@@ -1514,6 +1744,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) {
@@ -1525,6 +1756,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;
@@ -1537,20 +1770,26 @@ _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);
       }
    }
+
+   shProg->Uniforms->Uniforms[location].Initialized = GL_TRUE;
 }