Expand SkImage::MakeFromCompressed to support mipMapping

Bug: skia:9680
Change-Id: I41357439cdc087e4cc98d5d1659ae2ee19f49004
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/264398
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index a96e340..c8d9359 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -272,6 +272,7 @@
 sk_sp<GrTexture> GrGpu::createCompressedTexture(SkISize dimensions,
                                                 const GrBackendFormat& format,
                                                 SkBudgeted budgeted,
+                                                GrMipMapped mipMapped,
                                                 const void* data,
                                                 size_t dataSize) {
     this->handleDirtyContext();
@@ -291,11 +292,10 @@
     // TODO: expand CompressedDataIsCorrect to work here too
     SkImage::CompressionType compressionType = this->caps()->compressionType(format);
 
-    if (dataSize < GrCompressedDataSize(compressionType, dimensions,
-                                        nullptr, GrMipMapped::kNo)) {
+    if (dataSize < GrCompressedDataSize(compressionType, dimensions, nullptr, mipMapped)) {
         return nullptr;
     }
-    return this->onCreateCompressedTexture(dimensions, format, budgeted, data, dataSize);
+    return this->onCreateCompressedTexture(dimensions, format, budgeted, mipMapped, data, dataSize);
 }
 
 sk_sp<GrTexture> GrGpu::wrapBackendTexture(const GrBackendTexture& backendTex,
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 9683d86..677d163 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -133,7 +133,8 @@
                                    GrProtected isProtected);
 
     sk_sp<GrTexture> createCompressedTexture(SkISize dimensions, const GrBackendFormat&,
-                                             SkBudgeted, const void* data, size_t dataSize);
+                                             SkBudgeted, GrMipMapped,
+                                             const void* data, size_t dataSize);
 
     /**
      * Implements GrResourceProvider::wrapBackendTexture
@@ -668,7 +669,7 @@
                                              uint32_t levelClearMask) = 0;
     virtual sk_sp<GrTexture> onCreateCompressedTexture(SkISize dimensions,
                                                        const GrBackendFormat&,
-                                                       SkBudgeted,
+                                                       SkBudgeted, GrMipMapped,
                                                        const void* data, size_t dataSize) = 0;
     virtual sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrColorType,
                                                   GrWrapOwnership, GrWrapCacheable, GrIOType) = 0;
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index aa23eb7..f26dbfc 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -496,8 +496,8 @@
 }
 
 sk_sp<GrTextureProxy> GrProxyProvider::createCompressedTextureProxy(
-        SkISize dimensions, SkBudgeted budgeted, SkImage::CompressionType compressionType,
-        sk_sp<SkData> data) {
+        SkISize dimensions, SkBudgeted budgeted, GrMipMapped mipMapped,
+        SkImage::CompressionType compressionType, sk_sp<SkData> data) {
     ASSERT_SINGLE_OWNER
     if (this->isAbandoned()) {
         return nullptr;
@@ -514,14 +514,17 @@
         return nullptr;
     }
 
+    GrMipMapsStatus mipMapsStatus = (GrMipMapped::kYes == mipMapped)
+                                                                ? GrMipMapsStatus::kValid
+                                                                : GrMipMapsStatus::kNotAllocated;
+
     sk_sp<GrTextureProxy> proxy = this->createLazyProxy(
-            [dimensions, format, budgeted,
-             data](GrResourceProvider* resourceProvider) {
+            [dimensions, format, budgeted, mipMapped, data](GrResourceProvider* resourceProvider) {
                 return LazyCallbackResult(resourceProvider->createCompressedTexture(
-                    dimensions, format, budgeted, data.get()));
+                    dimensions, format, budgeted, mipMapped, data.get()));
             },
-            format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
-            GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact,
+            format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin, mipMapped,
+            mipMapsStatus, GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact,
             SkBudgeted::kYes, GrProtected::kNo, UseAllocator::kYes);
 
     if (!proxy) {
diff --git a/src/gpu/GrProxyProvider.h b/src/gpu/GrProxyProvider.h
index cf964c0..6b412ac 100644
--- a/src/gpu/GrProxyProvider.h
+++ b/src/gpu/GrProxyProvider.h
@@ -95,7 +95,7 @@
     /*
      * Create a texture proxy from compressed texture data.
      */
