GrSurface -> GrGpuBuffer transfer improvements:

GrCaps now only provides the offset alignment requirement. The row bytes
is always bpp * width.

GrGpu::transferPixelsFrom now just returns bool since row bytes value is
implicit. It now asserts offset is aligned with GrCap's provided value
in base class.

Implement caps for GL.

Bug: skia:8962
Change-Id: I3299b62efe9fe05bfe02f2a6a4c2704f647d0f8a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206686
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/include/gpu/gl/GrGLTypes.h b/include/gpu/gl/GrGLTypes.h
index f66a092..88d563a 100644
--- a/include/gpu/gl/GrGLTypes.h
+++ b/include/gpu/gl/GrGLTypes.h
@@ -68,6 +68,7 @@
 typedef unsigned short GrGLushort;
 typedef unsigned int GrGLuint;
 typedef uint64_t GrGLuint64;
+typedef unsigned short int GrGLhalf;
 typedef float GrGLfloat;
 typedef float GrGLclampf;
 typedef double GrGLdouble;
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 4624171..c4e24f0 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -277,13 +277,30 @@
     return surface->readOnly() ? false : this->onSurfaceSupportsWritePixels(surface);
 }
 
-bool GrCaps::transferFromBufferRequirements(GrColorType bufferColorType, int width,
-                                            size_t* rowBytes, size_t* offsetAlignment) const {
+size_t GrCaps::transferFromOffsetAlignment(GrColorType bufferColorType) const {
     if (!this->transferBufferSupport()) {
-        return false;
+        return 0;
     }
-    return this->onTransferFromBufferRequirements(bufferColorType, width, rowBytes,
-                                                  offsetAlignment);
+    size_t result = this->onTransferFromOffsetAlignment(bufferColorType);
+    if (!result) {
+        return 0;
+    }
+    // It's very convenient to access 1 byte-per-channel 32 bitvRGB/RGBA color types as uint32_t.
+    // Make those aligned reads out of the buffer even if the underlying API doesn't require it.
+    auto componentFlags = GrColorTypeComponentFlags(bufferColorType);
+    if ((componentFlags == kRGBA_SkColorTypeComponentFlags ||
+         componentFlags == kRGB_SkColorTypeComponentFlags) &&
+        GrColorTypeBytesPerPixel(bufferColorType) == 4) {
+        switch (result & 0b11) {
+            // offset alignment already a multiple of 4
+            case 0:     return result;
+            // offset alignment is a multiple of 2 but not 4.
+            case 2:     return 2 * result;
+            // offset alignment is not a multiple of 2.
+            default:    return 4 * result;
+        }
+    }
+    return result;
 }
 
 bool GrCaps::canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
@@ -329,12 +346,3 @@
 GrBackendFormat GrCaps::getBackendFormatFromColorType(SkColorType ct) const {
     return this->getBackendFormatFromGrColorType(SkColorTypeToGrColorType(ct), GrSRGBEncoded::kNo);
 }
-
-bool GrCaps::onTransferFromBufferRequirements(GrColorType bufferColorType, int width,
-                                              size_t* rowBytes, size_t* offsetAlignment) const {
-    // TODO(bsalomon): Provide backend-specific overrides of this the return the true requirements.
-    // Currently assuming tight row bytes rounded up to a multiple of 4 and 4 byte offset alignment.
-    *rowBytes = GrSizeAlignUp(GrColorTypeBytesPerPixel(bufferColorType) * width, 4);
-    *offsetAlignment = 4;
-    return true;
-}
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 4045e5f..2977ccd 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -224,21 +224,16 @@
     bool transferBufferSupport() const { return fTransferBufferSupport; }
 
     /**
-     * Gets the requirements to use GrGpu::transferPixelsFrom for a given GrColorType. To check
-     * whether a pixels as GrColorType can be read for a given surface see
-     * supportedReadPixelsColorType() and surfaceSupportsReadPixels().
+     * Gets the alignment requirement for the buffer offset used with GrGpu::transferPixelsFrom for
+     * a given GrColorType. To check whether a pixels as GrColorType can be read for a given surface
+     * see supportedReadPixelsColorType() and surfaceSupportsReadPixels().
      *
      * @param bufferColorType The color type of the pixel data that will be stored in the transfer
      *                        buffer.
-     * @param width  The number of color values per row that will be stored in the buffer.
-     * @param rowBytes The number of bytes per row that will be used in the transfer buffer.
-     * @param alignment The required alignment of the offset into the transfer buffer passed
-     *                         to GrGpu::transferPixelsFrom.
-     * @return true if transferPixelFrom is supported, false otherwise. If false then rowBytes and
-     * alignment are not updated.
+     * @return minimum required alignment for the buffer offset or zero if reading to the color type
+     *         is not supported.
      */
-    bool transferFromBufferRequirements(GrColorType bufferColorType, int width, size_t* rowBytes,
-                                        size_t* offsetAlignment) const;
+    size_t transferFromOffsetAlignment(GrColorType bufferColorType) const;
 
     bool suppressPrints() const { return fSuppressPrints; }
 
@@ -426,8 +421,7 @@
     virtual bool onSurfaceSupportsWritePixels(const GrSurface*) const = 0;
     virtual bool onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                                   const SkIRect& srcRect, const SkIPoint& dstPoint) const = 0;
