Vulkan: Move Streaming data to VertexArrayVk.

Instead of the ContextVk owning the translations for the various
attributes, make the VertexArrayVk own them. This way they can
handle the dirty bit state notifications directly instead of
needing their own Observers.

Bug: angleproject:2389
Change-Id: I5e571ba6c563e820a4c0d5f92db35031e6f2428a
Reviewed-on: https://chromium-review.googlesource.com/989258
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index df7bf21..3114538 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -15,17 +15,29 @@
 #include "libANGLE/renderer/vulkan/BufferVk.h"
 #include "libANGLE/renderer/vulkan/CommandGraph.h"
 #include "libANGLE/renderer/vulkan/ContextVk.h"
+#include "libANGLE/renderer/vulkan/RendererVk.h"
 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
 
 namespace rx
 {
+namespace
+{
+constexpr size_t kDynamicVertexDataSize = 1024 * 1024;
+constexpr size_t kDynamicIndexDataSize  = 1024 * 8;
+}  // anonymous namespace
 
 VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state)
     : VertexArrayImpl(state),
       mCurrentArrayBufferHandles{},
       mCurrentArrayBufferOffsets{},
       mCurrentArrayBufferResources{},
-      mCurrentElementArrayBufferResource(nullptr)
+      mCurrentElementArrayBufferHandle(VK_NULL_HANDLE),
+      mCurrentElementArrayBufferOffset(0),
+      mCurrentElementArrayBufferResource(nullptr),
+      mDynamicVertexData(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, kDynamicVertexDataSize),
+      mDynamicIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize),
+      mVertexBuffersDirty(false),
+      mIndexBufferDirty(false)
 {
     mCurrentArrayBufferHandles.fill(VK_NULL_HANDLE);
     mCurrentArrayBufferOffsets.fill(0);
@@ -33,6 +45,9 @@
 
     mPackedInputBindings.fill({0, 0});
     mPackedInputAttributes.fill({0, 0, 0});
+
+    mDynamicVertexData.init(1);
+    mDynamicIndexData.init(1);
 }
 
 VertexArrayVk::~VertexArrayVk()
@@ -41,29 +56,27 @@
 
 void VertexArrayVk::destroy(const gl::Context *context)
 {
+    VkDevice device = vk::GetImpl(context)->getRenderer()->getDevice();
+
+    mDynamicVertexData.destroy(device);
+    mDynamicIndexData.destroy(device);
+    mLineLoopHandler.destroy(device);
 }
 
