Create a GrGpu::createBackendTexture choke point

This also makes createBackendTexture take SkPixmaps (instead of a raw pixels pointer)

Change-Id: I5d8a5a58fa7b15862fbf46a3c232cb6ea7f58976
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/243158
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrContextPriv.cpp b/src/gpu/GrContextPriv.cpp
index 511639a..db64798 100644
--- a/src/gpu/GrContextPriv.cpp
+++ b/src/gpu/GrContextPriv.cpp
@@ -393,6 +393,5 @@
     // TODO: propagate the array of pixmaps interface to GrGpu
     return gpu->createBackendTexture(baseWidth, baseHeight, backendFormat,
                                      GrMipMapped::kNo, // TODO: use real mipmap setting here
-                                     renderable, srcData[0].addr(), srcData[0].rowBytes(),
-                                     nullptr, isProtected);
+                                     renderable, srcData, numLevels, nullptr, isProtected);
 }
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index efc000f..0262554 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -12,6 +12,7 @@
 #include "include/gpu/GrBackendSurface.h"
 #include "include/gpu/GrContext.h"
 #include "src/core/SkMathPriv.h"
+#include "src/core/SkMipMap.h"
 #include "src/gpu/GrAuditTrail.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrContextPriv.h"
@@ -701,6 +702,73 @@
     keys->push_back(SkString("shader_compilations")); values->push_back(fShaderCompilations);
 }
 
-#endif
+#endif // GR_GPU_STATS
+#endif // GR_TEST_UTILS
 
-#endif
+
+bool GrGpu::MipMapsAreCorrect(int baseWidth, int baseHeight, GrMipMapped mipMapped,
+                              const SkPixmap srcData[], int numMipLevels) {
+    if (!srcData) {
+        return true;
+    }
+
+    if (baseWidth != srcData[0].width() || baseHeight != srcData[0].height()) {
+        return false;
+    }
+
+    if (mipMapped == GrMipMapped::kYes) {
+        if (numMipLevels != SkMipMap::ComputeLevelCount(baseWidth, baseHeight) + 1) {
+            return false;
+        }
+
+        SkColorType colorType = srcData[0].colorType();
+
+        int currentWidth = baseWidth;
+        int currentHeight = baseHeight;
+        for (int i = 1; i < numMipLevels; ++i) {
+            currentWidth = SkTMax(1, currentWidth / 2);
+            currentHeight = SkTMax(1, currentHeight / 2);
+
+            if (srcData[i].colorType() != colorType) { // all levels must have same colorType
+                return false;
+            }
+
+            if (srcData[i].width() != currentWidth || srcData[i].height() != currentHeight) {
+                return false;
+            }
+        }
+    } else if (numMipLevels != 1) {
+        return false;
+    }
+
+    return true;
+}
+
+GrBackendTexture GrGpu::createBackendTexture(int w, int h, const GrBackendFormat& format,
+                                             GrMipMapped mipMapped, GrRenderable renderable,
+                                             const SkPixmap srcData[], int numMipLevels,
+                                             const SkColor4f* color, GrProtected isProtected) {
+    const GrCaps* caps = this->caps();
+
+    if (!format.isValid()) {
+        return {};
+    }
+
+    SkASSERT(!caps->isFormatCompressed(format) || !srcData);      // There is no ETC1 SkColorType
+
+    if (w < 1 || w > caps->maxTextureSize() || h < 1 || h > caps->maxTextureSize()) {
+        return {};
+    }
+
+    // TODO: maybe just ignore the mipMapped parameter in this case
+    if (mipMapped == GrMipMapped::kYes && !this->caps()->mipMapSupport()) {
+        return {};
+    }
+
+    if (!MipMapsAreCorrect(w, h, mipMapped, srcData, numMipLevels)) {
+        return {};
+    }
+
+    return this->onCreateBackendTexture(w, h, format, mipMapped, renderable,
+                                        srcData, numMipLevels, color, isProtected);
+}
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 3ef00d5..3a4732b 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -451,15 +451,24 @@
     /**
      * Creates a texture directly in the backend API without wrapping it in a GrTexture.
      * Must be matched with a call to deleteBackendTexture().
-     * Right now, the color is ignored if pixel data is provided.
-     * In the future, if neither a color nor pixels are provided then the backend texture
-     * will be uninitialized.
+     *
+     * If srcData is provided it will be used to initialize the texture. If srcData is
+     * not provided but a color is then it is used to initialize the texture. If neither
+     * srcData nor a color is provided then the texture is left uninitialized.
+     *
+     * If srcData is provided and mipMapped is kYes then data for all the miplevels must be
+     * provided (or the method will fail). If only a color is provided and mipMapped is kYes
+     * then all the mip levels will be allocated and initialized to the color. If neither
+     * srcData nor a color is provided but mipMapped is kYes then the mip levels will be allocated
+     * but left uninitialized.
+     *
+     * Note: if more than one pixmap is provided (i.e., for mipmap levels) they must all share
+     * the same SkColorType.
      */
