Refactor GrLayerCache for new API

The only substantive change in this CL is skipping atlasing
for any layers that are involved in nesting. Prior versions
have allowed nesting layers to be atlased.

--------------------------------------------------------

All times are on Windows with a repeat count of 200.
Format is: <time in ms> (<# of glBindframebuffer calls>)

How to interpret this:

For the boogie page:

 both columns should be the same (since boogie has no
        nested layers)
 without the new API the tiled case doesn't show any real
        benefit from hoisting

For the carsvg page:

 The nesting change does increase the number of FBO
      switches but doesn't kill performance
 Because of the location & size of the layers the
      tile case does show some improvement (even
      without the new API)

boogie
-------

		   w/o nested change   w/ nested change

simple                5.62 (811)           N/A

tiled                 7.72 (811)           N/A

simple w/ hoisting    5.23 (409)        5.77 (409)
(but no caching)

tiled w/ hoisting     7.57 (809)        7.49 (809)
(but no caching)

carsvg
------

		   w/o nested change   w/ nested change

simple 		      60.37 (141990)        N/A

tiled                115.13 (256929)        N/A

simple w/ hoisting    41.57 (64570)      42.82 (72279)
(but no caching)

tiled w/ hoisting     84.24 (154352)     84.71 (165630)
(but no caching)

R=bsalomon@google.com

Author: robertphillips@google.com

Review URL: https://codereview.chromium.org/476833004
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp
index 5fb76d7..c0510c0 100644
--- a/src/gpu/GrLayerCache.cpp
+++ b/src/gpu/GrLayerCache.cpp
@@ -117,37 +117,36 @@
     this->initAtlas();
 }
 
-GrCachedLayer* GrLayerCache::createLayer(const SkPicture* picture, 
+GrCachedLayer* GrLayerCache::createLayer(uint32_t pictureID, 
                                          int start, int stop, 
                                          const SkMatrix& ctm) {
-    SkASSERT(picture->uniqueID() != SK_InvalidGenID && start > 0 && stop > 0);
+    SkASSERT(pictureID != SK_InvalidGenID && start > 0 && stop > 0);
 
-    GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (picture->uniqueID(), start, stop, ctm));
+    GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (pictureID, start, stop, ctm));
     fLayerHash.add(layer);
     return layer;
 }
 
-GrCachedLayer* GrLayerCache::findLayer(const SkPicture* picture, 
+GrCachedLayer* GrLayerCache::findLayer(uint32_t pictureID,
                                        int start, int stop, 
                                        const SkMatrix& ctm) {
-    SkASSERT(picture->uniqueID() != SK_InvalidGenID && start > 0 && stop > 0);
-    return fLayerHash.find(GrCachedLayer::Key(picture->uniqueID(), start, stop, ctm));
+    SkASSERT(pictureID != SK_InvalidGenID && start > 0 && stop > 0);
+    return fLayerHash.find(GrCachedLayer::Key(pictureID, start, stop, ctm));
 }
 
-GrCachedLayer* GrLayerCache::findLayerOrCreate(const SkPicture* picture, 
+GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID,
                                                int start, int stop,
                                                const SkMatrix& ctm) {
-    SkASSERT(picture->uniqueID() != SK_InvalidGenID && start > 0 && stop > 0);
-    GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(picture->uniqueID(), 
-                                                              start, stop, ctm));
+    SkASSERT(pictureID != SK_InvalidGenID && start > 0 && stop > 0);
+    GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(pictureID, start, stop, ctm));
     if (NULL == layer) {
-        layer = this->createLayer(picture, start, stop, ctm);
+        layer = this->createLayer(pictureID, start, stop, ctm);
     }
 
     return layer;
 }
 
-bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
+bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool dontAtlas) {
     SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
 
     if (layer->locked()) {
@@ -155,19 +154,21 @@
 #ifdef SK_DEBUG
         if (layer->isAtlased()) {
             // It claims to be atlased
+            SkASSERT(!dontAtlas);
             SkASSERT(layer->rect().width() == desc.fWidth);
             SkASSERT(layer->rect().height() == desc.fHeight);
         }
 #endif
-        return true;
+        return false;
     }
 
     if (layer->isAtlased()) {
         // Hooray it is still in the atlas - make sure it stays there
+        SkASSERT(!dontAtlas);
         layer->setLocked(true);
         fPlotLocks[layer->plot()->id()]++;
-        return true;
-    } else if (PlausiblyAtlasable(desc.fWidth, desc.fHeight)) {
+        return false;
+    } else if (!dontAtlas && PlausiblyAtlasable(desc.fWidth, desc.fHeight)) {
         // Not in the atlas - will it fit?
         GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
         if (NULL == pictInfo) {
@@ -185,13 +186,13 @@
             if (NULL != plot) {
                 // The layer was successfully added to the atlas
                 GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY,
-                                                       SkToS16(desc.fWidth), 
+                                                       SkToS16(desc.fWidth),
                                                        SkToS16(desc.fHeight));
                 layer->setTexture(fAtlas->getTexture(), bounds);
                 layer->setPlot(plot);
                 layer->setLocked(true);
                 fPlotLocks[layer->plot()->id()]++;
-                return false;
+                return true;
             }
 
             // The layer was rejected by the atlas (even though we know it is 
@@ -210,7 +211,7 @@
 
     layer->setTexture(tex, GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeight)));
     layer->setLocked(true);
-    return false;
+    return true;
 }
 
 void GrLayerCache::unlock(GrCachedLayer* layer) {
@@ -228,7 +229,7 @@
         fPlotLocks[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 0
+#if DISABLE_CACHING
         // 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
@@ -264,7 +265,9 @@
         if (NULL != pictInfo) {
             // In aggressive cleanup mode a picture info should only exist if
             // it has some atlased layers
+#if !DISABLE_CACHING
             SkASSERT(!pictInfo->fPlotUsage.isEmpty());
+#endif
         } else {
             // If there is no picture info for this layer then all of its
             // layers should be non-atlased.
@@ -341,41 +344,59 @@
             continue;
         }
 
-        // We need to find all the layers in 'plot' and remove them.
-        SkTDArray<GrCachedLayer*> toBeRemoved;
-
-        SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
-        for (; !iter.done(); ++iter) {
-            if (plot == (*iter).plot()) {
-                *toBeRemoved.append() = &(*iter);
-            }
-        }
-
-        for (int i = 0; i < toBeRemoved.count(); ++i) {
-            SkASSERT(!toBeRemoved[i]->locked());
-
-            GrPictureInfo* pictInfo = fPictureHash.find(toBeRemoved[i]->pictureID());
-            SkASSERT(NULL != pictInfo);
-
-            GrAtlas::RemovePlot(&pictInfo->fPlotUsage, plot);
-
-            // Aggressively remove layers and, if now totally uncached, picture info
-            fLayerHash.remove(GrCachedLayer::GetKey(*toBeRemoved[i]));
-            SkDELETE(toBeRemoved[i]);
-
-            if (pictInfo->fPlotUsage.isEmpty()) {
-                fPictureHash.remove(pictInfo->fPictureID);
-                SkDELETE(pictInfo);
-            }
-        }
-
-        plot->resetRects();
+        this->purgePlot(plot);
         return true;
     }
 
     return false;
 }
 
