Make GrTextureStripAtlas DDL friendly

Change-Id: If8fdd7a1c027bc2b2791cfe1af13f99c2561d93d
Reviewed-on: https://skia-review.googlesource.com/113268
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 98d278a..eccad04 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -42,6 +42,7 @@
 class GrTextBlobCache;
 class GrTextContext;
 class GrTextureProxy;
+class GrTextureStripAtlasManager;
 class GrVertexBuffer;
 struct GrVkBackendContext;
 
@@ -99,24 +100,6 @@
     void resetContext(uint32_t state = kAll_GrBackendState);
 
     /**
-     * Callback function to allow classes to cleanup on GrContext destruction.
-     * The 'info' field is filled in with the 'info' passed to addCleanUp.
-     */
-    typedef void (*PFCleanUpFunc)(const GrContext* context, void* info);
-
-    /**
-     * Add a function to be called from within GrContext's destructor.
-     * This gives classes a chance to free resources held on a per context basis.
-     * The 'info' parameter will be stored and passed to the callback function.
-     */
-    void addCleanUp(PFCleanUpFunc cleanUp, void* info) {
-        CleanUpData* entry = fCleanUpData.push();
-
-        entry->fFunc = cleanUp;
-        entry->fInfo = info;
-    }
-
-    /**
      * Abandons all GPU resources and assumes the underlying backend 3D API context is no longer
      * usable. Call this if you have lost the associated GPU context, and thus internal texture,
      * buffer, etc. references/IDs are now invalid. Calling this ensures that the destructors of the
@@ -293,6 +276,7 @@
     GrResourceCache*                        fResourceCache;
     GrResourceProvider*                     fResourceProvider;
     GrProxyProvider*                        fProxyProvider;
+    std::unique_ptr<GrTextureStripAtlasManager> fTextureStripAtlasManager;
 
 
     GrGlyphCache*                           fGlyphCache;
@@ -311,13 +295,6 @@
 
     std::unique_ptr<SkTaskGroup>            fTaskGroup;
 
-    struct CleanUpData {
-        PFCleanUpFunc fFunc;
-        void*         fInfo;
-    };
-
-    SkTDArray<CleanUpData>                  fCleanUpData;
-
     const uint32_t                          fUniqueID;
 
     std::unique_ptr<GrDrawingManager>       fDrawingManager;
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 46bfba9..3896acd 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -444,16 +444,16 @@
     desc.fWidth  = bitmap.width();
     desc.fHeight = 128;
     desc.fRowHeight = bitmap.height();
-    // TODO: this seems a bit heavy handed (passing a GrContext as part of the desc)
-    desc.fContext = context;
     desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *context->caps());
 
     if (kUnknown_GrPixelConfig == desc.fConfig) {
         return nullptr;
     }
 
-    GrTextureStripAtlas* atlas = GrTextureStripAtlas::GetAtlas(desc);
-    int row = atlas->lockRow(bitmap);
+    auto atlasManager = context->contextPriv().textureStripAtlasManager();
+
+    GrTextureStripAtlas* atlas = atlasManager->getAtlas(desc);
+    int row = atlas->lockRow(context, bitmap);
     sk_sp<GrTextureProxy> proxy;
     if (-1 == row) {
         atlas = nullptr;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index f88da28..4b624ee 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -24,6 +24,7 @@
 #include "GrSurfaceProxyPriv.h"
 #include "GrTexture.h"
 #include "GrTextureContext.h"
+#include "GrTextureStripAtlas.h"
 #include "GrTracing.h"
 #include "SkConvertPixels.h"
 #include "SkDeferredDisplayList.h"
@@ -90,6 +91,8 @@
         fResourceCache->setProxyProvider(fProxyProvider);
     }
 
+    fTextureStripAtlasManager.reset(new GrTextureStripAtlasManager);
+
     fDisableGpuYUVConversion = options.fDisableGpuYUVConversion;
     fSharpenMipmappedTextures = options.fSharpenMipmappedTextures;
     fDidTestPMConversions = false;
@@ -146,10 +149,7 @@
         fDrawingManager->cleanup();
     }
 
-    for (int i = 0; i < fCleanUpData.count(); ++i) {
-        (*fCleanUpData[i].fFunc)(this, fCleanUpData[i].fInfo);
-    }
-
+    fTextureStripAtlasManager = nullptr;
     delete fResourceProvider;
     delete fResourceCache;
     delete fProxyProvider;
@@ -199,6 +199,7 @@
 void GrContext::abandonContext() {
     ASSERT_SINGLE_OWNER
 
+    fTextureStripAtlasManager->abandon();
     fProxyProvider->abandon();
     fResourceProvider->abandon();
 
@@ -219,6 +220,7 @@
 void GrContext::releaseResourcesAndAbandonContext() {
     ASSERT_SINGLE_OWNER
 
+    fTextureStripAtlasManager->abandon();
     fProxyProvider->abandon();
     fResourceProvider->abandon();
 
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 35dc4a9..7453976 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -186,6 +186,10 @@
 
     GrResourceCache* getResourceCache() { return fContext->fResourceCache; }
 
+    GrTextureStripAtlasManager* textureStripAtlasManager() {
+        return fContext->fTextureStripAtlasManager.get();
+    }
+
     GrGpu* getGpu() { return fContext->fGpu.get(); }
     const GrGpu* getGpu() const { return fContext->fGpu.get(); }
 
diff --git a/src/gpu/GrTextureStripAtlas.h b/src/gpu/GrTextureStripAtlas.h
index a67438b..7a777b5 100644
--- a/src/gpu/GrTextureStripAtlas.h
+++ b/src/gpu/GrTextureStripAtlas.h
@@ -26,10 +26,9 @@
 public:
     /**
      * Descriptor struct which we'll use as a hash table key
-     **/
+     */
     struct Desc {
         Desc() { sk_bzero(this, sizeof(*this)); }
-        GrContext* fContext;
         GrPixelConfig fConfig;
         uint16_t fWidth, fHeight, fRowHeight;
         uint16_t fUnusedPadding;
@@ -38,11 +37,6 @@
         }
     };
 
