Add automatic generation of mipmaps to Vulkan

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1916563002

Review URL: https://codereview.chromium.org/1916563002
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 5b62a37..b0f3acf 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -20,7 +20,7 @@
     /**************************************************************************
     * GrDrawTargetCaps fields
     **************************************************************************/
-    fMipMapSupport = false; //TODO: figure this out
+    fMipMapSupport = true;   // always available in Vulkan
     fNPOTTextureTileSupport = false; //TODO: figure this out
     fTwoSidedStencilSupport = false; //TODO: figure this out
     fStencilWrapOpsSupport = false; //TODO: figure this out
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index 94d4c9d..5bd43f2 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -260,21 +260,21 @@
 }
 
 void GrVkCommandBuffer::blitImage(const GrVkGpu* gpu,
-                                  GrVkImage* srcImage,
+                                  const GrVkImage::Resource* srcImage,
                                   VkImageLayout srcLayout,
-                                  GrVkImage* dstImage,
+                                  const GrVkImage::Resource*  dstImage,
                                   VkImageLayout dstLayout,
                                   uint32_t blitRegionCount,
                                   const VkImageBlit* blitRegions,
                                   VkFilter filter) {
     SkASSERT(fIsActive);
     SkASSERT(!fActiveRenderPass);
-    this->addResource(srcImage->resource());
-    this->addResource(dstImage->resource());
+    this->addResource(srcImage);
+    this->addResource(dstImage);
     GR_VK_CALL(gpu->vkInterface(), CmdBlitImage(fCmdBuffer,
-                                                srcImage->textureImage(),
+                                                srcImage->fImage,
                                                 srcLayout,
-                                                dstImage->textureImage(),
+                                                dstImage->fImage,
                                                 dstLayout,
                                                 blitRegionCount,
                                                 blitRegions,
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index 1b27c55..b2f6ef0 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -127,9 +127,9 @@
                    const VkImageCopy* copyRegions);
 
     void blitImage(const GrVkGpu* gpu,
-                   GrVkImage* srcImage,
+                   const GrVkImage::Resource* srcImage,
                    VkImageLayout srcLayout,
-                   GrVkImage* dstImage,
+                   const GrVkImage::Resource* dstImage,
                    VkImageLayout dstLayout,
                    uint32_t blitRegionCount,
                    const VkImageBlit* blitRegions,
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 4d0ecbf..76fe92a 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -469,7 +469,7 @@
     // For now we will set the VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT and
     // VK_IMAGE_USAGE_TRANSFER_SOURCE_BIT on every texture since we do not know whether or not we
     // will be using this texture in some copy or not. Also this assumes, as is the current case,
-    // that all render targets in vulkan are also texutres. If we change this practice of setting
+    // that all render targets in vulkan are also textures. If we change this practice of setting
     // both bits, we must make sure to set the destination bit if we are uploading srcData to the
     // texture.
     usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
@@ -478,14 +478,14 @@
                                                            VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
 
     // This ImageDesc refers to the texture that will be read by the client. Thus even if msaa is
-    // requested, this ImageDesc describes the resolved texutre. Therefore we always have samples set
+    // requested, this ImageDesc describes the resolved texture. Therefore we always have samples set
     // to 1.
     GrVkImage::ImageDesc imageDesc;
     imageDesc.fImageType = VK_IMAGE_TYPE_2D;
     imageDesc.fFormat = pixelFormat;
     imageDesc.fWidth = desc.fWidth;
     imageDesc.fHeight = desc.fHeight;
-    imageDesc.fLevels = 1;
+    imageDesc.fLevels = 1;  // TODO: support miplevels for optimal tiling
     imageDesc.fSamples = 1;
     imageDesc.fImageTiling = linearTiling ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
     imageDesc.fUsageFlags = usageFlags;
@@ -606,6 +606,112 @@
     return tgt;
 }
 
+void GrVkGpu::generateMipmap(GrVkTexture* tex) const {
+    // don't need to do anything for linearly tiled textures (can't have mipmaps)
+    if (tex->isLinearTiled()) {
+        return;
+    }
+
+    // We cannot generate mipmaps for images that are multisampled.
+    // TODO: does it even make sense for rendertargets in general?
+    if (tex->asRenderTarget() && tex->asRenderTarget()->numColorSamples() > 1) {
+        return;
+    }
+
+    // determine if we can blit to and from this format
+    const GrVkCaps& caps = this->vkCaps();
+    if (!caps.configCanBeDstofBlit(tex->config(), false) ||
+        !caps.configCanBeSrcofBlit(tex->config(), false)) {
+        return;
+    }
+
+    // change the original image's layout
+    VkImageLayout origSrcLayout = tex->currentLayout();
+    VkPipelineStageFlags srcStageMask = GrVkMemory::LayoutToPipelineStageFlags(origSrcLayout);
+    VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+    VkAccessFlags srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(origSrcLayout);
+    VkAccessFlags dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+
+    tex->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                        srcAccessMask, dstAccessMask, srcStageMask, dstStageMask, false);
+
+    // grab handle to the original image resource
+    const GrVkImage::Resource* oldResource = tex->resource();
+    oldResource->ref();
+
+    if (!tex->reallocForMipmap(this)) {
+        oldResource->unref(this);
+        return;
+    }
+
+    // change the new image's layout
+    VkImageLayout origDstLayout = tex->currentLayout();
+
+    srcStageMask = GrVkMemory::LayoutToPipelineStageFlags(origDstLayout);
+    dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+    srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(origDstLayout);
+    dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+    tex->setImageLayout(this,
+                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                        srcAccessMask,
+                        dstAccessMask,
+                        srcStageMask,
+                        dstStageMask,
+                        false);
+
+    // Blit original image
+    int width = tex->width();
+    int height = tex->height();
+    uint32_t mipLevel = 0;
+
+    VkImageBlit blitRegion;
+    memset(&blitRegion, 0, sizeof(VkImageBlit));
+    blitRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
+    blitRegion.srcOffsets[0] = { 0, 0, 0 };
+    blitRegion.srcOffsets[1] = { width, height, 0 };
+    blitRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, mipLevel, 0, 1 };
+    blitRegion.dstOffsets[0] = { 0, 0, 0 };
+    blitRegion.dstOffsets[1] = { width, height, 0 };
+
+    fCurrentCmdBuffer->blitImage(this,
+                                 oldResource,
+                                 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                                 tex->resource(),
+                                 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                                 1,
+                                 &blitRegion,
+                                 VK_FILTER_LINEAR);
+    // Blit the miplevels
+    while (width/2 > 0 && height/2 > 0) {
+        blitRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, mipLevel, 0, 1 };
+        blitRegion.srcOffsets[0] = { 0, 0, 0 };
+        blitRegion.srcOffsets[1] = { width, height, 0 };
+        blitRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, mipLevel+1, 0, 1 };
+        blitRegion.dstOffsets[0] = { 0, 0, 0 };
+        blitRegion.dstOffsets[1] = { width/2, height/2, 0 };
+
+        fCurrentCmdBuffer->blitImage(this,
+                                     tex->resource(),
+                                     VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                                     tex->resource(),
+                                     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                                     1,
+                                     &blitRegion,
+                                     VK_FILTER_LINEAR);
+
+        width /= 2;
+        height /= 2;
+        mipLevel++;
+    }
+
+    oldResource->unref(this);
+}
+
+
+
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrVkGpu::bindGeometry(const GrPrimitiveProcessor& primProc,
@@ -1268,9 +1374,9 @@
     blitRegion.dstOffsets[1] = { dstRect.fRight, dstRect.fBottom, 0 };
 
     fCurrentCmdBuffer->blitImage(this,
-                                 srcImage,
+                                 srcImage->resource(),
                                  VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-                                 dstImage,
+                                 dstImage->resource(),
                                  VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                  1,
                                  &blitRegion,
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index da9d8b6..9c8a48c 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -111,6 +111,8 @@
 
     void finishDrawTarget() override;
 
+    void generateMipmap(GrVkTexture* tex) const;
+
 private:
     GrVkGpu(GrContext* context, const GrContextOptions& options,
             const GrVkBackendContext* backendContext);
diff --git a/src/gpu/vk/GrVkImageView.cpp b/src/gpu/vk/GrVkImageView.cpp
index 1ab2475..d94aeec 100644
--- a/src/gpu/vk/GrVkImageView.cpp
+++ b/src/gpu/vk/GrVkImageView.cpp
@@ -9,8 +9,8 @@
 #include "GrVkGpu.h"
 #include "GrVkUtil.h"
 
-const GrVkImageView* GrVkImageView::Create(GrVkGpu* gpu, VkImage image, VkFormat format,
-                                           Type viewType) {
+const GrVkImageView* GrVkImageView::Create(const GrVkGpu* gpu, VkImage image, VkFormat format,
+                                           Type viewType, uint32_t miplevels) {
     VkImageView imageView;
 
     // Create the VkImageView
@@ -23,7 +23,7 @@
         format,                                                 // format
         { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
           VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A },     // components
-        { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },              // subresourceRange
+        { VK_IMAGE_ASPECT_COLOR_BIT, 0, miplevels, 0, 1 },      // subresourceRange
     };
     if (kStencil_Type == viewType) {
         viewInfo.components.r = VK_COMPONENT_SWIZZLE_ZERO;
diff --git a/src/gpu/vk/GrVkImageView.h b/src/gpu/vk/GrVkImageView.h
index f700808..4be0d19 100644
--- a/src/gpu/vk/GrVkImageView.h
+++ b/src/gpu/vk/GrVkImageView.h
@@ -21,7 +21,8 @@
         kStencil_Type
     };
 
-    static const GrVkImageView* Create(GrVkGpu* gpu, VkImage image, VkFormat format, Type viewType);
+    static const GrVkImageView* Create(const GrVkGpu* gpu, VkImage image, VkFormat format, 
+                                       Type viewType, uint32_t miplevels);
 
     VkImageView imageView() const { return fImageView; }
 
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 0194e29..4fe2929 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -8,6 +8,7 @@
 #include "GrVkPipelineState.h"
 
 #include "GrPipeline.h"
+#include "GrTexturePriv.h"
 #include "GrVkCommandBuffer.h"
 #include "GrVkDescriptorPool.h"
 #include "GrVkGpu.h"
@@ -21,6 +22,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
 #include "glsl/GrGLSLXferProcessor.h"
+#include "SkMipmap.h"
 
 GrVkPipelineState::GrVkPipelineState(GrVkGpu* gpu,
                                      const GrVkPipelineState::Desc& desc,
@@ -290,9 +292,17 @@
 
     for (int i = 0; i < textureBindings.count(); ++i) {
         const GrTextureParams& params = textureBindings[i]->getParams();
-        fSamplers.push(gpu->resourceProvider().findOrCreateCompatibleSampler(params));
 
         GrVkTexture* texture = static_cast<GrVkTexture*>(textureBindings[i]->getTexture());
+        if (GrTextureParams::kMipMap_FilterMode == params.filterMode()) {
+            if (texture->texturePriv().mipMapsAreDirty()) {
+                gpu->generateMipmap(texture);
+                texture->texturePriv().dirtyMipMaps(false);
+            }
+        }
+
+        fSamplers.push(gpu->resourceProvider().findOrCreateCompatibleSampler(params,
+                                                          texture->texturePriv().maxMipMapLevel()));
 
         const GrVkImage::Resource* textureResource = texture->resource();
         textureResource->ref();
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index 8ad3968..46099f2 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -145,7 +145,7 @@
 
         // Create Resolve attachment view
         resolveAttachmentView = GrVkImageView::Create(gpu, imageResource->fImage, pixelFormat,
-                                                      GrVkImageView::kColor_Type);
+                                                      GrVkImageView::kColor_Type, 1);
         if (!resolveAttachmentView) {
             msaaResource->unref(gpu);
             return nullptr;
@@ -157,7 +157,7 @@
 
     // Get color attachment view
     const GrVkImageView* colorAttachmentView = GrVkImageView::Create(gpu, colorImage, pixelFormat,
-                                                                     GrVkImageView::kColor_Type);
+                                                                     GrVkImageView::kColor_Type, 1);
     if (!colorAttachmentView) {
         if (msaaResource) {
             resolveAttachmentView->unref(gpu);
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 7def55d..8ba5ade 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -85,10 +85,11 @@
     return new GrVkDescriptorPool(fGpu, type, count);
 }
 
-GrVkSampler* GrVkResourceProvider::findOrCreateCompatibleSampler(const GrTextureParams& params) {
-    GrVkSampler* sampler = fSamplers.find(GrVkSampler::GenerateKey(params));
+GrVkSampler* GrVkResourceProvider::findOrCreateCompatibleSampler(const GrTextureParams& params,
+                                                                 uint32_t mipLevels) {
+    GrVkSampler* sampler = fSamplers.find(GrVkSampler::GenerateKey(params, mipLevels));
     if (!sampler) {
-        sampler = GrVkSampler::Create(fGpu, params);
+        sampler = GrVkSampler::Create(fGpu, params, mipLevels);
         fSamplers.add(sampler);
     }
     SkASSERT(sampler);
@@ -136,7 +137,7 @@
     fSimpleRenderPasses.reset();
 
     // Iterate through all store GrVkSamplers and unref them before resetting the hash.
-    SkTDynamicHash<GrVkSampler, uint8_t>::Iter iter(&fSamplers);
+    SkTDynamicHash<GrVkSampler, uint16_t>::Iter iter(&fSamplers);
     for (; !iter.done(); ++iter) {
         (*iter).unref(fGpu);
     }
@@ -166,7 +167,7 @@
     fSimpleRenderPasses.reset();
 
     // Iterate through all store GrVkSamplers and unrefAndAbandon them before resetting the hash.
-    SkTDynamicHash<GrVkSampler, uint8_t>::Iter iter(&fSamplers);
+    SkTDynamicHash<GrVkSampler, uint16_t>::Iter iter(&fSamplers);
     for (; !iter.done(); ++iter) {
         (*iter).unrefAndAbandon();
     }
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 1fdf144..4853bf1 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -63,7 +63,7 @@
 
     // Finds or creates a compatible GrVkSampler based on the GrTextureParams.
     // The refcount is incremented and a pointer returned.
-    GrVkSampler* findOrCreateCompatibleSampler(const GrTextureParams&);
+    GrVkSampler* findOrCreateCompatibleSampler(const GrTextureParams&, uint32_t mipLevels);
 
     sk_sp<GrVkPipelineState> findOrCreateCompatiblePipelineState(const GrPipeline&,
                                                                  const GrPrimitiveProcessor&,
@@ -136,7 +136,7 @@
 
     // Stores GrVkSampler objects that we've already created so we can reuse them across multiple
     // GrVkPipelineStates
-    SkTDynamicHash<GrVkSampler, uint8_t> fSamplers;
+    SkTDynamicHash<GrVkSampler, uint16_t> fSamplers;
 
     // Cache of GrVkPipelineStates
     PipelineStateCache* fPipelineStateCache;
diff --git a/src/gpu/vk/GrVkSampler.cpp b/src/gpu/vk/GrVkSampler.cpp
index 75c2ee8..904a8b7 100644
--- a/src/gpu/vk/GrVkSampler.cpp
+++ b/src/gpu/vk/GrVkSampler.cpp
@@ -23,7 +23,8 @@
     return gWrapModes[tm];
 }
 
-GrVkSampler* GrVkSampler::Create(const GrVkGpu* gpu, const GrTextureParams& params) {
+GrVkSampler* GrVkSampler::Create(const GrVkGpu* gpu, const GrTextureParams& params, 
+                                 uint32_t mipLevels) {
 
     static VkFilter vkMinFilterModes[] = {
         VK_FILTER_NEAREST,
@@ -58,7 +59,7 @@
     // level mip). If the filters weren't the same we could set min = 0 and max = 0.25 to force
     // the minFilter on mip level 0.
     createInfo.minLod = 0.0f;
-    createInfo.maxLod = 0.0f;
+    createInfo.maxLod = (mipLevels == 1) ? 0.0f : (float)(mipLevels);
     createInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
     createInfo.unnormalizedCoordinates = VK_FALSE;
 
@@ -68,7 +69,7 @@
                                                           nullptr,
                                                           &sampler));
 
-    return new GrVkSampler(sampler, GenerateKey(params));
+    return new GrVkSampler(sampler, GenerateKey(params, mipLevels));
 }
 
 void GrVkSampler::freeGPUData(const GrVkGpu* gpu) const {
@@ -76,15 +77,21 @@
     GR_VK_CALL(gpu->vkInterface(), DestroySampler(gpu->device(), fSampler, nullptr));
 }
 
-uint8_t GrVkSampler::GenerateKey(const GrTextureParams& params) {
+uint16_t GrVkSampler::GenerateKey(const GrTextureParams& params, uint32_t mipLevels) {
+    const int kTileModeXShift = 2;
+    const int kTileModeYShift = 4;
+    const int kMipLevelShift = 6;
 
-    uint8_t key = params.filterMode();
+    uint16_t key = params.filterMode();
 
     SkASSERT(params.filterMode() <= 3);
-    key |= (params.getTileModeX() << 2);
+    key |= (params.getTileModeX() << kTileModeXShift);
 
     GR_STATIC_ASSERT(SkShader::kTileModeCount <= 4);
-    key |= (params.getTileModeY() << 4);
+    key |= (params.getTileModeY() << kTileModeYShift);
+
+    SkASSERT(mipLevels < 1024);
+    key |= (mipLevels << kMipLevelShift);
 
     return key;
 }
diff --git a/src/gpu/vk/GrVkSampler.h b/src/gpu/vk/GrVkSampler.h
index ec97751..2aa5017 100644
--- a/src/gpu/vk/GrVkSampler.h
+++ b/src/gpu/vk/GrVkSampler.h
@@ -19,22 +19,22 @@
 
 class GrVkSampler : public GrVkResource {
 public:
-    static GrVkSampler* Create(const GrVkGpu* gpu, const GrTextureParams&);
+    static GrVkSampler* Create(const GrVkGpu* gpu, const GrTextureParams&, uint32_t mipLevels);
 
     VkSampler sampler() const { return fSampler; }
 
     // Helpers for hashing GrVkSampler
-    static uint8_t GenerateKey(const GrTextureParams&);
+    static uint16_t GenerateKey(const GrTextureParams&, uint32_t mipLevels);
 
-    static const uint8_t& GetKey(const GrVkSampler& sampler) { return sampler.fKey; }
-    static uint32_t Hash(const uint8_t& key) { return key; }
+    static const uint16_t& GetKey(const GrVkSampler& sampler) { return sampler.fKey; }
+    static uint32_t Hash(const uint16_t& key) { return key; }
 private:
-    GrVkSampler(VkSampler sampler, uint8_t key) : INHERITED(), fSampler(sampler), fKey(key) {}
+    GrVkSampler(VkSampler sampler, uint16_t key) : INHERITED(), fSampler(sampler), fKey(key) {}
 
     void freeGPUData(const GrVkGpu* gpu) const override;
 
     VkSampler  fSampler;
-    uint8_t   fKey;
+    uint16_t   fKey;
 
     typedef GrVkResource INHERITED;
 };
diff --git a/src/gpu/vk/GrVkStencilAttachment.cpp b/src/gpu/vk/GrVkStencilAttachment.cpp
index 3af5488..a876aea 100644
--- a/src/gpu/vk/GrVkStencilAttachment.cpp
+++ b/src/gpu/vk/GrVkStencilAttachment.cpp
@@ -51,7 +51,7 @@
 
     const GrVkImageView* imageView = GrVkImageView::Create(gpu, imageResource->fImage,
                                                            format.fInternalFormat,
-                                                           GrVkImageView::kStencil_Type);
+                                                           GrVkImageView::kStencil_Type, 1);
     if (!imageView) {
         imageResource->unref(gpu);
         return nullptr;
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index cdbc84f..736d8e3 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -8,7 +8,9 @@
 #include "GrVkTexture.h"
 #include "GrVkGpu.h"
 #include "GrVkImageView.h"
+#include "GrTexturePriv.h"
 #include "GrVkUtil.h"
+#include "SkMipmap.h"
 
 #include "vk/GrVkTypes.h"
 
@@ -60,8 +62,14 @@
                                  VkFormat format,
                                  const GrVkImage::Resource* imageResource) {
     VkImage image = imageResource->fImage;
+
+    uint32_t mipLevels = 1;
+    // TODO: enable when mipLevel loading is implemented in GrVkGpu
+    //if (desc.fIsMipMapped) {
+    //    mipLevels = SkMipMap::ComputeLevelCount(this->width(), this->height());
+    //}
     const GrVkImageView* imageView = GrVkImageView::Create(gpu, image, format,
-                                                           GrVkImageView::kColor_Type);
+                                                           GrVkImageView::kColor_Type, mipLevels);
     if (!imageView) {
         return nullptr;
     }
@@ -157,3 +165,56 @@
     SkASSERT(!this->wasDestroyed());
     return static_cast<GrVkGpu*>(this->getGpu());
 }
+
+bool GrVkTexture::reallocForMipmap(const GrVkGpu* gpu) {
+    const GrVkImage::Resource* oldResource = fResource;
+
+    // Does this even make sense for rendertargets?
+    bool renderTarget = SkToBool(fDesc.fFlags & kRenderTarget_GrSurfaceFlag);
+
+    VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
+    if (renderTarget) {
+        usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    }
+    usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+
+    uint32_t mipLevels = SkMipMap::ComputeLevelCount(this->width(), this->height());
+    if (mipLevels == 1) {
+        // don't need to do anything for a 1x1 texture
+        return false;
+    }
+
+    GrVkImage::ImageDesc imageDesc;
+    imageDesc.fImageType = VK_IMAGE_TYPE_2D;
+    imageDesc.fFormat = oldResource->fFormat;
+    imageDesc.fWidth = fDesc.fWidth;
+    imageDesc.fHeight = fDesc.fHeight;
+    imageDesc.fLevels = mipLevels;
+    imageDesc.fSamples = 1;
+    imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
+    imageDesc.fUsageFlags = usageFlags;
+    imageDesc.fMemProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+
+    const GrVkImage::Resource* imageResource = GrVkImage::CreateResource(gpu, imageDesc);
+    if (!imageResource) {
+        return false;
+    }
+
+    // have to create a new image view for new resource
+    const GrVkImageView* oldView = fTextureView;
+    VkImage image = imageResource->fImage;
+    const GrVkImageView* textureView = GrVkImageView::Create(gpu, image, imageResource->fFormat,
+                                                             GrVkImageView::kColor_Type, mipLevels);
+    if (!textureView) {
+        imageResource->unref(gpu);
+        return false;
+    }
+
+    oldResource->unref(gpu);
+    oldView->unref(gpu);
+    fResource = imageResource;
+    fTextureView = textureView;
+    this->texturePriv().setMaxMipMapLevel(mipLevels);
+
+    return true;
+}
diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h
index 936eeb7..a5e82e8 100644
--- a/src/gpu/vk/GrVkTexture.h
+++ b/src/gpu/vk/GrVkTexture.h
@@ -33,6 +33,8 @@
 
     const GrVkImageView* textureView() const { return fTextureView; }
 
+    bool reallocForMipmap(const GrVkGpu* gpu);
+
 protected:
     GrVkTexture(GrVkGpu*, const GrSurfaceDesc&,
                 const GrVkImage::Resource*, const GrVkImageView* imageView);
diff --git a/src/gpu/vk/GrVkTextureRenderTarget.cpp b/src/gpu/vk/GrVkTextureRenderTarget.cpp
index 6bfdf3e..6742a91 100644
--- a/src/gpu/vk/GrVkTextureRenderTarget.cpp
+++ b/src/gpu/vk/GrVkTextureRenderTarget.cpp
@@ -12,6 +12,8 @@
 #include "GrVkImageView.h"
 #include "GrVkUtil.h"
 
+#include "SkMipmap.h"
+
 #include "vk/GrVkTypes.h"
 
 #define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
@@ -24,8 +26,13 @@
                                                          const GrVkImage::Resource* imageResource) {
     VkImage image = imageResource->fImage;
     // Create the texture ImageView
+    uint32_t mipLevels = 1;
+    //TODO: does a mipmapped textureRenderTarget make sense?
+    //if (desc.fIsMipMapped) {
+    //    mipLevels = SkMipMap::ComputeLevelCount(this->width(), this->height());
+    //}
     const GrVkImageView* imageView = GrVkImageView::Create(gpu, image, format,
-                                                           GrVkImageView::kColor_Type);
+                                                           GrVkImageView::kColor_Type, mipLevels);
     if (!imageView) {
         return nullptr;
     }
@@ -67,7 +74,7 @@
             resolveAttachmentView->ref();
         } else {
             resolveAttachmentView = GrVkImageView::Create(gpu, image, pixelFormat,
-                                                          GrVkImageView::kColor_Type);
+                                                          GrVkImageView::kColor_Type, 1);
             if (!resolveAttachmentView) {
                 msaaImageResource->unref(gpu);
                 imageView->unref(gpu);
@@ -88,7 +95,7 @@
         colorAttachmentView->ref();
     } else {
         colorAttachmentView = GrVkImageView::Create(gpu, colorImage, pixelFormat,
-                                                    GrVkImageView::kColor_Type);
+                                                    GrVkImageView::kColor_Type, 1);
         if (!colorAttachmentView) {
             if (msaaImageResource) {
                 resolveAttachmentView->unref(gpu);