Vulkan: Use minimal dirty bits in VertexArrayVk.

This should slightly reduce draw call overhead.

BUG=angleproject:1898

Change-Id: I0e515bf2868f237f1d6948c12942f8cb6637c0c0
Reviewed-on: https://chromium-review.googlesource.com/707690
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 3b0d904..2762c04 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -341,6 +341,7 @@
       mAttachedVertexShader(nullptr),
       mAttachedComputeShader(nullptr),
       mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
+      mMaxActiveAttribLocation(0),
       mSamplerUniformRange(0, 0),
       mImageUniformRange(0, 0),
       mAtomicCounterUniformRange(0, 0),
@@ -847,6 +848,7 @@
 {
     mState.mAttributes.clear();
     mState.mActiveAttribLocationsMask.reset();
+    mState.mMaxActiveAttribLocation = 0;
     mState.mLinkedTransformFeedbackVaryings.clear();
     mState.mUniforms.clear();
     mState.mUniformLocations.clear();
@@ -2134,11 +2136,14 @@
     for (const sh::Attribute &attribute : mState.mAttributes)
     {
         ASSERT(attribute.location != -1);
-        int regs = VariableRegisterCount(attribute.type);
+        unsigned int regs = static_cast<unsigned int>(VariableRegisterCount(attribute.type));
 
-        for (int r = 0; r < regs; r++)
+        for (unsigned int r = 0; r < regs; r++)
         {
-            mState.mActiveAttribLocationsMask.set(attribute.location + r);
+            unsigned int location = static_cast<unsigned int>(attribute.location) + r;
+            mState.mActiveAttribLocationsMask.set(location);
+            mState.mMaxActiveAttribLocation =
+                std::max(mState.mMaxActiveAttribLocation, location + 1);
         }
     }
 
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index a2a80d9..4429868 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -272,6 +272,7 @@
     {
         return mActiveAttribLocationsMask;
     }
+    unsigned int getMaxActiveAttribLocation() const { return mMaxActiveAttribLocation; }
     DrawBufferMask getActiveOutputVariables() const { return mActiveOutputVariables; }
     const std::vector<sh::OutputVariable> &getOutputVariables() const { return mOutputVariables; }
     const std::map<int, VariableLocation> &getOutputLocations() const { return mOutputLocations; }
@@ -330,6 +331,7 @@
 
     std::vector<sh::Attribute> mAttributes;
     angle::BitSet<MAX_VERTEX_ATTRIBS> mActiveAttribLocationsMask;
+    unsigned int mMaxActiveAttribLocation;
 
     // Uniforms are sorted in order:
     //  1. Non-opaque uniforms
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index 6bbfd9b..11e2afd 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -11,6 +11,7 @@
 
 #include "common/bitset_utils.h"
 #include "common/debug.h"
+#include "libANGLE/Context.h"
 #include "libANGLE/Program.h"
 #include "libANGLE/renderer/vulkan/BufferVk.h"
 #include "libANGLE/renderer/vulkan/CompilerVk.h"
@@ -77,8 +78,9 @@
 
 gl::Error ContextVk::finish()
 {
-    UNIMPLEMENTED();
-    return gl::InternalError();
+    // TODO(jmadill): Implement finish.
+    // UNIMPLEMENTED();
+    return gl::NoError();
 }
 
 gl::Error ContextVk::initPipeline(const gl::Context *context)
@@ -302,46 +304,29 @@
     const auto &state     = mState.getState();
     const auto &programGL = state.getProgram();
     const auto &vao       = state.getVertexArray();
-    const auto &attribs   = vao->getVertexAttributes();
-    const auto &bindings  = vao->getVertexBindings();
+    VertexArrayVk *vkVAO  = GetImplAs<VertexArrayVk>(vao);
     const auto *drawFBO   = state.getDrawFramebuffer();
     FramebufferVk *vkFBO  = GetImplAs<FramebufferVk>(drawFBO);
     Serial queueSerial    = mRenderer->getCurrentQueueSerial();
+    uint32_t maxAttrib    = programGL->getState().getMaxActiveAttribLocation();
 
-    // Process vertex attributes
-    // TODO(jmadill): Caching with dirty bits.
-    std::vector<VkBuffer> vertexHandles;
-    std::vector<VkDeviceSize> vertexOffsets;
-
-    for (auto attribIndex : programGL->getActiveAttribLocationsMask())
-    {
-        const auto &attrib  = attribs[attribIndex];
-        const auto &binding = bindings[attrib.bindingIndex];
-        if (attrib.enabled)
-        {
-            // TODO(jmadill): Offset handling.
-            gl::Buffer *bufferGL = binding.getBuffer().get();
-            ASSERT(bufferGL);
-            BufferVk *bufferVk = GetImplAs<BufferVk>(bufferGL);
-            vertexHandles.push_back(bufferVk->getVkBuffer().getHandle());
-            vertexOffsets.push_back(0);
-
-            bufferVk->setQueueSerial(queueSerial);
-        }
-        else
-        {
-            UNIMPLEMENTED();
-        }
-    }
+    // Process vertex attributes. Assume zero offsets for now.
+    // TODO(jmadill): Offset handling.
+    const std::vector<VkBuffer> &vertexHandles = vkVAO->getCurrentVertexBufferHandlesCache();
+    angle::MemoryBuffer *zeroBuf               = nullptr;
+    ANGLE_TRY(context->getZeroFilledBuffer(maxAttrib * sizeof(VkDeviceSize), &zeroBuf));
 
     vk::CommandBuffer *commandBuffer = nullptr;
     ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer));
     ANGLE_TRY(vkFBO->ensureInRenderPass(context, device, commandBuffer, queueSerial, state));
 
     commandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline);
