Link interface blocks in ProgramImpl::link.

This allows the back-end to have access to the interface block info
in the link operation, and also allows the interface block info to
have direct access to the post-link Impl information.

BUG=angleproject:2208

Change-Id: Ib2bfb3c9155eee715bd3d29de1c3fdd67b16eed4
Reviewed-on: https://chromium-review.googlesource.com/753521
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index c484668..887a001 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -871,7 +871,7 @@
     }
 
     gatherAtomicCounterBuffers();
-    gatherInterfaceBlockInfo(context);
+    initInterfaceBlockBindings();
 
     setUniformValuesFromBindingQualifiers();
 
@@ -2874,55 +2874,8 @@
     // TODO(jie.a.chen@intel.com): Get the actual BUFFER_DATA_SIZE from backend for each buffer.
 }
 
-void Program::gatherUniformBlockInfo(const gl::Context *context)
+void Program::initInterfaceBlockBindings()
 {
-    UniformBlockLinker blockLinker(&mState.mUniformBlocks, &mState.mUniforms);
-    InitUniformBlockLinker(context, mState, &blockLinker);
-
-    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::gatherShaderStorageBlockInfo(const gl::Context *context)
-{
-    ShaderStorageBlockLinker blockLinker(&mState.mShaderStorageBlocks);
-    InitShaderStorageBlockLinker(context, mState, &blockLinker);
-
-    // 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)
-{
-    ASSERT(mState.mUniformBlocks.empty());
-    ASSERT(mState.mShaderStorageBlocks.empty());
-
-    gatherUniformBlockInfo(context);
-    if (context->getClientVersion() >= Version(3, 1))
-    {
-        gatherShaderStorageBlockInfo(context);
-    }
-
     // Set initial bindings from shader.
     for (unsigned int blockIndex = 0; blockIndex < mState.mUniformBlocks.size(); blockIndex++)
     {
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index d0dbfb7..1973f99 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -640,9 +640,7 @@
     void setUniformValuesFromBindingQualifiers();
 
     void gatherAtomicCounterBuffers();
-    void gatherUniformBlockInfo(const gl::Context *context);
-    void gatherShaderStorageBlockInfo(const gl::Context *context);
-    void gatherInterfaceBlockInfo(const Context *context);
+    void initInterfaceBlockBindings();
 
     // 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/ProgramLinkedResources.cpp b/src/libANGLE/ProgramLinkedResources.cpp
index 16ff05a..a7f64d6 100644
--- a/src/libANGLE/ProgramLinkedResources.cpp
+++ b/src/libANGLE/ProgramLinkedResources.cpp
@@ -637,6 +637,8 @@
 void InterfaceBlockLinker::linkBlocks(const GetBlockSize &getBlockSize,
                                       const GetBlockMemberInfo &getMemberInfo) const
 {
+    ASSERT(mBlocksOut->empty());
+
     std::set<std::string> visitedList;
 
     for (const auto &shaderBlocks : mShaderBlocks)
diff --git a/src/libANGLE/renderer/ProgramImpl.h b/src/libANGLE/renderer/ProgramImpl.h
index 7c5fce7..2371b27 100644
--- a/src/libANGLE/renderer/ProgramImpl.h
+++ b/src/libANGLE/renderer/ProgramImpl.h
@@ -83,17 +83,6 @@
     // TODO: synchronize in syncState when dirty bits exist.
     virtual void setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) = 0;
 
-    // May only be called after a successful link operation.
-    // Return false for inactive blocks.
-    virtual bool getUniformBlockSize(const std::string &blockName,
-                                     const std::string &blockMappedName,
-                                     size_t *sizeOut) const = 0;
-
-    // May only be called after a successful link operation.
-    // Returns false for inactive members.
-    virtual bool getUniformBlockMemberInfo(const std::string &memberUniformName,
-                                           const std::string &memberUniformMappedName,
-                                           sh::BlockMemberInfo *memberInfoOut) const = 0;
     // CHROMIUM_path_rendering
     // Set parameters to control fragment shader input variable interpolation
     virtual void setPathFragmentInputGen(const std::string &inputName,
diff --git a/src/libANGLE/renderer/ProgramImpl_mock.h b/src/libANGLE/renderer/ProgramImpl_mock.h
index 9c55312..4717ab8 100644
--- a/src/libANGLE/renderer/ProgramImpl_mock.h
+++ b/src/libANGLE/renderer/ProgramImpl_mock.h
@@ -63,10 +63,6 @@
     MOCK_CONST_METHOD3(getUniformuiv, void(const gl::Context *, GLint, GLuint *));
 
     MOCK_METHOD2(setUniformBlockBinding, void(GLuint, GLuint));
-    MOCK_CONST_METHOD3(getUniformBlockSize,
-                       bool(const std::string &, const std::string &, size_t *));
-    MOCK_CONST_METHOD3(getUniformBlockMemberInfo,
-                       bool(const std::string &, const std::string &, sh::BlockMemberInfo *));
     MOCK_METHOD4(setPathFragmentInputGen,
                  void(const std::string &, GLenum, GLint, const GLfloat *));
 
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 6310ba7..150ffa3 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -199,6 +199,97 @@
     UNREACHABLE();
 }
 
+class UniformBlockInfo final : angle::NonCopyable
+{
+  public:
+    UniformBlockInfo() {}
+
+    void getShaderBlockInfo(const gl::Context *context, gl::Shader *shader);
+
+    bool getBlockSize(const std::string &name, const std::string &mappedName, size_t *sizeOut);
+    bool getBlockMemberInfo(const std::string &name,
+                            const std::string &mappedName,
+                            sh::BlockMemberInfo *infoOut);
+
+  private:
+    size_t getBlockInfo(const sh::InterfaceBlock &interfaceBlock);
+
+    std::map<std::string, size_t> mBlockSizes;
+    sh::BlockLayoutMap mBlockLayout;
+};
+
+void UniformBlockInfo::getShaderBlockInfo(const gl::Context *context, gl::Shader *shader)
+{
+    for (const sh::InterfaceBlock &interfaceBlock : shader->getUniformBlocks(context))
+    {
+        if (!interfaceBlock.staticUse && interfaceBlock.layout == sh::BLOCKLAYOUT_PACKED)
+            continue;
+
+        if (mBlockSizes.count(interfaceBlock.name) > 0)
+            continue;
+
+        size_t dataSize                  = getBlockInfo(interfaceBlock);
+        mBlockSizes[interfaceBlock.name] = dataSize;
+    }
+}
+
+size_t UniformBlockInfo::getBlockInfo(const sh::InterfaceBlock &interfaceBlock)
+{
+    ASSERT(interfaceBlock.staticUse || interfaceBlock.layout != sh::BLOCKLAYOUT_PACKED);
+
+    // define member uniforms
+    sh::Std140BlockEncoder std140Encoder;
+    sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
+    sh::BlockLayoutEncoder *encoder = nullptr;
+
+    if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD140)
+    {
+        encoder = &std140Encoder;
+    }
+    else
+    {
+        encoder = &hlslEncoder;
+    }
+
+    sh::GetUniformBlockInfo(interfaceBlock.fields, interfaceBlock.fieldPrefix(), encoder,
+                            interfaceBlock.isRowMajorLayout, &mBlockLayout);
+
+    return encoder->getBlockSize();
+}
+
+bool UniformBlockInfo::getBlockSize(const std::string &name,
+                                    const std::string &mappedName,
+                                    size_t *sizeOut)
+{
+    size_t nameLengthWithoutArrayIndex;
+    gl::ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
+    std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
+    auto sizeIter        = mBlockSizes.find(baseName);
+    if (sizeIter == mBlockSizes.end())
+    {
+        *sizeOut = 0;
+        return false;
+    }
+
+    *sizeOut = sizeIter->second;
+    return true;
+};
+
+bool UniformBlockInfo::getBlockMemberInfo(const std::string &name,
+                                          const std::string &mappedName,
+                                          sh::BlockMemberInfo *infoOut)
+{
+    auto infoIter = mBlockLayout.find(name);
+    if (infoIter == mBlockLayout.end())
+    {
+        *infoOut = sh::BlockMemberInfo::getDefaultBlockInfo();
+        return false;
+    }
+
+    *infoOut = infoIter->second;
+    return true;
+};
+
 }  // anonymous namespace
 
 // D3DUniform Implementation
@@ -1028,10 +1119,6 @@
         stream->writeInt(uniform->registerElement);
     }
 
-    // Ensure we init the uniform block structure data if we should.
-    // http://anglebug.com/1637
-    ensureUniformBlocksInitialized();
-
     stream->writeInt(mD3DUniformBlocks.size());
     for (const D3DUniformBlock &uniformBlock : mD3DUniformBlocks)
     {
@@ -1505,8 +1592,6 @@
             infoLog << "Failed to create D3D compute shader.";
             return result;
         }
-
-        initUniformBlockInfo(context, computeShader);
     }
     else
     {
@@ -1582,11 +1667,10 @@
             infoLog << "Failed to create D3D shaders.";
             return result;
         }
