Add GrMipMapsStatus to GrTextureProxy

Bug: skia:
Change-Id: I5adf10f730b04c2b3b62c3b12aa5899da4db3431
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/229922
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/core/SkDeferredDisplayListRecorder.cpp b/src/core/SkDeferredDisplayListRecorder.cpp
index aedff7f..19f157b 100644
--- a/src/core/SkDeferredDisplayListRecorder.cpp
+++ b/src/core/SkDeferredDisplayListRecorder.cpp
@@ -153,6 +153,7 @@
     if (usesGLFBO0) {
         surfaceFlags |= GrInternalSurfaceFlags::kGLRTFBOIDIs0;
     }
+    // FIXME: Why do we use GrMipMapped::kNo instead of SkSurfaceCharacterization::fIsMipMapped?
     static constexpr GrProxyProvider::TextureInfo kTextureInfo{GrMipMapped::kNo,
                                                                GrTextureType::k2D};
     const GrProxyProvider::TextureInfo* optionalTextureInfo = nullptr;
@@ -174,6 +175,7 @@
             fCharacterization.origin(),
             surfaceFlags,
             optionalTextureInfo,
+            GrMipMapsStatus::kNotAllocated,
             SkBackingFit::kExact,
             SkBudgeted::kYes,
             fCharacterization.isProtected(),
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.cpp b/src/gpu/GrAHardwareBufferImageGenerator.cpp
index 14e8478..72c4979 100644
--- a/src/gpu/GrAHardwareBufferImageGenerator.cpp
+++ b/src/gpu/GrAHardwareBufferImageGenerator.cpp
@@ -187,8 +187,8 @@
                 return std::move(tex);
             },
             backendFormat, desc, GrRenderable::kNo, 1, fSurfaceOrigin, GrMipMapped::kNo,
-            GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact, SkBudgeted::kNo,
-            GrProtected::kNo);
+            GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact,
+            SkBudgeted::kNo, GrProtected::kNo);
 
     return texProxy;
 }
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index f1d6c6e..0f0ec38 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -152,6 +152,11 @@
     desc.fConfig = config;
     GrMipMapped mipMapped = fBackendTexture.hasMipMaps() ? GrMipMapped::kYes : GrMipMapped::kNo;
 
+    // Ganesh assumes that, when wrapping a mipmapped backend texture from a client, that its
+    // mipmaps are fully fleshed out.
+    GrMipMapsStatus mipMapsStatus = fBackendTexture.hasMipMaps()
+            ? GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated;
+
     // Must make copies of member variables to capture in the lambda since this image generator may
     // be deleted before we actually execute the lambda.
     sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
@@ -194,7 +199,7 @@
                 // unrelated to the whatever SkImage key may be assigned to the proxy.
                 return {std::move(tex), GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced};
             },
-            backendFormat, desc, GrRenderable::kNo, 1, fSurfaceOrigin, mipMapped,
+            backendFormat, desc, GrRenderable::kNo, 1, fSurfaceOrigin, mipMapped, mipMapsStatus,
             GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact, SkBudgeted::kNo,
             GrProtected::kNo);
     if (!proxy) {
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index e70917c..3d0bace 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -263,7 +263,7 @@
                         GrResourceProvider::Flags::kNoPendingIO));
             },
             format, desc, renderable, sampleCnt, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
-            surfaceFlags, fit, budgeted, GrProtected::kNo);
+            GrMipMapsStatus::kNotAllocated, surfaceFlags, fit, budgeted, GrProtected::kNo);
 
     if (!proxy) {
         return nullptr;
@@ -390,7 +390,7 @@
                         texels.get(), mipLevelCount));
             },
             format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kYes,
-            SkBackingFit::kExact, SkBudgeted::kYes, GrProtected::kNo);
+            GrMipMapsStatus::kValid, SkBackingFit::kExact, SkBudgeted::kYes, GrProtected::kNo);
 
     if (!proxy) {
         return nullptr;
@@ -461,6 +461,9 @@
         return nullptr;
     }
     GrSurfaceDesc copyDesc = desc;
+    GrMipMapsStatus mipMapsStatus = (GrMipMapped::kYes == mipMapped)
+            ? GrMipMapsStatus::kDirty
+            : GrMipMapsStatus::kNotAllocated;
     GrSwizzle texSwizzle = caps->getTextureSwizzle(format, colorType);
     if (renderable == GrRenderable::kYes) {
         renderTargetSampleCnt =
@@ -469,12 +472,13 @@
         // both texturable and renderable
         GrSwizzle outSwizzle = caps->getOutputSwizzle(format, colorType);
         return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(
-                *caps, format, copyDesc, renderTargetSampleCnt, origin, mipMapped, texSwizzle,
-                outSwizzle, fit, budgeted, isProtected, surfaceFlags));
+                *caps, format, copyDesc, renderTargetSampleCnt, origin, mipMapped, mipMapsStatus,
+                texSwizzle, outSwizzle, fit, budgeted, isProtected, surfaceFlags));
     }
 
