mesa: refactor GetActiveUniformsiv, use _mesa_program_resource_prop
[mesa.git] / src / mesa / main / uniform_query.cpp
index 77217cbee5c60a928efd42a7195435c4d427a64e..9611667b72df39d17860e7fab1f9b5701afc2f04 100644 (file)
@@ -45,9 +45,14 @@ _mesa_GetActiveUniform(GLuint program, GLuint index,
                        GLenum *type, GLcharARB *nameOut)
 {
    GET_CURRENT_CONTEXT(ctx);
-   struct gl_shader_program *shProg =
-      _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform");
+   struct gl_shader_program *shProg;
+
+   if (maxLength < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform(maxLength < 0)");
+      return;
+   }
 
+   shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform");
    if (!shProg)
       return;
 
@@ -74,6 +79,33 @@ _mesa_GetActiveUniform(GLuint program, GLuint index,
    }
 }
 
+static GLenum
+resource_prop_from_uniform_prop(GLenum uni_prop)
+{
+   switch (uni_prop) {
+   case GL_UNIFORM_TYPE:
+      return GL_TYPE;
+   case GL_UNIFORM_SIZE:
+      return GL_ARRAY_SIZE;
+   case GL_UNIFORM_NAME_LENGTH:
+      return GL_NAME_LENGTH;
+   case GL_UNIFORM_BLOCK_INDEX:
+      return GL_BLOCK_INDEX;
+   case GL_UNIFORM_OFFSET:
+      return GL_OFFSET;
+   case GL_UNIFORM_ARRAY_STRIDE:
+      return GL_ARRAY_STRIDE;
+   case GL_UNIFORM_MATRIX_STRIDE:
+      return GL_MATRIX_STRIDE;
+   case GL_UNIFORM_IS_ROW_MAJOR:
+      return GL_IS_ROW_MAJOR;
+   case GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX:
+      return GL_ATOMIC_COUNTER_BUFFER_INDEX;
+   default:
+      return 0;
+   }
+}
+
 extern "C" void GLAPIENTRY
 _mesa_GetActiveUniformsiv(GLuint program,
                          GLsizei uniformCount,
@@ -83,11 +115,8 @@ _mesa_GetActiveUniformsiv(GLuint program,
 {
    GET_CURRENT_CONTEXT(ctx);
    struct gl_shader_program *shProg;
-   GLsizei i;
-
-   shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform");
-   if (!shProg)
-      return;
+   struct gl_program_resource *res;
+   GLenum res_prop;
 
    if (uniformCount < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
@@ -95,80 +124,25 @@ _mesa_GetActiveUniformsiv(GLuint program,
       return;
    }
 
-   for (i = 0; i < uniformCount; i++) {
-      GLuint index = uniformIndices[i];
-
-      if (index >= shProg->NumUserUniformStorage) {
-        _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniformsiv(index)");
-        return;
-      }
-   }
-
-   for (i = 0; i < uniformCount; i++) {
-      GLuint index = uniformIndices[i];
-      const struct gl_uniform_storage *uni = &shProg->UniformStorage[index];
-
-      switch (pname) {
-      case GL_UNIFORM_TYPE:
-        params[i] = uni->type->gl_type;
-        break;
-
-      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;
-
-      case GL_UNIFORM_NAME_LENGTH:
-        params[i] = strlen(uni->name) + 1;
-
-         /* 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;
-
-      case GL_UNIFORM_BLOCK_INDEX:
-        params[i] = uni->block_index;
-        break;
-
-      case GL_UNIFORM_OFFSET:
-        params[i] = uni->offset;
-        break;
-
-      case GL_UNIFORM_ARRAY_STRIDE:
-        params[i] = uni->array_stride;
-        break;
-
-      case GL_UNIFORM_MATRIX_STRIDE:
-        params[i] = uni->matrix_stride;
-        break;
+   shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform");
+   if (!shProg)
+      return;
 
-      case GL_UNIFORM_IS_ROW_MAJOR:
-        params[i] = uni->row_major;
-        break;
+   res_prop = resource_prop_from_uniform_prop(pname);
 
-      case GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX:
-         if (!ctx->Extensions.ARB_shader_atomic_counters)
-            goto invalid_enum;
-         params[i] = uni->atomic_buffer_index;
+   for (int i = 0; i < uniformCount; i++) {
+      res = _mesa_program_resource_find_index(shProg, GL_UNIFORM,
+                                              uniformIndices[i]);
+      if (!res) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniformsiv(index)");
          break;
-
-      default:
-         goto invalid_enum;
       }
-   }
 
-   return;
-
- invalid_enum:
-   _mesa_error(ctx, GL_INVALID_ENUM, "glGetActiveUniformsiv(pname)");
+      if (!_mesa_program_resource_prop(shProg, res, uniformIndices[i],
+                                       res_prop, &params[i],
+                                       "glGetActiveUniformsiv"))
+         break;
+   }
 }
 
 static struct gl_uniform_storage *
@@ -176,46 +150,13 @@ validate_uniform_parameters(struct gl_context *ctx,
                            struct gl_shader_program *shProg,
                            GLint location, GLsizei count,
                            unsigned *array_index,
-                           const char *caller,
-                           bool negative_one_is_not_valid)
+                           const char *caller)
 {
-   if (!shProg || !shProg->LinkStatus) {
+   if (shProg == NULL) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)", caller);
       return NULL;
    }
 
-   if (location == -1) {
-      /* For glGetUniform, page 264 (page 278 of the PDF) of the OpenGL 2.1
-       * spec says:
-       *
-       *     "The error INVALID_OPERATION is generated if program has not been
-       *     linked successfully, or if location is not a valid location for
-       *     program."
-       *
-       * For glUniform, page 82 (page 96 of the PDF) of the OpenGL 2.1 spec
-       * says:
-       *
-       *     "If the value of location is -1, the Uniform* commands will
-       *     silently ignore the data passed in, and the current uniform
-       *     values will not be changed."
-       *
-       * Allowing -1 for the location parameter of glUniform allows
-       * applications to avoid error paths in the case that, for example, some
-       * uniform variable is removed by the compiler / linker after
-       * optimization.  In this case, the new value of the uniform is dropped
-       * on the floor.  For the case of glGetUniform, there is nothing
-       * sensible to do for a location of -1.
-       *
-       * The negative_one_is_not_valid flag selects between the two behaviors.
-       */
-      if (negative_one_is_not_valid) {
-        _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
-                    caller, location);
-      }
-
-      return NULL;
-   }
-
    /* From page 12 (page 26 of the PDF) of the OpenGL 2.1 spec:
     *
     *     "If a negative number is provided where an argument of type sizei or
@@ -226,10 +167,26 @@ validate_uniform_parameters(struct gl_context *ctx,
       return NULL;
    }
 
-   /* Check that the given location is in bounds of uniform remap table. */
-   if (location >= (GLint) shProg->NumUniformRemapTable) {
-      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
-                  caller, location);
+   /* Check that the given location is in bounds of uniform remap table.
+    * Unlinked programs will have NumUniformRemapTable == 0, so we can take
+    * the shProg->LinkStatus check out of the main path.
+    */
+   if (unlikely(location >= (GLint) shProg->NumUniformRemapTable)) {
+      if (!shProg->LinkStatus)
+         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)",
+                     caller);
+      else
+         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
+                     caller, location);
+
+      return NULL;
+   }
+
+   if (location == -1) {
+      if (!shProg->LinkStatus)
+         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)",
+                     caller);
+
       return NULL;
    }
 
