add counting to Globals, and refactor some for clarity

BUG=

Review URL: https://codereview.chromium.org/24447003

git-svn-id: http://skia.googlecode.com/svn/trunk@11484 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2013-09-26 19:28:27 +00:00
parent 97b4b67ee7
commit baed71fbfe
5 changed files with 289 additions and 181 deletions

View File

@ -101,6 +101,7 @@
'<(skia_src_path)/core/SkGeometry.cpp',
'<(skia_src_path)/core/SkGlyphCache.cpp',
'<(skia_src_path)/core/SkGlyphCache.h',
'<(skia_src_path)/core/SkGlyphCache_Globals.h',
'<(skia_src_path)/core/SkGraphics.cpp',
'<(skia_src_path)/core/SkInstCnt.cpp',
'<(skia_src_path)/core/SkImageFilter.cpp',

View File

@ -53,6 +53,25 @@ public:
*/
static size_t GetFontCacheUsed();
/**
* Return the number of entries in the font cache.
* A cache "entry" is associated with each typeface + pointSize + matrix.
*/
static int GetFontCacheCountUsed();
/**
* Return the current limit to the number of entries in the font cache.
* A cache "entry" is associated with each typeface + pointSize + matrix.
*/
static int GetFontCacheCountLimit();
/**
* Set the limit to the number of entries in the font cache, and return
* the previous value. If this new value is lower than the previous,
* it will automatically try to purge entries to meet the new limit.
*/
static int SetFontCacheCountLimit(int count);
/**
* For debugging purposes, this will attempt to purge the font cache. It
* does not change the limit, but will cause subsequent font measures and

View File

@ -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;
}
int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
if (newCount < 0) {
newCount = 0;
}
SkAutoMutexAcquire ac(fMutex);
int prevCount = fCacheCountLimit;
fCacheCountLimit = newCount;
this->internalPurge();
return prevCount;
}
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*),
@ -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 (!proc(cache, context)) { // need to reattach
if (insideMutex) {
SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed);
globals.fTotalMemoryUsed -= cache->fMemoryUsed;
}
} else { // 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);
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().attachCacheToHead(cache);
}
///////////////////////////////////////////////////////////////////////////////
SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
SkAutoMutexAcquire ac(fMutex);
this->validate();
cache->validate();
this->internalAttachCacheToHead(cache);
this->internalPurge();
}
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;
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 = 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);
}
}

View File

@ -197,27 +197,6 @@ private:
SkGlyph* lookupMetrics(uint32_t id, MetricsType);
static bool DetachProc(const SkGlyphCache*, void*) { return true; }
void detach(SkGlyphCache** head) {
if (fPrev) {
fPrev->fNext = fNext;
} else {
*head = fNext;
}
if (fNext) {
fNext->fPrev = fPrev;
}
fPrev = fNext = NULL;
}
void attachToHead(SkGlyphCache** head) {
SkASSERT(NULL == fPrev && NULL == fNext);
if (*head) {
(*head)->fPrev = this;
fNext = *head;
}
*head = this;
}
SkGlyphCache* fNext, *fPrev;
SkDescriptor* fDesc;
SkScalerContext* fScalerContext;
@ -258,9 +237,6 @@ private:
AuxProcRec* fAuxProcList;
void invokeAndRemoveAuxProcs();
// This relies on the caller to have already acquired the mutex to access the global cache
static size_t InternalFreeCache(SkGlyphCache_Globals*, size_t bytesNeeded);
inline static SkGlyphCache* FindTail(SkGlyphCache* head);
friend class SkGlyphCache_Globals;

View File

@ -0,0 +1,122 @@
/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkGlyphCache_Globals_DEFINED
#define SkGlyphCache_Globals_DEFINED
#include "SkGlyphCache.h"
#include "SkTLS.h"
#ifndef SK_DEFAULT_FONT_CACHE_COUNT_LIMIT
#define SK_DEFAULT_FONT_CACHE_COUNT_LIMIT 256
#endif
#ifndef SK_DEFAULT_FONT_CACHE_LIMIT
#define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024)
#endif
///////////////////////////////////////////////////////////////////////////////
class SkMutex;
class SkGlyphCache_Globals {
public:
enum UseMutex {
kNo_UseMutex, // thread-local cache
kYes_UseMutex // shared cache
};
SkGlyphCache_Globals(UseMutex um) {
fHead = NULL;
fTotalMemoryUsed = 0;
fCacheSizeLimit = SK_DEFAULT_FONT_CACHE_LIMIT;
fCacheCount = 0;
fCacheCountLimit = SK_DEFAULT_FONT_CACHE_COUNT_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* internalGetHead() const { return fHead; }
SkGlyphCache* internalGetTail() const;
size_t getTotalMemoryUsed() const { return fTotalMemoryUsed; }
int getCacheCountUsed() const { return fCacheCount; }
#ifdef SK_DEBUG
void validate() const;
#else
void validate() const {}
#endif
int getCacheCountLimit() const { return fCacheCountLimit; }
int setCacheCountLimit(int limit);
size_t getCacheSizeLimit() const { return fCacheSizeLimit; }
size_t setCacheSizeLimit(size_t limit);
// returns true if this cache is over-budget either due to size limit
// or count limit.
bool isOverBudget() const {
return fCacheCount > fCacheCountLimit ||
fTotalMemoryUsed > fCacheSizeLimit;
}
void purgeAll(); // does not change budget
// call when a glyphcache is available for caching (i.e. not in use)
void attachCacheToHead(SkGlyphCache*);
// can only be called when the mutex is already held
void internalDetachCache(SkGlyphCache*);
void internalAttachCacheToHead(SkGlyphCache*);
// 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:
SkGlyphCache* fHead;
size_t fTotalMemoryUsed;
size_t fCacheSizeLimit;
int32_t fCacheCountLimit;
int32_t fCacheCount;
// Checkout budgets, modulated by the specified min-bytes-needed-to-purge,
// and attempt to purge caches to match.
// Returns number of bytes freed.
size_t internalPurge(size_t minBytesNeeded = 0);
static void* CreateTLS() {
return SkNEW_ARGS(SkGlyphCache_Globals, (kNo_UseMutex));
}
static void DeleteTLS(void* ptr) {
SkDELETE((SkGlyphCache_Globals*)ptr);
}
};
#endif