-    return sk_sp<GrTextureProxy>(new GrTextureProxy(format, copyDesc, origin, mipMapped, texSwizzle,
-                                                    fit, budgeted, isProtected, surfaceFlags));
+    return sk_sp<GrTextureProxy>(new GrTextureProxy(
+            format, copyDesc, origin, mipMapped, mipMapsStatus, texSwizzle, fit, budgeted,
+            isProtected, surfaceFlags));
 }
 
 sk_sp<GrTextureProxy> GrProxyProvider::createCompressedTextureProxy(
@@ -499,7 +503,8 @@
                         width, height, compressionType, budgeted, data.get()));
             },
             format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
-            SkBackingFit::kExact, SkBudgeted::kYes, GrProtected::kNo);
+            GrMipMapsStatus::kNotAllocated, SkBackingFit::kExact, SkBudgeted::kYes,
+            GrProtected::kNo);
 
     if (!proxy) {
         return nullptr;
@@ -729,11 +734,12 @@
                                                        int renderTargetSampleCnt,
                                                        GrSurfaceOrigin origin,
                                                        GrMipMapped mipMapped,
+                                                       GrMipMapsStatus mipMapsStatus,
                                                        SkBackingFit fit,
                                                        SkBudgeted budgeted,
                                                        GrProtected isProtected) {
     return this->createLazyProxy(std::move(callback), format, desc, renderable,
-                                 renderTargetSampleCnt, origin, mipMapped,
+                                 renderTargetSampleCnt, origin, mipMapped, mipMapsStatus,
                                  GrInternalSurfaceFlags::kNone, fit, budgeted, isProtected);
 }
 
@@ -744,6 +750,7 @@
                                                        int renderTargetSampleCnt,
                                                        GrSurfaceOrigin origin,
                                                        GrMipMapped mipMapped,
+                                                       GrMipMapsStatus mipMapsStatus,
                                                        GrInternalSurfaceFlags surfaceFlags,
                                                        SkBackingFit fit,
                                                        SkBudgeted budgeted,
@@ -752,8 +759,8 @@
     LazyInstantiationType lazyType = this->renderingDirectly() ? LazyInstantiationType::kSingleUse
                                                                : LazyInstantiationType::kMultipleUse;
     return this->createLazyProxy(std::move(callback), format, desc, renderable,
-                                 renderTargetSampleCnt, origin, mipMapped, surfaceFlags, fit,
-                                 budgeted, isProtected, lazyType);
+                                 renderTargetSampleCnt, origin, mipMapped, mipMapsStatus,
+                                 surfaceFlags, fit, budgeted, isProtected, lazyType);
 }
 
 sk_sp<GrTextureProxy> GrProxyProvider::createLazyProxy(LazyInstantiateCallback&& callback,
@@ -763,6 +770,7 @@
                                                        int renderTargetSampleCnt,
                                                        GrSurfaceOrigin origin,
                                                        GrMipMapped mipMapped,
+                                                       GrMipMapsStatus mipMapsStatus,
                                                        GrInternalSurfaceFlags surfaceFlags,
                                                        SkBackingFit fit,
                                                        SkBudgeted budgeted,
@@ -786,22 +794,22 @@
     GrSwizzle texSwizzle = this->caps()->getTextureSwizzle(format, colorType);
     GrSwizzle outSwizzle = this->caps()->getOutputSwizzle(format, colorType);
 
-    return sk_sp<GrTextureProxy>(
-            renderable == GrRenderable::kYes
-                    ? new GrTextureRenderTargetProxy(std::move(callback), lazyType, format, desc,
-                                                     renderTargetSampleCnt, origin, mipMapped,
-                                                     texSwizzle, outSwizzle, fit, budgeted,
-                                                     isProtected, surfaceFlags)
-                    : new GrTextureProxy(std::move(callback), lazyType, format, desc, origin,
-                                         mipMapped, texSwizzle, fit, budgeted, isProtected,
-                                         surfaceFlags));
+    return sk_sp<GrTextureProxy>((renderable == GrRenderable::kYes)
+            ? new GrTextureRenderTargetProxy(
+                    std::move(callback), lazyType, format, desc, renderTargetSampleCnt, origin,
+                    mipMapped, mipMapsStatus, texSwizzle, outSwizzle, fit, budgeted, isProtected,
+                    surfaceFlags)
+            : new GrTextureProxy(
+                    std::move(callback), lazyType, format, desc, origin, mipMapped, mipMapsStatus,
+                    texSwizzle, fit, budgeted, isProtected, surfaceFlags));
 }
 
 sk_sp<GrRenderTargetProxy> GrProxyProvider::createLazyRenderTargetProxy(
         LazyInstantiateCallback&& callback, const GrBackendFormat& format,
         const GrSurfaceDesc& desc, int sampleCnt, GrSurfaceOrigin origin,
-        GrInternalSurfaceFlags surfaceFlags, const TextureInfo* textureInfo, SkBackingFit fit,
-        SkBudgeted budgeted, GrProtected isProtected, bool wrapsVkSecondaryCB) {
+        GrInternalSurfaceFlags surfaceFlags, const TextureInfo* textureInfo,
+        GrMipMapsStatus mipMapsStatus, SkBackingFit fit, SkBudgeted budgeted,
+        GrProtected isProtected, bool wrapsVkSecondaryCB) {
     SkASSERT((desc.fWidth <= 0 && desc.fHeight <= 0) ||
              (desc.fWidth > 0 && desc.fHeight > 0));
 
@@ -827,8 +835,8 @@
         SkASSERT(!wrapsVkSecondaryCB);
         return sk_sp<GrRenderTargetProxy>(new GrTextureRenderTargetProxy(
                 std::move(callback), lazyType, format, desc, sampleCnt, origin,
-                textureInfo->fMipMapped, texSwizzle, outSwizzle, fit, budgeted, isProtected,
-                surfaceFlags));
+                textureInfo->fMipMapped, mipMapsStatus, texSwizzle, outSwizzle, fit, budgeted,
+                isProtected, surfaceFlags));
     }
 
     GrRenderTargetProxy::WrapsVkSecondaryCB vkSCB =
