Discard atlas after every MultiPictureDraw::draw

This is intended to prevent ghosting on tiled architectures.

This CL also defers creation of the atlas (and its texture) until it is actually needed.

Committed: https://skia.googlesource.com/skia/+/6d5b5455743414ddb11d2b8c1fe9d7959f2b853d

Review URL: https://codereview.chromium.org/678403002
diff --git a/src/core/SkMultiPictureDraw.cpp b/src/core/SkMultiPictureDraw.cpp
index b59b63b..2690e8f 100644
--- a/src/core/SkMultiPictureDraw.cpp
+++ b/src/core/SkMultiPictureDraw.cpp
@@ -182,6 +182,9 @@
 #ifndef SK_IGNORE_GPU_LAYER_HOISTING
     GrLayerHoister::UnlockLayers(context, atlasedNeedRendering);
     GrLayerHoister::UnlockLayers(context, atlasedRecycled);
+#if !GR_CACHE_HOISTED_LAYERS
+    GrLayerHoister::PurgeCache(context);
+#endif
 #endif
 }
 
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp
index d897c9d..a24c9e0 100644
--- a/src/gpu/GrLayerCache.cpp
+++ b/src/gpu/GrLayerCache.cpp
@@ -81,7 +81,6 @@
 
 GrLayerCache::GrLayerCache(GrContext* context)
     : fContext(context) {
-    this->initAtlas();
     memset(fPlotLocks, 0, sizeof(fPlotLocks));
 }
 
@@ -120,11 +119,6 @@
 
     // The atlas only lets go of its texture when the atlas is deleted. 
     fAtlas.free();
