Revert "Remove code to push pixmaps to backend textures from GrGpu classes"

This reverts commit 45889d12696f8e0af2796a6bbd3938b6d2614eca.

Reason for revert: compressed texture assertion on D3D bot

Original change's description:
> Remove code to push pixmaps to backend textures from GrGpu classes
>
> Replace GrGpu::updateBackendTexture with narrower method that clears
> a backend texture.
>
> Creation of data for a solid color compressed texture is lifted up to
> GrDirectContext and goes through updateCompressedBackendTexture.
>
> Bug: skia:11786
> Change-Id: I1d617623df5e65686f30e57c361a64f78d77f7bd
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/392836
> Reviewed-by: Jim Van Verth <jvanverth@google.com>
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>

TBR=egdaniel@google.com,jvanverth@google.com,bsalomon@google.com

Change-Id: Ie58f52245c44c77f09742b0cb590cc97b97e6e37
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:11786
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/396097
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/include/gpu/GrBackendSurface.h b/include/gpu/GrBackendSurface.h
index b1413c8..99d2358 100644
--- a/include/gpu/GrBackendSurface.h
+++ b/include/gpu/GrBackendSurface.h
@@ -280,7 +280,6 @@
     SkISize dimensions() const { return {fWidth, fHeight}; }
     int width() const { return fWidth; }
     int height() const { return fHeight; }
-    GrMipmapped mipmapped() const { return fMipmapped; }
     bool hasMipmaps() const { return fMipmapped == GrMipmapped::kYes; }
     /** deprecated alias of hasMipmaps(). */
     bool hasMipMaps() const { return this->hasMipmaps(); }
diff --git a/src/gpu/GrDataUtils.cpp b/src/gpu/GrDataUtils.cpp
index e5a7adf..ce8f7bb 100644
--- a/src/gpu/GrDataUtils.cpp
+++ b/src/gpu/GrDataUtils.cpp
@@ -686,7 +686,7 @@
     return true;
 }
 
-bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, std::array<float, 4> color) {
+bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, SkColor4f color) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
 
     if (!dstInfo.isValid()) {
@@ -700,7 +700,7 @@
     }
     if (dstInfo.colorType() == GrColorType::kRGB_888) {
         // SkRasterPipeline doesn't handle writing to RGB_888. So we handle that specially here.
-        uint32_t rgba = SkColor4f{color[0], color[1], color[2], color[3]}.toBytes_RGBA();
+        uint32_t rgba = color.toBytes_RGBA();
         for (int y = 0; y < dstInfo.height(); ++y) {
             char* d = static_cast<char*>(dst) + y * dstRB;
             for (int x = 0; x < dstInfo.width(); ++x, d += 3) {
@@ -719,7 +719,7 @@
     char block[64];
     SkArenaAlloc alloc(block, sizeof(block), 1024);
     SkRasterPipeline_<256> pipeline;
-    pipeline.append_constant_color(&alloc, color.data());
+    pipeline.append_constant_color(&alloc, color);
     switch (lumMode) {
         case LumMode::kNone:
             break;
diff --git a/src/gpu/GrDataUtils.h b/src/gpu/GrDataUtils.h
index ff58246..dbc9661 100644
--- a/src/gpu/GrDataUtils.h
+++ b/src/gpu/GrDataUtils.h
@@ -39,7 +39,7 @@
 bool GrConvertPixels(const GrPixmap& dst, const GrCPixmap& src, bool flipY = false);
 
 /** Clears the dst image to a constant color. */
-bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, std::array<float, 4> color);
+bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, SkColor4f color);
 
 #if GR_TEST_UTILS
 /**
diff --git a/src/gpu/GrDirectContext.cpp b/src/gpu/GrDirectContext.cpp
index 4b0a867..69f60c5 100644
--- a/src/gpu/GrDirectContext.cpp
+++ b/src/gpu/GrDirectContext.cpp
@@ -10,9 +10,7 @@
 
 #include "include/core/SkTraceMemoryDump.h"
 #include "include/gpu/GrContextThreadSafeProxy.h"
-#include "src/core/SkAutoMalloc.h"
 #include "src/core/SkTaskGroup.h"
-#include "src/gpu/GrBackendUtils.h"
 #include "src/gpu/GrClientMappedBufferManager.h"
 #include "src/gpu/GrContextThreadSafeProxyPriv.h"
 #include "src/gpu/GrDirectContextPriv.h"
@@ -474,24 +472,26 @@
     return this->createBackendTexture(width, height, format, mipMapped, renderable, isProtected);
 }
 
-static GrBackendTexture create_and_clear_backend_texture(GrDirectContext* dContext,
-                                                         SkISize dimensions,
-                                                         const GrBackendFormat& backendFormat,
-                                                         GrMipmapped mipMapped,
-                                                         GrRenderable renderable,
-                                                         GrProtected isProtected,
-                                                         sk_sp<GrRefCntedCallback> finishedCallback,
-                                                         std::array<float, 4> color) {
+static GrBackendTexture create_and_update_backend_texture(
+        GrDirectContext* dContext,
+        SkISize dimensions,
+        const GrBackendFormat& backendFormat,
+        GrMipmapped mipMapped,
+        GrRenderable renderable,
+        GrProtected isProtected,
+        sk_sp<GrRefCntedCallback> finishedCallback,
+        const GrGpu::BackendTextureData* data) {
     GrGpu* gpu = dContext->priv().getGpu();
+    SkASSERT(data->type() == GrGpu::BackendTextureData::Type::kColor);
     GrBackendTexture beTex = gpu->createBackendTexture(dimensions, backendFormat, renderable,
                                                        mipMapped, isProtected);
     if (!beTex.isValid()) {
         return {};
     }
 
-    if (!dContext->priv().getGpu()->clearBackendTexture(beTex,
-                                                        std::move(finishedCallback),
-                                                        color)) {
+    if (!dContext->priv().getGpu()->updateBackendTexture(beTex,
+                                                         std::move(finishedCallback),
+                                                         data)) {
         dContext->deleteBackendTexture(beTex);
         return {};
     }
@@ -555,14 +555,10 @@
         return {};
     }
 
-    return create_and_clear_backend_texture(this,
-                                            {width, height},
-                                            backendFormat,
-                                            mipMapped,
-                                            renderable,
-                                            isProtected,
-                                            std::move(finishedCallback),
-                                            color.array());
+    GrGpu::BackendTextureData data(color);
+    return create_and_update_backend_texture(this, {width, height},
+                                             backendFormat, mipMapped, renderable, isProtected,
+                                             std::move(finishedCallback), &data);
 }
 
 GrBackendTexture GrDirectContext::createBackendTexture(int width, int height,
@@ -587,14 +583,10 @@
     GrColorType grColorType = SkColorTypeToGrColorType(skColorType);
     SkColor4f swizzledColor = this->caps()->getWriteSwizzle(format, grColorType).applyTo(color);
 
-    return create_and_clear_backend_texture(this,
-                                            {width, height},
-                                            format,
-                                            mipMapped,
-                                            renderable,
-                                            isProtected,
-                                            std::move(finishedCallback),
-                                            swizzledColor.array());
+    GrGpu::BackendTextureData data(swizzledColor);
+    return create_and_update_backend_texture(this, {width, height}, format,
+                                             mipMapped, renderable, isProtected,
+                                             std::move(finishedCallback), &data);
 }
 
 GrBackendTexture GrDirectContext::createBackendTexture(const SkPixmap srcData[],
@@ -655,7 +647,8 @@
         return false;
     }
 
-    return fGpu->clearBackendTexture(backendTexture, std::move(finishedCallback), color.array());
+    GrGpu::BackendTextureData data(color);
+    return fGpu->updateBackendTexture(backendTexture, std::move(finishedCallback), &data);
 }
 
 bool GrDirectContext::updateBackendTexture(const GrBackendTexture& backendTexture,
@@ -677,11 +670,9 @@
     }
 
     GrSwizzle swizzle = this->caps()->getWriteSwizzle(format, grColorType);
-    SkColor4f swizzledColor = swizzle.applyTo(color);
+    GrGpu::BackendTextureData data(swizzle.applyTo(color));
 
-    return fGpu->clearBackendTexture(backendTexture,
-                                     std::move(finishedCallback),
-                                     swizzledColor.array());
+    return fGpu->updateBackendTexture(backendTexture, std::move(finishedCallback), &data);
 }
 
 bool GrDirectContext::updateBackendTexture(const GrBackendTexture& backendTexture,
@@ -726,8 +717,7 @@
         GrMipmapped mipMapped,
         GrProtected isProtected,
         sk_sp<GrRefCntedCallback> finishedCallback,
-        const void* data,
-        size_t size) {
+        const GrGpu::BackendTextureData* data) {
     GrGpu* gpu = dContext->priv().getGpu();
 
     GrBackendTexture beTex = gpu->createCompressedBackendTexture(dimensions, backendFormat,
@@ -737,21 +727,20 @@
     }
 
     if (!dContext->priv().getGpu()->updateCompressedBackendTexture(
-                beTex, std::move(finishedCallback), data, size)) {
+                beTex, std::move(finishedCallback), data)) {
         dContext->deleteBackendTexture(beTex);
         return {};
     }
     return beTex;
 }
 
-GrBackendTexture GrDirectContext::createCompressedBackendTexture(
-        int width, int height,
-        const GrBackendFormat& backendFormat,
-        const SkColor4f& color,
-        GrMipmapped mipmapped,
-        GrProtected isProtected,
-        GrGpuFinishedProc finishedProc,
-        GrGpuFinishedContext finishedContext) {
+GrBackendTexture GrDirectContext::createCompressedBackendTexture(int width, int height,
+                                                             const GrBackendFormat& backendFormat,
+                                                             const SkColor4f& color,
+                                                             GrMipmapped mipMapped,
+                                                             GrProtected isProtected,
+                                                             GrGpuFinishedProc finishedProc,
+                                                             GrGpuFinishedContext finishedContext) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
     auto finishedCallback = GrRefCntedCallback::Make(finishedProc, finishedContext);
 
@@ -759,35 +748,19 @@
         return {};
     }
 
-    SkImage::CompressionType compression = GrBackendFormatToCompressionType(backendFormat);
-    if (compression == SkImage::CompressionType::kNone) {
-        return {};
-    }
-
-    size_t size = SkCompressedDataSize(compression,
-                                       {width, height},
-                                       nullptr,
-                                       mipmapped == GrMipmapped::kYes);
-    auto storage = std::make_unique<char[]>(size);
-    GrFillInCompressedData(compression, {width, height}, mipmapped, storage.get(), color);
-    return create_and_update_compressed_backend_texture(this,
-                                                        {width, height},
-                                                        backendFormat,
-                                                        mipmapped,
-                                                        isProtected,
-                                                        std::move(finishedCallback),
-                                                        storage.get(),
-                                                        size);
+    GrGpu::BackendTextureData data(color);
+    return create_and_update_compressed_backend_texture(this, {width, height},
+                                                        backendFormat, mipMapped, isProtected,
+                                                        std::move(finishedCallback), &data);
 }
 
-GrBackendTexture GrDirectContext::createCompressedBackendTexture(
-        int width, int height,
-        SkImage::CompressionType compression,
-        const SkColor4f& color,
-        GrMipmapped mipMapped,
-        GrProtected isProtected,
-        GrGpuFinishedProc finishedProc,
-        GrGpuFinishedContext finishedContext) {
+GrBackendTexture GrDirectContext::createCompressedBackendTexture(int width, int height,
+                                                             SkImage::CompressionType compression,
+                                                             const SkColor4f& color,
+                                                             GrMipmapped mipMapped,
+                                                             GrProtected isProtected,
+                                                             GrGpuFinishedProc finishedProc,
+                                                             GrGpuFinishedContext finishedContext) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
     GrBackendFormat format = this->compressedBackendFormat(compression);
     return this->createCompressedBackendTexture(width, height, format, color,
@@ -795,15 +768,14 @@
                                                 finishedContext);
 }
 
-GrBackendTexture GrDirectContext::createCompressedBackendTexture(
-        int width, int height,
-        const GrBackendFormat& backendFormat,
-        const void* compressedData,
-        size_t dataSize,
-        GrMipmapped mipMapped,
-        GrProtected isProtected,
-        GrGpuFinishedProc finishedProc,
-        GrGpuFinishedContext finishedContext) {
+GrBackendTexture GrDirectContext::createCompressedBackendTexture(int width, int height,
+                                                             const GrBackendFormat& backendFormat,
+                                                             const void* compressedData,
+                                                             size_t dataSize,
+                                                             GrMipmapped mipMapped,
+                                                             GrProtected isProtected,
+                                                             GrGpuFinishedProc finishedProc,
+                                                             GrGpuFinishedContext finishedContext) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
     auto finishedCallback = GrRefCntedCallback::Make(finishedProc, finishedContext);
 
@@ -811,24 +783,19 @@
         return {};
     }
 
-    return create_and_update_compressed_backend_texture(this,
-                                                        {width, height},
-                                                        backendFormat,
-                                                        mipMapped,
-                                                        isProtected,
-                                                        std::move(finishedCallback),
-                                                        compressedData,
-                                                        dataSize);
+    GrGpu::BackendTextureData data(compressedData, dataSize);
+    return create_and_update_compressed_backend_texture(this, {width, height},
+                                                        backendFormat, mipMapped, isProtected,
+                                                        std::move(finishedCallback), &data);
 }
 
-GrBackendTexture GrDirectContext::createCompressedBackendTexture(
-        int width, int height,
-        SkImage::CompressionType compression,
-        const void* data, size_t dataSize,
-        GrMipmapped mipMapped,
-        GrProtected isProtected,
-        GrGpuFinishedProc finishedProc,
-        GrGpuFinishedContext finishedContext) {
+GrBackendTexture GrDirectContext::createCompressedBackendTexture(int width, int height,
+                                                             SkImage::CompressionType compression,
+                                                             const void* data, size_t dataSize,
+                                                             GrMipmapped mipMapped,
+                                                             GrProtected isProtected,
+                                                             GrGpuFinishedProc finishedProc,
+                                                             GrGpuFinishedContext finishedContext) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
     GrBackendFormat format = this->compressedBackendFormat(compression);
     return this->createCompressedBackendTexture(width, height, format, data, dataSize, mipMapped,
@@ -845,25 +812,8 @@
         return false;
     }
 
-    SkImage::CompressionType compression =
-            GrBackendFormatToCompressionType(backendTexture.getBackendFormat());
-    if (compression == SkImage::CompressionType::kNone) {
-        return {};
-    }
-    size_t size = SkCompressedDataSize(compression,
-                                       backendTexture.dimensions(),
-                                       nullptr,
-                                       backendTexture.hasMipmaps());
-    SkAutoMalloc storage(size);
-    GrFillInCompressedData(compression,
-                           backendTexture.dimensions(),
-                           backendTexture.mipmapped(),
-                           static_cast<char*>(storage.get()),
-                           color);
-    return fGpu->updateCompressedBackendTexture(backendTexture,
-                                                std::move(finishedCallback),
-                                                storage.get(),
-                                                size);
+    GrGpu::BackendTextureData data(color);
+    return fGpu->updateCompressedBackendTexture(backendTexture, std::move(finishedCallback), &data);
 }
 
 bool GrDirectContext::updateCompressedBackendTexture(const GrBackendTexture& backendTexture,
@@ -881,10 +831,9 @@
         return false;
     }
 
-    return fGpu->updateCompressedBackendTexture(backendTexture,
-                                                std::move(finishedCallback),
-                                                compressedData,
-                                                dataSize);
+    GrGpu::BackendTextureData data(compressedData, dataSize);
+
+    return fGpu->updateCompressedBackendTexture(backendTexture, std::move(finishedCallback), &data);
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 3d94791..22a0172 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -755,16 +755,58 @@
 #endif // GR_GPU_STATS
 #endif // GR_TEST_UTILS
 
-bool GrGpu::CompressedDataIsCorrect(SkISize dimensions,
-                                    SkImage::CompressionType compressionType,
-                                    GrMipmapped mipMapped,
-                                    const void* data,
-                                    size_t length) {
-    size_t computedSize = SkCompressedDataSize(compressionType,
-                                               dimensions,
-                                               nullptr,
-                                               mipMapped == GrMipmapped::kYes);
-    return computedSize == length;
+bool GrGpu::MipMapsAreCorrect(SkISize dimensions,
+                              GrMipmapped mipmapped,
+                              const BackendTextureData* data) {
+    int numMipLevels = 1;
+    if (mipmapped == GrMipmapped::kYes) {
+        numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
+    }
+
+    if (!data || data->type() == BackendTextureData::Type::kColor) {
+        return true;
+    }
+
+    if (data->type() == BackendTextureData::Type::kCompressed) {
+        return false;  // This should be going through CompressedDataIsCorrect
+    }
+
+    SkASSERT(data->type() == BackendTextureData::Type::kPixmaps);
+
+    if (data->pixmap(0).dimensions() != dimensions) {
+        return false;
+    }
+
+    GrColorType colorType = data->pixmap(0).colorType();
+    for (int i = 1; i < numMipLevels; ++i) {
+        dimensions = {std::max(1, dimensions.width()/2), std::max(1, dimensions.height()/2)};
+        if (dimensions != data->pixmap(i).dimensions()) {
+            return false;
+        }
+        if (colorType != data->pixmap(i).colorType()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool GrGpu::CompressedDataIsCorrect(SkISize dimensions, SkImage::CompressionType compressionType,
+                                    GrMipmapped mipMapped, const BackendTextureData* data) {
+
+    if (!data || data->type() == BackendTextureData::Type::kColor) {
+        return true;
+    }
+
+    if (data->type() == BackendTextureData::Type::kPixmaps) {
+        return false;
+    }
+
+    SkASSERT(data->type() == BackendTextureData::Type::kCompressed);
+
+    size_t computedSize = SkCompressedDataSize(compressionType, dimensions,
+                                               nullptr, mipMapped == GrMipmapped::kYes);
+
+    return computedSize == data->compressedSize();
 }
 
 GrBackendTexture GrGpu::createBackendTexture(SkISize dimensions,
@@ -795,18 +837,33 @@
     return this->onCreateBackendTexture(dimensions, format, renderable, mipMapped, isProtected);
 }
 
-bool GrGpu::clearBackendTexture(const GrBackendTexture& backendTexture,
-                                sk_sp<GrRefCntedCallback> finishedCallback,
-                                std::array<float, 4> color) {
+bool GrGpu::updateBackendTexture(const GrBackendTexture& backendTexture,
+                                 sk_sp<GrRefCntedCallback> finishedCallback,
+                                 const BackendTextureData* data) {
+    SkASSERT(data);
+    const GrCaps* caps = this->caps();
+
     if (!backendTexture.isValid()) {
         return false;
     }
 
+    if (data->type() == BackendTextureData::Type::kPixmaps) {
+        auto ct = data->pixmap(0).colorType();
+        if (!caps->areColorTypeAndFormatCompatible(ct, backendTexture.getBackendFormat())) {
+            return false;
+        }
+    }
+
     if (backendTexture.hasMipmaps() && !this->caps()->mipmapSupport()) {
         return false;
     }
 
-    return this->onClearBackendTexture(backendTexture, std::move(finishedCallback), color);
+    GrMipmapped mipmapped = backendTexture.hasMipmaps() ? GrMipmapped::kYes : GrMipmapped::kNo;
+    if (!MipMapsAreCorrect(backendTexture.dimensions(), mipmapped, data)) {
+        return false;
+    }
+
+    return this->onUpdateBackendTexture(backendTexture, std::move(finishedCallback), data);
 }
 
 GrBackendTexture GrGpu::createCompressedBackendTexture(SkISize dimensions,
@@ -840,8 +897,7 @@
 
 bool GrGpu::updateCompressedBackendTexture(const GrBackendTexture& backendTexture,
                                            sk_sp<GrRefCntedCallback> finishedCallback,
-                                           const void* data,
-                                           size_t length) {
+                                           const BackendTextureData* data) {
     SkASSERT(data);
 
     if (!backendTexture.isValid()) {
@@ -862,16 +918,10 @@
 
     GrMipmapped mipMapped = backendTexture.hasMipmaps() ? GrMipmapped::kYes : GrMipmapped::kNo;
 
-    if (!CompressedDataIsCorrect(backendTexture.dimensions(),
-                                 compressionType,
-                                 mipMapped,
-                                 data,
-                                 length)) {
+    if (!CompressedDataIsCorrect(backendTexture.dimensions(), compressionType, mipMapped, data)) {
         return false;
     }
 
-    return this->onUpdateCompressedBackendTexture(backendTexture,
-                                                  std::move(finishedCallback),
-                                                  data,
-                                                  length);
+    return this->onUpdateCompressedBackendTexture(backendTexture, std::move(finishedCallback),
+                                                  data);
 }
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 498668c..3c2b09d 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -498,6 +498,58 @@
     Stats* stats() { return &fStats; }
     void dumpJSON(SkJSONWriter*) const;
 
+    /** Used to initialize a backend texture with either a constant color, pixmaps or
+     *  compressed data.
+     */
+    class BackendTextureData {
+    public:
+        enum class Type { kColor, kPixmaps, kCompressed };
+        BackendTextureData() = default;
+        BackendTextureData(const SkColor4f& color) : fType(Type::kColor), fColor(color) {}
+        BackendTextureData(const GrPixmap pixmaps[]) : fType(Type::kPixmaps), fPixmaps(pixmaps) {
+            SkASSERT(pixmaps);
+        }
+        BackendTextureData(const void* data, size_t size) : fType(Type::kCompressed) {
+            SkASSERT(data);
+            fCompressed.fData = data;
+            fCompressed.fSize = size;
+        }
+
+        Type type() const { return fType; }
+        SkColor4f color() const {
+            SkASSERT(this->type() == Type::kColor);
+            return fColor;
+        }
+
+        const GrPixmap& pixmap(int i) const {
+            SkASSERT(this->type() == Type::kPixmaps);
+            return fPixmaps[i];
+        }
+        const GrPixmap* pixmaps() const {
+            SkASSERT(this->type() == Type::kPixmaps);
+            return fPixmaps;
+        }
+
+        const void* compressedData() const {
+            SkASSERT(this->type() == Type::kCompressed);
+            return fCompressed.fData;
+        }
+        size_t compressedSize() const {
+            SkASSERT(this->type() == Type::kCompressed);
+            return fCompressed.fSize;
+        }
+
+    private:
+        Type fType = Type::kColor;
+        union {
+            SkColor4f fColor = {0, 0, 0, 0};
+            const GrPixmap* fPixmaps;
+            struct {
+                const void*  fData;
+                size_t       fSize;
+            } fCompressed;
+        };
+    };
 
     /**
      * Creates a texture directly in the backend API without wrapping it in a GrTexture.
@@ -519,9 +571,9 @@
                                           GrMipmapped,
                                           GrProtected);
 
-    bool clearBackendTexture(const GrBackendTexture&,
-                             sk_sp<GrRefCntedCallback> finishedCallback,
-                             std::array<float, 4> color);
+    bool updateBackendTexture(const GrBackendTexture&,
+                              sk_sp<GrRefCntedCallback> finishedCallback,
+                              const BackendTextureData*);
 
     /**
      * Same as the createBackendTexture case except compressed backend textures can
@@ -534,8 +586,7 @@
 
     bool updateCompressedBackendTexture(const GrBackendTexture&,
                                         sk_sp<GrRefCntedCallback> finishedCallback,
-                                        const void* data,
-                                        size_t length);
+                                        const BackendTextureData*);
 
     virtual bool setBackendTextureState(const GrBackendTexture&,
                                         const GrBackendSurfaceMutableState&,
@@ -635,11 +686,9 @@
     virtual void xferBarrier(GrRenderTarget*, GrXferBarrierType) = 0;
 
 protected:
-    static bool CompressedDataIsCorrect(SkISize dimensions,
-                                        SkImage::CompressionType,
-                                        GrMipmapped,
-                                        const void* data,
-                                        size_t length);
+    static bool MipMapsAreCorrect(SkISize dimensions, GrMipmapped, const BackendTextureData*);
+    static bool CompressedDataIsCorrect(SkISize dimensions, SkImage::CompressionType,
+                                        GrMipmapped, const BackendTextureData*);
 
     // Handles cases where a surface will be updated without a call to flushRenderTarget.
     void didWriteToSurface(GrSurface* surface, GrSurfaceOrigin origin, const SkIRect* bounds,
@@ -662,14 +711,13 @@
     virtual GrBackendTexture onCreateCompressedBackendTexture(
             SkISize dimensions, const GrBackendFormat&, GrMipmapped, GrProtected) = 0;
 
-    virtual bool onClearBackendTexture(const GrBackendTexture&,
-                                       sk_sp<GrRefCntedCallback> finishedCallback,
-                                       std::array<float, 4> color) = 0;
+    virtual bool onUpdateBackendTexture(const GrBackendTexture&,
+                                        sk_sp<GrRefCntedCallback> finishedCallback,
+                                        const BackendTextureData*) = 0;
 
     virtual bool onUpdateCompressedBackendTexture(const GrBackendTexture&,
                                                   sk_sp<GrRefCntedCallback> finishedCallback,
-                                                  const void* data,
-                                                  size_t length) = 0;
+                                                  const BackendTextureData*) = 0;
 
     // called when the 3D context state is unknown. Subclass should emit any
     // assumed 3D context state and dirty any state cache.
diff --git a/src/gpu/d3d/GrD3DGpu.cpp b/src/gpu/d3d/GrD3DGpu.cpp
index 1b62dcf..209c30e 100644
--- a/src/gpu/d3d/GrD3DGpu.cpp
+++ b/src/gpu/d3d/GrD3DGpu.cpp
@@ -995,12 +995,32 @@
     return GrBackendTexture(dimensions.width(), dimensions.height(), info);
 }
 
-static bool copy_color_data(const GrD3DCaps& caps,
-                            char* mapPtr,
-                            DXGI_FORMAT dxgiFormat,
-                            SkISize dimensions,
+static void copy_src_data(char* mapPtr,
+                          DXGI_FORMAT dxgiFormat,
+                          D3D12_PLACED_SUBRESOURCE_FOOTPRINT* placedFootprints,
+                          const GrPixmap srcData[],
+                          int numMipLevels) {
+    SkASSERT(srcData && numMipLevels);
+    SkASSERT(!GrDxgiFormatIsCompressed(dxgiFormat));
+    SkASSERT(mapPtr);
+
+    size_t bytesPerPixel = GrDxgiFormatBytesPerBlock(dxgiFormat);
+
+    for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
+        const size_t trimRowBytes = srcData[currentMipLevel].width() * bytesPerPixel;
+
+        // copy data into the buffer, skipping any trailing bytes
+        char* dst = mapPtr + placedFootprints[currentMipLevel].Offset;
+        SkRectMemcpy(dst, placedFootprints[currentMipLevel].Footprint.RowPitch,
+                     srcData[currentMipLevel].addr(), srcData[currentMipLevel].rowBytes(),
+                     trimRowBytes, srcData[currentMipLevel].height());
+    }
+}
+
+static bool copy_color_data(const GrD3DCaps& caps, char* mapPtr,
+                            DXGI_FORMAT dxgiFormat, SkISize dimensions,
                             D3D12_PLACED_SUBRESOURCE_FOOTPRINT* placedFootprints,
-                            std::array<float, 4> color) {
+                            SkColor4f color) {
     auto colorType = caps.getFormatColorType(dxgiFormat);
     if (colorType == GrColorType::kUnknown) {
         return false;
@@ -1013,12 +1033,11 @@
     return true;
 }
 
-bool GrD3DGpu::onClearBackendTexture(const GrBackendTexture& backendTexture,
-                                     sk_sp<GrRefCntedCallback> finishedCallback,
-                                     std::array<float, 4> color) {
+bool GrD3DGpu::onUpdateBackendTexture(const GrBackendTexture& backendTexture,
+                                      sk_sp<GrRefCntedCallback> finishedCallback,
+                                      const BackendTextureData* data) {
     GrD3DTextureResourceInfo info;
     SkAssertResult(backendTexture.getD3DTextureResourceInfo(&info));
-    SkASSERT(!GrDxgiFormatIsCompressed(info.fFormat));
 
     sk_sp<GrD3DResourceState> state = backendTexture.getGrD3DResourceState();
     SkASSERT(state);
@@ -1042,23 +1061,27 @@
     D3D12_RESOURCE_DESC desc = d3dResource->GetDesc();
     unsigned int mipLevelCount = 1;
     if (backendTexture.fMipmapped == GrMipmapped::kYes) {
-        mipLevelCount = SkMipmap::ComputeLevelCount(backendTexture.dimensions()) + 1;
+        mipLevelCount = SkMipmap::ComputeLevelCount(backendTexture.dimensions().width(),
+                                                    backendTexture.dimensions().height()) + 1;
     }
     SkASSERT(mipLevelCount == info.fLevelCount);
-    SkAutoSTMalloc<15, D3D12_PLACED_SUBRESOURCE_FOOTPRINT> placedFootprints(mipLevelCount);
-    UINT numRows;
-    UINT64 rowSizeInBytes;
+    SkAutoTMalloc<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> placedFootprints(mipLevelCount);
     UINT64 combinedBufferSize;
-    // We reuse the same top-level buffer area for all levels, hence passing 1 for level count.
-    fDevice->GetCopyableFootprints(&desc,
-                                   /* first resource  */ 0,
-                                   /* mip level count */ 1,
-                                   /* base offset     */ 0,
-                                   placedFootprints.get(),
-                                   &numRows,
-                                   &rowSizeInBytes,
-                                   &combinedBufferSize);
+    SkAutoTMalloc<UINT> numRows(mipLevelCount);
+    SkAutoTMalloc<UINT64> rowSizeInBytes(mipLevelCount);
+    fDevice->GetCopyableFootprints(&desc, 0, mipLevelCount, 0, placedFootprints.get(),
+                                   numRows.get(), rowSizeInBytes.get(), &combinedBufferSize);
     SkASSERT(combinedBufferSize);
+    if (data->type() == BackendTextureData::Type::kColor &&
+        !GrDxgiFormatIsCompressed(info.fFormat) && mipLevelCount > 1) {
+        // For a single uncompressed color, we reuse the same top-level buffer area for all levels.
+        combinedBufferSize =
+                placedFootprints[0].Footprint.RowPitch * placedFootprints[0].Footprint.Height;
+        for (unsigned int i = 1; i < mipLevelCount; ++i) {
+            placedFootprints[i].Offset = 0;
+            placedFootprints[i].Footprint.RowPitch = placedFootprints[0].Footprint.RowPitch;
+        }
+    }
 
     GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
             combinedBufferSize, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
@@ -1068,37 +1091,42 @@
 
     char* bufferData = (char*)slice.fOffsetMapPtr;
     SkASSERT(bufferData);
-    if (!copy_color_data(this->d3dCaps(),
-                         bufferData,
-                         info.fFormat,
-                         backendTexture.dimensions(),
-                         placedFootprints,
-                         color)) {
-        return false;
+
+    if (data->type() == BackendTextureData::Type::kPixmaps) {
+        copy_src_data(bufferData, info.fFormat, placedFootprints.get(), data->pixmaps(),
+                      info.fLevelCount);
+    } else if (data->type() == BackendTextureData::Type::kCompressed) {
+        copy_compressed_data(bufferData, info.fFormat, placedFootprints.get(), numRows.get(),
+                             rowSizeInBytes.get(), data->compressedData(), info.fLevelCount);
+    } else {
+        SkASSERT(data->type() == BackendTextureData::Type::kColor);
+        SkImage::CompressionType compression =
+                GrBackendFormatToCompressionType(backendTexture.getBackendFormat());
+        if (SkImage::CompressionType::kNone == compression) {
+            if (!copy_color_data(this->d3dCaps(), bufferData, info.fFormat,
+                                 backendTexture.dimensions(), placedFootprints, data->color())) {
+              return false;
+            }
+        } else {
+            size_t totalCompressedSize = SkCompressedFormatDataSize(compression,
+                                                                    backendTexture.dimensions(),
+                                                                    backendTexture.hasMipmaps());
+            SkAutoTMalloc<char> tempData(totalCompressedSize);
+            GrFillInCompressedData(compression, backendTexture.dimensions(),
+                                   backendTexture.fMipmapped, tempData, data->color());
+            copy_compressed_data(bufferData, info.fFormat, placedFootprints.get(), numRows.get(),
+                                 rowSizeInBytes.get(), tempData.get(), info.fLevelCount);
+        }
     }
-    // Update the offsets in the footprint to be relative to the slice's offset
-    placedFootprints[0].Offset += slice.fOffset;
-    // Since we're sharing data for all the levels, set all the upper level footprints to the base.
-    UINT w = placedFootprints[0].Footprint.Width;
-    UINT h = placedFootprints[0].Footprint.Height;
-    for (unsigned int i = 1; i < mipLevelCount; ++i) {
-        w = std::max(1U, w/2);
-        h = std::max(1U, h/2);
-        placedFootprints[i].Offset = placedFootprints[0].Offset;
-        placedFootprints[i].Footprint.Format   = placedFootprints[0].Footprint.Format;
-        placedFootprints[i].Footprint.Width    = w;
-        placedFootprints[i].Footprint.Height   = h;
-        placedFootprints[i].Footprint.Depth    = 1;
-        placedFootprints[i].Footprint.RowPitch = placedFootprints[0].Footprint.RowPitch;
+
+    // Update the offsets in the footprints to be relative to the slice's offset
+    for (unsigned int i = 0; i < mipLevelCount; ++i) {
+        placedFootprints[i].Offset += slice.fOffset;
     }
 
     ID3D12Resource* d3dBuffer = static_cast<GrD3DBuffer*>(slice.fBuffer)->d3dResource();
-    cmdList->copyBufferToTexture(d3dBuffer,
-                                 texture.get(),
-                                 mipLevelCount,
-                                 placedFootprints.get(),
-                                 /*left*/ 0,
-                                 /*top */ 0);
+    cmdList->copyBufferToTexture(d3dBuffer, texture.get(), mipLevelCount, placedFootprints.get(), 0,
+                                 0);
 
     if (finishedCallback) {
         this->addFinishedCallback(std::move(finishedCallback));
@@ -1116,88 +1144,8 @@
 
 bool GrD3DGpu::onUpdateCompressedBackendTexture(const GrBackendTexture& backendTexture,
                                                 sk_sp<GrRefCntedCallback> finishedCallback,
-                                                const void* data,
-                                                size_t size) {
-    GrD3DTextureResourceInfo info;
-    SkAssertResult(backendTexture.getD3DTextureResourceInfo(&info));
-
-    sk_sp<GrD3DResourceState> state = backendTexture.getGrD3DResourceState();
-    SkASSERT(state);
-    sk_sp<GrD3DTexture> texture = GrD3DTexture::MakeWrappedTexture(this,
-                                                                   backendTexture.dimensions(),
-                                                                   GrWrapCacheable::kNo,
-                                                                   kRW_GrIOType,
-                                                                   info,
-                                                                   std::move(state));
-    if (!texture) {
-        return false;
-    }
-
-    GrD3DDirectCommandList* cmdList = this->currentCommandList();
-    if (!cmdList) {
-        return false;
-    }
-
-    texture->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
-
-    ID3D12Resource* d3dResource = texture->d3dResource();
-    SkASSERT(d3dResource);
-    D3D12_RESOURCE_DESC desc = d3dResource->GetDesc();
-    unsigned int mipLevelCount = 1;
-    if (backendTexture.hasMipmaps()) {
-        mipLevelCount = SkMipmap::ComputeLevelCount(backendTexture.dimensions().width(),
-                                                    backendTexture.dimensions().height()) + 1;
-    }
-    SkASSERT(mipLevelCount == info.fLevelCount);
-    SkAutoTMalloc<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> placedFootprints(mipLevelCount);
-    UINT64 combinedBufferSize;
-    SkAutoTMalloc<UINT> numRows(mipLevelCount);
-    SkAutoTMalloc<UINT64> rowSizeInBytes(mipLevelCount);
-    fDevice->GetCopyableFootprints(&desc,
-                                   0,
-                                   mipLevelCount,
-                                   0,
-                                   placedFootprints.get(),
-                                   numRows.get(),
-                                   rowSizeInBytes.get(),
-                                   &combinedBufferSize);
-    SkASSERT(combinedBufferSize);
-    SkASSERT(!GrDxgiFormatIsCompressed(info.fFormat));
-
-    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
-            combinedBufferSize, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
-    if (!slice.fBuffer) {
-        return false;
-    }
-
-    char* bufferData = (char*)slice.fOffsetMapPtr;
-    SkASSERT(bufferData);
-    copy_compressed_data(bufferData,
-                         info.fFormat,
-                         placedFootprints.get(),
-                         numRows.get(),
-                         rowSizeInBytes.get(),
-                         data,
-                         info.fLevelCount);
-
-    // Update the offsets in the footprints to be relative to the slice's offset
-    for (unsigned int i = 0; i < mipLevelCount; ++i) {
-        placedFootprints[i].Offset += slice.fOffset;
-    }
-
-    ID3D12Resource* d3dBuffer = static_cast<GrD3DBuffer*>(slice.fBuffer)->d3dResource();
-    cmdList->copyBufferToTexture(d3dBuffer,
-                                 texture.get(),
-                                 mipLevelCount,
-                                 placedFootprints.get(),
-                                 0,
-                                 0);
-
-    if (finishedCallback) {
-        this->addFinishedCallback(std::move(finishedCallback));
-    }
-
-    return true;
+                                                const BackendTextureData* data) {
+    return this->onUpdateBackendTexture(backendTexture, std::move(finishedCallback), data);
 }
 
 void GrD3DGpu::deleteBackendTexture(const GrBackendTexture& tex) {
diff --git a/src/gpu/d3d/GrD3DGpu.h b/src/gpu/d3d/GrD3DGpu.h
index 7b6db87..fe952b8 100644
--- a/src/gpu/d3d/GrD3DGpu.h
+++ b/src/gpu/d3d/GrD3DGpu.h
@@ -218,9 +218,9 @@
                                             GrMipmapped,
                                             GrProtected) override;
 
-    bool onClearBackendTexture(const GrBackendTexture&,
-                               sk_sp<GrRefCntedCallback> finishedCallback,
-                               std::array<float, 4> color) override;
+    bool onUpdateBackendTexture(const GrBackendTexture&,
+                                sk_sp<GrRefCntedCallback> finishedCallback,
+                                const BackendTextureData*) override;
 
     GrBackendTexture onCreateCompressedBackendTexture(SkISize dimensions,
                                                       const GrBackendFormat&,
@@ -229,8 +229,7 @@
 
     bool onUpdateCompressedBackendTexture(const GrBackendTexture&,
                                           sk_sp<GrRefCntedCallback> finishedCallback,
-                                          const void* data,
-                                          size_t size) override;
+                                          const BackendTextureData*) override;
 
     bool submitDirectCommandList(SyncQueue sync);
 
diff --git a/src/gpu/dawn/GrDawnGpu.cpp b/src/gpu/dawn/GrDawnGpu.cpp
index 1de6d60..092216c 100644
--- a/src/gpu/dawn/GrDawnGpu.cpp
+++ b/src/gpu/dawn/GrDawnGpu.cpp
@@ -382,23 +382,35 @@
     }
 }
 
-bool GrDawnGpu::onClearBackendTexture(const GrBackendTexture& backendTexture,
-                                      sk_sp<GrRefCntedCallback> finishedCallback,
-                                      std::array<float, 4> color) {
+bool GrDawnGpu::onUpdateBackendTexture(const GrBackendTexture& backendTexture,
+                                       sk_sp<GrRefCntedCallback> finishedCallback,
+                                       const BackendTextureData* data) {
     GrDawnTextureInfo info;
     SkAssertResult(backendTexture.getDawnTextureInfo(&info));
 
+    size_t bpp = GrDawnBytesPerBlock(info.fFormat);
+    size_t baseLayerSize = bpp * backendTexture.width() * backendTexture.height();
+    const void* pixels;
+    SkAutoMalloc defaultStorage(baseLayerSize);
+    if (data && data->type() == BackendTextureData::Type::kPixmaps) {
+        int numMipLevels = info.fLevelCount;
+        SkAutoTArray<GrMipLevel> texels(numMipLevels);
+        GrColorType colorType = data->pixmap(0).colorType();
+        for (int i = 0; i < numMipLevels; ++i) {
+            texels[i] = {data->pixmap(i).addr(), data->pixmap(i).rowBytes(), nullptr};
+        }
+        SkIRect dstRect = SkIRect::MakeSize(backendTexture.dimensions());
+        this->uploadTextureData(colorType, texels.get(), numMipLevels, dstRect, info.fTexture);
+        return true;
+    }
+    pixels = defaultStorage.get();
     GrColorType colorType;
     if (!GrDawnFormatToGrColorType(info.fFormat, &colorType)) {
         return false;
     }
-
-    size_t bpp = GrDawnBytesPerBlock(info.fFormat);
-    size_t baseLayerSize = bpp * backendTexture.width() * backendTexture.height();
-    SkAutoMalloc defaultStorage(baseLayerSize);
-    GrImageInfo imageInfo(colorType, kUnpremul_SkAlphaType, nullptr, backendTexture.dimensions());
-    GrClearImage(imageInfo, defaultStorage.get(), bpp * backendTexture.width(), color);
-
+    SkISize size{backendTexture.width(), backendTexture.height()};
+    GrImageInfo imageInfo(colorType, kUnpremul_SkAlphaType, nullptr, size);
+    GrClearImage(imageInfo, defaultStorage.get(), bpp * backendTexture.width(), data->color());
     wgpu::Device device = this->device();
     wgpu::CommandEncoder copyEncoder = this->getCopyEncoder();
     int w = backendTexture.width(), h = backendTexture.height();
@@ -409,9 +421,9 @@
         GrStagingBufferManager::Slice stagingBuffer =
                 this->stagingBufferManager()->allocateStagingBufferSlice(size);
         if (rowBytes == origRowBytes) {
-            memcpy(stagingBuffer.fOffsetMapPtr, defaultStorage.get(), size);
+            memcpy(stagingBuffer.fOffsetMapPtr, pixels, size);
         } else {
-            const char* src = static_cast<const char*>(defaultStorage.get());
+            const char* src = static_cast<const char*>(pixels);
             char* dst = static_cast<char*>(stagingBuffer.fOffsetMapPtr);
             for (int row = 0; row < h; row++) {
                 memcpy(dst, src, origRowBytes);
@@ -443,8 +455,7 @@
 
 bool GrDawnGpu::onUpdateCompressedBackendTexture(const GrBackendTexture&,
                                                  sk_sp<GrRefCntedCallback> finishedCallback,
-                                                 const void* data,
-                                                 size_t size) {
+                                                 const BackendTextureData*) {
     return false;
 }
 
diff --git a/src/gpu/dawn/GrDawnGpu.h b/src/gpu/dawn/GrDawnGpu.h
index f1b34b4..5959a16 100644
--- a/src/gpu/dawn/GrDawnGpu.h
+++ b/src/gpu/dawn/GrDawnGpu.h
@@ -144,9 +144,9 @@
                                             GrMipmapped,
                                             GrProtected) override;
 
-    bool onClearBackendTexture(const GrBackendTexture&,
-                               sk_sp<GrRefCntedCallback> finishedCallback,
-                               std::array<float, 4> color) override;
+    bool onUpdateBackendTexture(const GrBackendTexture&,
+                                sk_sp<GrRefCntedCallback> finishedCallback,
+                                const BackendTextureData*) override;
 
     GrBackendTexture onCreateCompressedBackendTexture(SkISize dimensions,
                                                       const GrBackendFormat&,
@@ -155,8 +155,7 @@
 
     bool onUpdateCompressedBackendTexture(const GrBackendTexture&,
                                           sk_sp<GrRefCntedCallback> finishedCallback,
-                                          const void* data,
-                                          size_t size) override;
+                                          const BackendTextureData*) override;
 
     sk_sp<GrGpuBuffer> onCreateBuffer(size_t size, GrGpuBufferType type, GrAccessPattern,
                                       const void* data) override;
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index bf1f3d9..8e1e5fd 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -980,7 +980,7 @@
 bool GrGLGpu::uploadColorToTex(GrGLFormat textureFormat,
                                SkISize texDims,
                                GrGLenum target,
-                               std::array<float, 4> color,
+                               SkColor4f color,
                                uint32_t levelMask) {
     GrColorType colorType;
     GrGLenum externalFormat, externalType;
@@ -1386,11 +1386,8 @@
             fHWBoundRenderTargetUniqueID.makeInvalid();
         } else {
             this->bindTextureToScratchUnit(texDesc.fTarget, tex->textureID());
-            std::array<float, 4> zeros = {};
-            this->uploadColorToTex(texDesc.fFormat,
-                                   texDesc.fSize,
-                                   texDesc.fTarget,
-                                   zeros,
+            static constexpr SkColor4f kZeroColor = {0, 0, 0, 0};
+            this->uploadColorToTex(texDesc.fFormat, texDesc.fSize, texDesc.fTarget, kZeroColor,
                                    levelClearMask);
         }
     }
@@ -1485,8 +1482,11 @@
 
 bool GrGLGpu::onUpdateCompressedBackendTexture(const GrBackendTexture& backendTexture,
                                                sk_sp<GrRefCntedCallback> finishedCallback,
-                                               const void* data,
-                                               size_t length) {
+                                               const BackendTextureData* data) {
+    SkASSERT(data && data->type() != BackendTextureData::Type::kPixmaps);
+
+    this->handleDirtyContext();
+
     GrGLTextureInfo info;
     SkAssertResult(backendTexture.getGLTextureInfo(&info));
 
@@ -1499,6 +1499,27 @@
 
     GrMipmapped mipMapped = backendTexture.hasMipmaps() ? GrMipmapped::kYes : GrMipmapped::kNo;
 
+    const char* rawData = nullptr;
+    size_t rawDataSize = 0;
+    SkAutoMalloc am;
+    if (data->type() == BackendTextureData::Type::kCompressed) {
+        rawData = (const char*)data->compressedData();
+        rawDataSize = data->compressedSize();
+    } else {
+        SkASSERT(data->type() == BackendTextureData::Type::kColor);
+        SkASSERT(compression != SkImage::CompressionType::kNone);
+
+        rawDataSize = SkCompressedDataSize(compression, backendTexture.dimensions(), nullptr,
+                                           backendTexture.hasMipmaps());
+
+        am.reset(rawDataSize);
+
+        GrFillInCompressedData(compression, backendTexture.dimensions(), mipMapped, (char*)am.get(),
+                               data->color());
+
+        rawData = (const char*)am.get();
+    }
+
     this->bindTextureToScratchUnit(info.fTarget, info.fID);
 
     // If we have mips make sure the base level is set to 0 and the max level set to numMipLevels-1
@@ -1519,13 +1540,9 @@
         params->set(nullptr, nonsamplerState, fResetTimestampForTextureParameters);
     }
 
-    bool result = this->uploadCompressedTexData(compression,
-                                                glFormat,
-                                                backendTexture.dimensions(),
-                                                mipMapped,
-                                                GR_GL_TEXTURE_2D,
-                                                data,
-                                                length);
+    bool result = this->uploadCompressedTexData(
+            compression, glFormat, backendTexture.dimensions(),  mipMapped, GR_GL_TEXTURE_2D,
+            rawData, rawDataSize);
 
     // Unbind this texture from the scratch texture unit.
     this->bindTextureToScratchUnit(info.fTarget, 0);
@@ -3601,9 +3618,9 @@
                             std::move(parameters));
 }
 
-bool GrGLGpu::onClearBackendTexture(const GrBackendTexture& backendTexture,
-                                    sk_sp<GrRefCntedCallback> finishedCallback,
-                                    std::array<float, 4> color) {
+bool GrGLGpu::onUpdateBackendTexture(const GrBackendTexture& backendTexture,
+                                     sk_sp<GrRefCntedCallback> finishedCallback,
+                                     const BackendTextureData* data) {
     this->handleDirtyContext();
 
     GrGLTextureInfo info;
@@ -3635,12 +3652,26 @@
         params->set(nullptr, nonsamplerState, fResetTimestampForTextureParameters);
     }
 
-    uint32_t levelMask = (1 << numMipLevels) - 1;
-    bool result = this->uploadColorToTex(glFormat,
-                                         backendTexture.dimensions(),
-                                         info.fTarget,
-                                         color,
-                                         levelMask);
+    SkASSERT(data->type() != BackendTextureData::Type::kCompressed);
+    bool result = false;
+    if (data->type() == BackendTextureData::Type::kPixmaps) {
+        SkTArray<GrMipLevel> texels;
+        texels.push_back_n(numMipLevels);
+        GrColorType colorType = data->pixmap(0).colorType();
+        for (int i = 0; i < numMipLevels; ++i) {
+            texels[i] = {data->pixmap(i).addr(),
+                         data->pixmap(i).rowBytes(),
+                         data->pixmap(i).pixelStorage()};
+        }
+        SkIRect dstRect = SkIRect::MakeSize(backendTexture.dimensions());
+        result = this->uploadColorTypeTexData(glFormat, colorType, backendTexture.dimensions(),
+                                              info.fTarget, dstRect, colorType, texels.begin(),
+                                              texels.count());
+    } else if (data->type() == BackendTextureData::Type::kColor) {
+        uint32_t levelMask = (1 << numMipLevels) - 1;
+        result = this->uploadColorToTex(glFormat, backendTexture.dimensions(), info.fTarget,
+                                        data->color(), levelMask);
+    }
 
     // Unbind this texture from the scratch texture unit.
     this->bindTextureToScratchUnit(info.fTarget, 0);
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 9494930..0a47a9d 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -212,14 +212,13 @@
                                                       GrMipmapped,
                                                       GrProtected) override;
 
-    bool onClearBackendTexture(const GrBackendTexture&,
-                               sk_sp<GrRefCntedCallback> finishedCallback,
-                               std::array<float, 4> color) override;
+    bool onUpdateBackendTexture(const GrBackendTexture&,
+                                sk_sp<GrRefCntedCallback> finishedCallback,
+                                const BackendTextureData*) override;
 
     bool onUpdateCompressedBackendTexture(const GrBackendTexture&,
                                           sk_sp<GrRefCntedCallback> finishedCallback,
-                                          const void* data,
-                                          size_t length) override;
+                                          const BackendTextureData*) override;
 
     void onResetContext(uint32_t resetBits) override;
 
@@ -455,7 +454,7 @@
     bool uploadColorToTex(GrGLFormat textureFormat,
                           SkISize texDims,
                           GrGLenum target,
-                          std::array<float, 4> color,
+                          SkColor4f color,
                           uint32_t levelMask);
 
     // Pushes data to the currently bound texture to the currently active unit. 'dstRect' must be
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index 05064a1..085191f 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -165,9 +165,9 @@
                                             GrMipmapped,
                                             GrProtected) override;
 
-    bool onClearBackendTexture(const GrBackendTexture&,
-                               sk_sp<GrRefCntedCallback> finishedCallback,
-                               std::array<float, 4> color) override {
+    bool onUpdateBackendTexture(const GrBackendTexture&,
+                                sk_sp<GrRefCntedCallback> finishedCallback,
+                                const BackendTextureData*) override {
         return true;
     }
 
@@ -178,8 +178,7 @@
 
     bool onUpdateCompressedBackendTexture(const GrBackendTexture&,
                                           sk_sp<GrRefCntedCallback> finishedCallback,
-                                          const void*,
-                                          size_t) override {
+                                          const BackendTextureData*) override {
         return true;
     }
 
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 4502050..5370da2 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -131,9 +131,9 @@
                                             GrMipmapped,
                                             GrProtected) override;
 
-    bool onClearBackendTexture(const GrBackendTexture&,
-                               sk_sp<GrRefCntedCallback> finishedCallback,
-                               std::array<float, 4> color) override;
+    bool onUpdateBackendTexture(const GrBackendTexture&,
+                                sk_sp<GrRefCntedCallback> finishedCallback,
+                                const BackendTextureData*) override;
 
     GrBackendTexture onCreateCompressedBackendTexture(SkISize dimensions,
                                                       const GrBackendFormat&,
@@ -142,8 +142,7 @@
 
     bool onUpdateCompressedBackendTexture(const GrBackendTexture&,
                                           sk_sp<GrRefCntedCallback> finishedCallback,
-                                          const void* data,
-                                          size_t size) override;
+                                          const BackendTextureData*) override;
 
     sk_sp<GrTexture> onCreateTexture(SkISize,
                                      const GrBackendFormat&,
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index b43d7a6..3c4fbff 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -946,9 +946,9 @@
     return backendTex;
 }
 
-bool GrMtlGpu::onClearBackendTexture(const GrBackendTexture& backendTexture,
-                                     sk_sp<GrRefCntedCallback> finishedCallback,
-                                     std::array<float, 4> color) {
+bool GrMtlGpu::onUpdateBackendTexture(const GrBackendTexture& backendTexture,
+                                      sk_sp<GrRefCntedCallback> finishedCallback,
+                                      const BackendTextureData* data) {
     GrMtlTextureInfo info;
     SkAssertResult(backendTexture.getMtlTextureInfo(&info));
 
@@ -956,19 +956,45 @@
 
     const MTLPixelFormat mtlFormat = mtlTexture.pixelFormat;
 
+    int numMipLevels = mtlTexture.mipmapLevelCount;
+    GrMipmapped mipMapped = numMipLevels > 1 ? GrMipmapped::kYes : GrMipmapped::kNo;
+
+    SkImage::CompressionType compression = GrBackendFormatToCompressionType(
+            backendTexture.getBackendFormat());
+
     // Create a transfer buffer and fill with data.
     size_t bytesPerPixel = GrMtlFormatBytesPerBlock(mtlFormat);
+    SkSTArray<16, size_t> individualMipOffsets;
     size_t combinedBufferSize;
 
-    // Reuse the same buffer for all levels. Should be ok since we made the row bytes tight.
-    combinedBufferSize = bytesPerPixel*backendTexture.width()*backendTexture.height();
+    if (data->type() == BackendTextureData::Type::kColor &&
+        compression == SkImage::CompressionType::kNone) {
+        combinedBufferSize = bytesPerPixel*backendTexture.width()*backendTexture.height();
+        // Reuse the same buffer for all levels. Should be ok since we made the row bytes tight.
+        individualMipOffsets.push_back_n(numMipLevels, (size_t)0);
+    } else if (compression == SkImage::CompressionType::kNone) {
+        combinedBufferSize = GrComputeTightCombinedBufferSize(bytesPerPixel,
+                                                              backendTexture.dimensions(),
+                                                              &individualMipOffsets,
+                                                              numMipLevels);
+    } else {
+        combinedBufferSize = SkCompressedDataSize(compression, backendTexture.dimensions(),
+                                                  &individualMipOffsets,
+                                                  mipMapped == GrMipmapped::kYes);
+    }
+    SkASSERT(individualMipOffsets.count() == numMipLevels);
 
 #ifdef SK_BUILD_FOR_MAC
     static const size_t kMinAlignment = 4;
 #else
     static const size_t kMinAlignment = 1;
 #endif
-    size_t alignment = std::max(bytesPerPixel, kMinAlignment);
+    size_t alignment;
+    if (data->type() == BackendTextureData::Type::kCompressed) {
+        alignment = std::max(SkCompressedBlockSize(compression), kMinAlignment);
+    } else {
+        alignment = std::max(bytesPerPixel, kMinAlignment);
+    }
     GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
             combinedBufferSize, alignment);
     if (!slice.fBuffer) {
@@ -976,15 +1002,28 @@
     }
     char* buffer = (char*)slice.fOffsetMapPtr;
 
-    auto colorType = mtl_format_to_backend_tex_clear_colortype(mtlFormat);
-    if (colorType == GrColorType::kUnknown) {
-        return false;
-    }
-    GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, backendTexture.dimensions());
-    auto rb = ii.minRowBytes();
-    SkASSERT(rb == bytesPerPixel*backendTexture.width());
-    if (!GrClearImage(ii, buffer, rb, color)) {
-        return false;
+    if (data->type() == BackendTextureData::Type::kPixmaps) {
+        copy_src_data(buffer, bytesPerPixel, individualMipOffsets, data->pixmaps(),
+                      numMipLevels, combinedBufferSize);
+    } else if (data->type() == BackendTextureData::Type::kCompressed) {
+        memcpy(buffer, data->compressedData(), data->compressedSize());
+    } else {
+        SkASSERT(data->type() == BackendTextureData::Type::kColor);
+        if (compression == SkImage::CompressionType::kNone) {
+            auto colorType = mtl_format_to_backend_tex_clear_colortype(mtlFormat);
+            if (colorType == GrColorType::kUnknown) {
+                return false;
+            }
+            GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, backendTexture.dimensions());
+            auto rb = ii.minRowBytes();
+            SkASSERT(rb == bytesPerPixel*backendTexture.width());
+            if (!GrClearImage(ii, buffer, rb, data->color())) {
+                return false;
+            }
+        } else {
+            GrFillInCompressedData(compression, backendTexture.dimensions(), mipMapped, buffer,
+                                   data->color());
+        }
     }
 
     // Transfer buffer contents to texture
@@ -995,29 +1034,32 @@
     GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer);
 
     SkISize levelDimensions(backendTexture.dimensions());
