Add Vulkan implementation of transfering from GrSurface to a GrGpuBuffer

Bug: skia:8962
Change-Id: I3796bef5a9e1af741b933afc78e32effb2b57a4a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206703
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 3ef6d73..59ac518 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -413,6 +413,8 @@
 void GrVkCaps::applyDriverCorrectnessWorkarounds(const VkPhysicalDeviceProperties& properties) {
     if (kQualcomm_VkVendor == properties.vendorID) {
         fMustDoCopiesFromOrigin = true;
+        // Transfer doesn't support this workaround.
+        fTransferBufferSupport = false;
     }
 
 #if defined(SK_BUILD_FOR_WIN)
@@ -949,3 +951,30 @@
     return GrBackendFormat::MakeVk(format);
 }
 
+bool GrVkCaps::onTransferFromBufferRequirements(GrColorType bufferColorType, int width,
+                                                size_t* rowBytes, size_t* offsetAlignment) const {
+    // This GrColorType has 32 bpp but the Vulkan pixel format we use for with may have 24bpp
+    // (VK_FORMAT_R8G8B8_...) or may be 32 bpp. We don't support post transforming the pixel data
+    // for transfer-from currently and don't want to have to pass info about the src surface here.
+    if (bufferColorType == GrColorType::kRGB_888x) {
+        return false;
+    }
+    size_t bpp = GrColorTypeBytesPerPixel(bufferColorType);
+    // The VkBufferImageCopy bufferOffset field must be both a multiple of 4 and of a single texel.
+    switch (bpp & 0b11) {
+        case 0:  // bpp is a multiple of 4
+            *offsetAlignment = bpp;
+            break;
+        case 2:  // bpp is a multiple of 2
+            *offsetAlignment = 2 * bpp;
+            break;
+        case 1:
+        case 3:  // bpp neither a multiple of 2 nor 4.
+            *offsetAlignment = 4 * bpp;
+            break;
+    }
+    // The bufferRowLength member of VkBufferImageCopy is in texel units and must be >= the extent
+    // of the src VkImage region.
+    *rowBytes = width * bpp;
+    return true;
+}
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 24e3e6d..34abe70 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -199,6 +199,8 @@
     bool onSurfaceSupportsWritePixels(const GrSurface*) const override;
     bool onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                           const SkIRect& srcRect, const SkIPoint& dstPoint) const override;
+    bool onTransferFromBufferRequirements(GrColorType bufferColorType, int width, size_t* rowBytes,
+                                          size_t* offsetAlignment) const override;
 
     struct ConfigInfo {
         ConfigInfo() : fOptimalFlags(0), fLinearFlags(0) {}
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 546a548..f12a1a8 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -484,6 +484,88 @@
     return true;
 }
 
+size_t GrVkGpu::onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
+                                     GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
+                                     size_t offset) {
+    SkASSERT(surface);
+    SkASSERT(transferBuffer);
+
+    size_t rowBytes;
+    size_t offsetAlignment;
+    if (!this->vkCaps().transferFromBufferRequirements(bufferColorType, width, &rowBytes,
+                                                       &offsetAlignment)) {
+        return 0;
+    }
+
+    if (offset % offsetAlignment || offset + height * rowBytes > transferBuffer->size()) {
+        return 0;
+    }
+
+    GrVkTransferBuffer* vkBuffer = static_cast<GrVkTransferBuffer*>(transferBuffer);
+
+    GrVkImage* srcImage;
+    if (GrVkRenderTarget* rt = static_cast<GrVkRenderTarget*>(surface->asRenderTarget())) {
+        // Reading from render targets that wrap a secondary command buffer is not allowed since
+        // it would require us to know the VkImage, which we don't have, as well as need us to
+        // stop and start the VkRenderPass which we don't have access to.
+        if (rt->wrapsSecondaryCommandBuffer()) {
+            return false;
+        }
+        // resolve the render target if necessary
+        switch (rt->getResolveType()) {
+            case GrVkRenderTarget::kCantResolve_ResolveType:
+                return false;
+            case GrVkRenderTarget::kAutoResolves_ResolveType:
+                break;
+            case GrVkRenderTarget::kCanResolve_ResolveType:
+                this->resolveRenderTargetNoFlush(rt);
+                break;
+            default:
+                SK_ABORT("Unknown resolve type");
+        }
+        srcImage = rt;
+    } else {
+        srcImage = static_cast<GrVkTexture*>(surface->asTexture());
+    }
+
+    // Set up copy region
+    VkBufferImageCopy region;
+    memset(&region, 0, sizeof(VkBufferImageCopy));
+    region.bufferOffset = offset;
+    // We're assuming that GrVkCaps made the row bytes tight.
+    region.bufferRowLength = 0;
+#ifdef SK_DEBUG
+    int bpp = GrColorTypeBytesPerPixel(bufferColorType);
+    SkASSERT(rowBytes == width * (size_t)bpp);
+#endif
+    region.bufferImageHeight = 0;
+    region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
+    region.imageOffset = { left, top, 0 };
+    region.imageExtent = { (uint32_t)width, (uint32_t)height, 1 };
+
+    srcImage->setImageLayout(this,
+                             VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                             VK_ACCESS_TRANSFER_READ_BIT,
+                             VK_PIPELINE_STAGE_TRANSFER_BIT,
+                             false);
+
+    fCurrentCmdBuffer->copyImageToBuffer(this, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                                         vkBuffer, 1, &region);
+
+    // Make sure the copy to buffer has finished.
+    vkBuffer->addMemoryBarrier(this,
+                               VK_ACCESS_TRANSFER_WRITE_BIT,
+                               VK_ACCESS_HOST_READ_BIT,
+                               VK_PIPELINE_STAGE_TRANSFER_BIT,
+                               VK_PIPELINE_STAGE_HOST_BIT,
+                               false);
+
+    // The caller is responsible for syncing.
+    this->submitCommandBuffer(kSkip_SyncQueue);
+
+    return rowBytes;
+}
+
 void GrVkGpu::resolveImage(GrSurface* dst, GrVkRenderTarget* src, const SkIRect& srcRect,
                            const SkIPoint& dstPoint) {
     SkASSERT(dst);
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 2984e40..007ac17 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -213,11 +213,8 @@
 
     bool onTransferPixelsTo(GrTexture*, int left, int top, int width, int height, GrColorType,
                             GrGpuBuffer* transferBuffer, size_t offset, size_t rowBytes) override;
-    // TODO(bsalomon)
     size_t onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
-                                GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override {
-        return 0;
-    }
+                                GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override;
 
     bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src,
                        GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,