Re^4-land "Move Uniform and UBO info to the gl::Program layer."

This data was previously stored entirely in the Impl level. Move
as much as possible to the GL level, using a read-only view in the
Impl level. Some information in D3D-specific, and should be stored
separately in the Impl.

This patch has a lot of refactoring that splits the D3D and GL info,
and moves as much validation as possible to the GL layer, where it
is shared between the back-ends.

Re-land with fix for dEQP unused uniforms. The fix involves storing
a local copy of all uniform data in the GL layer. This will also
let us validate sampler indexes during draw calls at the GL layer.

Re-re-land with a fix for multiply defined symbols on Clang.

Re-re-re-land with a fix for boolean uniforms and Uniform{1234}f.

Re^4-land with a fix for boolean uniform arrays and UBO uniforms.

BUG=angleproject:1123
TEST=end2end_tests, bots, dEQP GLES3.ubo and GLES2.uniform_api

Change-Id: I4c9f5ed31b81380507bef7981f97086d642801ae
Reviewed-on: https://chromium-review.googlesource.com/298451
Tryjob-Request: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/common/mathutil.h b/src/common/mathutil.h
index 332ba3d..fb16a1c 100644
--- a/src/common/mathutil.h
+++ b/src/common/mathutil.h
@@ -69,13 +69,13 @@
 template <typename DestT, typename SrcT>
 inline DestT clampCast(SrcT value)
 {
-    // This assumes SrcT can properly represent DestT::min/max
-    // Unfortunately we can't use META_ASSERT without C++11 constexpr support
-    ASSERT(static_cast<DestT>(static_cast<SrcT>(std::numeric_limits<DestT>::min())) == std::numeric_limits<DestT>::min());
-    ASSERT(static_cast<DestT>(static_cast<SrcT>(std::numeric_limits<DestT>::max())) == std::numeric_limits<DestT>::max());
-
     SrcT lo = static_cast<SrcT>(std::numeric_limits<DestT>::min());
     SrcT hi = static_cast<SrcT>(std::numeric_limits<DestT>::max());
+
+    // This assumes SrcT can properly represent DestT::min/max. Checking this is a bit tricky,
+    // especially given floating point representations.
+    ASSERT(lo < hi);
+
     return static_cast<DestT>(value > lo ? (value > hi ? hi : value) : lo);
 }
 
diff --git a/src/compiler/translator/blocklayout.h b/src/compiler/translator/blocklayout.h
index 96d3740..0d0a2ed 100644
--- a/src/compiler/translator/blocklayout.h
+++ b/src/compiler/translator/blocklayout.h
@@ -26,6 +26,8 @@
 
 struct COMPILER_EXPORT BlockMemberInfo
 {
+    BlockMemberInfo() : offset(-1), arrayStride(-1), matrixStride(-1), isRowMajorMatrix(false) {}
+
     BlockMemberInfo(int offset, int arrayStride, int matrixStride, bool isRowMajorMatrix)
         : offset(offset),
           arrayStride(arrayStride),
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 30386f7..1efa216 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -22,6 +22,7 @@
 #include "libANGLE/features.h"
 #include "libANGLE/renderer/Renderer.h"
 #include "libANGLE/renderer/ProgramImpl.h"
+#include "libANGLE/queryconversions.h"
 
 namespace gl
 {
@@ -46,8 +47,135 @@
     return subscript;
 }
 
+void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var)
+{
+    stream->writeInt(var.type);
+    stream->writeInt(var.precision);
+    stream->writeString(var.name);
+    stream->writeString(var.mappedName);
+    stream->writeInt(var.arraySize);
+    stream->writeInt(var.staticUse);
+    stream->writeString(var.structName);
+    ASSERT(var.fields.empty());
 }
 
+void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var)
+{
+    var->type       = stream->readInt<GLenum>();
+    var->precision  = stream->readInt<GLenum>();
+    var->name       = stream->readString();
+    var->mappedName = stream->readString();
+    var->arraySize  = stream->readInt<unsigned int>();
+    var->staticUse  = stream->readBool();
+    var->structName = stream->readString();
+}
+
+template <typename VarT>
+void DefineUniformBlockMembers(const std::vector<VarT> &fields,
+                               const std::string &prefix,
+                               int blockIndex,
+                               std::vector<LinkedUniform> *uniformsOut)
+{
+    for (const VarT &field : fields)
+    {
+        const std::string &fieldName = (prefix.empty() ? field.name : prefix + "." + field.name);
+
+        if (field.isStruct())
+        {
+            for (unsigned int arrayElement = 0; arrayElement < field.elementCount(); arrayElement++)
+            {
+                const std::string uniformElementName =
+                    fieldName + (field.isArray() ? ArrayString(arrayElement) : "");
+                DefineUniformBlockMembers(field.fields, uniformElementName, blockIndex,
+                                          uniformsOut);
+            }
+        }
+        else
+        {
+            // TODO(jmadill): record row-majorness?
+            // Block layout is recorded in the Impl.
+            LinkedUniform newUniform(field.type, field.precision, fieldName, field.arraySize,
+                                     blockIndex, sh::BlockMemberInfo::getDefaultBlockInfo());
+
+            // Since block uniforms have no location, we don't need to store them in the uniform
+            // locations list.
+            uniformsOut->push_back(newUniform);
+        }
+    }
+}
+
+// This simplified cast function doesn't need to worry about advanced concepts like
+// depth range values, or casting to bool.
+template <typename DestT, typename SrcT>
+DestT UniformStateQueryCast(SrcT value);
+
+// From-Float-To-Integer Casts
+template <>
+GLint UniformStateQueryCast(GLfloat value)
+{
+    return clampCast<GLint>(roundf(value));
+}
+
+template <>
+GLuint UniformStateQueryCast(GLfloat value)
+{
+    return clampCast<GLuint>(roundf(value));
+}
+
+// From-Integer-to-Integer Casts
+template <>
+GLint UniformStateQueryCast(GLuint value)
+{
+    return clampCast<GLint>(value);
+}
+
+template <>
+GLuint UniformStateQueryCast(GLint value)
+{
+    return clampCast<GLuint>(value);
+}
+
+// From-Boolean-to-Anything Casts
+template <>
+GLfloat UniformStateQueryCast(GLboolean value)
+{
+    return (value == GL_TRUE ? 1.0f : 0.0f);
+}
+
+template <>
+GLint UniformStateQueryCast(GLboolean value)
+{
+    return (value == GL_TRUE ? 1 : 0);
+}
+
+template <>
+GLuint UniformStateQueryCast(GLboolean value)
+{
+    return (value == GL_TRUE ? 1u : 0u);
+}
+
+// Default to static_cast
+template <typename DestT, typename SrcT>
+DestT UniformStateQueryCast(SrcT value)
+{
+    return static_cast<DestT>(value);
+}
+
+template <typename SrcT, typename DestT>
+void UniformStateQueryCastLoop(DestT *dataOut, const uint8_t *srcPointer, int components)
+{
+    for (int comp = 0; comp < components; ++comp)
+    {
+        // We only work with strides of 4 bytes for uniform components. (GLfloat/GLint)
+        // Don't use SrcT stride directly since GLboolean has a stride of 1 byte.
+        size_t offset               = comp * 4;
+        const SrcT *typedSrcPointer = reinterpret_cast<const SrcT *>(&srcPointer[offset]);
+        dataOut[comp]               = UniformStateQueryCast<DestT>(*typedSrcPointer);
+    }
+}
+
+}  // anonymous namespace
+
 AttributeBindings::AttributeBindings()
 {
 }
@@ -162,6 +290,68 @@
     }
 }
 
+const LinkedUniform *Program::Data::getUniformByName(const std::string &name) const
+{
+    for (const LinkedUniform &linkedUniform : mUniforms)
+    {
+        if (linkedUniform.name == name)
+        {
+            return &linkedUniform;
+        }
+    }
+
+    return nullptr;
+}
+
+GLint Program::Data::getUniformLocation(const std::string &name) const
+{
+    size_t subscript     = GL_INVALID_INDEX;
+    std::string baseName = gl::ParseUniformName(name, &subscript);
+
+    for (size_t location = 0; location < mUniformLocations.size(); ++location)
+    {
+        const VariableLocation &uniformLocation = mUniformLocations[location];
+        const LinkedUniform &uniform            = mUniforms[uniformLocation.index];
+
+        if (uniform.name == baseName)
+        {
+            if ((uniform.isArray() && uniformLocation.element == subscript) ||
+                (subscript == GL_INVALID_INDEX))
+            {
+                return static_cast<GLint>(location);
+            }
+        }
+    }
+
+    return -1;
+}
+
+GLuint Program::Data::getUniformIndex(const std::string &name) const
+{
+    size_t subscript     = GL_INVALID_INDEX;
+    std::string baseName = gl::ParseUniformName(name, &subscript);
+
+    // The app is not allowed to specify array indices other than 0 for arrays of basic types
+    if (subscript != 0 && subscript != GL_INVALID_INDEX)
+    {
+        return GL_INVALID_INDEX;
+    }
+
+    for (size_t index = 0; index < mUniforms.size(); index++)
+    {
+        const LinkedUniform &uniform = mUniforms[index];
+        if (uniform.name == baseName)
+        {
+            if (uniform.isArray() || subscript == GL_INVALID_INDEX)
+            {
+                return static_cast<GLuint>(index);
+            }
+        }
+    }
+
+    return GL_INVALID_INDEX;
+}
+
 Program::Program(rx::ImplFactory *factory, ResourceManager *manager, GLuint handle)
     : mProgram(factory->createProgram(mData)),
       mValidated(false),
@@ -319,6 +509,7 @@
     }
 
     gatherTransformFeedbackVaryings(mergedVaryings);
+    mProgram->gatherUniformBlockInfo(&mData.mUniformBlocks, &mData.mUniforms);
 
     mLinked = true;
     return gl::Error(GL_NO_ERROR);
@@ -358,10 +549,11 @@
     mData.mAttributes.clear();
     mData.mActiveAttribLocationsMask.reset();
     mData.mTransformFeedbackVaryingVars.clear();
+    mData.mUniforms.clear();
+    mData.mUniformLocations.clear();
+    mData.mUniformBlocks.clear();
     mData.mOutputVariables.clear();
 
-    mProgram->reset();
-
     mValidated = false;
 
     mLinked = false;
@@ -415,15 +607,66 @@
     for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
     {
         sh::Attribute attrib;
-        attrib.type      = stream.readInt<GLenum>();
-        attrib.precision = stream.readInt<GLenum>();
-        attrib.name      = stream.readString();
-        attrib.arraySize = stream.readInt<GLint>();
-        attrib.location  = stream.readInt<int>();
-        attrib.staticUse = stream.readBool();
+        LoadShaderVar(&stream, &attrib);
+        attrib.location = stream.readInt<int>();
         mData.mAttributes.push_back(attrib);
     }
 
+    unsigned int uniformCount = stream.readInt<unsigned int>();
+    ASSERT(mData.mUniforms.empty());
+    for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
+    {
+        LinkedUniform uniform;
+        LoadShaderVar(&stream, &uniform);
+
+        uniform.blockIndex                 = stream.readInt<int>();
+        uniform.blockInfo.offset           = stream.readInt<int>();
+        uniform.blockInfo.arrayStride      = stream.readInt<int>();
+        uniform.blockInfo.matrixStride     = stream.readInt<int>();
+        uniform.blockInfo.isRowMajorMatrix = stream.readBool();
+
+        mData.mUniforms.push_back(uniform);
+    }
+
+    const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
+    ASSERT(mData.mUniformLocations.empty());
+    for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
+         uniformIndexIndex++)
+    {
+        VariableLocation variable;
+        stream.readString(&variable.name);
+        stream.readInt(&variable.element);
+        stream.readInt(&variable.index);
+
+        mData.mUniformLocations.push_back(variable);
+    }
+
+    unsigned int uniformBlockCount = stream.readInt<unsigned int>();
+    ASSERT(mData.mUniformBlocks.empty());
+    for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
+         ++uniformBlockIndex)
+    {
+        UniformBlock uniformBlock;
+        stream.readString(&uniformBlock.name);
+        stream.readBool(&uniformBlock.isArray);
+        stream.readInt(&uniformBlock.arrayElement);
+        stream.readInt(&uniformBlock.dataSize);
+        stream.readBool(&uniformBlock.vertexStaticUse);
+        stream.readBool(&uniformBlock.fragmentStaticUse);
+
+        unsigned int numMembers = stream.readInt<unsigned int>();
+        for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
+        {
+            uniformBlock.memberUniformIndexes.push_back(stream.readInt<unsigned int>());
+        }
+
+        // TODO(jmadill): Make D3D-only
+        stream.readInt(&uniformBlock.psRegisterIndex);
+        stream.readInt(&uniformBlock.vsRegisterIndex);
+
+        mData.mUniformBlocks.push_back(uniformBlock);
+    }
+
     stream.readInt(&mData.mTransformFeedbackBufferMode);
 
     unsigned int outputVarCount = stream.readInt<unsigned int>();
@@ -467,12 +710,52 @@
     stream.writeInt(mData.mAttributes.size());
     for (const sh::Attribute &attrib : mData.mAttributes)
     {
-        stream.writeInt(attrib.type);
-        stream.writeInt(attrib.precision);
-        stream.writeString(attrib.name);
-        stream.writeInt(attrib.arraySize);
+        WriteShaderVar(&stream, attrib);
         stream.writeInt(attrib.location);
-        stream.writeInt(attrib.staticUse);
+    }
+
+    stream.writeInt(mData.mUniforms.size());
+    for (const gl::LinkedUniform &uniform : mData.mUniforms)
+    {
+        WriteShaderVar(&stream, uniform);
+
+        // FIXME: referenced
+
+        stream.writeInt(uniform.blockIndex);
+        stream.writeInt(uniform.blockInfo.offset);
+        stream.writeInt(uniform.blockInfo.arrayStride);
+        stream.writeInt(uniform.blockInfo.matrixStride);
+        stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
+    }
+
+    stream.writeInt(mData.mUniformLocations.size());
+    for (const auto &variable : mData.mUniformLocations)
+    {
+        stream.writeString(variable.name);
+        stream.writeInt(variable.element);
+        stream.writeInt(variable.index);
+    }
+
+    stream.writeInt(mData.mUniformBlocks.size());
+    for (const UniformBlock &uniformBlock : mData.mUniformBlocks)
+    {
+        stream.writeString(uniformBlock.name);
+        stream.writeInt(uniformBlock.isArray);
+        stream.writeInt(uniformBlock.arrayElement);
+        stream.writeInt(uniformBlock.dataSize);
+
+        stream.writeInt(uniformBlock.vertexStaticUse);
+        stream.writeInt(uniformBlock.fragmentStaticUse);
+
+        stream.writeInt(uniformBlock.memberUniformIndexes.size());
+        for (unsigned int memberUniformIndex : uniformBlock.memberUniformIndexes)
+        {
+            stream.writeInt(memberUniformIndex);
+        }
+
+        // TODO(jmadill): make D3D-only
+        stream.writeInt(uniformBlock.psRegisterIndex);
+        stream.writeInt(uniformBlock.vsRegisterIndex);
     }
 
     stream.writeInt(mData.mTransformFeedbackBufferMode);
@@ -728,13 +1011,14 @@
 {
     if (mLinked)
     {
-        ASSERT(index < mProgram->getUniforms().size());   // index must be smaller than getActiveUniformCount()
-        LinkedUniform *uniform = mProgram->getUniforms()[index];
+        // index must be smaller than getActiveUniformCount()
+        ASSERT(index < mData.mUniforms.size());
+        const LinkedUniform &uniform = mData.mUniforms[index];
 
         if (bufsize > 0)
         {
-            std::string string = uniform->name;
-            if (uniform->isArray())
+            std::string string = uniform.name;
+            if (uniform.isArray())
             {
                 string += "[0]";
             }
@@ -748,8 +1032,8 @@
             }
         }
 
-        *size = uniform->elementCount();
-        *type = uniform->type;
+        *size = uniform.elementCount();
+        *type = uniform.type;
     }
     else
     {
@@ -772,7 +1056,7 @@
 {
     if (mLinked)
     {
-        return static_cast<GLint>(mProgram->getUniforms().size());
+        return static_cast<GLint>(mData.mUniforms.size());
     }
     else
     {
@@ -782,17 +1066,16 @@
 
 GLint Program::getActiveUniformMaxLength()
 {
-    int maxLength = 0;
+    size_t maxLength = 0;
 
     if (mLinked)
     {
-        unsigned int numUniforms = static_cast<unsigned int>(mProgram->getUniforms().size());
-        for (unsigned int uniformIndex = 0; uniformIndex < numUniforms; uniformIndex++)
+        for (const LinkedUniform &uniform : mData.mUniforms)
         {
-            if (!mProgram->getUniforms()[uniformIndex]->name.empty())
+            if (!uniform.name.empty())
             {
-                int length = (int)(mProgram->getUniforms()[uniformIndex]->name.length() + 1);
-                if (mProgram->getUniforms()[uniformIndex]->isArray())
+                size_t length = uniform.name.length() + 1u;
+                if (uniform.isArray())
                 {
                     length += 3;  // Counting in "[0]".
                 }
@@ -801,12 +1084,13 @@
         }
     }
 
-    return maxLength;
+    return static_cast<GLint>(maxLength);
 }
 
 GLint Program::getActiveUniformi(GLuint index, GLenum pname) const
 {
-    const gl::LinkedUniform& uniform = *mProgram->getUniforms()[index];
+    ASSERT(static_cast<size_t>(index) < mData.mUniforms.size());
+    const gl::LinkedUniform &uniform = mData.mUniforms[index];
     switch (pname)
     {
       case GL_UNIFORM_TYPE:         return static_cast<GLint>(uniform.type);
@@ -826,149 +1110,165 @@
 
 bool Program::isValidUniformLocation(GLint location) const
 {
-    const auto &uniformIndices = mProgram->getUniformIndices();
-    ASSERT(rx::IsIntegerCastSafe<GLint>(uniformIndices.size()));
-    return (location >= 0 && uniformIndices.find(location) != uniformIndices.end());
+    ASSERT(rx::IsIntegerCastSafe<GLint>(mData.mUniformLocations.size()));
+    return (location >= 0 && static_cast<size_t>(location) < mData.mUniformLocations.size());
 }
 
-LinkedUniform *Program::getUniformByLocation(GLint location) const
+const LinkedUniform &Program::getUniformByLocation(GLint location) const
 {
-    return mProgram->getUniformByLocation(location);
+    ASSERT(location >= 0 && static_cast<size_t>(location) < mData.mUniformLocations.size());
+    return mData.mUniforms[mData.mUniformLocations[location].index];
 }
 
-LinkedUniform *Program::getUniformByName(const std::string &name) const
+GLint Program::getUniformLocation(const std::string &name) const
 {
-    return mProgram->getUniformByName(name);
+    return mData.getUniformLocation(name);
 }
 
-GLint Program::getUniformLocation(const std::string &name)
+GLuint Program::getUniformIndex(const std::string &name) const
 {
-    return mProgram->getUniformLocation(name);
-}
-
-GLuint Program::getUniformIndex(const std::string &name)
-{
-    return mProgram->getUniformIndex(name);
+    return mData.getUniformIndex(name);
 }
 
 void Program::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
 {
+    setUniformInternal(location, count * 1, v);
     mProgram->setUniform1fv(location, count, v);
 }
 
 void Program::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
 {
+    setUniformInternal(location, count * 2, v);
     mProgram->setUniform2fv(location, count, v);
 }
 
 void Program::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
 {
+    setUniformInternal(location, count * 3, v);
     mProgram->setUniform3fv(location, count, v);
 }
 
 void Program::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
 {
+    setUniformInternal(location, count * 4, v);
     mProgram->setUniform4fv(location, count, v);
 }
 
 void Program::setUniform1iv(GLint location, GLsizei count, const GLint *v)
 {
+    setUniformInternal(location, count * 1, v);
     mProgram->setUniform1iv(location, count, v);
 }
 
 void Program::setUniform2iv(GLint location, GLsizei count, const GLint *v)
 {
+    setUniformInternal(location, count * 2, v);
     mProgram->setUniform2iv(location, count, v);
 }
 
 void Program::setUniform3iv(GLint location, GLsizei count, const GLint *v)
 {
+    setUniformInternal(location, count * 3, v);
     mProgram->setUniform3iv(location, count, v);
 }
 
 void Program::setUniform4iv(GLint location, GLsizei count, const GLint *v)
 {
+    setUniformInternal(location, count * 4, v);
     mProgram->setUniform4iv(location, count, v);
 }
 
 void Program::setUniform1uiv(GLint location, GLsizei count, const GLuint *v)
 {
+    setUniformInternal(location, count * 1, v);
     mProgram->setUniform1uiv(location, count, v);
 }
 
 void Program::setUniform2uiv(GLint location, GLsizei count, const GLuint *v)
 {
+    setUniformInternal(location, count * 2, v);
     mProgram->setUniform2uiv(location, count, v);
 }
 
 void Program::setUniform3uiv(GLint location, GLsizei count, const GLuint *v)
 {
+    setUniformInternal(location, count * 3, v);
     mProgram->setUniform3uiv(location, count, v);
 }
 
 void Program::setUniform4uiv(GLint location, GLsizei count, const GLuint *v)
 {
+    setUniformInternal(location, count * 4, v);
     mProgram->setUniform4uiv(location, count, v);
 }
 
 void Program::setUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<2, 2>(location, count, transpose, v);
     mProgram->setUniformMatrix2fv(location, count, transpose, v);
 }
 
 void Program::setUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<3, 3>(location, count, transpose, v);
     mProgram->setUniformMatrix3fv(location, count, transpose, v);
 }
 
 void Program::setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<4, 4>(location, count, transpose, v);
     mProgram->setUniformMatrix4fv(location, count, transpose, v);
 }
 
 void Program::setUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<2, 3>(location, count, transpose, v);
     mProgram->setUniformMatrix2x3fv(location, count, transpose, v);
 }
 
 void Program::setUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<2, 4>(location, count, transpose, v);
     mProgram->setUniformMatrix2x4fv(location, count, transpose, v);
 }
 
 void Program::setUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<3, 2>(location, count, transpose, v);
     mProgram->setUniformMatrix3x2fv(location, count, transpose, v);
 }
 
 void Program::setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<3, 4>(location, count, transpose, v);
     mProgram->setUniformMatrix3x4fv(location, count, transpose, v);
 }
 
 void Program::setUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<4, 2>(location, count, transpose, v);
     mProgram->setUniformMatrix4x2fv(location, count, transpose, v);
 }
 
 void Program::setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v)
 {
+    setMatrixUniformInternal<4, 3>(location, count, transpose, v);
     mProgram->setUniformMatrix4x3fv(location, count, transpose, v);
 }
 
 void Program::getUniformfv(GLint location, GLfloat *v)
 {
-    mProgram->getUniformfv(location, v);
+    getUniformInternal(location, v);
 }
 
 void Program::getUniformiv(GLint location, GLint *v)
 {
-    mProgram->getUniformiv(location, v);
+    getUniformInternal(location, v);
 }
 
 void Program::getUniformuiv(GLint location, GLuint *v)
 {
-    mProgram->getUniformuiv(location, v);
+    getUniformInternal(location, v);
 }
 
 void Program::flagForDeletion()
