Vulkan: vertex attributes in client memory.

Support vertex data stored in client memory passed to glVertexAttribPointer.
Only GL_FLOAT data is supported at this time.  Includes a simple test.

BUG=angleproject:1683

Change-Id: I3bc0cdefe02b02c046b0e85822019a0f1762235e
Reviewed-on: https://chromium-review.googlesource.com/425137
Commit-Queue: Frank Henigman <fjhenigman@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index 0a55297..de04ea5 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -23,10 +23,12 @@
 VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state)
     : VertexArrayImpl(state),
       mCurrentArrayBufferHandles{},
+      mCurrentArrayBufferOffsets{},
       mCurrentArrayBufferResources{},
       mCurrentElementArrayBufferResource(nullptr)
 {
     mCurrentArrayBufferHandles.fill(VK_NULL_HANDLE);
+    mCurrentArrayBufferOffsets.fill(0);
     mCurrentArrayBufferResources.fill(nullptr);
 
     mPackedInputBindings.fill({0, 0});
@@ -41,6 +43,53 @@
 {
 }
 
+gl::Error VertexArrayVk::streamVertexData(ContextVk *context,
+                                          StreamingBuffer *stream,
+                                          int firstVertex,
+                                          int lastVertex)
+{
+    const auto &attribs          = mState.getVertexAttributes();
+    const auto &bindings         = mState.getVertexBindings();
+    const gl::Program *programGL = context->getGLState().getProgram();
+
+    // 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 : programGL->getActiveAttribLocationsMask())
+    {
+        const auto &attrib   = attribs[attribIndex];
+        const auto &binding  = bindings[attrib.bindingIndex];
+        gl::Buffer *bufferGL = binding.getBuffer().get();
+
+        if (attrib.enabled && !bufferGL)
+        {
+            // TODO(fjhenigman): Work with more formats than just GL_FLOAT.
+            if (attrib.type != GL_FLOAT)
+            {
+                UNIMPLEMENTED();
+                return gl::InternalError();
+            }
+
+            // Only [firstVertex, lastVertex] is needed by the upcoming draw so that
+            // is all we copy, but we allocate space for [0, lastVertex] so indexing
+            // will work.  If we don't start at zero all the indices will be off.
+            // TODO(fjhenigman): See if we can account for indices being off by adjusting
+            // the offset, thus avoiding wasted memory.
+            const size_t firstByte = firstVertex * binding.getStride();
+            const size_t lastByte =
+                lastVertex * binding.getStride() + gl::ComputeVertexAttributeTypeSize(attrib);
+            uint8_t *dst = nullptr;
+            ANGLE_TRY(stream->allocate(context, lastByte, &dst,
+                                       &mCurrentArrayBufferHandles[attribIndex],
+                                       &mCurrentArrayBufferOffsets[attribIndex]));
+            memcpy(dst + firstByte, static_cast<const uint8_t *>(attrib.pointer) + firstByte,
+                   lastByte - firstByte);
+        }
+    }
+
+    ANGLE_TRY(stream->flush(context));
+    return gl::NoError();
+}
+
 void VertexArrayVk::syncState(const gl::Context *context,
                               const gl::VertexArray::DirtyBits &dirtyBits)
 {
@@ -94,6 +143,8 @@
                 mCurrentArrayBufferResources[attribIndex] = nullptr;
                 mCurrentArrayBufferHandles[attribIndex]   = VK_NULL_HANDLE;
             }
+            // TODO(jmadill): Offset handling.  Assume zero for now.
+            mCurrentArrayBufferOffsets[attribIndex] = 0;
         }
         else
         {
@@ -107,6 +158,11 @@
     return mCurrentArrayBufferHandles;
 }
 
+const gl::AttribArray<VkDeviceSize> &VertexArrayVk::getCurrentArrayBufferOffsets() const
+{
+    return mCurrentArrayBufferOffsets;
+}
+
 void VertexArrayVk::updateDrawDependencies(vk::CommandBufferNode *readNode,
                                            const gl::AttributesMask &activeAttribsMask,
                                            Serial serial,
@@ -115,8 +171,8 @@
     // Handle the bound array buffers.
     for (auto attribIndex : activeAttribsMask)
     {
-        ASSERT(mCurrentArrayBufferResources[attribIndex]);
-        mCurrentArrayBufferResources[attribIndex]->onReadResource(readNode, serial);
+        if (mCurrentArrayBufferResources[attribIndex])
+            mCurrentArrayBufferResources[attribIndex]->onReadResource(readNode, serial);
     }
 
     // Handle the bound element array buffer.