@@ -269,27 +226,30 @@ validate_uniform_parameters(struct gl_context *ctx,
 
    struct gl_uniform_storage *const uni = shProg->UniformRemapTable[location];
 
-   if (uni->array_elements == 0 && count > 1) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                 "%s(count > 1 for non-array, location=%d)",
-                 caller, location);
-      return NULL;
-   }
+   if (uni->array_elements == 0) {
+      if (count > 1) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "%s(count = %u for non-array \"%s\"@%d)",
+                     caller, count, uni->name, location);
+         return NULL;
+      }
 
-   /* The array index specified by the uniform location is just the uniform
-    * location minus the base location of of the uniform.
-    */
-   *array_index = location - uni->remap_location;
+      assert((location - uni->remap_location) == 0);
+      *array_index = 0;
+   } else {
+      /* The array index specified by the uniform location is just the uniform
+       * location minus the base location of of the uniform.
+       */
+      *array_index = location - uni->remap_location;
 
-   /* 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.
-    */
-   const unsigned limit = MAX2(uni->array_elements, 1);
-   if (*array_index >= limit) {
-      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
-                 caller, location);
-      return NULL;
+      /* If the uniform is an array, check that array_index is in bounds.
+       * array_index is unsigned so no need to check for less than zero.
+       */
+      if (*array_index >= uni->array_elements) {
+         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
+                     caller, location);
+         return NULL;
+      }
    }
    return uni;
 }