@@ -1007,22 +1307,23 @@
 
 GLuint Program::getActiveUniformBlockCount()
 {
-    return static_cast<GLuint>(mProgram->getUniformBlocks().size());
+    return static_cast<GLuint>(mData.mUniformBlocks.size());
 }
 
 void Program::getActiveUniformBlockName(GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) const
 {
-    ASSERT(uniformBlockIndex < mProgram->getUniformBlocks().size());   // index must be smaller than getActiveUniformBlockCount()
+    ASSERT(uniformBlockIndex <
+           mData.mUniformBlocks.size());  // index must be smaller than getActiveUniformBlockCount()
 
-    const UniformBlock &uniformBlock = *mProgram->getUniformBlocks()[uniformBlockIndex];
+    const UniformBlock &uniformBlock = mData.mUniformBlocks[uniformBlockIndex];
 
     if (bufSize > 0)
     {
         std::string string = uniformBlock.name;
 
-        if (uniformBlock.isArrayElement())
+        if (uniformBlock.isArray)
         {
-            string += ArrayString(uniformBlock.elementIndex);
+            string += ArrayString(uniformBlock.arrayElement);
         }
 
         strncpy(uniformBlockName, string.c_str(), bufSize);
@@ -1037,9 +1338,10 @@
 
 void Program::getActiveUniformBlockiv(GLuint uniformBlockIndex, GLenum pname, GLint *params) const
 {
-    ASSERT(uniformBlockIndex < mProgram->getUniformBlocks().size());   // index must be smaller than getActiveUniformBlockCount()
+    ASSERT(uniformBlockIndex <
+           mData.mUniformBlocks.size());  // index must be smaller than getActiveUniformBlockCount()
 
-    const UniformBlock &uniformBlock = *mProgram->getUniformBlocks()[uniformBlockIndex];
+    const UniformBlock &uniformBlock = mData.mUniformBlocks[uniformBlockIndex];
 
     switch (pname)
     {
@@ -1047,7 +1349,8 @@
         *params = static_cast<GLint>(uniformBlock.dataSize);
         break;
       case GL_UNIFORM_BLOCK_NAME_LENGTH:
-        *params = static_cast<GLint>(uniformBlock.name.size() + 1 + (uniformBlock.isArrayElement() ? 3 : 0));
+          *params =
+              static_cast<GLint>(uniformBlock.name.size() + 1 + (uniformBlock.isArray ? 3 : 0));
         break;
       case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
         *params = static_cast<GLint>(uniformBlock.memberUniformIndexes.size());
@@ -1061,10 +1364,10 @@
         }
         break;
       case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
-        *params = static_cast<GLint>(uniformBlock.isReferencedByVertexShader());
+          *params = static_cast<GLint>(uniformBlock.vertexStaticUse);
         break;
       case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
-        *params = static_cast<GLint>(uniformBlock.isReferencedByFragmentShader());
+          *params = static_cast<GLint>(uniformBlock.fragmentStaticUse);
         break;
       default: UNREACHABLE();
     }
@@ -1076,17 +1379,16 @@
 
     if (mLinked)
     {
-        unsigned int numUniformBlocks =
-            static_cast<unsigned int>(mProgram->getUniformBlocks().size());
+        unsigned int numUniformBlocks = static_cast<unsigned int>(mData.mUniformBlocks.size());
         for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < numUniformBlocks; uniformBlockIndex++)
         {
-            const UniformBlock &uniformBlock = *mProgram->getUniformBlocks()[uniformBlockIndex];
+            const UniformBlock &uniformBlock = mData.mUniformBlocks[uniformBlockIndex];
             if (!uniformBlock.name.empty())
             {
                 const int length = static_cast<int>(uniformBlock.name.length()) + 1;
 
                 // Counting in "[0]".
-                const int arrayLength = (uniformBlock.isArrayElement() ? 3 : 0);
+                const int arrayLength = (uniformBlock.isArray ? 3 : 0);
 
                 maxLength = std::max(length + arrayLength, maxLength);
             }
@@ -1098,12 +1400,32 @@
 
 GLuint Program::getUniformBlockIndex(const std::string &name)
 {
-    return mProgram->getUniformBlockIndex(name);
+    size_t subscript     = GL_INVALID_INDEX;
+    std::string baseName = gl::ParseUniformName(name, &subscript);
+
+    unsigned int numUniformBlocks = static_cast<unsigned int>(mData.mUniformBlocks.size());
+    for (unsigned int blockIndex = 0; blockIndex < numUniformBlocks; blockIndex++)
+    {
+        const gl::UniformBlock &uniformBlock = mData.mUniformBlocks[blockIndex];
+        if (uniformBlock.name == baseName)
+        {
+            const bool arrayElementZero =
+                (subscript == GL_INVALID_INDEX &&
+                 (!uniformBlock.isArray || uniformBlock.arrayElement == 0));
+            if (subscript == uniformBlock.arrayElement || arrayElementZero)
+            {
+                return blockIndex;
+            }
+        }
+    }
+
+    return GL_INVALID_INDEX;
 }
 
-const UniformBlock *Program::getUniformBlockByIndex(GLuint index) const
+const UniformBlock &Program::getUniformBlockByIndex(GLuint index) const
 {
-    return mProgram->getUniformBlockByIndex(index);
+    ASSERT(index < static_cast<GLuint>(mData.mUniformBlocks.size()));
+    return mData.mUniformBlocks[index];
 }
 
 void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
@@ -1243,17 +1565,17 @@
     return true;
 }
 
-bool Program::linkUniforms(gl::InfoLog &infoLog, const gl::Caps & /*caps*/) const
+bool Program::linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps)
 {
     const std::vector<sh::Uniform> &vertexUniforms   = mData.mAttachedVertexShader->getUniforms();
     const std::vector<sh::Uniform> &fragmentUniforms = mData.mAttachedFragmentShader->getUniforms();
 
     // Check that uniforms defined in the vertex and fragment shaders are identical
-    std::map<std::string, const sh::Uniform *> linkedUniforms;
+    std::map<std::string, LinkedUniform> linkedUniforms;
 
     for (const sh::Uniform &vertexUniform : vertexUniforms)
     {
-        linkedUniforms[vertexUniform.name] = &vertexUniform;
+        linkedUniforms[vertexUniform.name] = LinkedUniform(vertexUniform);
     }
 
     for (const sh::Uniform &fragmentUniform : fragmentUniforms)
@@ -1261,19 +1583,45 @@
         auto entry = linkedUniforms.find(fragmentUniform.name);
         if (entry != linkedUniforms.end())
         {
-            const sh::Uniform &vertexUniform = *entry->second;
-            const std::string &uniformName = "uniform '" + vertexUniform.name + "'";
-            if (!linkValidateUniforms(infoLog, uniformName, vertexUniform, fragmentUniform))
+            LinkedUniform *vertexUniform   = &entry->second;
+            const std::string &uniformName = "uniform '" + vertexUniform->name + "'";
+            if (!linkValidateUniforms(infoLog, uniformName, *vertexUniform, fragmentUniform))
             {
                 return false;
             }
         }
     }
 
-    // TODO(jmadill): check sampler uniforms with caps
+    // Flatten the uniforms list (nested fields) into a simple list (no nesting).
+    // Also check the maximum uniform vector and sampler counts.
+    if (!flattenUniformsAndCheckCaps(caps, infoLog))
+    {
+        return false;
+    }
+
+    indexUniforms();
+
     return true;
 }
 
+void Program::indexUniforms()
+{
+    for (size_t uniformIndex = 0; uniformIndex < mData.mUniforms.size(); uniformIndex++)
+    {
+        const gl::LinkedUniform &uniform = mData.mUniforms[uniformIndex];
+
+        for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
+        {
+            if (!uniform.isBuiltIn())
+            {
+                // Assign in-order uniform locations
+                mData.mUniformLocations.push_back(gl::VariableLocation(
+                    uniform.name, arrayIndex, static_cast<unsigned int>(uniformIndex)));
+            }
+        }
+    }
+}
+
 bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog, const std::string &uniformName, const sh::InterfaceBlockField &vertexUniform, const sh::InterfaceBlockField &fragmentUniform)
 {
     if (!linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform, true))
@@ -1453,6 +1801,8 @@
         }
     }
 
+    gatherInterfaceBlockInfo();
+
     return true;
 }
 
@@ -1730,4 +2080,282 @@
         }
     }
 }
+
+bool Program::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog)
+{
+    const gl::Shader *vertexShader = mData.getAttachedVertexShader();
+    VectorAndSamplerCount vsCounts;
+
+    for (const sh::Uniform &uniform : vertexShader->getUniforms())
+    {
+        if (uniform.staticUse)
+        {
+            vsCounts += flattenUniform(uniform, uniform.name);
+        }
+    }
+
+    if (vsCounts.vectorCount > caps.maxVertexUniformVectors)
+    {
+        infoLog << "Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS ("
+                << caps.maxVertexUniformVectors << ").";
+        return false;
+    }
+
+    if (vsCounts.samplerCount > caps.maxVertexTextureImageUnits)
+    {
+        infoLog << "Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS ("
+                << caps.maxVertexTextureImageUnits << ").";
+        return false;
+    }
+
+    const gl::Shader *fragmentShader = mData.getAttachedFragmentShader();
+    VectorAndSamplerCount fsCounts;
+
+    for (const sh::Uniform &uniform : fragmentShader->getUniforms())
+    {
+        if (uniform.staticUse)
+        {
+            fsCounts += flattenUniform(uniform, uniform.name);
+        }
+    }
+
+    if (fsCounts.vectorCount > caps.maxFragmentUniformVectors)
+    {
+        infoLog << "Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS ("
+                << caps.maxFragmentUniformVectors << ").";
+        return false;
+    }
+
+    if (fsCounts.samplerCount > caps.maxTextureImageUnits)
+    {
+        infoLog << "Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS ("
+                << caps.maxTextureImageUnits << ").";
+        return false;
+    }
+
+    return true;
+}
+
+Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable &uniform,
+                                                       const std::string &fullName)
+{
+    VectorAndSamplerCount vectorAndSamplerCount;
+
+    if (uniform.isStruct())
+    {
+        for (unsigned int elementIndex = 0; elementIndex < uniform.elementCount(); elementIndex++)
+        {
+            const std::string &elementString = (uniform.isArray() ? ArrayString(elementIndex) : "");
+
+            for (size_t fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++)
+            {
+                const sh::ShaderVariable &field  = uniform.fields[fieldIndex];
+                const std::string &fieldFullName = (fullName + elementString + "." + field.name);
+
+                vectorAndSamplerCount += flattenUniform(field, fieldFullName);
+            }
+        }
+
+        return vectorAndSamplerCount;
+    }
+
+    // Not a struct
+    if (mData.getUniformByName(fullName) == nullptr)
+    {
+        gl::LinkedUniform linkedUniform(uniform.type, uniform.precision, fullName,
+                                        uniform.arraySize, -1,
+                                        sh::BlockMemberInfo::getDefaultBlockInfo());
+        linkedUniform.staticUse = true;
+        mData.mUniforms.push_back(linkedUniform);
+    }
+
+    vectorAndSamplerCount.vectorCount =
+        (VariableRegisterCount(uniform.type) * uniform.elementCount());
+    vectorAndSamplerCount.samplerCount = (IsSamplerType(uniform.type) ? uniform.elementCount() : 0);
+
+    return vectorAndSamplerCount;
+}
+
+void Program::gatherInterfaceBlockInfo()
+{
+    std::set<std::string> visitedList;
+
+    const gl::Shader *vertexShader = mData.getAttachedVertexShader();
+
+    ASSERT(mData.mUniformBlocks.empty());
+    for (const sh::InterfaceBlock &vertexBlock : vertexShader->getInterfaceBlocks())
+    {
+        // Only 'packed' blocks are allowed to be considered inacive.
+        if (!vertexBlock.staticUse && vertexBlock.layout == sh::BLOCKLAYOUT_PACKED)
+            continue;
+
+        if (visitedList.count(vertexBlock.name) > 0)
+            continue;
+
+        defineUniformBlock(vertexBlock, GL_VERTEX_SHADER);
+        visitedList.insert(vertexBlock.name);
+    }
+
+    const gl::Shader *fragmentShader = mData.getAttachedFragmentShader();
+
+    for (const sh::InterfaceBlock &fragmentBlock : fragmentShader->getInterfaceBlocks())
+    {
+        // Only 'packed' blocks are allowed to be considered inacive.
+        if (!fragmentBlock.staticUse && fragmentBlock.layout == sh::BLOCKLAYOUT_PACKED)
+            continue;
+
+        if (visitedList.count(fragmentBlock.name) > 0)
+        {
+            for (gl::UniformBlock &block : mData.mUniformBlocks)
+            {
+                if (block.name == fragmentBlock.name)
+                {
+                    block.fragmentStaticUse = fragmentBlock.staticUse;
+                }
+            }
+
+            continue;
+        }
+
+        defineUniformBlock(fragmentBlock, GL_FRAGMENT_SHADER);
+        visitedList.insert(fragmentBlock.name);
+    }
+}
+
+void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType)
+{
+    int blockIndex                = static_cast<int>(mData.mUniformBlocks.size());
+    size_t firstBlockUniformIndex = mData.mUniforms.size();
+    DefineUniformBlockMembers(interfaceBlock.fields, "", blockIndex, &mData.mUniforms);
+    size_t lastBlockUniformIndex = mData.mUniforms.size();
+
+    std::vector<unsigned int> blockUniformIndexes;
+    for (size_t blockUniformIndex = firstBlockUniformIndex;
+         blockUniformIndex < lastBlockUniformIndex; ++blockUniformIndex)
+    {
+        blockUniformIndexes.push_back(static_cast<unsigned int>(blockUniformIndex));
+    }
+
+    if (interfaceBlock.arraySize > 0)
+    {
+        for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.arraySize; ++arrayElement)
+        {
+            UniformBlock block(interfaceBlock.name, true, arrayElement);
+            block.memberUniformIndexes = blockUniformIndexes;
+
+            if (shaderType == GL_VERTEX_SHADER)
+            {
+                block.vertexStaticUse = interfaceBlock.staticUse;
+            }
+            else
+            {
+                ASSERT(shaderType == GL_FRAGMENT_SHADER);
+                block.fragmentStaticUse = interfaceBlock.staticUse;
+            }
+
+            mData.mUniformBlocks.push_back(block);
+        }
+    }
+    else
+    {
+        UniformBlock block(interfaceBlock.name, false, 0);
+        block.memberUniformIndexes = blockUniformIndexes;
+
+        if (shaderType == GL_VERTEX_SHADER)
+        {
+            block.vertexStaticUse = interfaceBlock.staticUse;
+        }
+        else
+        {
+            ASSERT(shaderType == GL_FRAGMENT_SHADER);
+            block.fragmentStaticUse = interfaceBlock.staticUse;
+        }
+
+        mData.mUniformBlocks.push_back(block);
+    }
+}
+
+template <typename T>
+void Program::setUniformInternal(GLint location, GLsizei count, const T *v)
+{
+    const VariableLocation &locationInfo = mData.mUniformLocations[location];
+    LinkedUniform *linkedUniform         = &mData.mUniforms[locationInfo.index];
+    uint8_t *destPointer                 = linkedUniform->getDataPtrToElement(locationInfo.element);
+
+    if (VariableComponentType(linkedUniform->type) == GL_BOOL)
+    {
+        // Do a cast conversion for boolean types. From the spec:
+        // "The uniform is set to FALSE if the input value is 0 or 0.0f, and set to TRUE otherwise."
+        GLint *destAsInt = reinterpret_cast<GLint *>(destPointer);
+        for (GLsizei component = 0; component < count; ++component)
+        {
+            destAsInt[component] = (v[component] != static_cast<T>(0) ? GL_TRUE : GL_FALSE);
+        }
+    }
+    else
+    {
+        memcpy(destPointer, v, sizeof(T) * count);
+    }
+}
+
+template <size_t cols, size_t rows, typename T>
+void Program::setMatrixUniformInternal(GLint location,
+                                       GLsizei count,
+                                       GLboolean transpose,
+                                       const T *v)
+{
+    if (!transpose)
+    {
+        setUniformInternal(location, count * cols * rows, v);
+        return;
+    }
+
+    // Perform a transposing copy.
+    const VariableLocation &locationInfo = mData.mUniformLocations[location];
+    LinkedUniform *linkedUniform         = &mData.mUniforms[locationInfo.index];
+    T *destPtr = reinterpret_cast<T *>(linkedUniform->getDataPtrToElement(locationInfo.element));
+    for (size_t row = 0; row < rows; ++row)
+    {
+        for (size_t col = 0; col < cols; ++col)
+        {
+            destPtr[col * rows + row] = v[row * cols + col];
+        }
+    }
+}
+
+template <typename DestT>
+void Program::getUniformInternal(GLint location, DestT *dataOut) const
+{
+    const VariableLocation &locationInfo = mData.mUniformLocations[location];
+    const LinkedUniform &uniform         = mData.mUniforms[locationInfo.index];
+
+    const uint8_t *srcPointer = uniform.getDataPtrToElement(locationInfo.element);
+
+    GLenum componentType = VariableComponentType(uniform.type);
+    if (componentType == GLTypeToGLenum<DestT>::value)
+    {
+        memcpy(dataOut, srcPointer, uniform.getElementSize());
+        return;
+    }
+
+    int components = VariableComponentCount(uniform.type) * uniform.elementCount();
+
+    switch (componentType)
+    {
+        case GL_INT:
+            UniformStateQueryCastLoop<GLint>(dataOut, srcPointer, components);
+            break;
+        case GL_UNSIGNED_INT:
+            UniformStateQueryCastLoop<GLuint>(dataOut, srcPointer, components);
+            break;
+        case GL_BOOL:
+            UniformStateQueryCastLoop<GLboolean>(dataOut, srcPointer, components);
+            break;
+        case GL_FLOAT:
+            UniformStateQueryCastLoop<GLfloat>(dataOut, srcPointer, components);
+            break;
+        default:
+            UNREACHABLE();
+    }
+}
 }
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index b95d5b8..3dd7c46 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -189,6 +189,16 @@
         {
             return mOutputVariables;
         }