-    int numMipLevels = mtlTexture.mipmapLevelCount;
     for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
         size_t levelRowBytes;
         size_t levelSize;
 
-        levelRowBytes = levelDimensions.width() * bytesPerPixel;
-        levelSize = levelRowBytes * levelDimensions.height();
+        if (compression == SkImage::CompressionType::kNone) {
+            levelRowBytes = levelDimensions.width() * bytesPerPixel;
+            levelSize = levelRowBytes * levelDimensions.height();
+        } else {
+            levelRowBytes = GrCompressedRowBytes(compression, levelDimensions.width());
+            levelSize = SkCompressedDataSize(compression, levelDimensions, nullptr, false);
+        }
 
         // TODO: can this all be done in one go?
         [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer()
-                          sourceOffset: slice.fOffset
+                          sourceOffset: slice.fOffset + individualMipOffsets[currentMipLevel]
                      sourceBytesPerRow: levelRowBytes
                    sourceBytesPerImage: levelSize
                             sourceSize: MTLSizeMake(levelDimensions.width(),
-                                                    levelDimensions.height(),
-                                                    1)
+                                                    levelDimensions.height(), 1)
                              toTexture: mtlTexture
                       destinationSlice: 0
                       destinationLevel: currentMipLevel
                      destinationOrigin: origin];
 
