Add workaround for Adreno copyImageToBuffer calls on Vulkan

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2328823003

Review-Url: https://codereview.chromium.org/2328823003
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 9027f68..3348916 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -16,6 +16,7 @@
                    VkPhysicalDevice physDev, uint32_t featureFlags, uint32_t extensionFlags)
     : INHERITED(contextOptions) {
     fCanUseGLSLForShaderModule = false;
+    fMustDoCopiesFromOrigin = false;
 
     /**************************************************************************
     * GrDrawTargetCaps fields
@@ -66,6 +67,10 @@
         // fCanUseGLSLForShaderModule = true;
     }
 
+    if (kQualcomm_VkVendor == properties.vendorID) {
+        fMustDoCopiesFromOrigin = true;
+    }
+
     this->applyOptionsOverrides(contextOptions);
     GrGLSLCaps* glslCaps = static_cast<GrGLSLCaps*>(fShaderCaps.get());
     glslCaps->applyOptionsOverrides(contextOptions);
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 3733f2c..f1ab8d0 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -62,6 +62,10 @@
         return fCanUseGLSLForShaderModule;
     }
 
+    bool mustDoCopiesFromOrigin() const {
+        return fMustDoCopiesFromOrigin;
+    }
+
     /**
      * Returns both a supported and most prefered stencil format to use in draws.
      */
@@ -72,6 +76,10 @@
     GrGLSLCaps* glslCaps() const { return reinterpret_cast<GrGLSLCaps*>(fShaderCaps.get()); }
 
 private:
+    enum VkVendor {
+        kQualcomm_VkVendor = 20803,
+    };
+
     void init(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface,
               VkPhysicalDevice device, uint32_t featureFlags, uint32_t extensionFlags);
     void initGrCaps(const VkPhysicalDeviceProperties&,
@@ -107,6 +115,10 @@
     // Tells of if we can pass in straight GLSL string into vkCreateShaderModule
     bool fCanUseGLSLForShaderModule;
 
+    // On Adreno vulkan, they do not respect the imageOffset parameter at least in
+    // copyImageToBuffer. This flag says that we must do the copy starting from the origin always.
+    bool fMustDoCopiesFromOrigin;
+
     typedef GrCaps INHERITED;
 };
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 9a8a6e4..ed165c5 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1702,27 +1702,41 @@
                           VK_PIPELINE_STAGE_TRANSFER_BIT,
                           false);
 
-    GrVkTransferBuffer* transferBuffer =
-        static_cast<GrVkTransferBuffer*>(this->createBuffer(rowBytes * height,
-                                                            kXferGpuToCpu_GrBufferType,
-                                                            kStream_GrAccessPattern));
-
+    size_t bpp = GrBytesPerPixel(config);
+    size_t tightRowBytes = bpp * width;
     bool flipY = kBottomLeft_GrSurfaceOrigin == surface->origin();
-    VkOffset3D offset = {
-        left,
-        flipY ? surface->height() - top - height : top,
-        0
-    };
 
-    // Copy the image to a buffer so we can map it to cpu memory
     VkBufferImageCopy region;
     memset(&region, 0, sizeof(VkBufferImageCopy));
+
+    bool copyFromOrigin = this->vkCaps().mustDoCopiesFromOrigin();
+    if (copyFromOrigin) {
+        region.imageOffset = { 0, 0, 0 };
+        region.imageExtent = { (uint32_t)(left + width),
+                               (uint32_t)(flipY ? surface->height() - top : top + height),
+                               1
+                             };
+    } else {
+        VkOffset3D offset = {
+            left,
+            flipY ? surface->height() - top - height : top,
+            0
+        };
+        region.imageOffset = offset;
+        region.imageExtent = { (uint32_t)width, (uint32_t)height, 1 };
+    }
+
+    size_t transBufferRowBytes = bpp * region.imageExtent.width;
+    GrVkTransferBuffer* transferBuffer =
+            static_cast<GrVkTransferBuffer*>(this->createBuffer(transBufferRowBytes * height,
+                                                                kXferGpuToCpu_GrBufferType,
+                                                                kStream_GrAccessPattern));
+
+    // Copy the image to a buffer so we can map it to cpu memory
     region.bufferOffset = transferBuffer->offset();
     region.bufferRowLength = 0; // Forces RowLength to be width. We handle the rowBytes below.
     region.bufferImageHeight = 0; // Forces height to be tightly packed. Only useful for 3d images.
     region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
-    region.imageOffset = offset;
-    region.imageExtent = { (uint32_t)width, (uint32_t)height, 1 };
 
     fCurrentCmdBuffer->copyImageToBuffer(this,
                                          image,
@@ -1745,26 +1759,30 @@
 
     void* mappedMemory = transferBuffer->map();
 
-    size_t tightRowBytes = GrBytesPerPixel(config) * width;
+    if (copyFromOrigin) {
+        uint32_t skipRows = region.imageExtent.height - height;
+        mappedMemory = (char*)mappedMemory + transBufferRowBytes * skipRows + bpp * left;
+    }
+
     if (flipY) {
         const char* srcRow = reinterpret_cast<const char*>(mappedMemory);
         char* dstRow = reinterpret_cast<char*>(buffer)+(height - 1) * rowBytes;
         for (int y = 0; y < height; y++) {
             memcpy(dstRow, srcRow, tightRowBytes);
-            srcRow += tightRowBytes;
+            srcRow += transBufferRowBytes;
             dstRow -= rowBytes;
         }
     } else {
-        if (tightRowBytes == rowBytes) {
+        if (transBufferRowBytes == rowBytes) {
             memcpy(buffer, mappedMemory, rowBytes*height);
         } else {
-            SkRectMemcpy(buffer, rowBytes, mappedMemory, tightRowBytes, tightRowBytes, height);
+            SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, tightRowBytes,
+                         height);
         }
     }
 
     transferBuffer->unmap();
     transferBuffer->unref();
-
     return true;
 }