Change bitmapcache to not rely on lockpixels.

The Rec in the cache is the owner of the pixel memory
- discardable or
- malloc

Each external client has a pixelref that just points to those pixels,
and whose destructor will notify the rec.

This eliminates the dependency on lockPixels in pixelref, freeing us
to remove that entirely from pixelref.

Bug: skia:
Change-Id: If45ed0ae202a1211336626364235215253e8aa7c
Reviewed-on: https://skia-review.googlesource.com/10300
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Matt Sarett <msarett@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/src/core/SkBitmapCache.cpp b/src/core/SkBitmapCache.cpp
index 064fc46..50553a0 100644
--- a/src/core/SkBitmapCache.cpp
+++ b/src/core/SkBitmapCache.cpp
@@ -26,10 +26,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-SkBitmap::Allocator* SkBitmapCache::GetAllocator() {
-    return SkResourceCache::GetAllocator();
-}
-
 /**
  This function finds the bounds of the bitmap *within its pixelRef*.
  If the bitmap lacks a pixelRef, it will return an empty rect, since
@@ -109,61 +105,227 @@
 
     const SkBitmapCacheDesc fDesc;
 };
+}
 
-struct BitmapRec : public SkResourceCache::Rec {
-    BitmapRec(const SkBitmapCacheDesc& desc, const SkBitmap& result)
+//////////////////////
+#include "SkDiscardableMemory.h"
+#include "SkNextID.h"
+
+void SkBitmapCache_setImmutableWithID(SkPixelRef* pr, uint32_t id) {
+    pr->setImmutableWithID(id);
+}
+
+//#define REC_TRACE   SkDebugf
+static void REC_TRACE(const char format[], ...) {}
+
+// for diagnostics
+static int32_t gRecCounter;
+
+class SkBitmapCache::Rec : public SkResourceCache::Rec {
+public:
+    Rec(const SkBitmapCacheDesc& desc, const SkImageInfo& info, size_t rowBytes,
+        std::unique_ptr<SkDiscardableMemory> dm, void* block)
         : fKey(desc)
-        , fBitmap(result)
+        , fDM(std::move(dm))
+        , fMalloc(block)
+        , fInfo(info)
+        , fRowBytes(rowBytes)
+        , fExternalCounter(kBeforeFirstInstall_ExternalCounter)
     {
-#ifdef TRACE_NEW_BITMAP_CACHE_RECS
-        fKey.dump();
-#endif
+        SkASSERT(!(fDM && fMalloc));    // can't have both
+
+        // We need an ID to return with the bitmap/pixelref.
+        // If they are not scaling, we can return the same ID as the key/desc
+        // If they are scaling, we need a new ID
+        if (desc.fScaledWidth == 0 && desc.fScaledHeight == 0) {
+            fPrUniqueID = desc.fImageID;
+        } else {
+            fPrUniqueID = SkNextID::ImageID();
+        }
+        REC_TRACE(" Rec(%d): [%d %d] %d\n",
+                  sk_atomic_inc(&gRecCounter), fInfo.width(), fInfo.height(), fPrUniqueID);
+    }
+
+    ~Rec() override {
+        SkASSERT(0 == fExternalCounter || kBeforeFirstInstall_ExternalCounter == fExternalCounter);
+        if (fDM && kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
+            // we never installed, so we need to unlock before we destroy the DM
+            SkASSERT(fDM->data());
+            fDM->unlock();
+        }
+        REC_TRACE("~Rec(%d): [%d %d] %d\n",
+                  sk_atomic_dec(&gRecCounter) - 1, fInfo.width(), fInfo.height(), fPrUniqueID);
+        sk_free(fMalloc);   // may be null
     }
 
     const Key& getKey() const override { return fKey; }
-    size_t bytesUsed() const override { return sizeof(fKey) + fBitmap.getSize(); }
+    size_t bytesUsed() const override {
+        return sizeof(fKey) + fInfo.getSafeSize(fRowBytes);
+    }
+    bool canBePurged() override {
+        SkAutoMutexAcquire ama(fMutex);
+        return fExternalCounter == 0;
+    }
+    void postAddInstall(void* payload) override {
+        SkAssertResult(this->install(static_cast<SkBitmap*>(payload)));
+    }
 
     const char* getCategory() const override { return "bitmap"; }
     SkDiscardableMemory* diagnostic_only_getDiscardable() const override {
-        return fBitmap.pixelRef()->diagnostic_only_getDiscardable();
+        return fDM.get();
+    }
+
+    static void ReleaseProc(void* addr, void* ctx) {
+        Rec* rec = static_cast<Rec*>(ctx);
+        SkAutoMutexAcquire ama(rec->fMutex);
+
+        REC_TRACE(" Rec: [%d] releaseproc\n", rec->fPrUniqueID);
+
+        SkASSERT(rec->fExternalCounter > 0);
+        rec->fExternalCounter -= 1;
+        if (rec->fDM) {
+            SkASSERT(rec->fMalloc == nullptr);
+            if (rec->fExternalCounter == 0) {
+                REC_TRACE(" Rec [%d] unlock\n", rec->fPrUniqueID);
+                rec->fDM->unlock();
+            }
+        } else {
+            SkASSERT(rec->fMalloc != nullptr);
+        }
+    }
+    
+    bool install(SkBitmap* bitmap) {
+        SkAutoMutexAcquire ama(fMutex);
+
+        // are we still valid
+        if (!fDM && !fMalloc) {
+            REC_TRACE(" Rec: [%d] invalid\n", fPrUniqueID);
+            return false;
+        }
+
+        /*
+            constructor      fExternalCount < 0     fDM->data()
+            after install    fExternalCount > 0     fDM->data()
+            after Release    fExternalCount == 0    !fDM->data()
+        */
+        if (fDM) {
+            if (kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
+                SkASSERT(fDM->data());
+            } else if (fExternalCounter > 0) {
+                SkASSERT(fDM->data());
+            } else {
+                SkASSERT(fExternalCounter == 0);
+                if (!fDM->lock()) {
+                    REC_TRACE(" Rec [%d] re-lock failed\n", fPrUniqueID);
+                    fDM.reset(nullptr);
+                    return false;
+                }
+                REC_TRACE(" Rec [%d] re-lock succeeded\n", fPrUniqueID);
+            }
+            SkASSERT(fDM->data());
+        }
+
+        bitmap->installPixels(fInfo, fDM ? fDM->data() : fMalloc, fRowBytes, nullptr,
+                              ReleaseProc, this);
+        SkBitmapCache_setImmutableWithID(bitmap->pixelRef(), fPrUniqueID);
+
+        REC_TRACE(" Rec: [%d] install new pr\n", fPrUniqueID);
+
+        if (kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
+            fExternalCounter = 1;
+        } else {
+            fExternalCounter += 1;
+        }
+        SkASSERT(fExternalCounter > 0);
+        return true;
     }
 
     static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) {
-        const BitmapRec& rec = static_cast<const BitmapRec&>(baseRec);
+        Rec* rec = (Rec*)&baseRec;
         SkBitmap* result = (SkBitmap*)contextBitmap;
-
-        *result = rec.fBitmap;
-        result->lockPixels();
-        return SkToBool(result->getPixels());
+        REC_TRACE(" Rec: [%d] found\n", rec->fPrUniqueID);
+        return rec->install(result);
     }
 
 private:
     BitmapKey   fKey;