+        const std::vector<LinkedUniform> &getUniforms() const { return mUniforms; }
+        const std::vector<VariableLocation> &getUniformLocations() const
+        {
+            return mUniformLocations;
+        }
+        const std::vector<UniformBlock> &getUniformBlocks() const { return mUniformBlocks; }
+
+        const LinkedUniform *getUniformByName(const std::string &name) const;
+        GLint getUniformLocation(const std::string &name) const;
+        GLuint getUniformIndex(const std::string &name) const;
 
       private:
         friend class Program;
@@ -205,10 +215,12 @@
         std::vector<sh::Attribute> mAttributes;
         std::bitset<MAX_VERTEX_ATTRIBS> mActiveAttribLocationsMask;
 
+        std::vector<LinkedUniform> mUniforms;
+        std::vector<VariableLocation> mUniformLocations;
+        std::vector<UniformBlock> mUniformBlocks;
+
         // TODO(jmadill): use unordered/hash map when available
         std::map<int, VariableLocation> mOutputVariables;
-
-        // TODO(jmadill): move more state into Data.
     };
 
     Program(rx::ImplFactory *factory, ResourceManager *manager, GLuint handle);
@@ -251,11 +263,10 @@
     GLint getActiveUniformMaxLength();
     GLint getActiveUniformi(GLuint index, GLenum pname) const;
     bool isValidUniformLocation(GLint location) const;
-    LinkedUniform *getUniformByLocation(GLint location) const;
-    LinkedUniform *getUniformByName(const std::string &name) const;
+    const LinkedUniform &getUniformByLocation(GLint location) const;
 
-    GLint getUniformLocation(const std::string &name);
-    GLuint getUniformIndex(const std::string &name);
+    GLint getUniformLocation(const std::string &name) const;
+    GLuint getUniformIndex(const std::string &name) const;
     void setUniform1fv(GLint location, GLsizei count, const GLfloat *v);
     void setUniform2fv(GLint location, GLsizei count, const GLfloat *v);
     void setUniform3fv(GLint location, GLsizei count, const GLfloat *v);
@@ -292,7 +303,7 @@
     void bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding);
     GLuint getUniformBlockBinding(GLuint uniformBlockIndex) const;
 
-    const UniformBlock *getUniformBlockByIndex(GLuint index) const;
+    const UniformBlock &getUniformBlockByIndex(GLuint index) const;
 
     void setTransformFeedbackVaryings(GLsizei count, const GLchar *const *varyings, GLenum bufferMode);
     void getTransformFeedbackVarying(GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) const;
@@ -330,7 +341,8 @@
     static bool linkVaryings(InfoLog &infoLog,
                              const Shader *vertexShader,
                              const Shader *fragmentShader);
-    bool linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps) const;
+    bool linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps);
+    void indexUniforms();
     bool areMatchingInterfaceBlocks(gl::InfoLog &infoLog, const sh::InterfaceBlock &vertexInterfaceBlock,
                                     const sh::InterfaceBlock &fragmentInterfaceBlock);
 
@@ -352,6 +364,40 @@
     std::vector<const sh::Varying *> getMergedVaryings() const;
     void linkOutputVariables();
 
+    bool flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog);
+
+    struct VectorAndSamplerCount
+    {
+        VectorAndSamplerCount() : vectorCount(0), samplerCount(0) {}
+        VectorAndSamplerCount(const VectorAndSamplerCount &other) = default;
+        VectorAndSamplerCount &operator=(const VectorAndSamplerCount &other) = default;
+
+        VectorAndSamplerCount &operator+=(const VectorAndSamplerCount &other)
+        {
+            vectorCount += other.vectorCount;
+            samplerCount += other.samplerCount;
+            return *this;
+        }
+
+        unsigned int vectorCount;
+        unsigned int samplerCount;
+    };
+
+    VectorAndSamplerCount flattenUniform(const sh::ShaderVariable &uniform,
+                                         const std::string &fullName);
+
+    void gatherInterfaceBlockInfo();
+    void defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType);
+
+    template <typename T>
+    void setUniformInternal(GLint location, GLsizei count, const T *v);
+
+    template <size_t cols, size_t rows, typename T>
+    void setMatrixUniformInternal(GLint location, GLsizei count, GLboolean transpose, const T *v);
+
+    template <typename DestT>
+    void getUniformInternal(GLint location, DestT *dataOut) const;
+
     Data mData;
     rx::ProgramImpl *mProgram;
 
diff --git a/src/libANGLE/Uniform.cpp b/src/libANGLE/Uniform.cpp
index 24e7f11..3a20f4c 100644
--- a/src/libANGLE/Uniform.cpp
+++ b/src/libANGLE/Uniform.cpp
@@ -13,55 +13,51 @@
 namespace gl
 {
 
-LinkedUniform::LinkedUniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize,
-                             const int blockIndex, const sh::BlockMemberInfo &blockInfo)
-    : type(type),
-      precision(precision),
-      name(name),
-      arraySize(arraySize),
-      blockIndex(blockIndex),
-      blockInfo(blockInfo),
-      data(NULL),
-      dirty(true),
-      psRegisterIndex(GL_INVALID_INDEX),
-      vsRegisterIndex(GL_INVALID_INDEX),
-      registerCount(0),
-      registerElement(0)
+LinkedUniform::LinkedUniform()
+    : blockIndex(-1), blockInfo(sh::BlockMemberInfo::getDefaultBlockInfo())
 {
-    // We use data storage for default block uniforms to cache values that are sent to D3D during rendering
-    // Uniform blocks/buffers are treated separately by the Renderer (ES3 path only)
-    if (isInDefaultBlock())
-    {
-        size_t bytes = dataSize();
-        data = new unsigned char[bytes];
-        memset(data, 0, bytes);
-        registerCount = VariableRowCount(type) * elementCount();
-    }
+}
+
+LinkedUniform::LinkedUniform(GLenum typeIn,
+                             GLenum precisionIn,
+                             const std::string &nameIn,
+                             unsigned int arraySizeIn,
+                             const int blockIndexIn,
+                             const sh::BlockMemberInfo &blockInfoIn)
+    : blockIndex(blockIndexIn), blockInfo(blockInfoIn)
+{
+    type      = typeIn;
+    precision = precisionIn;
+    name      = nameIn;
+    arraySize = arraySizeIn;
+}
+
+LinkedUniform::LinkedUniform(const sh::Uniform &uniform)
+    : sh::Uniform(uniform), blockIndex(-1), blockInfo(sh::BlockMemberInfo::getDefaultBlockInfo())
+{
+}
+
+LinkedUniform::LinkedUniform(const LinkedUniform &uniform)
+    : sh::Uniform(uniform), blockIndex(uniform.blockIndex), blockInfo(uniform.blockInfo)
+{
+    // This function is not intended to be called during runtime.
+    ASSERT(uniform.mLazyData.empty());
+}
+
+LinkedUniform &LinkedUniform::operator=(const LinkedUniform &uniform)
+{
+    // This function is not intended to be called during runtime.
+    ASSERT(uniform.mLazyData.empty());
+
+    sh::Uniform::operator=(uniform);
+    blockIndex           = uniform.blockIndex;
+    blockInfo            = uniform.blockInfo;
+
+    return *this;
 }
 
 LinkedUniform::~LinkedUniform()
 {
-    delete[] data;
-}
-
-bool LinkedUniform::isArray() const
-{
-    return arraySize > 0;
-}
-
-unsigned int LinkedUniform::elementCount() const
-{
-    return arraySize > 0 ? arraySize : 1;
-}
-
-bool LinkedUniform::isReferencedByVertexShader() const
-{
-    return vsRegisterIndex != GL_INVALID_INDEX;
-}
-
-bool LinkedUniform::isReferencedByFragmentShader() const
-{
-    return psRegisterIndex != GL_INVALID_INDEX;
 }
 
 bool LinkedUniform::isInDefaultBlock() const
@@ -72,7 +68,30 @@
 size_t LinkedUniform::dataSize() const
 {
     ASSERT(type != GL_STRUCT_ANGLEX);
-    return VariableInternalSize(type) * elementCount();
+    if (mLazyData.empty())
+    {
+        mLazyData.resize(VariableExternalSize(type) * elementCount());
+        ASSERT(!mLazyData.empty());
+    }
+
+    return mLazyData.size();
+}
+
+uint8_t *LinkedUniform::data()
+{
+    if (mLazyData.empty())
+    {
+        // dataSize() will init the data store.
+        size_t size = dataSize();
+        memset(mLazyData.data(), 0, size);
+    }
+
+    return mLazyData.data();
+}
+
+const uint8_t *LinkedUniform::data() const
+{
+    return const_cast<LinkedUniform *>(this)->data();
 }
 
 bool LinkedUniform::isSampler() const
@@ -80,33 +99,48 @@
     return IsSamplerType(type);
 }
 
-bool LinkedUniform::isBuiltIn() const
+bool LinkedUniform::isField() const
 {
-    return name.compare(0, 3, "gl_") == 0;
+    return name.find('.') != std::string::npos;
 }
 
-UniformBlock::UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize)
-    : name(name),
-      elementIndex(elementIndex),
-      dataSize(dataSize),
+size_t LinkedUniform::getElementSize() const
+{
+    return VariableExternalSize(type);
+}
+
+uint8_t *LinkedUniform::getDataPtrToElement(size_t elementIndex)
+{
+    ASSERT((!isArray() && elementIndex == 0) || (isArray() && elementIndex < arraySize));
+    return data() + getElementSize() * elementIndex;
+}
+
+const uint8_t *LinkedUniform::getDataPtrToElement(size_t elementIndex) const
+{
+    return const_cast<LinkedUniform *>(this)->getDataPtrToElement(elementIndex);
+}
+
+UniformBlock::UniformBlock()
+    : isArray(false),
+      arrayElement(0),
+      dataSize(0),
+      vertexStaticUse(false),
+      fragmentStaticUse(false),
       psRegisterIndex(GL_INVALID_INDEX),
       vsRegisterIndex(GL_INVALID_INDEX)
 {
 }
 
-bool UniformBlock::isArrayElement() const
+UniformBlock::UniformBlock(const std::string &nameIn, bool isArrayIn, unsigned int arrayElementIn)
+    : name(nameIn),
+      isArray(isArrayIn),
+      arrayElement(arrayElementIn),
+      dataSize(0),
+      vertexStaticUse(false),
+      fragmentStaticUse(false),
+      psRegisterIndex(GL_INVALID_INDEX),
+      vsRegisterIndex(GL_INVALID_INDEX)
 {
-    return elementIndex != GL_INVALID_INDEX;
-}
-
-bool UniformBlock::isReferencedByVertexShader() const
-{
-    return vsRegisterIndex != GL_INVALID_INDEX;
-}
-
-bool UniformBlock::isReferencedByFragmentShader() const
-{
-    return psRegisterIndex != GL_INVALID_INDEX;
 }
 
 }
diff --git a/src/libANGLE/Uniform.h b/src/libANGLE/Uniform.h
index b73fc9d..040c978 100644
--- a/src/libANGLE/Uniform.h
+++ b/src/libANGLE/Uniform.h
@@ -12,6 +12,7 @@
 
 #include "angle_gl.h"
 #include "common/debug.h"
+#include "common/MemoryBuffer.h"
 #include "compiler/translator/blocklayout.h"
 #include "libANGLE/angletypes.h"
 
@@ -19,53 +20,47 @@
 {
 
 // Helper struct representing a single shader uniform
-struct LinkedUniform : angle::NonCopyable
+struct LinkedUniform : public sh::Uniform
 {
+    LinkedUniform();
     LinkedUniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, const int blockIndex, const sh::BlockMemberInfo &blockInfo);
-
+    LinkedUniform(const sh::Uniform &uniform);
+    LinkedUniform(const LinkedUniform &uniform);
+    LinkedUniform &operator=(const LinkedUniform &uniform);
     ~LinkedUniform();
 
-    bool isArray() const;
-    unsigned int elementCount() const;
-    bool isReferencedByVertexShader() const;
-    bool isReferencedByFragmentShader() const;
-    bool isInDefaultBlock() const;
     size_t dataSize() const;
+    uint8_t *data();
+    const uint8_t *data() const;
     bool isSampler() const;
-    bool isBuiltIn() const;
+    bool isInDefaultBlock() const;
+    bool isField() const;
+    size_t getElementSize() const;
+    uint8_t *getDataPtrToElement(size_t elementIndex);
+    const uint8_t *getDataPtrToElement(size_t elementIndex) const;
 
-    const GLenum type;
-    const GLenum precision;
-    const std::string name;
-    const unsigned int arraySize;
-    const int blockIndex;
-    const sh::BlockMemberInfo blockInfo;
+    int blockIndex;
+    sh::BlockMemberInfo blockInfo;
 
-    unsigned char *data;
-    bool dirty;
-
-    unsigned int psRegisterIndex;
-    unsigned int vsRegisterIndex;
-    unsigned int registerCount;
-
-    // Register "elements" are used for uniform structs in ES3, to appropriately identify single uniforms
-    // inside aggregate types, which are packed according C-like structure rules.
-    unsigned int registerElement;
+  private:
+    mutable rx::MemoryBuffer mLazyData;
 };
 
 // Helper struct representing a single shader uniform block
-struct UniformBlock : angle::NonCopyable
+struct UniformBlock
 {
-    // use GL_INVALID_INDEX for non-array elements
-    UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize);
+    UniformBlock();
+    UniformBlock(const std::string &nameIn, bool isArrayIn, unsigned int arrayElementIn);
+    UniformBlock(const UniformBlock &other) = default;
+    UniformBlock &operator=(const UniformBlock &other) = default;
 
-    bool isArrayElement() const;
-    bool isReferencedByVertexShader() const;
-    bool isReferencedByFragmentShader() const;
+    std::string name;
+    bool isArray;
+    unsigned int arrayElement;
+    unsigned int dataSize;
 
-    const std::string name;
-    const unsigned int elementIndex;
-    const unsigned int dataSize;
+    bool vertexStaticUse;
+    bool fragmentStaticUse;
 
     std::vector<unsigned int> memberUniformIndexes;
 
diff --git a/src/libANGLE/queryconversions.cpp b/src/libANGLE/queryconversions.cpp
index d18a8e1..5633ce4 100644
--- a/src/libANGLE/queryconversions.cpp
+++ b/src/libANGLE/queryconversions.cpp
@@ -6,32 +6,24 @@
 
 // queryconversions.cpp: Implementation of state query cast conversions
 