-        levelDimensions = {std::max(1, levelDimensions.width() / 2),
-                           std::max(1, levelDimensions.height() / 2)};
+        levelDimensions = { std::max(1, levelDimensions.width() / 2),
+                            std::max(1, levelDimensions.height() / 2) };
     }
 #ifdef SK_BUILD_FOR_MAC
     [mtlBuffer->mtlBuffer() didModifyRange: NSMakeRange(slice.fOffset, combinedBufferSize)];
@@ -1046,84 +1088,8 @@
 
 bool GrMtlGpu::onUpdateCompressedBackendTexture(const GrBackendTexture& backendTexture,
                                                 sk_sp<GrRefCntedCallback> finishedCallback,
-                                                const void* data,
-                                                size_t size) {
-    GrMtlTextureInfo info;
-    SkAssertResult(backendTexture.getMtlTextureInfo(&info));
-
-    id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture.get());
-
-    int numMipLevels = mtlTexture.mipmapLevelCount;
-    GrMipmapped mipMapped = numMipLevels > 1 ? GrMipmapped::kYes : GrMipmapped::kNo;
-
-    SkImage::CompressionType compression =
-            GrBackendFormatToCompressionType(backendTexture.getBackendFormat());
-    SkASSERT(compression != SkImage::CompressionType::kNone);
-
-    // Create a transfer buffer and fill with data.
-    SkSTArray<16, size_t> individualMipOffsets;
-    size_t combinedBufferSize;
-    combinedBufferSize = SkCompressedDataSize(compression,
-                                              backendTexture.dimensions(),
-                                              &individualMipOffsets,
-                                              mipMapped == GrMipmapped::kYes);
-    SkASSERT(individualMipOffsets.count() == numMipLevels);
-
-#ifdef SK_BUILD_FOR_MAC
-    static const size_t kMinAlignment = 4;
-#else
-    static const size_t kMinAlignment = 1;
-#endif
-    size_t alignment = std::max(SkCompressedBlockSize(compression), kMinAlignment);
-    GrStagingBufferManager::Slice slice =
-            fStagingBufferManager.allocateStagingBufferSlice(combinedBufferSize, alignment);
-    if (!slice.fBuffer) {
-        return false;
-    }
-    char* buffer = (char*)slice.fOffsetMapPtr;
-
-    memcpy(buffer, data, size);
-
-    // Transfer buffer contents to texture
-    MTLOrigin origin = MTLOriginMake(0, 0, 0);
-
-    GrMtlCommandBuffer* cmdBuffer = this->commandBuffer();
-    id<MTLBlitCommandEncoder> blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
-    GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer);
-
-    SkISize levelDimensions(backendTexture.dimensions());
-    for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
-        size_t levelRowBytes;
-        size_t levelSize;
-
-        levelRowBytes = GrCompressedRowBytes(compression, levelDimensions.width());
-        levelSize = SkCompressedDataSize(compression, levelDimensions, nullptr, false);
-
-        // TODO: can this all be done in one go?
-        [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer()
-                          sourceOffset: slice.fOffset + individualMipOffsets[currentMipLevel]
-                     sourceBytesPerRow: levelRowBytes
-                   sourceBytesPerImage: levelSize
-                            sourceSize: MTLSizeMake(levelDimensions.width(),
-                                                    levelDimensions.height(),
-                                                    1)
-                             toTexture: mtlTexture
-                      destinationSlice: 0
-                      destinationLevel: currentMipLevel
-                     destinationOrigin: origin];
-
-        levelDimensions = {std::max(1, levelDimensions.width() / 2),
-                           std::max(1, levelDimensions.height() / 2)};
-    }
-#ifdef SK_BUILD_FOR_MAC
-    [mtlBuffer->mtlBuffer() didModifyRange:NSMakeRange(slice.fOffset, combinedBufferSize)];
-#endif
-
-    if (finishedCallback) {
-        this->addFinishedCallback(std::move(finishedCallback));
-    }
-
-    return true;
+                                                const BackendTextureData* data) {
+    return this->onUpdateBackendTexture(backendTexture, std::move(finishedCallback), data);
 }
 
 void GrMtlGpu::deleteBackendTexture(const GrBackendTexture& tex) {
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index f5285b5..1e5655d 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -729,17 +729,14 @@
 
 // This fills in the 'regions' vector in preparation for copying a buffer to an image.
 // 'individualMipOffsets' is filled in as a side-effect.
-static size_t fill_in_compressed_regions(GrStagingBufferManager* stagingBufferManager,
-                                         SkTArray<VkBufferImageCopy>* regions,
-                                         SkTArray<size_t>* individualMipOffsets,
-                                         GrStagingBufferManager::Slice* slice,
-                                         SkImage::CompressionType compression,
-                                         VkFormat vkFormat,
-                                         SkISize dimensions,
-                                         GrMipmapped mipmapped) {
-    SkASSERT(compression != SkImage::CompressionType::kNone);
+static size_t fill_in_regions(GrStagingBufferManager* stagingBufferManager,
+                              SkTArray<VkBufferImageCopy>* regions,
+                              SkTArray<size_t>* individualMipOffsets,
+                              GrStagingBufferManager::Slice* slice,
+                              SkImage::CompressionType compression,
+                              VkFormat vkFormat, SkISize dimensions, GrMipmapped mipMapped) {
     int numMipLevels = 1;
-    if (mipmapped == GrMipmapped::kYes) {
+    if (mipMapped == GrMipmapped::kYes) {
         numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
     }
 
@@ -748,10 +745,15 @@
 
     size_t bytesPerBlock = GrVkFormatBytesPerBlock(vkFormat);
 
-    size_t bufferSize = SkCompressedDataSize(compression,
-                                             dimensions,
-                                             individualMipOffsets,
-                                             mipmapped == GrMipmapped::kYes);
+    size_t combinedBufferSize;
+    if (compression == SkImage::CompressionType::kNone) {
+        combinedBufferSize = GrComputeTightCombinedBufferSize(bytesPerBlock, dimensions,
+                                                              individualMipOffsets,
+                                                              numMipLevels);
+    } else {
+        combinedBufferSize = SkCompressedDataSize(compression, dimensions, individualMipOffsets,
+                                                  mipMapped == GrMipmapped::kYes);
+    }
     SkASSERT(individualMipOffsets->count() == numMipLevels);
 
     // Get a staging buffer slice to hold our mip data.
@@ -762,7 +764,7 @@
         case 2:     alignment *= 2; break;   // alignment is a multiple of 2 but not 4.
         default:    alignment *= 4; break;   // alignment is not a multiple of 2.
     }
-    *slice = stagingBufferManager->allocateStagingBufferSlice(bufferSize, alignment);
+    *slice = stagingBufferManager->allocateStagingBufferSlice(combinedBufferSize, alignment);
     if (!slice->fBuffer) {
         return 0;
     }
@@ -783,7 +785,7 @@
                       std::max(1, dimensions.height()/2)};
     }
 
-    return bufferSize;
+    return combinedBufferSize;
 }
 
 bool GrVkGpu::uploadTexDataOptimal(GrVkAttachment* texAttachment, int left, int top, int width,
@@ -931,14 +933,10 @@
     GrStagingBufferManager::Slice slice;
     SkTArray<VkBufferImageCopy> regions;
     SkTArray<size_t> individualMipOffsets;
-    SkDEBUGCODE(size_t combinedBufferSize =) fill_in_compressed_regions(&fStagingBufferManager,
-                                                                        &regions,
-                                                                        &individualMipOffsets,
-                                                                        &slice,
-                                                                        compression,
-                                                                        vkFormat,
-                                                                        dimensions,
-                                                                        mipMapped);
+    SkDEBUGCODE(size_t combinedBufferSize =) fill_in_regions(&fStagingBufferManager,
+                                                             &regions, &individualMipOffsets,
+                                                             &slice, compression, vkFormat,
+                                                             dimensions, mipMapped);
     if (!slice.fBuffer) {
         return false;
     }
@@ -1464,6 +1462,22 @@
     return true;
 }
 
