1 /*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkGlyphCache.h"
9 #include "SkGlyphCache_Globals.h"
10 #include "SkGraphics.h"
11 #include "SkOnce.h"
12 #include "SkPath.h"
13 #include "SkTemplates.h"
14 #include "SkTraceMemoryDump.h"
15 #include "SkTypeface.h"
16
17 #include <cctype>
18
19 //#define SPEW_PURGE_STATUS
20
21 namespace {
22 const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
23 } // namespace
24
25 // Returns the shared globals
get_globals()26 static SkGlyphCache_Globals& get_globals() {
27 static SkOnce once;
28 static SkGlyphCache_Globals* globals;
29
30 once([]{ globals = new SkGlyphCache_Globals; });
31 return *globals;
32 }
33
34 ///////////////////////////////////////////////////////////////////////////////
35
SkGlyphCache(const SkDescriptor * desc,std::unique_ptr<SkScalerContext> ctx)36 SkGlyphCache::SkGlyphCache(const SkDescriptor* desc, std::unique_ptr<SkScalerContext> ctx)
37 : fDesc(desc->copy())
38 , fScalerContext(std::move(ctx)) {
39 SkASSERT(desc);
40 SkASSERT(fScalerContext);
41
42 fPrev = fNext = nullptr;
43
44 fScalerContext->getFontMetrics(&fFontMetrics);
45
46 fMemoryUsed = sizeof(*this);
47 }
48
~SkGlyphCache()49 SkGlyphCache::~SkGlyphCache() {
50 fGlyphMap.foreach([](SkGlyph* g) {
51 if (g->fPathData) {
52 delete g->fPathData->fPath;
53 }
54 });
55 }
56
getCharGlyphRec(SkPackedUnicharID packedUnicharID)57 SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(SkPackedUnicharID packedUnicharID) {
58 if (!fPackedUnicharIDToPackedGlyphID) {
59 fPackedUnicharIDToPackedGlyphID.reset(new CharGlyphRec[kHashCount]);
60 }
61
62 return &fPackedUnicharIDToPackedGlyphID[packedUnicharID.hash() & kHashMask];
63 }
64
65 ///////////////////////////////////////////////////////////////////////////////
66
67 #ifdef SK_DEBUG
68 #define VALIDATE() AutoValidate av(this)
69 #else
70 #define VALIDATE()
71 #endif
72
unicharToGlyph(SkUnichar charCode)73 SkGlyphID SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
74 VALIDATE();
75 SkPackedUnicharID packedUnicharID(charCode);
76 CharGlyphRec* rec = this->getCharGlyphRec(packedUnicharID);
77
78 if (rec->fPackedUnicharID == packedUnicharID) {
79 // The glyph exists in the unichar to glyph mapping cache. Return it.
80 return rec->fPackedGlyphID.code();
81 } else {
82 // The glyph is not in the unichar to glyph mapping cache. Insert it.
83 rec->fPackedUnicharID = packedUnicharID;
84 SkGlyphID glyphID = fScalerContext->charToGlyphID(charCode);
85 rec->fPackedGlyphID = SkPackedGlyphID(glyphID);
86 return glyphID;
87 }
88 }
89
glyphToUnichar(SkGlyphID glyphID)90 SkUnichar SkGlyphCache::glyphToUnichar(SkGlyphID glyphID) {
91 return fScalerContext->glyphIDToChar(glyphID);
92 }
93
getGlyphCount() const94 unsigned SkGlyphCache::getGlyphCount() const {
95 return fScalerContext->getGlyphCount();
96 }
97
countCachedGlyphs() const98 int SkGlyphCache::countCachedGlyphs() const {
99 return fGlyphMap.count();
100 }
101
102 ///////////////////////////////////////////////////////////////////////////////
103
getUnicharAdvance(SkUnichar charCode)104 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
105 VALIDATE();
106 return *this->lookupByChar(charCode, kJustAdvance_MetricsType);
107 }
108
getGlyphIDAdvance(uint16_t glyphID)109 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
110 VALIDATE();
111 SkPackedGlyphID packedGlyphID(glyphID);
112 return *this->lookupByPackedGlyphID(packedGlyphID, kJustAdvance_MetricsType);
113 }
114
115 ///////////////////////////////////////////////////////////////////////////////
116
getUnicharMetrics(SkUnichar charCode)117 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
118 VALIDATE();
119 return *this->lookupByChar(charCode, kFull_MetricsType);
120 }
121
getUnicharMetrics(SkUnichar charCode,SkFixed x,SkFixed y)122 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, SkFixed x, SkFixed y) {
123 VALIDATE();
124 return *this->lookupByChar(charCode, kFull_MetricsType, x, y);
125 }
126
getGlyphIDMetrics(uint16_t glyphID)127 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
128 VALIDATE();
129 SkPackedGlyphID packedGlyphID(glyphID);
130 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType);
131 }
132
getGlyphIDMetrics(uint16_t glyphID,SkFixed x,SkFixed y)133 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, SkFixed x, SkFixed y) {
134 VALIDATE();
135 SkPackedGlyphID packedGlyphID(glyphID, x, y);
136 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType);
137 }
138
lookupByChar(SkUnichar charCode,MetricsType type,SkFixed x,SkFixed y)139 SkGlyph* SkGlyphCache::lookupByChar(SkUnichar charCode, MetricsType type, SkFixed x, SkFixed y) {
140 SkPackedUnicharID id(charCode, x, y);
141 CharGlyphRec* rec = this->getCharGlyphRec(id);
142 if (rec->fPackedUnicharID != id) {
143 rec->fPackedUnicharID = id;
144 rec->fPackedGlyphID = SkPackedGlyphID(fScalerContext->charToGlyphID(charCode), x, y);
145 }
146 return this->lookupByPackedGlyphID(rec->fPackedGlyphID, type);
147 }
148
lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID,MetricsType type)149 SkGlyph* SkGlyphCache::lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID, MetricsType type) {
150 SkGlyph* glyph = fGlyphMap.find(packedGlyphID);
151
152 if (nullptr == glyph) {
153 glyph = this->allocateNewGlyph(packedGlyphID, type);
154 } else {
155 if (type == kFull_MetricsType && glyph->isJustAdvance()) {
156 fScalerContext->getMetrics(glyph);
157 }
158 }
159 return glyph;
160 }
161
allocateNewGlyph(SkPackedGlyphID packedGlyphID,MetricsType mtype)162 SkGlyph* SkGlyphCache::allocateNewGlyph(SkPackedGlyphID packedGlyphID, MetricsType mtype) {
163 fMemoryUsed += sizeof(SkGlyph);
164
165 SkGlyph* glyphPtr;
166 {
167 SkGlyph glyph;
168 glyph.initWithGlyphID(packedGlyphID);
169 glyphPtr = fGlyphMap.set(glyph);
170 }
171
172 if (kJustAdvance_MetricsType == mtype) {
173 fScalerContext->getAdvance(glyphPtr);
174 } else {
175 SkASSERT(kFull_MetricsType == mtype);
176 fScalerContext->getMetrics(glyphPtr);
177 }
178
179 SkASSERT(glyphPtr->fID != SkPackedGlyphID());
180 return glyphPtr;
181 }
182
findImage(const SkGlyph & glyph)183 const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
184 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
185 if (nullptr == glyph.fImage) {
186 size_t size = const_cast<SkGlyph&>(glyph).allocImage(&fAlloc);
187 // check that alloc() actually succeeded
188 if (glyph.fImage) {
189 fScalerContext->getImage(glyph);
190 // TODO: the scaler may have changed the maskformat during
191 // getImage (e.g. from AA or LCD to BW) which means we may have
192 // overallocated the buffer. Check if the new computedImageSize
193 // is smaller, and if so, strink the alloc size in fImageAlloc.
194 fMemoryUsed += size;
195 }
196 }
197 }
198 return glyph.fImage;
199 }
200
findPath(const SkGlyph & glyph)201 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
202 if (glyph.fWidth) {
203 if (glyph.fPathData == nullptr) {
204 SkGlyph::PathData* pathData = fAlloc.make<SkGlyph::PathData>();
205 const_cast<SkGlyph&>(glyph).fPathData = pathData;
206 pathData->fIntercept = nullptr;
207 SkPath* path = pathData->fPath = new SkPath;
208 fScalerContext->getPath(glyph.getPackedID(), path);
209 fMemoryUsed += sizeof(SkPath) + path->countPoints() * sizeof(SkPoint);
210 }
211 }
212 return glyph.fPathData ? glyph.fPathData->fPath : nullptr;
213 }
214
215 #include "../pathops/SkPathOpsCubic.h"
216 #include "../pathops/SkPathOpsQuad.h"
217
quad_in_bounds(const SkScalar * pts,const SkScalar bounds[2])218 static bool quad_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) {
219 SkScalar min = SkTMin(SkTMin(pts[0], pts[2]), pts[4]);
220 if (bounds[1] < min) {
221 return false;
222 }
223 SkScalar max = SkTMax(SkTMax(pts[0], pts[2]), pts[4]);
224 return bounds[0] < max;
225 }
226
cubic_in_bounds(const SkScalar * pts,const SkScalar bounds[2])227 static bool cubic_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) {
228 SkScalar min = SkTMin(SkTMin(SkTMin(pts[0], pts[2]), pts[4]), pts[6]);
229 if (bounds[1] < min) {
230 return false;
231 }
232 SkScalar max = SkTMax(SkTMax(SkTMax(pts[0], pts[2]), pts[4]), pts[6]);
233 return bounds[0] < max;
234 }
235
OffsetResults(const SkGlyph::Intercept * intercept,SkScalar scale,SkScalar xPos,SkScalar * array,int * count)236 void SkGlyphCache::OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale,
237 SkScalar xPos, SkScalar* array, int* count) {
238 if (array) {
239 array += *count;
240 for (int index = 0; index < 2; index++) {
241 *array++ = intercept->fInterval[index] * scale + xPos;
242 }
243 }
244 *count += 2;
245 }
246
AddInterval(SkScalar val,SkGlyph::Intercept * intercept)247 void SkGlyphCache::AddInterval(SkScalar val, SkGlyph::Intercept* intercept) {
248 intercept->fInterval[0] = SkTMin(intercept->fInterval[0], val);
249 intercept->fInterval[1] = SkTMax(intercept->fInterval[1], val);
250 }
251
AddPoints(const SkPoint * pts,int ptCount,const SkScalar bounds[2],bool yAxis,SkGlyph::Intercept * intercept)252 void SkGlyphCache::AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2],
253 bool yAxis, SkGlyph::Intercept* intercept) {
254 for (int i = 0; i < ptCount; ++i) {
255 SkScalar val = *(&pts[i].fY - yAxis);
256 if (bounds[0] < val && val < bounds[1]) {
257 AddInterval(*(&pts[i].fX + yAxis), intercept);
258 }
259 }
260 }
261
AddLine(const SkPoint pts[2],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)262 void SkGlyphCache::AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis,
263 SkGlyph::Intercept* intercept) {
264 SkScalar t = yAxis ? (axis - pts[0].fX) / (pts[1].fX - pts[0].fX)
265 : (axis - pts[0].fY) / (pts[1].fY - pts[0].fY);
266 if (0 <= t && t < 1) { // this handles divide by zero above
267 AddInterval(yAxis ? pts[0].fY + t * (pts[1].fY - pts[0].fY)
268 : pts[0].fX + t * (pts[1].fX - pts[0].fX), intercept);
269 }
270 }
271
AddQuad(const SkPoint pts[2],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)272 void SkGlyphCache::AddQuad(const SkPoint pts[2], SkScalar axis, bool yAxis,
273 SkGlyph::Intercept* intercept) {
274 SkDQuad quad;
275 quad.set(pts);
276 double roots[2];
277 int count = yAxis ? quad.verticalIntersect(axis, roots)
278 : quad.horizontalIntersect(axis, roots);
279 while (--count >= 0) {
280 SkPoint pt = quad.ptAtT(roots[count]).asSkPoint();
281 AddInterval(*(&pt.fX + yAxis), intercept);
282 }
283 }
284
AddCubic(const SkPoint pts[3],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)285 void SkGlyphCache::AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis,
286 SkGlyph::Intercept* intercept) {
287 SkDCubic cubic;
288 cubic.set(pts);
289 double roots[3];
290 int count = yAxis ? cubic.verticalIntersect(axis, roots)
291 : cubic.horizontalIntersect(axis, roots);
292 while (--count >= 0) {
293 SkPoint pt = cubic.ptAtT(roots[count]).asSkPoint();
294 AddInterval(*(&pt.fX + yAxis), intercept);
295 }
296 }
297
MatchBounds(const SkGlyph * glyph,const SkScalar bounds[2])298 const SkGlyph::Intercept* SkGlyphCache::MatchBounds(const SkGlyph* glyph,
299 const SkScalar bounds[2]) {
300 if (!glyph->fPathData) {
301 return nullptr;
302 }
303 const SkGlyph::Intercept* intercept = glyph->fPathData->fIntercept;
304 while (intercept) {
305 if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) {
306 return intercept;
307 }
308 intercept = intercept->fNext;
309 }
310 return nullptr;
311 }
312
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,bool yAxis,SkGlyph * glyph,SkScalar * array,int * count)313 void SkGlyphCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
314 bool yAxis, SkGlyph* glyph, SkScalar* array, int* count) {
315 const SkGlyph::Intercept* match = MatchBounds(glyph, bounds);
316
317 if (match) {
318 if (match->fInterval[0] < match->fInterval[1]) {
319 OffsetResults(match, scale, xPos, array, count);
320 }
321 return;
322 }
323
324 SkGlyph::Intercept* intercept = fAlloc.make<SkGlyph::Intercept>();
325 intercept->fNext = glyph->fPathData->fIntercept;
326 intercept->fBounds[0] = bounds[0];
327 intercept->fBounds[1] = bounds[1];
328 intercept->fInterval[0] = SK_ScalarMax;
329 intercept->fInterval[1] = SK_ScalarMin;
330 glyph->fPathData->fIntercept = intercept;
331 const SkPath* path = glyph->fPathData->fPath;
332 const SkRect& pathBounds = path->getBounds();
333 if (*(&pathBounds.fBottom - yAxis) < bounds[0] || bounds[1] < *(&pathBounds.fTop - yAxis)) {
334 return;
335 }
336 SkPath::Iter iter(*path, false);
337 SkPoint pts[4];
338 SkPath::Verb verb;
339 while (SkPath::kDone_Verb != (verb = iter.next(pts))) {
340 switch (verb) {
341 case SkPath::kMove_Verb:
342 break;
343 case SkPath::kLine_Verb:
344 AddLine(pts, bounds[0], yAxis, intercept);
345 AddLine(pts, bounds[1], yAxis, intercept);
346 AddPoints(pts, 2, bounds, yAxis, intercept);
347 break;
348 case SkPath::kQuad_Verb:
349 if (!quad_in_bounds(&pts[0].fY - yAxis, bounds)) {
350 break;
351 }
352 AddQuad(pts, bounds[0], yAxis, intercept);
353 AddQuad(pts, bounds[1], yAxis, intercept);
354 AddPoints(pts, 3, bounds, yAxis, intercept);
355 break;
356 case SkPath::kConic_Verb:
357 SkASSERT(0); // no support for text composed of conics
358 break;
359 case SkPath::kCubic_Verb:
360 if (!cubic_in_bounds(&pts[0].fY - yAxis, bounds)) {
361 break;
362 }
363 AddCubic(pts, bounds[0], yAxis, intercept);
364 AddCubic(pts, bounds[1], yAxis, intercept);
365 AddPoints(pts, 4, bounds, yAxis, intercept);
366 break;
367 case SkPath::kClose_Verb:
368 break;
369 default:
370 SkASSERT(0);
371 break;
372 }
373 }
374 if (intercept->fInterval[0] >= intercept->fInterval[1]) {
375 intercept->fInterval[0] = SK_ScalarMax;
376 intercept->fInterval[1] = SK_ScalarMin;
377 return;
378 }
379 OffsetResults(intercept, scale, xPos, array, count);
380 }
381
dump() const382 void SkGlyphCache::dump() const {
383 const SkTypeface* face = fScalerContext->getTypeface();
384 const SkScalerContextRec& rec = fScalerContext->getRec();
385 SkMatrix matrix;
386 rec.getSingleMatrix(&matrix);
387 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
388 SkString name;
389 face->getFamilyName(&name);
390
391 SkString msg;
392 msg.printf("cache typeface:%x %25s:%d size:%2g [%g %g %g %g] lum:%02X devG:%d pntG:%d cntr:%d glyphs:%3d",
393 face->uniqueID(), name.c_str(), face->style(), rec.fTextSize,
394 matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewX],
395 matrix[SkMatrix::kMSkewY], matrix[SkMatrix::kMScaleY],
396 rec.fLumBits & 0xFF, rec.fDeviceGamma, rec.fPaintGamma, rec.fContrast,
397 fGlyphMap.count());
398 SkDebugf("%s\n", msg.c_str());
399 }
400
401 ///////////////////////////////////////////////////////////////////////////////
402 ///////////////////////////////////////////////////////////////////////////////
403
getTotalMemoryUsed() const404 size_t SkGlyphCache_Globals::getTotalMemoryUsed() const {
405 SkAutoExclusive ac(fLock);
406 return fTotalMemoryUsed;
407 }
408
getCacheCountUsed() const409 int SkGlyphCache_Globals::getCacheCountUsed() const {
410 SkAutoExclusive ac(fLock);
411 return fCacheCount;
412 }
413
getCacheCountLimit() const414 int SkGlyphCache_Globals::getCacheCountLimit() const {
415 SkAutoExclusive ac(fLock);
416 return fCacheCountLimit;
417 }
418
setCacheSizeLimit(size_t newLimit)419 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
420 static const size_t minLimit = 256 * 1024;
421 if (newLimit < minLimit) {
422 newLimit = minLimit;
423 }
424
425 SkAutoExclusive ac(fLock);
426
427 size_t prevLimit = fCacheSizeLimit;
428 fCacheSizeLimit = newLimit;
429 this->internalPurge();
430 return prevLimit;
431 }
432
getCacheSizeLimit() const433 size_t SkGlyphCache_Globals::getCacheSizeLimit() const {
434 SkAutoExclusive ac(fLock);
435 return fCacheSizeLimit;
436 }
437
setCacheCountLimit(int newCount)438 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
439 if (newCount < 0) {
440 newCount = 0;
441 }
442
443 SkAutoExclusive ac(fLock);
444
445 int prevCount = fCacheCountLimit;
446 fCacheCountLimit = newCount;
447 this->internalPurge();
448 return prevCount;
449 }
450
purgeAll()451 void SkGlyphCache_Globals::purgeAll() {
452 SkAutoExclusive ac(fLock);
453 this->internalPurge(fTotalMemoryUsed);
454 }
455
456 /* This guy calls the visitor from within the mutext lock, so the visitor
457 cannot:
458 - take too much time
459 - try to acquire the mutext again
460 - call a fontscaler (which might call into the cache)
461 */
VisitCache(SkTypeface * typeface,const SkScalerContextEffects & effects,const SkDescriptor * desc,bool (* proc)(const SkGlyphCache *,void *),void * context)462 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
463 const SkScalerContextEffects& effects,
464 const SkDescriptor* desc,
465 bool (*proc)(const SkGlyphCache*, void*),
466 void* context) {
467 if (!typeface) {
468 typeface = SkTypeface::GetDefaultTypeface();
469 }
470 SkASSERT(desc);
471
472 // Precondition: the typeface id must be the fFontID in the descriptor
473 SkDEBUGCODE(
474 uint32_t length = 0;
475 const SkScalerContext::Rec* rec = static_cast<const SkScalerContext::Rec*>(
476 desc->findEntry(kRec_SkDescriptorTag, &length));
477 SkASSERT(rec);
478 SkASSERT(length == sizeof(*rec));
479 SkASSERT(typeface->uniqueID() == rec->fFontID);
480 )
481
482 SkGlyphCache_Globals& globals = get_globals();
483 SkGlyphCache* cache;
484
485 {
486 SkAutoExclusive ac(globals.fLock);
487
488 globals.validate();
489
490 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
491 if (*cache->fDesc == *desc) {
492 globals.internalDetachCache(cache);
493 if (!proc(cache, context)) {
494 globals.internalAttachCacheToHead(cache);
495 cache = nullptr;
496 }
497 return cache;
498 }
499 }
500 }
501
502 // Check if we can create a scaler-context before creating the glyphcache.
503 // If not, we may have exhausted OS/font resources, so try purging the
504 // cache once and try again.
505 {
506 // pass true the first time, to notice if the scalercontext failed,
507 // so we can try the purge.
508 std::unique_ptr<SkScalerContext> ctx = typeface->createScalerContext(effects, desc, true);
509 if (!ctx) {
510 get_globals().purgeAll();
511 ctx = typeface->createScalerContext(effects, desc, false);
512 SkASSERT(ctx);
513 }
514 cache = new SkGlyphCache(desc, std::move(ctx));
515 }
516
517 AutoValidate av(cache);
518
519 if (!proc(cache, context)) { // need to reattach
520 globals.attachCacheToHead(cache);
521 cache = nullptr;
522 }
523 return cache;
524 }
525
AttachCache(SkGlyphCache * cache)526 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
527 SkASSERT(cache);
528 SkASSERT(cache->fNext == nullptr);
529
530 get_globals().attachCacheToHead(cache);
531 }
532
dump_visitor(const SkGlyphCache & cache,void * context)533 static void dump_visitor(const SkGlyphCache& cache, void* context) {
534 int* counter = (int*)context;
535 int index = *counter;
536 *counter += 1;
537
538 const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
539
540 SkDebugf("[%3d] ID %3d, glyphs %3d, size %g, scale %g, skew %g, [%g %g %g %g]\n",
541 index, rec.fFontID, cache.countCachedGlyphs(),
542 rec.fTextSize, rec.fPreScaleX, rec.fPreSkewX,
543 rec.fPost2x2[0][0], rec.fPost2x2[0][1], rec.fPost2x2[1][0], rec.fPost2x2[1][1]);
544 }
545
Dump()546 void SkGlyphCache::Dump() {
547 SkDebugf("GlyphCache [ used budget ]\n");
548 SkDebugf(" bytes [ %8zu %8zu ]\n",
549 SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
550 SkDebugf(" count [ %8zu %8zu ]\n",
551 SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());
552
553 int counter = 0;
554 SkGlyphCache::VisitAll(dump_visitor, &counter);
555 }
556
sk_trace_dump_visitor(const SkGlyphCache & cache,void * context)557 static void sk_trace_dump_visitor(const SkGlyphCache& cache, void* context) {
558 SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context);
559
560 const SkTypeface* face = cache.getScalerContext()->getTypeface();
561 const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
562
563 SkString fontName;
564 face->getFamilyName(&fontName);
565 // Replace all special characters with '_'.
566 for (size_t index = 0; index < fontName.size(); ++index) {
567 if (!std::isalnum(fontName[index])) {
568 fontName[index] = '_';
569 }
570 }
571
572 SkString dumpName = SkStringPrintf("%s/%s_%d/%p",
573 gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache);
574
575 dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", cache.getMemoryUsed());
576 dump->dumpNumericValue(dumpName.c_str(), "glyph_count", "objects", cache.countCachedGlyphs());
577 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
578 }
579
DumpMemoryStatistics(SkTraceMemoryDump * dump)580 void SkGlyphCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
581 dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
582 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
583 SkGraphics::GetFontCacheLimit());
584 dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
585 SkGraphics::GetFontCacheCountUsed());
586 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
587 SkGraphics::GetFontCacheCountLimit());
588
589 if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
590 dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
591 return;
592 }
593
594 SkGlyphCache::VisitAll(sk_trace_dump_visitor, dump);
595 }
596
VisitAll(Visitor visitor,void * context)597 void SkGlyphCache::VisitAll(Visitor visitor, void* context) {
598 SkGlyphCache_Globals& globals = get_globals();
599 SkAutoExclusive ac(globals.fLock);
600 SkGlyphCache* cache;
601
602 globals.validate();
603
604 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
605 visitor(*cache, context);
606 }
607 }
608
609 ///////////////////////////////////////////////////////////////////////////////
610
attachCacheToHead(SkGlyphCache * cache)611 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
612 SkAutoExclusive ac(fLock);
613
614 this->validate();
615 cache->validate();
616
617 this->internalAttachCacheToHead(cache);
618 this->internalPurge();
619 }
620
internalGetTail() const621 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
622 SkGlyphCache* cache = fHead;
623 if (cache) {
624 while (cache->fNext) {
625 cache = cache->fNext;
626 }
627 }
628 return cache;
629 }
630
internalPurge(size_t minBytesNeeded)631 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
632 this->validate();
633
634 size_t bytesNeeded = 0;
635 if (fTotalMemoryUsed > fCacheSizeLimit) {
636 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
637 }
638 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
639 if (bytesNeeded) {
640 // no small purges!
641 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
642 }
643
644 int countNeeded = 0;
645 if (fCacheCount > fCacheCountLimit) {
646 countNeeded = fCacheCount - fCacheCountLimit;
647 // no small purges!
648 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
649 }
650
651 // early exit
652 if (!countNeeded && !bytesNeeded) {
653 return 0;
654 }
655
656 size_t bytesFreed = 0;
657 int countFreed = 0;
658
659 // we start at the tail and proceed backwards, as the linklist is in LRU
660 // order, with unimportant entries at the tail.
661 SkGlyphCache* cache = this->internalGetTail();
662 while (cache != nullptr &&
663 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
664 SkGlyphCache* prev = cache->fPrev;
665 bytesFreed += cache->fMemoryUsed;
666 countFreed += 1;
667
668 this->internalDetachCache(cache);
669 delete cache;
670 cache = prev;
671 }
672
673 this->validate();
674
675 #ifdef SPEW_PURGE_STATUS
676 if (countFreed) {
677 SkDebugf("purging %dK from font cache [%d entries]\n",
678 (int)(bytesFreed >> 10), countFreed);
679 }
680 #endif
681
682 return bytesFreed;
683 }
684
internalAttachCacheToHead(SkGlyphCache * cache)685 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
686 SkASSERT(nullptr == cache->fPrev && nullptr == cache->fNext);
687 if (fHead) {
688 fHead->fPrev = cache;
689 cache->fNext = fHead;
690 }
691 fHead = cache;
692
693 fCacheCount += 1;
694 fTotalMemoryUsed += cache->fMemoryUsed;
695 }
696
internalDetachCache(SkGlyphCache * cache)697 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
698 SkASSERT(fCacheCount > 0);
699 fCacheCount -= 1;
700 fTotalMemoryUsed -= cache->fMemoryUsed;
701
702 if (cache->fPrev) {
703 cache->fPrev->fNext = cache->fNext;
704 } else {
705 fHead = cache->fNext;
706 }
707 if (cache->fNext) {
708 cache->fNext->fPrev = cache->fPrev;
709 }
710 cache->fPrev = cache->fNext = nullptr;
711 }
712
713 ///////////////////////////////////////////////////////////////////////////////
714
715 #ifdef SK_DEBUG
716
validate() const717 void SkGlyphCache::validate() const {
718 #ifdef SK_DEBUG_GLYPH_CACHE
719 int count = fGlyphArray.count();
720 for (int i = 0; i < count; i++) {
721 const SkGlyph* glyph = &fGlyphArray[i];
722 SkASSERT(glyph);
723 if (glyph->fImage) {
724 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
725 }
726 }
727 #endif
728 }
729
validate() const730 void SkGlyphCache_Globals::validate() const {
731 size_t computedBytes = 0;
732 int computedCount = 0;
733
734 const SkGlyphCache* head = fHead;
735 while (head != nullptr) {
736 computedBytes += head->fMemoryUsed;
737 computedCount += 1;
738 head = head->fNext;
739 }
740
741 SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d", fCacheCount,
742 computedCount);
743 SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computedBytes: %d",
744 fTotalMemoryUsed, computedBytes);
745 }
746
747 #endif
748
749 ///////////////////////////////////////////////////////////////////////////////
750 ///////////////////////////////////////////////////////////////////////////////
751
752 #include "SkTypefaceCache.h"
753
GetFontCacheLimit()754 size_t SkGraphics::GetFontCacheLimit() {
755 return get_globals().getCacheSizeLimit();
756 }
757
SetFontCacheLimit(size_t bytes)758 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
759 return get_globals().setCacheSizeLimit(bytes);
760 }
761
GetFontCacheUsed()762 size_t SkGraphics::GetFontCacheUsed() {
763 return get_globals().getTotalMemoryUsed();
764 }
765
GetFontCacheCountLimit()766 int SkGraphics::GetFontCacheCountLimit() {
767 return get_globals().getCacheCountLimit();
768 }
769
SetFontCacheCountLimit(int count)770 int SkGraphics::SetFontCacheCountLimit(int count) {
771 return get_globals().setCacheCountLimit(count);
772 }
773
GetFontCacheCountUsed()774 int SkGraphics::GetFontCacheCountUsed() {
775 return get_globals().getCacheCountUsed();
776 }
777
PurgeFontCache()778 void SkGraphics::PurgeFontCache() {
779 get_globals().purgeAll();
780 SkTypefaceCache::PurgeAll();
781 }
782
783 // TODO(herb): clean up TLS apis.
GetTLSFontCacheLimit()784 size_t SkGraphics::GetTLSFontCacheLimit() { return 0; }
SetTLSFontCacheLimit(size_t bytes)785 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { }
786