Support UBOs in RendererGL and ProgramGL.

BUG=angleproject:882
BUG=angleproject:883

Change-Id: I36f8ef42d87e289658a6ba4899380bc72b9bcebf
Reviewed-on: https://chromium-review.googlesource.com/299871
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Tested-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 14a50ba..13277b3 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -1520,6 +1520,7 @@
 void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
 {
     mData.mUniformBlockBindings[uniformBlockIndex] = uniformBlockBinding;
+    mProgram->setUniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
 }
 
 GLuint Program::getUniformBlockBinding(GLuint uniformBlockIndex) const
@@ -1533,6 +1534,7 @@
     {
         mData.mUniformBlockBindings[blockId] = 0;
     }
+    mData.mActiveUniformBlockBindings.reset();
 }
 
 void Program::setTransformFeedbackVaryings(GLsizei count, const GLchar *const *varyings, GLenum bufferMode)
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index 6e9d682..9a0eac9 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -182,6 +182,10 @@
             ASSERT(uniformBlockIndex < IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS);
             return mUniformBlockBindings[uniformBlockIndex];
         }
+        const UniformBlockBindingMask &getActiveUniformBlockBindingsMask() const
+        {
+            return mActiveUniformBlockBindings;
+        }
         const std::vector<sh::Attribute> &getAttributes() const { return mAttributes; }
         const AttributesMask &getActiveAttribLocationsMask() const
         {
@@ -213,6 +217,7 @@
         GLenum mTransformFeedbackBufferMode;
 
         GLuint mUniformBlockBindings[IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS];
+        UniformBlockBindingMask mActiveUniformBlockBindings;
 
         std::vector<sh::Attribute> mAttributes;
         std::bitset<MAX_VERTEX_ATTRIBS> mActiveAttribLocationsMask;
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 1e26f2b..746b514 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -995,32 +995,10 @@
     mUniformBuffers[index].set(buffer, offset, size);
 }
 
-GLuint State::getIndexedUniformBufferId(GLuint index) const
+const OffsetBindingPointer<Buffer> &State::getIndexedUniformBuffer(size_t index) const
 {
     ASSERT(static_cast<size_t>(index) < mUniformBuffers.size());
-
-    return mUniformBuffers[index].id();
-}
-
-Buffer *State::getIndexedUniformBuffer(GLuint index) const
-{
-    ASSERT(static_cast<size_t>(index) < mUniformBuffers.size());
-
-    return mUniformBuffers[index].get();
-}
-
-GLintptr State::getIndexedUniformBufferOffset(GLuint index) const
-{
-    ASSERT(static_cast<size_t>(index) < mUniformBuffers.size());
-
-    return mUniformBuffers[index].getOffset();
-}
-
-GLsizeiptr State::getIndexedUniformBufferSize(GLuint index) const
-{
-    ASSERT(static_cast<size_t>(index) < mUniformBuffers.size());
-
-    return mUniformBuffers[index].getSize();
+    return mUniformBuffers[index];
 }
 
 void State::setCopyReadBufferBinding(Buffer *buffer)
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 1102056..b53dce6 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -203,10 +203,7 @@
     // GL_UNIFORM_BUFFER - Both indexed and generic targets
     void setGenericUniformBufferBinding(Buffer *buffer);
     void setIndexedUniformBufferBinding(GLuint index, Buffer *buffer, GLintptr offset, GLsizeiptr size);
-    GLuint getIndexedUniformBufferId(GLuint index) const;
-    Buffer *getIndexedUniformBuffer(GLuint index) const;
-    GLintptr getIndexedUniformBufferOffset(GLuint index) const;
-    GLsizeiptr getIndexedUniformBufferSize(GLuint index) const;
+    const OffsetBindingPointer<Buffer> &getIndexedUniformBuffer(size_t index) const;
 
     // GL_COPY_[READ/WRITE]_BUFFER
     void setCopyReadBufferBinding(Buffer *buffer);
diff --git a/src/libANGLE/angletypes.h b/src/libANGLE/angletypes.h
index b7d9084..9fea750 100644
--- a/src/libANGLE/angletypes.h
+++ b/src/libANGLE/angletypes.h
@@ -273,6 +273,9 @@
 
 // Used in Program and VertexArray.
 typedef std::bitset<MAX_VERTEX_ATTRIBS> AttributesMask;