-    virtual bool onTransferFromBufferRequirements(GrColorType bufferColorType, int width,
-                                                  size_t* rowBytes, size_t* offsetAlignment) const;
+    virtual size_t onTransferFromOffsetAlignment(GrColorType bufferColorType) const = 0;
 
     // Backends should implement this if they have any extra requirements for use of window
     // rectangles for a specific GrBackendRenderTarget outside of basic support.
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index df909b6..810457a 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -330,11 +330,13 @@
     return false;
 }
 
-size_t GrGpu::transferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
-                                 GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
-                                 size_t offset) {
+bool GrGpu::transferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
+                               GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
+                               size_t offset) {
     SkASSERT(surface);
     SkASSERT(transferBuffer);
+    SkASSERT(this->caps()->transferFromOffsetAlignment(bufferColorType));
+    SkASSERT(offset % this->caps()->transferFromOffsetAlignment(bufferColorType) == 0);
 
     // We require that the write region is contained in the texture
     SkIRect subRect = SkIRect::MakeXYWH(left, top, width, height);
@@ -344,12 +346,12 @@
     }
 
     this->handleDirtyContext();
-    if (auto result = this->onTransferPixelsFrom(surface, left, top, width, height, bufferColorType,
-                                                 transferBuffer, offset)) {
+    if (this->onTransferPixelsFrom(surface, left, top, width, height, bufferColorType,
+                                   transferBuffer, offset)) {
         fStats.incTransfersFromSurface();
-        return result;
+        return true;
     }
-    return 0;
+    return false;
 }
 
 bool GrGpu::regenerateMipMapLevels(GrTexture* texture) {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 6b7ab84..a80fa12 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -227,10 +227,14 @@
 
     /**
      * Reads the pixels from a rectangle of a surface into a buffer. Use
-     * GrCaps::transferFromBufferRequirements to determine the requirements for the buffer size
-     * and offset alignment. If the surface is a MIP mapped texture, the base level is read.
+     * GrCaps::transferFromOffsetAlignment to determine the requirements for the buffer offset
+     * alignment. If the surface is a MIP mapped texture, the base level is read.
      *
-     * Returns the number of bytes per row in the buffer or zero on failure.
+     * If successful the row bytes in the buffer is always:
+     *   GrColorTypeBytesPerPixel(bufferColorType) * width
+     *
+     * Asserts that the caller has passed a properly aligned offset and that the buffer is
+     * large enough to hold the result
      *
      * @param surface          The surface to read from.
      * @param left             left edge of the rectangle to read (inclusive)
@@ -241,9 +245,9 @@
      * @param transferBuffer   GrBuffer to write pixels to (type must be "kXferGpuToCpu")
      * @param offset           offset from the start of the buffer
      */
-    size_t transferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
-                              GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
-                              size_t offset);
+    bool transferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
+                            GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
+                            size_t offset);
 
     // After the client interacts directly with the 3D context state the GrGpu
     // must resync its internal state and assumptions about 3D context state.