-    sk_sp<GrTextureProxy> createCompressedTextureProxy(SkISize dimensions, SkBudgeted,
+    sk_sp<GrTextureProxy> createCompressedTextureProxy(SkISize dimensions, SkBudgeted, GrMipMapped,
                                                        SkImage::CompressionType,
                                                        sk_sp<SkData> data);
 
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index bd6b34d..319f8d1 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -146,12 +146,14 @@
 
 sk_sp<GrTexture> GrResourceProvider::createCompressedTexture(SkISize dimensions,
                                                              const GrBackendFormat& format,
-                                                             SkBudgeted budgeted, SkData* data) {
+                                                             SkBudgeted budgeted,
+                                                             GrMipMapped mipMapped,
+                                                             SkData* data) {
     ASSERT_SINGLE_OWNER
     if (this->isAbandoned()) {
         return nullptr;
     }
-    return fGpu->createCompressedTexture(dimensions, format, budgeted,
+    return fGpu->createCompressedTexture(dimensions, format, budgeted, mipMapped,
                                          data->data(), data->size());
 }
 
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 17487f0..c8d337b 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -106,10 +106,10 @@
 
     /**
      * Creates a compressed texture. The GrGpu must support the SkImageImage::Compression type.
-     * This does not currently support MIP maps. It will not be renderable.
+     * It will not be renderable.
      */
     sk_sp<GrTexture> createCompressedTexture(SkISize dimensions, const GrBackendFormat&,
-                                             SkBudgeted, SkData* data);
+                                             SkBudgeted, GrMipMapped, SkData* data);
 
     ///////////////////////////////////////////////////////////////////////////
     // Wrapped Backend Surfaces
diff --git a/src/gpu/dawn/GrDawnGpu.cpp b/src/gpu/dawn/GrDawnGpu.cpp
index c1c19d3..e66649f 100644
--- a/src/gpu/dawn/GrDawnGpu.cpp
+++ b/src/gpu/dawn/GrDawnGpu.cpp
@@ -172,7 +172,7 @@
 }
 
 sk_sp<GrTexture> GrDawnGpu::onCreateCompressedTexture(SkISize dimensions, const GrBackendFormat&,
-                                                      SkBudgeted, const void* data,
+                                                      SkBudgeted, GrMipMapped, const void* data,
                                                       size_t dataSize) {
     SkASSERT(!"unimplemented");
     return nullptr;
diff --git a/src/gpu/dawn/GrDawnGpu.h b/src/gpu/dawn/GrDawnGpu.h
index 6fa0cca..81ecbce 100644
--- a/src/gpu/dawn/GrDawnGpu.h
+++ b/src/gpu/dawn/GrDawnGpu.h
@@ -121,7 +121,7 @@
 
     sk_sp<GrTexture> onCreateCompressedTexture(SkISize dimensions,
                                                const GrBackendFormat&,
-                                               SkBudgeted,
+                                               SkBudgeted, GrMipMapped,
                                                const void* data, size_t dataSize) override;
 
     sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrColorType, GrWrapOwnership,
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 2cb182c..6f00e5c 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1412,7 +1412,7 @@
 
 sk_sp<GrTexture> GrGLGpu::onCreateCompressedTexture(SkISize dimensions,
                                                     const GrBackendFormat& format,
-                                                    SkBudgeted budgeted,
+                                                    SkBudgeted budgeted, GrMipMapped mipMapped,
                                                     const void* data, size_t dataSize) {
     GrGLTextureParameters::SamplerOverriddenState initialState;
     GrGLTexture::Desc desc;
@@ -1422,7 +1422,7 @@
     desc.fOwnership = GrBackendObjectOwnership::kOwned;
     desc.fFormat = format.asGLFormat();
     desc.fID = this->createCompressedTexture2D(desc.fSize, desc.fFormat,
-                                               GrMipMapped::kNo, &initialState,
+                                               mipMapped, &initialState,
                                                data, dataSize);
     if (!desc.fID) {
         return nullptr;
@@ -1431,7 +1431,11 @@
     // Unbind this texture from the scratch texture unit.
     this->bindTextureToScratchUnit(GR_GL_TEXTURE_2D, 0);
 
-    auto tex = sk_make_sp<GrGLTexture>(this, budgeted, desc, GrMipMapsStatus::kNotAllocated);
+    GrMipMapsStatus mipMapsStatus = mipMapped == GrMipMapped::kYes
+                                                            ? GrMipMapsStatus::kValid
+                                                            : GrMipMapsStatus::kNotAllocated;
+
+    auto tex = sk_make_sp<GrGLTexture>(this, budgeted, desc, mipMapsStatus);
     // The non-sampler params are still at their default values.
     tex->parameters()->set(&initialState, GrGLTextureParameters::NonsamplerState(),
                            fResetTimestampForTextureParameters);
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index ca169ba..0baa364 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -201,7 +201,7 @@
                                      uint32_t levelClearMask) override;
     sk_sp<GrTexture> onCreateCompressedTexture(SkISize dimensions,
                                                const GrBackendFormat&,
-                                               SkBudgeted,
+                                               SkBudgeted, GrMipMapped,
                                                const void* data, size_t dataSize) override;
 
     sk_sp<GrGpuBuffer> onCreateBuffer(size_t size, GrGpuBufferType intendedType, GrAccessPattern,
diff --git a/src/gpu/mock/GrMockGpu.cpp b/src/gpu/mock/GrMockGpu.cpp
index 50f0e6b..cf86de3 100644
--- a/src/gpu/mock/GrMockGpu.cpp
+++ b/src/gpu/mock/GrMockGpu.cpp
@@ -165,7 +165,7 @@
 // TODO: why no 'isProtected' ?!
 sk_sp<GrTexture> GrMockGpu::onCreateCompressedTexture(SkISize dimensions,
                                                       const GrBackendFormat& format,
-                                                      SkBudgeted budgeted,
+                                                      SkBudgeted budgeted, GrMipMapped mipMapped,
                                                       const void* data, size_t dataSize) {
     if (fMockOptions.fFailTextureAllocations) {
         return nullptr;
@@ -180,7 +180,9 @@
     desc.fHeight = dimensions.height();
     desc.fConfig = GrCompressionTypeToPixelConfig(compression);
 
-    GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated;
+    GrMipMapsStatus mipMapsStatus = (mipMapped == GrMipMapped::kYes)
+                                                                ? GrMipMapsStatus::kValid
+                                                                : GrMipMapsStatus::kNotAllocated;
     GrMockTextureInfo texInfo(GrColorType::kUnknown,
                               format.asMockCompressionType(),
                               NextInternalTextureID());
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index dc4162c..d52bb40 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -72,7 +72,7 @@
                                      uint32_t levelClearMask) override;
 
     sk_sp<GrTexture> onCreateCompressedTexture(SkISize dimensions, const GrBackendFormat&,
-                                               SkBudgeted, const void* data,
+                                               SkBudgeted, GrMipMapped, const void* data,
                                                size_t dataSize) override;
 
     sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrColorType, GrWrapOwnership,
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 61dda2c..e9a7702 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -148,7 +148,7 @@
                                      int mipLevelCount,
                                      uint32_t levelClearMask) override;
     sk_sp<GrTexture> onCreateCompressedTexture(SkISize dimensions, const GrBackendFormat&,
-                                               SkBudgeted, const void* data,
+                                               SkBudgeted, GrMipMapped, const void* data,
                                                size_t dataSize) override;
 
     sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrColorType,
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index d643630..d9e8709 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -505,6 +505,7 @@
 sk_sp<GrTexture> GrMtlGpu::onCreateCompressedTexture(SkISize dimensions,
                                                      const GrBackendFormat& format,
                                                      SkBudgeted budgeted,
+                                                     GrMipMapped mipMapped,
                                                      const void* data,
                                                      size_t dataSize) {
     SkASSERT(this->caps()->isFormatTexturable(format));
@@ -515,6 +516,12 @@
     }
 
     MTLPixelFormat mtlPixelFormat = GrBackendFormatAsMTLPixelFormat(format);
+    SkASSERT(this->caps()->isFormatCompressed(format));
+
+    int numMipLevels = 1;
+    if (mipMapped == GrMipMapped::kYes) {
+        numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
+    }
 
     // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is
     // requested, this TexDesc describes the resolved texture. Therefore we always have samples
@@ -526,7 +533,7 @@
     texDesc.width = dimensions.width();
     texDesc.height = dimensions.height();
     texDesc.depth = 1;
-    texDesc.mipmapLevelCount = 1;
+    texDesc.mipmapLevelCount = numMipLevels;
     texDesc.sampleCount = 1;
     texDesc.arrayLength = 1;
     // Make all textures have private gpu only access. We can use transfer buffers or textures
@@ -536,12 +543,15 @@
         texDesc.usage = MTLTextureUsageShaderRead;
     }
 
+    GrMipMapsStatus mipMapsStatus = (mipMapped == GrMipMapped::kYes)
+                                                                ? GrMipMapsStatus::kValid
+                                                                : GrMipMapsStatus::kNotAllocated;
+
     GrSurfaceDesc desc;
     desc.fConfig = this->caps()->getConfigFromCompressedBackendFormat(format);
     desc.fWidth = dimensions.width();
     desc.fHeight = dimensions.height();
-    auto tex = GrMtlTexture::MakeNewTexture(this, budgeted, desc, texDesc,
-                                            GrMipMapsStatus::kNotAllocated);
+    auto tex = GrMtlTexture::MakeNewTexture(this, budgeted, desc, texDesc, mipMapsStatus);
     if (!tex) {
         return nullptr;
     }
@@ -553,6 +563,12 @@
     auto compressionType = GrMtlFormatToCompressionType(mtlTexture.pixelFormat);
     SkASSERT(compressionType != SkImage::CompressionType::kNone);
 
+    SkTArray<size_t> individualMipOffsets(numMipLevels);
+    SkDEBUGCODE(size_t combinedBufferSize =) GrCompressedDataSize(compressionType, dimensions,
+                                                                  &individualMipOffsets, mipMapped);
+    SkASSERT(individualMipOffsets.count() == numMipLevels);
+    SkASSERT(dataSize == combinedBufferSize);
+
     size_t bufferOffset;
     id<MTLBuffer> transferBuffer = this->resourceProvider().getDynamicBuffer(dataSize,
                                                                              &bufferOffset);
@@ -564,19 +580,32 @@
     MTLOrigin origin = MTLOriginMake(0, 0, 0);
 
     id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
-    const size_t rowBytes = GrCompressedRowBytes(compressionType, dimensions.width());
 
     // copy data into the buffer, skipping any trailing bytes
     memcpy(buffer, data, dataSize);
-    [blitCmdEncoder copyFromBuffer: transferBuffer
-                      sourceOffset: bufferOffset
-                 sourceBytesPerRow: rowBytes
-               sourceBytesPerImage: dataSize
-                        sourceSize: MTLSizeMake(dimensions.width(), dimensions.height(), 1)
-                         toTexture: mtlTexture
-                  destinationSlice: 0
-                  destinationLevel: 0
-                 destinationOrigin: origin];
+
+    size_t bytesPerPixel = fMtlCaps->bytesPerPixel(mtlPixelFormat);
+
+    SkISize levelDimensions = dimensions;
+    for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
+        size_t trimRowBytes = levelDimensions.width() * bytesPerPixel;
+        size_t levelSize = trimRowBytes * levelDimensions.height();
+
+        // TODO: can this all be done in one go?
+        [blitCmdEncoder copyFromBuffer: transferBuffer
+                          sourceOffset: individualMipOffsets[currentMipLevel]
+                     sourceBytesPerRow: trimRowBytes
+                   sourceBytesPerImage: levelSize
+                            sourceSize: MTLSizeMake(levelDimensions.width(),
+                                                    levelDimensions.height(), 1)
+                             toTexture: mtlTexture
+                      destinationSlice: 0
+                      destinationLevel: currentMipLevel
+                     destinationOrigin: origin];
+
+        levelDimensions = {SkTMax(1, levelDimensions.width() /2),
+                           SkTMax(1, levelDimensions.height()/2)};
+    }
 #ifdef SK_BUILD_FOR_MAC
     [transferBuffer didModifyRange: NSMakeRange(bufferOffset, dataSize)];
 #endif
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 35878e9..2847778 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -870,18 +870,32 @@
 
 // It's probably possible to roll this into uploadTexDataOptimal,
 // but for now it's easier to maintain as a separate entity.
-bool GrVkGpu::uploadTexDataCompressed(GrVkTexture* tex, int left, int top, int width, int height,
-                                      const void* data, size_t dataSize) {
+bool GrVkGpu::uploadTexDataCompressed(GrVkTexture* tex, VkFormat vkFormat, SkISize dimensions,
+                                      GrMipMapped mipMapped, const void* data, size_t dataSize) {
     SkASSERT(data);
     SkASSERT(!tex->isLinearTiled());
     // For now the assumption is that our rect is the entire texture.
     // Compressed textures are read-only so this should be a reasonable assumption.
-    SkASSERT(0 == left && 0 == top && width == tex->width() && height == tex->height());
+    SkASSERT(dimensions.fWidth == tex->width() && dimensions.fHeight == tex->height());
 
-    if (width == 0 || height == 0) {
+    if (dimensions.fWidth == 0 || dimensions.fHeight  == 0) {
         return false;
     }
 
+    SkImage::CompressionType compression = GrVkFormatToCompressionType(vkFormat);
+
+    int numMipLevels = 1;
+    if (mipMapped == GrMipMapped::kYes) {
+        numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
+    }
+
+    SkTArray<size_t> individualMipOffsets(numMipLevels);
+
+    SkDEBUGCODE(size_t combinedBufferSize =) GrCompressedDataSize(compression, dimensions,
+                                                                  &individualMipOffsets, mipMapped);
+    SkASSERT(individualMipOffsets.count() == numMipLevels);
+    SkASSERT(dataSize == combinedBufferSize);
+
     SkASSERT(this->vkCaps().isVkFormatTexturable(tex->imageFormat()));
 
     // allocate buffer to hold our mip data
@@ -891,22 +905,31 @@
         return false;
     }
 
-    int uploadLeft = left;
-    int uploadTop = top;
     GrVkTexture* uploadTexture = tex;
 
     char* buffer = (char*)transferBuffer->map();
 
     memcpy(buffer, data, dataSize);
 
-    VkBufferImageCopy region;
-    memset(&region, 0, sizeof(VkBufferImageCopy));
-    region.bufferOffset = transferBuffer->offset();
-    region.bufferRowLength = width;
-    region.bufferImageHeight = height;
-    region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
-    region.imageOffset = { uploadLeft, uploadTop, 0 };
-    region.imageExtent = { SkToU32(width), SkToU32(height), 1 };
+    SkTArray<VkBufferImageCopy> regions(numMipLevels);
+
+    SkISize levelDimensions = dimensions;
+    for (int i = 0; i < numMipLevels; ++i) {
+        // Submit copy command
+        VkBufferImageCopy& region = regions.push_back();
+        memset(&region, 0, sizeof(VkBufferImageCopy));
+        region.bufferOffset = transferBuffer->offset() + individualMipOffsets[i];
+        SkISize revisedDimensions = GrCompressedDimensions(compression, levelDimensions);
+        region.bufferRowLength = revisedDimensions.width();
+        region.bufferImageHeight = revisedDimensions.height();
+        region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, SkToU32(i), 0, 1};
+        region.imageOffset = {0, 0, 0};
+        region.imageExtent = {SkToU32(levelDimensions.width()),
+                              SkToU32(levelDimensions.height()), 1};
+
+        levelDimensions = {SkTMax(1, levelDimensions.width() /2),
+                           SkTMax(1, levelDimensions.height()/2)};
+    }
 
     // no need to flush non-coherent memory, unmap will do that for us
     transferBuffer->unmap();
@@ -923,13 +946,14 @@
                                          transferBuffer.get(),
                                          uploadTexture,
                                          VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                                         1,
-                                         &region);
+                                         regions.count(),
+                                         regions.begin());
 
     return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// TODO: make this take a GrMipMapped
 sk_sp<GrTexture> GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc,
                                           const GrBackendFormat& format,
                                           GrRenderable renderable,
