diff --git a/src/imports/qtcanvas3d/context3d.cpp b/src/imports/qtcanvas3d/context3d.cpp
index adc18595dd8c6d85bed94d32ec9c75ad255e64d1..cc95dba4cf012453c3f141d51e53a444e76b8970 100644
--- a/src/imports/qtcanvas3d/context3d.cpp
+++ b/src/imports/qtcanvas3d/context3d.cpp
@@ -574,6 +574,153 @@ bool CanvasContext::checkParent(QObject *obj, const char *function)
     return true;
 }
 
+/*!
+ * \internal
+ *
+ * Transposes matrices. \a dim is the dimensions of the square matrices in \a src.
+ * A newly allocated array containing transposed matrices is returned. \a count specifies how many
+ * matrices are handled.
+ * Required for uniformMatrix*fv functions in ES2.
+ */
+float *CanvasContext::transposeMatrix(int dim, int count, float *src)
+{
+    float *dest = new float[dim * dim * count];
+
+    for (int k = 0; k < count; k++) {
+        const int offset = k * dim * dim;
+        for (int i = 0; i < dim; i++) {
+            for (int j = 0; j < dim; j++)
+                dest[offset + (i * dim) + j] = src[offset + (j * dim) + i];
+        }
+    }
+
+    return dest;
+}
+
+/*!
+ * \internal
+ *
+ * Set matrix uniform values.
+ */
+void CanvasContext::uniformMatrixNfv(int dim, const QJSValue &location3D, bool transpose,
+                                     const QJSValue &array)
+{
+    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
+                                         << "(dim:" << dim
+                                         << ", uniformLocation:" << location3D.toString()
+                                         << ", transpose:" << transpose
+                                         << ", array:" << array.toString()
+                                         <<")";
+
+    if (!isOfType(location3D, "QtCanvas3D::CanvasUniformLocation"))
+        return;
+
+    CanvasUniformLocation *locationObj =
+            static_cast<CanvasUniformLocation *>(location3D.toQObject());
+
+    if (!checkParent(locationObj, __FUNCTION__))
+        return;
+
+    // Check if we have a JavaScript array
+    if (array.isArray()) {
+        uniformMatrixNfva(dim, locationObj, transpose, array.toVariant().toList());
+        return;
+    }
+
+    int arrayLen = 0;
+    float *uniformData = reinterpret_cast<float * >(
+                getTypedArrayAsRawDataPtr(array, arrayLen, QV4::Heap::TypedArray::Float32Array));
+
+    if (!m_currentProgram || !uniformData || !locationObj)
+        return;
+
+    int uniformLocation = locationObj->id();
+    int numMatrices = arrayLen / (dim * dim * 4);
+
+    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
+                                         << "numMatrices:" << numMatrices;
+
+    float *transposedMatrix = 0;
+    if (m_isOpenGLES2 && transpose) {
+        transpose = false;
+        transposedMatrix = transposeMatrix(dim, numMatrices, uniformData);
+        uniformData = transposedMatrix;
+    }
+
+    switch (dim) {
+    case 2:
+        glUniformMatrix2fv(uniformLocation, numMatrices, transpose, uniformData);
+        break;
+    case 3:
+        glUniformMatrix3fv(uniformLocation, numMatrices, transpose, uniformData);
+        break;
+    case 4:
+        glUniformMatrix4fv(uniformLocation, numMatrices, transpose, uniformData);
+        break;
+    default:
+        qWarning() << "Warning: Unsupported dim specified in" << __FUNCTION__;
+        break;
+    }
+
+    logAllGLErrors(__FUNCTION__);
+
+    delete[] transposedMatrix;
+}
+
+/*!
+ * \internal
+ *
+ * Set matrix uniform values from JS array.
+ */
+void CanvasContext::uniformMatrixNfva(int dim, CanvasUniformLocation *uniformLocation,
+                                     bool transpose, const QVariantList &array)
+{
+    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
+                                         << "(dim:" << dim
+                                         << ", location3D:" << uniformLocation
+                                         << ", transpose:" << transpose
+                                         << ", array:" << array
+                                         << ")";
+
+    if (!m_currentProgram || !uniformLocation)
+        return;
+
+    int location3D = uniformLocation->id();
+    int size = array.count();
+    float *dataArray = new float[size];
+    float *arrayPtr = dataArray;
+    int numMatrices = size / (dim * dim);
+
+    ArrayUtils::fillFloatArrayFromVariantList(array, arrayPtr);
+
+    float *transposedMatrix = 0;
+    if (m_isOpenGLES2 && transpose) {
+        transpose = false;
+        transposedMatrix = transposeMatrix(dim, numMatrices, arrayPtr);
+        arrayPtr = transposedMatrix;
+    }
+
+    switch (dim) {
+    case 2:
+        glUniformMatrix2fv(location3D, numMatrices, transpose, arrayPtr);
+        break;
+    case 3:
+        glUniformMatrix3fv(location3D, numMatrices, transpose, arrayPtr);
+        break;
+    case 4:
+        glUniformMatrix4fv(location3D, numMatrices, transpose, arrayPtr);
+        break;
+    default:
+        qWarning() << "Warning: Unsupported dim specified in" << __FUNCTION__;
+        break;
+    }
+
+    logAllGLErrors(__FUNCTION__);
+
+    delete[] dataArray;
+    delete[] transposedMatrix;
+}
+
 /*!
  * \qmlmethod void Context3D::generateMipmap(glEnums target)
  * Generates a complete set of mipmaps for a texture object of the currently active texture unit.
@@ -3801,42 +3948,7 @@ void CanvasContext::disableVertexAttribArray(int index)
  */
 void CanvasContext::uniformMatrix2fv(QJSValue location3D, bool transpose, QJSValue array)
 {
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "(uniformLocation:" << location3D.toString()
-                                         << ", transpose:" << transpose
-                                         << ", array:" << array.toString()
-                                         <<")";
-
-    if (!isOfType(location3D, "QtCanvas3D::CanvasUniformLocation"))
-        return;
-
-    CanvasUniformLocation *locationObj =
-            static_cast<CanvasUniformLocation *>(location3D.toQObject());
-
-    if (!checkParent(locationObj, __FUNCTION__))
-        return;
-
-    // Check if we have a JavaScript array
-    if (array.isArray()) {
-        uniformMatrix2fva(locationObj, transpose, array.toVariant().toList());
-        return;
-    }
-
-    int arrayLen = 0;
-    uchar *uniformData = getTypedArrayAsRawDataPtr(array, arrayLen,
-                                                   QV4::Heap::TypedArray::Float32Array);
-
-    if (!m_currentProgram || !uniformData || !locationObj)
-        return;
-
-    int uniformLocation = locationObj->id();
-    int numMatrices = arrayLen / (4 * 4);
-
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "numMatrices:" << numMatrices;
-
-    glUniformMatrix2fv(uniformLocation, numMatrices, transpose, (float *)uniformData);
-    logAllGLErrors(__FUNCTION__);
+    uniformMatrixNfv(2, location3D, transpose, array);
 }
 
 /*!
@@ -3849,41 +3961,7 @@ void CanvasContext::uniformMatrix2fv(QJSValue location3D, bool transpose, QJSVal
  */
 void CanvasContext::uniformMatrix3fv(QJSValue location3D, bool transpose, QJSValue array)
 {
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "(location3D:" << location3D.toString()
-                                         << ", transpose:" << transpose
-                                         << ", array:" << array.toString()
-                                         <<")";
-
-    if (!isOfType(location3D, "QtCanvas3D::CanvasUniformLocation"))
-        return;
-    CanvasUniformLocation *locationObj =
-            static_cast<CanvasUniformLocation *>(location3D.toQObject());
-
-    if (!checkParent(locationObj, __FUNCTION__))
-        return;
-
-    // Check if we have a JavaScript array
-    if (array.isArray()) {
-        uniformMatrix3fva(locationObj, transpose, array.toVariant().toList());
-        return;
-    }
-
-    int arrayLen = 0;
-    uchar *uniformData = getTypedArrayAsRawDataPtr(array, arrayLen,
-                                                   QV4::Heap::TypedArray::Float32Array);
-
-    if (!m_currentProgram || !uniformData || !locationObj)
-        return;
-
-    int uniformLocation = locationObj->id();
-    int numMatrices = arrayLen / (4 * 9);
-
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "numMatrices:" << numMatrices;
-
-    glUniformMatrix3fv(uniformLocation, numMatrices, transpose, (float *)uniformData);
-    logAllGLErrors(__FUNCTION__);
+    uniformMatrixNfv(3, location3D, transpose, array);
 }
 
 /*!
@@ -3896,124 +3974,7 @@ void CanvasContext::uniformMatrix3fv(QJSValue location3D, bool transpose, QJSVal
  */
 void CanvasContext::uniformMatrix4fv(QJSValue location3D, bool transpose, QJSValue array)
 {
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "(location3D:" << location3D.toString()
-                                         << ", transpose:" << transpose
-                                         << ", array:" << array.toString()
-                                         << ")";
-
-    if (!isOfType(location3D, "QtCanvas3D::CanvasUniformLocation"))
-        return;
-    CanvasUniformLocation *locationObj =
-            static_cast<CanvasUniformLocation *>(location3D.toQObject());
-
-    if (!checkParent(locationObj, __FUNCTION__))
-        return;
-
-    // Check if we have a JavaScript array
-    if (array.isArray()) {
-        uniformMatrix4fva(locationObj, transpose,array.toVariant().toList());
-        return;
-    }
-
-    int arrayLen = 0;
-    uchar *uniformData = getTypedArrayAsRawDataPtr(array, arrayLen,
-                                                   QV4::Heap::TypedArray::Float32Array);
-
-    if (!m_currentProgram || !uniformData || !locationObj)
-        return;
-
-    int uniformLocation = locationObj->id();
-    int numMatrices = arrayLen / (4 * 16);
-
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "numMatrices:" << numMatrices;
-
-    glUniformMatrix4fv(uniformLocation, numMatrices, transpose, (float *)uniformData);
-    logAllGLErrors(__FUNCTION__);
-}
-
-/*!
- * \internal
- */
-void CanvasContext::uniformMatrix4fva(CanvasUniformLocation *uniformLocation, bool transpose,
-                                      QVariantList array)
-{
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "(location3D:" << uniformLocation
-                                         << ", transpose:" << transpose
-                                         << ", array:" << array
-                                         << ")";
-    if (!m_currentProgram || !uniformLocation)
-        return;
-
-    int location3D = uniformLocation->id();
-    int size = array.count();
-    float *arrayData = new float[size];
-    int numMatrices = size / 16;
-
-    ArrayUtils::fillFloatArrayFromVariantList(array, arrayData);
-
-    glUniformMatrix4fv(location3D, numMatrices, transpose, arrayData);
-    logAllGLErrors(__FUNCTION__);
-
-    delete [] arrayData;
-}
-
-
-/*!
- * \internal
- */
-void CanvasContext::uniformMatrix3fva(CanvasUniformLocation *uniformLocation, bool transpose,
-                                      QVariantList array)
-{
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "(location3D:" << uniformLocation
-                                         << ", transpose:" << transpose
-                                         << ", array:" << array
-                                         << ")";
-    if (!m_currentProgram || !uniformLocation)
-        return;
-
-    int location3D = uniformLocation->id();
-    int size = array.count();
-    float *arrayData = new float[size];
-    int numMatrices = size / 9;
-
-    ArrayUtils::fillFloatArrayFromVariantList(array, arrayData);
-
-    glUniformMatrix3fv(location3D, numMatrices, transpose, arrayData);
-    logAllGLErrors(__FUNCTION__);
-
-    delete [] arrayData;
-}
-
-/*!
- * \internal
- */
-void CanvasContext::uniformMatrix2fva(CanvasUniformLocation *uniformLocation, bool transpose,
-                                      QVariantList array)
-{
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "(location3D:" << uniformLocation
-                                         << ", transpose:" << transpose
-                                         << ", array:" << array
-                                         << ")";
-
-    if (!m_currentProgram || !uniformLocation)
-        return;
-
-    int location3D = uniformLocation->id();
-    int size = array.count();
-    float *arrayData = new float[size];
-    int numMatrices = size / 4;
-
-    ArrayUtils::fillFloatArrayFromVariantList(array, arrayData);
-
-    glUniformMatrix2fv(location3D, numMatrices, transpose, arrayData);
-    logAllGLErrors(__FUNCTION__);
-
-    delete [] arrayData;
+    uniformMatrixNfv(4, location3D, transpose, array);
 }
 
 /*!
@@ -6015,123 +5976,171 @@ QJSValue CanvasContext::getUniform(QJSValue program3D, QJSValue location3D)
 
     uint programId = program->id();
     uint locationId = location->id();
-    CanvasActiveInfo *info = getActiveUniform(program3D, locationId);
+    int type = location->type();
+
+    if (type < 0) {
+        // Resolve location type.
+        // There is no easy way to determine this, as the active uniform
+        // indices do not usually match the uniform locations. We must query
+        // active uniforms until we hit the one we want. This is obviously
+        // extremely inefficient, but luckily getUniform is not something most
+        // users typically need or use.
+
+        const int maxCharCount = 512;
+        GLsizei length;
+        GLint size;
+        GLenum glType;
+        char nameBuf[maxCharCount];
+        GLint uniformCount = 0;
+        glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &uniformCount);
+        // Strip any [] from the uniform name, unless part of struct
+        QByteArray strippedName = location->name().toLatin1();
+        int idx = strippedName.indexOf('[');
+        if (idx >= 0) {
+            // Don't truncate if part of struct
+            if (strippedName.indexOf('.') == -1)
+                strippedName.truncate(idx);
+        }
+        for (int i = 0; i < uniformCount; i++) {
+            nameBuf[0] = '\0';
+            glGetActiveUniform(programId, i, maxCharCount, &length, &size, &glType, nameBuf);
+            QByteArray activeName(nameBuf, length);
+            idx = activeName.indexOf('[');
+            if (idx >= 0) {
+                // Don't truncate if part of struct
+                if (activeName.indexOf('.') == -1)
+                    activeName.truncate(idx);
+            }
 
-    int numValues = 4;
-    switch (info->type()) {
-    case SAMPLER_2D:
-        // Intentional flow through
-    case SAMPLER_CUBE:
-        // Intentional flow through
-    case INT: {
-        GLint value = 0;
-        glGetUniformiv(programId, locationId, &value);
-        logAllGLErrors(__FUNCTION__);
-        return QJSValue(value);
-    }
-    case FLOAT: {
-        GLfloat value = 0;
-        glGetUniformfv(programId, locationId, &value);
-        logAllGLErrors(__FUNCTION__);
-        return QJSValue(value);
-    }
-    case BOOL: {
-        GLint value = 0;
-        glGetUniformiv(programId, locationId, &value);
-        logAllGLErrors(__FUNCTION__);
-        return QJSValue(bool(value));
+            if (activeName == strippedName) {
+                type = glType;
+                location->setType(type);
+                break;
+            }
+        }
     }
-    case INT_VEC2:
-        numValues--;
-        // Intentional flow through
-    case INT_VEC3:
-        numValues--;
-        // Intentional flow through
-    case INT_VEC4: {
-        QV4::Scope scope(m_v4engine);
-        QV4::Scoped<QV4::ArrayBuffer> buffer(scope,
-                                             m_v4engine->memoryManager->alloc<QV4::ArrayBuffer>(
-                                                 m_v4engine,
-                                                 sizeof(int) * numValues));
-        glGetUniformiv(programId, locationId, (int *) buffer->data());
-        logAllGLErrors(__FUNCTION__);
 
-        QV4::ScopedFunctionObject constructor(scope,
-                                              m_v4engine->typedArrayCtors[
-                                              QV4::Heap::TypedArray::Int32Array]);
-        QV4::ScopedCallData callData(scope, 1);
-        callData->args[0] = buffer;
-        return QJSValue(m_v4engine, constructor->construct(callData));
-    }
-    case FLOAT_VEC2:
-        numValues--;
-        // Intentional flow through
-    case FLOAT_VEC3:
-        numValues--;
-        // Intentional flow through
-    case FLOAT_VEC4: {
-        QV4::Scope scope(m_v4engine);
-        QV4::Scoped<QV4::ArrayBuffer> buffer(scope,
-                                             m_v4engine->memoryManager->alloc<QV4::ArrayBuffer>(
-                                                 m_v4engine,
-                                                 sizeof(float) * numValues));
-        glGetUniformfv(programId, locationId, (float *) buffer->data());
-        logAllGLErrors(__FUNCTION__);
+    if (type < 0) {
+        qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
+                                               << ":INVALID_OPERATION:Uniform type could not be determined";
+        m_error |= CANVAS_INVALID_OPERATION;
+        return QJSValue(QJSValue::UndefinedValue);
+    } else {
+        int numValues = 4;
+        switch (type) {
+        case SAMPLER_2D:
+            // Intentional flow through
+        case SAMPLER_CUBE:
+            // Intentional flow through
+        case INT: {
+            GLint value = 0;
+            glGetUniformiv(programId, locationId, &value);
+            logAllGLErrors(__FUNCTION__);
+            return QJSValue(value);
+        }
+        case FLOAT: {
+            GLfloat value = 0;
+            glGetUniformfv(programId, locationId, &value);
+            logAllGLErrors(__FUNCTION__);
+            return QJSValue(value);
+        }
+        case BOOL: {
+            GLint value = 0;
+            glGetUniformiv(programId, locationId, &value);
+            logAllGLErrors(__FUNCTION__);
+            return QJSValue(bool(value));
+        }
+        case INT_VEC2:
+            numValues--;
+            // Intentional flow through
+        case INT_VEC3:
+            numValues--;
+            // Intentional flow through
+        case INT_VEC4: {
+            QV4::Scope scope(m_v4engine);
+            QV4::Scoped<QV4::ArrayBuffer> buffer(scope,
+                                                 m_v4engine->memoryManager->alloc<QV4::ArrayBuffer>(
+                                                     m_v4engine,
+                                                     sizeof(int) * numValues));
+            glGetUniformiv(programId, locationId, (int *) buffer->data());
+            logAllGLErrors(__FUNCTION__);
 
-        QV4::ScopedFunctionObject constructor(scope,
-                                              m_v4engine->typedArrayCtors[
-                                              QV4::Heap::TypedArray::Float32Array]);
-        QV4::ScopedCallData callData(scope, 1);
-        callData->args[0] = buffer;
-        return QJSValue(m_v4engine, constructor->construct(callData));
-    }
-    case BOOL_VEC2:
-        numValues--;
-        // Intentional flow through
-    case BOOL_VEC3:
-        numValues--;
-        // Intentional flow through
-    case BOOL_VEC4: {
-        GLint *value = new GLint[numValues];
-        QJSValue array = m_engine->newArray(numValues);
+            QV4::ScopedFunctionObject constructor(scope,
+                                                  m_v4engine->typedArrayCtors[
+                                                  QV4::Heap::TypedArray::Int32Array]);
+            QV4::ScopedCallData callData(scope, 1);
+            callData->args[0] = buffer;
+            return QJSValue(m_v4engine, constructor->construct(callData));
+        }
+        case FLOAT_VEC2:
+            numValues--;
+            // Intentional flow through
+        case FLOAT_VEC3:
+            numValues--;
+            // Intentional flow through
+        case FLOAT_VEC4: {
+            QV4::Scope scope(m_v4engine);
+            QV4::Scoped<QV4::ArrayBuffer> buffer(scope,
+                                                 m_v4engine->memoryManager->alloc<QV4::ArrayBuffer>(
+                                                     m_v4engine,
+                                                     sizeof(float) * numValues));
+            glGetUniformfv(programId, locationId, (float *) buffer->data());
+            logAllGLErrors(__FUNCTION__);
 
-        glGetUniformiv(programId, locationId, value);
-        logAllGLErrors(__FUNCTION__);
+            QV4::ScopedFunctionObject constructor(scope,
+                                                  m_v4engine->typedArrayCtors[
+                                                  QV4::Heap::TypedArray::Float32Array]);
+            QV4::ScopedCallData callData(scope, 1);
+            callData->args[0] = buffer;
+            return QJSValue(m_v4engine, constructor->construct(callData));
+        }
+        case BOOL_VEC2:
+            numValues--;
+            // Intentional flow through
+        case BOOL_VEC3:
+            numValues--;
+            // Intentional flow through
+        case BOOL_VEC4: {
+            GLint *value = new GLint[numValues];
+            QJSValue array = m_engine->newArray(numValues);
+
+            glGetUniformiv(programId, locationId, value);
+            logAllGLErrors(__FUNCTION__);
 
-        for (int i = 0; i < numValues; i++)
-            array.setProperty(i, bool(value[i]));
+            for (int i = 0; i < numValues; i++)
+                array.setProperty(i, bool(value[i]));
 
-        return array;
-    }
-    case FLOAT_MAT2:
-        numValues--;
-        // Intentional flow through
-    case FLOAT_MAT3:
-        numValues--;
-        // Intentional flow through
-    case FLOAT_MAT4: {
-        numValues = numValues * numValues;
+            return array;
+        }
+        case FLOAT_MAT2:
+            numValues--;
+            // Intentional flow through
+        case FLOAT_MAT3:
+            numValues--;
+            // Intentional flow through
+        case FLOAT_MAT4: {
+            numValues = numValues * numValues;
 
 
-        QV4::Scope scope(m_v4engine);
-        QV4::Scoped<QV4::ArrayBuffer> buffer(scope,
-                                             m_v4engine->memoryManager->alloc<QV4::ArrayBuffer>(
-                                                 m_v4engine,
-                                                 sizeof(float) * numValues));
-        glGetUniformfv(programId, locationId, (float *) buffer->data());
-        logAllGLErrors(__FUNCTION__);
+            QV4::Scope scope(m_v4engine);
+            QV4::Scoped<QV4::ArrayBuffer> buffer(scope,
+                                                 m_v4engine->memoryManager->alloc<QV4::ArrayBuffer>(
+                                                     m_v4engine,
+                                                     sizeof(float) * numValues));
+            glGetUniformfv(programId, locationId, (float *) buffer->data());
+            logAllGLErrors(__FUNCTION__);
 
-        QV4::ScopedFunctionObject constructor(scope,
-                                              m_v4engine->typedArrayCtors[
-                                              QV4::Heap::TypedArray::Float32Array]);
-        QV4::ScopedCallData callData(scope, 1);
-        callData->args[0] = buffer;
-        return QJSValue(m_v4engine, constructor->construct(callData));
-    }
-    default:
-        break;
+            QV4::ScopedFunctionObject constructor(scope,
+                                                  m_v4engine->typedArrayCtors[
+                                                  QV4::Heap::TypedArray::Float32Array]);
+            QV4::ScopedCallData callData(scope, 1);
+            callData->args[0] = buffer;
+            return QJSValue(m_v4engine, constructor->construct(callData));
+        }
+        default:
+            break;
+        }
     }
-
     return QJSValue(QJSValue::UndefinedValue);
 }
 
diff --git a/src/imports/qtcanvas3d/context3d_p.h b/src/imports/qtcanvas3d/context3d_p.h
index 6faddaec29f62d0f52ab73d5ffb949ede8198215..280bf9809af9ece63b1d4d125b2d483b121106d7 100644
--- a/src/imports/qtcanvas3d/context3d_p.h
+++ b/src/imports/qtcanvas3d/context3d_p.h
@@ -1210,10 +1210,6 @@ private:
     void uniform3iva(CanvasUniformLocation *location, QVariantList array);
     void uniform4iva(CanvasUniformLocation *location, QVariantList array);
 
-    void uniformMatrix2fva(CanvasUniformLocation *location, bool transpose, QVariantList value);
-    void uniformMatrix3fva(CanvasUniformLocation *location, bool transpose, QVariantList value);
-    void uniformMatrix4fva(CanvasUniformLocation *location, bool transpose, QVariantList value);
-
     void vertexAttrib1fva(uint indx, QVariantList values);
     void vertexAttrib2fva(uint indx, QVariantList values);
     void vertexAttrib3fva(uint indx, QVariantList values);
@@ -1236,6 +1232,12 @@ private:
     bool isValidTextureBound(glEnums target, const QString &funcName);
     bool checkParent(QObject *jsObj, const char *function);
 
+    float *transposeMatrix(int dim, int count, float *src);
+    void uniformMatrixNfv(int dim, const QJSValue &location3D, bool transpose,
+                          const QJSValue &array);
+    void uniformMatrixNfva(int dim, CanvasUniformLocation *uniformLocation, bool transpose,
+                           const QVariantList &array);
+
     typedef enum {
         CANVAS_NO_ERRORS = 0,
         CANVAS_INVALID_ENUM = 1 << 0,
diff --git a/src/imports/qtcanvas3d/uniformlocation.cpp b/src/imports/qtcanvas3d/uniformlocation.cpp
index f1f50c8ce18b6dcc5a62e3ea894eff0801885542..31c228f214d80bf4b802a96436a76fca6020812d 100644
--- a/src/imports/qtcanvas3d/uniformlocation.cpp
+++ b/src/imports/qtcanvas3d/uniformlocation.cpp
@@ -54,7 +54,8 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  */
 CanvasUniformLocation::CanvasUniformLocation(int location, QObject *parent) :
     CanvasAbstractObject(parent),