-    /**
-     * Try to find an atlas with the required parameters, creates a new one if necessary
-     */
-    static GrTextureStripAtlas* GetAtlas(const Desc& desc);
-
     ~GrTextureStripAtlas();
 
     /**
@@ -51,7 +45,8 @@
      *  @return The row index we inserted into, or -1 if we failed to find an open row. The caller
      *      is responsible for calling unlockRow() with this row index when it's done with it.
      */
-    int lockRow(const SkBitmap& data);
+    int lockRow(GrContext*, const SkBitmap&);
+
     /**
      * This is intended to be used when cloning a processor that already holds a lock. It is
      * assumed that the row already has at least one lock.
@@ -78,11 +73,12 @@
     SkScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
     SkScalar getNormalizedTexelHeight() const { return fNormalizedYHeight; }
 
-    GrContext* getContext() const { return fDesc.fContext; }
-
     sk_sp<GrTextureProxy> asTextureProxyRef() const;
 
 private:
+    friend class GrTextureStripAtlasManager; // for ctor
+
+    static uint32_t CreateUniqueID();
 
     // Key to indicate an atlas row without any meaningful data stored in it
     const static uint32_t kEmptyAtlasRowKey = 0xffffffff;
@@ -103,11 +99,11 @@
     };
 
     /**
-     * We'll only allow construction via the static GrTextureStripAtlas::GetAtlas
+     * Only the GrTextureStripAtlasManager is allowed to create GrTextureStripAtlases
      */
-    GrTextureStripAtlas(Desc desc);
+    GrTextureStripAtlas(const Desc& desc);
 
-    void lockTexture();
+    void lockTexture(GrContext*);
     void unlockTexture();
 
     /**
@@ -116,7 +112,8 @@
     void initLRU();
 
     /**
-     * Grabs the least recently used free row out of the LRU list, returns nullptr if no rows are free.
+     * Grabs the least recently used free row out of the LRU list, returns nullptr if no rows
+     * are free.
      */
     AtlasRow* getLRU();
 
@@ -140,37 +137,9 @@
     void validate();
 #endif
 
-    /**
-     * Clean up callback registered with GrContext. Allows this class to
-     * free up any allocated AtlasEntry and GrTextureStripAtlas objects
-     */
-    static void CleanUp(const GrContext* context, void* info);
-
-    // Hash table entry for atlases
-    class AtlasEntry : public ::SkNoncopyable {
-    public:
-        // for SkTDynamicHash
-        static const Desc& GetKey(const AtlasEntry& entry) { return entry.fDesc; }
-        static uint32_t Hash(const Desc& desc) { return SkOpts::hash(&desc, sizeof(Desc)); }
-
-        // AtlasEntry proper
-        AtlasEntry() : fAtlas(nullptr) {}
-        ~AtlasEntry() { delete fAtlas; }
-        Desc fDesc;
-        GrTextureStripAtlas* fAtlas;
-    };
-
-    class Hash;
-    static Hash* gAtlasCache;
-
-    static Hash* GetCache();
-
-    // We increment gCacheCount for each atlas
-    static int32_t gCacheCount;
-
-    // A unique ID for this texture (formed with: gCacheCount++), so we can be sure that if we
+    // A unique ID for this atlas, so we can be sure that if we
     // get a texture back from the texture cache, that it's the same one we last used.