+#include "libANGLE/queryconversions.h"
+
 #include "libANGLE/Context.h"
 #include "common/utilities.h"
 
 namespace gl
 {
 
-// Helper class for converting a GL type to a GLenum:
-// We can't use CastStateValueEnum generally, because of GLboolean + GLubyte overlap.
-// We restrict our use to CastStateValue, where it eliminates duplicate parameters.
+namespace
+{
 
-template <typename GLType>
-struct CastStateValueEnum { static GLenum mEnumForType; };
-
-template <> GLenum CastStateValueEnum<GLint>::mEnumForType      = GL_INT;
-template <> GLenum CastStateValueEnum<GLuint>::mEnumForType     = GL_UNSIGNED_INT;
-template <> GLenum CastStateValueEnum<GLboolean>::mEnumForType  = GL_BOOL;
-template <> GLenum CastStateValueEnum<GLint64>::mEnumForType    = GL_INT_64_ANGLEX;
-template <> GLenum CastStateValueEnum<GLfloat>::mEnumForType    = GL_FLOAT;
-
-static GLint64 ExpandFloatToInteger(GLfloat value)
+GLint64 ExpandFloatToInteger(GLfloat value)
 {
     return static_cast<GLint64>((static_cast<double>(0xFFFFFFFFULL) * value - 1.0) / 2.0);
 }
 
 template <typename QueryT>
-static QueryT ClampToQueryRange(GLint64 value)
+QueryT ClampToQueryRange(GLint64 value)
 {
     const GLint64 min = static_cast<GLint64>(std::numeric_limits<QueryT>::min());
     const GLint64 max = static_cast<GLint64>(std::numeric_limits<QueryT>::max());
@@ -41,8 +33,8 @@
 template <typename QueryT, typename NativeT>
 QueryT CastStateValueToInt(GLenum pname, NativeT value)
 {
-    GLenum queryType = CastStateValueEnum<QueryT>::mEnumForType;
-    GLenum nativeType = CastStateValueEnum<NativeT>::mEnumForType;
+    GLenum queryType  = GLTypeToGLenum<QueryT>::value;
+    GLenum nativeType = GLTypeToGLenum<NativeT>::value;
 
     if (nativeType == GL_FLOAT)
     {
@@ -72,18 +64,37 @@
 template <typename QueryT, typename NativeT>
 QueryT CastStateValue(GLenum pname, NativeT value)
 {
-    GLenum queryType = CastStateValueEnum<QueryT>::mEnumForType;
+    GLenum queryType = GLTypeToGLenum<QueryT>::value;
 
     switch (queryType)
     {
-      case GL_INT:              return CastStateValueToInt<QueryT, NativeT>(pname, value);
-      case GL_INT_64_ANGLEX:    return CastStateValueToInt<QueryT, NativeT>(pname, value);
-      case GL_FLOAT:            return static_cast<QueryT>(value);
-      case GL_BOOL:             return static_cast<QueryT>(value == static_cast<NativeT>(0) ? GL_FALSE : GL_TRUE);
-      default: UNREACHABLE();   return 0;
+        case GL_INT:
+            return CastStateValueToInt<QueryT, NativeT>(pname, value);
+        case GL_INT_64_ANGLEX:
+            return CastStateValueToInt<QueryT, NativeT>(pname, value);
+        case GL_FLOAT:
+            return static_cast<QueryT>(value);
+        case GL_BOOL:
+            return static_cast<QueryT>(value == static_cast<NativeT>(0) ? GL_FALSE : GL_TRUE);
+        default:
+            UNREACHABLE();
+            return 0;
     }
 }
 
+}  // anonymous namespace
+
+template <>
+GLenum GLTypeToGLenum<GLint>::value = GL_INT;
+template <>
+GLenum GLTypeToGLenum<GLuint>::value = GL_UNSIGNED_INT;
+template <>
+GLenum GLTypeToGLenum<GLboolean>::value = GL_BOOL;
+template <>
+GLenum GLTypeToGLenum<GLint64>::value = GL_INT_64_ANGLEX;
+template <>
+GLenum GLTypeToGLenum<GLfloat>::value = GL_FLOAT;
+
 template <typename QueryT>
 void CastStateValues(Context *context, GLenum nativeType, GLenum pname,
                      unsigned int numParams, QueryT *outParams)
diff --git a/src/libANGLE/queryconversions.h b/src/libANGLE/queryconversions.h
index da7047f..e0fdbe1 100644
--- a/src/libANGLE/queryconversions.h
+++ b/src/libANGLE/queryconversions.h
@@ -6,8 +6,25 @@
 
 // queryconversions.h: Declaration of state query cast conversions
 
+#ifndef LIBANGLE_QUERY_CONVERSIONS_H_
+#define LIBANGLE_QUERY_CONVERSIONS_H_
+
+#include "angle_gl.h"
+#include "common/angleutils.h"
+
 namespace gl
 {
+class Context;
+
+// Helper class for converting a GL type to a GLenum:
+// We can't use CastStateValueEnum generally, because of GLboolean + GLubyte overlap.
+// We restrict our use to CastStateValue, where it eliminates duplicate parameters.
+
+template <typename GLType>
+struct GLTypeToGLenum
+{
+    static GLenum value;
+};
 
 // The GL state query API types are: bool, int, uint, float, int64
 template <typename QueryT>
@@ -15,3 +32,5 @@
                      unsigned int numParams, QueryT *outParams);
 
 }
+
+#endif  // LIBANGLE_QUERY_CONVERSIONS_H_
diff --git a/src/libANGLE/renderer/ProgramImpl.cpp b/src/libANGLE/renderer/ProgramImpl.cpp
deleted file mode 100644
index 051ef16..0000000
--- a/src/libANGLE/renderer/ProgramImpl.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-//
-// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-// ProgramD3D.cpp: Defines the rx::ProgramD3D class which implements rx::ProgramImpl.
-
-#include "libANGLE/renderer/ProgramImpl.h"
-
-#include "common/utilities.h"
-
-namespace rx
-{
-
-LinkResult::LinkResult(bool linkSuccess, const gl::Error &error)
-    : linkSuccess(linkSuccess),
-      error(error)
-{
-}
-
-ProgramImpl::ProgramImpl(const gl::Program::Data &data) : mData(data)
-{
-}
-
-ProgramImpl::~ProgramImpl()
-{
-    // Ensure that reset was called by the inherited class during destruction
-    ASSERT(mUniformIndex.size() == 0);
-}
-
-gl::LinkedUniform *ProgramImpl::getUniformByLocation(GLint location) const
-{
-    ASSERT(location >= 0 && mUniformIndex.find(location) != mUniformIndex.end());
-    return mUniforms[mUniformIndex.at(location).index];
-}
-
-gl::LinkedUniform *ProgramImpl::getUniformByName(const std::string &name) const
-{
-    for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
-    {
-        if (mUniforms[uniformIndex]->name == name)
-        {
-            return mUniforms[uniformIndex];
-        }
-    }
-
-    return NULL;
-}
-
-gl::UniformBlock *ProgramImpl::getUniformBlockByIndex(GLuint blockIndex) const
-{
-    ASSERT(blockIndex < mUniformBlocks.size());
-    return mUniformBlocks[blockIndex];
-}
-
-GLint ProgramImpl::getUniformLocation(const std::string &name) const
-{
-    size_t subscript = GL_INVALID_INDEX;
-    std::string baseName = gl::ParseUniformName(name, &subscript);
-
-    for (const auto &info : mUniformIndex)
-    {
-        GLuint location = info.first;
-        const gl::VariableLocation &uniform = info.second;
-
-        if (uniform.name == baseName)
-        {
-            const bool isArray = mUniforms[uniform.index]->isArray();
-
-            if ((isArray && uniform.element == subscript) ||
-                (subscript == GL_INVALID_INDEX))
-            {
-                return location;
-            }
-        }
-    }
-
-    return -1;
-}
-
-GLuint ProgramImpl::getUniformIndex(const std::string &name) const
-{
-    size_t subscript = GL_INVALID_INDEX;
-    std::string baseName = gl::ParseUniformName(name, &subscript);
-
-    // The app is not allowed to specify array indices other than 0 for arrays of basic types
-    if (subscript != 0 && subscript != GL_INVALID_INDEX)
-    {
-        return GL_INVALID_INDEX;
-    }
-
-    unsigned int numUniforms = static_cast<unsigned int>(mUniforms.size());
-    for (unsigned int index = 0; index < numUniforms; index++)
-    {
-        if (mUniforms[index]->name == baseName)
-        {
-            if (mUniforms[index]->isArray() || subscript == GL_INVALID_INDEX)
-            {
-                return index;
-            }
-        }
-    }
-
-    return GL_INVALID_INDEX;
-}
-
-GLuint ProgramImpl::getUniformBlockIndex(const std::string &name) const
-{
-    size_t subscript = GL_INVALID_INDEX;
-    std::string baseName = gl::ParseUniformName(name, &subscript);
-
-    unsigned int numUniformBlocks = static_cast<unsigned int>(mUniformBlocks.size());
-    for (unsigned int blockIndex = 0; blockIndex < numUniformBlocks; blockIndex++)
-    {
-        const gl::UniformBlock &uniformBlock = *mUniformBlocks[blockIndex];
-        if (uniformBlock.name == baseName)
-        {
-            const bool arrayElementZero = (subscript == GL_INVALID_INDEX && uniformBlock.elementIndex == 0);
-            if (subscript == uniformBlock.elementIndex || arrayElementZero)
-            {
-                return blockIndex;
-            }
-        }
-    }
-
-    return GL_INVALID_INDEX;
-}
-
-void ProgramImpl::reset()
-{
-    SafeDeleteContainer(mUniforms);
-    mUniformIndex.clear();
-    SafeDeleteContainer(mUniformBlocks);
-}
-
-}
diff --git a/src/libANGLE/renderer/ProgramImpl.h b/src/libANGLE/renderer/ProgramImpl.h
index 94da0c7..47fb9be 100644
--- a/src/libANGLE/renderer/ProgramImpl.h
+++ b/src/libANGLE/renderer/ProgramImpl.h
@@ -23,16 +23,17 @@
 
 struct LinkResult
 {
+    LinkResult(bool linkSuccess, const gl::Error &error) : linkSuccess(linkSuccess), error(error) {}
+
     bool linkSuccess;
     gl::Error error;
-    LinkResult(bool linkSuccess, const gl::Error &error);
 };
 
 class ProgramImpl : angle::NonCopyable
 {
   public:
-    ProgramImpl(const gl::Program::Data &data);
-    virtual ~ProgramImpl();
+    ProgramImpl(const gl::Program::Data &data) : mData(data) {}
+    virtual ~ProgramImpl() {}
 
     virtual int getShaderVersion() const = 0;
 
@@ -65,41 +66,16 @@
     virtual void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) = 0;
     virtual void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) = 0;
 
-    virtual void getUniformfv(GLint location, GLfloat *params) = 0;
-    virtual void getUniformiv(GLint location, GLint *params) = 0;
-    virtual void getUniformuiv(GLint location, GLuint *params) = 0;
-
     // TODO: The following functions are possibly only applicable to D3D backends. The should be carefully evaluated to
     // determine if they can be removed from this interface.
     virtual bool validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps) = 0;
 
-    const std::vector<gl::LinkedUniform*> &getUniforms() const { return mUniforms; }
-    const std::map<GLuint, gl::VariableLocation> &getUniformIndices() const { return mUniformIndex; }
-    const std::vector<gl::UniformBlock*> &getUniformBlocks() const { return mUniformBlocks; }
-
-    std::vector<gl::LinkedUniform*> &getUniforms() { return mUniforms; }
-    std::map<GLuint, gl::VariableLocation> &getUniformIndices() { return mUniformIndex; }
-    std::vector<gl::UniformBlock*> &getUniformBlocks() { return mUniformBlocks; }
-
-    gl::LinkedUniform *getUniformByLocation(GLint location) const;
-    gl::LinkedUniform *getUniformByName(const std::string &name) const;
-    gl::UniformBlock *getUniformBlockByIndex(GLuint blockIndex) const;
-
-    GLint getUniformLocation(const std::string &name) const;
-    GLuint getUniformIndex(const std::string &name) const;
-    GLuint getUniformBlockIndex(const std::string &name) const;
-
-    virtual void reset();
+    // Gather uniform block active uniform indices, and uniform block offset info.
+    virtual void gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
+                                        std::vector<gl::LinkedUniform> *uniforms) = 0;
 
   protected:
     const gl::Program::Data &mData;
-
-    std::vector<gl::LinkedUniform*> mUniforms;
-
-    // TODO: use a hash map
-    std::map<GLuint, gl::VariableLocation> mUniformIndex;
-
-    std::vector<gl::UniformBlock*> mUniformBlocks;
 };
 
 }
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 13415a3..cfa7f8a 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -171,8 +171,92 @@
     return packedVaryings;
 }
 
+template <typename VarT>
+void GetUniformBlockInfo(const std::vector<VarT> &fields,
+                         const std::string &prefix,
+                         sh::BlockLayoutEncoder *encoder,
+                         bool inRowMajorLayout,
+                         std::map<std::string, sh::BlockMemberInfo> *blockInfoOut)
+{
+    for (const VarT &field : fields)
+    {
+        const std::string &fieldName = (prefix.empty() ? field.name : prefix + "." + field.name);
+
+        if (field.isStruct())
+        {
+            bool rowMajorLayout = (inRowMajorLayout || IsRowMajorLayout(field));
+
+            for (unsigned int arrayElement = 0; arrayElement < field.elementCount(); arrayElement++)
+            {
+                encoder->enterAggregateType();
+
+                const std::string uniformElementName =
+                    fieldName + (field.isArray() ? ArrayString(arrayElement) : "");
+                GetUniformBlockInfo(field.fields, uniformElementName, encoder, rowMajorLayout,
+                                    blockInfoOut);
+
+                encoder->exitAggregateType();
+            }
+        }
+        else
+        {
+            bool isRowMajorMatrix = (gl::IsMatrixType(field.type) && inRowMajorLayout);
+            (*blockInfoOut)[fieldName] =
+                encoder->encodeType(field.type, field.arraySize, isRowMajorMatrix);
+        }
+    }
+}
+
 }  // anonymous namespace
 
+D3DUniform::D3DUniform(GLenum typeIn,
+                       const std::string &nameIn,
+                       unsigned int arraySizeIn,
+                       bool defaultBlock)
+    : type(typeIn),
+      name(nameIn),
+      arraySize(arraySizeIn),
+      data(nullptr),
+      dirty(true),
+      psRegisterIndex(GL_INVALID_INDEX),
+      vsRegisterIndex(GL_INVALID_INDEX),
+      registerCount(0),
+      registerElement(0)
+{
+    // We use data storage for default block uniforms to cache values that are sent to D3D during
+    // rendering
+    // Uniform blocks/buffers are treated separately by the Renderer (ES3 path only)
+    if (defaultBlock)
+    {
+        size_t bytes = gl::VariableInternalSize(type) * elementCount();
+        data = new uint8_t[bytes];
+        memset(data, 0, bytes);
+
+        // TODO(jmadill): is this correct with non-square matrices?
+        registerCount = gl::VariableRowCount(type) * elementCount();
+    }
+}
+
+D3DUniform::~D3DUniform()
+{
+    SafeDeleteArray(data);
+}
+
+bool D3DUniform::isSampler() const
+{
+    return gl::IsSamplerType(type);
+}
+
+bool D3DUniform::isReferencedByVertexShader() const
+{
+    return vsRegisterIndex != GL_INVALID_INDEX;
+}
+
+bool D3DUniform::isReferencedByFragmentShader() const
+{
+    return psRegisterIndex != GL_INVALID_INDEX;
+}
+
 ProgramD3D::VertexExecutable::VertexExecutable(const gl::InputLayout &inputLayout,
                                                const Signature &signature,
                                                ShaderExecutableD3D *shaderExecutable)
@@ -356,47 +440,45 @@
     mDirtySamplerMapping = false;
 
     // Retrieve sampler uniform values
-    for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
+    for (const D3DUniform *d3dUniform : mD3DUniforms)
     {
-        gl::LinkedUniform *targetUniform = mUniforms[uniformIndex];
+        if (!d3dUniform->dirty)
+            continue;
 
-        if (targetUniform->dirty)
+        if (!d3dUniform->isSampler())
+            continue;
+
+        int count = d3dUniform->elementCount();
+        const GLint(*v)[4] = reinterpret_cast<const GLint(*)[4]>(d3dUniform->data);
+
+        if (d3dUniform->isReferencedByFragmentShader())
         {
-            if (gl::IsSamplerType(targetUniform->type))
+            unsigned int firstIndex = d3dUniform->psRegisterIndex;
+
+            for (int i = 0; i < count; i++)
             {
-                int count = targetUniform->elementCount();
-                GLint (*v)[4] = reinterpret_cast<GLint(*)[4]>(targetUniform->data);
+                unsigned int samplerIndex = firstIndex + i;
 
-                if (targetUniform->isReferencedByFragmentShader())
+                if (samplerIndex < mSamplersPS.size())
                 {
-                    unsigned int firstIndex = targetUniform->psRegisterIndex;
-
-                    for (int i = 0; i < count; i++)
-                    {
-                        unsigned int samplerIndex = firstIndex + i;
-
-                        if (samplerIndex < mSamplersPS.size())
-                        {
-                            ASSERT(mSamplersPS[samplerIndex].active);
-                            mSamplersPS[samplerIndex].logicalTextureUnit = v[i][0];
-                        }
-                    }
+                    ASSERT(mSamplersPS[samplerIndex].active);
+                    mSamplersPS[samplerIndex].logicalTextureUnit = v[i][0];
                 }
+            }
+        }
 
-                if (targetUniform->isReferencedByVertexShader())
+        if (d3dUniform->isReferencedByVertexShader())
+        {
+            unsigned int firstIndex = d3dUniform->vsRegisterIndex;
+
+            for (int i = 0; i < count; i++)
+            {
+                unsigned int samplerIndex = firstIndex + i;
+
+                if (samplerIndex < mSamplersVS.size())
                 {
-                    unsigned int firstIndex = targetUniform->vsRegisterIndex;
-
-                    for (int i = 0; i < count; i++)
-                    {
-                        unsigned int samplerIndex = firstIndex + i;
-
-                        if (samplerIndex < mSamplersVS.size())
-                        {
-                            ASSERT(mSamplersVS[samplerIndex].active);
-                            mSamplersVS[samplerIndex].logicalTextureUnit = v[i][0];
-                        }
-                    }
+                    ASSERT(mSamplersVS[samplerIndex].active);
+                    mSamplersVS[samplerIndex].logicalTextureUnit = v[i][0];
                 }
             }
         }
@@ -505,6 +587,8 @@
 
 LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream)
 {
+    reset();
+
     DeviceIdentifier binaryDeviceIdentifier = { 0 };
     stream->readBytes(reinterpret_cast<unsigned char*>(&binaryDeviceIdentifier), sizeof(DeviceIdentifier));
 
@@ -559,79 +643,21 @@
         return LinkResult(false, gl::Error(GL_NO_ERROR));
     }
 
-    mUniforms.resize(uniformCount);
+    const auto &linkedUniforms = mData.getUniforms();
+    ASSERT(mD3DUniforms.empty());
     for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; uniformIndex++)
     {
-        GLenum type = stream->readInt<GLenum>();
-        GLenum precision = stream->readInt<GLenum>();
-        std::string name = stream->readString();
-        unsigned int arraySize = stream->readInt<unsigned int>();
-        int blockIndex = stream->readInt<int>();
+        const gl::LinkedUniform &linkedUniform = linkedUniforms[uniformIndex];
 
-        int offset = stream->readInt<int>();
-        int arrayStride = stream->readInt<int>();
-        int matrixStride = stream->readInt<int>();
-        bool isRowMajorMatrix = stream->readBool();
+        D3DUniform *d3dUniform =
+            new D3DUniform(linkedUniform.type, linkedUniform.name, linkedUniform.arraySize,
+                           linkedUniform.isInDefaultBlock());
+        stream->readInt(&d3dUniform->psRegisterIndex);
+        stream->readInt(&d3dUniform->vsRegisterIndex);
+        stream->readInt(&d3dUniform->registerCount);
+        stream->readInt(&d3dUniform->registerElement);
 
-        const sh::BlockMemberInfo blockInfo(offset, arrayStride, matrixStride, isRowMajorMatrix);
-
-        gl::LinkedUniform *uniform = new gl::LinkedUniform(type, precision, name, arraySize, blockIndex, blockInfo);
-
-        stream->readInt(&uniform->psRegisterIndex);
-        stream->readInt(&uniform->vsRegisterIndex);
-        stream->readInt(&uniform->registerCount);
-        stream->readInt(&uniform->registerElement);
-
-        mUniforms[uniformIndex] = uniform;
-    }
-
-    const unsigned int uniformIndexCount = stream->readInt<unsigned int>();
-    if (stream->error())
-    {
-        infoLog << "Invalid program binary.";
-        return LinkResult(false, gl::Error(GL_NO_ERROR));
-    }
-
-    for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount; uniformIndexIndex++)
-    {
-        GLuint location;
-        stream->readInt(&location);
-
-        gl::VariableLocation variable;
-        stream->readString(&variable.name);
-        stream->readInt(&variable.element);
-        stream->readInt(&variable.index);
-
-        mUniformIndex[location] = variable;
-    }
-
-    unsigned int uniformBlockCount = stream->readInt<unsigned int>();
-    if (stream->error())
-    {
-        infoLog << "Invalid program binary.";
-        return LinkResult(false, gl::Error(GL_NO_ERROR));
-    }
-
-    mUniformBlocks.resize(uniformBlockCount);
-    for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; ++uniformBlockIndex)
-    {
-        std::string name = stream->readString();
-        unsigned int elementIndex = stream->readInt<unsigned int>();
-        unsigned int dataSize = stream->readInt<unsigned int>();
-
-        gl::UniformBlock *uniformBlock = new gl::UniformBlock(name, elementIndex, dataSize);
-
-        stream->readInt(&uniformBlock->psRegisterIndex);
-        stream->readInt(&uniformBlock->vsRegisterIndex);
-
-        unsigned int numMembers = stream->readInt<unsigned int>();
-        uniformBlock->memberUniformIndexes.resize(numMembers);
-        for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
-        {
-            stream->readInt(&uniformBlock->memberUniformIndexes[blockMemberIndex]);
-        }
-
-        mUniformBlocks[uniformBlockIndex] = uniformBlock;
+        mD3DUniforms.push_back(d3dUniform);
     }
 
     const unsigned int transformFeedbackVaryingCount = stream->readInt<unsigned int>();
@@ -805,59 +831,18 @@
     stream->writeInt(mUsedVertexSamplerRange);
     stream->writeInt(mUsedPixelSamplerRange);
 
-    stream->writeInt(mUniforms.size());
-    for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); ++uniformIndex)
+    stream->writeInt(mD3DUniforms.size());
+    for (size_t uniformIndex = 0; uniformIndex < mD3DUniforms.size(); ++uniformIndex)
     {
-        const gl::LinkedUniform &uniform = *mUniforms[uniformIndex];
+        const D3DUniform &uniform = *mD3DUniforms[uniformIndex];
 
-        stream->writeInt(uniform.type);
-        stream->writeInt(uniform.precision);
-        stream->writeString(uniform.name);
-        stream->writeInt(uniform.arraySize);
-        stream->writeInt(uniform.blockIndex);
-
-        stream->writeInt(uniform.blockInfo.offset);
-        stream->writeInt(uniform.blockInfo.arrayStride);
-        stream->writeInt(uniform.blockInfo.matrixStride);
-        stream->writeInt(uniform.blockInfo.isRowMajorMatrix);
-
+        // Type, name and arraySize are redundant, so aren't stored in the binary.
         stream->writeInt(uniform.psRegisterIndex);
         stream->writeInt(uniform.vsRegisterIndex);
         stream->writeInt(uniform.registerCount);
         stream->writeInt(uniform.registerElement);
     }
 
