Vulkan: More Vertex Array optimizations.

Inlines a number of Vulkan vertex array methods.

Also changes the way vertex buffers are bound. Note that Vulkan doesn't
support NULL buffer bindings. Thus we create an emulated NULL buffer
to work around the problem of having gaps in the bound vertex buffers.
This allows us to use a single bind call for ranges of vertex buffers
even when there are gaps.

Also changes how vertex array dirty bits are reset. Instead of calling
memset to clear the affected buffers we pass a mutable pointer to the
Vertex Array sync state. This allows us to only reset the dirty bits
that we sync. This saves on the memory clearing time.

Improves perf by about 10% in the Vulkan VBO state change test.

Bug: angleproject:3014
Change-Id: Ib7b742dff7897fc891606a652ea0b64255a24c86
Reviewed-on: https://chromium-review.googlesource.com/c/1390360
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index 5ee91ee..378321c 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -95,7 +95,7 @@
         kVertexBufferUsageFlags, 1024 * 8, true \
     }
 
-VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state, RendererVk *renderer)
+VertexArrayVk::VertexArrayVk(ContextVk *contextVk, const gl::VertexArrayState &state)
     : VertexArrayImpl(state),
       mCurrentArrayBufferHandles{},
       mCurrentArrayBufferOffsets{},
@@ -126,10 +126,18 @@
       mDynamicVertexData(kVertexBufferUsageFlags, kDynamicVertexDataSize, true),
       mDynamicIndexData(kIndexBufferUsageFlags, kDynamicIndexDataSize, true),
       mTranslatedByteIndexData(kIndexBufferUsageFlags, kDynamicIndexDataSize, true),
-      mLineLoopHelper(renderer),
+      mLineLoopHelper(contextVk->getRenderer()),
       mDirtyLineLoopTranslation(true)
 {
-    mCurrentArrayBufferHandles.fill(VK_NULL_HANDLE);
+    RendererVk *renderer = contextVk->getRenderer();
+
+    VkBufferCreateInfo createInfo = {};
+    createInfo.sType              = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+    createInfo.size               = 16;
+    createInfo.usage              = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+    (void)mTheNullBuffer.init(contextVk, createInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+    mCurrentArrayBufferHandles.fill(mTheNullBuffer.getBuffer().getHandle());
     mCurrentArrayBufferOffsets.fill(0);
     mCurrentArrayBuffers.fill(nullptr);
 
@@ -148,6 +156,8 @@
 {
     RendererVk *renderer = vk::GetImpl(context)->getRenderer();
 
+    mTheNullBuffer.release(renderer);
+
     for (vk::DynamicBuffer &buffer : mCurrentArrayBufferConversion)
     {
         buffer.release(renderer);
@@ -286,7 +296,7 @@
     return angle::Result::Continue;
 }
 
-void VertexArrayVk::ensureConversionReleased(RendererVk *renderer, size_t attribIndex)
+ANGLE_INLINE void VertexArrayVk::ensureConversionReleased(RendererVk *renderer, size_t attribIndex)
 {
     if (mCurrentArrayBufferConversionCanRelease[attribIndex])
     {
@@ -297,8 +307,8 @@
 
 angle::Result VertexArrayVk::syncState(const gl::Context *context,
                                        const gl::VertexArray::DirtyBits &dirtyBits,
-                                       const gl::VertexArray::DirtyAttribBitsArray &attribBits,
-                                       const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
+                                       gl::VertexArray::DirtyAttribBitsArray *attribBits,
+                                       gl::VertexArray::DirtyBindingBitsArray *bindingBits)
 {
     ASSERT(dirtyBits.any());
 
@@ -347,6 +357,7 @@
         ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX],                      \
                                   bindings[attribs[INDEX].bindingIndex], INDEX)); \
         invalidateContext = true;                                                 \
+        (*attribBits)[INDEX].reset();                                             \
         break;
 
                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC);
@@ -356,6 +367,7 @@
         ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX],                      \
                                   bindings[attribs[INDEX].bindingIndex], INDEX)); \
         invalidateContext = true;                                                 \
+        (*bindingBits)[INDEX].reset();                                            \
         break;
 
                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC);
@@ -404,7 +416,6 @@
                                              size_t attribIndex)
 {
     RendererVk *renderer               = contextVk->getRenderer();
-    bool releaseConversion             = true;
     bool anyVertexBufferConvertedOnGpu = false;
 
     if (attrib.enabled)
@@ -436,8 +447,6 @@
                     // http://anglebug.com/3009
                     ANGLE_TRY(convertVertexBufferCpu(contextVk, bufferVk, binding, attribIndex));
                 }
-
-                releaseConversion = false;
             }
             else
             {
@@ -446,15 +455,17 @@
                     bufferVk->getBuffer().getBuffer().getHandle();
                 mCurrentArrayBufferOffsets[attribIndex] = binding.getOffset();
                 mCurrentArrayBufferStrides[attribIndex] = binding.getStride();
+                ensureConversionReleased(renderer, attribIndex);
             }
         }
         else
         {
             mCurrentArrayBuffers[attribIndex]       = nullptr;
-            mCurrentArrayBufferHandles[attribIndex] = VK_NULL_HANDLE;
+            mCurrentArrayBufferHandles[attribIndex] = mTheNullBuffer.getBuffer().getHandle();
             mCurrentArrayBufferOffsets[attribIndex] = 0;
             mCurrentArrayBufferStrides[attribIndex] =
                 mCurrentArrayBufferFormats[attribIndex]->bufferFormat().pixelBytes;
+            ensureConversionReleased(renderer, attribIndex);
         }
 
         setPackedInputInfo(contextVk, attribIndex, attrib, binding);
@@ -465,13 +476,14 @@
 
         // These will be filled out by the ContextVk.
         mCurrentArrayBuffers[attribIndex]       = nullptr;
-        mCurrentArrayBufferHandles[attribIndex] = VK_NULL_HANDLE;
+        mCurrentArrayBufferHandles[attribIndex] = mTheNullBuffer.getBuffer().getHandle();
         mCurrentArrayBufferOffsets[attribIndex] = 0;
         mCurrentArrayBufferStrides[attribIndex] = 0;
         mCurrentArrayBufferFormats[attribIndex] =
             &renderer->getFormat(angle::FormatID::R32G32B32A32_FLOAT);
 
         setDefaultPackedInput(contextVk, attribIndex);
+        ensureConversionReleased(renderer, attribIndex);
     }
 
     if (anyVertexBufferConvertedOnGpu && renderer->getFeatures().flushAfterVertexConversion)
@@ -479,9 +491,6 @@
         ANGLE_TRY(renderer->flush(contextVk));
     }
 
-    if (releaseConversion)
-        ensureConversionReleased(renderer, attribIndex);
-
     return angle::Result::Continue;
 }