@@ -1017,7 +1041,7 @@
 
 sk_sp<GrTexture> GrVkGpu::onCreateCompressedTexture(SkISize dimensions,
                                                     const GrBackendFormat& format,
-                                                    SkBudgeted budgeted,
+                                                    SkBudgeted budgeted, GrMipMapped mipMapped,
                                                     const void* data, size_t dataSize) {
     VkFormat pixelFormat;
     SkAssertResult(format.asVkFormat(&pixelFormat));
@@ -1033,29 +1057,38 @@
     // texture.
     usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
 
+    int numMipLevels = 1;
+    if (mipMapped == GrMipMapped::kYes) {
+        numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height())+1;
+    }
+
     // Compressed textures with MIP levels or multiple samples are not supported as of now.
     GrVkImage::ImageDesc imageDesc;
     imageDesc.fImageType = VK_IMAGE_TYPE_2D;
     imageDesc.fFormat = pixelFormat;
     imageDesc.fWidth = dimensions.width();
     imageDesc.fHeight = dimensions.height();
-    imageDesc.fLevels = 1;
+    imageDesc.fLevels = numMipLevels;
     imageDesc.fSamples = 1;
     imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
     imageDesc.fUsageFlags = usageFlags;
     imageDesc.fIsProtected = GrProtected::kNo;
 