-    stream->writeInt(mUniformIndex.size());
-    for (const auto &uniform : mUniformIndex)
-    {
-        GLuint location = uniform.first;
-        stream->writeInt(location);
-
-        const gl::VariableLocation &variable = uniform.second;
-        stream->writeString(variable.name);
-        stream->writeInt(variable.element);
-        stream->writeInt(variable.index);
-    }
-
-    stream->writeInt(mUniformBlocks.size());
-    for (size_t uniformBlockIndex = 0; uniformBlockIndex < mUniformBlocks.size(); ++uniformBlockIndex)
-    {
-        const gl::UniformBlock& uniformBlock = *mUniformBlocks[uniformBlockIndex];
-
-        stream->writeString(uniformBlock.name);
-        stream->writeInt(uniformBlock.elementIndex);
-        stream->writeInt(uniformBlock.dataSize);
-
-        stream->writeInt(uniformBlock.memberUniformIndexes.size());
-        for (unsigned int blockMemberIndex = 0; blockMemberIndex < uniformBlock.memberUniformIndexes.size(); blockMemberIndex++)
-        {
-            stream->writeInt(uniformBlock.memberUniformIndexes[blockMemberIndex]);
-        }
-
-        stream->writeInt(uniformBlock.psRegisterIndex);
-        stream->writeInt(uniformBlock.vsRegisterIndex);
-    }
-
     stream->writeInt(mTransformFeedbackLinkedVaryings.size());
     for (size_t i = 0; i < mTransformFeedbackLinkedVaryings.size(); i++)
     {
@@ -1126,6 +1111,18 @@
 
 LinkResult ProgramD3D::link(const gl::Data &data, gl::InfoLog &infoLog)
 {
+    reset();
+
+    // TODO(jmadill): structures containing samplers
+    for (const gl::LinkedUniform &linkedUniform : mData.getUniforms())
+    {
+        if (linkedUniform.isSampler() && linkedUniform.isField())
+        {
+            infoLog << "Structures containing samplers not currently supported in D3D.";
+            return LinkResult(false, gl::Error(GL_NO_ERROR));
+        }
+    }
+
     const gl::Shader *vertexShader   = mData.getAttachedVertexShader();
     const gl::Shader *fragmentShader = mData.getAttachedFragmentShader();
 
@@ -1175,12 +1172,7 @@
 
     initSemanticIndex();
 
-    if (!defineUniforms(infoLog, *data.caps))
-    {
-        return LinkResult(false, gl::Error(GL_NO_ERROR));
-    }
-
-    defineUniformBlocks(*data.caps);
+    assignUniformRegisters();
 
     gatherTransformFeedbackVaryings(linkedVaryings);
 
@@ -1200,24 +1192,96 @@
     return validateSamplers(infoLog, caps);
 }
 
+void ProgramD3D::gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
+                                        std::vector<gl::LinkedUniform> *uniforms)
+{
+    const gl::Shader *vertexShader = mData.getAttachedVertexShader();
+
+    BlockInfoMap blockInfo;
+    std::map<std::string, size_t> blockDataSizes;
+
+    for (const sh::InterfaceBlock &vertexBlock : vertexShader->getInterfaceBlocks())
+    {
+        if (!vertexBlock.staticUse && vertexBlock.layout == sh::BLOCKLAYOUT_PACKED)
+            continue;
+
+        if (blockDataSizes.count(vertexBlock.name) > 0)
+            continue;
+
+        size_t dataSize                  = defineUniformBlock(vertexBlock, &blockInfo);
+        blockDataSizes[vertexBlock.name] = dataSize;
+    }
+
+    const gl::Shader *fragmentShader = mData.getAttachedFragmentShader();
+
+    for (const sh::InterfaceBlock &fragmentBlock : fragmentShader->getInterfaceBlocks())
+    {
+        if (!fragmentBlock.staticUse && fragmentBlock.layout == sh::BLOCKLAYOUT_PACKED)
+            continue;
+
+        if (blockDataSizes.count(fragmentBlock.name) > 0)
+            continue;
+
+        size_t dataSize                    = defineUniformBlock(fragmentBlock, &blockInfo);
+        blockDataSizes[fragmentBlock.name] = dataSize;
+    }
+
+    // Copy block info out to uniforms.
+    for (gl::LinkedUniform &linkedUniform : *uniforms)
+    {
+        const auto &infoEntry = blockInfo.find(linkedUniform.name);
+
+        if (infoEntry != blockInfo.end())
+        {
+            linkedUniform.blockInfo = infoEntry->second;
+        }
+    }
+
+    // Assign registers and update sizes.
+    const ShaderD3D *vertexShaderD3D   = GetImplAs<ShaderD3D>(vertexShader);
+    const ShaderD3D *fragmentShaderD3D = GetImplAs<ShaderD3D>(fragmentShader);
+
+    for (gl::UniformBlock &uniformBlock : *uniformBlocks)
+    {
+        unsigned int uniformBlockElement = uniformBlock.isArray ? uniformBlock.arrayElement : 0;
+
+        if (uniformBlock.vertexStaticUse)
+        {
+            unsigned int baseRegister =
+                vertexShaderD3D->getInterfaceBlockRegister(uniformBlock.name);
+            uniformBlock.vsRegisterIndex = baseRegister + uniformBlockElement;
+        }
+
+        if (uniformBlock.fragmentStaticUse)
+        {
+            unsigned int baseRegister =
+                fragmentShaderD3D->getInterfaceBlockRegister(uniformBlock.name);
+            uniformBlock.psRegisterIndex = baseRegister + uniformBlockElement;
+        }
+
+        ASSERT(blockDataSizes.count(uniformBlock.name) == 1);
+        uniformBlock.dataSize = static_cast<unsigned int>(blockDataSizes[uniformBlock.name]);
+    }
+}
+
 void ProgramD3D::initializeUniformStorage()
 {
     // Compute total default block size
     unsigned int vertexRegisters = 0;
     unsigned int fragmentRegisters = 0;
-    for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
+    for (const D3DUniform *d3dUniform : mD3DUniforms)
     {
-        const gl::LinkedUniform &uniform = *mUniforms[uniformIndex];
-
-        if (!gl::IsSamplerType(uniform.type))
+        if (!d3dUniform->isSampler())
         {
-            if (uniform.isReferencedByVertexShader())
+            if (d3dUniform->isReferencedByVertexShader())
             {
-                vertexRegisters = std::max(vertexRegisters, uniform.vsRegisterIndex + uniform.registerCount);
+                vertexRegisters = std::max(vertexRegisters,
+                                           d3dUniform->vsRegisterIndex + d3dUniform->registerCount);
             }
-            if (uniform.isReferencedByFragmentShader())
+            if (d3dUniform->isReferencedByFragmentShader())
             {
-                fragmentRegisters = std::max(fragmentRegisters, uniform.psRegisterIndex + uniform.registerCount);
+                fragmentRegisters = std::max(
+                    fragmentRegisters, d3dUniform->psRegisterIndex + d3dUniform->registerCount);
             }
         }
     }
@@ -1230,15 +1294,15 @@
 {
     updateSamplerMapping();
 
-    gl::Error error = mRenderer->applyUniforms(*this, mUniforms);
+    gl::Error error = mRenderer->applyUniforms(*this, mD3DUniforms);
     if (error.isError())
     {
         return error;
     }
 
-    for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
+    for (D3DUniform *d3dUniform : mD3DUniforms)
     {
-        mUniforms[uniformIndex]->dirty = false;
+        d3dUniform->dirty = false;
     }
 
     return gl::Error(GL_NO_ERROR);
@@ -1252,22 +1316,22 @@
     const unsigned int reservedBuffersInVS = mRenderer->getReservedVertexUniformBuffers();
     const unsigned int reservedBuffersInFS = mRenderer->getReservedFragmentUniformBuffers();
 
-    for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < mUniformBlocks.size(); uniformBlockIndex++)
+    const auto &uniformBlocks = mData.getUniformBlocks();
+    for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlocks.size();
+         uniformBlockIndex++)
     {
-        gl::UniformBlock *uniformBlock = mUniformBlocks[uniformBlockIndex];
+        const gl::UniformBlock &uniformBlock = uniformBlocks[uniformBlockIndex];
         GLuint blockBinding            = mData.getUniformBlockBinding(uniformBlockIndex);
 
-        ASSERT(uniformBlock);
-
         // Unnecessary to apply an unreferenced standard or shared UBO
-        if (!uniformBlock->isReferencedByVertexShader() && !uniformBlock->isReferencedByFragmentShader())
+        if (!uniformBlock.vertexStaticUse && !uniformBlock.fragmentStaticUse)
         {
             continue;
         }
 
-        if (uniformBlock->isReferencedByVertexShader())
+        if (uniformBlock.vertexStaticUse)
         {
-            unsigned int registerIndex = uniformBlock->vsRegisterIndex - reservedBuffersInVS;
+            unsigned int registerIndex = uniformBlock.vsRegisterIndex - reservedBuffersInVS;
             ASSERT(registerIndex < data.caps->maxVertexUniformBlocks);
 
             if (mVertexUBOCache.size() <= registerIndex)
@@ -1279,9 +1343,9 @@
             mVertexUBOCache[registerIndex] = blockBinding;
         }
 
-        if (uniformBlock->isReferencedByFragmentShader())
+        if (uniformBlock.fragmentStaticUse)
         {
-            unsigned int registerIndex = uniformBlock->psRegisterIndex - reservedBuffersInFS;
+            unsigned int registerIndex = uniformBlock.psRegisterIndex - reservedBuffersInFS;
             ASSERT(registerIndex < data.caps->maxFragmentUniformBlocks);
 
             if (mFragmentUBOCache.size() <= registerIndex)
@@ -1297,31 +1361,11 @@
     return mRenderer->setUniformBuffers(data, mVertexUBOCache, mFragmentUBOCache);
 }
 
-void ProgramD3D::assignUniformBlockRegister(gl::UniformBlock *uniformBlock,
-                                            GLenum shader,
-                                            unsigned int registerIndex,
-                                            const gl::Caps &caps)
-{
-    // Validation done in the GL-level Program.
-    if (shader == GL_VERTEX_SHADER)
-    {
-        uniformBlock->vsRegisterIndex = registerIndex;
-        ASSERT(registerIndex < caps.maxVertexUniformBlocks);
-    }
-    else if (shader == GL_FRAGMENT_SHADER)
-    {
-        uniformBlock->psRegisterIndex = registerIndex;
-        ASSERT(registerIndex < caps.maxFragmentUniformBlocks);
-    }
-    else UNREACHABLE();
-}
-
 void ProgramD3D::dirtyAllUniforms()
 {
-    unsigned int numUniforms = static_cast<unsigned int>(mUniforms.size());
-    for (unsigned int index = 0; index < numUniforms; index++)
+    for (D3DUniform *d3dUniform : mD3DUniforms)
     {
-        mUniforms[index]->dirty = true;
+        d3dUniform->dirty = true;
     }
 }
 
@@ -1430,93 +1474,68 @@
     setUniform(location, count, v, GL_UNSIGNED_INT_VEC4);
 }
 
-void ProgramD3D::getUniformfv(GLint location, GLfloat *params)
+void ProgramD3D::assignUniformRegisters()
 {
-    getUniformv(location, params, GL_FLOAT);
-}
+    const gl::Shader *vertexShader   = mData.getAttachedVertexShader();
+    const ShaderD3D *vertexShaderD3D = GetImplAs<ShaderD3D>(vertexShader);
 
-void ProgramD3D::getUniformiv(GLint location, GLint *params)
-{
-    getUniformv(location, params, GL_INT);
-}
+    for (const sh::Uniform &vertexUniform : vertexShader->getUniforms())
 
-void ProgramD3D::getUniformuiv(GLint location, GLuint *params)
-{
-    getUniformv(location, params, GL_UNSIGNED_INT);
-}
-
-bool ProgramD3D::defineUniforms(gl::InfoLog &infoLog, const gl::Caps &caps)
-{
-    const gl::Shader *vertexShader                 = mData.getAttachedVertexShader();
-    const std::vector<sh::Uniform> &vertexUniforms = vertexShader->getUniforms();
-    const ShaderD3D *vertexShaderD3D               = GetImplAs<ShaderD3D>(vertexShader);
-
-    if (vertexUniforms.size() > caps.maxVertexUniformVectors)
     {
-        infoLog << "Vertex shader active uniforms exceed GL_MAX_VERTEX_UNIFORM_VECTORS ("
-                << caps.maxVertexUniformVectors << ").";
-        return false;
-    }
-
-    for (const sh::Uniform &uniform : vertexUniforms)
-    {
-        if (uniform.staticUse)
+        if (vertexUniform.staticUse)
         {
-            unsigned int registerBase = uniform.isBuiltIn() ? GL_INVALID_INDEX :
-                vertexShaderD3D->getUniformRegister(uniform.name);
-            defineUniformBase(vertexShaderD3D, uniform, registerBase);
+            assignUniformRegistersBase(vertexShaderD3D, vertexUniform);
         }
     }
 
-    const gl::Shader *fragmentShader                 = mData.getAttachedFragmentShader();
-    const std::vector<sh::Uniform> &fragmentUniforms = fragmentShader->getUniforms();
-    const ShaderD3D *fragmentShaderD3D               = GetImplAs<ShaderD3D>(fragmentShader);
+    const gl::Shader *fragmentShader   = mData.getAttachedFragmentShader();
+    const ShaderD3D *fragmentShaderD3D = GetImplAs<ShaderD3D>(fragmentShader);
 
-    if (fragmentUniforms.size() > caps.maxFragmentUniformVectors)
+    for (const sh::Uniform &fragmentUniform : fragmentShader->getUniforms())
     {
-        infoLog << "Vertex shader active uniforms exceed GL_MAX_FRAGMENT_UNIFORM_VECTORS ("
-                << caps.maxFragmentUniformVectors << ").";
-        return false;
-    }
-
-    for (const sh::Uniform &uniform : fragmentUniforms)
-    {
-        if (uniform.staticUse)
+        if (fragmentUniform.staticUse)
         {
-            unsigned int registerBase = uniform.isBuiltIn() ? GL_INVALID_INDEX :
-                fragmentShaderD3D->getUniformRegister(uniform.name);
-            defineUniformBase(fragmentShaderD3D, uniform, registerBase);
+            assignUniformRegistersBase(fragmentShaderD3D, fragmentUniform);
         }
     }
 
-    // TODO(jmadill): move the validation part to gl::Program
-    if (!indexUniforms(infoLog, caps))
-    {
-        return false;
-    }
-
+    assignAllSamplerRegisters();
     initializeUniformStorage();
-
-    return true;
 }
 
-void ProgramD3D::defineUniformBase(const ShaderD3D *shader, const sh::Uniform &uniform, unsigned int uniformRegister)
+void ProgramD3D::assignUniformRegistersBase(const ShaderD3D *shader, const sh::Uniform &uniform)
 {
-    if (uniformRegister == GL_INVALID_INDEX)
+    if (uniform.isBuiltIn())
     {
-        defineUniform(shader, uniform, uniform.name, nullptr);
+        assignUniformRegisters(shader, uniform, uniform.name, nullptr);
         return;
     }
 
+    unsigned int startRegister = shader->getUniformRegister(uniform.name);
     ShShaderOutput outputType = shader->getCompilerOutputType();
     sh::HLSLBlockEncoder encoder(sh::HLSLBlockEncoder::GetStrategyFor(outputType));
-    encoder.skipRegisters(uniformRegister);
+    encoder.skipRegisters(startRegister);
 
-    defineUniform(shader, uniform, uniform.name, &encoder);
+    assignUniformRegisters(shader, uniform, uniform.name, &encoder);
 }
 
-void ProgramD3D::defineUniform(const ShaderD3D *shader, const sh::ShaderVariable &uniform,
-                               const std::string &fullName, sh::HLSLBlockEncoder *encoder)
+D3DUniform *ProgramD3D::getD3DUniformByName(const std::string &name)
+{
+    for (D3DUniform *d3dUniform : mD3DUniforms)
+    {
+        if (d3dUniform->name == name)
+        {
+            return d3dUniform;
+        }
+    }
+
+    return nullptr;
+}
+
+void ProgramD3D::assignUniformRegisters(const ShaderD3D *shader,
+                                        const sh::ShaderVariable &uniform,
+                                        const std::string &fullName,
+                                        sh::HLSLBlockEncoder *encoder)
 {
     if (uniform.isStruct())
     {
@@ -1532,86 +1551,68 @@
                 const sh::ShaderVariable &field = uniform.fields[fieldIndex];
                 const std::string &fieldFullName = (fullName + elementString + "." + field.name);
 
-                defineUniform(shader, field, fieldFullName, encoder);
+                assignUniformRegisters(shader, field, fieldFullName, encoder);
             }
 
             if (encoder)
                 encoder->exitAggregateType();
         }
+        return;
     }
-    else // Not a struct
+
+    // Not a struct. Arrays are treated as aggregate types.
+    if (uniform.isArray() && encoder)
     {
-        // Arrays are treated as aggregate types
-        if (uniform.isArray() && encoder)
-        {
-            encoder->enterAggregateType();
-        }
+        encoder->enterAggregateType();
+    }
 
-        gl::LinkedUniform *linkedUniform = getUniformByName(fullName);
+    // Advance the uniform offset, to track registers allocation for structs
+    sh::BlockMemberInfo blockInfo =
+        encoder ? encoder->encodeType(uniform.type, uniform.arraySize, false)
+                : sh::BlockMemberInfo::getDefaultBlockInfo();
 
-        // Advance the uniform offset, to track registers allocation for structs
-        sh::BlockMemberInfo blockInfo = encoder ?
-            encoder->encodeType(uniform.type, uniform.arraySize, false) :
-            sh::BlockMemberInfo::getDefaultBlockInfo();
+    D3DUniform *d3dUniform = getD3DUniformByName(fullName);
 
-        if (!linkedUniform)
-        {
-            linkedUniform = new gl::LinkedUniform(uniform.type, uniform.precision, fullName, uniform.arraySize,
-                                                  -1, sh::BlockMemberInfo::getDefaultBlockInfo());
-            ASSERT(linkedUniform);
+    if (!d3dUniform)
+    {
+        // We're building the list twice, make sure we use the same indexing. Special case
+        // built-ins.
+        ASSERT(fullName.compare(0, 3, "gl_") == 0 ||
+               mData.getUniformIndex(fullName) == static_cast<GLint>(mD3DUniforms.size()));
 
-            if (encoder)
-                linkedUniform->registerElement = static_cast<unsigned int>(
-                    sh::HLSLBlockEncoder::getBlockRegisterElement(blockInfo));
-            mUniforms.push_back(linkedUniform);
-        }
+        d3dUniform = new D3DUniform(uniform.type, fullName, uniform.arraySize, true);
+        mD3DUniforms.push_back(d3dUniform);
 
         if (encoder)
         {
-            if (shader->getShaderType() == GL_FRAGMENT_SHADER)
-            {
-                linkedUniform->psRegisterIndex =
-                    static_cast<unsigned int>(sh::HLSLBlockEncoder::getBlockRegister(blockInfo));
-            }
-            else if (shader->getShaderType() == GL_VERTEX_SHADER)
-            {
-                linkedUniform->vsRegisterIndex =
-                    static_cast<unsigned int>(sh::HLSLBlockEncoder::getBlockRegister(blockInfo));
-            }
-            else UNREACHABLE();
+            d3dUniform->registerElement =
+                static_cast<unsigned int>(sh::HLSLBlockEncoder::getBlockRegisterElement(blockInfo));
         }
+    }
+
+    if (encoder)
+    {
+        unsigned int reg =
+            static_cast<unsigned int>(sh::HLSLBlockEncoder::getBlockRegister(blockInfo));
+        if (shader->getShaderType() == GL_FRAGMENT_SHADER)
+        {
+            d3dUniform->psRegisterIndex = reg;
+        }
+        else if (shader->getShaderType() == GL_VERTEX_SHADER)
+        {
+            d3dUniform->vsRegisterIndex = reg;
+        }
+        else
+            UNREACHABLE();
 
         // Arrays are treated as aggregate types
-        if (uniform.isArray() && encoder)
+        if (uniform.isArray())
         {
             encoder->exitAggregateType();
         }
     }
 }
 
-void ProgramD3D::defineUniformBlocks(const gl::Caps &caps)
-{
-    const gl::Shader *vertexShader = mData.getAttachedVertexShader();
-
-    for (const sh::InterfaceBlock &vertexBlock : vertexShader->getInterfaceBlocks())
-    {
-        if (vertexBlock.staticUse || vertexBlock.layout != sh::BLOCKLAYOUT_PACKED)
-        {
-            defineUniformBlock(*vertexShader, vertexBlock, caps);
-        }
-    }
-
-    const gl::Shader *fragmentShader = mData.getAttachedFragmentShader();
-
-    for (const sh::InterfaceBlock &fragmentBlock : fragmentShader->getInterfaceBlocks())
-    {
-        if (fragmentBlock.staticUse || fragmentBlock.layout != sh::BLOCKLAYOUT_PACKED)
-        {
-            defineUniformBlock(*fragmentShader, fragmentBlock, caps);
-        }
-    }
-}
-
 template <typename T>
 static inline void SetIfDirty(T *dest, const T& source, bool *dirtyFlag)
 {
@@ -1623,22 +1624,22 @@
 }
 
 template <typename T>
