Refactor interface block linking.

This moves the logic for interface block linking. The new design is
intended to be flexible enough to be passed into a back-end call to
ProgramImpl::link. Then, the Impl object can be responsible for both
filtering out unreferenced interface blocks as well as having access
to the linked interface block information.

A future change will pass the InterfaceBlockLinker objects (or objects
when dealing with ES 3.1 and Shader Storage Blocks) to the Impl. This
will help fix a D3D11 back-end bug where we would need acess to the
Shader objects to finish a deferred uniform block link.

This should also potentially make it easier for the back-ends to
determine Shader Storage Block size and properties without defining
new Impl methods.

BUG=angleproject:2208

Change-Id: Ic5244a808dba44ba1a8c08d9ee701952034d2b18
Reviewed-on: https://chromium-review.googlesource.com/746203
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 286aedc..de9724c 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -2826,71 +2826,76 @@
     // TODO(jie.a.chen@intel.com): Get the actual BUFFER_DATA_SIZE from backend for each buffer.
 }
 
-void Program::gatherComputeBlockInfo(const std::vector<sh::InterfaceBlock> &computeBlocks)
+void Program::gatherUniformBlockInfo(const gl::Context *context)
 {
-    for (const sh::InterfaceBlock &computeBlock : computeBlocks)
+    UniformBlockLinker blockLinker(&mState.mUniformBlocks, &mState.mUniforms);
+
+    if (mState.mAttachedVertexShader)
     {
-
-        // Only 'packed' blocks are allowed to be considered inactive.
-        if (!computeBlock.staticUse && computeBlock.layout == sh::BLOCKLAYOUT_PACKED)
-            continue;
-
-        defineInterfaceBlock(computeBlock, GL_COMPUTE_SHADER);
+        blockLinker.addShaderBlocks(GL_VERTEX_SHADER,
+                                    &mState.mAttachedVertexShader->getUniformBlocks(context));
     }
+
+    if (mState.mAttachedFragmentShader)
+    {
+        blockLinker.addShaderBlocks(GL_FRAGMENT_SHADER,
+                                    &mState.mAttachedFragmentShader->getUniformBlocks(context));
+    }
+
+    if (mState.mAttachedComputeShader)
+    {
+        blockLinker.addShaderBlocks(GL_COMPUTE_SHADER,
+                                    &mState.mAttachedComputeShader->getUniformBlocks(context));
+    }
+
+    auto getImplBlockSize = [this](const std::string &name, const std::string &mappedName,
+                                   size_t *sizeOut) {
+        return this->mProgram->getUniformBlockSize(name, mappedName, sizeOut);
+    };
+
+    auto getImplMemberInfo = [this](const std::string &name, const std::string &mappedName,
+                                    sh::BlockMemberInfo *infoOut) {
+        return this->mProgram->getUniformBlockMemberInfo(name, mappedName, infoOut);
+    };
+
+    blockLinker.linkBlocks(getImplBlockSize, getImplMemberInfo);
 }
 