-
-        initUniformBlockInfo(context, vertexShader);
-        initUniformBlockInfo(context, fragmentShader);
     }
 
+    linkResources(context, resources);
+
     return true;
 }
 
@@ -1596,29 +1680,15 @@
     return GL_TRUE;
 }
 
-void ProgramD3D::initUniformBlockInfo(const gl::Context *context, gl::Shader *shader)
+void ProgramD3D::initializeUniformBlocks()
 {
-    for (const sh::InterfaceBlock &interfaceBlock : shader->getUniformBlocks(context))
-    {
-        if (!interfaceBlock.staticUse && interfaceBlock.layout == sh::BLOCKLAYOUT_PACKED)
-            continue;
-
-        if (mBlockDataSizes.count(interfaceBlock.name) > 0)
-            continue;
-
-        size_t dataSize                      = getUniformBlockInfo(interfaceBlock);
-        mBlockDataSizes[interfaceBlock.name] = dataSize;
-    }
-}
-
-void ProgramD3D::ensureUniformBlocksInitialized()
-{
-    // Lazy init.
-    if (mState.getUniformBlocks().empty() || !mD3DUniformBlocks.empty())
+    if (mState.getUniformBlocks().empty())
     {
         return;
     }
 
+    ASSERT(mD3DUniformBlocks.empty());
+
     // Assign registers and update sizes.
     const ShaderD3D *vertexShaderD3D = SafeGetImplAs<ShaderD3D>(mState.getAttachedVertexShader());
     const ShaderD3D *fragmentShaderD3D =
@@ -1731,8 +1801,6 @@
         return;
     }
 