-gl::AttributesMask VertexArrayVk::getAttribsToStream(const gl::Context *context) const
-{
-    const gl::Program *programGL = context->getGLState().getProgram();
-    return mClientMemoryAttribs & programGL->getActiveAttribLocationsMask();
-}
-
-gl::Error VertexArrayVk::streamVertexData(const gl::Context *context,
-                                          DynamicBuffer *dynamicBuffer,
+gl::Error VertexArrayVk::streamVertexData(RendererVk *renderer,
+                                          const gl::AttributesMask &attribsToStream,
                                           const gl::DrawCallParams &drawCallParams)
 {
-    ContextVk *contextVk         = vk::GetImpl(context);
+    ASSERT(!attribsToStream.none());
+
     const auto &attribs          = mState.getVertexAttributes();
     const auto &bindings         = mState.getVertexBindings();
 
-    ANGLE_TRY(drawCallParams.ensureIndexRangeResolved(context));
-
     const size_t lastVertex = drawCallParams.firstVertex() + drawCallParams.vertexCount();
 
     // TODO(fjhenigman): When we have a bunch of interleaved attributes, they end up
     // un-interleaved, wasting space and copying time.  Consider improving on that.
-    for (auto attribIndex : getAttribsToStream(context))
+    for (auto attribIndex : attribsToStream)
     {
         const gl::VertexAttribute &attrib = attribs[attribIndex];
         const gl::VertexBinding &binding  = bindings[attrib.bindingIndex];
@@ -86,14 +99,48 @@
             lastVertex * binding.getStride() + gl::ComputeVertexAttributeTypeSize(attrib);
         uint8_t *dst = nullptr;
         uint32_t offset = 0;
-        ANGLE_TRY(dynamicBuffer->allocate(
-            contextVk, lastByte, &dst, &mCurrentArrayBufferHandles[attribIndex], &offset, nullptr));
+        ANGLE_TRY(mDynamicVertexData.allocate(
+            renderer, lastByte, &dst, &mCurrentArrayBufferHandles[attribIndex], &offset, nullptr));
         mCurrentArrayBufferOffsets[attribIndex] = static_cast<VkDeviceSize>(offset);
         memcpy(dst + firstByte, static_cast<const uint8_t *>(attrib.pointer) + firstByte,
                lastByte - firstByte);
     }
 
-    ANGLE_TRY(dynamicBuffer->flush(contextVk));
+    ANGLE_TRY(mDynamicVertexData.flush(renderer->getDevice()));
+    return gl::NoError();
+}
+
+gl::Error VertexArrayVk::streamIndexData(RendererVk *renderer,
+                                         const gl::DrawCallParams &drawCallParams)
+{
+    ASSERT(!mState.getElementArrayBuffer().get());
+
+    uint32_t offset = 0;
+
+    const GLsizei amount = sizeof(GLushort) * drawCallParams.indexCount();
+    GLubyte *dst         = nullptr;
+
+    ANGLE_TRY(mDynamicIndexData.allocate(renderer, amount, &dst, &mCurrentElementArrayBufferHandle,
+                                         &offset, nullptr));
+    if (drawCallParams.type() == GL_UNSIGNED_BYTE)
+    {
+        // Unsigned bytes don't have direct support in Vulkan so we have to expand the
+        // memory to a GLushort.
+        const GLubyte *in     = static_cast<const GLubyte *>(drawCallParams.indices());
+        GLushort *expandedDst = reinterpret_cast<GLushort *>(dst);
+        for (GLsizei index = 0; index < drawCallParams.indexCount(); index++)
+        {
+            expandedDst[index] = static_cast<GLushort>(in[index]);
+        }
+    }
+    else
+    {
+        memcpy(dst, drawCallParams.indices(), amount);
+    }
+    ANGLE_TRY(mDynamicIndexData.flush(renderer->getDevice()));
+
+    mCurrentElementArrayBufferOffset = offset;
+
     return gl::NoError();
 }
 
@@ -104,9 +151,10 @@
 {
     ASSERT(dirtyBits.any());
 
+    bool invalidatePipeline = false;
+
     // Invalidate current pipeline.
     ContextVk *contextVk = vk::GetImpl(context);
-    contextVk->onVertexArrayChange();
 
     // Rebuild current attribute buffers cache. This will fail horribly if the buffer changes.
     // TODO(jmadill): Handle buffer storage changes.
@@ -122,16 +170,24 @@
                 gl::Buffer *bufferGL = mState.getElementArrayBuffer().get();
                 if (bufferGL)
                 {
-                    mCurrentElementArrayBufferResource = vk::GetImpl(bufferGL);
+                    BufferVk *bufferVk                 = vk::GetImpl(bufferGL);
+                    mCurrentElementArrayBufferResource = bufferVk;
+                    mCurrentElementArrayBufferHandle   = bufferVk->getVkBuffer().getHandle();
+                    mCurrentElementArrayBufferOffset   = 0;
                 }
                 else
                 {
                     mCurrentElementArrayBufferResource = nullptr;
+                    mCurrentElementArrayBufferHandle   = VK_NULL_HANDLE;
+                    mCurrentElementArrayBufferOffset   = 0;
                 }
+                mIndexBufferDirty = true;
                 break;
             }
 
             case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
+                mLineLoopBufferFirstIndex.reset();
+                mLineLoopBufferLastIndex.reset();
                 break;
 
             default:
@@ -170,11 +226,19 @@
                     mClientMemoryAttribs.reset(attribIndex);
                     UNIMPLEMENTED();
                 }
+
+                invalidatePipeline = true;
                 break;
             }
         }
     }
 
+    if (invalidatePipeline)
+    {
+        mVertexBuffersDirty = true;
+        contextVk->invalidateCurrentPipeline();
+    }
+
     return gl::NoError();
 }
 
@@ -188,30 +252,25 @@
     return mCurrentArrayBufferOffsets;
 }
 