@@ -860,17 +868,16 @@
     GrSwizzle texSwizzle = caps.getTextureSwizzle(format, colorType);
     GrSwizzle outSwizzle = caps.getOutputSwizzle(format, colorType);
 
-    return sk_sp<GrTextureProxy>(
-            (GrRenderable::kYes == renderable)
-                    ? new GrTextureRenderTargetProxy(
-                              std::move(callback), LazyInstantiationType::kSingleUse, format, desc,
-                              renderTargetSampleCnt, origin, GrMipMapped::kNo, texSwizzle,
-                              outSwizzle, SkBackingFit::kApprox, SkBudgeted::kYes, isProtected,
-                              surfaceFlags)
-                    : new GrTextureProxy(std::move(callback), LazyInstantiationType::kSingleUse,
-                                         format, desc, origin, GrMipMapped::kNo, texSwizzle,
-                                         SkBackingFit::kApprox, SkBudgeted::kYes, isProtected,
-                                         surfaceFlags));
+    return sk_sp<GrTextureProxy>((GrRenderable::kYes == renderable)
+            ? new GrTextureRenderTargetProxy(
+                    std::move(callback), LazyInstantiationType::kSingleUse, format, desc,
+                    renderTargetSampleCnt, origin, GrMipMapped::kNo, GrMipMapsStatus::kNotAllocated,
+                    texSwizzle, outSwizzle, SkBackingFit::kApprox, SkBudgeted::kYes, isProtected,
+                    surfaceFlags)
+            : new GrTextureProxy(
+                    std::move(callback), LazyInstantiationType::kSingleUse, format, desc, origin,
+                    GrMipMapped::kNo, GrMipMapsStatus::kNotAllocated, texSwizzle,
+                    SkBackingFit::kApprox, SkBudgeted::kYes, isProtected, surfaceFlags));
 }
 
 bool GrProxyProvider::IsFunctionallyExact(GrSurfaceProxy* proxy) {
diff --git a/src/gpu/GrProxyProvider.h b/src/gpu/GrProxyProvider.h
index bfaca5d..6a971a6 100644
--- a/src/gpu/GrProxyProvider.h
+++ b/src/gpu/GrProxyProvider.h
@@ -166,20 +166,21 @@
      */
     sk_sp<GrTextureProxy> createLazyProxy(LazyInstantiateCallback&&, const GrBackendFormat&,
                                           const GrSurfaceDesc&, GrRenderable,
-                                          int renderTargetSampleCnt, GrSurfaceOrigin, GrMipMapped,
-                                          GrInternalSurfaceFlags, SkBackingFit, SkBudgeted,
-                                          GrProtected, LazyInstantiationType);
+                                          int renderTargetSampleCnt, GrSurfaceOrigin,
+                                          GrMipMapped, GrMipMapsStatus, GrInternalSurfaceFlags,
+                                          SkBackingFit, SkBudgeted, GrProtected,
+                                          LazyInstantiationType);
 
     sk_sp<GrTextureProxy> createLazyProxy(LazyInstantiateCallback&&, const GrBackendFormat&,
                                           const GrSurfaceDesc&, GrRenderable,
                                           int renderTargetSampleCnt, GrSurfaceOrigin, GrMipMapped,
-                                          GrInternalSurfaceFlags, SkBackingFit, SkBudgeted,
-                                          GrProtected);
+                                          GrMipMapsStatus, GrInternalSurfaceFlags, SkBackingFit,
+                                          SkBudgeted, GrProtected);
 
     sk_sp<GrTextureProxy> createLazyProxy(LazyInstantiateCallback&&, const GrBackendFormat&,
                                           const GrSurfaceDesc&, GrRenderable,
                                           int renderTargetSampleCnt, GrSurfaceOrigin, GrMipMapped,
-                                          SkBackingFit, SkBudgeted, GrProtected);
+                                          GrMipMapsStatus, SkBackingFit, SkBudgeted, GrProtected);
 
     /** A null TextureInfo indicates a non-textureable render target. */
     sk_sp<GrRenderTargetProxy> createLazyRenderTargetProxy(LazyInstantiateCallback&&,
@@ -188,10 +189,8 @@
                                                            int renderTargetSampleCnt,
                                                            GrSurfaceOrigin origin,
                                                            GrInternalSurfaceFlags,
-                                                           const TextureInfo*,
-                                                           SkBackingFit,
-                                                           SkBudgeted,
-                                                           GrProtected,
+                                                           const TextureInfo*, GrMipMapsStatus,
+                                                           SkBackingFit, SkBudgeted, GrProtected,
                                                            bool wrapsVkSecondaryCB);
 
     /**
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index 381539c..97f4478 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -19,6 +19,7 @@
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrRenderTargetContext.h"
 #include "src/gpu/GrResourceAllocator.h"
+#include "src/gpu/GrTexturePriv.h"
 #include "src/gpu/geometry/GrRect.h"
 #include "src/gpu/ops/GrClearOp.h"
 #include "src/gpu/ops/GrCopySurfaceOp.h"
@@ -468,6 +469,15 @@
     // no ops just to do a discard.
     if (fOpChains.empty() && GrLoadOp::kClear != fColorLoadOp &&
         GrLoadOp::kDiscard != fColorLoadOp) {
+        // TEMPORARY: We are in the process of moving GrMipMapsStatus from the texture to the proxy.
+        // During this time we want to assert that the proxy resolves mipmaps at the exact same
+        // times the old code would have. A null opList is very exceptional, and the proxy will have
+        // assumed mipmaps are dirty in this scenario. We mark them dirty here on the texture as
+        // well, in order to keep the assert passing.
+        GrTexture* tex = fTarget->peekTexture();
+        if (tex && GrMipMapped::kYes == tex->texturePriv().mipMapped()) {
+            tex->texturePriv().markMipMapsDirty();
+        }
         return false;
     }
 
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 078541b..c45d6be 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -160,13 +160,20 @@
             texels[i].fPixels = nullptr;
             texels[i].fRowBytes = 0;
         }
-
         surface = resourceProvider->createTexture(desc, renderable, sampleCnt, fBudgeted,
                                                   fIsProtected, texels.get(), mipCount);
+#ifdef SK_DEBUG
         if (surface) {
-            SkASSERT(surface->asTexture());
-            SkASSERT(GrMipMapped::kYes == surface->asTexture()->texturePriv().mipMapped());
+            const GrTextureProxy* thisTexProxy = this->asTextureProxy();
+            SkASSERT(thisTexProxy);
+
+            GrTexture* texture = surface->asTexture();
+            SkASSERT(texture);
+
+            SkASSERT(GrMipMapped::kYes == texture->texturePriv().mipMapped());
+            SkASSERT(thisTexProxy->fInitialMipMapsStatus == texture->texturePriv().mipMapsStatus());
         }
+#endif
     } else {
         if (SkBackingFit::kApprox == fFit) {
             surface = resourceProvider->createApproxTexture(desc, renderable, sampleCnt,
diff --git a/src/gpu/GrTextureOpList.cpp b/src/gpu/GrTextureOpList.cpp
index 0613c5f..b58f735 100644
--- a/src/gpu/GrTextureOpList.cpp
+++ b/src/gpu/GrTextureOpList.cpp
@@ -16,6 +16,7 @@
 #include "src/gpu/GrMemoryPool.h"
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrResourceAllocator.h"
+#include "src/gpu/GrTexturePriv.h"
 #include "src/gpu/GrTextureProxy.h"
 #include "src/gpu/ops/GrCopySurfaceOp.h"
 #include "src/gpu/ops/GrTransferFromOp.h"
@@ -98,6 +99,15 @@
 
 bool GrTextureOpList::onExecute(GrOpFlushState* flushState) {
     if (0 == fRecordedOps.count()) {
+        // TEMPORARY: We are in the process of moving GrMipMapsStatus from the texture to the proxy.
+        // During this time we want to assert that the proxy resolves mipmaps at the exact same
+        // times the old code would have. A null opList is very exceptional, and the proxy will have
+        // assumed mipmaps are dirty in this scenario. We mark them dirty here on the texture as
+        // well, in order to keep the assert passing.
+        GrTexture* tex = fTarget->peekTexture();
+        if (tex && GrMipMapped::kYes == tex->texturePriv().mipMapped()) {
+            tex->texturePriv().markMipMapsDirty();
+        }
         return false;
     }
 
diff --git a/src/gpu/GrTexturePriv.h b/src/gpu/GrTexturePriv.h
index 69404b0..8603ad5 100644
--- a/src/gpu/GrTexturePriv.h
+++ b/src/gpu/GrTexturePriv.h
@@ -25,12 +25,14 @@
         fTexture->markMipMapsClean();
     }
 
+    GrMipMapsStatus mipMapsStatus() const { return fTexture->fMipMapsStatus; }
+
     bool mipMapsAreDirty() const {
-        return GrMipMapsStatus::kValid != fTexture->fMipMapsStatus;
+        return GrMipMapsStatus::kValid != this->mipMapsStatus();
     }
 
     GrMipMapped mipMapped() const {
-        if (GrMipMapsStatus::kNotAllocated != fTexture->fMipMapsStatus) {
+        if (GrMipMapsStatus::kNotAllocated != this->mipMapsStatus()) {
             return GrMipMapped::kYes;
         }
         return GrMipMapped::kNo;
diff --git a/src/gpu/GrTextureProxy.cpp b/src/gpu/GrTextureProxy.cpp
index a71b9f3..5c7d304 100644
--- a/src/gpu/GrTextureProxy.cpp
+++ b/src/gpu/GrTextureProxy.cpp
@@ -18,12 +18,14 @@
 // Deferred version - no data
 GrTextureProxy::GrTextureProxy(const GrBackendFormat& format, const GrSurfaceDesc& srcDesc,
                                GrSurfaceOrigin origin, GrMipMapped mipMapped,
-                               const GrSwizzle& textureSwizzle, SkBackingFit fit,
-                               SkBudgeted budgeted, GrProtected isProtected,
+                               GrMipMapsStatus mipMapsStatus, const GrSwizzle& textureSwizzle,
+                               SkBackingFit fit, SkBudgeted budgeted, GrProtected isProtected,
                                GrInternalSurfaceFlags surfaceFlags)
         : INHERITED(format, srcDesc, GrRenderable::kNo, origin, textureSwizzle, fit, budgeted,
                     isProtected, surfaceFlags)
         , fMipMapped(mipMapped)
+        , fMipMapsStatus(mipMapsStatus)
+          SkDEBUGCODE(, fInitialMipMapsStatus(fMipMapsStatus))
         , fProxyProvider(nullptr)
         , fDeferredUploader(nullptr) {}
 
@@ -31,11 +33,14 @@
 GrTextureProxy::GrTextureProxy(LazyInstantiateCallback&& callback, LazyInstantiationType lazyType,
                                const GrBackendFormat& format, const GrSurfaceDesc& desc,
                                GrSurfaceOrigin origin, GrMipMapped mipMapped,
-                               const GrSwizzle& texSwizzle, SkBackingFit fit, SkBudgeted budgeted,
-                               GrProtected isProtected, GrInternalSurfaceFlags surfaceFlags)
+                               GrMipMapsStatus mipMapsStatus, const GrSwizzle& texSwizzle,
+                               SkBackingFit fit, SkBudgeted budgeted, GrProtected isProtected,
+                               GrInternalSurfaceFlags surfaceFlags)
         : INHERITED(std::move(callback), lazyType, format, desc, GrRenderable::kNo, origin,
                     texSwizzle, fit, budgeted, isProtected, surfaceFlags)
         , fMipMapped(mipMapped)
+        , fMipMapsStatus(mipMapsStatus)
+          SkDEBUGCODE(, fInitialMipMapsStatus(fMipMapsStatus))
         , fProxyProvider(nullptr)
         , fDeferredUploader(nullptr) {}
 
@@ -44,6 +49,8 @@
                                const GrSwizzle& textureSwizzle)
         : INHERITED(std::move(surf), origin, textureSwizzle, SkBackingFit::kExact)
         , fMipMapped(fTarget->asTexture()->texturePriv().mipMapped())
+        , fMipMapsStatus(fTarget->asTexture()->texturePriv().mipMapsStatus())
+          SkDEBUGCODE(, fInitialMipMapsStatus(fMipMapsStatus))
         , fProxyProvider(nullptr)
         , fDeferredUploader(nullptr) {
     if (fTarget->getUniqueKey().isValid()) {
@@ -162,6 +169,7 @@
 
     // Anything that is checked here should be duplicated in GrTextureRenderTargetProxy's version
     SkASSERT(surface->asTexture());
+    // It is possible to fulfill a non-mipmapped proxy with a mipmapped texture.
     SkASSERT(GrMipMapped::kNo == this->proxyMipMapped() ||
              GrMipMapped::kYes == surface->asTexture()->texturePriv().mipMapped());
 
diff --git a/src/gpu/GrTextureProxy.h b/src/gpu/GrTextureProxy.h
index c0f24dc..091d0ca 100644
--- a/src/gpu/GrTextureProxy.h
+++ b/src/gpu/GrTextureProxy.h
@@ -36,6 +36,12 @@
     // generation later.
     GrMipMapped mipMapped() const;
 
+    bool mipMapsAreDirty() const {
+        SkASSERT((GrMipMapped::kNo == fMipMapped) ==
+                 (GrMipMapsStatus::kNotAllocated == fMipMapsStatus));
+        return GrMipMapped::kYes == fMipMapped && GrMipMapsStatus::kValid != fMipMapsStatus;
+    }
+
     // Returns the GrMipMapped value of the proxy from creation time regardless of whether it has
     // been instantiated or not.
     GrMipMapped proxyMipMapped() const { return fMipMapped; }
@@ -93,8 +99,8 @@
 
     // Deferred version - no data.
     GrTextureProxy(const GrBackendFormat&, const GrSurfaceDesc& srcDesc, GrSurfaceOrigin,
-                   GrMipMapped, const GrSwizzle& textureSwizzle, SkBackingFit, SkBudgeted,
-                   GrProtected, GrInternalSurfaceFlags);
+                   GrMipMapped, GrMipMapsStatus, const GrSwizzle& textureSwizzle, SkBackingFit,
+                   SkBudgeted, GrProtected, GrInternalSurfaceFlags);
 
     // Lazy-callback version
     // There are two main use cases for lazily-instantiated proxies:
@@ -107,8 +113,8 @@
     // The minimal knowledge version is used for CCPR where we are generating an atlas but we do not
     // know the final size until flush time.
     GrTextureProxy(LazyInstantiateCallback&&, LazyInstantiationType, const GrBackendFormat&,
-                   const GrSurfaceDesc& desc, GrSurfaceOrigin, GrMipMapped, const GrSwizzle&,
-                   SkBackingFit, SkBudgeted, GrProtected, GrInternalSurfaceFlags);
+                   const GrSurfaceDesc& desc, GrSurfaceOrigin, GrMipMapped, GrMipMapsStatus,
+                   const GrSwizzle&, SkBackingFit, SkBudgeted, GrProtected, GrInternalSurfaceFlags);
 
     // Wrapped version
     GrTextureProxy(sk_sp<GrSurface>, GrSurfaceOrigin, const GrSwizzle&);
@@ -129,6 +135,19 @@
     // address of other types, leading to this problem.
 
     GrMipMapped      fMipMapped;
+
+    // This tracks the mipmap status at the proxy level and is thus somewhat distinct from the
+    // backing GrTexture's mipmap status. In particular, this status is used to determine when
+    // mipmap levels need to be explicitly regenerated during the execution of a DAG of opLists.
+    GrMipMapsStatus  fMipMapsStatus;
+    // TEMPORARY: We are in the process of moving GrMipMapsStatus from the texture to the proxy.
+    // We track the fInitialMipMapsStatus here so we can assert that the proxy did indeed expect
+    // the correct mipmap status immediately after instantiation.
+    //
+    // NOTE: fMipMapsStatus may no longer be equal to fInitialMipMapsStatus by the time the texture
+    // is instantiated, since it tracks mipmaps in the time frame in which the DAG is being built.
+    SkDEBUGCODE(const GrMipMapsStatus fInitialMipMapsStatus);
+
     bool             fSyncTargetKey = true;  // Should target's unique key be sync'ed with ours.
 
     GrUniqueKey      fUniqueKey;
diff --git a/src/gpu/GrTextureRenderTargetProxy.cpp b/src/gpu/GrTextureRenderTargetProxy.cpp
index 63716b5..3d17a67 100644
--- a/src/gpu/GrTextureRenderTargetProxy.cpp
+++ b/src/gpu/GrTextureRenderTargetProxy.cpp
@@ -24,6 +24,7 @@
                                                        int sampleCnt,
                                                        GrSurfaceOrigin origin,
                                                        GrMipMapped mipMapped,
+                                                       GrMipMapsStatus mipMapsStatus,
                                                        const GrSwizzle& texSwizzle,
                                                        const GrSwizzle& outSwizzle,
                                                        SkBackingFit fit,
@@ -35,8 +36,8 @@
         // for now textures w/ data are always wrapped
         , GrRenderTargetProxy(caps, format, desc, sampleCnt, origin, texSwizzle, outSwizzle, fit,
                               budgeted, isProtected, surfaceFlags)
-        , GrTextureProxy(format, desc, origin, mipMapped, texSwizzle, fit, budgeted, isProtected,
-                         surfaceFlags) {}
+        , GrTextureProxy(format, desc, origin, mipMapped, mipMapsStatus, texSwizzle, fit, budgeted,
+                         isProtected, surfaceFlags) {}
 
 // Lazy-callback version
 GrTextureRenderTargetProxy::GrTextureRenderTargetProxy(LazyInstantiateCallback&& callback,
@@ -46,6 +47,7 @@
                                                        int sampleCnt,
                                                        GrSurfaceOrigin origin,
                                                        GrMipMapped mipMapped,
+                                                       GrMipMapsStatus mipMapsStatus,
                                                        const GrSwizzle& texSwizzle,
                                                        const GrSwizzle& outSwizzle,
                                                        SkBackingFit fit,
@@ -60,7 +62,7 @@
                               texSwizzle, outSwizzle, fit, budgeted, isProtected, surfaceFlags,
                               WrapsVkSecondaryCB::kNo)
         , GrTextureProxy(LazyInstantiateCallback(), lazyType, format, desc, origin, mipMapped,
-                         texSwizzle, fit, budgeted, isProtected, surfaceFlags) {}
+                         mipMapsStatus, texSwizzle, fit, budgeted, isProtected, surfaceFlags) {}
 
 // Wrapped version
 // This class is virtually derived from GrSurfaceProxy (via both GrTextureProxy and
diff --git a/src/gpu/GrTextureRenderTargetProxy.h b/src/gpu/GrTextureRenderTargetProxy.h
index 3270d05..a736793 100644
--- a/src/gpu/GrTextureRenderTargetProxy.h
+++ b/src/gpu/GrTextureRenderTargetProxy.h
@@ -29,16 +29,16 @@
 
     // Deferred version
     GrTextureRenderTargetProxy(const GrCaps&, const GrBackendFormat&, const GrSurfaceDesc&,
-                               int sampleCnt, GrSurfaceOrigin, GrMipMapped,
+                               int sampleCnt, GrSurfaceOrigin, GrMipMapped, GrMipMapsStatus,
                                const GrSwizzle& textureSwizzle, const GrSwizzle& outputSwizzle,
                                SkBackingFit, SkBudgeted, GrProtected, GrInternalSurfaceFlags);
 
     // Lazy-callback version
     GrTextureRenderTargetProxy(LazyInstantiateCallback&&, LazyInstantiationType,
                                const GrBackendFormat&, const GrSurfaceDesc& desc, int sampleCnt,
-                               GrSurfaceOrigin, GrMipMapped, const GrSwizzle& textureSwizzle,
-                               const GrSwizzle& outputSwizzle, SkBackingFit, SkBudgeted,
-                               GrProtected, GrInternalSurfaceFlags);
+                               GrSurfaceOrigin, GrMipMapped, GrMipMapsStatus,
+                               const GrSwizzle& textureSwizzle, const GrSwizzle& outputSwizzle,
+                               SkBackingFit, SkBudgeted, GrProtected, GrInternalSurfaceFlags);
 
     // Wrapped version
     GrTextureRenderTargetProxy(sk_sp<GrSurface>, GrSurfaceOrigin, const GrSwizzle& textureSwizzle,
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 8ede79b..ea1b29c 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -990,7 +990,8 @@
                                           int mipLevelCount,
                                           int baseWidth,
                                           int baseHeight,
-                                          bool* changedUnpackRowLength) {
+                                          bool* changedUnpackRowLength,
+                                          GrMipMapsStatus* mipMapsStatus) {
     CLEAR_ERROR_BEFORE_ALLOC(&interface);
 
     if (caps.configSupportsTexStorage(config)) {
@@ -1006,6 +1007,9 @@
             for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
                 const void* currentMipData = texels[currentMipLevel].fPixels;
                 if (currentMipData == nullptr) {
+                    if (mipMapsStatus) {
+                        *mipMapsStatus = GrMipMapsStatus::kDirty;
+                    }
                     continue;
                 }
                 int twoToTheMipLevel = 1 << currentMipLevel;
@@ -1061,7 +1065,8 @@
                 const int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel);
                 const int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel);
 
-                if (texels[currentMipLevel].fPixels) {
+                const void* currentMipData = texels[currentMipLevel].fPixels;
+                if (currentMipData) {
                     const size_t trimRowBytes = currentWidth * bpp;
                     const size_t rowBytes = texels[currentMipLevel].fRowBytes;
                     if (rowBytes != trimRowBytes) {
@@ -1074,9 +1079,10 @@
                         GR_GL_CALL(&interface, PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
                         *changedUnpackRowLength = false;
                     }
+                } else if (mipMapsStatus) {
+                    *mipMapsStatus = GrMipMapsStatus::kDirty;
                 }
 
-                const void* currentMipData = texels[currentMipLevel].fPixels;
                 // Even if curremtMipData is nullptr, continue to call TexImage2D.
                 // This will allocate texture memory which we can later populate.
                 GL_ALLOC_CALL(&interface,
@@ -1180,11 +1186,8 @@
     SkAutoSMalloc<128 * 128> tempStorage;
 
     if (mipMapsStatus) {
-        *mipMapsStatus = GrMipMapsStatus::kValid;
-    }
-
-    if (mipMapsStatus && mipLevelCount <= 1) {
-        *mipMapsStatus = GrMipMapsStatus::kNotAllocated;
+        *mipMapsStatus = (mipLevelCount > 1) ?
+                GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated;
     }
 
     if (mipLevelCount) {
@@ -1197,13 +1200,16 @@
             succeeded = allocate_and_populate_texture(
                     texConfig, *interface, caps, target, internalFormat,
                     internalFormatForTexStorage, externalFormat, externalType, texels,
-                    mipLevelCount, width, height, &restoreGLRowLength);
+                    mipLevelCount, width, height, &restoreGLRowLength, mipMapsStatus);
         } else {
             succeeded = false;
         }
     } else {
         for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
             if (!texels[currentMipLevel].fPixels) {
+                if (mipMapsStatus) {
+                    *mipMapsStatus = GrMipMapsStatus::kDirty;
+                }
                 continue;
             }
             int twoToTheMipLevel = 1 << currentMipLevel;
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 60fc1c4..f319f9f 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -491,10 +491,15 @@
     desc.fHeight = height;
     desc.fConfig = config;
 
+    // Ganesh assumes that, when wrapping a mipmapped backend texture from a client, that its
+    // mipmaps are fully fleshed out.
+    GrMipMapsStatus mipMapsStatus = (GrMipMapped::kYes == mipMapped)
+            ? GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated;
+
     // We pass kReadOnly here since we should treat content of the client's texture as immutable.
     // The promise API provides no way for the client to indicated that the texture is protected.
     return proxyProvider->createLazyProxy(
             std::move(callback), backendFormat, desc, GrRenderable::kNo, 1, origin, mipMapped,
-            GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact, SkBudgeted::kNo,
+            mipMapsStatus, GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact, SkBudgeted::kNo,
             GrProtected::kNo, GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
 }
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index c3d1685..72aa374 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -562,8 +562,9 @@
                 auto proxy = context->priv().proxyProvider()->createLazyProxy(
                         singleUseLazyCB, backendFormat, desc, renderable, 1,
                         GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
-                        GrInternalSurfaceFlags ::kNone, SkBackingFit::kExact, budgeted,
-                        GrProtected::kNo, GrSurfaceProxy::LazyInstantiationType::kSingleUse);
+                        GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags ::kNone,
+                        SkBackingFit::kExact, budgeted, GrProtected::kNo,
+                        GrSurfaceProxy::LazyInstantiationType::kSingleUse);
                 rtc->drawTexture(GrNoClip(), proxy, GrSamplerState::Filter::kNearest,
                                  SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
                                  SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
@@ -599,8 +600,9 @@
                 proxy = context->priv().proxyProvider()->createLazyProxy(
                         deinstantiateLazyCB, backendFormat, desc, renderable, 1,
                         GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
-                        GrInternalSurfaceFlags ::kNone, SkBackingFit::kExact, budgeted,
-                        GrProtected::kNo, GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
+                        GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags ::kNone,
+                        SkBackingFit::kExact, budgeted, GrProtected::kNo,
+                        GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
                 rtc->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kNearest,
                                  SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
                                  SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
diff --git a/tests/LazyProxyTest.cpp b/tests/LazyProxyTest.cpp
index a94b880..1307f0c 100644
--- a/tests/LazyProxyTest.cpp
+++ b/tests/LazyProxyTest.cpp
@@ -269,8 +269,9 @@
             };
             sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
                     TestCallback(&testCount), format, desc, GrRenderable::kNo, 1,
-                    kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, GrInternalSurfaceFlags::kNone,
-                    SkBackingFit::kExact, SkBudgeted::kNo, GrProtected::kNo, lazyType);
+                    kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, GrMipMapsStatus::kNotAllocated,
+                    GrInternalSurfaceFlags::kNone, SkBackingFit::kExact, SkBudgeted::kNo,
+                    GrProtected::kNo, lazyType);
 
             REPORTER_ASSERT(reporter, proxy.get());
             REPORTER_ASSERT(reporter, 0 == testCount);
@@ -341,7 +342,8 @@
                             GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced};
                 },
                 format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
-                SkBackingFit::kExact, SkBudgeted::kNo, GrProtected::kNo);
+                GrMipMapsStatus::kNotAllocated, SkBackingFit::kExact, SkBudgeted::kNo,
+                GrProtected::kNo);
 
         SkASSERT(fLazyProxy.get());
 
@@ -479,8 +481,8 @@
                     return std::move(texture);
                 },
                 format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
-                GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact, SkBudgeted::kNo,
-                GrProtected::kNo, lazyType);
+                GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags::kReadOnly,
+                SkBackingFit::kExact, SkBudgeted::kNo, GrProtected::kNo, lazyType);
 
         REPORTER_ASSERT(reporter, lazyProxy.get());
 
diff --git a/tests/ResourceAllocatorTest.cpp b/tests/ResourceAllocatorTest.cpp
index ce257b5..2b81256 100644
--- a/tests/ResourceAllocatorTest.cpp
+++ b/tests/ResourceAllocatorTest.cpp
@@ -296,9 +296,9 @@
     auto lazyType = deinstantiate ? GrSurfaceProxy::LazyInstantiationType ::kDeinstantiate
                                   : GrSurfaceProxy::LazyInstantiationType ::kSingleUse;
     GrInternalSurfaceFlags flags = GrInternalSurfaceFlags::kNone;
-    return proxyProvider->createLazyProxy(callback, format, desc, p.fRenderable, p.fSampleCnt,
-                                          p.fOrigin, GrMipMapped::kNo, flags, p.fFit, p.fBudgeted,
-                                          GrProtected::kNo, lazyType);
+    return proxyProvider->createLazyProxy(
+            callback, format, desc, p.fRenderable, p.fSampleCnt, p.fOrigin, GrMipMapped::kNo,
+            GrMipMapsStatus::kNotAllocated, flags, p.fFit, p.fBudgeted, GrProtected::kNo, lazyType);
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(LazyDeinstantiation, reporter, ctxInfo) {