+void GrLayerCache::purgePlot(GrPlot* plot) {
+    SkASSERT(0 == fPlotLocks[plot->id()]);
+
+    // We need to find all the layers in 'plot' and remove them.
+    SkTDArray<GrCachedLayer*> toBeRemoved;
+
+    SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
+    for (; !iter.done(); ++iter) {
+        if (plot == (*iter).plot()) {
+            *toBeRemoved.append() = &(*iter);
+        }
+    }
+
+    for (int i = 0; i < toBeRemoved.count(); ++i) {
+        SkASSERT(!toBeRemoved[i]->locked());
+
+        GrPictureInfo* pictInfo = fPictureHash.find(toBeRemoved[i]->pictureID());
+        SkASSERT(NULL != pictInfo);
+
+        GrAtlas::RemovePlot(&pictInfo->fPlotUsage, plot);
+
+        // Aggressively remove layers and, if now totally uncached, picture info
+        fLayerHash.remove(GrCachedLayer::GetKey(*toBeRemoved[i]));
+        SkDELETE(toBeRemoved[i]);
+
+        if (pictInfo->fPlotUsage.isEmpty()) {
+            fPictureHash.remove(pictInfo->fPictureID);
+            SkDELETE(pictInfo);
+        }
+    }
+
+    plot->resetRects();
+}
+
+void GrLayerCache::purgeAll() {
+    GrAtlas::PlotIter iter;
+    GrPlot* plot;
+    for (plot = fAtlas->iterInit(&iter, GrAtlas::kLRUFirst_IterOrder);
+         NULL != plot;
+         plot = iter.prev()) {
+        SkASSERT(0 == fPlotLocks[plot->id()]);
+
+        this->purgePlot(plot);
+    }
+}
+
 class GrPictureDeletionListener : public SkPicture::DeletionListener {
     virtual void onDeletion(uint32_t pictureID) SK_OVERRIDE{
         const GrPictureDeletedMessage message = { pictureID };
diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h
index f027a26..fcf62f9 100644
--- a/src/gpu/GrLayerCache.h
+++ b/src/gpu/GrLayerCache.h
@@ -117,7 +117,7 @@
     const GrIRect16& rect() const { return fRect; }
 
     void setPlot(GrPlot* plot) {
-        SkASSERT(NULL == fPlot);
+        SkASSERT(NULL == plot || NULL == fPlot);
         fPlot = plot;
     }
     GrPlot* plot() { return fPlot; }
@@ -171,16 +171,15 @@
     // elements by the GrContext
     void freeAll();
 
-    GrCachedLayer* findLayer(const SkPicture* picture, int start, int stop, const SkMatrix& ctm);
-    GrCachedLayer* findLayerOrCreate(const SkPicture* picture, 
+    GrCachedLayer* findLayer(uint32_t pictureID, int start, int stop, const SkMatrix& ctm);
+    GrCachedLayer* findLayerOrCreate(uint32_t pictureID,
                                      int start, int stop, 
                                      const SkMatrix& ctm);
-    
-    // Inform the cache that layer's cached image is now required. Return true
-    // if it was found in the ResourceCache and doesn't need to be regenerated.
-    // If false is returned the caller should (re)render the layer into the
-    // newly acquired texture.
-    bool lock(GrCachedLayer* layer, const GrTextureDesc& desc);
+
+    // Inform the cache that layer's cached image is now required. 
+    // Return true if the layer must be re-rendered. Return false if the
+    // layer was found in the cache and can be reused.
+    bool lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool dontAtlas);
 
     // Inform the cache that layer's cached image is not currently required
     void unlock(GrCachedLayer* layer);
@@ -228,7 +227,10 @@
     int fPlotLocks[kNumPlotsX * kNumPlotsY];
 
     void initAtlas();
-    GrCachedLayer* createLayer(const SkPicture* picture, int start, int stop, const SkMatrix& ctm);
+    GrCachedLayer* createLayer(uint32_t pictureID, int start, int stop, const SkMatrix& ctm);
+
+public:
+    void purgeAll();
 
     // Remove all the layers (and unlock any resources) associated with 'pictureID'
     void purge(uint32_t pictureID);
@@ -237,6 +239,8 @@
         return width <= kPlotWidth && height <= kPlotHeight;
     }
 
+    void purgePlot(GrPlot* plot);
+
     // Try to find a purgeable plot and clear it out. Return true if a plot
     // was purged; false otherwise.
     bool purgePlot();
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index e459a11..64439a1 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1989,7 +1989,7 @@
         if (pullForward[i]) {
             const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i);
 
-            GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, 
+            GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture->uniqueID(), 
                                                                                 info.fSaveLayerOpID, 
                                                                                 info.fRestoreOpID, 
                                                                                 info.fCTM);
@@ -2007,7 +2007,8 @@
             desc.fConfig = kSkia8888_GrPixelConfig;
             // TODO: need to deal with sample count
 
-            bool needsRendering = !fContext->getLayerCache()->lock(layer, desc);
+            bool needsRendering = fContext->getLayerCache()->lock(layer, desc,
+                                                    info.fHasNestedLayers || info.fIsNested);
             if (NULL == layer->texture()) {
                 continue;
             }
@@ -2126,13 +2127,22 @@
     for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
         const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i);
 