-    ensureUniformBlocksInitialized();
-
     mVertexUBOCache.clear();
     mFragmentUBOCache.clear();
 
@@ -2278,30 +2346,6 @@
     }
 }
 
-size_t ProgramD3D::getUniformBlockInfo(const sh::InterfaceBlock &interfaceBlock)
-{
-    ASSERT(interfaceBlock.staticUse || interfaceBlock.layout != sh::BLOCKLAYOUT_PACKED);
-
-    // define member uniforms
-    sh::Std140BlockEncoder std140Encoder;
-    sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
-    sh::BlockLayoutEncoder *encoder = nullptr;
-
-    if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD140)
-    {
-        encoder = &std140Encoder;
-    }
-    else
-    {
-        encoder = &hlslEncoder;
-    }
-
-    sh::GetUniformBlockInfo(interfaceBlock.fields, interfaceBlock.fieldPrefix(), encoder,
-                            interfaceBlock.isRowMajorLayout, &mBlockInfo);
-
-    return encoder->getBlockSize();
-}
-
 void ProgramD3D::assignAllSamplerRegisters()
 {
     for (D3DUniform *d3dUniform : mD3DUniforms)
@@ -2595,40 +2639,6 @@
     return mD3DUniforms[mState.getUniformLocations()[location].index];
 }
 
-bool ProgramD3D::getUniformBlockSize(const std::string &blockName,
-                                     const std::string & /* blockMappedName */,
-                                     size_t *sizeOut) const
-{
-    size_t nameLengthWithoutArrayIndex;
-    gl::ParseArrayIndex(blockName, &nameLengthWithoutArrayIndex);
-    std::string baseName = blockName.substr(0u, nameLengthWithoutArrayIndex);
-
-    auto sizeIter = mBlockDataSizes.find(baseName);
-    if (sizeIter == mBlockDataSizes.end())
-    {
-        *sizeOut = 0;
-        return false;
-    }
-
-    *sizeOut = sizeIter->second;
-    return true;
-}
-
-bool ProgramD3D::getUniformBlockMemberInfo(const std::string &memberUniformName,
-                                           const std::string & /* memberUniformMappedName */,
-                                           sh::BlockMemberInfo *memberInfoOut) const
-{
-    auto infoIter = mBlockInfo.find(memberUniformName);
-    if (infoIter == mBlockInfo.end())
-    {
-        *memberInfoOut = sh::BlockMemberInfo::getDefaultBlockInfo();
-        return false;
-    }
-
-    *memberInfoOut = infoIter->second;
-    return true;
-}
-
 void ProgramD3D::setPathFragmentInputGen(const std::string &inputName,
                                          GLenum genMode,
                                          GLint components,
@@ -2720,4 +2730,56 @@
     }
 }
 
