diff options
author | reed <reed@google.com> | 2015-02-19 08:22:54 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-19 08:22:54 -0800 |
commit | 4675819b9dbb3ad71ec851776e5de26d342f29fe (patch) | |
tree | ba268a491ce8a458f5c8ac9f0f6e5f8bfd5f658e /src | |
parent | 67b21a7eef393bbee86482c75b7ea8dfc474d08a (diff) |
notify resource caches when pixelref genID goes stale
BUG=skia:
Review URL: https://codereview.chromium.org/825263005
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkBitmapCache.cpp | 84 | ||||
-rw-r--r-- | src/core/SkBitmapCache.h | 10 | ||||
-rw-r--r-- | src/core/SkPixelRef.cpp | 11 | ||||
-rw-r--r-- | src/core/SkResourceCache.cpp | 39 | ||||
-rw-r--r-- | src/core/SkResourceCache.h | 38 |
5 files changed, 159 insertions, 23 deletions
diff --git a/src/core/SkBitmapCache.cpp b/src/core/SkBitmapCache.cpp index 3af582232b..9929080164 100644 --- a/src/core/SkBitmapCache.cpp +++ b/src/core/SkBitmapCache.cpp @@ -33,11 +33,11 @@ static unsigned gBitmapKeyNamespaceLabel; struct BitmapKey : public SkResourceCache::Key { public: - BitmapKey(uint32_t genID, SkScalar scaleX, SkScalar scaleY, const SkIRect& bounds) - : fGenID(genID) - , fScaleX(scaleX) - , fScaleY(scaleY) - , fBounds(bounds) + BitmapKey(uint32_t genID, SkScalar sx, SkScalar sy, const SkIRect& bounds) + : fGenID(genID) + , fScaleX(sx) + , fScaleY(sy) + , fBounds(bounds) { this->init(&gBitmapKeyNamespaceLabel, sizeof(fGenID) + sizeof(fScaleX) + sizeof(fScaleY) + sizeof(fBounds)); @@ -49,8 +49,6 @@ public: SkIRect fBounds; }; -////////////////////////////////////////////////////////////////////////////////////////// - struct BitmapRec : public SkResourceCache::Rec { BitmapRec(uint32_t genID, SkScalar scaleX, SkScalar scaleY, const SkIRect& bounds, const SkBitmap& result) @@ -58,13 +56,10 @@ struct BitmapRec : public SkResourceCache::Rec { , fBitmap(result) {} - BitmapKey fKey; - SkBitmap fBitmap; - const Key& getKey() const SK_OVERRIDE { return fKey; } size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + fBitmap.getSize(); } - static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextBitmap) { + static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) { const BitmapRec& rec = static_cast<const BitmapRec&>(baseRec); SkBitmap* result = (SkBitmap*)contextBitmap; @@ -72,6 +67,22 @@ struct BitmapRec : public SkResourceCache::Rec { result->lockPixels(); return SkToBool(result->getPixels()); } + + static SkResourceCache::PurgeVisitorResult PurgeGenID(const SkResourceCache::Rec& baseRec, + void* contextGenID) { + const BitmapRec& rec = static_cast<const BitmapRec&>(baseRec); + uintptr_t genID = (uintptr_t)contextGenID; + if (rec.fKey.fGenID == genID) { +// SkDebugf("BitmapRec purging genID %d\n", genID); + return SkResourceCache::kPurgeAndContinue_PurgeVisitorResult; + } else { + return SkResourceCache::kRetainAndContinue_PurgeVisitorResult; + } + } + +private: + BitmapKey fKey; + SkBitmap fBitmap; }; } // namespace @@ -86,7 +97,7 @@ bool SkBitmapCache::Find(const SkBitmap& src, SkScalar invScaleX, SkScalar invSc } BitmapKey key(src.getGenerationID(), invScaleX, invScaleY, get_bounds_from_bitmap(src)); - return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Visitor, result); + return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result); } void SkBitmapCache::Add(const SkBitmap& src, SkScalar invScaleX, SkScalar invScaleY, @@ -105,7 +116,7 @@ bool SkBitmapCache::Find(uint32_t genID, const SkIRect& subset, SkBitmap* result SkResourceCache* localCache) { BitmapKey key(genID, SK_Scalar1, SK_Scalar1, subset); - return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Visitor, result); + return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result); } bool SkBitmapCache::Add(uint32_t genID, const SkIRect& subset, const SkBitmap& result, @@ -125,11 +136,31 @@ bool SkBitmapCache::Add(uint32_t genID, const SkIRect& subset, const SkBitmap& r return true; } } + +void SkBitmapCache::NotifyGenIDStale(uint32_t genID) { + SkResourceCache::Purge(&gBitmapKeyNamespaceLabel, BitmapRec::PurgeGenID, + (void*)((uintptr_t)genID)); +} + ////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +namespace { +static unsigned gMipMapKeyNamespaceLabel; + +struct MipMapKey : public SkResourceCache::Key { +public: + MipMapKey(uint32_t genID, const SkIRect& bounds) : fGenID(genID), fBounds(bounds) { + this->init(&gMipMapKeyNamespaceLabel, sizeof(fGenID) + sizeof(fBounds)); + } + + uint32_t fGenID; + SkIRect fBounds; +}; struct MipMapRec : public SkResourceCache::Rec { MipMapRec(const SkBitmap& src, const SkMipMap* result) - : fKey(src.getGenerationID(), 0, 0, get_bounds_from_bitmap(src)) + : fKey(src.getGenerationID(), get_bounds_from_bitmap(src)) , fMipMap(result) { fMipMap->attachToCacheAndRef(); @@ -142,7 +173,7 @@ struct MipMapRec : public SkResourceCache::Rec { const Key& getKey() const SK_OVERRIDE { return fKey; } size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + fMipMap->size(); } - static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextMip) { + static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) { const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec); const SkMipMap* mm = SkRef(rec.fMipMap); // the call to ref() above triggers a "lock" in the case of discardable memory, @@ -156,16 +187,29 @@ struct MipMapRec : public SkResourceCache::Rec { return true; } + static SkResourceCache::PurgeVisitorResult PurgeGenID(const SkResourceCache::Rec& baseRec, + void* contextGenID) { + const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec); + uintptr_t genID = (uintptr_t)contextGenID; + if (rec.fKey.fGenID == genID) { +// SkDebugf("MipMapRec purging genID %d\n", genID); + return SkResourceCache::kPurgeAndContinue_PurgeVisitorResult; + } else { + return SkResourceCache::kRetainAndContinue_PurgeVisitorResult; + } + } + private: - BitmapKey fKey; + MipMapKey fKey; const SkMipMap* fMipMap; }; +} const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmap& src, SkResourceCache* localCache) { - BitmapKey key(src.getGenerationID(), 0, 0, get_bounds_from_bitmap(src)); + MipMapKey key(src.getGenerationID(), get_bounds_from_bitmap(src)); const SkMipMap* result; - if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Visitor, &result)) { + if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) { result = NULL; } return result; @@ -185,3 +229,7 @@ const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmap& src, SkResourceCache* l return mipmap; } +void SkMipMapCache::NotifyGenIDStale(uint32_t genID) { + SkResourceCache::Purge(&gMipMapKeyNamespaceLabel, MipMapRec::PurgeGenID, + (void*)((uintptr_t)genID)); +} diff --git a/src/core/SkBitmapCache.h b/src/core/SkBitmapCache.h index ec8a6b803e..abda1b4d66 100644 --- a/src/core/SkBitmapCache.h +++ b/src/core/SkBitmapCache.h @@ -48,12 +48,22 @@ public: */ static bool Add(uint32_t genID, const SkIRect& subset, const SkBitmap& result, SkResourceCache* localCache = NULL); + + /** + * Call this to (as a hint) preemptively purge caches related to this genID + */ + static void NotifyGenIDStale(uint32_t); }; class SkMipMapCache { public: static const SkMipMap* FindAndRef(const SkBitmap& src, SkResourceCache* localCache = NULL); static const SkMipMap* AddAndRef(const SkBitmap& src, SkResourceCache* localCache = NULL); + + /** + * Call this to (as a hint) preemptively purge caches related to this genID + */ + static void NotifyGenIDStale(uint32_t); }; #endif diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp index f56298165c..a2638c9431 100644 --- a/src/core/SkPixelRef.cpp +++ b/src/core/SkPixelRef.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkBitmapCache.h" #include "SkPixelRef.h" #include "SkThread.h" @@ -222,6 +223,7 @@ void SkPixelRef::addGenIDChangeListener(GenIDChangeListener* listener) { *fGenIDChangeListeners.append() = listener; } +// we need to be called *before* the genID gets changed or zerod void SkPixelRef::callGenIDChangeListeners() { // We don't invalidate ourselves if we think another SkPixelRef is sharing our genID. if (fUniqueGenerationID) { @@ -231,6 +233,15 @@ void SkPixelRef::callGenIDChangeListeners() { } // Listeners get at most one shot, so whether these triggered or not, blow them away. fGenIDChangeListeners.deleteAll(); + + // if fGenerationID is 0, then perhaps we never had one, and we are in the destructor + if (fGenerationID) { + // If the ResourceCache ever generalizes/standardizes on accessing the gen-id field, + // then it would be more efficient to roll these together, and only grab the mutex + // and fixing the resources once... + SkBitmapCache::NotifyGenIDStale(fGenerationID); + SkMipMapCache::NotifyGenIDStale(fGenerationID); + } } void SkPixelRef::notifyPixelsChanged() { diff --git a/src/core/SkResourceCache.cpp b/src/core/SkResourceCache.cpp index 4ed889a0ab..62263605de 100644 --- a/src/core/SkResourceCache.cpp +++ b/src/core/SkResourceCache.cpp @@ -61,6 +61,8 @@ void SkResourceCache::init() { // One of these should be explicit set by the caller after we return. fTotalByteLimit = 0; fDiscardableFactory = NULL; + + fInsidePurgeAllCounter = 0; } #include "SkDiscardableMemory.h" @@ -199,7 +201,7 @@ SkResourceCache::~SkResourceCache() { //////////////////////////////////////////////////////////////////////////////// -bool SkResourceCache::find(const Key& key, VisitorProc visitor, void* context) { +bool SkResourceCache::find(const Key& key, FindVisitor visitor, void* context) { Rec* rec = fHash->find(key); if (rec) { if (visitor(*rec, context)) { @@ -294,6 +296,34 @@ void SkResourceCache::purgeAsNeeded(bool forcePurge) { } } +void SkResourceCache::purge(const void* nameSpace, PurgeVisitor proc, void* context) { + if (this->insidePurgeAll()) { + return; + } + + // go backwards, just like purgeAsNeeded, just to make the code similar. + // could iterate either direction and still be correct. + Rec* rec = fTail; + while (rec) { + Rec* prev = rec->fPrev; + if (rec->getKey().getNamespace() == nameSpace) { + switch (proc(*rec, context)) { + case kRetainAndContinue_PurgeVisitorResult: + break; + case kPurgeAndContinue_PurgeVisitorResult: + this->remove(rec); + break; + case kRetainAndStop_PurgeVisitorResult: + return; + case kPurgeAndStop_PurgeVisitorResult: + this->remove(rec); + return; + } + } + rec = prev; + } +} + size_t SkResourceCache::setTotalByteLimit(size_t newLimit) { size_t prevLimit = fTotalByteLimit; fTotalByteLimit = newLimit; @@ -532,12 +562,17 @@ size_t SkResourceCache::GetEffectiveSingleAllocationByteLimit() { return get_cache()->getEffectiveSingleAllocationByteLimit(); } +void SkResourceCache::Purge(const void* nameSpace, PurgeVisitor proc, void* context) { + SkAutoMutexAcquire am(gMutex); + return get_cache()->purge(nameSpace, proc, context); +} + void SkResourceCache::PurgeAll() { SkAutoMutexAcquire am(gMutex); return get_cache()->purgeAll(); } -bool SkResourceCache::Find(const Key& key, VisitorProc visitor, void* context) { +bool SkResourceCache::Find(const Key& key, FindVisitor visitor, void* context) { SkAutoMutexAcquire am(gMutex); return get_cache()->find(key, visitor, context); } diff --git a/src/core/SkResourceCache.h b/src/core/SkResourceCache.h index 88ccb87ed8..36ca27cd73 100644 --- a/src/core/SkResourceCache.h +++ b/src/core/SkResourceCache.h @@ -35,6 +35,8 @@ public: // length must be a multiple of 4 void init(void* nameSpace, size_t length); + void* getNamespace() const { return fNamespace; } + // This is only valid after having called init(). uint32_t hash() const { return fHash; } @@ -92,7 +94,21 @@ public: * true, then the Rec is considered "valid". If false is returned, the Rec will be considered * "stale" and will be purged from the cache. */ - typedef bool (*VisitorProc)(const Rec&, void* context); + typedef bool (*FindVisitor)(const Rec&, void* context); + + enum PurgeVisitorResult { + kRetainAndContinue_PurgeVisitorResult, + kPurgeAndContinue_PurgeVisitorResult, + kRetainAndStop_PurgeVisitorResult, + kPurgeAndStop_PurgeVisitorResult, + }; + + /** + * Callback function for purge(). If called, the cache will have found a match for the + * specified Key, and will pass in the corresponding Rec, along with a caller-specified + * context. The function can read the data in Rec. + */ + typedef PurgeVisitorResult (*PurgeVisitor)(const Rec&, void* context); /** * Returns a locked/pinned SkDiscardableMemory instance for the specified @@ -114,7 +130,7 @@ public: * true : Rec is valid * false : Rec is "stale" -- the cache will purge it. */ - static bool Find(const Key& key, VisitorProc, void* context); + static bool Find(const Key& key, FindVisitor, void* context); static void Add(Rec*); static size_t GetTotalBytesUsed(); @@ -125,6 +141,12 @@ public: static size_t GetSingleAllocationByteLimit(); static size_t GetEffectiveSingleAllocationByteLimit(); + /** + * Visit all Rec that match the specified namespace, and purge entries as indicated by the + * visitor. + */ + static void Purge(const void* nameSpace, PurgeVisitor, void* context); + static void PurgeAll(); /** @@ -174,7 +196,7 @@ public: * true : Rec is valid * false : Rec is "stale" -- the cache will purge it. */ - bool find(const Key&, VisitorProc, void* context); + bool find(const Key&, FindVisitor, void* context); void add(Rec*); size_t getTotalBytesUsed() const { return fTotalBytesUsed; } @@ -198,8 +220,12 @@ public: */ size_t setTotalByteLimit(size_t newLimit); + void purge(const void* nameSpace, PurgeVisitor, void* context); + void purgeAll() { + fInsidePurgeAllCounter += 1; this->purgeAsNeeded(true); + fInsidePurgeAllCounter -= 1; } DiscardableFactory discardableFactory() const { return fDiscardableFactory; } @@ -228,6 +254,12 @@ private: size_t fSingleAllocationByteLimit; int fCount; + bool insidePurgeAll() const { + SkASSERT(fInsidePurgeAllCounter >= 0); + return fInsidePurgeAllCounter > 0; + } + int fInsidePurgeAllCounter; + void purgeAsNeeded(bool forcePurge = false); // linklist management |