blob: 5904edd3c0c7394c00acc13c2255acd5e5370cd4 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkGlyphCache.h"
reed@google.com77407ca2011-11-08 13:48:32 +000011#include "SkGraphics.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkPaint.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000013#include "SkPath.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkTemplates.h"
reed@google.com6172d672012-05-17 13:38:03 +000015#include "SkTLS.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016
reed@google.com8887be02012-01-25 16:04:18 +000017//#define SPEW_PURGE_STATUS
reed@android.com8a1c16f2008-12-17 15:59:43 +000018//#define USE_CACHE_HASH
19//#define RECORD_HASH_EFFICIENCY
20
reed@google.com11b4d8a2011-11-02 21:02:36 +000021bool gSkSuppressFontCachePurgeSpew;
22
reed@android.com8a1c16f2008-12-17 15:59:43 +000023///////////////////////////////////////////////////////////////////////////////
24
25#ifdef RECORD_HASH_EFFICIENCY
26 static uint32_t gHashSuccess;
27 static uint32_t gHashCollision;
28
29 static void RecordHashSuccess() {
30 gHashSuccess += 1;
31 }
32
33 static void RecordHashCollisionIf(bool pred) {
34 if (pred) {
35 gHashCollision += 1;
reed@google.com22a02212011-03-01 21:33:48 +000036
reed@android.com8a1c16f2008-12-17 15:59:43 +000037 uint32_t total = gHashSuccess + gHashCollision;
38 SkDebugf("Font Cache Hash success rate: %d%%\n",
39 100 * gHashSuccess / total);
40 }
41 }
42#else
43 #define RecordHashSuccess() (void)0
44 #define RecordHashCollisionIf(pred) (void)0
45#endif
46#define RecordHashCollision() RecordHashCollisionIf(true)
47
48///////////////////////////////////////////////////////////////////////////////
49
50#define kMinGlphAlloc (sizeof(SkGlyph) * 64)
51#define kMinImageAlloc (24 * 64) // should be pointsize-dependent
52
53#define METRICS_RESERVE_COUNT 128 // so we don't grow this array a lot
54
55SkGlyphCache::SkGlyphCache(const SkDescriptor* desc)
56 : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) {
57 fPrev = fNext = NULL;
58
59 fDesc = desc->copy();
60 fScalerContext = SkScalerContext::Create(desc);
61 fScalerContext->getFontMetrics(NULL, &fFontMetricsY);
62
63 // init to 0 so that all of the pointers will be null
64 memset(fGlyphHash, 0, sizeof(fGlyphHash));
65 // init with 0xFF so that the charCode field will be -1, which is invalid
66 memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
reed@google.com22a02212011-03-01 21:33:48 +000067
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc;
reed@google.com22a02212011-03-01 21:33:48 +000069
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 fGlyphArray.setReserve(METRICS_RESERVE_COUNT);
71
72 fMetricsCount = 0;
73 fAdvanceCount = 0;
74 fAuxProcList = NULL;
75}
76
77SkGlyphCache::~SkGlyphCache() {
78 SkGlyph** gptr = fGlyphArray.begin();
79 SkGlyph** stop = fGlyphArray.end();
80 while (gptr < stop) {
81 SkPath* path = (*gptr)->fPath;
82 if (path) {
83 SkDELETE(path);
84 }
85 gptr += 1;
86 }
87 SkDescriptor::Free(fDesc);
88 SkDELETE(fScalerContext);
89 this->invokeAndRemoveAuxProcs();
90}
91
92///////////////////////////////////////////////////////////////////////////////
93
94#ifdef SK_DEBUG
reed@android.comf2b98d62010-12-20 18:26:13 +000095#define VALIDATE() AutoValidate av(this)
reed@android.com8a1c16f2008-12-17 15:59:43 +000096#else
97#define VALIDATE()
98#endif
99
100uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
101 VALIDATE();
102 uint32_t id = SkGlyph::MakeID(charCode);
103 const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000104
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 if (rec.fID == id) {
106 return rec.fGlyph->getGlyphID();
107 } else {
108 return fScalerContext->charToGlyphID(charCode);
109 }
110}
111
reed@android.com9d3a9852010-01-08 14:07:42 +0000112SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
113 return fScalerContext->glyphIDToChar(glyphID);
114}
115
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000116unsigned SkGlyphCache::getGlyphCount() {
117 return fScalerContext->getGlyphCount();
118}
119
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120///////////////////////////////////////////////////////////////////////////////
121
122const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
123 VALIDATE();
124 uint32_t id = SkGlyph::MakeID(charCode);
125 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000126
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 if (rec->fID != id) {
128 // this ID is based on the UniChar
129 rec->fID = id;
130 // this ID is based on the glyph index
131 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
132 rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
133 }
134 return *rec->fGlyph;
135}
136
137const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
138 VALIDATE();
139 uint32_t id = SkGlyph::MakeID(glyphID);
140 unsigned index = ID2HashIndex(id);
141 SkGlyph* glyph = fGlyphHash[index];
142
143 if (NULL == glyph || glyph->fID != id) {
144 glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
145 fGlyphHash[index] = glyph;
146 }
147 return *glyph;
148}
149
150///////////////////////////////////////////////////////////////////////////////
151
152const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
153 VALIDATE();
154 uint32_t id = SkGlyph::MakeID(charCode);
155 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000156
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (rec->fID != id) {
158 RecordHashCollisionIf(rec->fGlyph != NULL);
159 // this ID is based on the UniChar
160 rec->fID = id;
161 // this ID is based on the glyph index
162 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
163 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
164 } else {
165 RecordHashSuccess();
166 if (rec->fGlyph->isJustAdvance()) {
167 fScalerContext->getMetrics(rec->fGlyph);
168 }
169 }
170 SkASSERT(rec->fGlyph->isFullMetrics());
171 return *rec->fGlyph;
172}
173
174const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
175 SkFixed x, SkFixed y) {
176 VALIDATE();
177 uint32_t id = SkGlyph::MakeID(charCode, x, y);
178 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 if (rec->fID != id) {
181 RecordHashCollisionIf(rec->fGlyph != NULL);
182 // this ID is based on the UniChar
183 rec->fID = id;
184 // this ID is based on the glyph index
185 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
186 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
187 } else {
188 RecordHashSuccess();
189 if (rec->fGlyph->isJustAdvance()) {
190 fScalerContext->getMetrics(rec->fGlyph);
191 }
192 }
193 SkASSERT(rec->fGlyph->isFullMetrics());
194 return *rec->fGlyph;
195}
196
197const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
198 VALIDATE();
199 uint32_t id = SkGlyph::MakeID(glyphID);
200 unsigned index = ID2HashIndex(id);
201 SkGlyph* glyph = fGlyphHash[index];
reed@google.com22a02212011-03-01 21:33:48 +0000202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 if (NULL == glyph || glyph->fID != id) {
204 RecordHashCollisionIf(glyph != NULL);
205 glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
206 fGlyphHash[index] = glyph;
207 } else {
208 RecordHashSuccess();
209 if (glyph->isJustAdvance()) {
210 fScalerContext->getMetrics(glyph);
211 }
212 }
213 SkASSERT(glyph->isFullMetrics());
214 return *glyph;
215}
216
217const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
218 SkFixed x, SkFixed y) {
219 VALIDATE();
220 uint32_t id = SkGlyph::MakeID(glyphID, x, y);
221 unsigned index = ID2HashIndex(id);
222 SkGlyph* glyph = fGlyphHash[index];
223
224 if (NULL == glyph || glyph->fID != id) {
225 RecordHashCollisionIf(glyph != NULL);
226 glyph = this->lookupMetrics(id, kFull_MetricsType);
227 fGlyphHash[index] = glyph;
228 } else {
229 RecordHashSuccess();
230 if (glyph->isJustAdvance()) {
231 fScalerContext->getMetrics(glyph);
232 }
233 }
234 SkASSERT(glyph->isFullMetrics());
235 return *glyph;
236}
237
238SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
239 SkGlyph* glyph;
240
241 int hi = 0;
242 int count = fGlyphArray.count();
243
244 if (count) {
245 SkGlyph** gptr = fGlyphArray.begin();
246 int lo = 0;
247
248 hi = count - 1;
249 while (lo < hi) {
250 int mid = (hi + lo) >> 1;
251 if (gptr[mid]->fID < id) {
252 lo = mid + 1;
253 } else {
254 hi = mid;
255 }
256 }
257 glyph = gptr[hi];
258 if (glyph->fID == id) {
259 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
260 fScalerContext->getMetrics(glyph);
261 }
262 return glyph;
263 }
264
265 // check if we need to bump hi before falling though to the allocator
266 if (glyph->fID < id) {
267 hi += 1;
268 }
269 }
270
271 // not found, but hi tells us where to inser the new glyph
272 fMemoryUsed += sizeof(SkGlyph);
273
274 glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
275 SkChunkAlloc::kThrow_AllocFailType);
reed@android.comf2b98d62010-12-20 18:26:13 +0000276 glyph->init(id);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 *fGlyphArray.insert(hi) = glyph;
reed@google.com22a02212011-03-01 21:33:48 +0000278
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 if (kJustAdvance_MetricsType == mtype) {
280 fScalerContext->getAdvance(glyph);
281 fAdvanceCount += 1;
282 } else {
283 SkASSERT(kFull_MetricsType == mtype);
284 fScalerContext->getMetrics(glyph);
285 fMetricsCount += 1;
286 }
287
288 return glyph;
289}
290
291const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
scarybeasts@gmail.com17f694b2010-10-18 23:29:36 +0000292 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 if (glyph.fImage == NULL) {
294 size_t size = glyph.computeImageSize();
295 const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size,
296 SkChunkAlloc::kReturnNil_AllocFailType);
reed@google.com22a02212011-03-01 21:33:48 +0000297 // check that alloc() actually succeeded
298 if (glyph.fImage) {
299 fScalerContext->getImage(glyph);
reed@google.com5e2df642011-09-21 18:42:09 +0000300 // TODO: the scaler may have changed the maskformat during
301 // getImage (e.g. from AA or LCD to BW) which means we may have
302 // overallocated the buffer. Check if the new computedImageSize
303 // is smaller, and if so, strink the alloc size in fImageAlloc.
reed@google.com22a02212011-03-01 21:33:48 +0000304 fMemoryUsed += size;
305 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 }
307 }
308 return glyph.fImage;
309}
310
311const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
312 if (glyph.fWidth) {
313 if (glyph.fPath == NULL) {
314 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
315 fScalerContext->getPath(glyph, glyph.fPath);
316 fMemoryUsed += sizeof(SkPath) +
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000317 glyph.fPath->countPoints() * sizeof(SkPoint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 }
319 }
320 return glyph.fPath;
321}
322
323///////////////////////////////////////////////////////////////////////////////
324
325bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
326 const AuxProcRec* rec = fAuxProcList;
327 while (rec) {
328 if (rec->fProc == proc) {
329 if (dataPtr) {
330 *dataPtr = rec->fData;
331 }
332 return true;
333 }
334 rec = rec->fNext;
335 }
336 return false;
337}
338
339void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
340 if (proc == NULL) {
341 return;
342 }
343
344 AuxProcRec* rec = fAuxProcList;
345 while (rec) {
346 if (rec->fProc == proc) {
347 rec->fData = data;
348 return;
349 }
350 rec = rec->fNext;
351 }
352 // not found, create a new rec
353 rec = SkNEW(AuxProcRec);
354 rec->fProc = proc;
355 rec->fData = data;
356 rec->fNext = fAuxProcList;
357 fAuxProcList = rec;
358}
359
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360void SkGlyphCache::invokeAndRemoveAuxProcs() {
361 AuxProcRec* rec = fAuxProcList;
362 while (rec) {
363 rec->fProc(rec->fData);
364 AuxProcRec* next = rec->fNext;
365 SkDELETE(rec);
366 rec = next;
367 }
368}
369
370///////////////////////////////////////////////////////////////////////////////
371///////////////////////////////////////////////////////////////////////////////
372
reed@google.com09837012012-04-23 15:04:44 +0000373#ifndef SK_DEFAULT_FONT_CACHE_LIMIT
374 #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024)
375#endif
376
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377#ifdef USE_CACHE_HASH
378 #define HASH_BITCOUNT 6
379 #define HASH_COUNT (1 << HASH_BITCOUNT)
380 #define HASH_MASK (HASH_COUNT - 1)
reed@google.com22a02212011-03-01 21:33:48 +0000381
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 static unsigned desc_to_hashindex(const SkDescriptor* desc)
383 {
384 SkASSERT(HASH_MASK < 256); // since our munging reduces to 8 bits
385
386 uint32_t n = *(const uint32_t*)desc; //desc->getChecksum();
387 SkASSERT(n == desc->getChecksum());
388
389 // don't trust that the low bits of checksum vary enough, so...
390 n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30);
391
392 return n & HASH_MASK;
393 }
394#endif
395
reed@google.com5d248bc2011-11-17 21:49:02 +0000396#include "SkThread.h"
397
398class SkGlyphCache_Globals {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399public:
reed@google.com6172d672012-05-17 13:38:03 +0000400 enum UseMutex {
401 kNo_UseMutex, // thread-local cache
reed@google.com73c48f12012-07-20 11:35:40 +0000402 kYes_UseMutex // shared cache
reed@google.com6172d672012-05-17 13:38:03 +0000403 };
404
405 SkGlyphCache_Globals(UseMutex um) {
reed@google.com9998c662011-11-17 22:09:47 +0000406 fHead = NULL;
407 fTotalMemoryUsed = 0;
reed@google.com09837012012-04-23 15:04:44 +0000408 fFontCacheLimit = SK_DEFAULT_FONT_CACHE_LIMIT;
reed@google.com6172d672012-05-17 13:38:03 +0000409 fMutex = (kYes_UseMutex == um) ? SkNEW(SkMutex) : NULL;
reed@google.com09837012012-04-23 15:04:44 +0000410
reed@google.com9998c662011-11-17 22:09:47 +0000411#ifdef USE_CACHE_HASH
412 sk_bzero(fHash, sizeof(fHash));
413#endif
414 }
415
reed@google.com6172d672012-05-17 13:38:03 +0000416 ~SkGlyphCache_Globals() {
reed@google.com7b578922012-05-21 15:29:27 +0000417 SkGlyphCache* cache = fHead;
418 while (cache) {
419 SkGlyphCache* next = cache->fNext;
420 SkDELETE(cache);
421 cache = next;
422 }
423
reed@google.com6172d672012-05-17 13:38:03 +0000424 SkDELETE(fMutex);
425 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000426
reed@google.com6172d672012-05-17 13:38:03 +0000427 SkMutex* fMutex;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 SkGlyphCache* fHead;
429 size_t fTotalMemoryUsed;
430#ifdef USE_CACHE_HASH
431 SkGlyphCache* fHash[HASH_COUNT];
432#endif
433
434#ifdef SK_DEBUG
435 void validate() const;
436#else
437 void validate() const {}
438#endif
reed@google.com09837012012-04-23 15:04:44 +0000439
440 size_t getFontCacheLimit() const { return fFontCacheLimit; }
441 size_t setFontCacheLimit(size_t limit);
reed@google.com26344cf2012-06-27 18:23:01 +0000442 void purgeAll(); // does not change budget
reed@google.com09837012012-04-23 15:04:44 +0000443
reed@google.com6172d672012-05-17 13:38:03 +0000444 // can return NULL
445 static SkGlyphCache_Globals* FindTLS() {
446 return (SkGlyphCache_Globals*)SkTLS::Find(CreateTLS);
447 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000448
reed@google.com6172d672012-05-17 13:38:03 +0000449 static SkGlyphCache_Globals& GetTLS() {
450 return *(SkGlyphCache_Globals*)SkTLS::Get(CreateTLS, DeleteTLS);
451 }
452
453 static void DeleteTLS() { SkTLS::Delete(CreateTLS); }
454
reed@google.com09837012012-04-23 15:04:44 +0000455private:
456 size_t fFontCacheLimit;
reed@google.com6172d672012-05-17 13:38:03 +0000457
458 static void* CreateTLS() {
459 return SkNEW_ARGS(SkGlyphCache_Globals, (kNo_UseMutex));
460 }
461
462 static void DeleteTLS(void* ptr) {
463 SkDELETE((SkGlyphCache_Globals*)ptr);
464 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465};
466
reed@google.com09837012012-04-23 15:04:44 +0000467size_t SkGlyphCache_Globals::setFontCacheLimit(size_t newLimit) {
468 static const size_t minLimit = 256 * 1024;
469 if (newLimit < minLimit) {
470 newLimit = minLimit;
471 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000472
reed@google.com09837012012-04-23 15:04:44 +0000473 size_t prevLimit = fFontCacheLimit;
474 fFontCacheLimit = newLimit;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000475
reed@google.com09837012012-04-23 15:04:44 +0000476 size_t currUsed = fTotalMemoryUsed;
477 if (currUsed > newLimit) {
reed@google.com6172d672012-05-17 13:38:03 +0000478 SkAutoMutexAcquire ac(fMutex);
reed@google.com09837012012-04-23 15:04:44 +0000479 SkGlyphCache::InternalFreeCache(this, currUsed - newLimit);
reed@google.com09837012012-04-23 15:04:44 +0000480 }
481 return prevLimit;
482}
483
reed@google.com26344cf2012-06-27 18:23:01 +0000484void SkGlyphCache_Globals::purgeAll() {
485 SkAutoMutexAcquire ac(fMutex);
486 SkGlyphCache::InternalFreeCache(this, fTotalMemoryUsed);
487}
488
reed@google.com09837012012-04-23 15:04:44 +0000489// Returns the shared globals
490static SkGlyphCache_Globals& getSharedGlobals() {
reed@google.com5d248bc2011-11-17 21:49:02 +0000491 // we leak this, so we don't incur any shutdown cost of the destructor
reed@google.com6172d672012-05-17 13:38:03 +0000492 static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
493 (SkGlyphCache_Globals::kYes_UseMutex));
reed@google.com5d248bc2011-11-17 21:49:02 +0000494 return *gGlobals;
495}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496
reed@google.com09837012012-04-23 15:04:44 +0000497// Returns the TLS globals (if set), or the shared globals
498static SkGlyphCache_Globals& getGlobals() {
reed@google.com6172d672012-05-17 13:38:03 +0000499 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
500 return tls ? *tls : getSharedGlobals();
reed@google.com09837012012-04-23 15:04:44 +0000501}
502
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
504 void* context) {
reed@google.com5d248bc2011-11-17 21:49:02 +0000505 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 SkAutoMutexAcquire ac(globals.fMutex);
507 SkGlyphCache* cache;
reed@google.com22a02212011-03-01 21:33:48 +0000508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 globals.validate();
reed@google.com22a02212011-03-01 21:33:48 +0000510
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
512 if (proc(cache, context)) {
513 break;
514 }
515 }
516
517 globals.validate();
518}
519
520/* This guy calls the visitor from within the mutext lock, so the visitor
521 cannot:
522 - take too much time
523 - try to acquire the mutext again
524 - call a fontscaler (which might call into the cache)
525*/
526SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc,
527 bool (*proc)(const SkGlyphCache*, void*),
528 void* context) {
529 SkASSERT(desc);
530
reed@google.com5d248bc2011-11-17 21:49:02 +0000531 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 SkAutoMutexAcquire ac(globals.fMutex);
533 SkGlyphCache* cache;
534 bool insideMutex = true;
535
536 globals.validate();
537
538#ifdef USE_CACHE_HASH
539 SkGlyphCache** hash = globals.fHash;
540 unsigned index = desc_to_hashindex(desc);
541 cache = hash[index];
542 if (cache && *cache->fDesc == *desc) {
543 cache->detach(&globals.fHead);
544 goto FOUND_IT;
545 }
546#endif
547
548 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
549 if (cache->fDesc->equals(*desc)) {
550 cache->detach(&globals.fHead);
551 goto FOUND_IT;
552 }
553 }
554
555 /* Release the mutex now, before we create a new entry (which might have
556 side-effects like trying to access the cache/mutex (yikes!)
557 */
558 ac.release(); // release the mutex now
559 insideMutex = false; // can't use globals anymore
560
561 cache = SkNEW_ARGS(SkGlyphCache, (desc));
562
563FOUND_IT:
reed@android.comf2b98d62010-12-20 18:26:13 +0000564
565 AutoValidate av(cache);
566
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 if (proc(cache, context)) { // stay detached
568 if (insideMutex) {
569 SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed);
570 globals.fTotalMemoryUsed -= cache->fMemoryUsed;
571#ifdef USE_CACHE_HASH
572 hash[index] = NULL;
573#endif
574 }
575 } else { // reattach
576 if (insideMutex) {
577 cache->attachToHead(&globals.fHead);
578#ifdef USE_CACHE_HASH
579 hash[index] = cache;
580#endif
581 } else {
582 AttachCache(cache);
583 }
584 cache = NULL;
585 }
586 return cache;
587}
588
589void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
590 SkASSERT(cache);
591 SkASSERT(cache->fNext == NULL);
592
reed@google.com5d248bc2011-11-17 21:49:02 +0000593 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 SkAutoMutexAcquire ac(globals.fMutex);
595
596 globals.validate();
reed@android.comf2b98d62010-12-20 18:26:13 +0000597 cache->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598
599 // if we have a fixed budget for our cache, do a purge here
600 {
601 size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed;
reed@google.com77407ca2011-11-08 13:48:32 +0000602 size_t budgeted = SkGraphics::GetFontCacheLimit();
603 if (allocated > budgeted) {
604 (void)InternalFreeCache(&globals, allocated - budgeted);
605 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 }
607
608 cache->attachToHead(&globals.fHead);
609 globals.fTotalMemoryUsed += cache->fMemoryUsed;
610
611#ifdef USE_CACHE_HASH
612 unsigned index = desc_to_hashindex(cache->fDesc);
613 SkASSERT(globals.fHash[index] != cache);
614 globals.fHash[index] = cache;
615#endif
616
617 globals.validate();
618}
619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620///////////////////////////////////////////////////////////////////////////////
621
622SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
623 if (cache) {
624 while (cache->fNext) {
625 cache = cache->fNext;
626 }
627 }
628 return cache;
629}
630
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631#ifdef SK_DEBUG
632void SkGlyphCache_Globals::validate() const {
reed@google.comeb9a9bf2012-04-23 13:43:30 +0000633 size_t computed = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000634
reed@google.com331560e2012-04-23 14:09:38 +0000635 const SkGlyphCache* head = fHead;
reed@google.comeb9a9bf2012-04-23 13:43:30 +0000636 while (head != NULL) {
637 computed += head->fMemoryUsed;
638 head = head->fNext;
639 }
640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 if (fTotalMemoryUsed != computed) {
642 printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed);
643 }
644 SkASSERT(fTotalMemoryUsed == computed);
645}
646#endif
647
648size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals,
649 size_t bytesNeeded) {
650 globals->validate();
651
652 size_t bytesFreed = 0;
653 int count = 0;
654
655 // don't do any "small" purges
656 size_t minToPurge = globals->fTotalMemoryUsed >> 2;
657 if (bytesNeeded < minToPurge)
658 bytesNeeded = minToPurge;
659
660 SkGlyphCache* cache = FindTail(globals->fHead);
661 while (cache != NULL && bytesFreed < bytesNeeded) {
662 SkGlyphCache* prev = cache->fPrev;
663 bytesFreed += cache->fMemoryUsed;
664
665#ifdef USE_CACHE_HASH
666 unsigned index = desc_to_hashindex(cache->fDesc);
667 if (cache == globals->fHash[index]) {
668 globals->fHash[index] = NULL;
669 }
670#endif
671
672 cache->detach(&globals->fHead);
673 SkDELETE(cache);
674 cache = prev;
675 count += 1;
676 }
677
678 SkASSERT(bytesFreed <= globals->fTotalMemoryUsed);
679 globals->fTotalMemoryUsed -= bytesFreed;
680 globals->validate();
681
682#ifdef SPEW_PURGE_STATUS
reed@google.com11b4d8a2011-11-02 21:02:36 +0000683 if (count && !gSkSuppressFontCachePurgeSpew) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 SkDebugf("purging %dK from font cache [%d entries]\n",
685 (int)(bytesFreed >> 10), count);
686 }
687#endif
688
689 return bytesFreed;
690}
691
reed@android.comf2b98d62010-12-20 18:26:13 +0000692///////////////////////////////////////////////////////////////////////////////
reed@android.comf2b98d62010-12-20 18:26:13 +0000693
reed@google.com09837012012-04-23 15:04:44 +0000694#ifdef SK_DEBUG
reed@android.comf2b98d62010-12-20 18:26:13 +0000695void SkGlyphCache::validate() const {
696 int count = fGlyphArray.count();
697 for (int i = 0; i < count; i++) {
698 const SkGlyph* glyph = fGlyphArray[i];
699 SkASSERT(glyph);
700 SkASSERT(fGlyphAlloc.contains(glyph));
701 if (glyph->fImage) {
702 SkASSERT(fImageAlloc.contains(glyph->fImage));
703 }
704 }
705}
reed@android.comf2b98d62010-12-20 18:26:13 +0000706#endif
reed@google.com09837012012-04-23 15:04:44 +0000707
708///////////////////////////////////////////////////////////////////////////////
709///////////////////////////////////////////////////////////////////////////////
710
711#include "SkTypefaceCache.h"
712
713size_t SkGraphics::GetFontCacheLimit() {
714 return getSharedGlobals().getFontCacheLimit();
715}
716
717size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
718 return getSharedGlobals().setFontCacheLimit(bytes);
719}
720
reed@google.com79a1c34ee2012-07-30 13:08:01 +0000721size_t SkGraphics::GetFontCacheUsed() {
722 return getSharedGlobals().fTotalMemoryUsed;
723}
724
reed@google.com09837012012-04-23 15:04:44 +0000725void SkGraphics::PurgeFontCache() {
reed@google.com26344cf2012-06-27 18:23:01 +0000726 getSharedGlobals().purgeAll();
reed@google.com09837012012-04-23 15:04:44 +0000727 SkTypefaceCache::PurgeAll();
728}
729
reed@google.com6172d672012-05-17 13:38:03 +0000730size_t SkGraphics::GetTLSFontCacheLimit() {
731 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
732 return tls ? tls->getFontCacheLimit() : 0;
733}
734
reed@google.com803c67d2012-05-17 13:50:36 +0000735void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
reed@google.com6172d672012-05-17 13:38:03 +0000736 if (0 == bytes) {
737 SkGlyphCache_Globals::DeleteTLS();
738 } else {
739 SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes);
740 }
741}
742