Implement GrGpu::transferFromBufferToBuffer on Vulkan

Bug: skia:13427
Bug: skia:12720
Change-Id: I91c2707bda1c3ed6a850627f9187cfc5b4b81517
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/550706
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ganesh/vk/GrVkBuffer.cpp b/src/gpu/ganesh/vk/GrVkBuffer.cpp
index f029bf3..1fe9c0c 100644
--- a/src/gpu/ganesh/vk/GrVkBuffer.cpp
+++ b/src/gpu/ganesh/vk/GrVkBuffer.cpp
@@ -184,19 +184,6 @@
     GrVkMemory::UnmapAlloc(gpu, fAlloc);
 }
 
-static VkAccessFlags buffer_type_to_access_flags(GrGpuBufferType type) {
-    switch (type) {
-        case GrGpuBufferType::kIndex:
-            return VK_ACCESS_INDEX_READ_BIT;
-        case GrGpuBufferType::kVertex:
-            return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
-        default:
-            // This helper is only called for static buffers so we should only ever see index or
-            // vertex buffers types
-            SkUNREACHABLE;
-    }
-}
-
 void GrVkBuffer::copyCpuDataToGpuBuffer(const void* src, size_t size) {
     SkASSERT(src);
 
@@ -220,15 +207,12 @@
             return;
         }
 
-        gpu->copyBuffer(std::move(transferBuffer), sk_ref_sp(this), /*srcOffset=*/0,
-                        /*dstOffset=*/0, size);
+        gpu->transferFromBufferToBuffer(std::move(transferBuffer),
+                                        /*srcOffset=*/0,
+                                        sk_ref_sp(this),
+                                        /*dstOffset=*/0,
+                                        size);
     }
-
-    this->addMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT,
-                           buffer_type_to_access_flags(this->intendedType()),
-                           VK_PIPELINE_STAGE_TRANSFER_BIT,
-                           VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
-                           /*byRegion=*/false);
 }
 
 void GrVkBuffer::addMemoryBarrier(VkAccessFlags srcAccessMask,
diff --git a/src/gpu/ganesh/vk/GrVkCaps.cpp b/src/gpu/ganesh/vk/GrVkCaps.cpp
index b0a9ce7..1dad2dc 100644
--- a/src/gpu/ganesh/vk/GrVkCaps.cpp
+++ b/src/gpu/ganesh/vk/GrVkCaps.cpp
@@ -62,6 +62,7 @@
 
     fTransferFromBufferToTextureSupport = true;
     fTransferFromSurfaceToBufferSupport = true;
+    fTransferFromBufferToBufferSupport  = true;
 
     fMaxRenderTargetSize = 4096; // minimum required by spec
     fMaxTextureSize = 4096; // minimum required by spec
diff --git a/src/gpu/ganesh/vk/GrVkGpu.cpp b/src/gpu/ganesh/vk/GrVkGpu.cpp
index 70fbb5b..b7ef05b 100644
--- a/src/gpu/ganesh/vk/GrVkGpu.cpp
+++ b/src/gpu/ganesh/vk/GrVkGpu.cpp
@@ -536,6 +536,55 @@
     return success;
 }
 