+    GrMipMapsStatus mipMapsStatus = (mipMapped == GrMipMapped::kYes)
+                                                                ? GrMipMapsStatus::kValid
+                                                                : GrMipMapsStatus::kNotAllocated;
+
     GrSurfaceDesc desc;
     desc.fConfig = this->vkCaps().getConfigFromCompressedBackendFormat(format);
     desc.fWidth = dimensions.width();
     desc.fHeight = dimensions.height();
     auto tex = GrVkTexture::MakeNewTexture(this, budgeted, desc, imageDesc,
-                                           GrMipMapsStatus::kNotAllocated);
+                                           mipMapsStatus);
     if (!tex) {
         return nullptr;
     }
 
-    if (!this->uploadTexDataCompressed(tex.get(), 0, 0, desc.fWidth, desc.fHeight,
+    if (!this->uploadTexDataCompressed(tex.get(), pixelFormat, dimensions, mipMapped,
                                        data, dataSize)) {
         return nullptr;
     }
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 9721cec..ae61252 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -194,7 +194,7 @@
                                      int mipLevelCount,
                                      uint32_t levelClearMask) override;
     sk_sp<GrTexture> onCreateCompressedTexture(SkISize dimensions, const GrBackendFormat&,
-                                               SkBudgeted, const void* data,
+                                               SkBudgeted, GrMipMapped, const void* data,
                                                size_t dataSize) override;
 
     sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrColorType, GrWrapOwnership,
@@ -264,8 +264,8 @@
                              GrColorType colorType, const void* data, size_t rowBytes);
     bool uploadTexDataOptimal(GrVkTexture* tex, int left, int top, int width, int height,
                               GrColorType colorType, const GrMipLevel texels[], int mipLevelCount);
-    bool uploadTexDataCompressed(GrVkTexture* tex, int left, int top, int width, int height,
-                                 const void* data, size_t dataSize);
+    bool uploadTexDataCompressed(GrVkTexture* tex, VkFormat vkFormat, SkISize dimensions,
+                                 GrMipMapped mipMapped, const void* data, size_t dataSize);
     void resolveImage(GrSurface* dst, GrVkRenderTarget* src, const SkIRect& srcRect,
                       const SkIPoint& dstPoint);