+void ProgramD3D::linkResources(const gl::Context *context,
+                               const gl::ProgramLinkedResources &resources)
+{
+    UniformBlockInfo uniformBlockInfo;
+
+    if (mState.getAttachedVertexShader())
+    {
+        uniformBlockInfo.getShaderBlockInfo(context, mState.getAttachedVertexShader());
+    }
+
+    if (mState.getAttachedFragmentShader())
+    {
+        uniformBlockInfo.getShaderBlockInfo(context, mState.getAttachedFragmentShader());
+    }
+
+    if (mState.getAttachedComputeShader())
+    {
+        uniformBlockInfo.getShaderBlockInfo(context, mState.getAttachedComputeShader());
+    }
+
+    // Gather interface block info.
+    auto getUniformBlockSize = [&uniformBlockInfo](const std::string &name,
+                                                   const std::string &mappedName, size_t *sizeOut) {
+        return uniformBlockInfo.getBlockSize(name, mappedName, sizeOut);
+    };
+
+    auto getUniformBlockMemberInfo = [&uniformBlockInfo](const std::string &name,
+                                                         const std::string &mappedName,
+                                                         sh::BlockMemberInfo *infoOut) {
+        return uniformBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
+    };
+
+    resources.uniformBlockLinker.linkBlocks(getUniformBlockSize, getUniformBlockMemberInfo);
+    initializeUniformBlocks();
+
+    // TODO(jiajia.qin@intel.com): Determine correct shader storage block info.
+    auto getShaderStorageBlockSize = [](const std::string &name, const std::string &mappedName,
+                                        size_t *sizeOut) {
+        *sizeOut = 0;
+        return true;
+    };
+
+    auto getShaderStorageBlockMemberInfo =
+        [](const std::string &name, const std::string &mappedName, sh::BlockMemberInfo *infoOut) {
+            *infoOut = sh::BlockMemberInfo::getDefaultBlockInfo();
+            return true;
+        };
+
+    resources.shaderStorageBlockLinker.linkBlocks(getShaderStorageBlockSize,
+                                                  getShaderStorageBlockMemberInfo);
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h
index 0d8996d..c9634d3 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -196,12 +196,6 @@
                         gl::InfoLog &infoLog) override;
     GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
 
-    bool getUniformBlockSize(const std::string &blockName,
-                             const std::string &blockMappedName,
-                             size_t *sizeOut) const override;
-    bool getUniformBlockMemberInfo(const std::string &memberUniformName,
-                                   const std::string &memberUniformMappedName,
-                                   sh::BlockMemberInfo *memberInfoOut) const override;
     void setPathFragmentInputGen(const std::string &inputName,
                                  GLenum genMode,
                                  GLint components,
@@ -428,16 +422,15 @@
     void initAttribLocationsToD3DSemantic(const gl::Context *context);
 
     void reset();
-    void ensureUniformBlocksInitialized();
-
-    void initUniformBlockInfo(const gl::Context *context, gl::Shader *shader);
-    size_t getUniformBlockInfo(const sh::InterfaceBlock &interfaceBlock);
+    void initializeUniformBlocks();
 
     void updateCachedInputLayoutFromShader(const gl::Context *context);
     void updateCachedOutputLayoutFromShader();
     void updateCachedVertexExecutableIndex();
     void updateCachedPixelExecutableIndex();
 
+    void linkResources(const gl::Context *context, const gl::ProgramLinkedResources &resources);
+
     RendererD3D *mRenderer;
     DynamicHLSL *mDynamicHLSL;
 
@@ -498,9 +491,6 @@
     bool mFragmentUniformsDirty;
     bool mComputeUniformsDirty;
 
-    std::map<std::string, sh::BlockMemberInfo> mBlockInfo;
-    std::map<std::string, size_t> mBlockDataSizes;
-
     static unsigned int issueSerial();
     static unsigned int mCurrentSerial;
 
diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp
index b1a0c3f..466f1fa 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.cpp
+++ b/src/libANGLE/renderer/gl/ProgramGL.cpp
@@ -14,6 +14,7 @@
 #include "common/string_utils.h"
 #include "common/utilities.h"
 #include "libANGLE/Context.h"
+#include "libANGLE/ProgramLinkedResources.h"
 #include "libANGLE/Uniform.h"
 #include "libANGLE/renderer/gl/ContextGL.h"
 #include "libANGLE/renderer/gl/FunctionsGL.h"
@@ -212,6 +213,7 @@
         mStateManager->forceUseProgram(mProgramID);
     }
 