@@ -308,9 +268,39 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location,
 
    struct gl_uniform_storage *const uni =
       validate_uniform_parameters(ctx, shProg, location, 1,
-                                  &offset, "glGetUniform", true);
-   if (uni == NULL)
+                                  &offset, "glGetUniform");
+   if (uni == NULL) {
+      /* For glGetUniform, page 264 (page 278 of the PDF) of the OpenGL 2.1
+       * spec says:
+       *
+       *     "The error INVALID_OPERATION is generated if program has not been
+       *     linked successfully, or if location is not a valid location for
+       *     program."
+       *
+       * For glUniform, page 82 (page 96 of the PDF) of the OpenGL 2.1 spec
+       * says:
+       *
+       *     "If the value of location is -1, the Uniform* commands will
+       *     silently ignore the data passed in, and the current uniform
+       *     values will not be changed."
+       *
+       * Allowing -1 for the location parameter of glUniform allows
+       * applications to avoid error paths in the case that, for example, some
+       * uniform variable is removed by the compiler / linker after
+       * optimization.  In this case, the new value of the uniform is dropped
+       * on the floor.  For the case of glGetUniform, there is nothing
+       * sensible to do for a location of -1.
+       *
+       * If the location was -1, validate_unfirom_parameters will return NULL
+       * without raising an error.  Raise the error here.
+       */
+      if (location == -1) {
+         _mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniform(location=%d)",
+                     location);
+      }
+
       return;
