Re^6-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.

Re^5-land with a fix for a test warning on Linux.

Re^6-land with a fix for transposed matrix uniform arrays.

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

Change-Id: Ie6fcde1c16eb05d67191b629338b88302a2563f5
Reviewed-on: https://chromium-review.googlesource.com/298971
Tryjob-Request: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 30386f7..6922adf 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,287 @@
         }
     }
 }
+
+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 (GLsizei element = 0; element < count; ++element)
+    {
+        size_t elementOffset = element * rows * cols;
+
+        for (size_t row = 0; row < rows; ++row)
+        {
+            for (size_t col = 0; col < cols; ++col)
+            {
+                destPtr[col * rows + row + elementOffset] = v[row * cols + col + elementOffset];
+            }
+        }
+    }
+}
+
+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();
+    }
+}
 }