-    virtual GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
-                                                  GrMipMapped, GrRenderable,
-                                                  const void* pixels, size_t rowBytes,
-                                                  const SkColor4f* color,
-                                                  GrProtected isProtected) = 0;
+    GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
+                                          GrMipMapped, GrRenderable,
+                                          const SkPixmap srcData[], int numMipLevels,
+                                          const SkColor4f* color, GrProtected isProtected);
 
     /**
      * Frees a texture created by createBackendTexture(). If ownership of the backend
@@ -534,6 +543,9 @@
     virtual void storeVkPipelineCacheData() {}
 
 protected:
+    static bool MipMapsAreCorrect(int baseWidth, int baseHeight, GrMipMapped,
+                                  const SkPixmap srcData[], int numMipLevels);
+
     // Handles cases where a surface will be updated without a call to flushRenderTarget.
     void didWriteToSurface(GrSurface* surface, GrSurfaceOrigin origin, const SkIRect* bounds,
                            uint32_t mipLevels = 1) const;
@@ -544,6 +556,11 @@
     sk_sp<const GrCaps>              fCaps;
 
 private:
+    virtual GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
+                                                    GrMipMapped, GrRenderable,
+                                                    const SkPixmap srcData[], int numMipLevels,
+                                                    const SkColor4f* color, GrProtected) = 0;
+
     // called when the 3D context state is unknown. Subclass should emit any
     // assumed 3D context state and dirty any state cache.
     virtual void onResetContext(uint32_t resetBits) = 0;
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 89c1fca..8014d3b 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -3526,15 +3526,22 @@
     SkUNREACHABLE;
 }
 
-GrBackendTexture GrGLGpu::createBackendTexture(int w, int h,
-                                               const GrBackendFormat& format,
-                                               GrMipMapped mipMapped,
-                                               GrRenderable renderable,
-                                               const void* srcPixels, size_t rowBytes,
-                                               const SkColor4f* color,
-                                               GrProtected isProtected) {
+GrBackendTexture GrGLGpu::onCreateBackendTexture(int w, int h,
+                                                 const GrBackendFormat& format,
+                                                 GrMipMapped mipMapped,
+                                                 GrRenderable renderable,
+                                                 const SkPixmap srcData[], int numMipLevels,
+                                                 const SkColor4f* color,
+                                                 GrProtected isProtected) {
     this->handleDirtyContext();
 
+    SkDEBUGCODE(const GrCaps* caps = this->caps();)
+
+    // GrGpu::createBackendTexture should've ensured these conditions
+    SkASSERT(w >= 1 && w <= caps->maxTextureSize() && h >= 1 && h <= caps->maxTextureSize());
+    SkASSERT(GrGpu::MipMapsAreCorrect(w, h, mipMapped, srcData, numMipLevels));
+    SkASSERT(mipMapped == GrMipMapped::kNo || caps->mipMapSupport());
+
     GrGLFormat glFormat = format.asGLFormat();
     if (glFormat == GrGLFormat::kUnknown) {
         return GrBackendTexture();  // invalid
@@ -3548,21 +3555,13 @@
 
     auto textureColorType = GrPixelConfigToColorType(config);
 
+    // TODO: move the texturability check up to GrGpu::createBackendTexture and just assert here
     if (!this->caps()->isFormatTexturableAndUploadable(textureColorType, format)) {
         return GrBackendTexture();  // invalid
     }
 
-    if (w < 1 || w > this->caps()->maxTextureSize() ||
-        h < 1 || h > this->caps()->maxTextureSize()) {
-        return GrBackendTexture();  // invalid
-    }
-
     // Currently we don't support uploading pixel data when mipped.
-    if (srcPixels && GrMipMapped::kYes == mipMapped) {
-        return GrBackendTexture();  // invalid
-    }
-
-    if (mipMapped == GrMipMapped::kYes && !this->caps()->mipMapSupport()) {
+    if (srcData && GrMipMapped::kYes == mipMapped) {
         return GrBackendTexture();  // invalid
     }
 
@@ -3573,19 +3572,19 @@
     SkAutoMalloc pixelStorage;
     SkImage::CompressionType compressionType;
     if (GrGLFormatToCompressionType(glFormat, &compressionType)) {
-        // Compressed textures currently must be non-MIP mapped and have initial data.
+        SkASSERT(!srcData); // there is no ETC1 SkColorType
+        if (!color) {
+            return GrBackendTexture();
+        }
+
+        // Compressed textures currently must be non-MIP mapped and have an initial color.
         if (mipMapped == GrMipMapped::kYes) {
             return GrBackendTexture();
         }
-        if (!srcPixels) {
-            if (!color) {
-                return GrBackendTexture();
-            }
-            SkASSERT(0 == rowBytes);
-            size_t size = GrCompressedDataSize(compressionType, w, h);
-            srcPixels = pixelStorage.reset(size);
-            GrFillInCompressedData(compressionType, w, h, (char*)srcPixels, *color);
-        }
+        size_t size = GrCompressedDataSize(compressionType, w, h);
+        const void* srcPixels = pixelStorage.reset(size);
+        GrFillInCompressedData(compressionType, w, h, (char*)srcPixels, *color);
+
         info.fID = this->createCompressedTexture2D(
                 {w, h}, glFormat, compressionType, &initialState, srcPixels);
         if (!info.fID) {
@@ -3594,9 +3593,9 @@
         info.fFormat = GrGLFormatToEnum(glFormat);
         info.fTarget = GR_GL_TEXTURE_2D;
     } else {
-        if (srcPixels) {
+        if (srcData) {
             texels.append(1);
-            texels[0] = {srcPixels, rowBytes};
+            texels[0] = { srcData->addr(), srcData->rowBytes() };
         } else if (color) {
             int mipLevelCount = 1;
             if (GrMipMapped::kYes == mipMapped) {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 908ba5d..1510fdf 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -129,11 +129,6 @@
 
     GrStencilAttachment* createStencilAttachmentForRenderTarget(
             const GrRenderTarget* rt, int width, int height, int numStencilSamples) override;
-    GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
-                                          GrMipMapped, GrRenderable,
-                                          const void* pixels, size_t rowBytes,
-                                          const SkColor4f* color,
-                                          GrProtected isProtected) override;
     void deleteBackendTexture(const GrBackendTexture&) override;
 
     bool precompileShader(const SkData& key, const SkData& data) override {
@@ -181,6 +176,11 @@
     GrGLGpu(std::unique_ptr<GrGLContext>, GrContext*);
 
     // GrGpu overrides
+    GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
+                                            GrMipMapped, GrRenderable,
+                                            const SkPixmap srcData[], int numMipLevels,
+                                            const SkColor4f* color, GrProtected) override;
+
     void onResetContext(uint32_t resetBits) override;
 
     void onResetTextureBindings() override;
diff --git a/src/gpu/mock/GrMockGpu.cpp b/src/gpu/mock/GrMockGpu.cpp
index bc91c75..da36226 100644
--- a/src/gpu/mock/GrMockGpu.cpp
+++ b/src/gpu/mock/GrMockGpu.cpp
@@ -258,14 +258,14 @@
     return new GrMockStencilAttachment(this, width, height, kBits, rt->numSamples());
 }
 
-GrBackendTexture GrMockGpu::createBackendTexture(int w, int h,
-                                                 const GrBackendFormat& format,
-                                                 GrMipMapped mipMapped,
-                                                 GrRenderable /* renderable */,
-                                                 const void* /* pixels */,
-                                                 size_t /* rowBytes */,
-                                                 const SkColor4f* /* color */,
-                                                 GrProtected /* isProtected */) {
+GrBackendTexture GrMockGpu::onCreateBackendTexture(int w, int h,
+                                                   const GrBackendFormat& format,
+                                                   GrMipMapped mipMapped,
+                                                   GrRenderable /* renderable */,
+                                                   const SkPixmap /*srcData*/[],
+                                                   int /*numMipLevels*/,
+                                                   const SkColor4f* /* color */,
+                                                   GrProtected /* isProtected */) {
     auto colorType = format.asMockColorType();
     if (!this->caps()->isFormatTexturable(format)) {
         return GrBackendTexture();  // invalid
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index 7fec665..5731069 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -129,10 +129,10 @@
 
     GrStencilAttachment* createStencilAttachmentForRenderTarget(
             const GrRenderTarget*, int width, int height, int numStencilSamples) override;
-    GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
-                                          GrMipMapped, GrRenderable,
-                                          const void* pixels, size_t rowBytes,
-                                          const SkColor4f* color, GrProtected isProtected) override;
+    GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
+                                            GrMipMapped, GrRenderable,
+                                            const SkPixmap srcData[], int numMipLevels,
+                                            const SkColor4f* color, GrProtected) override;
     void deleteBackendTexture(const GrBackendTexture&) override;
 
 #if GR_TEST_UTILS
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index c574d4d..48ac005 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -56,12 +56,6 @@
     // command buffer to finish before creating a new buffer and returning.
     void submitCommandBuffer(SyncQueue sync);
 
-    GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
-                                          GrMipMapped, GrRenderable,
-                                          const void* pixels, size_t rowBytes,
-                                          const SkColor4f* color,
-                                          GrProtected isProtected) override;
-
     void deleteBackendTexture(const GrBackendTexture&) override;
 
 #if GR_TEST_UTILS
