blob: d33a13f4bc9b67c292994466f428c609bdc40909 [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@google.com90808e82013-03-19 14:44:54 +000016#include "SkTypeface.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
reed@google.com8887be02012-01-25 16:04:18 +000018//#define SPEW_PURGE_STATUS
reed@android.com8a1c16f2008-12-17 15:59:43 +000019//#define USE_CACHE_HASH
20//#define RECORD_HASH_EFFICIENCY
21
reed@google.com11b4d8a2011-11-02 21:02:36 +000022bool gSkSuppressFontCachePurgeSpew;
23
reed@android.com8a1c16f2008-12-17 15:59:43 +000024///////////////////////////////////////////////////////////////////////////////
25
26#ifdef RECORD_HASH_EFFICIENCY
27 static uint32_t gHashSuccess;
28 static uint32_t gHashCollision;
29
30 static void RecordHashSuccess() {
31 gHashSuccess += 1;
32 }
33
34 static void RecordHashCollisionIf(bool pred) {
35 if (pred) {
36 gHashCollision += 1;
reed@google.com22a02212011-03-01 21:33:48 +000037
reed@android.com8a1c16f2008-12-17 15:59:43 +000038 uint32_t total = gHashSuccess + gHashCollision;
39 SkDebugf("Font Cache Hash success rate: %d%%\n",
40 100 * gHashSuccess / total);
41 }
42 }
43#else
44 #define RecordHashSuccess() (void)0
45 #define RecordHashCollisionIf(pred) (void)0
46#endif
47#define RecordHashCollision() RecordHashCollisionIf(true)
48
49///////////////////////////////////////////////////////////////////////////////
50
51#define kMinGlphAlloc (sizeof(SkGlyph) * 64)
52#define kMinImageAlloc (24 * 64) // should be pointsize-dependent
53
54#define METRICS_RESERVE_COUNT 128 // so we don't grow this array a lot
55
reed@google.com90808e82013-03-19 14:44:54 +000056SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc)
57 : fGlyphAlloc(kMinGlphAlloc)
58 , fImageAlloc(kMinImageAlloc) {
59 SkASSERT(typeface);
60
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 fPrev = fNext = NULL;
62
63 fDesc = desc->copy();
reed@google.com90808e82013-03-19 14:44:54 +000064 fScalerContext = typeface->createScalerContext(desc);
reed@android.com8a1c16f2008-12-17 15:59:43 +000065 fScalerContext->getFontMetrics(NULL, &fFontMetricsY);
66
67 // init to 0 so that all of the pointers will be null
68 memset(fGlyphHash, 0, sizeof(fGlyphHash));
69 // init with 0xFF so that the charCode field will be -1, which is invalid
70 memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
reed@google.com22a02212011-03-01 21:33:48 +000071
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc;
reed@google.com22a02212011-03-01 21:33:48 +000073
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 fGlyphArray.setReserve(METRICS_RESERVE_COUNT);
75
76 fMetricsCount = 0;
77 fAdvanceCount = 0;
78 fAuxProcList = NULL;
79}
80
81SkGlyphCache::~SkGlyphCache() {
82 SkGlyph** gptr = fGlyphArray.begin();
83 SkGlyph** stop = fGlyphArray.end();
84 while (gptr < stop) {
85 SkPath* path = (*gptr)->fPath;
86 if (path) {
87 SkDELETE(path);
88 }
89 gptr += 1;
90 }
91 SkDescriptor::Free(fDesc);
92 SkDELETE(fScalerContext);
93 this->invokeAndRemoveAuxProcs();
94}
95
96///////////////////////////////////////////////////////////////////////////////
97
98#ifdef SK_DEBUG
reed@android.comf2b98d62010-12-20 18:26:13 +000099#define VALIDATE() AutoValidate av(this)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100#else
101#define VALIDATE()
102#endif
103
104uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
105 VALIDATE();
106 uint32_t id = SkGlyph::MakeID(charCode);
107 const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 if (rec.fID == id) {
110 return rec.fGlyph->getGlyphID();
111 } else {
112 return fScalerContext->charToGlyphID(charCode);
113 }
114}
115
reed@android.com9d3a9852010-01-08 14:07:42 +0000116SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
117 return fScalerContext->glyphIDToChar(glyphID);
118}
119
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000120unsigned SkGlyphCache::getGlyphCount() {
121 return fScalerContext->getGlyphCount();
122}
123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124///////////////////////////////////////////////////////////////////////////////
125
126const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
127 VALIDATE();
128 uint32_t id = SkGlyph::MakeID(charCode);
129 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000130
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 if (rec->fID != id) {
132 // this ID is based on the UniChar
133 rec->fID = id;
134 // this ID is based on the glyph index
135 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
136 rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
137 }
138 return *rec->fGlyph;
139}
140
141const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
142 VALIDATE();
143 uint32_t id = SkGlyph::MakeID(glyphID);
144 unsigned index = ID2HashIndex(id);
145 SkGlyph* glyph = fGlyphHash[index];
146
147 if (NULL == glyph || glyph->fID != id) {
148 glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
149 fGlyphHash[index] = glyph;
150 }
151 return *glyph;
152}
153
154///////////////////////////////////////////////////////////////////////////////
155
156const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
157 VALIDATE();
158 uint32_t id = SkGlyph::MakeID(charCode);
159 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000160
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 if (rec->fID != id) {
162 RecordHashCollisionIf(rec->fGlyph != NULL);
163 // this ID is based on the UniChar
164 rec->fID = id;
165 // this ID is based on the glyph index
166 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
167 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
168 } else {
169 RecordHashSuccess();
170 if (rec->fGlyph->isJustAdvance()) {
171 fScalerContext->getMetrics(rec->fGlyph);
172 }
173 }
174 SkASSERT(rec->fGlyph->isFullMetrics());
175 return *rec->fGlyph;
176}
177
178const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
179 SkFixed x, SkFixed y) {
180 VALIDATE();
181 uint32_t id = SkGlyph::MakeID(charCode, x, y);
182 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 if (rec->fID != id) {
185 RecordHashCollisionIf(rec->fGlyph != NULL);
186 // this ID is based on the UniChar
187 rec->fID = id;
188 // this ID is based on the glyph index
189 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
190 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
191 } else {
192 RecordHashSuccess();
193 if (rec->fGlyph->isJustAdvance()) {
194 fScalerContext->getMetrics(rec->fGlyph);
195 }
196 }
197 SkASSERT(rec->fGlyph->isFullMetrics());
198 return *rec->fGlyph;
199}
200
201const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
202 VALIDATE();
203 uint32_t id = SkGlyph::MakeID(glyphID);
204 unsigned index = ID2HashIndex(id);
205 SkGlyph* glyph = fGlyphHash[index];
reed@google.com22a02212011-03-01 21:33:48 +0000206
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 if (NULL == glyph || glyph->fID != id) {
208 RecordHashCollisionIf(glyph != NULL);
209 glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
210 fGlyphHash[index] = glyph;
211 } else {
212 RecordHashSuccess();
213 if (glyph->isJustAdvance()) {
214 fScalerContext->getMetrics(glyph);
215 }
216 }
217 SkASSERT(glyph->isFullMetrics());
218 return *glyph;
219}
220
221const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
222 SkFixed x, SkFixed y) {
223 VALIDATE();
224 uint32_t id = SkGlyph::MakeID(glyphID, x, y);
225 unsigned index = ID2HashIndex(id);
226 SkGlyph* glyph = fGlyphHash[index];
227
228 if (NULL == glyph || glyph->fID != id) {
229 RecordHashCollisionIf(glyph != NULL);
230 glyph = this->lookupMetrics(id, kFull_MetricsType);
231 fGlyphHash[index] = glyph;
232 } else {
233 RecordHashSuccess();
234 if (glyph->isJustAdvance()) {
235 fScalerContext->getMetrics(glyph);
236 }
237 }
238 SkASSERT(glyph->isFullMetrics());
239 return *glyph;
240}
241
242SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
243 SkGlyph* glyph;
244
245 int hi = 0;
246 int count = fGlyphArray.count();
247
248 if (count) {
249 SkGlyph** gptr = fGlyphArray.begin();
250 int lo = 0;
251
252 hi = count - 1;
253 while (lo < hi) {
254 int mid = (hi + lo) >> 1;
255 if (gptr[mid]->fID < id) {
256 lo = mid + 1;
257 } else {
258 hi = mid;
259 }
260 }
261 glyph = gptr[hi];
262 if (glyph->fID == id) {
263 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
264 fScalerContext->getMetrics(glyph);
265 }
266 return glyph;
267 }
268
269 // check if we need to bump hi before falling though to the allocator
270 if (glyph->fID < id) {
271 hi += 1;
272 }
273 }
274
275 // not found, but hi tells us where to inser the new glyph
276 fMemoryUsed += sizeof(SkGlyph);
277
278 glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
279 SkChunkAlloc::kThrow_AllocFailType);
reed@android.comf2b98d62010-12-20 18:26:13 +0000280 glyph->init(id);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 *fGlyphArray.insert(hi) = glyph;
reed@google.com22a02212011-03-01 21:33:48 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 if (kJustAdvance_MetricsType == mtype) {
284 fScalerContext->getAdvance(glyph);
285 fAdvanceCount += 1;
286 } else {
287 SkASSERT(kFull_MetricsType == mtype);
288 fScalerContext->getMetrics(glyph);
289 fMetricsCount += 1;
290 }
291
292 return glyph;
293}
294
295const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
scarybeasts@gmail.com17f694b2010-10-18 23:29:36 +0000296 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 if (glyph.fImage == NULL) {
298 size_t size = glyph.computeImageSize();
299 const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size,
300 SkChunkAlloc::kReturnNil_AllocFailType);
reed@google.com22a02212011-03-01 21:33:48 +0000301 // check that alloc() actually succeeded
302 if (glyph.fImage) {
303 fScalerContext->getImage(glyph);
reed@google.com5e2df642011-09-21 18:42:09 +0000304 // TODO: the scaler may have changed the maskformat during
305 // getImage (e.g. from AA or LCD to BW) which means we may have
306 // overallocated the buffer. Check if the new computedImageSize
307 // is smaller, and if so, strink the alloc size in fImageAlloc.
reed@google.com22a02212011-03-01 21:33:48 +0000308 fMemoryUsed += size;
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 }
311 }
312 return glyph.fImage;
313}
314
315const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
316 if (glyph.fWidth) {
317 if (glyph.fPath == NULL) {
318 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
319 fScalerContext->getPath(glyph, glyph.fPath);
320 fMemoryUsed += sizeof(SkPath) +
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000321 glyph.fPath->countPoints() * sizeof(SkPoint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 }
323 }
324 return glyph.fPath;
325}
326
327///////////////////////////////////////////////////////////////////////////////
328
329bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
330 const AuxProcRec* rec = fAuxProcList;
331 while (rec) {
332 if (rec->fProc == proc) {
333 if (dataPtr) {
334 *dataPtr = rec->fData;
335 }
336 return true;
337 }
338 rec = rec->fNext;
339 }
340 return false;
341}
342
343void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
344 if (proc == NULL) {
345 return;
346 }
347
348 AuxProcRec* rec = fAuxProcList;
349 while (rec) {
350 if (rec->fProc == proc) {
351 rec->fData = data;
352 return;
353 }
354 rec = rec->fNext;
355 }
356 // not found, create a new rec
357 rec = SkNEW(AuxProcRec);
358 rec->fProc = proc;
359 rec->fData = data;
360 rec->fNext = fAuxProcList;
361 fAuxProcList = rec;
362}
363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364void SkGlyphCache::invokeAndRemoveAuxProcs() {
365 AuxProcRec* rec = fAuxProcList;
366 while (rec) {
367 rec->fProc(rec->fData);
368 AuxProcRec* next = rec->fNext;
369 SkDELETE(rec);
370 rec = next;
371 }
372}
373
374///////////////////////////////////////////////////////////////////////////////
375///////////////////////////////////////////////////////////////////////////////
376
reed@google.com09837012012-04-23 15:04:44 +0000377#ifndef SK_DEFAULT_FONT_CACHE_LIMIT
378 #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024)
379#endif
380
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381#ifdef USE_CACHE_HASH
382 #define HASH_BITCOUNT 6
383 #define HASH_COUNT (1 << HASH_BITCOUNT)
384 #define HASH_MASK (HASH_COUNT - 1)
reed@google.com22a02212011-03-01 21:33:48 +0000385
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386 static unsigned desc_to_hashindex(const SkDescriptor* desc)
387 {
388 SkASSERT(HASH_MASK < 256); // since our munging reduces to 8 bits
389
390 uint32_t n = *(const uint32_t*)desc; //desc->getChecksum();
391 SkASSERT(n == desc->getChecksum());
392
393 // don't trust that the low bits of checksum vary enough, so...
394 n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30);
395
396 return n & HASH_MASK;
397 }
398#endif
399
reed@google.com5d248bc2011-11-17 21:49:02 +0000400#include "SkThread.h"
401
402class SkGlyphCache_Globals {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403public:
reed@google.com6172d672012-05-17 13:38:03 +0000404 enum UseMutex {
405 kNo_UseMutex, // thread-local cache
reed@google.com73c48f12012-07-20 11:35:40 +0000406 kYes_UseMutex // shared cache
reed@google.com6172d672012-05-17 13:38:03 +0000407 };
408
409 SkGlyphCache_Globals(UseMutex um) {
reed@google.com9998c662011-11-17 22:09:47 +0000410 fHead = NULL;
411 fTotalMemoryUsed = 0;
reed@google.com09837012012-04-23 15:04:44 +0000412 fFontCacheLimit = SK_DEFAULT_FONT_CACHE_LIMIT;
reed@google.com6172d672012-05-17 13:38:03 +0000413 fMutex = (kYes_UseMutex == um) ? SkNEW(SkMutex) : NULL;
reed@google.com09837012012-04-23 15:04:44 +0000414
reed@google.com9998c662011-11-17 22:09:47 +0000415#ifdef USE_CACHE_HASH
416 sk_bzero(fHash, sizeof(fHash));
417#endif
418 }
419
reed@google.com6172d672012-05-17 13:38:03 +0000420 ~SkGlyphCache_Globals() {
reed@google.com7b578922012-05-21 15:29:27 +0000421 SkGlyphCache* cache = fHead;
422 while (cache) {
423 SkGlyphCache* next = cache->fNext;
424 SkDELETE(cache);
425 cache = next;
426 }
427
reed@google.com6172d672012-05-17 13:38:03 +0000428 SkDELETE(fMutex);
429 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000430
reed@google.com6172d672012-05-17 13:38:03 +0000431 SkMutex* fMutex;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 SkGlyphCache* fHead;
433 size_t fTotalMemoryUsed;
434#ifdef USE_CACHE_HASH
435 SkGlyphCache* fHash[HASH_COUNT];
436#endif
437
438#ifdef SK_DEBUG
439 void validate() const;
440#else
441 void validate() const {}
442#endif
reed@google.com09837012012-04-23 15:04:44 +0000443
444 size_t getFontCacheLimit() const { return fFontCacheLimit; }
445 size_t setFontCacheLimit(size_t limit);
reed@google.com26344cf2012-06-27 18:23:01 +0000446 void purgeAll(); // does not change budget
reed@google.com09837012012-04-23 15:04:44 +0000447
reed@google.com6172d672012-05-17 13:38:03 +0000448 // can return NULL
449 static SkGlyphCache_Globals* FindTLS() {
450 return (SkGlyphCache_Globals*)SkTLS::Find(CreateTLS);
451 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000452
reed@google.com6172d672012-05-17 13:38:03 +0000453 static SkGlyphCache_Globals& GetTLS() {
454 return *(SkGlyphCache_Globals*)SkTLS::Get(CreateTLS, DeleteTLS);
455 }
456
457 static void DeleteTLS() { SkTLS::Delete(CreateTLS); }
458
reed@google.com09837012012-04-23 15:04:44 +0000459private:
460 size_t fFontCacheLimit;
reed@google.com6172d672012-05-17 13:38:03 +0000461
462 static void* CreateTLS() {
463 return SkNEW_ARGS(SkGlyphCache_Globals, (kNo_UseMutex));
464 }
465
466 static void DeleteTLS(void* ptr) {
467 SkDELETE((SkGlyphCache_Globals*)ptr);
468 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469};
470
reed@google.com09837012012-04-23 15:04:44 +0000471size_t SkGlyphCache_Globals::setFontCacheLimit(size_t newLimit) {
472 static const size_t minLimit = 256 * 1024;
473 if (newLimit < minLimit) {
474 newLimit = minLimit;
475 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000476
reed@google.com09837012012-04-23 15:04:44 +0000477 size_t prevLimit = fFontCacheLimit;
478 fFontCacheLimit = newLimit;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000479
reed@google.com09837012012-04-23 15:04:44 +0000480 size_t currUsed = fTotalMemoryUsed;
481 if (currUsed > newLimit) {
reed@google.com6172d672012-05-17 13:38:03 +0000482 SkAutoMutexAcquire ac(fMutex);
reed@google.com09837012012-04-23 15:04:44 +0000483 SkGlyphCache::InternalFreeCache(this, currUsed - newLimit);
reed@google.com09837012012-04-23 15:04:44 +0000484 }
485 return prevLimit;
486}
487
reed@google.com26344cf2012-06-27 18:23:01 +0000488void SkGlyphCache_Globals::purgeAll() {
489 SkAutoMutexAcquire ac(fMutex);
490 SkGlyphCache::InternalFreeCache(this, fTotalMemoryUsed);
491}
492
reed@google.com09837012012-04-23 15:04:44 +0000493// Returns the shared globals
494static SkGlyphCache_Globals& getSharedGlobals() {
reed@google.com5d248bc2011-11-17 21:49:02 +0000495 // we leak this, so we don't incur any shutdown cost of the destructor
reed@google.com6172d672012-05-17 13:38:03 +0000496 static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
497 (SkGlyphCache_Globals::kYes_UseMutex));
reed@google.com5d248bc2011-11-17 21:49:02 +0000498 return *gGlobals;
499}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500
reed@google.com09837012012-04-23 15:04:44 +0000501// Returns the TLS globals (if set), or the shared globals
502static SkGlyphCache_Globals& getGlobals() {
reed@google.com6172d672012-05-17 13:38:03 +0000503 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
504 return tls ? *tls : getSharedGlobals();
reed@google.com09837012012-04-23 15:04:44 +0000505}
506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
508 void* context) {
reed@google.com5d248bc2011-11-17 21:49:02 +0000509 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 SkAutoMutexAcquire ac(globals.fMutex);
511 SkGlyphCache* cache;
reed@google.com22a02212011-03-01 21:33:48 +0000512
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 globals.validate();
reed@google.com22a02212011-03-01 21:33:48 +0000514
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
516 if (proc(cache, context)) {
517 break;
518 }
519 }
520
521 globals.validate();
522}
523
524/* This guy calls the visitor from within the mutext lock, so the visitor
525 cannot:
526 - take too much time
527 - try to acquire the mutext again
528 - call a fontscaler (which might call into the cache)
529*/
reed@google.com90808e82013-03-19 14:44:54 +0000530SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
531 const SkDescriptor* desc,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 bool (*proc)(const SkGlyphCache*, void*),
533 void* context) {
reed@google.com90808e82013-03-19 14:44:54 +0000534 if (!typeface) {
535 typeface = SkTypeface::GetDefaultTypeface();
536 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 SkASSERT(desc);
538
reed@google.com5d248bc2011-11-17 21:49:02 +0000539 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 SkAutoMutexAcquire ac(globals.fMutex);
541 SkGlyphCache* cache;
542 bool insideMutex = true;
543
544 globals.validate();
545
546#ifdef USE_CACHE_HASH
547 SkGlyphCache** hash = globals.fHash;
548 unsigned index = desc_to_hashindex(desc);
549 cache = hash[index];
550 if (cache && *cache->fDesc == *desc) {
551 cache->detach(&globals.fHead);
552 goto FOUND_IT;
553 }
554#endif
555
556 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
557 if (cache->fDesc->equals(*desc)) {
558 cache->detach(&globals.fHead);
559 goto FOUND_IT;
560 }
561 }
562
563 /* Release the mutex now, before we create a new entry (which might have
564 side-effects like trying to access the cache/mutex (yikes!)
565 */
566 ac.release(); // release the mutex now
567 insideMutex = false; // can't use globals anymore
568
reed@google.com90808e82013-03-19 14:44:54 +0000569 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570
571FOUND_IT:
reed@android.comf2b98d62010-12-20 18:26:13 +0000572
573 AutoValidate av(cache);
574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 if (proc(cache, context)) { // stay detached
576 if (insideMutex) {
577 SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed);
578 globals.fTotalMemoryUsed -= cache->fMemoryUsed;
579#ifdef USE_CACHE_HASH
580 hash[index] = NULL;
581#endif
582 }
583 } else { // reattach
584 if (insideMutex) {
585 cache->attachToHead(&globals.fHead);
586#ifdef USE_CACHE_HASH
587 hash[index] = cache;
588#endif
589 } else {
590 AttachCache(cache);
591 }
592 cache = NULL;
593 }
594 return cache;
595}
596
597void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
598 SkASSERT(cache);
599 SkASSERT(cache->fNext == NULL);
600
reed@google.com5d248bc2011-11-17 21:49:02 +0000601 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 SkAutoMutexAcquire ac(globals.fMutex);
603
604 globals.validate();
reed@android.comf2b98d62010-12-20 18:26:13 +0000605 cache->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606
607 // if we have a fixed budget for our cache, do a purge here
608 {
609 size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed;
bungeman@google.com0c3b9cf2012-10-29 20:16:30 +0000610 size_t budgeted = globals.getFontCacheLimit();
reed@google.com77407ca2011-11-08 13:48:32 +0000611 if (allocated > budgeted) {
612 (void)InternalFreeCache(&globals, allocated - budgeted);
613 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 }
615
616 cache->attachToHead(&globals.fHead);
617 globals.fTotalMemoryUsed += cache->fMemoryUsed;
618
619#ifdef USE_CACHE_HASH
620 unsigned index = desc_to_hashindex(cache->fDesc);
621 SkASSERT(globals.fHash[index] != cache);
622 globals.fHash[index] = cache;
623#endif
624
625 globals.validate();
626}
627
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628///////////////////////////////////////////////////////////////////////////////
629
630SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
631 if (cache) {
632 while (cache->fNext) {
633 cache = cache->fNext;
634 }
635 }
636 return cache;
637}
638
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639#ifdef SK_DEBUG
640void SkGlyphCache_Globals::validate() const {
reed@google.comeb9a9bf2012-04-23 13:43:30 +0000641 size_t computed = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000642
reed@google.com331560e2012-04-23 14:09:38 +0000643 const SkGlyphCache* head = fHead;
reed@google.comeb9a9bf2012-04-23 13:43:30 +0000644 while (head != NULL) {
645 computed += head->fMemoryUsed;
646 head = head->fNext;
647 }
648
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 if (fTotalMemoryUsed != computed) {
650 printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed);
651 }
652 SkASSERT(fTotalMemoryUsed == computed);
653}
654#endif
655
656size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals,
657 size_t bytesNeeded) {
658 globals->validate();
659
660 size_t bytesFreed = 0;
661 int count = 0;
662
663 // don't do any "small" purges
664 size_t minToPurge = globals->fTotalMemoryUsed >> 2;
665 if (bytesNeeded < minToPurge)
666 bytesNeeded = minToPurge;
667
668 SkGlyphCache* cache = FindTail(globals->fHead);
669 while (cache != NULL && bytesFreed < bytesNeeded) {
670 SkGlyphCache* prev = cache->fPrev;
671 bytesFreed += cache->fMemoryUsed;
672
673#ifdef USE_CACHE_HASH
674 unsigned index = desc_to_hashindex(cache->fDesc);
675 if (cache == globals->fHash[index]) {
676 globals->fHash[index] = NULL;
677 }
678#endif
679
680 cache->detach(&globals->fHead);
681 SkDELETE(cache);
682 cache = prev;
683 count += 1;
684 }
685
686 SkASSERT(bytesFreed <= globals->fTotalMemoryUsed);
687 globals->fTotalMemoryUsed -= bytesFreed;
688 globals->validate();
689
690#ifdef SPEW_PURGE_STATUS
reed@google.com11b4d8a2011-11-02 21:02:36 +0000691 if (count && !gSkSuppressFontCachePurgeSpew) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 SkDebugf("purging %dK from font cache [%d entries]\n",
693 (int)(bytesFreed >> 10), count);
694 }
695#endif
696
697 return bytesFreed;
698}
699
reed@android.comf2b98d62010-12-20 18:26:13 +0000700///////////////////////////////////////////////////////////////////////////////
reed@android.comf2b98d62010-12-20 18:26:13 +0000701
reed@google.com09837012012-04-23 15:04:44 +0000702#ifdef SK_DEBUG
reed@android.comf2b98d62010-12-20 18:26:13 +0000703void SkGlyphCache::validate() const {
djsollen@google.com000dea72012-11-30 16:19:32 +0000704#ifdef SK_DEBUG_GLYPH_CACHE
reed@android.comf2b98d62010-12-20 18:26:13 +0000705 int count = fGlyphArray.count();
706 for (int i = 0; i < count; i++) {
707 const SkGlyph* glyph = fGlyphArray[i];
708 SkASSERT(glyph);
709 SkASSERT(fGlyphAlloc.contains(glyph));
710 if (glyph->fImage) {
711 SkASSERT(fImageAlloc.contains(glyph->fImage));
712 }
713 }
djsollen@google.com000dea72012-11-30 16:19:32 +0000714#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000715}
reed@android.comf2b98d62010-12-20 18:26:13 +0000716#endif
reed@google.com09837012012-04-23 15:04:44 +0000717
718///////////////////////////////////////////////////////////////////////////////
719///////////////////////////////////////////////////////////////////////////////
720
721#include "SkTypefaceCache.h"
722
723size_t SkGraphics::GetFontCacheLimit() {
724 return getSharedGlobals().getFontCacheLimit();
725}
726
727size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
728 return getSharedGlobals().setFontCacheLimit(bytes);
729}
730
reed@google.com79a1c34ee2012-07-30 13:08:01 +0000731size_t SkGraphics::GetFontCacheUsed() {
732 return getSharedGlobals().fTotalMemoryUsed;
733}
734
reed@google.com09837012012-04-23 15:04:44 +0000735void SkGraphics::PurgeFontCache() {
reed@google.com26344cf2012-06-27 18:23:01 +0000736 getSharedGlobals().purgeAll();
reed@google.com09837012012-04-23 15:04:44 +0000737 SkTypefaceCache::PurgeAll();
738}
739
reed@google.com6172d672012-05-17 13:38:03 +0000740size_t SkGraphics::GetTLSFontCacheLimit() {
741 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
742 return tls ? tls->getFontCacheLimit() : 0;
743}
744
reed@google.com803c67d2012-05-17 13:50:36 +0000745void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
reed@google.com6172d672012-05-17 13:38:03 +0000746 if (0 == bytes) {
747 SkGlyphCache_Globals::DeleteTLS();
748 } else {
749 SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes);
750 }
751}