@@ -532,9 +536,9 @@
                                     GrColorType colorType, GrGpuBuffer* transferBuffer,
                                     size_t offset, size_t rowBytes) = 0;
     // overridden by backend-specific derived class to perform the surface transfer
-    virtual size_t onTransferPixelsFrom(GrSurface*, int left, int top, int width, int height,
-                                        GrColorType colorType, GrGpuBuffer* transferBuffer,
-                                        size_t offset) = 0;
+    virtual bool onTransferPixelsFrom(GrSurface*, int left, int top, int width, int height,
+                                      GrColorType colorType, GrGpuBuffer* transferBuffer,
+                                      size_t offset) = 0;
 
     // overridden by backend-specific derived class to perform the resolve
     virtual void onResolveRenderTarget(GrRenderTarget* target) = 0;
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 7e6f942..8e9952a 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -3259,3 +3259,44 @@
     return GrBackendFormat::MakeGL(this->configSizedInternalFormat(config), GR_GL_TEXTURE_2D);
 }
 
+size_t GrGLCaps::onTransferFromOffsetAlignment(GrColorType bufferColorType) const {
+    // This implementation is highly coupled with decisions made in initConfigTable and GrGLGpu's
+    // read pixels implementation and may not be correct for all types. TODO(bsalomon): This will be
+    // cleaned up/unified as part of removing GrPixelConfig.
+    // There are known problems with 24 vs 32 bit BPP with this color type. Just fail for now.
+    if (GrColorType::kRGB_888x == bufferColorType) {
+        return 0;
+    }
+    auto config = GrColorTypeToPixelConfig(bufferColorType, GrSRGBEncoded::kNo);
+    // This switch is derived from a table titled "Pixel data type parameter values and the
+    // corresponding GL data types" in the OpenGL spec (Table 8.2 in OpenGL 4.5).
+    switch (fConfigTable[config].fFormats.fExternalType) {
+        case GR_GL_UNSIGNED_BYTE:                   return sizeof(GrGLubyte);
+        case GR_GL_BYTE:                            return sizeof(GrGLbyte);
+        case GR_GL_UNSIGNED_SHORT:                  return sizeof(GrGLushort);
+        case GR_GL_SHORT:                           return sizeof(GrGLshort);
+        case GR_GL_UNSIGNED_INT:                    return sizeof(GrGLuint);
+        case GR_GL_INT:                             return sizeof(GrGLint);
+        case GR_GL_HALF_FLOAT:                      return sizeof(GrGLhalf);
+        case GR_GL_FLOAT:                           return sizeof(GrGLfloat);
+        case GR_GL_UNSIGNED_SHORT_5_6_5:            return sizeof(GrGLushort);
+        case GR_GL_UNSIGNED_SHORT_4_4_4_4:          return sizeof(GrGLushort);
+        case GR_GL_UNSIGNED_SHORT_5_5_5_1:          return sizeof(GrGLushort);
+        case GR_GL_UNSIGNED_INT_2_10_10_10_REV:     return sizeof(GrGLuint);
+#if 0  // GL types we currently don't use. Here for future reference.
+        case GR_GL_UNSIGNED_BYTE_3_3_2:             return sizeof(GrGLubyte);
+        case GR_GL_UNSIGNED_BYTE_2_3_3_REV:         return sizeof(GrGLubyte);
+        case GR_GL_UNSIGNED_SHORT_5_6_5_REV:        return sizeof(GrGLushort);
+        case GR_GL_UNSIGNED_SHORT_4_4_4_4_REV:      return sizeof(GrGLushort);
+        case GR_GL_UNSIGNED_SHORT_1_5_5_5_REV:      return sizeof(GrGLushort);
+        case GR_GL_UNSIGNED_INT_8_8_8_8:            return sizeof(GrGLuint);
+        case GR_GL_UNSIGNED_INT_8_8_8_8_REV:        return sizeof(GrGLuint);
+        case GR_GL_UNSIGNED_INT_10_10_10_2:         return sizeof(GrGLuint);
+        case GR_GL_UNSIGNED_INT_24_8:               return sizeof(GrGLuint);
+        case GR_GL_UNSIGNED_INT_10F_11F_11F_REV:    return sizeof(GrGLuint);
+        case GR_GL_UNSIGNED_INT_5_9_9_9_REV:        return sizeof(GrGLuint);
+        case GR_GL_FLOAT_32_UNSIGNED_INT_24_8_REV:  return sizeof(GrGLuint);
+#endif
+        default:                                    return 0;
+    }
+}
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 4a97e84..a2c6ccf 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -465,6 +465,7 @@
     bool onSurfaceSupportsWritePixels(const GrSurface*) const override;
     bool onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                           const SkIRect& srcRect, const SkIPoint& dstPoint) const override;
