Chromium Code Reviews| Index: src/core/SkGlyphCache.cpp |
| diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp |
| index 538c9682f529848f438ddc5a1e0f1c18af2cda14..30b325026e63360d37c52ae48a0af1ec1a0b1367 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 |
| @@ -75,6 +90,8 @@ SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkSca |
| fMetricsCount = 0; |
| fAdvanceCount = 0; |
| fAuxProcList = NULL; |
| + |
| + getGlobals().registerCache(this); |
| } |
| SkGlyphCache::~SkGlyphCache() { |
| @@ -114,6 +131,8 @@ SkGlyphCache::~SkGlyphCache() { |
| SkDescriptor::Free(fDesc); |
| SkDELETE(fScalerContext); |
| this->invokeAndRemoveAuxProcs(); |
| + |
| + getGlobals().unregisterCache(this); |
| } |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -397,108 +416,25 @@ 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; |
| + SkAutoMutexAcquire ac(fMutex); |
| - size_t currUsed = fTotalMemoryUsed; |
| - if (currUsed > newLimit) { |
| - SkAutoMutexAcquire ac(fMutex); |
| - SkGlyphCache::InternalFreeCache(this, currUsed - newLimit); |
| - } |
| + size_t prevLimit = fCacheSizeLimit; |
| + fCacheSizeLimit = newLimit; |
| + this->internalPurge(); |
| return prevLimit; |
| } |
| void SkGlyphCache_Globals::purgeAll() { |
| SkAutoMutexAcquire ac(fMutex); |
| - SkGlyphCache::InternalFreeCache(this, fTotalMemoryUsed); |
| -} |
| - |
| -// 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(); |
| + this->internalPurge(fTotalMemoryUsed); |
| } |
| void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), |
| @@ -592,25 +528,7 @@ void SkGlyphCache::AttachCache(SkGlyphCache* cache) { |
| SkASSERT(cache); |
| SkASSERT(cache->fNext == NULL); |
| - SkGlyphCache_Globals& globals = getGlobals(); |
| - SkAutoMutexAcquire ac(globals.fMutex); |
| - |
| - 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); |
| - } |
| - } |
| - |
| - cache->attachToHead(&globals.fHead); |
| - globals.fTotalMemoryUsed += cache->fMemoryUsed; |
| - |
| - globals.validate(); |
| + getGlobals().attachCache(cache); |
| } |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -624,63 +542,92 @@ SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { |
| return cache; |
| } |
| -#ifdef SK_DEBUG |
| -void SkGlyphCache_Globals::validate() const { |
| - size_t computed = 0; |
| +/////////////////////////////////////////////////////////////////////////////// |
| - const SkGlyphCache* head = fHead; |
| - while (head != NULL) { |
| - computed += head->fMemoryUsed; |
| - head = head->fNext; |
| +void SkGlyphCache_Globals::attachCache(SkGlyphCache* cache) { |
| + SkAutoMutexAcquire ac(fMutex); |
| + |
| + this->validate(); |
| + cache->validate(); |
| + |
| + cache->attachToHead(&fHead); |
| + fTotalMemoryUsed += cache->fMemoryUsed; |
| + |
| + this->internalPurge(); |
| +} |
| + |
| +size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) { |
| + this->validate(); |
| + |
| + 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; |
| + int countFreed = 0; |
| - // don't do any "small" purges |
| - size_t minToPurge = globals->fTotalMemoryUsed >> 2; |
| - if (bytesNeeded < minToPurge) |
| - bytesNeeded = minToPurge; |
| - |
| - 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 = SkGlyphCache::FindTail(fHead); |
| + while (cache != NULL && |
| + (bytesFreed < bytesNeeded || countFreed < countNeeded)) { |
| SkGlyphCache* prev = cache->fPrev; |
| bytesFreed += cache->fMemoryUsed; |
| + countFreed += 1; |
| - cache->detach(&globals->fHead); |
| + cache->detach(&fHead); |
| SkDELETE(cache); |
| cache = prev; |
| - count += 1; |
| } |
| - SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); |
| - globals->fTotalMemoryUsed -= bytesFreed; |
| - globals->validate(); |
| + SkASSERT(bytesFreed <= fTotalMemoryUsed); |
| + SkASSERT(countFreed <= fCacheCount); |
| + |
| + fTotalMemoryUsed -= bytesFreed; |
| + fCacheCount -= countFreed; |
| + 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::registerCache(SkGlyphCache*) { |
| + sk_atomic_inc(&fCacheCount); |
|
bungeman-skia
2013/09/25 14:46:07
This is going to set off the chromium tsan bots. O
reed1
2013/09/25 17:30:31
Help me understand what you're saying. We use sk_a
mtklein
2013/09/25 18:15:25
I'm confused too.
Memory barriers are about preve
bungeman-skia
2013/09/25 22:37:08
This use case probably isn't that different from o
|
| +} |
| + |
| +// call when SkGlyphCache is destroyed |
| +void SkGlyphCache_Globals::unregisterCache(SkGlyphCache*) { |
| + sk_atomic_dec(&fCacheCount); |
| +} |
| + |
| /////////////////////////////////////////////////////////////////////////////// |
| #ifdef SK_DEBUG |
| + |
| void SkGlyphCache::validate() const { |
| #ifdef SK_DEBUG_GLYPH_CACHE |
| int count = fGlyphArray.count(); |
| @@ -694,6 +641,19 @@ void SkGlyphCache::validate() const { |
| } |
| #endif |
| } |
| + |
| +void SkGlyphCache_Globals::validate() const { |
| + size_t computedBytes = 0; |
| + |
| + const SkGlyphCache* head = fHead; |
| + while (head != NULL) { |
| + computedBytes += head->fMemoryUsed; |
| + head = head->fNext; |
| + } |
| + |
| + SkASSERT(fTotalMemoryUsed == computedBytes); |
| +} |
| + |
| #endif |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -702,11 +662,11 @@ 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() { |
| @@ -720,13 +680,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) { |
|
bungeman-skia
2013/09/25 14:46:07
I realize that this isn't part of this change, but
reed1
2013/09/25 17:30:31
Agreed, we should rename/document this "feature" b
|
| if (0 == bytes) { |
| SkGlyphCache_Globals::DeleteTLS(); |
| } else { |
| - SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes); |
| + SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes); |
| } |
| } |