+    commandBuffer->bindVertexBuffers(0, maxAttrib, vertexHandles.data(),
+                                     reinterpret_cast<const VkDeviceSize *>(zeroBuf->data()));
+
     // TODO(jmadill): the queue serial should be bound to the pipeline.
     setQueueSerial(queueSerial);
-    commandBuffer->bindVertexBuffers(0, vertexHandles, vertexOffsets);
+    vkVAO->updateCurrentBufferSerials(programGL->getActiveAttribLocationsMask(), queueSerial);
 
     return gl::NoError();
 }
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index 5d5497a..896dd56 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -12,12 +12,16 @@
 #include "common/debug.h"
 
 #include "libANGLE/Context.h"
+#include "libANGLE/renderer/vulkan/BufferVk.h"
 #include "libANGLE/renderer/vulkan/ContextVk.h"
 
 namespace rx
 {
 
-VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state) : VertexArrayImpl(state)
+VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state)
+    : VertexArrayImpl(state),
+      mCurrentVertexBufferHandlesCache(state.getMaxAttribs(), VK_NULL_HANDLE),
+      mCurrentVkBuffersCache(state.getMaxAttribs(), nullptr)
 {
 }
 
@@ -30,9 +34,60 @@
 {
     ASSERT(dirtyBits.any());
 
+    // Invalidate current pipeline.
     // TODO(jmadill): Use pipeline cache.
     auto contextVk = GetImplAs<ContextVk>(context);
     contextVk->invalidateCurrentPipeline();
+
+    // Rebuild current attribute buffers cache. This will fail horribly if the buffer changes.
+    // TODO(jmadill): Handle buffer storage changes.
+    const auto &attribs  = mState.getVertexAttributes();
+    const auto &bindings = mState.getVertexBindings();
+
+    for (auto dirtyBit : dirtyBits)
+    {
+        if (dirtyBit == gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER)
+            continue;
+
+        size_t attribIndex = gl::VertexArray::GetVertexIndexFromDirtyBit(dirtyBit);
+
+        const auto &attrib  = attribs[attribIndex];
+        const auto &binding = bindings[attrib.bindingIndex];
+        if (attrib.enabled)
+        {
+            gl::Buffer *bufferGL = binding.getBuffer().get();
+
+            if (bufferGL)
+            {
+                BufferVk *bufferVk                            = GetImplAs<BufferVk>(bufferGL);
+                mCurrentVkBuffersCache[attribIndex]           = bufferVk;
+                mCurrentVertexBufferHandlesCache[attribIndex] = bufferVk->getVkBuffer().getHandle();
+            }
+            else
+            {
+                mCurrentVkBuffersCache[attribIndex]           = nullptr;
+                mCurrentVertexBufferHandlesCache[attribIndex] = VK_NULL_HANDLE;
+            }
+        }
+        else
+        {
+            UNIMPLEMENTED();
+        }
+    }
+}
+
+const std::vector<VkBuffer> &VertexArrayVk::getCurrentVertexBufferHandlesCache() const
+{
+    return mCurrentVertexBufferHandlesCache;
+}
+
+void VertexArrayVk::updateCurrentBufferSerials(const gl::AttributesMask &activeAttribsMask,
+                                               Serial serial)
+{
+    for (auto attribIndex : activeAttribsMask)
+    {
+        mCurrentVkBuffersCache[attribIndex]->setQueueSerial(serial);
+    }
 }
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.h b/src/libANGLE/renderer/vulkan/VertexArrayVk.h
index 40c3e86..1624838 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.h
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.h
@@ -11,9 +11,11 @@
 #define LIBANGLE_RENDERER_VULKAN_VERTEXARRAYVK_H_
 
 #include "libANGLE/renderer/VertexArrayImpl.h"