+
+// Use in Program
+typedef std::bitset<IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS> UniformBlockBindingMask;
 }
 
 namespace rx
diff --git a/src/libANGLE/renderer/ProgramImpl.h b/src/libANGLE/renderer/ProgramImpl.h
index a47b18e..5cc4abe 100644
--- a/src/libANGLE/renderer/ProgramImpl.h
+++ b/src/libANGLE/renderer/ProgramImpl.h
@@ -66,6 +66,9 @@
     virtual void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) = 0;
     virtual void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) = 0;
 
+    // TODO: synchronize in syncState when dirty bits exist.
+    virtual void setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) = 0;
+
     // Gather uniform block active uniform indices, and uniform block offset info.
     virtual void gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
                                         std::vector<gl::LinkedUniform> *uniforms) = 0;
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 9011f86..ae50617 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -1344,6 +1344,10 @@
     setUniform(location, count, v, GL_UNSIGNED_INT_VEC4);
 }
 
+void ProgramD3D::setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+}
+
 void ProgramD3D::defineUniformsAndAssignRegisters()
 {
     D3DUniformMap uniformMap;
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h
index 148371e..f08e0fe 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -131,6 +131,8 @@
     void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
     void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 
+    void setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
+
     const UniformStorageD3D &getVertexUniformStorage() const { return *mVertexUniformStorage; }
     const UniformStorageD3D &getFragmentUniformStorage() const { return *mFragmentUniformStorage; }
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 1cb4a0b..ab01482 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -1008,13 +1008,14 @@
             continue;
         }
 
-        gl::Buffer *uniformBuffer = data.state->getIndexedUniformBuffer(binding);
-        GLintptr uniformBufferOffset = data.state->getIndexedUniformBufferOffset(binding);
-        GLsizeiptr uniformBufferSize = data.state->getIndexedUniformBufferSize(binding);
+        const OffsetBindingPointer<gl::Buffer> &uniformBuffer =
+            data.state->getIndexedUniformBuffer(binding);
+        GLintptr uniformBufferOffset = uniformBuffer.getOffset();
+        GLsizeiptr uniformBufferSize = uniformBuffer.getSize();
 
-        if (uniformBuffer)
+        if (uniformBuffer.get() != nullptr)
         {
-            Buffer11 *bufferStorage = GetImplAs<Buffer11>(uniformBuffer);
+            Buffer11 *bufferStorage = GetImplAs<Buffer11>(uniformBuffer.get());
             ID3D11Buffer *constantBuffer;
 
             if (mRenderer11DeviceCaps.supportsConstantBufferOffsets)
@@ -1068,13 +1069,14 @@
             continue;
         }
 
-        gl::Buffer *uniformBuffer = data.state->getIndexedUniformBuffer(binding);
-        GLintptr uniformBufferOffset = data.state->getIndexedUniformBufferOffset(binding);
-        GLsizeiptr uniformBufferSize = data.state->getIndexedUniformBufferSize(binding);
+        const OffsetBindingPointer<gl::Buffer> &uniformBuffer =
+            data.state->getIndexedUniformBuffer(binding);
+        GLintptr uniformBufferOffset = uniformBuffer.getOffset();
+        GLsizeiptr uniformBufferSize = uniformBuffer.getSize();
 
-        if (uniformBuffer)
+        if (uniformBuffer.get() != nullptr)
         {
-            Buffer11 *bufferStorage = GetImplAs<Buffer11>(uniformBuffer);
+            Buffer11 *bufferStorage = GetImplAs<Buffer11>(uniformBuffer.get());
             ID3D11Buffer *constantBuffer;
 
             if (mRenderer11DeviceCaps.supportsConstantBufferOffsets)
diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp
index bbb4540..3363c72 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.cpp
+++ b/src/libANGLE/renderer/gl/ProgramGL.cpp
@@ -299,6 +299,12 @@
     mFunctions->uniformMatrix4x3fv(uniLoc(location), count, transpose, value);
 }
 