+   }
 
    {
       unsigned elements = (uni->type->is_sampler())
@@ -342,8 +332,7 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location,
        */
       if (returnType == uni->type->base_type
          || ((returnType == GLSL_TYPE_INT
-              || returnType == GLSL_TYPE_UINT
-              || returnType == GLSL_TYPE_SAMPLER)
+              || returnType == GLSL_TYPE_UINT)
              &&
              (uni->type->base_type == GLSL_TYPE_INT
               || uni->type->base_type == GLSL_TYPE_UINT
@@ -449,6 +438,9 @@ log_uniform(const void *values, enum glsl_base_type basicType,
       case GLSL_TYPE_FLOAT:
         printf("%g ", v[i].f);
         break;
+      case GLSL_TYPE_DOUBLE:
+         printf("%g ", *(double* )&v[i * 2].f);
+         break;
       default:
         assert(!"Should not get here.");
         break;
@@ -509,11 +501,12 @@ _mesa_propagate_uniforms_to_driver_storage(struct gl_uniform_storage *uni,
     */
    const unsigned components = MAX2(1, uni->type->vector_elements);
    const unsigned vectors = MAX2(1, uni->type->matrix_columns);
+   const int dmul = uni->type->base_type == GLSL_TYPE_DOUBLE ? 2 : 1;
 
    /* Store the data in the driver's requested type in the driver's storage
     * areas.
     */
-   unsigned src_vector_byte_stride = components * 4;
+   unsigned src_vector_byte_stride = components * 4 * dmul;
 
    for (i = 0; i < uni->num_driver_storage; i++) {
       struct gl_uniform_driver_storage *const store = &uni->driver_storage[i];
@@ -521,7 +514,7 @@ _mesa_propagate_uniforms_to_driver_storage(struct gl_uniform_storage *uni,
       const unsigned extra_stride =
         store->element_stride - (vectors * store->vector_stride);
       const uint8_t *src =
-        (uint8_t *) (&uni->storage[array_index * (components * vectors)].i);
+        (uint8_t *) (&uni->storage[array_index * (dmul * components * vectors)].i);
 
 #if 0
       printf("%s: %p[%d] components=%u vectors=%u count=%u vector_stride=%u "
@@ -577,6 +570,46 @@ _mesa_propagate_uniforms_to_driver_storage(struct gl_uniform_storage *uni,
    }
 }
 
+
+/**
+ * Return printable string for a given GLSL_TYPE_x
+ */
+static const char *
+glsl_type_name(enum glsl_base_type type)
+{
+   switch (type) {
+   case GLSL_TYPE_UINT:
+      return "uint";
+   case GLSL_TYPE_INT:
+      return "int";
+   case GLSL_TYPE_FLOAT:
+      return "float";
+   case GLSL_TYPE_DOUBLE:
+      return "double";
+   case GLSL_TYPE_BOOL:
+      return "bool";
+   case GLSL_TYPE_SAMPLER:
+      return "sampler";
+   case GLSL_TYPE_IMAGE:
+      return "image";
+   case GLSL_TYPE_ATOMIC_UINT:
+      return "atomic_uint";
+   case GLSL_TYPE_STRUCT:
+      return "struct";
+   case GLSL_TYPE_INTERFACE:
+      return "interface";
+   case GLSL_TYPE_ARRAY:
+      return "array";
+   case GLSL_TYPE_VOID:
+      return "void";
+   case GLSL_TYPE_ERROR:
+      return "error";
+   default:
+      return "other";
+   }
+}
+
+
 /**
  * Called via glUniform*() functions.
  */
@@ -588,26 +621,40 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
               unsigned src_components)
 {
    unsigned offset;
-   unsigned components;
+   int size_mul = basicType == GLSL_TYPE_DOUBLE ? 2 : 1;
 
    struct gl_uniform_storage *const uni =
       validate_uniform_parameters(ctx, shProg, location, count,
-                                  &offset, "glUniform", false);
+                                  &offset, "glUniform");
    if (uni == NULL)
       return;
 
+   if (uni->type->is_matrix()) {
+      /* Can't set matrix uniforms (like mat4) with glUniform */
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glUniform%u(uniform \"%s\"@%d is matrix)",
+                  src_components, uni->name, location);
+      return;
+   }
+
    /* Verify that the types are compatible.
     */
-   if (uni->type->is_sampler()) {
-      components = 1;
-   } else {
-      components = uni->type->vector_elements;
+   const unsigned components = uni->type->is_sampler()
+      ? 1 : uni->type->vector_elements;
+
+   if (components != src_components) {
+      /* glUniformN() must match float/vecN type */
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glUniform%u(\"%s\"@%u has %u components, not %u)",
+                  src_components, uni->name, location,
+                  components, src_components);
+      return;
    }
 
    bool match;
    switch (uni->type->base_type) {
    case GLSL_TYPE_BOOL:
-      match = true;
+      match = (basicType != GLSL_TYPE_DOUBLE);
       break;
    case GLSL_TYPE_SAMPLER:
    case GLSL_TYPE_IMAGE:
@@ -618,12 +665,16 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
       break;
    }
 
-   if (uni->type->is_matrix() || components != src_components || !match) {
-      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)");
+   if (!match) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glUniform%u(\"%s\"@%d is %s, not %s)",
+                  src_components, uni->name, location,
+                  glsl_type_name(uni->type->base_type),
+                  glsl_type_name(basicType));
       return;
    }
 
-   if (ctx->_Shader->Flags & GLSL_UNIFORMS) {
+   if (unlikely(ctx->_Shader->Flags & GLSL_UNIFORMS)) {
       log_uniform(values, basicType, components, 1, count,
                  false, shProg, location, uni);
    }
@@ -646,9 +697,7 @@ _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++) {
+      for (int i = 0; i < count; i++) {
         const unsigned texUnit = ((unsigned *) values)[i];
 
          /* check that the sampler (tex unit index) is legal */
@@ -663,9 +712,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
    }
 
    if (uni->type->is_image()) {
-      int i;
-
-      for (i = 0; i < count; i++) {
+      for (int i = 0; i < count; i++) {
          const int unit = ((GLint *) values)[i];
 
          /* check that the image unit is legal */
@@ -698,16 +745,15 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
    /* Store the data in the "actual type" backing storage for the uniform.
     */
    if (!uni->type->is_boolean()) {
-      memcpy(&uni->storage[components * offset], values,
-            sizeof(uni->storage[0]) * components * count);
+      memcpy(&uni->storage[size_mul * components * offset], values,
+            sizeof(uni->storage[0]) * components * count * size_mul);
    } else {
       const union gl_constant_value *src =
         (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++) {
+      for (unsigned i = 0; i < elems; i++) {
         if (basicType == GLSL_TYPE_FLOAT) {
             dst[i].i = src[i].f != 0.0f ? ctx->Const.UniformBooleanTrue : 0;
         } else {
@@ -724,19 +770,16 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
     * the changes through.
     */
    if (uni->type->is_sampler()) {
-      int i;
-
       bool flushed = false;
-      for (i = 0; i < MESA_SHADER_STAGES; i++) {
+      for (int i = 0; i < MESA_SHADER_STAGES; i++) {
         struct gl_shader *const sh = shProg->_LinkedShaders[i];
-         int j;
 
         /* If the shader stage doesn't use the sampler uniform, skip this.
          */
         if (sh == NULL || !uni->sampler[i].active)
            continue;
 
-         for (j = 0; j < count; j++) {
+         for (int j = 0; j < count; j++) {
             sh->SamplerUnits[uni->sampler[i].index + offset + j] =
                ((unsigned *) values)[j];
          }
@@ -749,7 +792,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
          * been modified.
          */
         bool changed = false;
-        for (unsigned j = 0; j < Elements(prog->SamplerUnits); j++) {
+        for (unsigned j = 0; j < ARRAY_SIZE(prog->SamplerUnits); j++) {
            if ((sh->active_samplers & (1U << j)) != 0
                && (prog->SamplerUnits[j] != sh->SamplerUnits[j])) {
               changed = true;
@@ -778,13 +821,11 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
     * uniforms to image units present in the shader data structure.
     */
    if (uni->type->is_image()) {
-      int i, j;
-
-      for (i = 0; i < MESA_SHADER_STAGES; i++) {
+      for (int i = 0; i < MESA_SHADER_STAGES; i++) {
         if (uni->image[i].active) {
             struct gl_shader *sh = shProg->_LinkedShaders[i];
 
-            for (j = 0; j < count; j++)
+            for (int j = 0; j < count; j++)
                sh->ImageUnits[uni->image[i].index + offset + j] =
                   ((GLint *) values)[j];
          }
@@ -802,16 +843,17 @@ extern "C" void
 _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
                     GLuint cols, GLuint rows,
                      GLint location, GLsizei count,
-                     GLboolean transpose, const GLfloat *values)
+                     GLboolean transpose,
+                     const GLvoid *values, GLenum type)
 {
    unsigned offset;
    unsigned vectors;
    unsigned components;
    unsigned elements;
-
+   int size_mul;
    struct gl_uniform_storage *const uni =
       validate_uniform_parameters(ctx, shProg, location, count,
-                                  &offset, "glUniformMatrix", false);
+                                  &offset, "glUniformMatrix");
    if (uni == NULL)
       return;
 
@@ -821,6 +863,9 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       return;
    }
 
+   assert(type == GL_FLOAT || type == GL_DOUBLE);
+   size_mul = type == GL_DOUBLE ? 2 : 1;
+
    assert(!uni->type->is_sampler());
    vectors = uni->type->matrix_columns;
    components = uni->type->vector_elements;
@@ -835,18 +880,18 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
    }
 
    /* 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) {
+    * http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml
+    */
+   if (transpose) {
+      if (ctx->API == API_OPENGLES2 && ctx->Version < 30) {
         _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,
+   if (unlikely(ctx->_Shader->Flags & GLSL_UNIFORMS)) {
+      log_uniform(values, uni->type->base_type, components, vectors, count,
                  bool(transpose), shProg, location, uni);
    }
 
@@ -873,13 +918,28 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
 
    if (!transpose) {
       memcpy(&uni->storage[elements * offset], values,
-            sizeof(uni->storage[0]) * elements * count);
-   } else {
+            sizeof(uni->storage[0]) * elements * count * size_mul);
+   } else if (type == GL_FLOAT) {
       /* Copy and transpose the matrix.
        */
-      const float *src = values;
+      const float *src = (const float *)values;
       float *dst = &uni->storage[elements * offset].f;
 
+      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)];
+           }
+        }
+
+        dst += elements;
+        src += elements;
+      }
+   } else {
+      assert(type == GL_DOUBLE);
+      const double *src = (const double *)values;
+      double *dst = (double *)&uni->storage[elements * offset].f;
+
       for (int i = 0; i < count; i++) {
         for (unsigned r = 0; r < rows; r++) {
            for (unsigned c = 0; c < cols; c++) {