+#include "libANGLE/renderer/vulkan/renderervk_utils.h"
 
 namespace rx
 {
+class BufferVk;
 
 class VertexArrayVk : public VertexArrayImpl
 {
@@ -23,6 +25,14 @@
 
     void syncState(const gl::Context *context,
                    const gl::VertexArray::DirtyBits &dirtyBits) override;
+
+    const std::vector<VkBuffer> &getCurrentVertexBufferHandlesCache() const;
+
+    void updateCurrentBufferSerials(const gl::AttributesMask &activeAttribsMask, Serial serial);
+
+  private:
+    std::vector<VkBuffer> mCurrentVertexBufferHandlesCache;
+    std::vector<BufferVk *> mCurrentVkBuffersCache;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/renderervk_utils.cpp b/src/libANGLE/renderer/vulkan/renderervk_utils.cpp
index e8db5b6..a5e7aa9 100644
--- a/src/libANGLE/renderer/vulkan/renderervk_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/renderervk_utils.cpp
@@ -446,12 +446,12 @@
 }
 
 void CommandBuffer::bindVertexBuffers(uint32_t firstBinding,
-                                      const std::vector<VkBuffer> &buffers,
-                                      const std::vector<VkDeviceSize> &offsets)
+                                      uint32_t bindingCount,
+                                      const VkBuffer *buffers,
+                                      const VkDeviceSize *offsets)
 {
-    ASSERT(valid() && buffers.size() == offsets.size());
-    vkCmdBindVertexBuffers(mHandle, firstBinding, static_cast<uint32_t>(buffers.size()),
-                           buffers.data(), offsets.data());
+    ASSERT(valid());
+    vkCmdBindVertexBuffers(mHandle, firstBinding, bindingCount, buffers, offsets);
 }
 
 void CommandBuffer::bindIndexBuffer(const vk::Buffer &buffer,
diff --git a/src/libANGLE/renderer/vulkan/renderervk_utils.h b/src/libANGLE/renderer/vulkan/renderervk_utils.h
index df1e621..ffa386a 100644
--- a/src/libANGLE/renderer/vulkan/renderervk_utils.h
+++ b/src/libANGLE/renderer/vulkan/renderervk_utils.h
@@ -239,8 +239,9 @@
 
     void bindPipeline(VkPipelineBindPoint pipelineBindPoint, const vk::Pipeline &pipeline);
     void bindVertexBuffers(uint32_t firstBinding,
-                           const std::vector<VkBuffer> &buffers,
-                           const std::vector<VkDeviceSize> &offsets);
+                           uint32_t bindingCount,
+                           const VkBuffer *buffers,
+                           const VkDeviceSize *offsets);
     void bindIndexBuffer(const vk::Buffer &buffer, VkDeviceSize offset, VkIndexType indexType);
 
   private: