Add memoryless attachment support for vulkan dmsaa.

For this initial landing, we actually have the memoryless support
disabled because we need to update Chrome's vk memory allocator to
handle the new lazy flag added in this CL. Otherwise we will fail to
make dmsaa attachments and not draw anything.

I tested this on ARM and the it does look to keep the size of all the
lazy msaa attachments at 0. I test with both 4 and 8 sample counts. To
confirm the size check, I changed the store op on the msaa attachments
from discard to store and the reported memory size did grow.

Bug: skia:11809
Change-Id: I977f337b922cdbdbce16d67945369246e3547c17
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/451296
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/GrAttachment.cpp b/src/gpu/GrAttachment.cpp
index 08f1c73..29660589 100644
--- a/src/gpu/GrAttachment.cpp
+++ b/src/gpu/GrAttachment.cpp
@@ -22,7 +22,7 @@
     // the msaa and stencil attachments track their own size because they do get cached separately.
     // For all GrTexture* based things we will continue to to use the GrTexture* to report size and
     // the owned attachments will have no size and be uncached.
-    if (!(fSupportedUsages & UsageFlags::kTexture)) {
+    if (!(fSupportedUsages & UsageFlags::kTexture) && fMemoryless == GrMemoryless::kNo) {
         GrBackendFormat format = this->backendFormat();
         SkImage::CompressionType compression = GrBackendFormatToCompressionType(format);
 
@@ -41,12 +41,14 @@
                       GrAttachment::UsageFlags requiredUsage,
                       int sampleCnt,
                       GrMipmapped mipmapped,
-                      GrProtected isProtected) {
+                      GrProtected isProtected,
+                      GrMemoryless memoryless) {
     SkASSERT(!dimensions.isEmpty());
 
     SkASSERT(static_cast<uint32_t>(isProtected) <= 1);
+    SkASSERT(static_cast<uint32_t>(memoryless) <= 1);
     SkASSERT(static_cast<uint32_t>(requiredUsage) < (1u << 8));
-    SkASSERT(static_cast<uint32_t>(sampleCnt) < (1u << (32 - 9)));
+    SkASSERT(static_cast<uint32_t>(sampleCnt) < (1u << (32 - 10)));
 
     uint64_t formatKey = caps.computeFormatKey(format);
     (*builder)[0] = dimensions.width();
@@ -54,8 +56,9 @@
     (*builder)[2] = formatKey & 0xFFFFFFFF;
     (*builder)[3] = (formatKey >> 32) & 0xFFFFFFFF;
     (*builder)[4] = (static_cast<uint32_t>(isProtected) << 0) |
-                    (static_cast<uint32_t>(requiredUsage) << 1) |
-                    (static_cast<uint32_t>(sampleCnt) << 9);
+                    (static_cast<uint32_t>(memoryless) << 1) |
+                    (static_cast<uint32_t>(requiredUsage) << 2) |
+                    (static_cast<uint32_t>(sampleCnt) << 10);
 }
 
 void GrAttachment::ComputeSharedAttachmentUniqueKey(const GrCaps& caps,
@@ -65,11 +68,13 @@
                                                     int sampleCnt,
                                                     GrMipmapped mipmapped,
                                                     GrProtected isProtected,
+                                                    GrMemoryless memoryless,
                                                     GrUniqueKey* key) {
     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
 
     GrUniqueKey::Builder builder(key, kDomain, 5);
-    build_key(&builder, caps, format, dimensions, requiredUsage, sampleCnt, mipmapped, isProtected);
+    build_key(&builder, caps, format, dimensions, requiredUsage, sampleCnt, mipmapped, isProtected,
+              memoryless);
 }
 
 void GrAttachment::ComputeScratchKey(const GrCaps& caps,
@@ -79,18 +84,26 @@
                                      int sampleCnt,
                                      GrMipmapped mipmapped,
                                      GrProtected isProtected,
+                                     GrMemoryless memoryless,
                                      GrScratchKey* key) {
     static const GrScratchKey::ResourceType kType = GrScratchKey::GenerateResourceType();
 
     GrScratchKey::Builder builder(key, kType, 5);
-    build_key(&builder, caps, format, dimensions, requiredUsage, sampleCnt, mipmapped, isProtected);
+    build_key(&builder, caps, format, dimensions, requiredUsage, sampleCnt, mipmapped, isProtected,
+              memoryless);
 }
 
 void GrAttachment::computeScratchKey(GrScratchKey* key) const {
     if (!SkToBool(fSupportedUsages & UsageFlags::kStencilAttachment)) {
         auto isProtected = this->isProtected() ? GrProtected::kYes : GrProtected::kNo;
-        ComputeScratchKey(*this->getGpu()->caps(), this->backendFormat(), this->dimensions(),
-                          fSupportedUsages, this->numSamples(), this->mipmapped(), isProtected,
+        ComputeScratchKey(*this->getGpu()->caps(),
+                          this->backendFormat(),
+                          this->dimensions(),
+                          fSupportedUsages,
+                          this->numSamples(),
+                          this->mipmapped(),
+                          isProtected,
+                          fMemoryless,
                           key);
     }
 }
diff --git a/src/gpu/GrAttachment.h b/src/gpu/GrAttachment.h
index 03933a1..42d0702 100644
--- a/src/gpu/GrAttachment.h
+++ b/src/gpu/GrAttachment.h
@@ -54,6 +54,7 @@
                                                  int sampleCnt,
                                                  GrMipmapped mipmapped,
                                                  GrProtected isProtected,
+                                                 GrMemoryless memoryless,
                                                  GrUniqueKey* key);
 
     // TODO: Once attachments start having multiple usages, we'll need to figure out how to search
@@ -65,15 +66,18 @@
                                   int sampleCnt,
                                   GrMipmapped mipmapped,
                                   GrProtected,
+                                  GrMemoryless,
                                   GrScratchKey* key);
 
 protected:
     GrAttachment(GrGpu* gpu, SkISize dimensions, UsageFlags supportedUsages, int sampleCnt,
-                 GrMipmapped mipmapped, GrProtected isProtected)
+                 GrMipmapped mipmapped, GrProtected isProtected,
+                 GrMemoryless memoryless = GrMemoryless::kNo)
             : INHERITED(gpu, dimensions, isProtected)
             , fSupportedUsages(supportedUsages)
             , fSampleCnt(sampleCnt)
-            , fMipmapped(mipmapped) {}
+            , fMipmapped(mipmapped)
+            , fMemoryless(memoryless) {}
 
 private:
     size_t onGpuMemorySize() const final;
@@ -93,6 +97,7 @@
     int fSampleCnt;
     GrMipmapped fMipmapped;
     bool fHasPerformedInitialClear = false;
+    GrMemoryless fMemoryless;
 
     using INHERITED = GrSurface;
 };
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 404d3d5..152ddbc 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -619,7 +619,7 @@
         GrAttachment::ComputeSharedAttachmentUniqueKey(
                 *this->caps(), stencilFormat, rt->dimensions(),
                 GrAttachment::UsageFlags::kStencilAttachment, numStencilSamples, GrMipmapped::kNo,
-                isProtected, &sbKey);
+                isProtected, GrMemoryless::kNo, &sbKey);
         auto keyedStencil = this->findByUniqueKey<GrAttachment>(sbKey);
         if (!keyedStencil) {
             // Need to try and create a new stencil
@@ -641,7 +641,8 @@
 sk_sp<GrAttachment> GrResourceProvider::getDiscardableMSAAAttachment(SkISize dimensions,
                                                                      const GrBackendFormat& format,
                                                                      int sampleCnt,
-                                                                     GrProtected isProtected) {
+                                                                     GrProtected isProtected,
+                                                                     GrMemoryless memoryless) {
     ASSERT_SINGLE_OWNER
 
     SkASSERT(sampleCnt > 1);
@@ -667,13 +668,14 @@
                                                    sampleCnt,
                                                    GrMipmapped::kNo,
                                                    isProtected,
+                                                   memoryless,
                                                    &key);
     auto msaaAttachment = this->findByUniqueKey<GrAttachment>(key);
     if (msaaAttachment) {
         return msaaAttachment;
     }
     msaaAttachment = this->makeMSAAAttachment(dimensions, format, sampleCnt, isProtected,
-                                              GrMemoryless::kNo);
+                                              memoryless);
     if (msaaAttachment) {
         this->assignUniqueKeyToResource(key, msaaAttachment.get());
     }
@@ -684,7 +686,7 @@
                                                            const GrBackendFormat& format,
                                                            int sampleCnt,
                                                            GrProtected isProtected,
-                                                           GrMemoryless isMemoryless) {
+                                                           GrMemoryless memoryless) {
     ASSERT_SINGLE_OWNER
 
     SkASSERT(sampleCnt > 1);
@@ -702,18 +704,23 @@
         return nullptr;
     }
 
-    auto scratch = this->refScratchMSAAAttachment(dimensions, format, sampleCnt, isProtected);
+    auto scratch = this->refScratchMSAAAttachment(dimensions,
+                                                  format,
+                                                  sampleCnt,
+                                                  isProtected,
+                                                  memoryless);
     if (scratch) {
         return scratch;
     }
 
-    return fGpu->makeMSAAAttachment(dimensions, format, sampleCnt, isProtected, isMemoryless);
+    return fGpu->makeMSAAAttachment(dimensions, format, sampleCnt, isProtected, memoryless);
 }
 
 sk_sp<GrAttachment> GrResourceProvider::refScratchMSAAAttachment(SkISize dimensions,
                                                                  const GrBackendFormat& format,
                                                                  int sampleCnt,
-                                                                 GrProtected isProtected) {
+                                                                 GrProtected isProtected,
+                                                                 GrMemoryless memoryless) {
     ASSERT_SINGLE_OWNER
     SkASSERT(!this->isAbandoned());
     SkASSERT(!this->caps()->isFormatCompressed(format));
@@ -727,7 +734,7 @@
     GrScratchKey key;
     GrAttachment::ComputeScratchKey(*this->caps(), format, dimensions,
                                     GrAttachment::UsageFlags::kColorAttachment, sampleCnt,
-                                    GrMipmapped::kNo, isProtected, &key);
+                                    GrMipmapped::kNo, isProtected, memoryless, &key);
     GrGpuResource* resource = fCache->findAndRefScratchResource(key);
     if (resource) {
         fGpu->stats()->incNumScratchMSAAAttachmentsReused();
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 3feaafe..0490188 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -307,7 +307,8 @@
     sk_sp<GrAttachment> getDiscardableMSAAAttachment(SkISize dimensions,
                                                      const GrBackendFormat& format,
                                                      int sampleCnt,
-                                                     GrProtected isProtected);
+                                                     GrProtected isProtected,
+                                                     GrMemoryless memoryless);
 
     /**
      * Assigns a unique key to a resource. If the key is associated with another resource that
@@ -356,7 +357,8 @@
     sk_sp<GrAttachment> refScratchMSAAAttachment(SkISize dimensions,
                                                  const GrBackendFormat&,
                                                  int sampleCnt,
-                                                 GrProtected);
+                                                 GrProtected,
+                                                 GrMemoryless memoryless);
 
     // Used to perform any conversions necessary to texel data before creating a texture with
     // existing data or uploading to a scratch texture.
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index fd1716a..e743f9d 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -216,7 +216,7 @@
     fDynamicMSAAAttachment.reset(
             static_cast<GrGLAttachment*>(resourceProvider->getDiscardableMSAAAttachment(
                     this->dimensions(), this->backendFormat(), internalSampleCount,
-                    GrProtected(this->isProtected())).release()));
+                    GrProtected(this->isProtected()), GrMemoryless::kNo).release()));
     if (!fDynamicMSAAAttachment) {
         return false;
     }
diff --git a/src/gpu/vk/GrVkAMDMemoryAllocator.cpp b/src/gpu/vk/GrVkAMDMemoryAllocator.cpp
index f6e87c1..a742604 100644
--- a/src/gpu/vk/GrVkAMDMemoryAllocator.cpp
+++ b/src/gpu/vk/GrVkAMDMemoryAllocator.cpp
@@ -119,7 +119,7 @@
     }
 
     if (AllocationPropertyFlags::kLazyAllocation & flags) {
-        info.preferredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
+        info.requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
     }
 
     if (AllocationPropertyFlags::kProtected & flags) {
@@ -226,6 +226,9 @@
     if (!SkToBool(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & memFlags)) {
         flags |= GrVkAlloc::kNoncoherent_Flag;
     }
+    if (VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT & memFlags) {
+        flags |= GrVkAlloc::kLazilyAllocated_Flag;
+    }
 
     alloc->fMemory        = vmaInfo.deviceMemory;
     alloc->fOffset        = vmaInfo.offset;
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 9215dde..8e26df5 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -396,6 +396,9 @@
     // On ARM devices we are seeing an average perf win of around 50%-60% across the board.
     if (kARM_VkVendor == properties.vendorID) {
         fPreferDiscardableMSAAAttachment = true;
+        // TODO: Turn the memoryless back on once we update chrome's vk memory allocator to
+        // handle new memoryless flag.
+        //fSupportsMemorylessAttachments = true;
     }
 
     this->initGrCaps(vkInterface, physDev, properties, memoryProperties, features, extensions);
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 8391d6a..b331d44 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -267,6 +267,8 @@
 
     bool dmsaaResolveCanBeUsedAsTextureInSameRenderPass() const override { return false; }
 
+    bool supportsMemorylessAttachments() const { return fSupportsMemorylessAttachments; }
+
 #if GR_TEST_UTILS
     std::vector<TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
@@ -415,6 +417,7 @@
     bool fPreferDiscardableMSAAAttachment = false;
     bool fMustLoadFullImageWithDiscardableMSAA = false;
     bool fSupportsDiscardableMSAAForDMSAA = true;
+    bool fSupportsMemorylessAttachments = false;
 
     uint32_t fMaxDrawIndirectDrawCount = 0;
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index ee93679..a8d4954 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1487,17 +1487,14 @@
                                                 const GrBackendFormat& format,
                                                 int numSamples,
                                                 GrProtected isProtected,
-                                                GrMemoryless isMemoryless) {
-    // TODO: add memoryless support
-    SkASSERT(isMemoryless == GrMemoryless::kNo);
-
+                                                GrMemoryless memoryless) {
     VkFormat pixelFormat;
     SkAssertResult(format.asVkFormat(&pixelFormat));
     SkASSERT(!GrVkFormatIsCompressed(pixelFormat));
     SkASSERT(this->vkCaps().isFormatRenderable(pixelFormat, numSamples));
 
     fStats.incMSAAAttachmentCreates();
-    return GrVkImage::MakeMSAA(this, dimensions, numSamples, pixelFormat, isProtected);
+    return GrVkImage::MakeMSAA(this, dimensions, numSamples, pixelFormat, isProtected, memoryless);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp
index 6a26410..c9c7a7f 100644
--- a/src/gpu/vk/GrVkImage.cpp
+++ b/src/gpu/vk/GrVkImage.cpp
@@ -29,6 +29,7 @@
                            /*mipLevels=*/1,
                            vkUsageFlags,
                            GrProtected::kNo,
+                           GrMemoryless::kNo,
                            SkBudgeted::kYes);
 }
 
@@ -36,12 +37,16 @@
                                      SkISize dimensions,
                                      int numSamples,
                                      VkFormat format,
-                                     GrProtected isProtected) {
+                                     GrProtected isProtected,
+                                     GrMemoryless memoryless) {
     SkASSERT(numSamples > 1);
 
-    VkImageUsageFlags vkUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
-                                     VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
-                                     VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    VkImageUsageFlags vkUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    if (memoryless == GrMemoryless::kYes) {
+        vkUsageFlags |= VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
+    } else {
+        vkUsageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    }
     return GrVkImage::Make(gpu,
                            dimensions,
                            UsageFlags::kColorAttachment,
@@ -50,6 +55,7 @@
                            /*mipLevels=*/1,
                            vkUsageFlags,
                            isProtected,
+                           memoryless,
                            SkBudgeted::kYes);
 }
 
@@ -79,6 +85,7 @@
                            mipLevels,
                            vkUsageFlags,
                            isProtected,
+                           GrMemoryless::kNo,
                            budgeted);
 }
 
@@ -128,6 +135,7 @@
                                  uint32_t mipLevels,
                                  VkImageUsageFlags vkUsageFlags,
                                  GrProtected isProtected,
+                                 GrMemoryless memoryless,
                                  SkBudgeted budgeted) {
     GrVkImage::ImageDesc imageDesc;
     imageDesc.fImageType = VK_IMAGE_TYPE_2D;
@@ -209,7 +217,9 @@
                        supportedUsages,
                        info.fSampleCount,
                        info.fLevelCount > 1 ? GrMipmapped::kYes : GrMipmapped::kNo,
-                       info.fProtected)
+                       info.fProtected,
+                       info.fAlloc.fFlags & GrVkAlloc::kLazilyAllocated_Flag ? GrMemoryless::kYes
+                                                                             : GrMemoryless::kNo)
         , fInfo(info)
         , fInitialQueueFamily(info.fCurrentQueueFamily)
         , fMutableState(std::move(mutableState))
@@ -221,15 +231,15 @@
 }
 
 GrVkImage::GrVkImage(GrVkGpu* gpu,
-                               SkISize dimensions,
-                               UsageFlags supportedUsages,
-                               const GrVkImageInfo& info,
-                               sk_sp<GrBackendSurfaceMutableStateImpl> mutableState,
-                               sk_sp<const GrVkImageView> framebufferView,
-                               sk_sp<const GrVkImageView> textureView,
-                               GrBackendObjectOwnership ownership,
-                               GrWrapCacheable cacheable,
-                               bool forSecondaryCB)
+                     SkISize dimensions,
+                     UsageFlags supportedUsages,
+                     const GrVkImageInfo& info,
+                     sk_sp<GrBackendSurfaceMutableStateImpl> mutableState,
+                     sk_sp<const GrVkImageView> framebufferView,
+                     sk_sp<const GrVkImageView> textureView,
+                     GrBackendObjectOwnership ownership,
+                     GrWrapCacheable cacheable,
+                     bool forSecondaryCB)
         : GrAttachment(gpu,
                        dimensions,
                        supportedUsages,
@@ -253,8 +263,15 @@
     if (fInfo.fImageUsageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
         SkASSERT(SkToBool(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT));
     } else {
-        SkASSERT(SkToBool(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) &&
-                 SkToBool(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
+        if (fInfo.fAlloc.fFlags & GrVkAlloc::kLazilyAllocated_Flag) {
+            SkASSERT(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT);
+            SkASSERT(!SkToBool(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) &&
+                     !SkToBool(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
+        } else {
+            SkASSERT(!SkToBool(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT));
+            SkASSERT(SkToBool(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) &&
+                     SkToBool(fInfo.fImageUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
+        }
     }
     // We can't transfer from the non graphics queue to the graphics queue since we can't
     // release the image from the original queue without having that queue. This limits us in terms
@@ -354,6 +371,15 @@
                                             VkPipelineStageFlags dstStageMask,
                                             bool byRegion,
                                             uint32_t newQueueFamilyIndex) {
+// Enable the following block to test new devices to confirm their lazy images stay at 0 memory use.
+#if 0
+    if (fInfo.fAlloc.fFlags & GrVkAlloc::kLazilyAllocated_Flag) {
+        VkDeviceSize size;
+        VK_CALL(gpu, GetDeviceMemoryCommitment(gpu->device(), fInfo.fAlloc.fMemory, &size));
+
+        SkDebugf("Lazy Image. This: %p, image: %d, size: %d\n", this, fInfo.fImage, size);
+    }
+#endif
     SkASSERT(!gpu->isDeviceLost());
     SkASSERT(newLayout == this->currentLayout() ||
              (VK_IMAGE_LAYOUT_UNDEFINED != newLayout &&
@@ -438,8 +464,6 @@
     if ((imageDesc.fIsProtected == GrProtected::kYes) && !gpu->vkCaps().supportsProtectedMemory()) {
         return false;
     }
-    VkImage image = VK_NULL_HANDLE;
-    GrVkAlloc alloc;
 
     bool isLinear = VK_IMAGE_TILING_LINEAR == imageDesc.fImageTiling;
     VkImageLayout initialLayout = isLinear ? VK_IMAGE_LAYOUT_PREINITIALIZED
@@ -476,13 +500,20 @@
         initialLayout                                // initialLayout
     };
 
+    VkImage image = VK_NULL_HANDLE;
     VkResult result;
     GR_VK_CALL_RESULT(gpu, result, CreateImage(gpu->device(), &imageCreateInfo, nullptr, &image));
     if (result != VK_SUCCESS) {
         return false;
     }
 
-    if (!GrVkMemory::AllocAndBindImageMemory(gpu, image, &alloc)) {
+    GrMemoryless memoryless = imageDesc.fUsageFlags & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT
+                                      ? GrMemoryless::kYes
+                                      : GrMemoryless::kNo;
+    GrVkAlloc alloc;
+    if (!GrVkMemory::AllocAndBindImageMemory(gpu, image, memoryless, &alloc) ||
+        (memoryless == GrMemoryless::kYes &&
+         !SkToBool(alloc.fFlags & GrVkAlloc::kLazilyAllocated_Flag))) {
         VK_CALL(gpu, DestroyImage(gpu->device(), image, nullptr));
         return false;
     }
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index 789fa16..b28a986 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -39,7 +39,8 @@
                                      SkISize dimensions,
                                      int numSamples,
                                      VkFormat format,
-                                     GrProtected isProtected);
+                                     GrProtected isProtected,
+                                     GrMemoryless memoryless);
 
     static sk_sp<GrVkImage> MakeTexture(GrVkGpu* gpu,
                                         SkISize dimensions,
@@ -215,6 +216,7 @@
                                  uint32_t mipLevels,
                                  VkImageUsageFlags vkUsageFlags,
                                  GrProtected isProtected,
+                                 GrMemoryless,
                                  SkBudgeted);
 
     GrVkImage(GrVkGpu* gpu,
diff --git a/src/gpu/vk/GrVkMemory.cpp b/src/gpu/vk/GrVkMemory.cpp
index 610e722..6e82ec3 100644
--- a/src/gpu/vk/GrVkMemory.cpp
+++ b/src/gpu/vk/GrVkMemory.cpp
@@ -57,6 +57,7 @@
 
 bool GrVkMemory::AllocAndBindImageMemory(GrVkGpu* gpu,
                                          VkImage image,
+                                         GrMemoryless memoryless,
                                          GrVkAlloc* alloc) {
     GrVkMemoryAllocator* allocator = gpu->memoryAllocator();
     GrVkBackendMemory memory = 0;
@@ -79,6 +80,10 @@
         propFlags |= AllocationPropertyFlags::kProtected;
     }
 
+    if (memoryless == GrMemoryless::kYes) {
+        propFlags |= AllocationPropertyFlags::kLazyAllocation;
+    }
+
     VkResult result = allocator->allocateImageMemory(image, propFlags, &memory);
     if (!gpu->checkVkResult(result)) {
         return false;
diff --git a/src/gpu/vk/GrVkMemory.h b/src/gpu/vk/GrVkMemory.h
index 37947b2..4e678dd 100644
--- a/src/gpu/vk/GrVkMemory.h
+++ b/src/gpu/vk/GrVkMemory.h
@@ -10,6 +10,7 @@
 
 #include "include/gpu/vk/GrVkMemoryAllocator.h"
 #include "include/gpu/vk/GrVkTypes.h"
+#include "include/private/GrTypesPriv.h"
 #include "include/private/SkTArray.h"
 
 class GrVkGpu;
@@ -27,6 +28,7 @@
 
     bool AllocAndBindImageMemory(GrVkGpu* gpu,
                                  VkImage image,
+                                 GrMemoryless,
                                  GrVkAlloc* alloc);
     void FreeImageMemory(const GrVkGpu* gpu, const GrVkAlloc& alloc);
 
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index eebe423..4e7a729 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -226,11 +226,15 @@
 
     const GrBackendFormat& format = nonMSAAColorAttachment->backendFormat();
 
+    GrMemoryless memoryless =
+            gpu->vkCaps().supportsMemorylessAttachments() ? GrMemoryless::kYes : GrMemoryless::kNo;
+
     sk_sp<GrAttachment> msaaAttachment =
             rp->getDiscardableMSAAAttachment(nonMSAAColorAttachment->dimensions(),
                                              format,
                                              gpu->caps()->internalMultisampleCount(format),
-                                             GrProtected(nonMSAAColorAttachment->isProtected()));
+                                             GrProtected(nonMSAAColorAttachment->isProtected()),
+                                             memoryless);
     if (!msaaAttachment) {
         return nullptr;
     }