-    m_location(location)
+    m_location(location),
+    m_type(-1)
 {
 }
 
@@ -73,6 +74,22 @@ int CanvasUniformLocation::id()
     return m_location;
 }
 
+/*!
+ * \internal
+ */
+int CanvasUniformLocation::type()
+{
+    return m_type;
+}
+
+/*!
+ * \internal
+ */
+void CanvasUniformLocation::setType(int type)
+{
+    m_type = type;
+}
+
 /*!
  * \internal
  */
diff --git a/src/imports/qtcanvas3d/uniformlocation_p.h b/src/imports/qtcanvas3d/uniformlocation_p.h
index fb1548ba50f5f5027c94ea2ab6cb41e60b77f5d3..cb99ca1e736a220192023c468fdb06d5a9720aea 100644
--- a/src/imports/qtcanvas3d/uniformlocation_p.h
+++ b/src/imports/qtcanvas3d/uniformlocation_p.h
@@ -63,11 +63,14 @@ public:
     virtual ~CanvasUniformLocation();
 
     int id();
+    int type();
+    void setType(int type);
 
     friend QDebug operator<< (QDebug d, const CanvasUniformLocation *uLoc);
 
 private:
     int m_location;
+    int m_type;
 };
 
 QT_CANVAS3D_END_NAMESPACE