-void VertexArrayVk::updateDrawDependencies(vk::CommandGraphNode *readNode,
-                                           const gl::AttributesMask &activeAttribsMask,
-                                           ResourceVk *elementArrayBufferOverride,
-                                           Serial serial,
-                                           bool isDrawElements)
+void VertexArrayVk::updateArrayBufferReadDependencies(vk::CommandGraphNode *drawNode,
+                                                      const gl::AttributesMask &activeAttribsMask,
+                                                      Serial serial)
 {
     // Handle the bound array buffers.
-    for (auto attribIndex : activeAttribsMask)
+    for (size_t attribIndex : activeAttribsMask)
     {
         if (mCurrentArrayBufferResources[attribIndex])
-            mCurrentArrayBufferResources[attribIndex]->onReadResource(readNode, serial);
+            mCurrentArrayBufferResources[attribIndex]->onReadResource(drawNode, serial);
     }
+}
 
+void VertexArrayVk::updateElementArrayBufferReadDependency(vk::CommandGraphNode *drawNode,
+                                                           Serial serial)
+{
     // Handle the bound element array buffer.
-    if (isDrawElements)
+    if (mCurrentElementArrayBufferResource)
     {
-        if (elementArrayBufferOverride != nullptr)
-        {
-            elementArrayBufferOverride->onReadResource(readNode, serial);
-        }
-        else if (mCurrentElementArrayBufferResource != nullptr)
-        {
-            mCurrentElementArrayBufferResource->onReadResource(readNode, serial);
-        }
+        mCurrentElementArrayBufferResource->onReadResource(drawNode, serial);
     }
 }
 
@@ -271,4 +330,155 @@
     attribDesc.offset = static_cast<uint32_t>(ComputeVertexAttributeOffset(attrib, binding));
 }
 