-    // GrLayerCache always assumes an atlas exists so recreate it. The atlas 
-    // lazily allocates a replacement texture so reallocating a new 
-    // atlas here won't disrupt a GrContext::abandonContext or freeGpuResources.
-    // TODO: Make GrLayerCache lazily allocate the atlas manager?
-    this->initAtlas();
 }
 
 GrCachedLayer* GrLayerCache::createLayer(uint32_t pictureID, 
@@ -164,12 +158,13 @@
 bool GrLayerCache::tryToAtlas(GrCachedLayer* layer, 
                               const GrSurfaceDesc& desc, 
                               bool* needsRendering) {
-    SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
+    SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTexture() : NULL, layer);)
 
     SkASSERT(PlausiblyAtlasable(desc.fWidth, desc.fHeight));
 
     if (layer->locked()) {
         // This layer is already locked
+        SkASSERT(fAtlas);
         SkASSERT(layer->isAtlased());
         SkASSERT(layer->rect().width() == desc.fWidth);
         SkASSERT(layer->rect().height() == desc.fHeight);
@@ -178,12 +173,19 @@
     }
 
     if (layer->isAtlased()) {
+        SkASSERT(fAtlas);
         // Hooray it is still in the atlas - make sure it stays there
         layer->setLocked(true);
         this->incPlotLock(layer->plot()->id());
         *needsRendering = false;
         return true;
     } else {
+        if (!fAtlas) {
+            this->initAtlas();
+            if (!fAtlas) {
+                return false;
+            }
+        }
         // Not in the atlas - will it fit?
         GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
         if (NULL == pictInfo) {
@@ -243,7 +245,7 @@
 }
 
 void GrLayerCache::unlock(GrCachedLayer* layer) {
-    SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
+    SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTexture() : NULL, layer);)
 
     if (NULL == layer || !layer->locked()) {
         // invalid or not locked
@@ -256,7 +258,7 @@
         this->decPlotLock(plotID);
         // At this point we could aggressively clear out un-locked plots but
         // by delaying we may be able to reuse some of the atlased layers later.
-#if DISABLE_CACHING
+#if !GR_CACHE_HOISTED_LAYERS
         // This testing code aggressively removes the atlased layers. This
         // can be used to separate the performance contribution of less
         // render target pingponging from that due to the re-use of cached layers
@@ -285,7 +287,7 @@
     for (; !iter.done(); ++iter) {
         const GrCachedLayer* layer = &(*iter);
 
-        layer->validate(fAtlas->getTexture());
+        layer->validate(fAtlas.get() ? fAtlas->getTexture() : NULL);
 
         const GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
         if (!pictInfo) {
@@ -355,6 +357,7 @@
 
 bool GrLayerCache::purgePlot() {
     SkDEBUGCODE(GrAutoValidateCache avc(this);)
+    SkASSERT(fAtlas);
 
     GrAtlas::PlotIter iter;
     GrPlot* plot;
@@ -409,7 +412,12 @@
     plot->resetRects();
 }
 
+#if !GR_CACHE_HOISTED_LAYERS
 void GrLayerCache::purgeAll() {
+    if (!fAtlas) {
+        return;
+    }
+
     GrAtlas::PlotIter iter;
     GrPlot* plot;
     for (plot = fAtlas->iterInit(&iter, GrAtlas::kLRUFirst_IterOrder);
@@ -419,7 +427,10 @@
 
         this->purgePlot(plot);
     }
+
+    fContext->discardRenderTarget(fAtlas->getTexture()->asRenderTarget());
 }
+#endif
 
 class GrPictureDeletionListener : public SkPicture::DeletionListener {
     virtual void onDeletion(uint32_t pictureID) SK_OVERRIDE{
@@ -448,12 +459,14 @@
 #ifdef SK_DEVELOPER
 void GrLayerCache::writeLayersToDisk(const SkString& dirName) {
 
-    GrTexture* atlasTexture = fAtlas->getTexture();
-    if (NULL != atlasTexture) {
-        SkString fileName(dirName);
-        fileName.append("\\atlas.png");
+    if (fAtlas) {
+        GrTexture* atlasTexture = fAtlas->getTexture();
+        if (NULL != atlasTexture) {
+            SkString fileName(dirName);
+            fileName.append("\\atlas.png");
 
-        atlasTexture->surfacePriv().savePixels(fileName.c_str());
+            atlasTexture->surfacePriv().savePixels(fileName.c_str());
+        }
     }
 
     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h
index c772332..632cd00 100644
--- a/src/gpu/GrLayerCache.h
+++ b/src/gpu/GrLayerCache.h
@@ -18,6 +18,9 @@
 
 class SkPicture;
 
+// Set to 0 to disable caching of hoisted layers
+#define GR_CACHE_HOISTED_LAYERS 0
+
 // The layer cache listens for these messages to purge picture-related resources.
 struct GrPictureDeletedMessage {
     uint32_t pictureID;
@@ -249,6 +252,10 @@
         return width <= kPlotWidth && height <= kPlotHeight;
     }
 
+#if !GR_CACHE_HOISTED_LAYERS
+    void purgeAll();
+#endif
+
 private:
     static const int kAtlasTextureWidth = 1024;
     static const int kAtlasTextureHeight = 1024;
@@ -291,8 +298,6 @@
                                const SkIRect& bounds, const SkMatrix& ctm, 
                                const SkPaint* paint);
 
-    void purgeAll();
-
     // Remove all the layers (and unlock any resources) associated with 'pictureID'
     void purge(uint32_t pictureID);
 
diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp
index 1f7ce41..0057671 100644
--- a/src/gpu/GrLayerHoister.cpp
+++ b/src/gpu/GrLayerHoister.cpp
@@ -314,13 +314,16 @@
         layerCache->removeUse(layers[i].fLayer);
     }
 
-#if DISABLE_CACHING
+    SkDEBUGCODE(layerCache->validate();)
+}
+
+void GrLayerHoister::PurgeCache(GrContext* context) {
+#if !GR_CACHE_HOISTED_LAYERS
+    GrLayerCache* layerCache = context->getLayerCache();
+
     // This code completely clears out the atlas. It is required when
     // caching is disabled so the atlas doesn't fill up and force more
     // free floating layers
     layerCache->purgeAll();
 #endif
-
-    SkDEBUGCODE(layerCache->validate();)
 }
-
diff --git a/src/gpu/GrLayerHoister.h b/src/gpu/GrLayerHoister.h
index 82dd967..c3a451d 100644
--- a/src/gpu/GrLayerHoister.h
+++ b/src/gpu/GrLayerHoister.h
@@ -87,6 +87,12 @@
         @param layers     Unneeded layers in the atlas
     */
     static void UnlockLayers(GrContext* context, const SkTDArray<GrHoistedLayer>& layers);
+
+    /** Forceably remove all cached layers and release the atlas. Useful for debugging and timing.
+        This is only functional when GR_CACHE_HOISTED_LAYERS is set to 1 in GrLayerCache.h
+        @param context    Owner of the layer cache (and thus the layers)
+     */
+    static void PurgeCache(GrContext* context);
 };
 
 #endif