-    const int32_t fCacheKey;
+    const uint32_t fCacheKey;
 
     // Total locks on all rows (when this reaches zero, we can unlock our texture)
     int32_t fLockedRows;
@@ -195,4 +164,45 @@
     SkTDArray<AtlasRow*> fKeyTable;
 };
 
+class GrTextureStripAtlasManager {
+public:
+    GrTextureStripAtlasManager() {}
+    ~GrTextureStripAtlasManager();
+
+    void abandon();
+
+    /**
+     * Try to find an atlas with the required parameters, creates a new one if necessary
+     */
+    GrTextureStripAtlas* getAtlas(const GrTextureStripAtlas::Desc&);
+
+private:
+    void deleteAllAtlases();
+
+    // Hash table entry for atlases
+    class AtlasEntry : public ::SkNoncopyable {
+    public:
+        AtlasEntry(const GrTextureStripAtlas::Desc& desc, GrTextureStripAtlas* atlas)
+            : fDesc(desc)
+            , fAtlas(atlas) {
+        }
+        ~AtlasEntry() { delete fAtlas; }
+
+        // for SkTDynamicHash
+        static const GrTextureStripAtlas::Desc& GetKey(const AtlasEntry& entry) {
+            return entry.fDesc;
+        }
+        static uint32_t Hash(const GrTextureStripAtlas::Desc& desc) {
+            return SkOpts::hash(&desc, sizeof(GrTextureStripAtlas::Desc));
+        }
+
+        const GrTextureStripAtlas::Desc fDesc;
+        GrTextureStripAtlas* fAtlas;
+    };
+
+    typedef SkTDynamicHash<AtlasEntry, GrTextureStripAtlas::Desc> AtlasHash;
+
+    AtlasHash fAtlasCache;
+};
+
 #endif
diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp
index 59a35bc..4159d39 100644
--- a/src/gpu/effects/GrTextureStripAtlas.cpp
+++ b/src/gpu/effects/GrTextureStripAtlas.cpp
@@ -20,59 +20,50 @@
     #define VALIDATE
 #endif
 