+void ProgramGL::setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+    mFunctions->uniformBlockBinding(mProgramID, mUniformBlockRealLocationMap[uniformBlockIndex],
+                                    uniformBlockBinding);
+}
+
 void ProgramGL::reset()
 {
     mUniformRealLocationMap.clear();
@@ -316,9 +322,104 @@
     return mSamplerBindings;
 }
 
-void ProgramGL::gatherUniformBlockInfo(std::vector<gl::UniformBlock> * /*uniformBlocks*/,
-                                       std::vector<gl::LinkedUniform> * /*uniforms*/)
+void ProgramGL::gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
+                                       std::vector<gl::LinkedUniform> *uniforms)
 {
-    // TODO(jmadill): Gather uniform block layout info, and data sizes.
+    mUniformBlockRealLocationMap.resize(uniformBlocks->size(), 0);
+
+    for (int i = 0; i < uniformBlocks->size(); i++)
+    {
+        auto &uniformBlock = uniformBlocks->at(i);
+
+        std::stringstream fullNameStr;
+        fullNameStr << uniformBlock.name;
+        if (uniformBlock.isArray)
+        {
+            fullNameStr << "[" << uniformBlock.arrayElement << "]";
+        }
+
+        GLuint blockIndex = mFunctions->getUniformBlockIndex(mProgramID, fullNameStr.str().c_str());
+        if (blockIndex != GL_INVALID_INDEX)
+        {
+            mUniformBlockRealLocationMap[i] = blockIndex;
+
+            GLint dataSize = 0;
+            mFunctions->getActiveUniformBlockiv(mProgramID, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE,
+                                                &dataSize);
+            uniformBlock.dataSize = dataSize;
+        }
+        else
+        {
+            // Remove this uniform block
+            uniformBlocks->erase(uniformBlocks->begin() + i);
+            i--;
+        }
+    }
+
+    for (int uniformIdx = 0; uniformIdx < uniforms->size(); uniformIdx++)
+    {
+        auto &uniform = uniforms->at(uniformIdx);
+        if (uniform.isInDefaultBlock())
+        {
+            continue;
+        }
+
+        const GLchar *uniformName = uniform.name.c_str();
+        GLuint uniformIndex = 0;
+        mFunctions->getUniformIndices(mProgramID, 1, &uniformName, &uniformIndex);
+
+        if (uniformIndex == -1)
+        {
+            // Uniform member has been optimized out, remove it from the list
+            // TODO: Clean this up by using a class to wrap around the uniforms so manual removal is
+            // not needed.
+            for (size_t uniformBlockIdx = 0; uniformBlockIdx < uniformBlocks->size();
+                 uniformBlockIdx++)
+            {
+                auto &uniformBlock = uniformBlocks->at(uniformBlockIdx);
+                for (int memberIndex = 0; memberIndex < uniformBlock.memberUniformIndexes.size();
+                     memberIndex++)
+                {
+                    if (uniformBlock.memberUniformIndexes[memberIndex] ==
+                        static_cast<unsigned int>(uniformIdx))
+                    {
+                        uniformBlock.memberUniformIndexes.erase(
+                            uniformBlock.memberUniformIndexes.begin() + memberIndex);
+                        memberIndex--;
+                    }
+                    else if (uniformBlock.memberUniformIndexes[memberIndex] >
+                             static_cast<unsigned int>(uniformIdx))
+                    {
+                        uniformBlock.memberUniformIndexes[memberIndex]--;
+                    }
+                }
+            }
+            uniforms->erase(uniforms->begin() + uniformIdx);
+            uniformIdx--;
+        }
+        else
+        {
+            GLint offset = 0;
+            mFunctions->getActiveUniformsiv(mProgramID, 1, &uniformIndex, GL_UNIFORM_OFFSET,
+                                            &offset);
+            uniform.blockInfo.offset = offset;
+
+            GLint arrayStride = 0;
+            mFunctions->getActiveUniformsiv(mProgramID, 1, &uniformIndex, GL_UNIFORM_ARRAY_STRIDE,
+                                            &arrayStride);
+            uniform.blockInfo.arrayStride = arrayStride;
+
+            GLint matrixStride = 0;
+            mFunctions->getActiveUniformsiv(mProgramID, 1, &uniformIndex, GL_UNIFORM_MATRIX_STRIDE,
+                                            &matrixStride);
+            uniform.blockInfo.matrixStride = matrixStride;
+
+            // TODO: determine this at the gl::Program level.
+            GLint isRowMajorMatrix = 0;
+            mFunctions->getActiveUniformsiv(mProgramID, 1, &uniformIndex, GL_UNIFORM_IS_ROW_MAJOR,
+                                            &isRowMajorMatrix);
+            uniform.blockInfo.isRowMajorMatrix = isRowMajorMatrix != GL_FALSE;
+        }
+    }
 }
 }