-void Program::gatherVertexAndFragmentBlockInfo(
-    const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks,
-    const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks)
+void Program::gatherShaderStorageBlockInfo(const gl::Context *context)
 {
-    std::set<std::string> visitedList;
+    ShaderStorageBlockLinker blockLinker(&mState.mShaderStorageBlocks);
 
-    for (const sh::InterfaceBlock &vertexBlock : vertexInterfaceBlocks)
+    if (mState.mAttachedVertexShader)
     {
-        // Only 'packed' blocks are allowed to be considered inactive.
-        if (!vertexBlock.staticUse && vertexBlock.layout == sh::BLOCKLAYOUT_PACKED)
-            continue;
-
-        defineInterfaceBlock(vertexBlock, GL_VERTEX_SHADER);
-        visitedList.insert(vertexBlock.name);
+        blockLinker.addShaderBlocks(GL_VERTEX_SHADER,
+                                    &mState.mAttachedVertexShader->getShaderStorageBlocks(context));
     }
 
-    for (const sh::InterfaceBlock &fragmentBlock : fragmentInterfaceBlocks)
+    if (mState.mAttachedFragmentShader)
     {
-        // Only 'packed' blocks are allowed to be considered inactive.
-        if (!fragmentBlock.staticUse && fragmentBlock.layout == sh::BLOCKLAYOUT_PACKED)
-            continue;
-
-        if (visitedList.count(fragmentBlock.name) > 0)
-        {
-            if (fragmentBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
-            {
-                for (InterfaceBlock &block : mState.mUniformBlocks)
-                {
-                    if (block.name == fragmentBlock.name)
-                    {
-                        block.fragmentStaticUse = fragmentBlock.staticUse;
-                    }
-                }
-            }
-            else
-            {
-                ASSERT(fragmentBlock.blockType == sh::BlockType::BLOCK_BUFFER);
-                for (InterfaceBlock &block : mState.mShaderStorageBlocks)
-                {
-                    if (block.name == fragmentBlock.name)
-                    {
-                        block.fragmentStaticUse = fragmentBlock.staticUse;
-                    }
-                }
-            }
-
-            continue;
-        }
-
-        defineInterfaceBlock(fragmentBlock, GL_FRAGMENT_SHADER);
-        visitedList.insert(fragmentBlock.name);
+        blockLinker.addShaderBlocks(
+            GL_FRAGMENT_SHADER, &mState.mAttachedFragmentShader->getShaderStorageBlocks(context));
     }
+
+    if (mState.mAttachedComputeShader)
+    {
+        blockLinker.addShaderBlocks(
+            GL_COMPUTE_SHADER, &mState.mAttachedComputeShader->getShaderStorageBlocks(context));
+    }
+
+    // We don't have a way of determining block info for shader storage blocks yet.
+    // TODO(jiajia.qin@intel.com): Determine correct block size and layout.
+    auto getImplBlockSize = [this](const std::string &name, const std::string &mappedName,
+                                   size_t *sizeOut) {
+        return this->mProgram->getUniformBlockSize(name, mappedName, sizeOut);
+    };
+
+    auto getImplMemberInfo = [this](const std::string &name, const std::string &mappedName,
+                                    sh::BlockMemberInfo *infoOut) {
+        return this->mProgram->getUniformBlockMemberInfo(name, mappedName, infoOut);
+    };
+
+    blockLinker.linkBlocks(getImplBlockSize, getImplMemberInfo);
 }
 
 void Program::gatherInterfaceBlockInfo(const Context *context)
@@ -2898,24 +2903,10 @@
     ASSERT(mState.mUniformBlocks.empty());
     ASSERT(mState.mShaderStorageBlocks.empty());
 
-    if (mState.mAttachedComputeShader)
-    {
-        Shader *computeShader = mState.getAttachedComputeShader();
-
-        gatherComputeBlockInfo(computeShader->getUniformBlocks(context));
-        gatherComputeBlockInfo(computeShader->getShaderStorageBlocks(context));
-        return;
-    }
-
-    Shader *vertexShader   = mState.getAttachedVertexShader();
-    Shader *fragmentShader = mState.getAttachedFragmentShader();
-
-    gatherVertexAndFragmentBlockInfo(vertexShader->getUniformBlocks(context),
-                                     fragmentShader->getUniformBlocks(context));
+    gatherUniformBlockInfo(context);
     if (context->getClientVersion() >= Version(3, 1))
     {
-        gatherVertexAndFragmentBlockInfo(vertexShader->getShaderStorageBlocks(context),
-                                         fragmentShader->getShaderStorageBlocks(context));
+        gatherShaderStorageBlockInfo(context);
     }
 
     // Set initial bindings from shader.
@@ -2926,154 +2917,6 @@
     }
 }
 