+// If dst is a vertex/index buffer then this functions inserts a mem barrier for that use case.
+// Otherwise, does nothing.
+static void add_dst_buffer_mem_barrier(GrVkGpu* gpu, GrVkBuffer* dst, size_t offset, size_t size) {
+    if (dst->intendedType() != GrGpuBufferType::kIndex &&
+        dst->intendedType() != GrGpuBufferType::kVertex) {
+        return;
+    }
+    VkAccessFlags dstAccessMask = dst->intendedType() == GrGpuBufferType::kIndex
+                                  ? VK_ACCESS_INDEX_READ_BIT
+                                  : VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
+    VkBufferMemoryBarrier bufferMemoryBarrier = {
+            VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,  // sType
+            nullptr,                                  // pNext
+            VK_ACCESS_TRANSFER_WRITE_BIT,             // srcAccessMask
+            dstAccessMask,                            // dstAccessMask
+            VK_QUEUE_FAMILY_IGNORED,                  // srcQueueFamilyIndex
+            VK_QUEUE_FAMILY_IGNORED,                  // dstQueueFamilyIndex
+            dst->vkBuffer(),                          // buffer
+            offset,                                   // offset
+            size,                                     // size
+    };
+
+    gpu->addBufferMemoryBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
+                                VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
+                                /*byRegion=*/false,
+                                &bufferMemoryBarrier);
+
+}
+
+bool GrVkGpu::onTransferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
+                                           size_t srcOffset,
+                                           sk_sp<GrGpuBuffer> dst,
+                                           size_t dstOffset,
+                                           size_t size) {
+    if (!this->currentCommandBuffer()) {
+        return false;
+    }
+
+    VkBufferCopy copyRegion;
+    copyRegion.srcOffset = srcOffset;
+    copyRegion.dstOffset = dstOffset;
+    copyRegion.size = size;
+    this->currentCommandBuffer()->copyBuffer(this, std::move(src), dst, 1, &copyRegion);
+
+    add_dst_buffer_mem_barrier(this, static_cast<GrVkBuffer*>(dst.get()), dstOffset, size);
+
+    return true;
+}
+
 bool GrVkGpu::onTransferPixelsTo(GrTexture* texture,
                                  SkIRect rect,
                                  GrColorType surfaceColorType,
@@ -1137,29 +1186,15 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrVkGpu::copyBuffer(sk_sp<GrGpuBuffer> srcBuffer,
-                         sk_sp<GrGpuBuffer> dstBuffer,
-                         VkDeviceSize srcOffset,
-                         VkDeviceSize dstOffset,
-                         VkDeviceSize size) {
-    if (!this->currentCommandBuffer()) {
-        return;
-    }
-    VkBufferCopy copyRegion;
-    copyRegion.srcOffset = srcOffset;
-    copyRegion.dstOffset = dstOffset;
-    copyRegion.size = size;
-    this->currentCommandBuffer()->copyBuffer(this, std::move(srcBuffer), std::move(dstBuffer), 1,
-                                             &copyRegion);
-}
-
 bool GrVkGpu::updateBuffer(sk_sp<GrVkBuffer> buffer, const void* src,
                            VkDeviceSize offset, VkDeviceSize size) {
     if (!this->currentCommandBuffer()) {
         return false;
     }
     // Update the buffer
-    this->currentCommandBuffer()->updateBuffer(this, std::move(buffer), offset, size, src);
+    this->currentCommandBuffer()->updateBuffer(this, buffer, offset, size, src);
+
+    add_dst_buffer_mem_barrier(this, static_cast<GrVkBuffer*>(buffer.get()), offset, size);
 
     return true;
 }
diff --git a/src/gpu/ganesh/vk/GrVkGpu.h b/src/gpu/ganesh/vk/GrVkGpu.h
index 44bd21d..b1f2522 100644
--- a/src/gpu/ganesh/vk/GrVkGpu.h
+++ b/src/gpu/ganesh/vk/GrVkGpu.h
@@ -174,8 +174,6 @@
 
     std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
-    void copyBuffer(sk_sp<GrGpuBuffer> srcBuffer, sk_sp<GrGpuBuffer> dstBuffer,
-                    VkDeviceSize srcOffset, VkDeviceSize dstOffset, VkDeviceSize size);
     bool updateBuffer(sk_sp<GrVkBuffer> buffer, const void* src, VkDeviceSize offset,
                       VkDeviceSize size);
 
@@ -286,6 +284,12 @@
                        int mipLevelCount,
                        bool prepForTexSampling) override;
 
+    bool onTransferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
+                                      size_t srcOffset,
+                                      sk_sp<GrGpuBuffer> dst,
+                                      size_t dstOffset,
+                                      size_t size) override;
+
     bool onTransferPixelsTo(GrTexture*,
                             SkIRect,
                             GrColorType textureColorType,
diff --git a/tests/GrGpuBufferTest.cpp b/tests/GrGpuBufferTest.cpp
index 455abc4..5b03707 100644
--- a/tests/GrGpuBufferTest.cpp
+++ b/tests/GrGpuBufferTest.cpp
@@ -195,7 +195,7 @@
         return rp->createBuffer(points.get(),
                                 totalVertices*sizeof(SkPoint),
                                 GrGpuBufferType::kXferCpuToGpu,
-                                kStream_GrAccessPattern);
+                                kDynamic_GrAccessPattern);
     };
 
     auto create_vertex_buffer = [&](sk_sp<GrGpuBuffer> srcBuffer,