diff --git a/tests/auto/qmltest/canvas3d/tst_uniforms.js b/tests/auto/qmltest/canvas3d/tst_uniforms.js
new file mode 100644
index 0000000000000000000000000000000000000000..d6227bccd47b0f9de5f0c59f02b0c96317010976
--- /dev/null
+++ b/tests/auto/qmltest/canvas3d/tst_uniforms.js
@@ -0,0 +1,922 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+var gl;
+var positionLocation;
+var inactiveUniformLocation;
+var testUniformLocation;
+var shaderProgram;
+var buffer;
+var vertexShader;
+var fragmentShader;
+
+var uniform1i;
+var uniform2i;
+var uniform3i;
+var uniform4i;
+var uniform1f;
+var uniform2f;
+var uniform3f;
+var uniform4f;
+var uniform1b;
+var uniform2b;
+var uniform3b;
+var uniform4b;
+var uniform1iv;
+var uniform2iv;
+var uniform3iv;
+var uniform4iv;
+var uniform1fv;
+var uniform2fv;
+var uniform3fv;
+var uniform4fv;
+var uniform1bv;
+var uniform2bv;
+var uniform3bv;
+var uniform4bv;
+var uniformMatrix2fv;
+var uniformMatrix3fv;
+var uniformMatrix4fv;
+var uniformSampler;
+
+var fillValue;
+var testArray;
+
+function constructTestArrayJS(count) {
+    testArray = [];
+    for (var i = 0; i < count; i++) {
+        testArray[i] = fillValue++;
+    }
+    return testArray;
+}
+
+function constructTestArrayBooleanJS(count) {
+    testArray = [];
+    for (var i = 0; i < count; i++) {
+        testArray[i] = Boolean(fillValue++ % 2);
+    }
+    return testArray;
+}
+
+function constructTestArrayInt(count) {
+    testArray = new Int32Array(count);
+    for (var i = 0; i < count; i++) {
+        testArray[i] = fillValue++;
+    }
+    return testArray;
+}
+
+function constructTestArrayFloat(count) {
+    testArray = new Float32Array(count);
+    for (var i = 0; i < count; i++) {
+        testArray[i] = fillValue++;
+    }
+    return testArray;
+}
+
+function compareUniformValues(a, b) {
+    if (a !== b) {
+        console.log("initializeGL(): FAILURE: returned uniform doesn't have expected contents.",
+                    "Expected:", a, "Actual:", b);
+        return false;
+    }
+
+    return true;
+}
+
+function compareUniformArrays(a, b) {
+    if (a.length !== b.length) {
+        console.log("initializeGL(): FAILURE: returned uniform array not the expected length. Expected:",
+                    a.length, "Actual:", b.length);
+        return false;
+    }
+
+    for (var i = 0; i < a.length; i++) {
+        if (a[i] !== b[i]) {
+            console.log("initializeGL(): FAILURE: returned uniform array doesn't have expected contents.",
+                        "Expected:", a[i], "Actual:", b[i], "Index:", i);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+function compareUniformSubArrays(index, len, a, b) {
+    var newArray = [];
+
+    if (len !== b.length) {
+        console.log("initializeGL(): FAILURE: returned uniform arrays not the expected length. Expected:",
+                    a.length - index, "Actual:", b.length);
+        return false;
+    }
+
+    for (var i = 0; i < len; i++) {
+        newArray[i] = a[i + index];
+    }
+
+    return compareUniformArrays(newArray, b);
+}
+function compareUniformSubArraysTransposed(index, len, a, b) {
+    var newArray = [];
+
+    if (len !== b.length) {
+        console.log("initializeGL(): FAILURE: returned uniform arrays not the expected length. Expected:",
+                    a.length - index, "Actual:", b.length);
+        return false;
+    }
+
+    var dim = Math.sqrt(len);
+
+    // Transpose the original array
+    var newIndex = 0;
+    for (var i = 0; i < dim; i++) {
+        for (var j = 0; j < dim; j++) {
+            newArray[newIndex++] = a[index + (j * dim) + i];
+        }
+    }
+
+    return compareUniformArrays(newArray, b);
+}
+
+function debugPrintArray(title, a) {
+    var len = a.length;
+    console.log(title);
+    for (var i = 0; i < len; i++) {
+        console.log(i, ":", a[i]);
+    }
+}
+
+function getUniformValue(uniformName) {
+    var uniformLocation = gl.getUniformLocation(shaderProgram, uniformName);
+    var uniformValue = gl.getUniform(shaderProgram, uniformLocation);
+
+    return uniformValue;
+}
+
+function initializeGL(canvas) {
+    var initStatus = 0
+    try {
+        gl = canvas.getContext("3d");
+        buffer = gl.createBuffer();
+        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+        gl.bufferData(
+                    gl.ARRAY_BUFFER, new Float32Array(
+                        [-1.0, -1.0,
+                         1.0, -1.0,
+                         -1.0,  1.0
+                        ]),
+                    gl.STATIC_DRAW);
+
+        if (!initShaders()) {
+            initStatus = 1;
+        } else {
+            gl.useProgram(shaderProgram);
+
+            positionLocation = gl.getAttribLocation(shaderProgram, "a_position");
+            gl.enableVertexAttribArray(positionLocation);
+            gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
+
+            inactiveUniformLocation = gl.getUniformLocation(shaderProgram, "inactiveUniform");
+            testUniformLocation = gl.getUniformLocation(shaderProgram, "testUniform");
+
+            uniform1i = gl.getUniformLocation(shaderProgram, "i1");
+            uniform2i = gl.getUniformLocation(shaderProgram, "i2");
+            uniform3i = gl.getUniformLocation(shaderProgram, "i3");
+            uniform4i = gl.getUniformLocation(shaderProgram, "i4");
+            uniform1f = gl.getUniformLocation(shaderProgram, "f1");
+            uniform2f = gl.getUniformLocation(shaderProgram, "f2");
+            uniform3f = gl.getUniformLocation(shaderProgram, "f3");
+            uniform4f = gl.getUniformLocation(shaderProgram, "f4");
+            uniform1b = gl.getUniformLocation(shaderProgram, "b1");
+            uniform2b = gl.getUniformLocation(shaderProgram, "b2");
+            uniform3b = gl.getUniformLocation(shaderProgram, "b3");
+            uniform4b = gl.getUniformLocation(shaderProgram, "b4");
+            uniform1iv = gl.getUniformLocation(shaderProgram, "i1v");
+            uniform2iv = gl.getUniformLocation(shaderProgram, "i2v");
+            uniform3iv = gl.getUniformLocation(shaderProgram, "i3v");
+            uniform4iv = gl.getUniformLocation(shaderProgram, "i4v");
+            uniform1fv = gl.getUniformLocation(shaderProgram, "f1v");
+            uniform2fv = gl.getUniformLocation(shaderProgram, "f2v");
+            uniform3fv = gl.getUniformLocation(shaderProgram, "f3v");
+            uniform4fv = gl.getUniformLocation(shaderProgram, "f4v");
+            uniform1bv = gl.getUniformLocation(shaderProgram, "b1v");
+            uniform2bv = gl.getUniformLocation(shaderProgram, "b2v");
+            uniform3bv = gl.getUniformLocation(shaderProgram, "b3v");
+            uniform4bv = gl.getUniformLocation(shaderProgram, "b4v");
+            uniformMatrix2fv = gl.getUniformLocation(shaderProgram, "matrix2");
+            uniformMatrix3fv = gl.getUniformLocation(shaderProgram, "matrix3");
+            uniformMatrix4fv = gl.getUniformLocation(shaderProgram, "matrix4");
+            uniformSampler = gl.getUniformLocation(shaderProgram, "uSampler")
+
+            gl.activeTexture(gl.TEXTURE0);
+            gl.uniform1i(uniformSampler, 0);
+
+            gl.clearColor(0.0, 0.0, 0.0, 1.0);
+
+            gl.viewport(0, 0,
+                        canvas.width * canvas.devicePixelRatio,
+                        canvas.height * canvas.devicePixelRatio);
+
+            // Test getActiveUniform
+            var uniformCount = 0;
+            var expectedUniformCount = 37;
+            var actualUniformCount = gl.getProgramParameter(shaderProgram, gl.ACTIVE_UNIFORMS);
+
+            for (var i = 0; i < actualUniformCount; i++) {
+                var activeInfo = gl.getActiveUniform(shaderProgram, i);
+                if (activeInfo.name !== "")
+                    uniformCount++;
+                if (activeInfo.name === "testUniform") {
+                    if (activeInfo.size !== 1) {
+                        console.log("initializeGL(): FAILURE: testUniform size wrong");
+                        return 2;
+                    }
+
+                    if (activeInfo.type !== Context3D.FLOAT) {
+                        console.log("initializeGL(): FAILURE: testUniform type wrong");
+                        return 3;
+                    }
+                }
+            }
+
+            if (uniformCount !== expectedUniformCount) {
+                console.log("initializeGL(): FAILURE: active uniform count wrong, expected:",
+                            expectedUniformCount, "actual:", uniformCount);
+                return 4;
+            }
+
+            // Test uniform setters and getters
+            var uniformValue = 0;
+            var checkValue = false;
+            fillValue = 1;
+
+            gl.uniform1i(uniform1i, fillValue);
+            uniformValue = gl.getUniform(shaderProgram, uniform1i);
+            if (!compareUniformValues(fillValue, uniformValue))
+                return 5;
+            fillValue++;
+
+            constructTestArrayJS(2);
+            gl.uniform2i(uniform2i, testArray[0], testArray[1]);
+            uniformValue = gl.getUniform(shaderProgram, uniform2i);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 6;
+
+            constructTestArrayJS(3);
+            gl.uniform3i(uniform3i, testArray[0], testArray[1], testArray[2]);
+            uniformValue = gl.getUniform(shaderProgram, uniform3i);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 7;
+
+            constructTestArrayJS(4);
+            gl.uniform4i(uniform4i, testArray[0], testArray[1], testArray[2], testArray[3]);
+            uniformValue = gl.getUniform(shaderProgram, uniform4i);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 8;
+
+            gl.uniform1f(uniform1f, fillValue);
+            uniformValue = gl.getUniform(shaderProgram, uniform1f);
+            if (!compareUniformValues(fillValue, uniformValue))
+                return 9;
+            fillValue++;
+
+            constructTestArrayJS(2);
+            gl.uniform2f(uniform2f, testArray[0], testArray[1]);
+            uniformValue = gl.getUniform(shaderProgram, uniform2f);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 10;
+
+            constructTestArrayJS(3);
+            gl.uniform3f(uniform3f, testArray[0], testArray[1], testArray[2]);
+            uniformValue = gl.getUniform(shaderProgram, uniform3f);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 11;
+
+            constructTestArrayJS(4);
+            gl.uniform4f(uniform4f, testArray[0], testArray[1], testArray[2], testArray[3]);
+            uniformValue = gl.getUniform(shaderProgram, uniform4f);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 12;
+
+            gl.uniform1i(uniform1b, true);
+            uniformValue = gl.getUniform(shaderProgram, uniform1b);
+            if (!compareUniformValues(true, uniformValue))
+                return 13;
+            fillValue++;
+
+            constructTestArrayBooleanJS(2);
+            gl.uniform2i(uniform2b, testArray[0], testArray[1]);
+            uniformValue = gl.getUniform(shaderProgram, uniform2b);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 14;
+
+            constructTestArrayBooleanJS(3);
+            gl.uniform3i(uniform3b, testArray[0], testArray[1], testArray[2]);
+            uniformValue = gl.getUniform(shaderProgram, uniform3b);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 15;
+
+            constructTestArrayBooleanJS(4);
+            gl.uniform4i(uniform4b, testArray[0], testArray[1], testArray[2], testArray[3]);
+            uniformValue = gl.getUniform(shaderProgram, uniform4b);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 16;
+
+            // Test both javascript arrays and typed arrays for functions that take arrays
+            var arrayElements = 3;
+            var elementLen = 1;
+            gl.uniform1iv(uniform1iv, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("i1v[0]");
+            if (!compareUniformValues(testArray[0], uniformValue))
+                return 17;
+            uniformValue = getUniformValue("i1v[1]");
+            if (!compareUniformValues(testArray[1], uniformValue))
+                return 18;
+            uniformValue = getUniformValue("i1v[2]");
+            if (!compareUniformValues(testArray[2], uniformValue))
+                return 19;
+
+            elementLen = 2;
+            gl.uniform2iv(uniform2iv, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("i2v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 20;
+            uniformValue = getUniformValue("i2v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 21;
+            uniformValue = getUniformValue("i2v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 22;
+
+            elementLen = 3;
+            gl.uniform3iv(uniform3iv, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("i3v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 23;
+            uniformValue = getUniformValue("i3v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 24;
+            uniformValue = getUniformValue("i3v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 25;
+
+            elementLen = 4;
+            gl.uniform4iv(uniform4iv, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("i4v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 26;
+            uniformValue = getUniformValue("i4v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 27;
+            uniformValue = getUniformValue("i4v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 28;
+
+            elementLen = 1;
+            gl.uniform1fv(uniform1fv, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("f1v[0]");
+            if (!compareUniformValues(testArray[0], uniformValue))
+                return 29;
+            uniformValue = getUniformValue("f1v[1]");
+            if (!compareUniformValues(testArray[1], uniformValue))
+                return 30;
+            uniformValue = getUniformValue("f1v[2]");
+            if (!compareUniformValues(testArray[2], uniformValue))
+                return 31;
+
+            elementLen = 2;
+            gl.uniform2fv(uniform2fv, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("f2v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 32;
+            uniformValue = getUniformValue("f2v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 33;
+            uniformValue = getUniformValue("f2v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 34;
+
+            elementLen = 3;
+            gl.uniform3fv(uniform3fv, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("f3v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 35;
+            uniformValue = getUniformValue("f3v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 36;
+            uniformValue = getUniformValue("f3v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 37;
+
+            elementLen = 4;
+            gl.uniform4fv(uniform4fv, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("f4v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 38;
+            uniformValue = getUniformValue("f4v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 39;
+            uniformValue = getUniformValue("f4v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 40;
+
+            elementLen = 1
+            gl.uniform1iv(uniform1bv, constructTestArrayBooleanJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("b1v[0]");
+            if (!compareUniformValues(testArray[0], uniformValue))
+                return 41;
+            uniformValue = getUniformValue("b1v[1]");
+            if (!compareUniformValues(testArray[1], uniformValue))
+                return 42;
+            uniformValue = getUniformValue("b1v[2]");
+            if (!compareUniformValues(testArray[2], uniformValue))
+                return 43;
+
+            elementLen = 2;
+            gl.uniform2iv(uniform2bv, constructTestArrayBooleanJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("b2v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 44;
+            uniformValue = getUniformValue("b2v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 45;
+            uniformValue = getUniformValue("b2v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 46;
+
+            elementLen = 3;
+            gl.uniform3iv(uniform3bv, constructTestArrayBooleanJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("b3v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 47;
+            uniformValue = getUniformValue("b3v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 48;
+            uniformValue = getUniformValue("b3v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 49;
+
+            elementLen = 4;
+            gl.uniform4iv(uniform4bv, constructTestArrayBooleanJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("b4v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 50;
+            uniformValue = getUniformValue("b4v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 51;
+            uniformValue = getUniformValue("b4v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 52;
+
+            elementLen = 2 * 2;
+            gl.uniformMatrix2fv(uniformMatrix2fv, false, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix2[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 53;
+            uniformValue = getUniformValue("matrix2[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 54;
+            uniformValue = getUniformValue("matrix2[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 55;
+
+            gl.uniformMatrix2fv(uniformMatrix2fv, true, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix2[0]");
+            if (!compareUniformSubArraysTransposed(0 * elementLen, elementLen, testArray, uniformValue))
+                return 56;
+            uniformValue = getUniformValue("matrix2[1]");
+            if (!compareUniformSubArraysTransposed(1 * elementLen, elementLen, testArray, uniformValue))
+                return 57;
+            uniformValue = getUniformValue("matrix2[2]");
+            if (!compareUniformSubArraysTransposed(2 * elementLen, elementLen, testArray, uniformValue))
+                return 58;
+
+            elementLen = 3 * 3;
+            gl.uniformMatrix3fv(uniformMatrix3fv, false, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix3[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 59;
+            uniformValue = getUniformValue("matrix3[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 60;
+            uniformValue = getUniformValue("matrix3[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 61;
+
+            gl.uniformMatrix3fv(uniformMatrix3fv, true, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix3[0]");
+            if (!compareUniformSubArraysTransposed(0 * elementLen, elementLen, testArray, uniformValue))
+                return 62;
+            uniformValue = getUniformValue("matrix3[1]");
+            if (!compareUniformSubArraysTransposed(1 * elementLen, elementLen, testArray, uniformValue))
+                return 63;
+            uniformValue = getUniformValue("matrix3[2]");
+            if (!compareUniformSubArraysTransposed(2 * elementLen, elementLen, testArray, uniformValue))
+                return 64;
+
+            elementLen = 4 * 4;
+            gl.uniformMatrix4fv(uniformMatrix4fv, false, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix4[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 65;
+            uniformValue = getUniformValue("matrix4[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 66;
+            uniformValue = getUniformValue("matrix4[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 67;
+
+            gl.uniformMatrix4fv(uniformMatrix4fv, true, constructTestArrayJS(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix4[0]");
+            if (!compareUniformSubArraysTransposed(0 * elementLen, elementLen, testArray, uniformValue))
+                return 68;
+            uniformValue = getUniformValue("matrix4[1]");
+            if (!compareUniformSubArraysTransposed(1 * elementLen, elementLen, testArray, uniformValue))
+                return 69;
+            uniformValue = getUniformValue("matrix4[2]");
+            if (!compareUniformSubArraysTransposed(2 * elementLen, elementLen, testArray, uniformValue))
+                return 70;
+
+            elementLen = 1;
+            gl.uniform1iv(uniform1iv, constructTestArrayInt(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("i1v[0]");
+            if (!compareUniformValues(testArray[0], uniformValue))
+                return 71;
+            uniformValue = getUniformValue("i1v[1]");
+            if (!compareUniformValues(testArray[1], uniformValue))
+                return 72;
+            uniformValue = getUniformValue("i1v[2]");
+            if (!compareUniformValues(testArray[2], uniformValue))
+                return 73;
+
+            elementLen = 2;
+            gl.uniform2iv(uniform2iv, constructTestArrayInt(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("i2v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 74;
+            uniformValue = getUniformValue("i2v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 75;
+            uniformValue = getUniformValue("i2v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 76;
+
+            elementLen = 3;
+            gl.uniform3iv(uniform3iv, constructTestArrayInt(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("i3v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 77;
+            uniformValue = getUniformValue("i3v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 78;
+            uniformValue = getUniformValue("i3v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 79;
+
+            elementLen = 4;
+            gl.uniform4iv(uniform4iv, constructTestArrayInt(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("i4v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 80;
+            uniformValue = getUniformValue("i4v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 81;
+            uniformValue = getUniformValue("i4v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 82;
+
+            elementLen = 1;
+            gl.uniform1fv(uniform1fv, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("f1v[0]");
+            if (!compareUniformValues(testArray[0], uniformValue))
+                return 83;
+            uniformValue = getUniformValue("f1v[1]");
+            if (!compareUniformValues(testArray[1], uniformValue))
+                return 84;
+            uniformValue = getUniformValue("f1v[2]");
+            if (!compareUniformValues(testArray[2], uniformValue))
+                return 85;
+
+            elementLen = 2;
+            gl.uniform2fv(uniform2fv, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("f2v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 86;
+            uniformValue = getUniformValue("f2v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 87;
+            uniformValue = getUniformValue("f2v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 88;
+
+            elementLen = 3;
+            gl.uniform3fv(uniform3fv, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("f3v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 89;
+            uniformValue = getUniformValue("f3v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 90;
+            uniformValue = getUniformValue("f3v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 91;
+
+            elementLen = 4;
+            gl.uniform4fv(uniform4fv, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("f4v[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 92;
+            uniformValue = getUniformValue("f4v[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 93;
+            uniformValue = getUniformValue("f4v[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 94;
+
+            elementLen = 2 * 2;
+            gl.uniformMatrix2fv(uniformMatrix2fv, false, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix2[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 95;
+            uniformValue = getUniformValue("matrix2[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 96;
+            uniformValue = getUniformValue("matrix2[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 97;
+
+            gl.uniformMatrix2fv(uniformMatrix2fv, true, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix2[0]");
+            if (!compareUniformSubArraysTransposed(0 * elementLen, elementLen, testArray, uniformValue))
+                return 98;
+            uniformValue = getUniformValue("matrix2[1]");
+            if (!compareUniformSubArraysTransposed(1 * elementLen, elementLen, testArray, uniformValue))
+                return 99;
+            uniformValue = getUniformValue("matrix2[2]");
+            if (!compareUniformSubArraysTransposed(2 * elementLen, elementLen, testArray, uniformValue))
+                return 100;
+
+            elementLen = 3 * 3;
+            gl.uniformMatrix3fv(uniformMatrix3fv, false, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix3[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 101;
+            uniformValue = getUniformValue("matrix3[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 102;
+            uniformValue = getUniformValue("matrix3[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 103;
+
+            gl.uniformMatrix3fv(uniformMatrix3fv, true, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix3[0]");
+            if (!compareUniformSubArraysTransposed(0 * elementLen, elementLen, testArray, uniformValue))
+                return 104;
+            uniformValue = getUniformValue("matrix3[1]");
+            if (!compareUniformSubArraysTransposed(1 * elementLen, elementLen, testArray, uniformValue))
+                return 105;
+            uniformValue = getUniformValue("matrix3[2]");
+            if (!compareUniformSubArraysTransposed(2 * elementLen, elementLen, testArray, uniformValue))
+                return 106;
+
+            elementLen = 4 * 4;
+            gl.uniformMatrix4fv(uniformMatrix4fv, false, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix4[0]");
+            if (!compareUniformSubArrays(0 * elementLen, elementLen, testArray, uniformValue))
+                return 107;
+            uniformValue = getUniformValue("matrix4[1]");
+            if (!compareUniformSubArrays(1 * elementLen, elementLen, testArray, uniformValue))
+                return 108;
+            uniformValue = getUniformValue("matrix4[2]");
+            if (!compareUniformSubArrays(2 * elementLen, elementLen, testArray, uniformValue))
+                return 109;
+
+            gl.uniformMatrix4fv(uniformMatrix4fv, true, constructTestArrayFloat(arrayElements * elementLen));
+
+            uniformValue = getUniformValue("matrix4[0]");
+            if (!compareUniformSubArraysTransposed(0 * elementLen, elementLen, testArray, uniformValue))
+                return 110;
+            uniformValue = getUniformValue("matrix4[1]");
+            if (!compareUniformSubArraysTransposed(1 * elementLen, elementLen, testArray, uniformValue))
+                return 111;
+            uniformValue = getUniformValue("matrix4[2]");
+            if (!compareUniformSubArraysTransposed(2 * elementLen, elementLen, testArray, uniformValue))
+                return 112;
+
+            gl.uniform1i(uniformSampler, 1);
+            uniformValue = gl.getUniform(shaderProgram, uniformSampler);
+            if (!compareUniformValues(1, uniformValue))
+                return 113;
+
+            constructTestArrayJS(3);
+            var uniformLocation = gl.getUniformLocation(shaderProgram, "uStruct.first");
+            gl.uniform3f(uniformLocation, testArray[0], testArray[1], testArray[2]);
+            uniformValue = gl.getUniform(shaderProgram, uniformLocation);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 114;
+
+            uniformLocation = gl.getUniformLocation(shaderProgram, "uStruct.second");
+            gl.uniform1i(uniformLocation, fillValue);
+            uniformValue = gl.getUniform(shaderProgram, uniformLocation);
+            if (!compareUniformValues(fillValue, uniformValue))
+                return 115;
+            fillValue++;
+
+            constructTestArrayJS(3);
+            uniformLocation = gl.getUniformLocation(shaderProgram, "uStructv[0].first");
+            gl.uniform3f(uniformLocation, testArray[0], testArray[1], testArray[2]);
+            uniformValue = gl.getUniform(shaderProgram, uniformLocation);
+            if (!compareUniformArrays(testArray, uniformValue))
+                return 116;
+
+            uniformLocation = gl.getUniformLocation(shaderProgram, "uStructv[0].second");
+            gl.uniform1i(uniformLocation, fillValue);
+            uniformValue = gl.getUniform(shaderProgram, uniformLocation);
+            if (!compareUniformValues(fillValue, uniformValue))
+                return 117;
+            fillValue++;
+        }
+    } catch(e) {
+        console.log("initializeGL(): FAILURE!");
+        console.log(""+e);
+        console.log(""+e.message);
+        initStatus = -1;
+    }
+    return initStatus;
+}
+
+function paintGL(canvas) {
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+    return 0;
+}
+
+function initShaders()
+{
+    vertexShader = compileShader("attribute vec2 a_position; \
+                                  void main() { \
+                                      gl_Position = vec4(a_position, 0.0, 1.0); \
+                                  }", gl.VERTEX_SHADER);
+    fragmentShader = compileShader("struct TestStruct \
+                                    { \
+                                        highp vec3 first; \
+                                        int second; \
+                                    }; \
+                                    uniform highp float inactiveUniform; \
+                                    uniform highp float testUniform; \
+                                    uniform int i1; \
+                                    uniform ivec2 i2; \
+                                    uniform ivec3 i3; \
+                                    uniform ivec4 i4; \
+                                    uniform highp float f1; \
+                                    uniform highp vec2 f2; \
+                                    uniform highp vec3 f3; \
+                                    uniform highp vec4 f4; \
+                                    uniform bool b1; \
+                                    uniform bvec2 b2; \
+                                    uniform bvec3 b3; \
+                                    uniform bvec4 b4; \
+                                    uniform int i1v[3]; \
+                                    uniform ivec2 i2v[3]; \
+                                    uniform ivec3 i3v[3]; \
+                                    uniform ivec4 i4v[3]; \
+                                    uniform highp float f1v[3]; \
+                                    uniform highp vec2 f2v[3]; \
+                                    uniform highp vec3 f3v[3]; \
+                                    uniform highp vec4 f4v[3]; \
+                                    uniform bool b1v[3]; \
+                                    uniform bvec2 b2v[3]; \
+                                    uniform bvec3 b3v[3]; \
+                                    uniform bvec4 b4v[3]; \
+                                    uniform highp mat2 matrix2[3]; \
+                                    uniform highp mat3 matrix3[3]; \
+                                    uniform highp mat4 matrix4[3]; \
+                                    uniform sampler2D uSampler; \
+                                    uniform TestStruct uStruct; \
+                                    uniform TestStruct uStructv[3]; \
+                                    void main() { \
+                                        int iValue = i1 + i2.x + i3.y + i4.z; \
+                                        highp float fValue = f1 + f2.x + f3.y + f4.z; \
+                                        if (b1 || b2.x || b3.y || b4.z) \
+                                            iValue++; \
+                                        fValue += uStruct.first.x; \
+                                        iValue += uStruct.second; \
+                                        for (int i = 0; i < 3; i++) { \
+                                            iValue += i1v[i]; \
+                                            iValue += i2v[i].x; \
+                                            iValue += i3v[i].y; \
+                                            iValue += i4v[i].z; \
+                                            fValue += f1v[i]; \
+                                            fValue += f2v[i].x; \
+                                            fValue += f3v[i].y; \
+                                            fValue += f4v[i].z; \
+                                            fValue += matrix2[i][0][0]; \
+                                            fValue += matrix3[i][1][1]; \
+                                            fValue += matrix4[i][2][2]; \
+                                            if (b1v[i] || b2v[i].x || b3v[i].y || b4v[i].z) \
+                                                iValue++; \
+                                            fValue += uStructv[i].first.x; \
+                                            iValue += uStructv[i].second; \
+                                        } \
+                                        highp vec4 texelColor = texture2D(uSampler, vec2(fValue, fValue)); \
+                                        if (iValue > 30) { \
+                                            gl_FragColor = vec4(fValue, testUniform, texelColor.b, 1.0); \
+                                        } else { \
+                                            gl_FragColor = texelColor; \
+                                        } \
+                                    }", gl.FRAGMENT_SHADER);
+
+    shaderProgram = gl.createProgram();
+    gl.attachShader(shaderProgram, vertexShader);
+    gl.attachShader(shaderProgram, fragmentShader);
+    gl.linkProgram(shaderProgram);
+
+    // Check linking status
+    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+        console.log("Could not initialize shaders");
+        console.log(gl.getProgramInfoLog(shaderProgram));
+        return false;
+    }
+
+    return true;
+}
+
+function compileShader(str, type) {
+    var shader = gl.createShader(type);
+    gl.shaderSource(shader, str);
+    gl.compileShader(shader);
+
+    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+        console.log("JS:Shader compile failed");
+        console.log(gl.getShaderInfoLog(shader));
+        return null;
+    }
+
+    return shader;
+}
diff --git a/tests/auto/qmltest/canvas3d/tst_uniforms.qml b/tests/auto/qmltest/canvas3d/tst_uniforms.qml
new file mode 100644
index 0000000000000000000000000000000000000000..a42d89ce1e6cf873af69b598073f726289ca6a9f
--- /dev/null
+++ b/tests/auto/qmltest/canvas3d/tst_uniforms.qml
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtCanvas3D 1.0
+import QtTest 1.0
+
+import "tst_uniforms.js" as Content
+
+Item {
+    id: top
+    height: 300
+    width: 300
+
+    Canvas3D {
+        id: uniforms_test
+        property bool heightChanged: false
+        property bool widthChanged: false
+        property int initStatus: -1
+        property int renderStatus: -1
+
+        anchors.fill: parent
+        onInitializeGL: initStatus = Content.initializeGL(uniforms_test)
+        onPaintGL: {
+            renderStatus = Content.paintGL(uniforms_test)
+        }
+        onHeightChanged: heightChanged = true
+        onWidthChanged: widthChanged = true
+    }
+
+    TestCase {
+        name: "Canvas3D_test_uniforms"
+        when: windowShown
+
+        function test_uniforms() {
+            waitForRendering(uniforms_test)
+            tryCompare(uniforms_test, "initStatus", 0)
+            tryCompare(uniforms_test, "renderStatus", 0)
+        }
+    }
+}