-void ProgramD3D::setUniform(GLint location, GLsizei count, const T* v, GLenum targetUniformType)
+void ProgramD3D::setUniform(GLint location, GLsizei countIn, const T *v, GLenum targetUniformType)
 {
     const int components = gl::VariableComponentCount(targetUniformType);
     const GLenum targetBoolType = gl::VariableBoolVectorType(targetUniformType);
 
-    gl::LinkedUniform *targetUniform = getUniformByLocation(location);
+    D3DUniform *targetUniform = getD3DUniformFromLocation(location);
 
-    int elementCount = targetUniform->elementCount();
-
-    count = std::min(elementCount - (int)mUniformIndex[location].element, count);
+    unsigned int elementCount = targetUniform->elementCount();
+    unsigned int arrayElement = mData.getUniformLocations()[location].element;
+    unsigned int count        = std::min(elementCount - arrayElement, static_cast<unsigned int>(countIn));
 
     if (targetUniform->type == targetUniformType)
     {
-        T *target = reinterpret_cast<T*>(targetUniform->data) + mUniformIndex[location].element * 4;
+        T *target = reinterpret_cast<T *>(targetUniform->data) + arrayElement * 4;
 
-        for (int i = 0; i < count; i++)
+        for (unsigned int i = 0; i < count; i++)
         {
             T *dest = target + (i * 4);
             const T *source = v + (i * components);
@@ -1655,9 +1656,9 @@
     }
     else if (targetUniform->type == targetBoolType)
     {
-        GLint *boolParams = reinterpret_cast<GLint*>(targetUniform->data) + mUniformIndex[location].element * 4;
+        GLint *boolParams = reinterpret_cast<GLint *>(targetUniform->data) + arrayElement * 4;
 
-        for (int i = 0; i < count; i++)
+        for (unsigned int i = 0; i < count; i++)
         {
             GLint *dest = boolParams + (i * 4);
             const T *source = v + (i * components);
@@ -1672,15 +1673,15 @@
             }
         }
     }
-    else if (gl::IsSamplerType(targetUniform->type))
+    else if (targetUniform->isSampler())
     {
         ASSERT(targetUniformType == GL_INT);
 
-        GLint *target = reinterpret_cast<GLint*>(targetUniform->data) + mUniformIndex[location].element * 4;
+        GLint *target = reinterpret_cast<GLint *>(targetUniform->data) + arrayElement * 4;
 
         bool wasDirty = targetUniform->dirty;
 
-        for (int i = 0; i < count; i++)
+        for (unsigned int i = 0; i < count; i++)
         {
             GLint *dest = target + (i * 4);
             const GLint *source = reinterpret_cast<const GLint*>(v) + (i * components);
@@ -1768,17 +1769,23 @@
 }
 
 template <int cols, int rows>
-void ProgramD3D::setUniformMatrixfv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value, GLenum targetUniformType)
+void ProgramD3D::setUniformMatrixfv(GLint location,
+                                    GLsizei countIn,
+                                    GLboolean transpose,
+                                    const GLfloat *value,
+                                    GLenum targetUniformType)
 {
-    gl::LinkedUniform *targetUniform = getUniformByLocation(location);
+    D3DUniform *targetUniform = getD3DUniformFromLocation(location);
 
-    int elementCount = targetUniform->elementCount();
+    unsigned int elementCount = targetUniform->elementCount();
+    unsigned int arrayElement = mData.getUniformLocations()[location].element;
+    unsigned int count        = std::min(elementCount - arrayElement, static_cast<unsigned int>(countIn));
 
-    count = std::min(elementCount - (int)mUniformIndex[location].element, count);
     const unsigned int targetMatrixStride = (4 * rows);
-    GLfloat *target = (GLfloat*)(targetUniform->data + mUniformIndex[location].element * sizeof(GLfloat) * targetMatrixStride);
+    GLfloat *target =
+        (GLfloat *)(targetUniform->data + arrayElement * sizeof(GLfloat) * targetMatrixStride);
 
-    for (int i = 0; i < count; i++)
+    for (unsigned int i = 0; i < count; i++)
     {
         // Internally store matrices as transposed versions to accomodate HLSL matrix indexing
         if (transpose == GL_FALSE)
@@ -1794,186 +1801,63 @@
     }
 }
 
-template <typename T>
-void ProgramD3D::getUniformv(GLint location, T *params, GLenum uniformType)
+size_t ProgramD3D::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock,
+                                      BlockInfoMap *blockInfoOut)
 {
-    gl::LinkedUniform *targetUniform = mUniforms[mUniformIndex[location].index];
+    ASSERT(interfaceBlock.staticUse || interfaceBlock.layout != sh::BLOCKLAYOUT_PACKED);
 
-    if (gl::IsMatrixType(targetUniform->type))
+    // define member uniforms
+    sh::Std140BlockEncoder std140Encoder;
+    sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED);
+    sh::BlockLayoutEncoder *encoder = nullptr;
+
+    if (interfaceBlock.layout == sh::BLOCKLAYOUT_STANDARD)
     {
-        const int rows = gl::VariableRowCount(targetUniform->type);
-        const int cols = gl::VariableColumnCount(targetUniform->type);
-        transposeMatrix(params, (GLfloat*)targetUniform->data + mUniformIndex[location].element * 4 * rows, rows, cols, 4, rows);
-    }
-    else if (uniformType == gl::VariableComponentType(targetUniform->type))
-    {
-        unsigned int size = gl::VariableComponentCount(targetUniform->type);
-        memcpy(params, targetUniform->data + mUniformIndex[location].element * 4 * sizeof(T),
-                size * sizeof(T));
+        encoder = &std140Encoder;
     }
     else
     {
-        unsigned int size = gl::VariableComponentCount(targetUniform->type);
-        switch (gl::VariableComponentType(targetUniform->type))
-        {
-          case GL_BOOL:
-            {
-                GLint *boolParams = (GLint*)targetUniform->data + mUniformIndex[location].element * 4;
-
-                for (unsigned int i = 0; i < size; i++)
-                {
-                    params[i] = (boolParams[i] == GL_FALSE) ? static_cast<T>(0) : static_cast<T>(1);
-                }
-            }
-            break;
-
-          case GL_FLOAT:
-            {
-                GLfloat *floatParams = (GLfloat*)targetUniform->data + mUniformIndex[location].element * 4;
-
-                for (unsigned int i = 0; i < size; i++)
-                {
-                    params[i] = static_cast<T>(roundf(floatParams[i]));
-                }
-            }
-            break;
-
-          case GL_INT:
-            {
-                GLint *intParams = (GLint*)targetUniform->data + mUniformIndex[location].element * 4;
-
-                for (unsigned int i = 0; i < size; i++)
-                {
-                    params[i] = static_cast<T>(intParams[i]);
-                }
-            }
-            break;
-
-          case GL_UNSIGNED_INT:
-            {
-                GLuint *uintParams = (GLuint*)targetUniform->data + mUniformIndex[location].element * 4;
-
-                for (unsigned int i = 0; i < size; i++)
-                {
-                    params[i] = static_cast<T>(uintParams[i]);
-                }
-            }
-            break;
-
-          default: UNREACHABLE();
-        }
+        encoder = &hlslEncoder;
     }
+
+    GetUniformBlockInfo(interfaceBlock.fields, "", encoder, interfaceBlock.isRowMajorLayout,
+                        blockInfoOut);
+
+    return encoder->getBlockSize();
 }
 
-template <typename VarT>
-void ProgramD3D::defineUniformBlockMembers(const std::vector<VarT> &fields, const std::string &prefix, int blockIndex,
-                                           sh::BlockLayoutEncoder *encoder, std::vector<unsigned int> *blockUniformIndexes,
-                                           bool inRowMajorLayout)
+void ProgramD3D::assignAllSamplerRegisters()
 {
-    for (unsigned int uniformIndex = 0; uniformIndex < fields.size(); uniformIndex++)
+    for (const D3DUniform *d3dUniform : mD3DUniforms)
     {
-        const VarT &field = fields[uniformIndex];
-        const std::string &fieldName = (prefix.empty() ? field.name : prefix + "." + field.name);
-
-        if (field.isStruct())
+        if (d3dUniform->isSampler())
         {
-            bool rowMajorLayout = (inRowMajorLayout || IsRowMajorLayout(field));
-
-            for (unsigned int arrayElement = 0; arrayElement < field.elementCount(); arrayElement++)
-            {
-                encoder->enterAggregateType();
-
-                const std::string uniformElementName = fieldName + (field.isArray() ? ArrayString(arrayElement) : "");
-                defineUniformBlockMembers(field.fields, uniformElementName, blockIndex, encoder, blockUniformIndexes, rowMajorLayout);
-
-                encoder->exitAggregateType();
-            }
-        }
-        else
-        {
-            bool isRowMajorMatrix = (gl::IsMatrixType(field.type) && inRowMajorLayout);
-
-            sh::BlockMemberInfo memberInfo = encoder->encodeType(field.type, field.arraySize, isRowMajorMatrix);
-
-            gl::LinkedUniform *newUniform = new gl::LinkedUniform(field.type, field.precision, fieldName, field.arraySize,
-                                                          blockIndex, memberInfo);
-
-            // add to uniform list, but not index, since uniform block uniforms have no location
-            blockUniformIndexes->push_back(static_cast<GLenum>(mUniforms.size()));
-            mUniforms.push_back(newUniform);
+            assignSamplerRegisters(d3dUniform);
         }
     }
 }
 
-void ProgramD3D::defineUniformBlock(const gl::Shader &shader,
-                                    const sh::InterfaceBlock &interfaceBlock,
-                                    const gl::Caps &caps)
+void ProgramD3D::assignSamplerRegisters(const D3DUniform *d3dUniform)
 {
-    const ShaderD3D* shaderD3D = GetImplAs<ShaderD3D>(&shader);
+    ASSERT(d3dUniform->isSampler());
+    ASSERT(d3dUniform->vsRegisterIndex != GL_INVALID_INDEX ||
+           d3dUniform->psRegisterIndex != GL_INVALID_INDEX);
 
-    // create uniform block entries if they do not exist
-    if (getUniformBlockIndex(interfaceBlock.name) == GL_INVALID_INDEX)
+    if (d3dUniform->vsRegisterIndex != GL_INVALID_INDEX)
     {
-        std::vector<unsigned int> blockUniformIndexes;
-        const unsigned int blockIndex = static_cast<unsigned int>(mUniformBlocks.size());
-
-        // define member uniforms
-        sh::BlockLayoutEncoder *encoder = NULL;
-
-        if (interfaceBlock.layout == sh::BLOCKLAYOUT_STANDARD)
-        {
-            encoder = new sh::Std140BlockEncoder;
-        }
-        else
-        {
-            encoder = new sh::HLSLBlockEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED);
-        }
-        ASSERT(encoder);
-
-        defineUniformBlockMembers(interfaceBlock.fields, "", blockIndex, encoder, &blockUniformIndexes, interfaceBlock.isRowMajorLayout);
-
-        unsigned int dataSize = static_cast<unsigned int>(encoder->getBlockSize());
-
-        // create all the uniform blocks
-        if (interfaceBlock.arraySize > 0)
-        {
-            for (unsigned int uniformBlockElement = 0; uniformBlockElement < interfaceBlock.arraySize; uniformBlockElement++)
-            {
-                gl::UniformBlock *newUniformBlock = new gl::UniformBlock(interfaceBlock.name, uniformBlockElement, dataSize);
-                newUniformBlock->memberUniformIndexes = blockUniformIndexes;
-                mUniformBlocks.push_back(newUniformBlock);
-            }
-        }
-        else
-        {
-            gl::UniformBlock *newUniformBlock = new gl::UniformBlock(interfaceBlock.name, GL_INVALID_INDEX, dataSize);
-            newUniformBlock->memberUniformIndexes = blockUniformIndexes;
-            mUniformBlocks.push_back(newUniformBlock);
-        }
+        AssignSamplers(d3dUniform->vsRegisterIndex, d3dUniform->type, d3dUniform->arraySize,
+                       mSamplersVS, &mUsedVertexSamplerRange);
     }
 
-    if (interfaceBlock.staticUse)
+    if (d3dUniform->psRegisterIndex != GL_INVALID_INDEX)
     {
-        // Assign registers to the uniform blocks
-        const GLuint blockIndex = getUniformBlockIndex(interfaceBlock.name);
-        const unsigned int elementCount = std::max(1u, interfaceBlock.arraySize);
-        ASSERT(blockIndex != GL_INVALID_INDEX);
-        ASSERT(blockIndex + elementCount <= mUniformBlocks.size());
-
-        unsigned int interfaceBlockRegister = shaderD3D->getInterfaceBlockRegister(interfaceBlock.name);
-
-        for (unsigned int uniformBlockElement = 0; uniformBlockElement < elementCount; uniformBlockElement++)
-        {
-            gl::UniformBlock *uniformBlock = mUniformBlocks[blockIndex + uniformBlockElement];
-            ASSERT(uniformBlock->name == interfaceBlock.name);
-
-            assignUniformBlockRegister(uniformBlock, shader.getType(),
-                                       interfaceBlockRegister + uniformBlockElement, caps);
-        }
+        AssignSamplers(d3dUniform->psRegisterIndex, d3dUniform->type, d3dUniform->arraySize,
+                       mSamplersPS, &mUsedPixelSamplerRange);
     }
 }
 
-bool ProgramD3D::assignSamplers(unsigned int startSamplerIndex,
+// static
+void ProgramD3D::AssignSamplers(unsigned int startSamplerIndex,
                                 GLenum samplerType,
                                 unsigned int samplerCount,
                                 std::vector<Sampler> &outSamplers,
@@ -1983,103 +1867,18 @@
 
     do
     {
-        if (samplerIndex < outSamplers.size())
-        {
-            Sampler& sampler = outSamplers[samplerIndex];
-            sampler.active = true;
-            sampler.textureType = GetTextureType(samplerType);
-            sampler.logicalTextureUnit = 0;
-            *outUsedRange = std::max(samplerIndex + 1, *outUsedRange);
-        }
-        else
-        {
-            return false;
-        }
-
+        ASSERT(samplerIndex < outSamplers.size());
+        Sampler *sampler            = &outSamplers[samplerIndex];
+        sampler->active             = true;
+        sampler->textureType        = GetTextureType(samplerType);
+        sampler->logicalTextureUnit = 0;
+        *outUsedRange               = std::max(samplerIndex + 1, *outUsedRange);
         samplerIndex++;
     } while (samplerIndex < startSamplerIndex + samplerCount);
-
-    return true;
-}
-
-bool ProgramD3D::indexSamplerUniform(const gl::LinkedUniform &uniform, gl::InfoLog &infoLog, const gl::Caps &caps)
-{
-    ASSERT(gl::IsSamplerType(uniform.type));
-    ASSERT(uniform.vsRegisterIndex != GL_INVALID_INDEX || uniform.psRegisterIndex != GL_INVALID_INDEX);
-
-    if (uniform.vsRegisterIndex != GL_INVALID_INDEX)
-    {
-        if (!assignSamplers(uniform.vsRegisterIndex, uniform.type, uniform.arraySize, mSamplersVS,
-                            &mUsedVertexSamplerRange))
-        {
-            infoLog << "Vertex shader sampler count exceeds the maximum vertex texture units ("
-                    << mSamplersVS.size() << ").";
-            return false;
-        }
-
-        unsigned int maxVertexVectors = mRenderer->getReservedVertexUniformVectors() + caps.maxVertexUniformVectors;
-        if (uniform.vsRegisterIndex + uniform.registerCount > maxVertexVectors)
-        {
-            infoLog << "Vertex shader active uniforms exceed GL_MAX_VERTEX_UNIFORM_VECTORS ("
-                    << caps.maxVertexUniformVectors << ").";
-            return false;
-        }
-    }
-
-    if (uniform.psRegisterIndex != GL_INVALID_INDEX)
-    {
-        if (!assignSamplers(uniform.psRegisterIndex, uniform.type, uniform.arraySize, mSamplersPS,
-                            &mUsedPixelSamplerRange))
-        {
-            infoLog << "Pixel shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS ("
-                    << mSamplersPS.size() << ").";
-            return false;
-        }
-
-        unsigned int maxFragmentVectors = mRenderer->getReservedFragmentUniformVectors() + caps.maxFragmentUniformVectors;
-        if (uniform.psRegisterIndex + uniform.registerCount > maxFragmentVectors)
-        {
-            infoLog << "Fragment shader active uniforms exceed GL_MAX_FRAGMENT_UNIFORM_VECTORS ("
-                    << caps.maxFragmentUniformVectors << ").";
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool ProgramD3D::indexUniforms(gl::InfoLog &infoLog, const gl::Caps &caps)
-{
-    for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
-    {
-        const gl::LinkedUniform &uniform = *mUniforms[uniformIndex];
-
-        if (gl::IsSamplerType(uniform.type))
-        {
-            if (!indexSamplerUniform(uniform, infoLog, caps))
-            {
-                return false;
-            }
-        }
-
-        for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
-        {
-            if (!uniform.isBuiltIn())
-            {
-                // Assign in-order uniform locations
-                mUniformIndex[static_cast<GLuint>(mUniformIndex.size())] = gl::VariableLocation(
-                    uniform.name, arrayIndex, static_cast<unsigned int>(uniformIndex));
-            }
-        }
-    }
-
-    return true;
 }
 
 void ProgramD3D::reset()
 {
-    ProgramImpl::reset();
-
     SafeDeleteContainer(mVertexExecutables);
     SafeDeleteContainer(mPixelExecutables);
     SafeDelete(mGeometryExecutable);
@@ -2094,6 +1893,8 @@
     mPixelShaderKey.clear();
     mUsesPointSize = false;
 
+    SafeDeleteContainer(mD3DUniforms);
+
     SafeDelete(mVertexUniformStorage);
     SafeDelete(mFragmentUniformStorage);
 
@@ -2203,4 +2004,9 @@
         }
     }
 }
+
+D3DUniform *ProgramD3D::getD3DUniformFromLocation(GLint location)
+{
+    return mD3DUniforms[mData.getUniformLocations()[location].index];
+}
 }
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h
index 6efc654..5e86516 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -20,13 +20,6 @@
 #include "libANGLE/renderer/d3d/DynamicHLSL.h"
 #include "libANGLE/renderer/d3d/WorkaroundsD3D.h"
 
-namespace gl
-{
-struct LinkedUniform;
-struct VariableLocation;
-struct VertexFormat;
-}
-
 namespace rx
 {
 class RendererD3D;
@@ -39,6 +32,43 @@
 #define ANGLE_COMPILE_OPTIMIZATION_LEVEL D3DCOMPILE_OPTIMIZATION_LEVEL1
 #endif
 
+// Helper struct representing a single shader uniform
+struct D3DUniform
+{
+    D3DUniform(GLenum typeIn,
+               const std::string &nameIn,
+               unsigned int arraySizeIn,
+               bool defaultBlock);
+    ~D3DUniform();
+
+    bool isSampler() const;
+    unsigned int elementCount() const { return std::max(1u, arraySize); }
+    bool isReferencedByVertexShader() const;
+    bool isReferencedByFragmentShader() const;
+
+    // Duplicated from the GL layer
+    GLenum type;
+    std::string name;
+    unsigned int arraySize;
+
+    // Pointer to a system copy of the data.
+    // TODO(jmadill): remove this in favor of gl::LinkedUniform::data().
+    uint8_t *data;
+
+    // Has the data been updated since the last sync?
+    bool dirty;
+
+    // Register information.
+    unsigned int vsRegisterIndex;
+    unsigned int psRegisterIndex;
+    unsigned int registerCount;
+
+    // Register "elements" are used for uniform structs in ES3, to appropriately identify single
+    // uniforms
+    // inside aggregate types, which are packed according C-like structure rules.
+    unsigned int registerElement;
+};
+
 class ProgramD3D : public ProgramImpl
 {
   public:
@@ -73,13 +103,12 @@
     LinkResult link(const gl::Data &data, gl::InfoLog &infoLog) override;
     GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
 
+    void gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
+                                std::vector<gl::LinkedUniform> *uniforms) override;
+
     void initializeUniformStorage();
     gl::Error applyUniforms();
     gl::Error applyUniformBuffers(const gl::Data &data);
-    void assignUniformBlockRegister(gl::UniformBlock *uniformBlock,
-                                    GLenum shader,
-                                    unsigned int registerIndex,
-                                    const gl::Caps &caps);
     void dirtyAllUniforms();
 
     void setUniform1fv(GLint location, GLsizei count, const GLfloat *v);
@@ -104,15 +133,9 @@
     void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
     void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 
-    void getUniformfv(GLint location, GLfloat *params);
-    void getUniformiv(GLint location, GLint *params);
-    void getUniformuiv(GLint location, GLuint *params);
-
     const UniformStorageD3D &getVertexUniformStorage() const { return *mVertexUniformStorage; }
     const UniformStorageD3D &getFragmentUniformStorage() const { return *mFragmentUniformStorage; }
 
-    void reset();
-
     unsigned int getSerial() const;
 
     void sortAttributesByLayout(const std::vector<TranslatedAttribute> &unsortedAttributes,
@@ -175,19 +198,24 @@
         GLenum textureType;
     };
 
-    bool defineUniforms(gl::InfoLog &infoLog, const gl::Caps &caps);
-    void defineUniformBase(const ShaderD3D *shader, const sh::Uniform &uniform, unsigned int uniformRegister);
-    void defineUniform(const ShaderD3D *shader, const sh::ShaderVariable &uniform, const std::string &fullName,
-                       sh::HLSLBlockEncoder *encoder);
-    bool indexSamplerUniform(const gl::LinkedUniform &uniform, gl::InfoLog &infoLog, const gl::Caps &caps);
-    bool indexUniforms(gl::InfoLog &infoLog, const gl::Caps &caps);
-    static bool assignSamplers(unsigned int startSamplerIndex, GLenum samplerType, unsigned int samplerCount,
-                               std::vector<Sampler> &outSamplers, GLuint *outUsedRange);
+    typedef std::map<std::string, sh::BlockMemberInfo> BlockInfoMap;
 
-    void defineUniformBlocks(const gl::Caps &caps);
-    void defineUniformBlock(const gl::Shader &shader,
-                            const sh::InterfaceBlock &interfaceBlock,
-                            const gl::Caps &caps);
+    void assignUniformRegisters();
+    void assignUniformRegistersBase(const ShaderD3D *shader, const sh::Uniform &uniform);
+    void assignUniformRegisters(const ShaderD3D *shader,
+                                const sh::ShaderVariable &uniform,
+                                const std::string &fullName,
+                                sh::HLSLBlockEncoder *encoder);
+    void assignAllSamplerRegisters();
+    void assignSamplerRegisters(const D3DUniform *d3dUniform);
+
+    static void AssignSamplers(unsigned int startSamplerIndex,
+                               GLenum samplerType,
+                               unsigned int samplerCount,
+                               std::vector<Sampler> &outSamplers,
+                               GLuint *outUsedRange);
+
+    size_t defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, BlockInfoMap *blockInfoOut);
 
     template <typename T>
     void setUniform(GLint location, GLsizei count, const T* v, GLenum targetUniformType);
@@ -195,23 +223,19 @@
     template <int cols, int rows>
     void setUniformMatrixfv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value, GLenum targetUniformType);
 