+gl::Error VertexArrayVk::drawArrays(const gl::Context *context,
+                                    RendererVk *renderer,
+                                    const gl::DrawCallParams &drawCallParams,
+                                    vk::CommandGraphNode *drawNode,
+                                    bool newCommandBuffer)
+{
+    vk::CommandBuffer *commandBuffer = drawNode->getInsideRenderPassCommands();
+    ASSERT(commandBuffer->valid());
+
+    ANGLE_TRY(onDraw(context, renderer, drawCallParams, drawNode, newCommandBuffer));
+
+    if (drawCallParams.mode() != GL_LINE_LOOP)
+    {
+        commandBuffer->draw(drawCallParams.vertexCount(), 1, drawCallParams.firstVertex(), 0);
+        return gl::NoError();
+    }
+
+    // Handle GL_LINE_LOOP drawArrays.
+    int lastVertex = drawCallParams.firstVertex() + drawCallParams.vertexCount();
+    if (!mLineLoopBufferFirstIndex.valid() || !mLineLoopBufferLastIndex.valid() ||
+        mLineLoopBufferFirstIndex != drawCallParams.firstVertex() ||
+        mLineLoopBufferLastIndex != lastVertex)
+    {
+        ANGLE_TRY(mLineLoopHandler.createIndexBuffer(renderer, drawCallParams,
+                                                     &mCurrentElementArrayBufferHandle,
+                                                     &mCurrentElementArrayBufferOffset));
+
+        mLineLoopBufferFirstIndex = drawCallParams.firstVertex();
+        mLineLoopBufferLastIndex  = lastVertex;
+    }
+
+    commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle,
+                                   mCurrentElementArrayBufferOffset, VK_INDEX_TYPE_UINT32);
+
+    vk::LineLoopHandler::Draw(drawCallParams.vertexCount(), commandBuffer);
+
+    return gl::NoError();
+}
+
+gl::Error VertexArrayVk::drawElements(const gl::Context *context,
+                                      RendererVk *renderer,
+                                      const gl::DrawCallParams &drawCallParams,
+                                      vk::CommandGraphNode *drawNode,
+                                      bool newCommandBuffer)
+{
+    vk::CommandBuffer *commandBuffer = drawNode->getInsideRenderPassCommands();
+    ASSERT(commandBuffer->valid());
+
+    if (drawCallParams.mode() != GL_LINE_LOOP)
+    {
+        ANGLE_TRY(onIndexedDraw(context, renderer, drawCallParams, drawNode, newCommandBuffer));
+        commandBuffer->drawIndexed(drawCallParams.indexCount(), 1, 0, 0, 0);
+        return gl::NoError();
+    }
+
+    // Handle GL_LINE_LOOP drawElements.
+    gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer().get();
+    if (!elementArrayBuffer)
+    {
+        UNIMPLEMENTED();
+        return gl::InternalError() << "Line loop indices in client memory not supported";
+    }
+
+    BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
+
+    VkIndexType indexType = gl_vk::GetIndexType(drawCallParams.type());
+
+    ANGLE_TRY(mLineLoopHandler.createIndexBufferFromElementArrayBuffer(
+        renderer, elementArrayBufferVk, indexType, drawCallParams.indexCount(),
+        &mCurrentElementArrayBufferHandle, &mCurrentElementArrayBufferOffset));
+
+    ANGLE_TRY(onIndexedDraw(context, renderer, drawCallParams, drawNode, newCommandBuffer));
+    vk::LineLoopHandler::Draw(drawCallParams.indexCount(), commandBuffer);
+
+    return gl::NoError();
+}
+
+gl::Error VertexArrayVk::onDraw(const gl::Context *context,
+                                RendererVk *renderer,
+                                const gl::DrawCallParams &drawCallParams,
+                                vk::CommandGraphNode *drawNode,
+                                bool newCommandBuffer)
+{
+    const gl::State &state                  = context->getGLState();
+    const gl::Program *programGL            = state.getProgram();
+    const gl::AttributesMask &activeAttribs = programGL->getActiveAttribLocationsMask();
+    uint32_t maxAttrib                      = programGL->getState().getMaxActiveAttribLocation();
+
+    if (mClientMemoryAttribs.any())
+    {
+        const gl::AttributesMask &attribsToStream = (mClientMemoryAttribs & activeAttribs);
+        if (attribsToStream.any())
+        {
+            ANGLE_TRY(drawCallParams.ensureIndexRangeResolved(context));
+            ANGLE_TRY(streamVertexData(renderer, attribsToStream, drawCallParams));
+            vk::CommandBuffer *commandBuffer = drawNode->getInsideRenderPassCommands();
+            commandBuffer->bindVertexBuffers(0, maxAttrib, mCurrentArrayBufferHandles.data(),
+                                             mCurrentArrayBufferOffsets.data());
+        }
+    }
+    else if (mVertexBuffersDirty || newCommandBuffer)
+    {
+        vk::CommandBuffer *commandBuffer = drawNode->getInsideRenderPassCommands();
+        commandBuffer->bindVertexBuffers(0, maxAttrib, mCurrentArrayBufferHandles.data(),
+                                         mCurrentArrayBufferOffsets.data());
+        updateArrayBufferReadDependencies(drawNode, activeAttribs,
+                                          renderer->getCurrentQueueSerial());
+        mVertexBuffersDirty = false;
+    }
+
+    return gl::NoError();
+}
+
+gl::Error VertexArrayVk::onIndexedDraw(const gl::Context *context,
+                                       RendererVk *renderer,
+                                       const gl::DrawCallParams &drawCallParams,
+                                       vk::CommandGraphNode *drawNode,
+                                       bool newCommandBuffer)
+{
+    ANGLE_TRY(onDraw(context, renderer, drawCallParams, drawNode, newCommandBuffer));
+
+    if (!mState.getElementArrayBuffer().get())
+    {
+        ANGLE_TRY(drawCallParams.ensureIndexRangeResolved(context));
+        ANGLE_TRY(streamIndexData(renderer, drawCallParams));
+        vk::CommandBuffer *commandBuffer = drawNode->getInsideRenderPassCommands();
+        commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle,
+                                       mCurrentElementArrayBufferOffset,
+                                       gl_vk::GetIndexType(drawCallParams.type()));
+    }
+    else if (mIndexBufferDirty || newCommandBuffer)
+    {
+        if (drawCallParams.type() == GL_UNSIGNED_BYTE)
+        {
+            // TODO(fjhenigman): Index format translation.
+            UNIMPLEMENTED();
+            return gl::InternalError()
+                   << "Unsigned byte translation is not implemented for indices in a buffer object";
+        }
+
+        vk::CommandBuffer *commandBuffer = drawNode->getInsideRenderPassCommands();
+        commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle,
+                                       mCurrentElementArrayBufferOffset,
+                                       gl_vk::GetIndexType(drawCallParams.type()));
+        updateElementArrayBufferReadDependency(drawNode, renderer->getCurrentQueueSerial());
+        mIndexBufferDirty = false;
+    }
+
+    return gl::NoError();
+}
+
 }  // namespace rx