@@ -133,6 +127,11 @@
 
     void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
 
+    GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
+                                            GrMipMapped, GrRenderable,
+                                            const SkPixmap srcData[], int numMipLevels,
+                                            const SkColor4f* color, GrProtected) override;
+
     sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc& desc,
                                      const GrBackendFormat& format,
                                      GrRenderable,
@@ -224,9 +223,9 @@
 
     bool createMtlTextureForBackendSurface(MTLPixelFormat,
                                            int w, int h, bool texturable,
-                                           bool renderable, GrMipMapped mipMapped,
-                                           const void* srcData, size_t srcRowBytes,
-                                           const SkColor4f* color, GrMtlTextureInfo* info);
+                                           bool renderable, GrMipMapped,
+                                           const SkPixmap srcData[], int numMipLevels,
+                                           const SkColor4f* color, GrMtlTextureInfo*);
 
     sk_sp<GrMtlCaps> fMtlCaps;
 
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 553f2f0..fe91d8c 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -715,12 +715,12 @@
 bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
                                                  int w, int h, bool texturable,
                                                  bool renderable, GrMipMapped mipMapped,
-                                                 const void* srcData, size_t srcRowBytes,
+                                                 const SkPixmap srcData[], int numMipLevels,
                                                  const SkColor4f* color, GrMtlTextureInfo* info) {
     SkASSERT(texturable || renderable);
     if (!texturable) {
         SkASSERT(GrMipMapped::kNo == mipMapped);
-        SkASSERT(!srcData);
+        SkASSERT(!srcData && !numMipLevels);
     }
 
     if (texturable && !fMtlCaps->isFormatTexturable(format)) {
@@ -738,7 +738,10 @@
     }
 
     int mipLevelCount = 1;
-    if (GrMipMapped::kYes == mipMapped) {
+    if (srcData) {
+        SkASSERT(numMipLevels > 0);
+        mipLevelCount = numMipLevels;
+    } else if (GrMipMapped::kYes == mipMapped) {
         mipLevelCount = SkMipMap::ComputeLevelCount(w, h) + 1;
     }
 
@@ -797,21 +800,16 @@
 
     // Fill buffer with data
     if (srcData) {
-        if (isCompressed) {
-            memcpy(buffer, srcData, combinedBufferSize);
-        } else {
-            const size_t trimRowBytes = w * bytesPerPixel;
+        SkASSERT(!isCompressed);   // There is no ETC1 SkColorType
 
-            // TODO: support mipmapping
-            SkASSERT(1 == mipLevelCount);
-            if (!srcRowBytes) {
-                srcRowBytes = trimRowBytes;
-            }
+        const size_t trimRowBytes = w * bytesPerPixel;
 
-            // copy data into the buffer, skipping the trailing bytes
-            const char* src = (const char*) srcData;
-            SkRectMemcpy(buffer, trimRowBytes, src, srcRowBytes, trimRowBytes, h);
-        }
+        // TODO: support mipmapping
+        SkASSERT(1 == mipLevelCount);
+
+        // copy data into the buffer, skipping the trailing bytes
+        const char* src = (const char*) srcData->addr();
+        SkRectMemcpy(buffer, trimRowBytes, src, srcData->rowBytes(), trimRowBytes, h);
     } else if (color) {
         if (isCompressed) {
             GrFillInCompressedData(compressionType, w, h, buffer, *color);
@@ -869,22 +867,26 @@
     return true;
 }
 
-GrBackendTexture GrMtlGpu::createBackendTexture(int w, int h,
-                                                const GrBackendFormat& format,
-                                                GrMipMapped mipMapped,
-                                                GrRenderable renderable,
-                                                const void* pixels, size_t rowBytes,
-                                                const SkColor4f* color, GrProtected isProtected) {
-    if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) {
-        return GrBackendTexture();
-    }
+GrBackendTexture GrMtlGpu::onCreateBackendTexture(int w, int h,
+                                                  const GrBackendFormat& format,
+                                                  GrMipMapped mipMapped,
+                                                  GrRenderable renderable,
+                                                  const SkPixmap srcData[], int numMipLevels,
+                                                  const SkColor4f* color,
+                                                  GrProtected isProtected) {
+    SkDEBUGCODE(const GrMtlCaps& caps = this->mtlCaps();)
+
+    // GrGpu::createBackendTexture should've ensured these conditions
+    SkASSERT(w >= 1 && w <= caps.maxTextureSize() && h >= 1 && h <= caps.maxTextureSize());
+    SkASSERT(GrGpu::MipMapsAreCorrect(w, h, mipMapped, srcData, numMipLevels));
+    SkASSERT(mipMapped == GrMipMapped::kNo || caps.mipMapSupport());
 
     const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format);
     GrMtlTextureInfo info;
     if (!this->createMtlTextureForBackendSurface(mtlFormat,
                                                  w, h, true,
                                                  GrRenderable::kYes == renderable, mipMapped,
-                                                 pixels, rowBytes, color, &info)) {
+                                                 srcData, numMipLevels, color, &info)) {
         return {};
     }
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 7b68e4d..1c1fa2f 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1531,13 +1531,13 @@
 
 bool GrVkGpu::createVkImageForBackendSurface(VkFormat vkFormat, int w, int h, bool texturable,
                                              bool renderable, GrMipMapped mipMapped,
-                                             const void* srcData, size_t srcRowBytes,
+                                             const SkPixmap srcData[], int numMipLevels,
                                              const SkColor4f* color, GrVkImageInfo* info,
                                              GrProtected isProtected) {
     SkASSERT(texturable || renderable);
     if (!texturable) {
         SkASSERT(GrMipMapped::kNo == mipMapped);
-        SkASSERT(!srcData);
+        SkASSERT(!srcData && !numMipLevels);
     }
 
     if (fProtectedContext != isProtected) {
@@ -1568,9 +1568,12 @@
     }
 
     // Figure out the number of mip levels.
-    uint32_t mipLevels = 1;
-    if (GrMipMapped::kYes == mipMapped) {
-        mipLevels = SkMipMap::ComputeLevelCount(w, h) + 1;
+    uint32_t mipLevelCount = 1;
+    if (srcData) {
+        SkASSERT(numMipLevels > 0);
+        mipLevelCount = numMipLevels;
+    } else if (GrMipMapped::kYes == mipMapped) {
+        mipLevelCount = SkMipMap::ComputeLevelCount(w, h) + 1;
     }
 
     GrVkImage::ImageDesc imageDesc;
@@ -1578,7 +1581,7 @@
     imageDesc.fFormat = vkFormat;
     imageDesc.fWidth = w;
     imageDesc.fHeight = h;
-    imageDesc.fLevels = mipLevels;
+    imageDesc.fLevels = mipLevelCount;
     imageDesc.fSamples = 1;
     imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
     imageDesc.fUsageFlags = usageFlags;
@@ -1627,25 +1630,32 @@
 
     // Set image layout and add barrier
     set_image_layout(this->vkInterface(), cmdBuffer, info, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                     mipLevels, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+                     mipLevelCount, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+    const void* srcPixels = nullptr;
+    size_t srcRowBytes = 0;
+    if (srcData) {
+        srcPixels = srcData->addr();
+        srcRowBytes = srcData->rowBytes();
+    }
 
     // TODO: Lift this to GrContext level.
     SkImage::CompressionType compressionType;
     bool isCompressed = GrVkFormatToCompressionType(vkFormat, &compressionType);
     std::unique_ptr<char[]> tempData;
-    if (isCompressed && !srcData) {
-        SkASSERT(color);
+    if (isCompressed) {
+        SkASSERT(!srcData && color);
         size_t size = GrCompressedDataSize(compressionType, w, h);
         tempData.reset(new char[size]);
         GrFillInCompressedData(compressionType, w, h, tempData.get(), *color);
-        srcData = tempData.get();
+        srcPixels = tempData.get();
     }
 
-    if (srcData) {
+    if (srcPixels) {
         size_t bytesPerPixel = GrVkBytesPerFormat(vkFormat);
         SkASSERT(w && h);
 
-        SkTArray<size_t> individualMipOffsets(mipLevels);
+        SkTArray<size_t> individualMipOffsets(mipLevelCount);
 
         SkImage::CompressionType compressionType;
         bool isCompressed = GrVkFormatToCompressionType(vkFormat, &compressionType);
@@ -1660,7 +1670,8 @@
             individualMipOffsets.push_back(0);
         } else {
             combinedBufferSize = GrComputeTightCombinedBufferSize(bytesPerPixel, w, h,
-                                                                  &individualMipOffsets, mipLevels);
+                                                                  &individualMipOffsets,
+                                                                  mipLevelCount);
         }
 
         VkBufferCreateInfo bufInfo;
@@ -1692,10 +1703,10 @@
 
         bool result;
         if (isCompressed) {
-            result = copy_compressed_src_data(this, bufferAlloc, compressionType, w, h, srcData);
+            result = copy_compressed_src_data(this, bufferAlloc, compressionType, w, h, srcPixels);
         } else {
-            SkASSERT(1 == mipLevels);
-            result = copy_src_data(this, bufferAlloc, vkFormat, w, h, srcData, srcRowBytes);
+            SkASSERT(1 == mipLevelCount);
+            result = copy_src_data(this, bufferAlloc, vkFormat, w, h, srcPixels, srcRowBytes);
         }
         if (!result) {
             GrVkImage::DestroyImageInfo(this, info);
@@ -1706,11 +1717,11 @@
             return false;
         }
 
-        SkTArray<VkBufferImageCopy> regions(mipLevels);
+        SkTArray<VkBufferImageCopy> regions(mipLevelCount);
 
         int currentWidth = w;
         int currentHeight = h;
-        for (uint32_t currentMipLevel = 0; currentMipLevel < mipLevels; currentMipLevel++) {
+        for (uint32_t currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
             // Submit copy command
             VkBufferImageCopy& region = regions.push_back();
             memset(&region, 0, sizeof(VkBufferImageCopy));
@@ -1740,18 +1751,18 @@
         range.baseArrayLayer = 0;
         range.baseMipLevel = 0;
         range.layerCount = 1;
-        range.levelCount = mipLevels;
+        range.levelCount = mipLevelCount;
         VK_CALL(CmdClearColorImage(cmdBuffer, info->fImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                    &vkColor, 1, &range));
     }
 
-    if (!srcData && renderable) {
+    if (!srcPixels && renderable) {
         SkASSERT(color);
 
         // Change image layout to color-attachment-optimal since if we use this texture as a
         // borrowed texture within Ganesh we are probably going to render to it
         set_image_layout(this->vkInterface(), cmdBuffer, info,
-                         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, mipLevels,
+                         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, mipLevelCount,
                          VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
                          VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
                          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
@@ -1759,7 +1770,7 @@
         // Change image layout to shader read since if we use this texture as a borrowed
         // texture within Ganesh we require that its layout be set to that
         set_image_layout(this->vkInterface(), cmdBuffer, info,
-                         VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, mipLevels,
+                         VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, mipLevelCount,
                          VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
     }
 
@@ -1825,20 +1836,22 @@
     return true;
 }
 
-GrBackendTexture GrVkGpu::createBackendTexture(int w, int h,
-                                               const GrBackendFormat& format,
-                                               GrMipMapped mipMapped,
-                                               GrRenderable renderable,
-                                               const void* srcData, size_t rowBytes,
-                                               const SkColor4f* color, GrProtected isProtected) {
-    const GrVkCaps& caps = this->vkCaps();
+GrBackendTexture GrVkGpu::onCreateBackendTexture(int w, int h,
+                                                 const GrBackendFormat& format,
+                                                 GrMipMapped mipMapped,
+                                                 GrRenderable renderable,
+                                                 const SkPixmap srcData[], int numMipLevels,
+                                                 const SkColor4f* color, GrProtected isProtected) {
     this->handleDirtyContext();
 
-    if (fProtectedContext != isProtected) {
-        return GrBackendTexture();
-    }
+    const GrVkCaps& caps = this->vkCaps();
 
-    if (w > caps.maxTextureSize() || h > caps.maxTextureSize()) {
+    // GrGpu::createBackendTexture should've ensured these conditions
+    SkASSERT(w >= 1 && w <= caps.maxTextureSize() && h >= 1 && h <= caps.maxTextureSize());
+    SkASSERT(GrGpu::MipMapsAreCorrect(w, h, mipMapped, srcData, numMipLevels));
+    SkASSERT(mipMapped == GrMipMapped::kNo || caps.mipMapSupport());
+
+    if (fProtectedContext != isProtected) {
         return GrBackendTexture();
     }
 
@@ -1848,6 +1861,7 @@
         return GrBackendTexture();
     }
 
+    // TODO: move the texturability check up to GrGpu::createBackendTexture and just assert here
     if (!caps.isVkFormatTexturable(vkFormat)) {
         SkDebugf("Config is not texturable\n");
         return GrBackendTexture();
@@ -1860,8 +1874,8 @@
 
     GrVkImageInfo info;
     if (!this->createVkImageForBackendSurface(vkFormat, w, h, true,
-                                              GrRenderable::kYes == renderable, mipMapped, srcData,
-                                              rowBytes, color, &info, isProtected)) {
+                                              GrRenderable::kYes == renderable, mipMapped,
+                                              srcData, numMipLevels, color, &info, isProtected)) {
         SkDebugf("Failed to create testing only image\n");
         return GrBackendTexture();
     }
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 4863445..35d1a14 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -79,11 +79,6 @@
 
     void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
 
-    GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
-                                          GrMipMapped, GrRenderable,
-                                          const void* pixels, size_t rowBytes,
-                                          const SkColor4f* color,
-                                          GrProtected isProtected) override;
     void deleteBackendTexture(const GrBackendTexture&) override;
 #if GR_TEST_UTILS
     bool isTestingOnlyBackendTexture(const GrBackendTexture&) const override;
@@ -190,6 +185,10 @@
 
     void destroyResources();
 
+    GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
+                                            GrMipMapped, GrRenderable,
+                                            const SkPixmap srcData[], int numMipLevels,
+                                            const SkColor4f* color, GrProtected) override;
     sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc&,
                                      const GrBackendFormat& format,
                                      GrRenderable,
@@ -275,9 +274,10 @@
                       const SkIPoint& dstPoint);
 
     bool createVkImageForBackendSurface(VkFormat vkFormat, int w, int h, bool texturable,
-                                        bool renderable, GrMipMapped mipMapped, const void* srcData,
-                                        size_t srcRowBytes, const SkColor4f* color,
-                                        GrVkImageInfo* info, GrProtected isProtected);
+                                        bool renderable, GrMipMapped mipMapped,
+                                        const SkPixmap srcData[], int numMipLevels,
+                                        const SkColor4f* color, GrVkImageInfo* info,
+                                        GrProtected isProtected);
 
     sk_sp<const GrVkInterface>                            fInterface;
     sk_sp<GrVkMemoryAllocator>                            fMemoryAllocator;