-    template <typename T>
-    void getUniformv(GLint location, T *params, GLenum uniformType);
-
-    template <typename VarT>
-    void defineUniformBlockMembers(const std::vector<VarT> &fields, const std::string &prefix, int blockIndex,
-                                   sh::BlockLayoutEncoder *encoder, std::vector<unsigned int> *blockUniformIndexes,
-                                   bool inRowMajorLayout);
-
     LinkResult compileProgramExecutables(gl::InfoLog &infoLog,
                                          int registers,
                                          const std::vector<PackedVarying> &packedVaryings);
 
     void gatherTransformFeedbackVaryings(const std::vector<gl::LinkedVarying> &varyings);
+    D3DUniform *getD3DUniformByName(const std::string &name);
+    D3DUniform *getD3DUniformFromLocation(GLint location);
 
     void initSemanticIndex();
     void initAttributesByLayout();
 
+    void reset();
+
     RendererD3D *mRenderer;
     DynamicHLSL *mDynamicHLSL;
 
@@ -259,6 +283,7 @@
     gl::InputLayout mCachedInputLayout;
 
     std::vector<gl::LinkedVarying> mTransformFeedbackLinkedVaryings;
+    std::vector<D3DUniform *> mD3DUniforms;
 
     static unsigned int issueSerial();
     static unsigned int mCurrentSerial;
diff --git a/src/libANGLE/renderer/d3d/RendererD3D.h b/src/libANGLE/renderer/d3d/RendererD3D.h
index f2629dd..87bbadd 100644
--- a/src/libANGLE/renderer/d3d/RendererD3D.h
+++ b/src/libANGLE/renderer/d3d/RendererD3D.h
@@ -37,8 +37,10 @@
 
 namespace rx
 {
+struct D3DUniform;
 class ImageD3D;
 class IndexBuffer;
+class ProgramD3D;
 class RenderTargetD3D;
 class ShaderExecutableD3D;
 class SwapChainD3D;
@@ -157,7 +159,8 @@
                                    const gl::Framebuffer *framebuffer,
                                    bool rasterizerDiscard,
                                    bool transformFeedbackActive) = 0;
-    virtual gl::Error applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray) = 0;
+    virtual gl::Error applyUniforms(const ProgramD3D &programD3D,
+                                    const std::vector<D3DUniform *> &uniformArray) = 0;
     virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize) = 0;
     virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceIndexInfo) = 0;
     virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo, SourceIndexData *sourceIndexInfo) = 0;
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 33901aa..7eb834c 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -2095,7 +2095,8 @@
     return gl::Error(GL_NO_ERROR);
 }
 
-gl::Error Renderer11::applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray)
+gl::Error Renderer11::applyUniforms(const ProgramD3D &programD3D,
+                                    const std::vector<D3DUniform *> &uniformArray)
 {
     unsigned int totalRegisterCountVS = 0;
     unsigned int totalRegisterCountPS = 0;
@@ -2103,26 +2104,25 @@
     bool vertexUniformsDirty = false;
     bool pixelUniformsDirty = false;
 
-    for (size_t uniformIndex = 0; uniformIndex < uniformArray.size(); uniformIndex++)
+    for (const D3DUniform *uniform : uniformArray)
     {
-        const gl::LinkedUniform &uniform = *uniformArray[uniformIndex];
-
-        if (uniform.isReferencedByVertexShader() && !uniform.isSampler())
+        if (uniform->isReferencedByVertexShader() && !uniform->isSampler())
         {
-            totalRegisterCountVS += uniform.registerCount;
-            vertexUniformsDirty = (vertexUniformsDirty || uniform.dirty);
+            totalRegisterCountVS += uniform->registerCount;
+            vertexUniformsDirty = (vertexUniformsDirty || uniform->dirty);
         }
 
-        if (uniform.isReferencedByFragmentShader() && !uniform.isSampler())
+        if (uniform->isReferencedByFragmentShader() && !uniform->isSampler())
         {
-            totalRegisterCountPS += uniform.registerCount;
-            pixelUniformsDirty = (pixelUniformsDirty || uniform.dirty);
+            totalRegisterCountPS += uniform->registerCount;
+            pixelUniformsDirty = (pixelUniformsDirty || uniform->dirty);
         }
     }
 
-    const ProgramD3D *programD3D = GetAs<ProgramD3D>(&program);
-    const UniformStorage11 *vertexUniformStorage = GetAs<UniformStorage11>(&programD3D->getVertexUniformStorage());
-    const UniformStorage11 *fragmentUniformStorage = GetAs<UniformStorage11>(&programD3D->getFragmentUniformStorage());
+    const UniformStorage11 *vertexUniformStorage =
+        GetAs<UniformStorage11>(&programD3D.getVertexUniformStorage());
+    const UniformStorage11 *fragmentUniformStorage =
+        GetAs<UniformStorage11>(&programD3D.getFragmentUniformStorage());
     ASSERT(vertexUniformStorage);
     ASSERT(fragmentUniformStorage);
 
@@ -2150,26 +2150,26 @@
         mapPS = (float(*)[4])map.pData;
     }
 
-    for (size_t uniformIndex = 0; uniformIndex < uniformArray.size(); uniformIndex++)
+    for (const D3DUniform *uniform : uniformArray)
     {
-        gl::LinkedUniform *uniform = uniformArray[uniformIndex];
+        if (uniform->isSampler())
+            continue;
 
-        if (!uniform->isSampler())
+        unsigned int componentCount = (4 - uniform->registerElement);
+
+        // we assume that uniforms from structs are arranged in struct order in our uniforms list.
+        // otherwise we would overwrite previously written regions of memory.
+
+        if (uniform->isReferencedByVertexShader() && mapVS)
         {
-            unsigned int componentCount = (4 - uniform->registerElement);
+            memcpy(&mapVS[uniform->vsRegisterIndex][uniform->registerElement], uniform->data,
+                   uniform->registerCount * sizeof(float) * componentCount);
+        }
 
-            // we assume that uniforms from structs are arranged in struct order in our uniforms list. otherwise we would
-            // overwrite previously written regions of memory.
-
-            if (uniform->isReferencedByVertexShader() && mapVS)
-            {
-                memcpy(&mapVS[uniform->vsRegisterIndex][uniform->registerElement], uniform->data, uniform->registerCount * sizeof(float) * componentCount);
-            }
-
-            if (uniform->isReferencedByFragmentShader() && mapPS)
-            {
-                memcpy(&mapPS[uniform->psRegisterIndex][uniform->registerElement], uniform->data, uniform->registerCount * sizeof(float) * componentCount);
-            }
+        if (uniform->isReferencedByFragmentShader() && mapPS)
+        {
+            memcpy(&mapPS[uniform->psRegisterIndex][uniform->registerElement], uniform->data,
+                   uniform->registerCount * sizeof(float) * componentCount);
         }
     }
 
@@ -2255,7 +2255,7 @@
     }
 
     // GSSetConstantBuffers triggers device removal on 9_3, so we should only call it if necessary
-    if (programD3D->usesGeometryShader())
+    if (programD3D.usesGeometryShader())
     {
         // needed for the point sprite geometry shader
         if (mCurrentGeometryConstantBuffer != mDriverConstantBufferPS)
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
index 6260add..2bce423 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -137,7 +137,8 @@
                            bool rasterizerDiscard,
                            bool transformFeedbackActive) override;
 
-    virtual gl::Error applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray);
+    gl::Error applyUniforms(const ProgramD3D &programD3D,
+                            const std::vector<D3DUniform *> &uniformArray) override;
     virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceIndexInfo);
     virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo, SourceIndexData *sourceIndexInfo);
     void applyTransformFeedbackBuffers(const gl::State &state) override;
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
index 8c59545..61dec0a 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
@@ -1910,46 +1910,45 @@
     return gl::Error(GL_NO_ERROR);
 }
 
-gl::Error Renderer9::applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray)
+gl::Error Renderer9::applyUniforms(const ProgramD3D &programD3D,
+                                   const std::vector<D3DUniform *> &uniformArray)
 {
-    for (size_t uniformIndex = 0; uniformIndex < uniformArray.size(); uniformIndex++)
+    for (const D3DUniform *targetUniform : uniformArray)
     {
-        gl::LinkedUniform *targetUniform = uniformArray[uniformIndex];
+        if (!targetUniform->dirty)
+            continue;
 
-        if (targetUniform->dirty)
+        GLfloat *f = (GLfloat *)targetUniform->data;
+        GLint *i   = (GLint *)targetUniform->data;
+
+        switch (targetUniform->type)
         {
-            GLfloat *f = (GLfloat*)targetUniform->data;
-            GLint *i = (GLint*)targetUniform->data;
-
-            switch (targetUniform->type)
-            {
-              case GL_SAMPLER_2D:
-              case GL_SAMPLER_CUBE:
+            case GL_SAMPLER_2D:
+            case GL_SAMPLER_CUBE:
                 break;
-              case GL_BOOL:
-              case GL_BOOL_VEC2:
-              case GL_BOOL_VEC3:
-              case GL_BOOL_VEC4:
+            case GL_BOOL:
+            case GL_BOOL_VEC2:
+            case GL_BOOL_VEC3:
+            case GL_BOOL_VEC4:
                 applyUniformnbv(targetUniform, i);
                 break;
-              case GL_FLOAT:
-              case GL_FLOAT_VEC2:
-              case GL_FLOAT_VEC3:
-              case GL_FLOAT_VEC4:
-              case GL_FLOAT_MAT2:
-              case GL_FLOAT_MAT3:
-              case GL_FLOAT_MAT4:
+            case GL_FLOAT:
+            case GL_FLOAT_VEC2:
+            case GL_FLOAT_VEC3:
+            case GL_FLOAT_VEC4:
+            case GL_FLOAT_MAT2:
+            case GL_FLOAT_MAT3:
+            case GL_FLOAT_MAT4:
                 applyUniformnfv(targetUniform, f);
                 break;
-              case GL_INT:
-              case GL_INT_VEC2:
-              case GL_INT_VEC3:
-              case GL_INT_VEC4:
+            case GL_INT:
+            case GL_INT_VEC2:
+            case GL_INT_VEC3:
+            case GL_INT_VEC4:
                 applyUniformniv(targetUniform, i);
                 break;
-              default:
+            default:
                 UNREACHABLE();
-            }
         }
     }
 
@@ -1964,7 +1963,7 @@
     return gl::Error(GL_NO_ERROR);
 }
 
-void Renderer9::applyUniformnfv(gl::LinkedUniform *targetUniform, const GLfloat *v)
+void Renderer9::applyUniformnfv(const D3DUniform *targetUniform, const GLfloat *v)
 {
     if (targetUniform->isReferencedByFragmentShader())
     {
@@ -1977,7 +1976,7 @@
     }
 }
 
-void Renderer9::applyUniformniv(gl::LinkedUniform *targetUniform, const GLint *v)
+void Renderer9::applyUniformniv(const D3DUniform *targetUniform, const GLint *v)
 {
     ASSERT(targetUniform->registerCount <= MAX_VERTEX_CONSTANT_VECTORS_D3D9);
     GLfloat vector[MAX_VERTEX_CONSTANT_VECTORS_D3D9][4];
@@ -1993,7 +1992,7 @@
     applyUniformnfv(targetUniform, (GLfloat*)vector);
 }
 
-void Renderer9::applyUniformnbv(gl::LinkedUniform *targetUniform, const GLint *v)
+void Renderer9::applyUniformnbv(const D3DUniform *targetUniform, const GLint *v)
 {
     ASSERT(targetUniform->registerCount <= MAX_VERTEX_CONSTANT_VECTORS_D3D9);
     GLfloat vector[MAX_VERTEX_CONSTANT_VECTORS_D3D9][4];
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
index 2cd84fc..95d9a52 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
@@ -32,10 +32,12 @@
 {
 class Blit9;
 class IndexDataManager;
+class ProgramD3D;
 class StreamingIndexBufferInterface;
 class StaticIndexBufferInterface;
 class VertexDataManager;
 struct ClearParameters;
+struct D3DUniform;
 struct TranslatedAttribute;
 
 enum D3D9InitError
@@ -111,7 +113,8 @@
                            const gl::Framebuffer *framebuffer,
                            bool rasterizerDiscard,
                            bool transformFeedbackActive) override;
-    virtual gl::Error applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray);
+    gl::Error applyUniforms(const ProgramD3D &programD3D,
+                            const std::vector<D3DUniform *> &uniformArray) override;
     virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize);
     virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceInfo);
     virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo, SourceIndexData *sourceIndexInfo);
@@ -258,9 +261,9 @@
 
     void release();
 
-    void applyUniformnfv(gl::LinkedUniform *targetUniform, const GLfloat *v);
-    void applyUniformniv(gl::LinkedUniform *targetUniform, const GLint *v);
-    void applyUniformnbv(gl::LinkedUniform *targetUniform, const GLint *v);
+    void applyUniformnfv(const D3DUniform *targetUniform, const GLfloat *v);
+    void applyUniformniv(const D3DUniform *targetUniform, const GLint *v);
+    void applyUniformnbv(const D3DUniform *targetUniform, const GLint *v);
 
     gl::Error drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer);
     gl::Error drawIndexedPoints(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer);
diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp
index 3a70ae9..bae8389 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.cpp
+++ b/src/libANGLE/renderer/gl/ProgramGL.cpp
@@ -115,68 +115,41 @@
     }
 
     // Query the uniform information
-    // TODO: A lot of this logic should be done at the gl::Program level
-    GLint activeUniformMaxLength = 0;
-    mFunctions->getProgramiv(mProgramID, GL_ACTIVE_UNIFORM_MAX_LENGTH, &activeUniformMaxLength);
-
-    std::vector<GLchar> uniformNameBuffer(activeUniformMaxLength);
-
-    GLint uniformCount = 0;
-    mFunctions->getProgramiv(mProgramID, GL_ACTIVE_UNIFORMS, &uniformCount);
-    for (GLint i = 0; i < uniformCount; i++)
+    ASSERT(mUniformRealLocationMap.empty());
+    const auto &uniforms = mData.getUniforms();
+    for (const gl::VariableLocation &entry : mData.getUniformLocations())
     {
-        GLsizei uniformNameLength = 0;
-        GLint uniformSize = 0;
-        GLenum uniformType = GL_NONE;
-        mFunctions->getActiveUniform(mProgramID, i, static_cast<GLsizei>(uniformNameBuffer.size()),
-                                     &uniformNameLength, &uniformSize, &uniformType,
-                                     &uniformNameBuffer[0]);
-
-        size_t subscript = 0;
-        std::string uniformName = gl::ParseUniformName(std::string(&uniformNameBuffer[0], uniformNameLength), &subscript);
-
-        bool isArray = uniformSize > 1 || subscript != GL_INVALID_INDEX;
-
-        for (size_t arrayIndex = 0; arrayIndex < static_cast<size_t>(uniformSize); arrayIndex++)
+        // From the spec:
+        // "Locations for sequential array indices are not required to be sequential."
+        const gl::LinkedUniform &uniform = uniforms[entry.index];
+        std::stringstream fullNameStr;
+        fullNameStr << uniform.name;
+        if (uniform.isArray())
         {
-            std::string locationName = uniformName;
-            if (isArray)
-            {
-                locationName += "[" + Str(static_cast<int>(arrayIndex)) + "]";
-            }
-
-            GLint location = mFunctions->getUniformLocation(mProgramID, locationName.c_str());
-            if (location >= 0)
-            {
-                mUniformIndex[location] =
-                    gl::VariableLocation(uniformName, static_cast<unsigned int>(arrayIndex),
-                                         static_cast<unsigned int>(mUniforms.size()));
-
-                // If the uniform is a sampler, track it in the sampler bindings array
-                if (gl::IsSamplerType(uniformType))
-                {
-                    SamplerLocation samplerLoc;
-                    samplerLoc.samplerIndex = mSamplerBindings.size();
-                    samplerLoc.arrayIndex = arrayIndex;
-                    mSamplerUniformMap[location] = samplerLoc;
-                }
-            }
+            fullNameStr << "[" << entry.element << "]";
         }
+        const std::string &fullName = fullNameStr.str();
 
-        // ANGLE uses 0 to identify an non-array uniform.
-        unsigned int arraySize = isArray ? static_cast<unsigned int>(uniformSize) : 0;
+        GLint realLocation = mFunctions->getUniformLocation(mProgramID, fullName.c_str());
+        mUniformRealLocationMap.push_back(realLocation);
+    }
 
-        // TODO: determine uniform precision
-        mUniforms.push_back(new gl::LinkedUniform(uniformType, GL_NONE, uniformName, arraySize, -1, sh::BlockMemberInfo::getDefaultBlockInfo()));
+    mUniformIndexToSamplerIndex.resize(mData.getUniforms().size(), GL_INVALID_INDEX);
+
+    for (size_t uniformId = 0; uniformId < uniforms.size(); ++uniformId)
+    {
+        const gl::LinkedUniform &linkedUniform = uniforms[uniformId];
+
+        if (!linkedUniform.isSampler() || !linkedUniform.staticUse)
+            continue;
+
+        mUniformIndexToSamplerIndex[uniformId] = mSamplerBindings.size();
 
         // If uniform is a sampler type, insert it into the mSamplerBindings array
-        if (gl::IsSamplerType(uniformType))
-        {
-            SamplerBindingGL samplerBinding;
-            samplerBinding.textureType = gl::SamplerTypeToTextureType(uniformType);
-            samplerBinding.boundTextureUnits.resize(uniformSize, 0);
-            mSamplerBindings.push_back(samplerBinding);
-        }
+        SamplerBindingGL samplerBinding;
+        samplerBinding.textureType = gl::SamplerTypeToTextureType(linkedUniform.type);
+        samplerBinding.boundTextureUnits.resize(linkedUniform.elementCount(), 0);
+        mSamplerBindings.push_back(samplerBinding);
     }
 
     return LinkResult(true, gl::Error(GL_NO_ERROR));
@@ -191,59 +164,61 @@
 void ProgramGL::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform1fv(location, count, v);
+    mFunctions->uniform1fv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform2fv(location, count, v);
+    mFunctions->uniform2fv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform3fv(location, count, v);
+    mFunctions->uniform3fv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform4fv(location, count, v);
+    mFunctions->uniform4fv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform1iv(GLint location, GLsizei count, const GLint *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform1iv(location, count, v);
+    mFunctions->uniform1iv(uniLoc(location), count, v);
 
-    auto iter = mSamplerUniformMap.find(location);
-    if (iter != mSamplerUniformMap.end())
+    const gl::VariableLocation &locationEntry = mData.getUniformLocations()[location];
+
+    size_t samplerIndex = mUniformIndexToSamplerIndex[locationEntry.index];
+    if (samplerIndex != GL_INVALID_INDEX)
     {
-        const SamplerLocation &samplerLoc = iter->second;
-        std::vector<GLuint> &boundTextureUnits = mSamplerBindings[samplerLoc.samplerIndex].boundTextureUnits;
+        std::vector<GLuint> &boundTextureUnits = mSamplerBindings[samplerIndex].boundTextureUnits;
 
-        size_t copyCount = std::max<size_t>(count, boundTextureUnits.size() - samplerLoc.arrayIndex);
-        std::copy(v, v + copyCount, boundTextureUnits.begin() + samplerLoc.arrayIndex);
+        size_t copyCount =
+            std::max<size_t>(count, boundTextureUnits.size() - locationEntry.element);
+        std::copy(v, v + copyCount, boundTextureUnits.begin() + locationEntry.element);
     }
 }
 
 void ProgramGL::setUniform2iv(GLint location, GLsizei count, const GLint *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform2iv(location, count, v);
+    mFunctions->uniform2iv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform3iv(GLint location, GLsizei count, const GLint *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform3iv(location, count, v);
+    mFunctions->uniform3iv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform4iv(GLint location, GLsizei count, const GLint *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform4iv(location, count, v);
+    mFunctions->uniform4iv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform1uiv(GLint location, GLsizei count, const GLuint *v)
@@ -255,88 +230,73 @@
 void ProgramGL::setUniform2uiv(GLint location, GLsizei count, const GLuint *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform2uiv(location, count, v);
+    mFunctions->uniform2uiv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform3uiv(GLint location, GLsizei count, const GLuint *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform3uiv(location, count, v);
+    mFunctions->uniform3uiv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniform4uiv(GLint location, GLsizei count, const GLuint *v)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniform4uiv(location, count, v);
+    mFunctions->uniform4uiv(uniLoc(location), count, v);
 }
 
 void ProgramGL::setUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix2fv(location, count, transpose, value);
+    mFunctions->uniformMatrix2fv(uniLoc(location), count, transpose, value);
 }
 
 void ProgramGL::setUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix3fv(location, count, transpose, value);
+    mFunctions->uniformMatrix3fv(uniLoc(location), count, transpose, value);
 }
 
 void ProgramGL::setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix4fv(location, count, transpose, value);
+    mFunctions->uniformMatrix4fv(uniLoc(location), count, transpose, value);
 }
 
 void ProgramGL::setUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix2x3fv(location, count, transpose, value);
+    mFunctions->uniformMatrix2x3fv(uniLoc(location), count, transpose, value);
 }
 
 void ProgramGL::setUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix3x2fv(location, count, transpose, value);