-template <typename VarT>
-void Program::defineUniformBlockMembers(const std::vector<VarT> &fields,
-                                        const std::string &prefix,
-                                        const std::string &mappedPrefix,
-                                        int blockIndex)
-{
-    for (const VarT &field : fields)
-    {
-        std::string fullName = (prefix.empty() ? field.name : prefix + "." + field.name);
-
-        std::string fullMappedName =
-            (mappedPrefix.empty() ? field.mappedName : mappedPrefix + "." + field.mappedName);
-
-        if (field.isStruct())
-        {
-            for (unsigned int arrayElement = 0; arrayElement < field.elementCount(); arrayElement++)
-            {
-                const std::string uniformElementName =
-                    fullName + (field.isArray() ? ArrayString(arrayElement) : "");
-                const std::string uniformElementMappedName =
-                    fullMappedName + (field.isArray() ? ArrayString(arrayElement) : "");
-                defineUniformBlockMembers(field.fields, uniformElementName,
-                                          uniformElementMappedName, blockIndex);
-            }
-        }
-        else
-        {
-            // If getBlockMemberInfo returns false, the uniform is optimized out.
-            sh::BlockMemberInfo memberInfo;
-            if (!mProgram->getUniformBlockMemberInfo(fullName, fullMappedName, &memberInfo))
-            {
-                continue;
-            }
-
-            if (field.isArray())
-            {
-                fullName += "[0]";
-                fullMappedName += "[0]";
-            }
-
-            LinkedUniform newUniform(field.type, field.precision, fullName, field.arraySize, -1, -1,
-                                     -1, blockIndex, memberInfo);
-            newUniform.mappedName = fullMappedName;
-
-            // Since block uniforms have no location, we don't need to store them in the uniform
-            // locations list.
-            mState.mUniforms.push_back(newUniform);
-        }
-    }
-}
-
-void Program::defineInterfaceBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType)
-{
-    size_t blockSize = 0;
-    std::vector<unsigned int> blockIndexes;
-
-    if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
-    {
-        int blockIndex = static_cast<int>(mState.mUniformBlocks.size());
-        // Track the first and last uniform index to determine the range of active uniforms in the
-        // block.
-        size_t firstBlockUniformIndex = mState.mUniforms.size();
-        defineUniformBlockMembers(interfaceBlock.fields, interfaceBlock.fieldPrefix(),
-                                  interfaceBlock.fieldMappedPrefix(), blockIndex);
-        size_t lastBlockUniformIndex = mState.mUniforms.size();
-
-        for (size_t blockUniformIndex = firstBlockUniformIndex;
-             blockUniformIndex < lastBlockUniformIndex; ++blockUniformIndex)
-        {
-            blockIndexes.push_back(static_cast<unsigned int>(blockUniformIndex));
-        }
-    }
-    else
-    {
-        // TODO(jiajia.qin@intel.com) : Add buffer variables support and calculate the block index.
-        ASSERT(interfaceBlock.blockType == sh::BlockType::BLOCK_BUFFER);
-    }
-    // ESSL 3.10 section 4.4.4 page 58:
-    // Any uniform or shader storage block declared without a binding qualifier is initially
-    // assigned to block binding point zero.
-    int blockBinding = (interfaceBlock.binding == -1 ? 0 : interfaceBlock.binding);
-    if (interfaceBlock.arraySize > 0)
-    {
-        for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.arraySize; ++arrayElement)
-        {
-            // TODO(jiajia.qin@intel.com) : use GetProgramResourceiv to calculate BUFFER_DATA_SIZE
-            // of UniformBlock and ShaderStorageBlock.
-            if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
-            {
-                // Don't define this block at all if it's not active in the implementation.
-                if (!mProgram->getUniformBlockSize(
-                        interfaceBlock.name + ArrayString(arrayElement),
-                        interfaceBlock.mappedName + ArrayString(arrayElement), &blockSize))
-                {
-                    continue;
-                }
-            }
-
-            InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName, true, arrayElement,
-                                 blockBinding + arrayElement);
-            block.memberIndexes = blockIndexes;
-            block.setStaticUse(shaderType, interfaceBlock.staticUse);
-
-            // Since all block elements in an array share the same active interface blocks, they
-            // will all be active once any block member is used. So, since interfaceBlock.name[0]
-            // was active, here we will add every block element in the array.
-            block.dataSize = static_cast<unsigned int>(blockSize);
-            if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
-            {
-                mState.mUniformBlocks.push_back(block);
-            }
-            else
-            {
-                ASSERT(interfaceBlock.blockType == sh::BlockType::BLOCK_BUFFER);
-                mState.mShaderStorageBlocks.push_back(block);
-            }
-        }
-    }
-    else
-    {
-        // TODO(jiajia.qin@intel.com) : use GetProgramResourceiv to calculate BUFFER_DATA_SIZE
-        // of UniformBlock and ShaderStorageBlock.
-        if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
-        {
-            if (!mProgram->getUniformBlockSize(interfaceBlock.name, interfaceBlock.mappedName,
-                                               &blockSize))
-            {
-                return;
-            }
-        }
-
-        InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName, false, 0,
-                             blockBinding);
-        block.memberIndexes = blockIndexes;
-        block.setStaticUse(shaderType, interfaceBlock.staticUse);
-        block.dataSize = static_cast<unsigned int>(blockSize);
-        if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
-        {
-            mState.mUniformBlocks.push_back(block);
-        }
-        else
-        {
-            ASSERT(interfaceBlock.blockType == sh::BlockType::BLOCK_BUFFER);
-            mState.mShaderStorageBlocks.push_back(block);
-        }
-    }
-}
-
 void Program::updateSamplerUniform(const VariableLocation &locationInfo,
                                    GLsizei clampedCount,
                                    const GLint *v)
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index 50364ea..99f3959 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -646,18 +646,9 @@
     void setUniformValuesFromBindingQualifiers();
 
     void gatherAtomicCounterBuffers();