+    linkResources(resources);
     postLink();
 
     return true;
@@ -805,4 +807,35 @@
     }
 }
 
+void ProgramGL::linkResources(const gl::ProgramLinkedResources &resources)
+{
+    // Gather interface block info.
+    auto getUniformBlockSize = [this](const std::string &name, const std::string &mappedName,
+                                      size_t *sizeOut) {
+        return this->getUniformBlockSize(name, mappedName, sizeOut);
+    };
+
+    auto getUniformBlockMemberInfo = [this](const std::string &name, const std::string &mappedName,
+                                            sh::BlockMemberInfo *infoOut) {
+        return this->getUniformBlockMemberInfo(name, mappedName, infoOut);
+    };
+
+    resources.uniformBlockLinker.linkBlocks(getUniformBlockSize, getUniformBlockMemberInfo);
+
+    // TODO(jiajia.qin@intel.com): Determine correct shader storage block info.
+    auto getShaderStorageBlockSize = [](const std::string &name, const std::string &mappedName,
+                                        size_t *sizeOut) {
+        *sizeOut = 0;
+        return true;
+    };
+
+    auto getShaderStorageBlockMemberInfo =
+        [](const std::string &name, const std::string &mappedName, sh::BlockMemberInfo *infoOut) {
+            *infoOut = sh::BlockMemberInfo::getDefaultBlockInfo();
+            return true;
+        };
+    resources.shaderStorageBlockLinker.linkBlocks(getShaderStorageBlockSize,
+                                                  getShaderStorageBlockMemberInfo);
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/gl/ProgramGL.h b/src/libANGLE/renderer/gl/ProgramGL.h
index fc92511..1fee677 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.h
+++ b/src/libANGLE/renderer/gl/ProgramGL.h
@@ -71,13 +71,6 @@
 
     void setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
 
-    bool getUniformBlockSize(const std::string &blockName,
-                             const std::string &blockMappedName,
-                             size_t *sizeOut) const override;
-    bool getUniformBlockMemberInfo(const std::string &memberUniformName,
-                                   const std::string &memberUniformMappedName,
-                                   sh::BlockMemberInfo *memberInfoOut) const override;
-
     void setPathFragmentInputGen(const std::string &inputName,
                                  GLenum genMode,
                                  GLint components,
@@ -97,6 +90,14 @@
     void postLink();
     void reapplyUBOBindingsIfNeeded(const gl::Context *context);
 
+    bool getUniformBlockSize(const std::string &blockName,
+                             const std::string &blockMappedName,
+                             size_t *sizeOut) const;
+    bool getUniformBlockMemberInfo(const std::string &memberUniformName,
+                                   const std::string &memberUniformMappedName,
+                                   sh::BlockMemberInfo *memberInfoOut) const;
+    void linkResources(const gl::ProgramLinkedResources &resources);
+
     // Helper function, makes it simpler to type.
     GLint uniLoc(GLint glLocation) const { return mUniformRealLocationMap[glLocation]; }
 
diff --git a/src/libANGLE/renderer/null/ProgramNULL.cpp b/src/libANGLE/renderer/null/ProgramNULL.cpp
index d312fd7..f7aeb18 100644
--- a/src/libANGLE/renderer/null/ProgramNULL.cpp
+++ b/src/libANGLE/renderer/null/ProgramNULL.cpp
@@ -183,23 +183,6 @@
 {
 }
 
-bool ProgramNULL::getUniformBlockSize(const std::string &blockName,
-                                      const std::string &blockMappedName,
-                                      size_t *sizeOut) const
-{
-    // TODO(geofflang): Compute reasonable sizes?
-    *sizeOut = 0;
-    return true;
-}
-
-bool ProgramNULL::getUniformBlockMemberInfo(const std::string &memberUniformName,
-                                            const std::string &memberUniformMappedName,
-                                            sh::BlockMemberInfo *memberInfoOut) const
-{
-    // TODO(geofflang): Compute reasonable values?
-    return true;
-}
-
 void ProgramNULL::setPathFragmentInputGen(const std::string &inputName,
                                           GLenum genMode,
                                           GLint components,
diff --git a/src/libANGLE/renderer/null/ProgramNULL.h b/src/libANGLE/renderer/null/ProgramNULL.h
index 61ff61b..4dc8097 100644
--- a/src/libANGLE/renderer/null/ProgramNULL.h
+++ b/src/libANGLE/renderer/null/ProgramNULL.h
@@ -89,17 +89,6 @@
     // TODO: synchronize in syncState when dirty bits exist.
     void setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
 
-    // May only be called after a successful link operation.
-    // Return false for inactive blocks.
-    bool getUniformBlockSize(const std::string &blockName,
-                             const std::string &blockMappedName,
-                             size_t *sizeOut) const override;
-
-    // May only be called after a successful link operation.
-    // Returns false for inactive members.
-    bool getUniformBlockMemberInfo(const std::string &memberUniformName,
-                                   const std::string &memberUniformMappedName,
-                                   sh::BlockMemberInfo *memberInfoOut) const override;
     // CHROMIUM_path_rendering
     // Set parameters to control fragment shader input variable interpolation
     void setPathFragmentInputGen(const std::string &inputName,
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
index ee2d7c0..c464641 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
@@ -528,22 +528,6 @@
     UNIMPLEMENTED();
 }
 
-bool ProgramVk::getUniformBlockSize(const std::string &blockName,
-                                    const std::string &blockMappedName,
-                                    size_t *sizeOut) const
-{
-    UNIMPLEMENTED();
-    return bool();
-}
-
-bool ProgramVk::getUniformBlockMemberInfo(const std::string &memberUniformName,
-                                          const std::string &memberUniformMappedName,
-                                          sh::BlockMemberInfo *memberInfoOut) const
-{
-    UNIMPLEMENTED();
-    return bool();
-}
-
 void ProgramVk::setPathFragmentInputGen(const std::string &inputName,
                                         GLenum genMode,
                                         GLint components,
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.h b/src/libANGLE/renderer/vulkan/ProgramVk.h
index e31c9f9..aa382dd 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.h
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.h
@@ -94,18 +94,6 @@
     // TODO: synchronize in syncState when dirty bits exist.
     void setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
 
-    // May only be called after a successful link operation.
-    // Return false for inactive blocks.
-    bool getUniformBlockSize(const std::string &blockName,
-                             const std::string &blockMappedName,
-                             size_t *sizeOut) const override;
-
-    // May only be called after a successful link operation.
-    // Returns false for inactive members.
-    bool getUniformBlockMemberInfo(const std::string &memberUniformName,
-                                   const std::string &memberUniformMappedName,
-                                   sh::BlockMemberInfo *memberInfoOut) const override;
-
     void setPathFragmentInputGen(const std::string &inputName,
                                  GLenum genMode,
                                  GLint components,
diff --git a/src/tests/gl_tests/UniformBufferTest.cpp b/src/tests/gl_tests/UniformBufferTest.cpp
index 05fa369..be525e3 100644
--- a/src/tests/gl_tests/UniformBufferTest.cpp
+++ b/src/tests/gl_tests/UniformBufferTest.cpp
@@ -1156,6 +1156,46 @@
     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
 }
 
+// Tests the detaching shaders from the program and using uniform blocks works.
+// This covers a bug in ANGLE's D3D back-end.
+TEST_P(UniformBufferTest, DetachShaders)
+{
+    GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, mVertexShaderSource);
+    ASSERT_NE(0u, vertexShader);
+    GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, mFragmentShaderSource);
+    ASSERT_NE(0u, fragmentShader);
+
+    GLuint program = glCreateProgram();
+    glAttachShader(program, vertexShader);
+    glAttachShader(program, fragmentShader);
+
+    ASSERT_TRUE(LinkAttachedProgram(program));
+
+    glDetachShader(program, vertexShader);
+    glDetachShader(program, fragmentShader);
+    glDeleteShader(vertexShader);
+    glDeleteShader(fragmentShader);
+
+    glClear(GL_COLOR_BUFFER_BIT);
+    float floatData[4] = {0.5f, 0.75f, 0.25f, 1.0f};
+
+    glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
+    glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 4, floatData, GL_STATIC_DRAW);
+
+    glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
+
+    GLint uniformBufferIndex = glGetUniformBlockIndex(mProgram, "uni");
+    ASSERT_NE(uniformBufferIndex, -1);
+
+    glUniformBlockBinding(program, uniformBufferIndex, 0);
+    drawQuad(program, "position", 0.5f);
+
+    ASSERT_GL_NO_ERROR();
+    EXPECT_PIXEL_NEAR(0, 0, 128, 191, 64, 255, 1);
+
+    glDeleteProgram(program);
+}
+
 // 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(),