3 * Copyright 2006 The Android Open Source Project
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
10 #include "SkGlyphCache.h"
11 #include "SkGlyphCache_Globals.h"
12 #include "SkGraphics.h"
15 #include "SkTemplates.h"
17 #include "SkTypeface.h"
19 //#define SPEW_PURGE_STATUS
20 //#define RECORD_HASH_EFFICIENCY
22 bool gSkSuppressFontCachePurgeSpew;
24 // Returns the shared globals
25 static SkGlyphCache_Globals& getSharedGlobals() {
26 // we leak this, so we don't incur any shutdown cost of the destructor
27 static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
28 (SkGlyphCache_Globals::kYes_UseMutex));
32 // Returns the TLS globals (if set), or the shared globals
33 static SkGlyphCache_Globals& getGlobals() {
34 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
35 return tls ? *tls : getSharedGlobals();
38 ///////////////////////////////////////////////////////////////////////////////
40 #ifdef RECORD_HASH_EFFICIENCY
41 static uint32_t gHashSuccess;
42 static uint32_t gHashCollision;
44 static void RecordHashSuccess() {
48 static void RecordHashCollisionIf(bool pred) {
52 uint32_t total = gHashSuccess + gHashCollision;
53 SkDebugf("Font Cache Hash success rate: %d%%\n",
54 100 * gHashSuccess / total);
58 #define RecordHashSuccess() (void)0
59 #define RecordHashCollisionIf(pred) (void)0
61 #define RecordHashCollision() RecordHashCollisionIf(true)
63 ///////////////////////////////////////////////////////////////////////////////
65 // so we don't grow our arrays a lot
66 #define kMinGlyphCount 16
67 #define kMinGlyphImageSize (16*2)
68 #define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount)
70 SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx)
71 : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) {
79 fScalerContext->getFontMetrics(&fFontMetrics);
81 // init to 0 so that all of the pointers will be null
82 memset(fGlyphHash, 0, sizeof(fGlyphHash));
83 // init with 0xFF so that the charCode field will be -1, which is invalid
84 memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
86 fMemoryUsed = sizeof(*this);
88 fGlyphArray.setReserve(kMinGlyphCount);
95 SkGlyphCache::~SkGlyphCache() {
98 size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
99 size_t glyphAlloc = fGlyphAlloc.totalCapacity();
100 size_t glyphHashUsed = 0;
101 size_t uniHashUsed = 0;
102 for (int i = 0; i < kHashCount; ++i) {
103 glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0;
104 uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0;
106 size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph);
107 size_t imageUsed = 0;
108 for (int i = 0; i < fGlyphArray.count(); ++i) {
109 const SkGlyph& g = *fGlyphArray[i];
111 imageUsed += g.fHeight * g.rowBytes();
115 printf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n",
116 ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(fCharToGlyphHash), uniHashUsed);
120 SkGlyph** gptr = fGlyphArray.begin();
121 SkGlyph** stop = fGlyphArray.end();
122 while (gptr < stop) {
123 SkPath* path = (*gptr)->fPath;
129 SkDescriptor::Free(fDesc);
130 SkDELETE(fScalerContext);
131 this->invokeAndRemoveAuxProcs();
134 ///////////////////////////////////////////////////////////////////////////////
137 #define VALIDATE() AutoValidate av(this)
142 uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
144 uint32_t id = SkGlyph::MakeID(charCode);
145 const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
148 return rec.fGlyph->getGlyphID();
150 return fScalerContext->charToGlyphID(charCode);
154 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
155 return fScalerContext->glyphIDToChar(glyphID);
158 unsigned SkGlyphCache::getGlyphCount() {
159 return fScalerContext->getGlyphCount();
162 ///////////////////////////////////////////////////////////////////////////////
164 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
166 uint32_t id = SkGlyph::MakeID(charCode);
167 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
169 if (rec->fID != id) {
170 // this ID is based on the UniChar
172 // this ID is based on the glyph index
173 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
174 rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
179 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
181 uint32_t id = SkGlyph::MakeID(glyphID);
182 unsigned index = ID2HashIndex(id);
183 SkGlyph* glyph = fGlyphHash[index];
185 if (NULL == glyph || glyph->fID != id) {
186 glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
187 fGlyphHash[index] = glyph;
192 ///////////////////////////////////////////////////////////////////////////////
194 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
196 uint32_t id = SkGlyph::MakeID(charCode);
197 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
199 if (rec->fID != id) {
200 RecordHashCollisionIf(rec->fGlyph != NULL);
201 // this ID is based on the UniChar
203 // this ID is based on the glyph index
204 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
205 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
208 if (rec->fGlyph->isJustAdvance()) {
209 fScalerContext->getMetrics(rec->fGlyph);
212 SkASSERT(rec->fGlyph->isFullMetrics());
216 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
217 SkFixed x, SkFixed y) {
219 uint32_t id = SkGlyph::MakeID(charCode, x, y);
220 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
222 if (rec->fID != id) {
223 RecordHashCollisionIf(rec->fGlyph != NULL);
224 // this ID is based on the UniChar
226 // this ID is based on the glyph index
227 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
228 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
231 if (rec->fGlyph->isJustAdvance()) {
232 fScalerContext->getMetrics(rec->fGlyph);
235 SkASSERT(rec->fGlyph->isFullMetrics());
239 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
241 uint32_t id = SkGlyph::MakeID(glyphID);
242 unsigned index = ID2HashIndex(id);
243 SkGlyph* glyph = fGlyphHash[index];
245 if (NULL == glyph || glyph->fID != id) {
246 RecordHashCollisionIf(glyph != NULL);
247 glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
248 fGlyphHash[index] = glyph;
251 if (glyph->isJustAdvance()) {
252 fScalerContext->getMetrics(glyph);
255 SkASSERT(glyph->isFullMetrics());
259 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
260 SkFixed x, SkFixed y) {
262 uint32_t id = SkGlyph::MakeID(glyphID, x, y);
263 unsigned index = ID2HashIndex(id);
264 SkGlyph* glyph = fGlyphHash[index];
266 if (NULL == glyph || glyph->fID != id) {
267 RecordHashCollisionIf(glyph != NULL);
268 glyph = this->lookupMetrics(id, kFull_MetricsType);
269 fGlyphHash[index] = glyph;
272 if (glyph->isJustAdvance()) {
273 fScalerContext->getMetrics(glyph);
276 SkASSERT(glyph->isFullMetrics());
280 SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
284 int count = fGlyphArray.count();
287 SkGlyph** gptr = fGlyphArray.begin();
292 int mid = (hi + lo) >> 1;
293 if (gptr[mid]->fID < id) {
300 if (glyph->fID == id) {
301 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
302 fScalerContext->getMetrics(glyph);
307 // check if we need to bump hi before falling though to the allocator
308 if (glyph->fID < id) {
313 // not found, but hi tells us where to inser the new glyph
314 fMemoryUsed += sizeof(SkGlyph);
316 glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
317 SkChunkAlloc::kThrow_AllocFailType);
319 *fGlyphArray.insert(hi) = glyph;
321 if (kJustAdvance_MetricsType == mtype) {
322 fScalerContext->getAdvance(glyph);
325 SkASSERT(kFull_MetricsType == mtype);
326 fScalerContext->getMetrics(glyph);
333 const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
334 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
335 if (glyph.fImage == NULL) {
336 size_t size = glyph.computeImageSize();
337 const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
338 SkChunkAlloc::kReturnNil_AllocFailType);
339 // check that alloc() actually succeeded
341 fScalerContext->getImage(glyph);
342 // TODO: the scaler may have changed the maskformat during
343 // getImage (e.g. from AA or LCD to BW) which means we may have
344 // overallocated the buffer. Check if the new computedImageSize
345 // is smaller, and if so, strink the alloc size in fImageAlloc.
353 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
355 if (glyph.fPath == NULL) {
356 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
357 fScalerContext->getPath(glyph, glyph.fPath);
358 fMemoryUsed += sizeof(SkPath) +
359 glyph.fPath->countPoints() * sizeof(SkPoint);
365 ///////////////////////////////////////////////////////////////////////////////
367 bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
368 const AuxProcRec* rec = fAuxProcList;
370 if (rec->fProc == proc) {
372 *dataPtr = rec->fData;
381 void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
386 AuxProcRec* rec = fAuxProcList;
388 if (rec->fProc == proc) {
394 // not found, create a new rec
395 rec = SkNEW(AuxProcRec);
398 rec->fNext = fAuxProcList;
402 void SkGlyphCache::invokeAndRemoveAuxProcs() {
403 AuxProcRec* rec = fAuxProcList;
405 rec->fProc(rec->fData);
406 AuxProcRec* next = rec->fNext;
412 ///////////////////////////////////////////////////////////////////////////////
413 ///////////////////////////////////////////////////////////////////////////////
415 #include "SkThread.h"
417 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
418 static const size_t minLimit = 256 * 1024;
419 if (newLimit < minLimit) {
423 SkAutoMutexAcquire ac(fMutex);
425 size_t prevLimit = fCacheSizeLimit;
426 fCacheSizeLimit = newLimit;
427 this->internalPurge();
431 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
436 SkAutoMutexAcquire ac(fMutex);
438 int prevCount = fCacheCountLimit;
439 fCacheCountLimit = newCount;
440 this->internalPurge();
444 void SkGlyphCache_Globals::purgeAll() {
445 SkAutoMutexAcquire ac(fMutex);
446 this->internalPurge(fTotalMemoryUsed);
449 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
451 SkGlyphCache_Globals& globals = getGlobals();
452 SkAutoMutexAcquire ac(globals.fMutex);
457 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
458 if (proc(cache, context)) {
466 /* This guy calls the visitor from within the mutext lock, so the visitor
469 - try to acquire the mutext again
470 - call a fontscaler (which might call into the cache)
472 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
473 const SkDescriptor* desc,
474 bool (*proc)(const SkGlyphCache*, void*),
477 typeface = SkTypeface::GetDefaultTypeface();
481 SkGlyphCache_Globals& globals = getGlobals();
482 SkAutoMutexAcquire ac(globals.fMutex);
484 bool insideMutex = true;
488 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
489 if (cache->fDesc->equals(*desc)) {
490 globals.internalDetachCache(cache);
495 /* Release the mutex now, before we create a new entry (which might have
496 side-effects like trying to access the cache/mutex (yikes!)
498 ac.release(); // release the mutex now
499 insideMutex = false; // can't use globals anymore
501 // Check if we can create a scaler-context before creating the glyphcache.
502 // If not, we may have exhausted OS/font resources, so try purging the
503 // cache once and try again.
505 // pass true the first time, to notice if the scalercontext failed,
506 // so we can try the purge.
507 SkScalerContext* ctx = typeface->createScalerContext(desc, true);
509 getSharedGlobals().purgeAll();
510 ctx = typeface->createScalerContext(desc, false);
513 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
518 AutoValidate av(cache);
520 if (!proc(cache, context)) { // need to reattach
522 globals.internalAttachCacheToHead(cache);
524 globals.attachCacheToHead(cache);
531 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
533 SkASSERT(cache->fNext == NULL);
535 getGlobals().attachCacheToHead(cache);
538 ///////////////////////////////////////////////////////////////////////////////
540 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
541 SkAutoMutexAcquire ac(fMutex);
546 this->internalAttachCacheToHead(cache);
547 this->internalPurge();
550 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
551 SkGlyphCache* cache = fHead;
553 while (cache->fNext) {
554 cache = cache->fNext;
560 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
563 size_t bytesNeeded = 0;
564 if (fTotalMemoryUsed > fCacheSizeLimit) {
565 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
567 bytesNeeded = SkMax32(bytesNeeded, minBytesNeeded);
570 bytesNeeded = SkMax32(bytesNeeded, fTotalMemoryUsed >> 2);
574 if (fCacheCount > fCacheCountLimit) {
575 countNeeded = fCacheCount - fCacheCountLimit;
577 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
581 if (!countNeeded && !bytesNeeded) {
585 size_t bytesFreed = 0;
588 // we start at the tail and proceed backwards, as the linklist is in LRU
589 // order, with unimportant entries at the tail.
590 SkGlyphCache* cache = this->internalGetTail();
591 while (cache != NULL &&
592 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
593 SkGlyphCache* prev = cache->fPrev;
594 bytesFreed += cache->fMemoryUsed;
597 this->internalDetachCache(cache);
604 #ifdef SPEW_PURGE_STATUS
605 if (countFreed && !gSkSuppressFontCachePurgeSpew) {
606 SkDebugf("purging %dK from font cache [%d entries]\n",
607 (int)(bytesFreed >> 10), countFreed);
614 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
615 SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
617 fHead->fPrev = cache;
618 cache->fNext = fHead;
623 fTotalMemoryUsed += cache->fMemoryUsed;
626 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
627 SkASSERT(fCacheCount > 0);
629 fTotalMemoryUsed -= cache->fMemoryUsed;
632 cache->fPrev->fNext = cache->fNext;
634 fHead = cache->fNext;
637 cache->fNext->fPrev = cache->fPrev;
639 cache->fPrev = cache->fNext = NULL;
642 ///////////////////////////////////////////////////////////////////////////////
646 void SkGlyphCache::validate() const {
647 #ifdef SK_DEBUG_GLYPH_CACHE
648 int count = fGlyphArray.count();
649 for (int i = 0; i < count; i++) {
650 const SkGlyph* glyph = fGlyphArray[i];
652 SkASSERT(fGlyphAlloc.contains(glyph));
654 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
660 void SkGlyphCache_Globals::validate() const {
661 size_t computedBytes = 0;
662 int computedCount = 0;
664 const SkGlyphCache* head = fHead;
665 while (head != NULL) {
666 computedBytes += head->fMemoryUsed;
671 SkASSERT(fTotalMemoryUsed == computedBytes);
672 SkASSERT(fCacheCount == computedCount);
677 ///////////////////////////////////////////////////////////////////////////////
678 ///////////////////////////////////////////////////////////////////////////////
680 #include "SkTypefaceCache.h"
682 size_t SkGraphics::GetFontCacheLimit() {
683 return getSharedGlobals().getCacheSizeLimit();
686 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
687 return getSharedGlobals().setCacheSizeLimit(bytes);
690 size_t SkGraphics::GetFontCacheUsed() {
691 return getSharedGlobals().getTotalMemoryUsed();
694 int SkGraphics::GetFontCacheCountLimit() {
695 return getSharedGlobals().getCacheCountLimit();
698 int SkGraphics::SetFontCacheCountLimit(int count) {
699 return getSharedGlobals().setCacheCountLimit(count);
702 int SkGraphics::GetFontCacheCountUsed() {
703 return getSharedGlobals().getCacheCountUsed();
706 void SkGraphics::PurgeFontCache() {
707 getSharedGlobals().purgeAll();
708 SkTypefaceCache::PurgeAll();
711 size_t SkGraphics::GetTLSFontCacheLimit() {
712 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
713 return tls ? tls->getCacheSizeLimit() : 0;
716 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
718 SkGlyphCache_Globals::DeleteTLS();
720 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);