diff --git a/src/libANGLE/renderer/gl/ProgramGL.h b/src/libANGLE/renderer/gl/ProgramGL.h
index 982d5bb..15ced61 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.h
+++ b/src/libANGLE/renderer/gl/ProgramGL.h
@@ -62,6 +62,8 @@
     void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override;
     void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override;
 
+    void setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
+
     void gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
                                 std::vector<gl::LinkedUniform> *uniforms) override;
 
@@ -78,6 +80,7 @@
     StateManagerGL *mStateManager;
 
     std::vector<GLint> mUniformRealLocationMap;
+    std::vector<GLuint> mUniformBlockRealLocationMap;
 
     // An array of the samplers that are used by the program
     std::vector<SamplerBindingGL> mSamplerBindings;
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.cpp b/src/libANGLE/renderer/gl/StateManagerGL.cpp
index 7c7a246..33dc97c 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.cpp
+++ b/src/libANGLE/renderer/gl/StateManagerGL.cpp
@@ -22,12 +22,18 @@
 
 namespace rx
 {
+
+StateManagerGL::IndexedBufferBinding::IndexedBufferBinding() : offset(0), size(0), buffer(0)
+{
+}
+
 StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &rendererCaps)
     : mFunctions(functions),
       mProgram(0),
       mVAO(0),
       mVertexAttribCurrentValues(rendererCaps.maxVertexAttributes),
       mBuffers(),
+      mIndexedBuffers(),
       mTextureUnitIndex(0),
       mTextures(),
       mSamplers(rendererCaps.maxCombinedTextureImageUnits, 0),
@@ -103,6 +109,8 @@
     mTextures[GL_TEXTURE_2D_ARRAY].resize(rendererCaps.maxCombinedTextureImageUnits);
     mTextures[GL_TEXTURE_3D].resize(rendererCaps.maxCombinedTextureImageUnits);
 
+    mIndexedBuffers[GL_UNIFORM_BUFFER].resize(rendererCaps.maxCombinedUniformBlocks);
+
     // Initialize point sprite state for desktop GL
     if (mFunctions->standard == STANDARD_GL_DESKTOP)
     {
@@ -193,6 +201,17 @@
             }
         }
 
+        for (const auto &bufferTypeIter : mIndexedBuffers)
+        {
+            for (size_t bindIndex = 0; bindIndex < bufferTypeIter.second.size(); bindIndex++)
+            {
+                if (bufferTypeIter.second[bindIndex].buffer == buffer)
+                {
+                    bindBufferBase(bufferTypeIter.first, bindIndex, 0);
+                }
+            }
+        }
+
         mFunctions->deleteBuffers(1, &buffer);
     }
 }
@@ -255,6 +274,35 @@
     }
 }
 