-        GrCachedLayer* layer = fContext->getLayerCache()->findLayer(picture, 
+        GrCachedLayer* layer = fContext->getLayerCache()->findLayer(picture->uniqueID(), 
                                                                     info.fSaveLayerOpID,
                                                                     info.fRestoreOpID,
                                                                     info.fCTM);
         fContext->getLayerCache()->unlock(layer);
     }
 
+#if DISABLE_CACHING
+    // 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
+    fContext->getLayerCache()->purge(picture->uniqueID());
+
+    fContext->getLayerCache()->purgeAll();
+#endif
+
     return true;
 }
 
diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp
index 789854c..5e18197 100644
--- a/tests/GpuLayerCacheTest.cpp
+++ b/tests/GpuLayerCacheTest.cpp
@@ -31,11 +31,11 @@
                           int idOffset) {
 
     for (int i = 0; i < numToAdd; ++i) {
-        GrCachedLayer* layer = cache->findLayerOrCreate(&picture, 
+        GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(), 
                                                         idOffset+i+1, idOffset+i+2, 
                                                         SkMatrix::I());
         REPORTER_ASSERT(reporter, NULL != layer);
-        GrCachedLayer* temp = cache->findLayer(&picture, idOffset+i+1, idOffset+i+2, SkMatrix::I());
+        GrCachedLayer* temp = cache->findLayer(picture.uniqueID(), idOffset+i+1, idOffset+i+2, SkMatrix::I());
         REPORTER_ASSERT(reporter, temp == layer);
 
         REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1);
@@ -60,11 +60,11 @@
     desc.fHeight = 512;
     desc.fConfig = kSkia8888_GrPixelConfig;
 
-    bool foundInCache = cache->lock(layer, desc);
-    REPORTER_ASSERT(reporter, !foundInCache);
+    bool needsRerendering = cache->lock(layer, desc, false);
+    REPORTER_ASSERT(reporter, needsRerendering);
 
-    foundInCache = cache->lock(layer, desc);
-    REPORTER_ASSERT(reporter, foundInCache);
+    needsRerendering = cache->lock(layer, desc, false);
+    REPORTER_ASSERT(reporter, !needsRerendering);
 
     REPORTER_ASSERT(reporter, NULL != layer->texture());
     REPORTER_ASSERT(reporter, layer->locked());
@@ -99,7 +99,7 @@
         create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
 
         for (int i = 0; i < kInitialNumLayers; ++i) {
-            GrCachedLayer* layer = cache.findLayer(picture, i+1, i+2, SkMatrix::I());
+            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, SkMatrix::I());
             REPORTER_ASSERT(reporter, NULL != layer);
 
             lock_layer(reporter, &cache, layer);
@@ -116,14 +116,14 @@
 
         // Unlock the textures
         for (int i = 0; i < kInitialNumLayers; ++i) {
-            GrCachedLayer* layer = cache.findLayer(picture, i+1, i+2, SkMatrix::I());
+            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, SkMatrix::I());
             REPORTER_ASSERT(reporter, NULL != layer);
 
             cache.unlock(layer);
         }
 
         for (int i = 0; i < kInitialNumLayers; ++i) {
-            GrCachedLayer* layer = cache.findLayer(picture, i+1, i+2, SkMatrix::I());
+            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, SkMatrix::I());
             REPORTER_ASSERT(reporter, NULL != layer);
 
             REPORTER_ASSERT(reporter, !layer->locked());
@@ -142,7 +142,7 @@
             // Add an additional layer. Since all the layers are unlocked this 
             // will force out the first atlased layer
             create_layers(reporter, &cache, *picture, 1, kInitialNumLayers);
-            GrCachedLayer* layer = cache.findLayer(picture, 
+            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), 
                                                    kInitialNumLayers+1, kInitialNumLayers+2, 
                                                    SkMatrix::I());
             REPORTER_ASSERT(reporter, NULL != layer);
@@ -152,7 +152,7 @@
         }
 
         for (int i = 0; i < kInitialNumLayers+1; ++i) {
-            GrCachedLayer* layer = cache.findLayer(picture, i+1, i+2, SkMatrix::I());
+            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, SkMatrix::I());
             // 3 old layers plus the new one should be in the atlas.
             if (1 == i || 2 == i || 3 == i || 5 == i) {
                 REPORTER_ASSERT(reporter, NULL != layer);