+    mFunctions->uniformMatrix3x2fv(uniLoc(location), count, transpose, value);
 }
 
 void ProgramGL::setUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix2x4fv(location, count, transpose, value);
+    mFunctions->uniformMatrix2x4fv(uniLoc(location), count, transpose, value);
 }
 
 void ProgramGL::setUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix4x2fv(location, count, transpose, value);
+    mFunctions->uniformMatrix4x2fv(uniLoc(location), count, transpose, value);
 }
 
 void ProgramGL::setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix3x4fv(location, count, transpose, value);
+    mFunctions->uniformMatrix3x4fv(uniLoc(location), count, transpose, value);
 }
 
 void ProgramGL::setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     mStateManager->useProgram(mProgramID);
-    mFunctions->uniformMatrix4x3fv(location, count, transpose, value);
-}
-
-void ProgramGL::getUniformfv(GLint location, GLfloat *params)
-{
-    mFunctions->getUniformfv(mProgramID, location, params);
-}
-
-void ProgramGL::getUniformiv(GLint location, GLint *params)
-{
-    mFunctions->getUniformiv(mProgramID, location, params);
-}
-
-void ProgramGL::getUniformuiv(GLint location, GLuint *params)
-{
-    mFunctions->getUniformuiv(mProgramID, location, params);
+    mFunctions->uniformMatrix4x3fv(uniLoc(location), count, transpose, value);
 }
 
 bool ProgramGL::validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps)
@@ -347,10 +307,9 @@
 
 void ProgramGL::reset()
 {
-    ProgramImpl::reset();
-
-    mSamplerUniformMap.clear();
+    mUniformRealLocationMap.clear();
     mSamplerBindings.clear();
+    mUniformIndexToSamplerIndex.clear();
 }
 
 GLuint ProgramGL::getProgramID() const
@@ -363,4 +322,9 @@
     return mSamplerBindings;
 }
 
+void ProgramGL::gatherUniformBlockInfo(std::vector<gl::UniformBlock> * /*uniformBlocks*/,
+                                       std::vector<gl::LinkedUniform> * /*uniforms*/)
+{
+    // TODO(jmadill): Gather uniform block layout info, and data sizes.
+}
 }
diff --git a/src/libANGLE/renderer/gl/ProgramGL.h b/src/libANGLE/renderer/gl/ProgramGL.h
index e83d7da..9cc0a14 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.h
+++ b/src/libANGLE/renderer/gl/ProgramGL.h
@@ -62,32 +62,31 @@
     void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override;
     void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override;
 
-    void getUniformfv(GLint location, GLfloat *params) override;
-    void getUniformiv(GLint location, GLint *params) override;
-    void getUniformuiv(GLint location, GLuint *params) override;
-
     bool validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps) override;
 
-    void reset() override;
+    void gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
+                                std::vector<gl::LinkedUniform> *uniforms) override;
 
     GLuint getProgramID() const;
     const std::vector<SamplerBindingGL> &getAppliedSamplerUniforms() const;
 
   private:
+    void reset();
+
+    // Helper function, makes it simpler to type.
+    GLint uniLoc(GLint glLocation) const { return mUniformRealLocationMap[glLocation]; }
+
     const FunctionsGL *mFunctions;
     StateManagerGL *mStateManager;
 
-    // A map from uniform location to index of mSamplerBindings and array index of the uniform
-    struct SamplerLocation
-    {
-        size_t samplerIndex;
-        size_t arrayIndex;
-    };
-    std::map<GLint, SamplerLocation> mSamplerUniformMap;
+    std::vector<GLint> mUniformRealLocationMap;
 
     // An array of the samplers that are used by the program
     std::vector<SamplerBindingGL> mSamplerBindings;
 
+    // A map from a mData.getUniforms() index to a mSamplerBindings index.
+    std::vector<size_t> mUniformIndexToSamplerIndex;
+
     GLuint mProgramID;
 };
 
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index d8e1dd7..db00caf 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -1145,8 +1145,11 @@
     return true;
 }
 
-static bool ValidateUniformCommonBase(gl::Context *context, GLenum targetUniformType,
-                                      GLint location, GLsizei count, LinkedUniform **uniformOut)
+static bool ValidateUniformCommonBase(gl::Context *context,
+                                      GLenum targetUniformType,
+                                      GLint location,
+                                      GLsizei count,
+                                      const LinkedUniform **uniformOut)
 {
     if (count < 0)
     {
@@ -1173,16 +1176,16 @@
         return false;
     }
 
-    LinkedUniform *uniform = program->getUniformByLocation(location);
+    const LinkedUniform &uniform = program->getUniformByLocation(location);
 
     // attempting to write an array to a non-array uniform is an INVALID_OPERATION
-    if (!uniform->isArray() && count > 1)
+    if (!uniform.isArray() && count > 1)
     {
         context->recordError(Error(GL_INVALID_OPERATION));
         return false;
     }
 
-    *uniformOut = uniform;
+    *uniformOut = &uniform;
     return true;
 }
 
@@ -1195,7 +1198,7 @@
         return false;
     }
 
-    LinkedUniform *uniform = NULL;
+    const LinkedUniform *uniform = nullptr;
     if (!ValidateUniformCommonBase(context, uniformType, location, count, &uniform))
     {
         return false;
@@ -1230,7 +1233,7 @@
         return false;
     }
 
-    LinkedUniform *uniform = NULL;
+    const LinkedUniform *uniform = nullptr;
     if (!ValidateUniformCommonBase(context, matrixType, location, count, &uniform))
     {
         return false;
@@ -1526,7 +1529,7 @@
     // Uniform buffer validation
     for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
     {
-        const gl::UniformBlock *uniformBlock = program->getUniformBlockByIndex(uniformBlockIndex);
+        const gl::UniformBlock &uniformBlock = program->getUniformBlockByIndex(uniformBlockIndex);
         GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex);
         const gl::Buffer *uniformBuffer = state.getIndexedUniformBuffer(blockBinding);
 
@@ -1545,7 +1548,7 @@
             uniformBufferSize = static_cast<size_t>(uniformBuffer->getSize());
         }
 
-        if (uniformBufferSize < uniformBlock->dataSize)
+        if (uniformBufferSize < uniformBlock.dataSize)
         {
             // undefined behaviour
             context->recordError(Error(GL_INVALID_OPERATION, "It is undefined behaviour to use a uniform buffer that is too small."));
@@ -1944,8 +1947,8 @@
     ASSERT(programObject);
 
     // sized queries -- ensure the provided buffer is large enough
-    LinkedUniform *uniform = programObject->getUniformByLocation(location);
-    size_t requiredBytes = VariableExternalSize(uniform->type);
+    const LinkedUniform &uniform = programObject->getUniformByLocation(location);
+    size_t requiredBytes = VariableExternalSize(uniform.type);
     if (static_cast<size_t>(bufSize) < requiredBytes)
     {
         context->recordError(Error(GL_INVALID_OPERATION));
diff --git a/src/libGLESv2.gypi b/src/libGLESv2.gypi
index b27fabc..634e3e4 100644
--- a/src/libGLESv2.gypi
+++ b/src/libGLESv2.gypi
@@ -140,7 +140,6 @@
             'libANGLE/renderer/FramebufferImpl.h',
             'libANGLE/renderer/ImageImpl.h',
             'libANGLE/renderer/ImplFactory.h',
-            'libANGLE/renderer/ProgramImpl.cpp',
             'libANGLE/renderer/ProgramImpl.h',
             'libANGLE/renderer/QueryImpl.h',
             'libANGLE/renderer/RenderbufferImpl.h',
diff --git a/src/tests/deqp_support/deqp_gles2_test_expectations.txt b/src/tests/deqp_support/deqp_gles2_test_expectations.txt
index e478f98..4c3bb61 100644
--- a/src/tests/deqp_support/deqp_gles2_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles2_test_expectations.txt
@@ -144,9 +144,12 @@
 1031 WIN : dEQP-GLES2.functional.uniform_api.random.41 = FAIL
 1031 WIN : dEQP-GLES2.functional.uniform_api.random.51 = FAIL
 1031 WIN : dEQP-GLES2.functional.uniform_api.random.54 = FAIL
+1031 WIN : dEQP-GLES2.functional.uniform_api.random.61 = FAIL
 1031 WIN : dEQP-GLES2.functional.uniform_api.random.72 = FAIL
 1031 WIN : dEQP-GLES2.functional.uniform_api.random.79 = FAIL
 1031 WIN : dEQP-GLES2.functional.uniform_api.random.82 = FAIL
+1031 WIN : dEQP-GLES2.functional.uniform_api.random.87 = FAIL
+1031 WIN : dEQP-GLES2.functional.uniform_api.random.93 = FAIL
 504 WIN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic_struct.sampler2D_samplerCube_* = FAIL
 504 WIN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.struct_in_array.sampler2D_samplerCube_* = FAIL
 504 WIN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.array_in_struct.sampler2D_samplerCube_* = FAIL
diff --git a/src/tests/deqp_support/deqp_gles3_test_expectations.txt b/src/tests/deqp_support/deqp_gles3_test_expectations.txt
index 727fc1f..5e80236 100644
--- a/src/tests/deqp_support/deqp_gles3_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles3_test_expectations.txt
@@ -982,11 +982,14 @@
 1098 WIN : dEQP-GLES3.functional.uniform_api.random.3 = FAIL
 1098 WIN : dEQP-GLES3.functional.uniform_api.random.6 = FAIL
 1098 WIN : dEQP-GLES3.functional.uniform_api.random.8 = FAIL
+1098 WIN : dEQP-GLES3.functional.uniform_api.random.17 = FAIL
 1098 WIN : dEQP-GLES3.functional.uniform_api.random.20 = FAIL
 1098 WIN : dEQP-GLES3.functional.uniform_api.random.21 = FAIL
 1098 WIN : dEQP-GLES3.functional.uniform_api.random.29 = FAIL
+1098 WIN : dEQP-GLES3.functional.uniform_api.random.54 = FAIL
 1098 WIN : dEQP-GLES3.functional.uniform_api.random.81 = FAIL
 1098 WIN : dEQP-GLES3.functional.uniform_api.random.83 = FAIL
+1098 WIN : dEQP-GLES3.functional.uniform_api.random.87 = FAIL
 1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_float = FAIL
 1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.max_cond_float = FAIL
 1099 WIN : dEQP-GLES3.functional.attribute_location.bind_aliasing.cond_vec2 = FAIL
diff --git a/src/tests/gl_tests/UniformBufferTest.cpp b/src/tests/gl_tests/UniformBufferTest.cpp
index dcb1750..d25c5ef 100644
--- a/src/tests/gl_tests/UniformBufferTest.cpp
+++ b/src/tests/gl_tests/UniformBufferTest.cpp
@@ -313,6 +313,51 @@
     }
 }
 
+// Tests that active uniforms have the right names.
+TEST_P(UniformBufferTest, ActiveUniformNames)
+{
+    const std::string &vertexShaderSource =
+        "#version 300 es\n"
+        "in vec2 position;\n"
+        "out float v;\n"
+        "uniform blockName {\n"
+        "  float f;\n"
+        "} instanceName;\n"
+        "void main() {\n"
+        "  v = instanceName.f;\n"
+        "  gl_Position = vec4(position, 0, 1);\n"
+        "}";
+
+    const std::string &fragmentShaderSource =
+        "#version 300 es\n"
+        "precision highp float;\n"
+        "in float v;\n"
+        "out vec4 color;\n"
+        "void main() {\n"
+        "  color = vec4(v, 0, 0, 1);\n"
+        "}";
+
+    GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
+    ASSERT_NE(0u, program);
+
+    GLint activeUniforms;
+    glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms);
+
+    ASSERT_EQ(1, activeUniforms);
+
+    GLint maxLength, size;
+    GLenum type;
+    GLsizei length;
+    glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
+    std::vector<GLchar> strBuffer(maxLength + 1, 0);
+    glGetActiveUniform(program, 0, maxLength, &length, &size, &type, &strBuffer[0]);
+
+    ASSERT_GL_NO_ERROR();
+    EXPECT_EQ(1, size);
+    EXPECT_EQ(GL_FLOAT, type);
+    EXPECT_EQ("blockName.f", std::string(&strBuffer[0]));
+}
+
 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
 ANGLE_INSTANTIATE_TEST(UniformBufferTest, ES3_D3D11(), ES3_D3D11_FL11_1(), ES3_D3D11_FL11_1_REFERENCE());
 
diff --git a/src/tests/gl_tests/UniformTest.cpp b/src/tests/gl_tests/UniformTest.cpp
index 581f5ff..6f34218 100644
--- a/src/tests/gl_tests/UniformTest.cpp
+++ b/src/tests/gl_tests/UniformTest.cpp
@@ -16,11 +16,7 @@
 class UniformTest : public ANGLETest
 {
   protected:
-    UniformTest()
-        : mProgram(0),
-          mUniformFLocation(-1),
-          mUniformILocation(-1),
-          mUniformBLocation(-1)
+    UniformTest() : mProgram(0), mUniformFLocation(-1), mUniformILocation(-1), mUniformBLocation(-1)
     {
         setWindowWidth(128);
         setWindowHeight(128);
@@ -40,7 +36,12 @@
             "uniform float uniF;\n"
             "uniform int uniI;\n"
             "uniform bool uniB;\n"
-            "void main() { gl_FragColor = vec4(uniF + float(uniI) + (uniB ? 1.0 : 0.0)); }";
+            "uniform bool uniBArr[4];\n"
+            "void main() {\n"
+            "  gl_FragColor = vec4(uniF + float(uniI));\n"
+            "  gl_FragColor += vec4(uniB ? 1.0 : 0.0);\n"
+            "  gl_FragColor += vec4(uniBArr[0] ? 1.0 : 0.0);\n"
+            "}";
 
         mProgram = CompileProgram(vertexShader, fragShader);
         ASSERT_NE(mProgram, 0u);
@@ -169,13 +170,6 @@
 // Test that float to integer GetUniform rounds values correctly.
 TEST_P(UniformTest, FloatUniformStateQuery)
 {
-    // TODO(jmadill): remove this suppression once we support ANGLE-only state queries.
-    if (isAMD() && (GetParam() == ES2_OPENGL() || GetParam() == ES3_OPENGL()))
-    {
-        std::cout << "Skipping test due to a driver bug on AMD." << std::endl;
-        return;
-    }
-
     std::vector<GLfloat> inValues;
     std::vector<GLfloat> expectedFValues;
     std::vector<GLint> expectedIValues;
@@ -214,7 +208,7 @@
 
     for (size_t index = 0; index < inValues.size(); ++index)
     {
-        GLfloat inValue = inValues[index];
+        GLfloat inValue       = inValues[index];
         GLfloat expectedValue = expectedFValues[index];
 
         glUniform1f(mUniformFLocation, inValue);
@@ -226,7 +220,7 @@
 
     for (size_t index = 0; index < inValues.size(); ++index)
     {
-        GLfloat inValue = inValues[index];
+        GLfloat inValue     = inValues[index];
         GLint expectedValue = expectedIValues[index];
 
         glUniform1f(mUniformFLocation, inValue);
@@ -240,13 +234,6 @@
 // Test that integer to float GetUniform rounds values correctly.
 TEST_P(UniformTest, IntUniformStateQuery)
 {
-    // TODO(jmadill): remove this suppression once we support ANGLE-only state queries.
-    if ((isAMD() || isIntel()) && (GetParam() == ES2_OPENGL() || GetParam() == ES3_OPENGL()))
-    {
-        std::cout << "Skipping test due to a driver bug." << std::endl;
-        return;
-    }
-
     std::vector<GLint> inValues;
     std::vector<GLint> expectedIValues;
     std::vector<GLfloat> expectedFValues;
@@ -274,7 +261,7 @@
 
     for (size_t index = 0; index < inValues.size(); ++index)
     {
-        GLint inValue = inValues[index];
+        GLint inValue       = inValues[index];
         GLint expectedValue = expectedIValues[index];
 
         glUniform1i(mUniformILocation, inValue);
@@ -286,7 +273,7 @@
 
     for (size_t index = 0; index < inValues.size(); ++index)
     {
-        GLint inValue = inValues[index];
+        GLint inValue         = inValues[index];
         GLfloat expectedValue = expectedFValues[index];
 
         glUniform1i(mUniformILocation, inValue);
@@ -301,9 +288,10 @@
 TEST_P(UniformTest, BooleanUniformStateQuery)
 {
     glUseProgram(mProgram);
-    GLint intValue = 0;
+    GLint intValue     = 0;
     GLfloat floatValue = 0.0f;
 
+    // Calling Uniform1i
     glUniform1i(mUniformBLocation, GL_FALSE);
 
     glGetUniformiv(mProgram, mUniformBLocation, &intValue);
@@ -320,6 +308,67 @@
     glGetUniformfv(mProgram, mUniformBLocation, &floatValue);
     EXPECT_EQ(1.0f, floatValue);
 
+    // Calling Uniform1f
+    glUniform1f(mUniformBLocation, 0.0f);
+
+    glGetUniformiv(mProgram, mUniformBLocation, &intValue);
+    EXPECT_EQ(0, intValue);
+
+    glGetUniformfv(mProgram, mUniformBLocation, &floatValue);
+    EXPECT_EQ(0.0f, floatValue);
+
+    glUniform1f(mUniformBLocation, 1.0f);
+
+    glGetUniformiv(mProgram, mUniformBLocation, &intValue);
+    EXPECT_EQ(1, intValue);
+
+    glGetUniformfv(mProgram, mUniformBLocation, &floatValue);
+    EXPECT_EQ(1.0f, floatValue);
+
+    ASSERT_GL_NO_ERROR();
+}
+
+// Test queries for arrays of boolean uniforms.
+TEST_P(UniformTest, BooleanArrayUniformStateQuery)
+{
+    glUseProgram(mProgram);
+    GLint intValues[4]     = {0};
+    GLfloat floatValues[4] = {0.0f};
+    GLint boolValuesi[4]   = {0, 1, 0, 1};
+    GLfloat boolValuesf[4] = {0, 1, 0, 1};
+
+    GLint location = glGetUniformLocation(mProgram, "uniBArr");
+
+    // Calling Uniform1iv
+    glUniform1iv(location, 4, boolValuesi);
+
+    glGetUniformiv(mProgram, location, intValues);
+    for (unsigned int idx = 0; idx < 4; ++idx)
+    {
+        EXPECT_EQ(boolValuesi[idx], intValues[idx]);
+    }
+
+    glGetUniformfv(mProgram, location, floatValues);
+    for (unsigned int idx = 0; idx < 4; ++idx)
+    {
+        EXPECT_EQ(boolValuesf[idx], floatValues[idx]);
+    }
+
+    // Calling Uniform1fv
+    glUniform1fv(location, 4, boolValuesf);
+
+    glGetUniformiv(mProgram, location, intValues);
+    for (unsigned int idx = 0; idx < 4; ++idx)
+    {
+        EXPECT_EQ(boolValuesi[idx], intValues[idx]);
+    }
+
+    glGetUniformfv(mProgram, location, floatValues);
+    for (unsigned int idx = 0; idx < 4; ++idx)
+    {
+        EXPECT_EQ(boolValuesf[idx], floatValues[idx]);
+    }
+
     ASSERT_GL_NO_ERROR();
 }