-    SkBitmap    fBitmap;
+
+    SkMutex     fMutex;
+
+    // either fDM or fMalloc can be non-null, but not both
+    std::unique_ptr<SkDiscardableMemory> fDM;
+    void*       fMalloc;
+
+    SkImageInfo fInfo;
+    size_t      fRowBytes;
+    uint32_t    fPrUniqueID;
+
+    // This field counts the number of external pixelrefs we have created. They notify us when
+    // they are destroyed so we can decrement this.
+    //
+    //  > 0     we have outstanding pixelrefs
+    // == 0     we have no outstanding pixelrefs, and can be safely purged
+    //  < 0     we have been created, but not yet "installed" the first time.
+    //
+    int         fExternalCounter;
+
+    enum {
+        kBeforeFirstInstall_ExternalCounter = -1
+    };
 };
-} // namespace
+
+void SkBitmapCache::PrivateDeleteRec(Rec* rec) { delete rec; }
+
+SkBitmapCache::RecPtr SkBitmapCache::Alloc(const SkBitmapCacheDesc& desc, const SkImageInfo& info,
+                                           SkPixmap* pmap) {
+    // Ensure that the caller is self-consistent:
+    //  - if they are scaling, the info matches the scaled size
+    //  - if they are not, the info matches the subset (i.e. the subset is the entire image)
+    if (desc.fScaledWidth == 0 && desc.fScaledHeight == 0) {
+        SkASSERT(info.width() == desc.fSubset.width());
+        SkASSERT(info.height() == desc.fSubset.height());
+    } else {
+        SkASSERT(info.width() == desc.fScaledWidth);
+        SkASSERT(info.height() == desc.fScaledHeight);
+    }
+
+    const size_t rb = info.minRowBytes();
+    size_t size = info.getSafeSize(rb);
+    if (0 == size) {
+        return nullptr;
+    }
+
+    std::unique_ptr<SkDiscardableMemory> dm;
+    void* block = nullptr;
+
+    auto factory = SkResourceCache::GetDiscardableFactory();
+    if (factory) {
+        dm.reset(factory(size));
+    } else {
+        block = sk_malloc_flags(size, 0);
+    }
+    if (!dm && !block) {
+        return nullptr;
+    }
+    *pmap = SkPixmap(info, dm ? dm->data() : block, rb);
+    return RecPtr(new Rec(desc, info, rb, std::move(dm), block));
+}
+
+void SkBitmapCache::Add(RecPtr rec, SkBitmap* bitmap) {
+    SkResourceCache::Add(rec.release(), bitmap);
+}
+
+bool SkBitmapCache::Find(const SkBitmapCacheDesc& desc, SkBitmap* result) {
+    desc.validate();
+    return SkResourceCache::Find(BitmapKey(desc), SkBitmapCache::Rec::Finder, result);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
 
 #define CHECK_LOCAL(localCache, localName, globalName, ...) \
     ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__))
 
-bool SkBitmapCache::Find(const SkBitmapCacheDesc& desc, SkBitmap* result,
-                         SkResourceCache* localCache) {
-    desc.validate();
-    return CHECK_LOCAL(localCache, find, Find, BitmapKey(desc), BitmapRec::Finder, result);
-}
-
-bool SkBitmapCache::Add(const SkBitmapCacheDesc& desc, const SkBitmap& result,
-                        SkResourceCache* localCache) {
-    desc.validate();
-    SkASSERT(result.isImmutable());
-    BitmapRec* rec = new BitmapRec(desc, result);
-    CHECK_LOCAL(localCache, add, Add, rec);
-    return true;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////
-
 namespace {
 static unsigned gMipMapKeyNamespaceLabel;