-class GrTextureStripAtlas::Hash : public SkTDynamicHash<GrTextureStripAtlas::AtlasEntry,
-                                                        GrTextureStripAtlas::Desc> {};
-
-int32_t GrTextureStripAtlas::gCacheCount = 0;
-
-// DDL TODO: The texture strip atlas can't have this global!
-GrTextureStripAtlas::Hash* GrTextureStripAtlas::gAtlasCache = nullptr;
-
-GrTextureStripAtlas::Hash* GrTextureStripAtlas::GetCache() {
-
-    if (nullptr == gAtlasCache) {
-        gAtlasCache = new Hash;
-    }
-
-    return gAtlasCache;
+////////////////////////////////////////////////////////////////////////////////
+GrTextureStripAtlasManager::~GrTextureStripAtlasManager() {
+    this->deleteAllAtlases();
 }
 
-// Remove the specified atlas from the cache
-void GrTextureStripAtlas::CleanUp(const GrContext*, void* info) {
-    SkASSERT(info);
-
-    AtlasEntry* entry = static_cast<AtlasEntry*>(info);
-
-    // remove the cache entry
-    GetCache()->remove(entry->fDesc);
-
-    // remove the actual entry
-    delete entry;
-
-    if (0 == GetCache()->count()) {
-        delete gAtlasCache;
-        gAtlasCache = nullptr;
+void GrTextureStripAtlasManager::deleteAllAtlases() {
+    AtlasHash::Iter iter(&fAtlasCache);
+    while (!iter.done()) {
+        AtlasEntry* tmp = &(*iter);
+        ++iter;
+        delete tmp;
     }
+    fAtlasCache.reset();
 }
 
-GrTextureStripAtlas* GrTextureStripAtlas::GetAtlas(const GrTextureStripAtlas::Desc& desc) {
-    AtlasEntry* entry = GetCache()->find(desc);
-    if (nullptr == entry) {
-        entry = new AtlasEntry;
+void GrTextureStripAtlasManager::abandon() {
+    this->deleteAllAtlases();
+}
 
-        entry->fAtlas = new GrTextureStripAtlas(desc);
-        entry->fDesc = desc;
+GrTextureStripAtlas* GrTextureStripAtlasManager::getAtlas(const GrTextureStripAtlas::Desc& desc) {
+    AtlasEntry* entry = fAtlasCache.find(desc);
+    if (!entry) {
+        // TODO: Does the AtlasEntry need a copy of the Desc if the GrTextureStripAtlas has one?
+        entry = new AtlasEntry(desc, new GrTextureStripAtlas(desc));
 
-        desc.fContext->addCleanUp(CleanUp, entry);
-
-        GetCache()->add(entry);
+        fAtlasCache.add(entry);
     }
 
     return entry->fAtlas;
 }
 
-GrTextureStripAtlas::GrTextureStripAtlas(GrTextureStripAtlas::Desc desc)
-    : fCacheKey(sk_atomic_inc(&gCacheCount))
+////////////////////////////////////////////////////////////////////////////////
+uint32_t GrTextureStripAtlas::CreateUniqueID() {
+    static int32_t gUniqueID = SK_InvalidUniqueID;
+    uint32_t id;
+    // Loop in case our global wraps around, as we never want to return a 0.
+    do {
+        id = static_cast<uint32_t>(sk_atomic_inc(&gUniqueID) + 1);
+    } while (id == SK_InvalidUniqueID);
+    return id;
+}
+
+GrTextureStripAtlas::GrTextureStripAtlas(const Desc& desc)
+    : fCacheKey(CreateUniqueID())
     , fLockedRows(0)
     , fDesc(desc)
     , fNumRows(desc.fHeight / desc.fRowHeight)
@@ -94,10 +85,10 @@
     ++fLockedRows;
 }
 
-int GrTextureStripAtlas::lockRow(const SkBitmap& bitmap) {
+int GrTextureStripAtlas::lockRow(GrContext* context, const SkBitmap& bitmap) {
     VALIDATE;
 
-    if (!this->getContext()->contextPriv().resourceProvider()) {
+    if (!context->contextPriv().resourceProvider()) {
         // DDL TODO: For DDL we need to schedule inline & ASAP uploads. However these systems
         // currently use the flushState which we can't use for the opList-based DDL phase.
         // For the opList-based solution every texture strip will get its own texture proxy.
@@ -106,7 +97,7 @@
     }
 
     if (0 == fLockedRows) {
-        this->lockTexture();
+        this->lockTexture(context);
         if (!fTexContext) {
             return -1;
         }
@@ -139,7 +130,7 @@
 
         if (nullptr == row) {
             // force a flush, which should unlock all the rows; then try again
-            fDesc.fContext->contextPriv().flush(nullptr); // tighten this up?
+            context->contextPriv().flush(nullptr); // tighten this up?
             row = this->getLRU();
             if (nullptr == row) {
                 --fLockedRows;
@@ -209,7 +200,7 @@
     return row;
 }
 
-void GrTextureStripAtlas::lockTexture() {
+void GrTextureStripAtlas::lockTexture(GrContext* context) {
 
     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
     GrUniqueKey key;
@@ -217,7 +208,7 @@
     builder[0] = static_cast<uint32_t>(fCacheKey);
     builder.finish();
 
-    GrProxyProvider* proxyProvider = fDesc.fContext->contextPriv().proxyProvider();
+    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
 
     sk_sp<GrTextureProxy> proxy = proxyProvider->findOrCreateProxyByUniqueKey(
                                                                 key, kTopLeft_GrSurfaceOrigin);
@@ -240,7 +231,7 @@
         fKeyTable.rewind();
     }
     SkASSERT(proxy);
-    fTexContext = fDesc.fContext->contextPriv().makeWrappedSurfaceContext(std::move(proxy));
+    fTexContext = context->contextPriv().makeWrappedSurfaceContext(std::move(proxy));
 }
 
 void GrTextureStripAtlas::unlockTexture() {
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index 8f0c5c8..e5f0c7f 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -1291,21 +1291,21 @@
         shader.getGradientTableBitmap(&bitmap, bitmapType);
         SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
 
+        auto atlasManager = args.fContext->contextPriv().textureStripAtlasManager();
 
         GrTextureStripAtlas::Desc desc;
         desc.fWidth  = bitmap.width();
         desc.fHeight = 32;
         desc.fRowHeight = bitmap.height(); // always 1 here
-        desc.fContext = args.fContext;
         desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *args.fContext->caps());
-        fAtlas = GrTextureStripAtlas::GetAtlas(desc);
+        fAtlas = atlasManager->getAtlas(desc);
         SkASSERT(fAtlas);
 
         // We always filter the gradient table. Each table is one row of a texture, always
         // y-clamp.
         GrSamplerState samplerState(args.fWrapMode, GrSamplerState::Filter::kBilerp);
 
-        fRow = fAtlas->lockRow(bitmap);
+        fRow = fAtlas->lockRow(args.fContext, bitmap);
         if (-1 != fRow) {
             fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
             // This is 1/2 places where auto-normalization is disabled