Index: src/core/SkGlyphCache.cpp |
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp |
index 538c9682f529848f438ddc5a1e0f1c18af2cda14..9dd235c1118564f1ad4e5c07a68abf83e36ba297 100644 |
--- a/src/core/SkGlyphCache.cpp |
+++ b/src/core/SkGlyphCache.cpp |
@@ -8,6 +8,7 @@ |
#include "SkGlyphCache.h" |
+#include "SkGlyphCache_Globals.h" |
#include "SkGraphics.h" |
#include "SkPaint.h" |
#include "SkPath.h" |
@@ -20,6 +21,20 @@ |
bool gSkSuppressFontCachePurgeSpew; |
+// Returns the shared globals |
+static SkGlyphCache_Globals& getSharedGlobals() { |
+ // we leak this, so we don't incur any shutdown cost of the destructor |
+ static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals, |
+ (SkGlyphCache_Globals::kYes_UseMutex)); |
+ return *gGlobals; |
+} |
+ |
+// Returns the TLS globals (if set), or the shared globals |
+static SkGlyphCache_Globals& getGlobals() { |
+ SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); |
+ return tls ? *tls : getSharedGlobals(); |
+} |
+ |
/////////////////////////////////////////////////////////////////////////////// |
#ifdef RECORD_HASH_EFFICIENCY |
@@ -397,108 +412,38 @@ void SkGlyphCache::invokeAndRemoveAuxProcs() { |
/////////////////////////////////////////////////////////////////////////////// |
/////////////////////////////////////////////////////////////////////////////// |
-#ifndef SK_DEFAULT_FONT_CACHE_LIMIT |
- #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024) |
-#endif |
- |
#include "SkThread.h" |
-class SkGlyphCache_Globals { |
-public: |
- enum UseMutex { |
- kNo_UseMutex, // thread-local cache |
- kYes_UseMutex // shared cache |
- }; |
- |
- SkGlyphCache_Globals(UseMutex um) { |
- fHead = NULL; |
- fTotalMemoryUsed = 0; |
- fFontCacheLimit = SK_DEFAULT_FONT_CACHE_LIMIT; |
- fMutex = (kYes_UseMutex == um) ? SkNEW(SkMutex) : NULL; |
- } |
- |
- ~SkGlyphCache_Globals() { |
- SkGlyphCache* cache = fHead; |
- while (cache) { |
- SkGlyphCache* next = cache->fNext; |
- SkDELETE(cache); |
- cache = next; |
- } |
- |
- SkDELETE(fMutex); |
- } |
- |
- SkMutex* fMutex; |
- SkGlyphCache* fHead; |
- size_t fTotalMemoryUsed; |
- |
-#ifdef SK_DEBUG |
- void validate() const; |
-#else |
- void validate() const {} |
-#endif |
- |
- size_t getFontCacheLimit() const { return fFontCacheLimit; } |
- size_t setFontCacheLimit(size_t limit); |
- void purgeAll(); // does not change budget |
- |
- // can return NULL |
- static SkGlyphCache_Globals* FindTLS() { |
- return (SkGlyphCache_Globals*)SkTLS::Find(CreateTLS); |
- } |
- |
- static SkGlyphCache_Globals& GetTLS() { |
- return *(SkGlyphCache_Globals*)SkTLS::Get(CreateTLS, DeleteTLS); |
- } |
- |
- static void DeleteTLS() { SkTLS::Delete(CreateTLS); } |
- |
-private: |
- size_t fFontCacheLimit; |
- |
- static void* CreateTLS() { |
- return SkNEW_ARGS(SkGlyphCache_Globals, (kNo_UseMutex)); |
- } |
- |
- static void DeleteTLS(void* ptr) { |
- SkDELETE((SkGlyphCache_Globals*)ptr); |
- } |
-}; |
- |
-size_t SkGlyphCache_Globals::setFontCacheLimit(size_t newLimit) { |
+size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) { |
static const size_t minLimit = 256 * 1024; |
if (newLimit < minLimit) { |
newLimit = minLimit; |
} |
- |
- size_t prevLimit = fFontCacheLimit; |
- fFontCacheLimit = newLimit; |
- |
- size_t currUsed = fTotalMemoryUsed; |
- if (currUsed > newLimit) { |
- SkAutoMutexAcquire ac(fMutex); |
- SkGlyphCache::InternalFreeCache(this, currUsed - newLimit); |
- } |
+ |
+ SkAutoMutexAcquire ac(fMutex); |
+ |
+ size_t prevLimit = fCacheSizeLimit; |
+ fCacheSizeLimit = newLimit; |
+ this->internalPurge(); |
return prevLimit; |
} |
-void SkGlyphCache_Globals::purgeAll() { |
+int SkGlyphCache_Globals::setCacheCountLimit(int newCount) { |
+ if (newCount < 0) { |
+ newCount = 0; |
+ } |
+ |
SkAutoMutexAcquire ac(fMutex); |
- SkGlyphCache::InternalFreeCache(this, fTotalMemoryUsed); |
+ |
+ int prevCount = fCacheCountLimit; |
+ fCacheCountLimit = newCount; |
+ this->internalPurge(); |
+ return prevCount; |
} |
-// Returns the shared globals |
-static SkGlyphCache_Globals& getSharedGlobals() { |
- // we leak this, so we don't incur any shutdown cost of the destructor |
- static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals, |
- (SkGlyphCache_Globals::kYes_UseMutex)); |
- return *gGlobals; |
-} |
- |
-// Returns the TLS globals (if set), or the shared globals |
-static SkGlyphCache_Globals& getGlobals() { |
- SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); |
- return tls ? *tls : getSharedGlobals(); |
+void SkGlyphCache_Globals::purgeAll() { |
+ SkAutoMutexAcquire ac(fMutex); |
+ this->internalPurge(fTotalMemoryUsed); |
} |
void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), |
@@ -509,7 +454,7 @@ void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), |
globals.validate(); |
- for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { |
+ for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) { |
if (proc(cache, context)) { |
break; |
} |
@@ -540,9 +485,9 @@ SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface, |
globals.validate(); |
- for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { |
+ for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) { |
if (cache->fDesc->equals(*desc)) { |
- cache->detach(&globals.fHead); |
+ globals.internalDetachCache(cache); |
goto FOUND_IT; |
} |
} |
@@ -572,16 +517,11 @@ FOUND_IT: |
AutoValidate av(cache); |
- if (proc(cache, context)) { // stay detached |
- if (insideMutex) { |
- SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed); |
- globals.fTotalMemoryUsed -= cache->fMemoryUsed; |
- } |
- } else { // reattach |
+ if (!proc(cache, context)) { // need to reattach |
if (insideMutex) { |
- cache->attachToHead(&globals.fHead); |
+ globals.internalAttachCacheToHead(cache); |
} else { |
- AttachCache(cache); |
+ globals.attachCacheToHead(cache); |
} |
cache = NULL; |
} |
@@ -592,30 +532,23 @@ void SkGlyphCache::AttachCache(SkGlyphCache* cache) { |
SkASSERT(cache); |
SkASSERT(cache->fNext == NULL); |
- SkGlyphCache_Globals& globals = getGlobals(); |
- SkAutoMutexAcquire ac(globals.fMutex); |
+ getGlobals().attachCacheToHead(cache); |
+} |
- globals.validate(); |
- cache->validate(); |
+/////////////////////////////////////////////////////////////////////////////// |
- // if we have a fixed budget for our cache, do a purge here |
- { |
- size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed; |
- size_t budgeted = globals.getFontCacheLimit(); |
- if (allocated > budgeted) { |
- (void)InternalFreeCache(&globals, allocated - budgeted); |
- } |
- } |
+void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) { |
+ SkAutoMutexAcquire ac(fMutex); |
- cache->attachToHead(&globals.fHead); |
- globals.fTotalMemoryUsed += cache->fMemoryUsed; |
+ this->validate(); |
+ cache->validate(); |
- globals.validate(); |
+ this->internalAttachCacheToHead(cache); |
+ this->internalPurge(); |
} |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { |
+SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const { |
+ SkGlyphCache* cache = fHead; |
if (cache) { |
while (cache->fNext) { |
cache = cache->fNext; |
@@ -624,63 +557,92 @@ SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { |
return cache; |
} |
-#ifdef SK_DEBUG |
-void SkGlyphCache_Globals::validate() const { |
- size_t computed = 0; |
+size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) { |
+ this->validate(); |
- const SkGlyphCache* head = fHead; |
- while (head != NULL) { |
- computed += head->fMemoryUsed; |
- head = head->fNext; |
+ size_t bytesNeeded = 0; |
+ if (fTotalMemoryUsed > fCacheSizeLimit) { |
+ bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit; |
+ } |
+ bytesNeeded = SkMax32(bytesNeeded, minBytesNeeded); |
+ if (bytesNeeded) { |
+ // no small purges! |
+ bytesNeeded = SkMax32(bytesNeeded, fTotalMemoryUsed >> 2); |
} |
- if (fTotalMemoryUsed != computed) { |
- printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed); |
+ int countNeeded = 0; |
+ if (fCacheCount > fCacheCountLimit) { |
+ countNeeded = fCacheCount - fCacheCountLimit; |
+ // no small purges! |
+ countNeeded = SkMax32(countNeeded, fCacheCount >> 2); |
} |
- SkASSERT(fTotalMemoryUsed == computed); |
-} |
-#endif |
-size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals, |
- size_t bytesNeeded) { |
- globals->validate(); |
+ // early exit |
+ if (!countNeeded && !bytesNeeded) { |
+ return 0; |
+ } |
size_t bytesFreed = 0; |
- int count = 0; |
- |
- // don't do any "small" purges |
- size_t minToPurge = globals->fTotalMemoryUsed >> 2; |
- if (bytesNeeded < minToPurge) |
- bytesNeeded = minToPurge; |
+ int countFreed = 0; |
- SkGlyphCache* cache = FindTail(globals->fHead); |
- while (cache != NULL && bytesFreed < bytesNeeded) { |
+ // we start at the tail and proceed backwards, as the linklist is in LRU |
+ // order, with unimportant entries at the tail. |
+ SkGlyphCache* cache = this->internalGetTail(); |
+ while (cache != NULL && |
+ (bytesFreed < bytesNeeded || countFreed < countNeeded)) { |
SkGlyphCache* prev = cache->fPrev; |
bytesFreed += cache->fMemoryUsed; |
+ countFreed += 1; |
- cache->detach(&globals->fHead); |
+ this->internalDetachCache(cache); |
SkDELETE(cache); |
cache = prev; |
- count += 1; |
} |
- SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); |
- globals->fTotalMemoryUsed -= bytesFreed; |
- globals->validate(); |
+ this->validate(); |
#ifdef SPEW_PURGE_STATUS |
- if (count && !gSkSuppressFontCachePurgeSpew) { |
+ if (countFreed && !gSkSuppressFontCachePurgeSpew) { |
SkDebugf("purging %dK from font cache [%d entries]\n", |
- (int)(bytesFreed >> 10), count); |
+ (int)(bytesFreed >> 10), countFreed); |
} |
#endif |
return bytesFreed; |
} |
+void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) { |
+ SkASSERT(NULL == cache->fPrev && NULL == cache->fNext); |
+ if (fHead) { |
+ fHead->fPrev = cache; |
+ cache->fNext = fHead; |
+ } |
+ fHead = cache; |
+ |
+ fCacheCount += 1; |
+ fTotalMemoryUsed += cache->fMemoryUsed; |
+} |
+ |
+void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) { |
+ SkASSERT(fCacheCount > 0); |
+ fCacheCount -= 1; |
+ fTotalMemoryUsed -= cache->fMemoryUsed; |
+ |
+ if (cache->fPrev) { |
+ cache->fPrev->fNext = cache->fNext; |
+ } else { |
+ fHead = cache->fNext; |
+ } |
+ if (cache->fNext) { |
+ cache->fNext->fPrev = cache->fPrev; |
+ } |
+ cache->fPrev = cache->fNext = NULL; |
+} |
+ |
/////////////////////////////////////////////////////////////////////////////// |
#ifdef SK_DEBUG |
+ |
void SkGlyphCache::validate() const { |
#ifdef SK_DEBUG_GLYPH_CACHE |
int count = fGlyphArray.count(); |
@@ -694,6 +656,22 @@ void SkGlyphCache::validate() const { |
} |
#endif |
} |
+ |
+void SkGlyphCache_Globals::validate() const { |
+ size_t computedBytes = 0; |
+ int computedCount = 0; |
+ |
+ const SkGlyphCache* head = fHead; |
+ while (head != NULL) { |
+ computedBytes += head->fMemoryUsed; |
+ computedCount += 1; |
+ head = head->fNext; |
+ } |
+ |
+ SkASSERT(fTotalMemoryUsed == computedBytes); |
+ SkASSERT(fCacheCount == computedCount); |
+} |
+ |
#endif |
/////////////////////////////////////////////////////////////////////////////// |
@@ -702,15 +680,27 @@ void SkGlyphCache::validate() const { |
#include "SkTypefaceCache.h" |
size_t SkGraphics::GetFontCacheLimit() { |
- return getSharedGlobals().getFontCacheLimit(); |
+ return getSharedGlobals().getCacheSizeLimit(); |
} |
size_t SkGraphics::SetFontCacheLimit(size_t bytes) { |
- return getSharedGlobals().setFontCacheLimit(bytes); |
+ return getSharedGlobals().setCacheSizeLimit(bytes); |
} |
size_t SkGraphics::GetFontCacheUsed() { |
- return getSharedGlobals().fTotalMemoryUsed; |
+ return getSharedGlobals().getTotalMemoryUsed(); |
+} |
+ |
+int SkGraphics::GetFontCacheCountLimit() { |
+ return getSharedGlobals().getCacheCountLimit(); |
+} |
+ |
+int SkGraphics::SetFontCacheCountLimit(int count) { |
+ return getSharedGlobals().setCacheCountLimit(count); |
+} |
+ |
+int SkGraphics::GetFontCacheCountUsed() { |
+ return getSharedGlobals().getCacheCountUsed(); |
} |
void SkGraphics::PurgeFontCache() { |
@@ -720,13 +710,13 @@ void SkGraphics::PurgeFontCache() { |
size_t SkGraphics::GetTLSFontCacheLimit() { |
const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); |
- return tls ? tls->getFontCacheLimit() : 0; |
+ return tls ? tls->getCacheSizeLimit() : 0; |
} |
void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { |
if (0 == bytes) { |
SkGlyphCache_Globals::DeleteTLS(); |
} else { |
- SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes); |
+ SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes); |
} |
} |