+    size_t onTransferFromOffsetAlignment(GrColorType bufferColorType) const override;
 
     GrGLStandard fStandard;
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 8a847ed..c81fe6b 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -959,31 +959,14 @@
     return true;
 }
 
-size_t GrGLGpu::onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
-                                     GrColorType dstColorType, GrGpuBuffer* transferBuffer,
-                                     size_t offset) {
-    size_t offsetAlignment;
-    size_t rowBytes;
-    if (!this->glCaps().transferFromBufferRequirements(dstColorType, width, &rowBytes,
-                                                       &offsetAlignment)) {
-        return false;
-    }
-    if (offset % offsetAlignment) {
-        return false;
-    }
-    if (offset + rowBytes * height > transferBuffer->size()) {
-        return false;
-    }
-
+bool GrGLGpu::onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
+                                   GrColorType dstColorType, GrGpuBuffer* transferBuffer,
+                                   size_t offset) {
     auto* glBuffer = static_cast<GrGLBuffer*>(transferBuffer);
     this->bindBuffer(GrGpuBufferType::kXferGpuToCpu, glBuffer);
-
     auto offsetAsPtr = reinterpret_cast<void*>(offset);
-    if (this->readOrTransferPixelsFrom(surface, left, top, width, height, dstColorType, offsetAsPtr,
-                                       rowBytes)) {
-        return rowBytes;
-    }
-    return 0;
+    return this->readOrTransferPixelsFrom(surface, left, top, width, height, dstColorType,
+                                          offsetAsPtr, width);
 }
 
 /**
@@ -2363,7 +2346,7 @@
 
 bool GrGLGpu::readOrTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
                                        GrColorType dstColorType, void* offsetOrPtr,
-                                       size_t rowBytes) {
+                                       int rowWidthInPixels) {
     SkASSERT(surface);
 
     GrGLRenderTarget* renderTarget = static_cast<GrGLRenderTarget*>(surface->asRenderTarget());
@@ -2413,13 +2396,10 @@
     GrGLIRect readRect;
     readRect.setRelativeTo(glvp, left, top, width, height, kTopLeft_GrSurfaceOrigin);
 
-    int bytesPerPixel = GrBytesPerPixel(dstAsConfig);
-    size_t tightRowBytes = bytesPerPixel * width;
     // determine if GL can read using the passed rowBytes or if we need a scratch buffer.
-    if (rowBytes != tightRowBytes) {
+    if (rowWidthInPixels != width) {
         SkASSERT(this->glCaps().packRowLengthSupport());
-        SkASSERT(!(rowBytes % bytesPerPixel));
-        GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, static_cast<GrGLint>(rowBytes / bytesPerPixel)));
+        GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, rowWidthInPixels));
     }
     GL_CALL(PixelStorei(GR_GL_PACK_ALIGNMENT, config_alignment(dstAsConfig)));
 
@@ -2444,7 +2424,7 @@
                                         GR_GL_RENDERBUFFER, stencilAttachment->renderbufferID()));
     }
 
-    if (rowBytes != tightRowBytes) {
+    if (rowWidthInPixels != width) {
         SkASSERT(this->glCaps().packRowLengthSupport());
         GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0));
     }
@@ -2459,36 +2439,37 @@
                            GrColorType dstColorType, void* buffer, size_t rowBytes) {
     SkASSERT(surface);
 
-    // TODO: Avoid this conversion by making GrGLCaps work with color types.
-    auto dstAsConfig = GrColorTypeToPixelConfig(dstColorType, GrSRGBEncoded::kNo);
+    int bytesPerPixel = GrColorTypeBytesPerPixel(dstColorType);
 
-    int bytesPerPixel = GrBytesPerPixel(dstAsConfig);
-    size_t tightRowBytes = bytesPerPixel * width;
-
-    size_t readDstRowBytes = tightRowBytes;
+    // GL_PACK_ROW_LENGTH is in terms of pixels not bytes.
+    int rowPixelWidth;
     void* readDst = buffer;
 
     // determine if GL can read using the passed rowBytes or if we need a scratch buffer.
     SkAutoSMalloc<32 * sizeof(GrColor)> scratch;
-    if (rowBytes != tightRowBytes) {
+    if (!rowBytes || rowBytes == (size_t)(width * bytesPerPixel)) {
+        rowPixelWidth = width;
+    } else {
         if (this->glCaps().packRowLengthSupport() && !(rowBytes % bytesPerPixel)) {
-            readDstRowBytes = rowBytes;
+            rowPixelWidth = rowBytes / bytesPerPixel;
         } else {
-            scratch.reset(tightRowBytes * height);
+            scratch.reset(width * bytesPerPixel * height);
             readDst = scratch.get();
+            rowPixelWidth = width;
         }
     }
     if (!this->readOrTransferPixelsFrom(surface, left, top, width, height, dstColorType, readDst,
-                                        readDstRowBytes)) {
+                                        rowPixelWidth)) {
         return false;
     }
 
     if (readDst != buffer) {
         SkASSERT(readDst != buffer);
-        SkASSERT(rowBytes != tightRowBytes);
+        SkASSERT(rowBytes != (size_t)(rowPixelWidth * bytesPerPixel));
         const char* src = reinterpret_cast<const char*>(readDst);
         char* dst = reinterpret_cast<char*>(buffer);
-        SkRectMemcpy(dst, rowBytes, src, readDstRowBytes, tightRowBytes, height);
+        SkRectMemcpy(dst, rowBytes, src, rowPixelWidth * bytesPerPixel, width * bytesPerPixel,
+                     height);
     }
     return true;
 }
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 86504d8..57505b4 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -239,10 +239,10 @@
 
     bool onTransferPixelsTo(GrTexture*, int left, int top, int width, int height, GrColorType,
                             GrGpuBuffer* transferBuffer, size_t offset, size_t rowBytes) override;
-    size_t onTransferPixelsFrom(GrSurface*, int left, int top, int width, int height, GrColorType,
-                                GrGpuBuffer* transferBuffer, size_t offset) override;
+    bool onTransferPixelsFrom(GrSurface*, int left, int top, int width, int height, GrColorType,
+                              GrGpuBuffer* transferBuffer, size_t offset) override;
     bool readOrTransferPixelsFrom(GrSurface*, int left, int top, int width, int height, GrColorType,
-                                  void* offsetOrPtr, size_t rowBytes);
+                                  void* offsetOrPtr, int rowWidthInPixels);
 
     // Before calling any variation of TexImage, TexSubImage, etc..., call this to ensure that the
     // PIXEL_UNPACK_BUFFER is unbound.
diff --git a/src/gpu/mock/GrMockCaps.h b/src/gpu/mock/GrMockCaps.h
index 4ce5f89..1a0a559 100644
--- a/src/gpu/mock/GrMockCaps.h
+++ b/src/gpu/mock/GrMockCaps.h
@@ -112,6 +112,10 @@
                           const SkIRect& srcRect, const SkIPoint& dstPoint) const override {
         return true;
     }
+    size_t onTransferFromOffsetAlignment(GrColorType bufferColorType) const override {
+        // arbitrary
+        return GrSizeAlignUp(GrColorTypeBytesPerPixel(bufferColorType), 4);
+    }
 
     static const int kMaxSampleCnt = 16;
 
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index c1e0065..1e99683 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -96,8 +96,8 @@
                             size_t rowBytes) override {
         return true;
     }
-    size_t onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
-                                GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override {
+    bool onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
+                              GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override {
         return true;
     }
     bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src,
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index 6ff4694..b80c349 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -85,6 +85,10 @@
     bool onSurfaceSupportsWritePixels(const GrSurface*) const override;
     bool onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                           const SkIRect& srcRect, const SkIPoint& dstPoint) const override;
+    size_t onTransferFromOffsetAlignment(GrColorType bufferColorType) const override {
+        // Transfer buffers not yet supported.
+        return 0;
+    }
 
     struct ConfigInfo {
         ConfigInfo() : fFlags(0) {}
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 5e5c0a9..895866d 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -169,12 +169,10 @@
         // TODO: not sure this is worth the work since nobody uses it
         return false;
     }
-    size_t onTransferPixelsFrom(GrSurface*,
-                                int left, int top, int width, int height,
-                                GrColorType, GrGpuBuffer*,
-                                size_t offset) override {
+    bool onTransferPixelsFrom(GrSurface*, int left, int top, int width, int height, GrColorType,
+                              GrGpuBuffer*, size_t offset) override {
         // TODO: Will need to implement this to support async read backs.
-        return 0;
+        return false;
     }
 
     bool onRegenerateMipMapLevels(GrTexture*) override;
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 59ac518..a207938 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -951,8 +951,7 @@
     return GrBackendFormat::MakeVk(format);
 }
 
-bool GrVkCaps::onTransferFromBufferRequirements(GrColorType bufferColorType, int width,
-                                                size_t* rowBytes, size_t* offsetAlignment) const {
+size_t GrVkCaps::onTransferFromOffsetAlignment(GrColorType bufferColorType) 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.
@@ -962,19 +961,11 @@
     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;
+        // bpp is already a multiple of 4.
+        case 0:     return bpp;
+        // bpp is a multiple of 2 but not 4.
+        case 2:     return 2 * bpp;
+        // bpp is not a multiple of 2.
+        default:    return 4 * bpp;
     }
-    // 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 34abe70..1d3929c 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -199,8 +199,7 @@
     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;
+    size_t onTransferFromOffsetAlignment(GrColorType bufferColorType) const override;
 
     struct ConfigInfo {
         ConfigInfo() : fOptimalFlags(0), fLinearFlags(0) {}
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 207e5a3..389a49a 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -493,23 +493,12 @@
     return true;
 }
 
-size_t GrVkGpu::onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
-                                     GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
-                                     size_t offset) {
+bool 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;
@@ -541,12 +530,7 @@
     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.bufferRowLength = width;
     region.bufferImageHeight = 0;
     region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
     region.imageOffset = { left, top, 0 };
@@ -572,7 +556,7 @@
     // The caller is responsible for syncing.
     this->submitCommandBuffer(kSkip_SyncQueue);
 
-    return rowBytes;
+    return true;
 }
 
 void GrVkGpu::resolveImage(GrSurface* dst, GrVkRenderTarget* src, const SkIRect& srcRect,
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 1f820b2..93f746e 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -213,8 +213,8 @@
 
     bool onTransferPixelsTo(GrTexture*, int left, int top, int width, int height, GrColorType,
                             GrGpuBuffer* transferBuffer, size_t offset, size_t rowBytes) override;
-    size_t onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
-                                GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override;
+    bool onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
+                              GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override;
 
     bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src,
                        GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
diff --git a/tests/TransferPixelsTest.cpp b/tests/TransferPixelsTest.cpp
index c55e4ef..771f9c7 100644
--- a/tests/TransferPixelsTest.cpp
+++ b/tests/TransferPixelsTest.cpp
@@ -212,21 +212,16 @@
     const int kPartialWidth = 10;
     const int kPartialHeight = 2;
 
-    size_t fullBufferRowBytes;
-    size_t partialBufferRowBytes;
-    size_t fullBufferOffsetAlignment;
-    size_t partialBufferOffsetAlignment;
-
-    SkAssertResult(context->priv().caps()->transferFromBufferRequirements(
-            colorType, kTextureWidth, &fullBufferRowBytes, &fullBufferOffsetAlignment));
-    SkAssertResult(context->priv().caps()->transferFromBufferRequirements(
-            colorType, kPartialWidth, &partialBufferRowBytes, &partialBufferOffsetAlignment));
+    size_t bpp = GrColorTypeBytesPerPixel(readColorType);
+    size_t fullBufferRowBytes = kTextureWidth * bpp;
+    size_t partialBufferRowBytes = kPartialWidth * bpp;
+    size_t offsetAlignment = context->priv().caps()->transferFromOffsetAlignment(readColorType);
+    SkASSERT(offsetAlignment);
 
     size_t bufferSize = fullBufferRowBytes * kTextureHeight;
     // Arbitrary starting offset for the partial read.
-    size_t partialReadOffset = GrSizeAlignUp(11, partialBufferOffsetAlignment);
-    bufferSize =
-            SkTMax(bufferSize, partialReadOffset + partialBufferRowBytes * partialBufferRowBytes);
+    size_t partialReadOffset = GrSizeAlignUp(11, offsetAlignment);
+    bufferSize = SkTMax(bufferSize, partialReadOffset + partialBufferRowBytes * kPartialHeight);
 
     sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(
             bufferSize, GrGpuBufferType::kXferGpuToCpu, kDynamic_GrAccessPattern));
@@ -269,10 +264,10 @@
 
         //////////////////////////
         // transfer full data
-        auto bufferRowBytes = gpu->transferPixelsFrom(
-                tex.get(), 0, 0, kTextureWidth, kTextureHeight, readColorType, buffer.get(), 0);
-        REPORTER_ASSERT(reporter, bufferRowBytes = fullBufferRowBytes);
-        if (!bufferRowBytes) {
+        bool result = gpu->transferPixelsFrom(tex.get(), 0, 0, kTextureWidth, kTextureHeight,
+                                              readColorType, buffer.get(), 0);
+        if (!result) {
+            ERRORF(reporter, "transferPixelsFrom failed.");
             continue;
         }
         ++expectedTransferCnt;
@@ -291,17 +286,17 @@
                                                                  kTextureWidth,
                                                                  kTextureHeight,
                                                                  textureDataRowBytes,
-                                                                 bufferRowBytes,
+                                                                 fullBufferRowBytes,
                                                                  readColorType != colorType));
         buffer->unmap();
 
         ///////////////////////
         // Now test a partial read at an offset into the buffer.
-        bufferRowBytes = gpu->transferPixelsFrom(tex.get(), kPartialLeft, kPartialTop,
-                                                 kPartialWidth, kPartialHeight, readColorType,
-                                                 buffer.get(), partialReadOffset);
-        REPORTER_ASSERT(reporter, bufferRowBytes = partialBufferRowBytes);
-        if (!bufferRowBytes) {
+        result = gpu->transferPixelsFrom(tex.get(), kPartialLeft, kPartialTop, kPartialWidth,
+                                         kPartialHeight, readColorType, buffer.get(),
+                                         partialReadOffset);
+        if (!result) {
+            ERRORF(reporter, "transferPixelsFrom failed.");
             continue;
         }
         ++expectedTransferCnt;
@@ -325,7 +320,7 @@
                                                                  kPartialWidth,
                                                                  kPartialHeight,
                                                                  textureDataRowBytes,
-                                                                 bufferRowBytes,
+                                                                 partialBufferRowBytes,
                                                                  readColorType != colorType));
         buffer->unmap();
     }