+void StateManagerGL::bindBufferBase(GLenum type, size_t index, GLuint buffer)
+{
+    auto &binding = mIndexedBuffers[type][index];
+    if (binding.buffer != buffer || binding.offset != static_cast<size_t>(-1) ||
+        binding.size != static_cast<size_t>(-1))
+    {
+        binding.buffer = buffer;
+        binding.offset = static_cast<size_t>(-1);
+        binding.size = static_cast<size_t>(-1);
+        mFunctions->bindBufferBase(type, static_cast<GLuint>(index), buffer);
+    }
+}
+
+void StateManagerGL::bindBufferRange(GLenum type,
+                                     size_t index,
+                                     GLuint buffer,
+                                     size_t offset,
+                                     size_t size)
+{
+    auto &binding = mIndexedBuffers[type][index];
+    if (binding.buffer != buffer || binding.offset != offset || binding.size != size)
+    {
+        binding.buffer = buffer;
+        binding.offset = offset;
+        binding.size = size;
+        mFunctions->bindBufferRange(type, static_cast<GLuint>(index), buffer, offset, size);
+    }
+}
+
 void StateManagerGL::activeTexture(size_t unit)
 {
     if (mTextureUnitIndex != unit)
@@ -498,6 +546,30 @@
     const ProgramGL *programGL = GetImplAs<ProgramGL>(program);
     useProgram(programGL->getProgramID());
 
+    for (size_t uniformBlockIndex = 0;
+         uniformBlockIndex < gl::IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS;
+         uniformBlockIndex++)
+    {
+        GLuint binding = program->getUniformBlockBinding(static_cast<GLuint>(uniformBlockIndex));
+        const OffsetBindingPointer<gl::Buffer> &uniformBuffer =
+            data.state->getIndexedUniformBuffer(binding);
+
+        if (uniformBuffer.get() != nullptr)
+        {
+            BufferGL *bufferGL = GetImplAs<BufferGL>(uniformBuffer.get());
+
+            if (uniformBuffer.getSize() == 0)
+            {
+                bindBufferBase(GL_UNIFORM_BUFFER, binding, bufferGL->getBufferID());
+            }
+            else
+            {
+                bindBufferRange(GL_UNIFORM_BUFFER, binding, bufferGL->getBufferID(),
+                                uniformBuffer.getOffset(), uniformBuffer.getSize());
+            }
+        }
+    }
+
     const std::vector<SamplerBindingGL> &appliedSamplerUniforms = programGL->getAppliedSamplerUniforms();
     for (const SamplerBindingGL &samplerUniform : appliedSamplerUniforms)
     {
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.h b/src/libANGLE/renderer/gl/StateManagerGL.h
index e999af3..2697acc 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.h
+++ b/src/libANGLE/renderer/gl/StateManagerGL.h
@@ -45,6 +45,8 @@
     void useProgram(GLuint program);
     void bindVertexArray(GLuint vao, GLuint elementArrayBuffer);
     void bindBuffer(GLenum type, GLuint buffer);
+    void bindBufferBase(GLenum type, size_t index, GLuint buffer);
+    void bindBufferRange(GLenum type, size_t index, GLuint buffer, size_t offset, size_t size);
     void activeTexture(size_t unit);
     void bindTexture(GLenum type, GLuint texture);
     void bindSampler(size_t unit, GLuint sampler);
@@ -140,6 +142,16 @@
 
     std::map<GLenum, GLuint> mBuffers;
 
+    struct IndexedBufferBinding
+    {
+        IndexedBufferBinding();
+
+        size_t offset;
+        size_t size;
+        GLuint buffer;
+    };
+    std::map<GLenum, std::vector<IndexedBufferBinding>> mIndexedBuffers;
+
     size_t mTextureUnitIndex;
     std::map<GLenum, std::vector<GLuint>> mTextures;
     std::vector<GLuint> mSamplers;
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 3d6b708..d233931 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -1531,17 +1531,17 @@
     {
         const gl::UniformBlock &uniformBlock = program->getUniformBlockByIndex(uniformBlockIndex);
         GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex);
-        const gl::Buffer *uniformBuffer = state.getIndexedUniformBuffer(blockBinding);
+        const OffsetBindingPointer<Buffer> &uniformBuffer =
+            state.getIndexedUniformBuffer(blockBinding);
 
-        if (!uniformBuffer)
+        if (uniformBuffer.get() == nullptr)
         {
             // undefined behaviour
             context->recordError(Error(GL_INVALID_OPERATION, "It is undefined behaviour to have a used but unbound uniform buffer."));
             return false;
         }
 
-        size_t uniformBufferSize = state.getIndexedUniformBufferSize(blockBinding);
-
+        size_t uniformBufferSize = uniformBuffer.getSize();
         if (uniformBufferSize == 0)
         {
             // Bind the whole buffer.