-    void gatherComputeBlockInfo(const std::vector<sh::InterfaceBlock> &computeBlocks);
-    void gatherVertexAndFragmentBlockInfo(
-        const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks,
-        const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks);
+    void gatherUniformBlockInfo(const gl::Context *context);
+    void gatherShaderStorageBlockInfo(const gl::Context *context);
     void gatherInterfaceBlockInfo(const Context *context);
-    template <typename VarT>
-    void defineUniformBlockMembers(const std::vector<VarT> &fields,
-                                   const std::string &prefix,
-                                   const std::string &mappedPrefix,
-                                   int blockIndex);
-
-    void defineInterfaceBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType);
 
     // Both these function update the cached uniform values and return a modified "count"
     // so that the uniform update doesn't overflow the uniform.
diff --git a/src/libANGLE/UniformLinker.cpp b/src/libANGLE/UniformLinker.cpp
index a14ac53..37e7036 100644
--- a/src/libANGLE/UniformLinker.cpp
+++ b/src/libANGLE/UniformLinker.cpp
@@ -466,7 +466,7 @@
     std::vector<LinkedUniform> *atomicCounterUniforms,
     GLenum shaderType)
 {
-    int location                          = uniform.location;
+    int location = uniform.location;
     ShaderUniformCount shaderUniformCount =
         flattenUniformImpl(uniform, uniform.name, uniform.mappedName, samplerUniforms,
                            imageUniforms, atomicCounterUniforms, shaderType, uniform.staticUse,
@@ -588,7 +588,7 @@
     shaderUniformCount.vectorCount =
         (IsOpaqueType(uniform.type) ? 0 : (VariableRegisterCount(uniform.type) * elementCount));
     shaderUniformCount.samplerCount = (isSampler ? elementCount : 0);
-    shaderUniformCount.imageCount   = (isImage ? elementCount : 0);
+    shaderUniformCount.imageCount         = (isImage ? elementCount : 0);
     shaderUniformCount.atomicCounterCount = (isAtomicCounter ? elementCount : 0);
 
     if (*location != -1)
@@ -618,4 +618,220 @@
     return true;
 }
 
+// InterfaceBlockLinker implementation.
+InterfaceBlockLinker::InterfaceBlockLinker(std::vector<InterfaceBlock> *blocksOut)
+    : mBlocksOut(blocksOut)
+{
+}
+
+InterfaceBlockLinker::~InterfaceBlockLinker()
+{
+}
+
+void InterfaceBlockLinker::addShaderBlocks(GLenum shader,
+                                           const std::vector<sh::InterfaceBlock> *blocks)
+{
+    mShaderBlocks.push_back(std::make_pair(shader, blocks));
+}
+
+void InterfaceBlockLinker::linkBlocks(const GetBlockSize &getBlockSize,
+                                      const GetBlockMemberInfo &getMemberInfo) const
+{
+    std::set<std::string> visitedList;
+
+    for (const auto &shaderBlocks : mShaderBlocks)
+    {
+        const GLenum shaderType = shaderBlocks.first;
+
+        for (const auto &block : *shaderBlocks.second)
+        {
+            // Only 'packed' blocks are allowed to be considered inactive.
+            if (!block.staticUse && block.layout == sh::BLOCKLAYOUT_PACKED)
+                continue;
+
+            if (visitedList.count(block.name) > 0)
+            {
+                if (block.staticUse)
+                {
+                    for (InterfaceBlock &priorBlock : *mBlocksOut)
+                    {
+                        if (block.name == priorBlock.name)
+                        {
+                            priorBlock.setStaticUse(shaderType, true);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                defineInterfaceBlock(getBlockSize, getMemberInfo, block, shaderType);
+                visitedList.insert(block.name);
+            }
+        }
+    }
+}
+
+template <typename VarT>
+void InterfaceBlockLinker::defineBlockMembers(const GetBlockMemberInfo &getMemberInfo,
+                                              const std::vector<VarT> &fields,
+                                              const std::string &prefix,
+                                              const std::string &mappedPrefix,
+                                              int blockIndex) const
+{
+    for (const VarT &field : fields)
+    {
+        std::string fullName = (prefix.empty() ? field.name : prefix + "." + field.name);
+
+        std::string fullMappedName =
+            (mappedPrefix.empty() ? field.mappedName : mappedPrefix + "." + field.mappedName);
+
+        if (field.isStruct())
+        {
+            for (unsigned int arrayElement = 0; arrayElement < field.elementCount(); arrayElement++)
+            {
+                const std::string elementName =
+                    fullName + (field.isArray() ? ArrayString(arrayElement) : "");
+                const std::string elementMappedName =
+                    fullMappedName + (field.isArray() ? ArrayString(arrayElement) : "");
+                defineBlockMembers(getMemberInfo, field.fields, elementName, elementMappedName,
+                                   blockIndex);
+            }
+        }
+        else
+        {
+            // If getBlockMemberInfo returns false, the variable is optimized out.
+            sh::BlockMemberInfo memberInfo;
+            if (!getMemberInfo(fullName, fullMappedName, &memberInfo))
+            {
+                continue;
+            }
+
+            if (field.isArray())
+            {
+                fullName += "[0]";
+                fullMappedName += "[0]";
+            }
+
+            defineBlockMember(field, fullName, fullMappedName, blockIndex, memberInfo);
+        }
+    }
+}
+
+void InterfaceBlockLinker::defineInterfaceBlock(const GetBlockSize &getBlockSize,
+                                                const GetBlockMemberInfo &getMemberInfo,
+                                                const sh::InterfaceBlock &interfaceBlock,
+                                                GLenum shaderType) const
+{
+    size_t blockSize = 0;
+    std::vector<unsigned int> blockIndexes;
+
+    int blockIndex = static_cast<int>(mBlocksOut->size());
+    // Track the first and last uniform index to determine the range of active uniforms in the
+    // block.
+    size_t firstBlockMemberIndex = getCurrentBlockMemberIndex();
+    defineBlockMembers(getMemberInfo, interfaceBlock.fields, interfaceBlock.fieldPrefix(),
+                       interfaceBlock.fieldMappedPrefix(), blockIndex);
+    size_t lastBlockMemberIndex = getCurrentBlockMemberIndex();
+
+    for (size_t blockMemberIndex = firstBlockMemberIndex; blockMemberIndex < lastBlockMemberIndex;
+         ++blockMemberIndex)
+    {
+        blockIndexes.push_back(static_cast<unsigned int>(blockMemberIndex));
+    }
+
+    // ESSL 3.10 section 4.4.4 page 58:
+    // Any uniform or shader storage block declared without a binding qualifier is initially
+    // assigned to block binding point zero.
+    int blockBinding = (interfaceBlock.binding == -1 ? 0 : interfaceBlock.binding);
+    for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.elementCount();
+         ++arrayElement)
+    {
+        // We don't currently have the getBlockSize implemented for SSBOs.
+        // TODO(jiajia.qin@intel.com): Remove the if when we have getBlockSize for SSBOs.
+        if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
+        {
+            std::string blockArrayName       = interfaceBlock.name;
+            std::string blockMappedArrayName = interfaceBlock.mappedName;
+            if (interfaceBlock.isArray())
+            {
+                blockArrayName += ArrayString(arrayElement);
+                blockMappedArrayName += ArrayString(arrayElement);
+            }
+
+            // Don't define this block at all if it's not active in the implementation.
+            if (!getBlockSize(blockArrayName, blockMappedArrayName, &blockSize))
+            {
+                continue;
+            }
+        }
+
+        InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName,
+                             interfaceBlock.isArray(), arrayElement, blockBinding + arrayElement);
+        block.memberIndexes = blockIndexes;
+        block.setStaticUse(shaderType, interfaceBlock.staticUse);
+
+        // Since all block elements in an array share the same active interface blocks, they
+        // will all be active once any block member is used. So, since interfaceBlock.name[0]
+        // was active, here we will add every block element in the array.
+        block.dataSize = static_cast<unsigned int>(blockSize);
+        mBlocksOut->push_back(block);
+    }
+}
+
+// UniformBlockLinker implementation.
+UniformBlockLinker::UniformBlockLinker(std::vector<InterfaceBlock> *blocksOut,
+                                       std::vector<LinkedUniform> *uniformsOut)
+    : InterfaceBlockLinker(blocksOut), mUniformsOut(uniformsOut)
+{
+}
+
+UniformBlockLinker::~UniformBlockLinker()
+{
+}
+
+void UniformBlockLinker::defineBlockMember(const sh::ShaderVariable &field,
+                                           const std::string &fullName,
+                                           const std::string &fullMappedName,
+                                           int blockIndex,
+                                           const sh::BlockMemberInfo &memberInfo) const
+{
+    LinkedUniform newUniform(field.type, field.precision, fullName, field.arraySize, -1, -1, -1,
+                             blockIndex, memberInfo);
+    newUniform.mappedName = fullMappedName;
+
+    // Since block uniforms have no location, we don't need to store them in the uniform locations
+    // list.
+    mUniformsOut->push_back(newUniform);
+}
+
+size_t UniformBlockLinker::getCurrentBlockMemberIndex() const
+{
+    return mUniformsOut->size();
+}
+
+// ShaderStorageBlockLinker implementation.
+ShaderStorageBlockLinker::ShaderStorageBlockLinker(std::vector<InterfaceBlock> *blocksOut)
+    : InterfaceBlockLinker(blocksOut)
+{
+}
+
+ShaderStorageBlockLinker::~ShaderStorageBlockLinker()
+{
+}
+
+void ShaderStorageBlockLinker::defineBlockMember(const sh::ShaderVariable &field,
+                                                 const std::string &fullName,
+                                                 const std::string &fullMappedName,
+                                                 int blockIndex,
+                                                 const sh::BlockMemberInfo &memberInfo) const
+{
+    // TODO(jiajia.qin@intel.com): Add buffer variables support.
+}
+
+size_t ShaderStorageBlockLinker::getCurrentBlockMemberIndex() const
+{
+    // TODO(jiajia.qin@intel.com): Add buffer variables support.
+    return 0;
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/UniformLinker.h b/src/libANGLE/UniformLinker.h
index 3c827c8..3aac690 100644
--- a/src/libANGLE/UniformLinker.h
+++ b/src/libANGLE/UniformLinker.h
@@ -14,6 +14,8 @@
 #include "libANGLE/Program.h"
 #include "libANGLE/Uniform.h"
 
+#include <functional>
+
 namespace gl
 {
 
@@ -111,6 +113,87 @@
     std::vector<VariableLocation> mUniformLocations;
 };
 
+// This class is intended to be used during the link step to store interface block information.
+// It is called by the Impl class during ProgramImpl::link so that it has access to the
+// real block size and layout.
+class InterfaceBlockLinker : angle::NonCopyable
+{
+  public:
+    virtual ~InterfaceBlockLinker();
+
+    using GetBlockSize = std::function<
+        bool(const std::string &blockName, const std::string &blockMappedName, size_t *sizeOut)>;
+    using GetBlockMemberInfo = std::function<
+        bool(const std::string &name, const std::string &mappedName, sh::BlockMemberInfo *infoOut)>;
+
+    // This is called once per shader stage. It stores a pointer to the block vector, so it's
+    // important that this class does not persist longer than the duration of Program::link.
+    void addShaderBlocks(GLenum shader, const std::vector<sh::InterfaceBlock> *blocks);
+
+    // This is called once during a link operation, after all shader blocks are added.
+    void linkBlocks(const GetBlockSize &getBlockSize,
+                    const GetBlockMemberInfo &getMemberInfo) const;
+
+  protected:
+    InterfaceBlockLinker(std::vector<InterfaceBlock> *blocksOut);
+    void defineInterfaceBlock(const GetBlockSize &getBlockSize,
+                              const GetBlockMemberInfo &getMemberInfo,
+                              const sh::InterfaceBlock &interfaceBlock,
+                              GLenum shaderType) const;
+
+    template <typename VarT>
+    void defineBlockMembers(const GetBlockMemberInfo &getMemberInfo,
+                            const std::vector<VarT> &fields,
+                            const std::string &prefix,
+                            const std::string &mappedPrefix,
+                            int blockIndex) const;
+
+    virtual void defineBlockMember(const sh::ShaderVariable &field,
+                                   const std::string &fullName,
+                                   const std::string &fullMappedName,
+                                   int blockIndex,
+                                   const sh::BlockMemberInfo &memberInfo) const = 0;
+    virtual size_t getCurrentBlockMemberIndex() const                           = 0;
+
+    using ShaderBlocks = std::pair<GLenum, const std::vector<sh::InterfaceBlock> *>;
+    std::vector<ShaderBlocks> mShaderBlocks;
+
+    std::vector<InterfaceBlock> *mBlocksOut;
+};
+
+class UniformBlockLinker final : public InterfaceBlockLinker
+{
+  public:
+    UniformBlockLinker(std::vector<InterfaceBlock> *blocksOut,
+                       std::vector<LinkedUniform> *uniformsOut);
+    virtual ~UniformBlockLinker();
+
+  private:
+    void defineBlockMember(const sh::ShaderVariable &field,
+                           const std::string &fullName,
+                           const std::string &fullMappedName,
+                           int blockIndex,
+                           const sh::BlockMemberInfo &memberInfo) const override;
+    size_t getCurrentBlockMemberIndex() const override;
+    std::vector<LinkedUniform> *mUniformsOut;
+};
+
+// TODO(jiajia.qin@intel.com): Add buffer variables support.
+class ShaderStorageBlockLinker final : public InterfaceBlockLinker
+{
+  public:
+    ShaderStorageBlockLinker(std::vector<InterfaceBlock> *blocksOut);
+    virtual ~ShaderStorageBlockLinker();
+
+  private:
+    void defineBlockMember(const sh::ShaderVariable &field,
+                           const std::string &fullName,
+                           const std::string &fullMappedName,
+                           int blockIndex,
+                           const sh::BlockMemberInfo &memberInfo) const override;
+    size_t getCurrentBlockMemberIndex() const override;
+};
+
 }  // namespace gl
 
 #endif  // LIBANGLE_UNIFORMLINKER_H_