+bool copy_compressed_data(GrVkGpu* gpu, char* mapPtr,
+                          const void* rawData, size_t dataSize) {
+    SkASSERT(mapPtr);
+    memcpy(mapPtr, rawData, dataSize);
+    return true;
+}
+
+bool generate_compressed_data(GrVkGpu* gpu, char* mapPtr,
+                              SkImage::CompressionType compression, SkISize dimensions,
+                              GrMipmapped mipMapped, const SkColor4f& color) {
+    SkASSERT(mapPtr);
+    GrFillInCompressedData(compression, dimensions, mipMapped, mapPtr, color);
+
+    return true;
+}
+
 bool GrVkGpu::createVkImageForBackendSurface(VkFormat vkFormat,
                                              SkISize dimensions,
                                              int sampleCnt,
@@ -1531,9 +1545,9 @@
     return true;
 }
 
-bool GrVkGpu::onClearBackendTexture(const GrBackendTexture& backendTexture,
-                                    sk_sp<GrRefCntedCallback> finishedCallback,
-                                    std::array<float, 4> color) {
+bool GrVkGpu::onUpdateBackendTexture(const GrBackendTexture& backendTexture,
+                                     sk_sp<GrRefCntedCallback> finishedCallback,
+                                     const BackendTextureData* data) {
     GrVkImageInfo info;
     SkAssertResult(backendTexture.getVkImageInfo(&info));
 
@@ -1557,23 +1571,68 @@
                                   VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
                                   false);
 
-    // CmdClearColorImage doesn't work for compressed formats
-    SkASSERT(!GrVkFormatIsCompressed(info.fFormat));
+    // Unfortunately, CmdClearColorImage doesn't work for compressed formats
+    bool fastPath = data->type() == BackendTextureData::Type::kColor &&
+                    !GrVkFormatIsCompressed(info.fFormat);
 
-    VkClearColorValue vkColor;
-    // If we ever support SINT or UINT formats this needs to be updated to use the int32 and
-    // uint32 union members in those cases.
-    vkColor.float32[0] = color[0];
-    vkColor.float32[1] = color[1];
-    vkColor.float32[2] = color[2];
-    vkColor.float32[3] = color[3];
-    VkImageSubresourceRange range;
-    range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-    range.baseArrayLayer = 0;
-    range.baseMipLevel = 0;
-    range.layerCount = 1;
-    range.levelCount = info.fLevelCount;
-    cmdBuffer->clearColorImage(this, texAttachment, &vkColor, 1, &range);
+    if (fastPath) {
+        SkASSERT(data->type() == BackendTextureData::Type::kColor);
+        VkClearColorValue vkColor;
+        SkColor4f color = data->color();
+        // If we ever support SINT or UINT formats this needs to be updated to use the int32 and
+        // uint32 union members in those cases.
+        vkColor.float32[0] = color.fR;
+        vkColor.float32[1] = color.fG;
+        vkColor.float32[2] = color.fB;
+        vkColor.float32[3] = color.fA;
+        VkImageSubresourceRange range;
+        range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+        range.baseArrayLayer = 0;
+        range.baseMipLevel = 0;
+        range.layerCount = 1;
+        range.levelCount = info.fLevelCount;
+        cmdBuffer->clearColorImage(this, texAttachment, &vkColor, 1, &range);
+    } else {
+        SkImage::CompressionType compression = GrBackendFormatToCompressionType(
+                backendTexture.getBackendFormat());
+
+        SkTArray<VkBufferImageCopy> regions;
+        SkTArray<size_t> individualMipOffsets;
+        GrStagingBufferManager::Slice slice;
+
+        fill_in_regions(&fStagingBufferManager, &regions, &individualMipOffsets,
+                        &slice, compression, info.fFormat, backendTexture.dimensions(),
+                        backendTexture.fMipmapped);
+
+        if (!slice.fBuffer) {
+            return false;
+        }
+
+        bool result;
+        if (data->type() == BackendTextureData::Type::kPixmaps) {
+            result = copy_src_data((char*)slice.fOffsetMapPtr, info.fFormat, individualMipOffsets,
+                                   data->pixmaps(), info.fLevelCount);
+        } else if (data->type() == BackendTextureData::Type::kCompressed) {
+            result = copy_compressed_data(this, (char*)slice.fOffsetMapPtr,
+                                          data->compressedData(), data->compressedSize());
+        } else {
+            SkASSERT(data->type() == BackendTextureData::Type::kColor);
+            result = generate_compressed_data(this, (char*)slice.fOffsetMapPtr, compression,
+                                              backendTexture.dimensions(),
+                                              backendTexture.fMipmapped, data->color());
+        }
+
+        cmdBuffer->addGrSurface(texture);
+        // Copy the buffer to the image. This call takes the raw VkBuffer instead of a GrGpuBuffer
+        // because we don't need the command buffer to ref the buffer here. The reason being is that
+        // the buffer is coming from the staging manager and the staging manager will make sure the
+        // command buffer has a ref on the buffer. This avoids having to add and remove a ref for
+        // every upload in the frame.
+        const GrVkBuffer* vkBuffer = static_cast<GrVkBuffer*>(slice.fBuffer);
+        cmdBuffer->copyBufferToImage(this, vkBuffer->vkBuffer(), texAttachment,
+                                     texAttachment->currentLayout(), regions.count(),
+                                     regions.begin());
+    }
 
     // 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
@@ -1630,82 +1689,8 @@
 
 bool GrVkGpu::onUpdateCompressedBackendTexture(const GrBackendTexture& backendTexture,
                                                sk_sp<GrRefCntedCallback> finishedCallback,
-                                               const void* data,
-                                               size_t size) {
-    GrVkImageInfo info;
-    SkAssertResult(backendTexture.getVkImageInfo(&info));
-
-    sk_sp<GrBackendSurfaceMutableStateImpl> mutableState = backendTexture.getMutableState();
-    SkASSERT(mutableState);
-    sk_sp<GrVkTexture> texture = GrVkTexture::MakeWrappedTexture(this,
-                                                                 backendTexture.dimensions(),
-                                                                 kBorrow_GrWrapOwnership,
-                                                                 GrWrapCacheable::kNo,
-                                                                 kRW_GrIOType,
-                                                                 info,
-                                                                 std::move(mutableState));
-    if (!texture) {
-        return false;
-    }
-
-    GrVkPrimaryCommandBuffer* cmdBuffer = this->currentCommandBuffer();
-    if (!cmdBuffer) {
-        return false;
-    }
-    GrVkAttachment* attachment = texture->textureAttachment();
-    attachment->setImageLayout(this,
-                               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                               VK_ACCESS_TRANSFER_WRITE_BIT,
-                               VK_PIPELINE_STAGE_TRANSFER_BIT,
-                               false);
-
-    SkImage::CompressionType compression =
-            GrBackendFormatToCompressionType(backendTexture.getBackendFormat());
-
-    SkTArray<VkBufferImageCopy> regions;
-    SkTArray<size_t> individualMipOffsets;
-    GrStagingBufferManager::Slice slice;
-
-    fill_in_compressed_regions(&fStagingBufferManager,
-                               &regions,
-                               &individualMipOffsets,
-                               &slice,
-                               compression,
-                               info.fFormat,
-                               backendTexture.dimensions(),
-                               backendTexture.fMipmapped);
-
-    if (!slice.fBuffer) {
-        return false;
-    }
-
-    memcpy(slice.fOffsetMapPtr, data, size);
-
-    cmdBuffer->addGrSurface(texture);
-    // Copy the buffer to the image. This call takes the raw VkBuffer instead of a GrGpuBuffer
-    // because we don't need the command buffer to ref the buffer here. The reason being is that
-    // the buffer is coming from the staging manager and the staging manager will make sure the
-    // command buffer has a ref on the buffer. This avoids having to add and remove a ref for
-    // every upload in the frame.
-    cmdBuffer->copyBufferToImage(this,
-                                 static_cast<GrVkBuffer*>(slice.fBuffer)->vkBuffer(),
-                                 attachment,
-                                 attachment->currentLayout(),
-                                 regions.count(),
-                                 regions.begin());
-
-    // 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
-    attachment->setImageLayout(this,
-                               VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
-                               VK_ACCESS_SHADER_READ_BIT,
-                               VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
-                               false);
-
-    if (finishedCallback) {
-        this->addFinishedCallback(std::move(finishedCallback));
-    }
-    return true;
+                                               const BackendTextureData* data) {
+    return this->onUpdateBackendTexture(backendTexture, std::move(finishedCallback), data);
 }
 
 void set_layout_and_queue_from_mutable_state(GrVkGpu* gpu, GrVkImage* image,
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 8dbc3c9..d4070ec 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -218,14 +218,13 @@
                                                       GrMipmapped,
                                                       GrProtected) override;
 
-    bool onClearBackendTexture(const GrBackendTexture&,
-                               sk_sp<GrRefCntedCallback> finishedCallback,
-                               std::array<float, 4> color) override;
+    bool onUpdateBackendTexture(const GrBackendTexture&,
+                                sk_sp<GrRefCntedCallback> finishedCallback,
+                                const BackendTextureData*) override;
 
     bool onUpdateCompressedBackendTexture(const GrBackendTexture&,
                                           sk_sp<GrRefCntedCallback> finishedCallback,
-                                          const void* data,
-                                          size_t length) override;
+                                          const BackendTextureData*) override;
 
     bool setBackendSurfaceState(GrVkImageInfo info,
                                 sk_sp<GrBackendSurfaceMutableStateImpl> currentState,
diff --git a/tests/BackendAllocationTest.cpp b/tests/BackendAllocationTest.cpp
index 75a68de..e504f9d 100644
--- a/tests/BackendAllocationTest.cpp
+++ b/tests/BackendAllocationTest.cpp
@@ -222,7 +222,7 @@
 static SkColor4f get_expected_color(SkColor4f orig, GrColorType ct) {
     GrImageInfo ii(ct, kUnpremul_SkAlphaType, nullptr, {1, 1});
     std::unique_ptr<char[]> data(new char[ii.minRowBytes()]);
-    GrClearImage(ii, data.get(), ii.minRowBytes(), orig.array());
+    GrClearImage(ii, data.get(), ii.minRowBytes(), orig);
 
     // Read back to SkColor4f.
     SkColor4f result;