1 /*
2 * Copyright 2018 Google Inc.
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 "SkStrikeCache.h"
9
10 #include <cctype>
11
12 #include "SkGlyphRunPainter.h"
13 #include "SkGraphics.h"
14 #include "SkMutex.h"
15 #include "SkStrike.h"
16 #include "SkTemplates.h"
17 #include "SkTraceMemoryDump.h"
18 #include "SkTypeface.h"
19
20 class SkStrikeCache::Node final : public SkStrikeInterface {
21 public:
Node(SkStrikeCache * strikeCache,const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,const SkFontMetrics & metrics,std::unique_ptr<SkStrikePinner> pinner)22 Node(SkStrikeCache* strikeCache,
23 const SkDescriptor& desc,
24 std::unique_ptr<SkScalerContext> scaler,
25 const SkFontMetrics& metrics,
26 std::unique_ptr<SkStrikePinner> pinner)
27 : fStrikeCache{strikeCache}
28 , fCache{desc, std::move(scaler), metrics}
29 , fPinner{std::move(pinner)} {}
30
rounding() const31 SkVector rounding() const override {
32 return fCache.rounding();
33 }
34
getGlyphMetrics(SkGlyphID glyphID,SkPoint position)35 const SkGlyph& getGlyphMetrics(SkGlyphID glyphID, SkPoint position) override {
36 return fCache.getGlyphMetrics(glyphID, position);
37 }
38
hasImage(const SkGlyph & glyph)39 bool hasImage(const SkGlyph& glyph) override {
40 return fCache.hasImage(glyph);
41 }
42
hasPath(const SkGlyph & glyph)43 bool hasPath(const SkGlyph& glyph) override {
44 return fCache.hasPath(glyph);
45 }
46
47 SkStrikeCache* const fStrikeCache;
48 Node* fNext{nullptr};
49 Node* fPrev{nullptr};
50 SkStrike fCache;
51 std::unique_ptr<SkStrikePinner> fPinner;
52 };
53
GlobalStrikeCache()54 SkStrikeCache* SkStrikeCache::GlobalStrikeCache() {
55 static auto* cache = new SkStrikeCache;
56 return cache;
57 }
58
ExclusiveStrikePtr(SkStrikeCache::Node * node)59 SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr(SkStrikeCache::Node* node)
60 : fNode{node} {}
61
ExclusiveStrikePtr()62 SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr()
63 : fNode{nullptr} {}
64
ExclusiveStrikePtr(ExclusiveStrikePtr && o)65 SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr(ExclusiveStrikePtr&& o)
66 : fNode{o.fNode} {
67 o.fNode = nullptr;
68 }
69
70 SkStrikeCache::ExclusiveStrikePtr&
operator =(ExclusiveStrikePtr && o)71 SkStrikeCache::ExclusiveStrikePtr::operator = (ExclusiveStrikePtr&& o) {
72 if (fNode != nullptr) {
73 fNode->fStrikeCache->attachNode(fNode);
74 }
75 fNode = o.fNode;
76 o.fNode = nullptr;
77 return *this;
78 }
79
~ExclusiveStrikePtr()80 SkStrikeCache::ExclusiveStrikePtr::~ExclusiveStrikePtr() {
81 if (fNode != nullptr) {
82 fNode->fStrikeCache->attachNode(fNode);
83 }
84 }
85
get() const86 SkStrike* SkStrikeCache::ExclusiveStrikePtr::get() const {
87 return &fNode->fCache;
88 }
89
operator ->() const90 SkStrike* SkStrikeCache::ExclusiveStrikePtr::operator -> () const {
91 return this->get();
92 }
93
operator *() const94 SkStrike& SkStrikeCache::ExclusiveStrikePtr::operator * () const {
95 return *this->get();
96 }
97
operator bool() const98 SkStrikeCache::ExclusiveStrikePtr::operator bool () const {
99 return fNode != nullptr;
100 }
101
operator ==(const SkStrikeCache::ExclusiveStrikePtr & lhs,const SkStrikeCache::ExclusiveStrikePtr & rhs)102 bool operator == (const SkStrikeCache::ExclusiveStrikePtr& lhs,
103 const SkStrikeCache::ExclusiveStrikePtr& rhs) {
104 return lhs.fNode == rhs.fNode;
105 }
106
operator ==(const SkStrikeCache::ExclusiveStrikePtr & lhs,decltype(nullptr) )107 bool operator == (const SkStrikeCache::ExclusiveStrikePtr& lhs, decltype(nullptr)) {
108 return lhs.fNode == nullptr;
109 }
110
operator ==(decltype(nullptr) ,const SkStrikeCache::ExclusiveStrikePtr & rhs)111 bool operator == (decltype(nullptr), const SkStrikeCache::ExclusiveStrikePtr& rhs) {
112 return nullptr == rhs.fNode;
113 }
114
~SkStrikeCache()115 SkStrikeCache::~SkStrikeCache() {
116 Node* node = fHead;
117 while (node) {
118 Node* next = node->fNext;
119 delete node;
120 node = next;
121 }
122 }
123
FindStrikeExclusive(const SkDescriptor & desc)124 SkExclusiveStrikePtr SkStrikeCache::FindStrikeExclusive(const SkDescriptor& desc) {
125 return GlobalStrikeCache()->findStrikeExclusive(desc);
126 }
127
CreateScalerContext(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)128 std::unique_ptr<SkScalerContext> SkStrikeCache::CreateScalerContext(
129 const SkDescriptor& desc,
130 const SkScalerContextEffects& effects,
131 const SkTypeface& typeface) {
132 auto scaler = typeface.createScalerContext(effects, &desc, true /* can fail */);
133
134 // Check if we can create a scaler-context before creating the glyphcache.
135 // If not, we may have exhausted OS/font resources, so try purging the
136 // cache once and try again
137 // pass true the first time, to notice if the scalercontext failed,
138 if (scaler == nullptr) {
139 PurgeAll();
140 scaler = typeface.createScalerContext(effects, &desc, false /* must succeed */);
141 }
142 return scaler;
143 }
144
FindOrCreateStrikeExclusive(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)145 SkExclusiveStrikePtr SkStrikeCache::FindOrCreateStrikeExclusive(
146 const SkDescriptor& desc, const SkScalerContextEffects& effects, const SkTypeface& typeface)
147 {
148 return GlobalStrikeCache()->findOrCreateStrikeExclusive(desc, effects, typeface);
149 }
150
findOrCreateStrikeExclusive(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)151 SkExclusiveStrikePtr SkStrikeCache::findOrCreateStrikeExclusive(
152 const SkDescriptor& desc, const SkScalerContextEffects& effects, const SkTypeface& typeface)
153 {
154 return SkExclusiveStrikePtr(this->findOrCreateStrike(desc, effects, typeface));
155 }
156
findOrCreateStrike(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)157 auto SkStrikeCache::findOrCreateStrike(const SkDescriptor& desc,
158 const SkScalerContextEffects& effects,
159 const SkTypeface& typeface) -> Node* {
160 Node* node = this->findAndDetachStrike(desc);
161 if (node == nullptr) {
162 auto scaler = CreateScalerContext(desc, effects, typeface);
163 node = this->createStrike(desc, std::move(scaler));
164 }
165 return node;
166 }
167
FindOrCreateStrikeExclusive(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)168 SkExclusiveStrikePtr SkStrikeCache::FindOrCreateStrikeExclusive(
169 const SkFont& font,
170 const SkPaint& paint,
171 const SkSurfaceProps& surfaceProps,
172 SkScalerContextFlags scalerContextFlags,
173 const SkMatrix& deviceMatrix)
174 {
175 return SkExclusiveStrikePtr(
176 GlobalStrikeCache()->findOrCreateStrike(
177 font, paint, surfaceProps, scalerContextFlags,deviceMatrix));
178 }
179
findOrCreateStrike(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)180 auto SkStrikeCache::findOrCreateStrike(
181 const SkFont& font,
182 const SkPaint& paint,
183 const SkSurfaceProps& surfaceProps,
184 SkScalerContextFlags scalerContextFlags,
185 const SkMatrix& deviceMatrix) -> Node*
186 {
187 SkAutoDescriptor ad;
188 SkScalerContextEffects effects;
189
190 auto desc = SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
191 font, paint, surfaceProps, scalerContextFlags, deviceMatrix, &ad, &effects);
192
193 auto tf = font.getTypefaceOrDefault();
194
195 return this->findOrCreateStrike(*desc, effects, *tf);
196 }
197
FindOrCreateStrikeWithNoDeviceExclusive(const SkFont & font)198 SkExclusiveStrikePtr SkStrikeCache::FindOrCreateStrikeWithNoDeviceExclusive(const SkFont& font) {
199 return FindOrCreateStrikeWithNoDeviceExclusive(font, SkPaint());
200 }
201
FindOrCreateStrikeWithNoDeviceExclusive(const SkFont & font,const SkPaint & paint)202 SkExclusiveStrikePtr SkStrikeCache::FindOrCreateStrikeWithNoDeviceExclusive(const SkFont& font,
203 const SkPaint& paint) {
204 SkAutoDescriptor ad;
205 SkScalerContextEffects effects;
206 auto desc = SkScalerContext::CreateDescriptorAndEffectsUsingPaint(font, paint,
207 SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType),
208 kFakeGammaAndBoostContrast, SkMatrix::I(), &ad, &effects);
209 auto typeface = font.getTypefaceOrDefault();
210 return SkStrikeCache::FindOrCreateStrikeExclusive(*desc, effects, *typeface);
211 }
212
PurgeAll()213 void SkStrikeCache::PurgeAll() {
214 GlobalStrikeCache()->purgeAll();
215 }
216
Dump()217 void SkStrikeCache::Dump() {
218 SkDebugf("GlyphCache [ used budget ]\n");
219 SkDebugf(" bytes [ %8zu %8zu ]\n",
220 SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
221 SkDebugf(" count [ %8zu %8zu ]\n",
222 SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());
223
224 int counter = 0;
225
226 auto visitor = [&counter](const SkStrike& cache) {
227 const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
228
229 SkDebugf("index %d\n", counter);
230 SkDebugf("%s", rec.dump().c_str());
231 counter += 1;
232 };
233
234 GlobalStrikeCache()->forEachStrike(visitor);
235 }
236
237 namespace {
238 const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
239 } // namespace
240
DumpMemoryStatistics(SkTraceMemoryDump * dump)241 void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
242 dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
243 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
244 SkGraphics::GetFontCacheLimit());
245 dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
246 SkGraphics::GetFontCacheCountUsed());
247 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
248 SkGraphics::GetFontCacheCountLimit());
249
250 if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
251 dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
252 return;
253 }
254
255 auto visitor = [&dump](const SkStrike& cache) {
256 const SkTypeface* face = cache.getScalerContext()->getTypeface();
257 const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
258
259 SkString fontName;
260 face->getFamilyName(&fontName);
261 // Replace all special characters with '_'.
262 for (size_t index = 0; index < fontName.size(); ++index) {
263 if (!std::isalnum(fontName[index])) {
264 fontName[index] = '_';
265 }
266 }
267
268 SkString dumpName = SkStringPrintf(
269 "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache);
270
271 dump->dumpNumericValue(dumpName.c_str(),
272 "size", "bytes", cache.getMemoryUsed());
273 dump->dumpNumericValue(dumpName.c_str(),
274 "glyph_count", "objects", cache.countCachedGlyphs());
275 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
276 };
277
278 GlobalStrikeCache()->forEachStrike(visitor);
279 }
280
281
attachNode(Node * node)282 void SkStrikeCache::attachNode(Node* node) {
283 if (node == nullptr) {
284 return;
285 }
286 SkAutoExclusive ac(fLock);
287
288 this->validate();
289 node->fCache.validate();
290
291 this->internalAttachToHead(node);
292 this->internalPurge();
293 }
294
findStrikeExclusive(const SkDescriptor & desc)295 SkExclusiveStrikePtr SkStrikeCache::findStrikeExclusive(const SkDescriptor& desc) {
296 return SkExclusiveStrikePtr(this->findAndDetachStrike(desc));
297 }
298
findAndDetachStrike(const SkDescriptor & desc)299 auto SkStrikeCache::findAndDetachStrike(const SkDescriptor& desc) -> Node* {
300 SkAutoExclusive ac(fLock);
301
302 for (Node* node = internalGetHead(); node != nullptr; node = node->fNext) {
303 if (node->fCache.getDescriptor() == desc) {
304 this->internalDetachCache(node);
305 return node;
306 }
307 }
308
309 return nullptr;
310 }
311
312
loose_compare(const SkDescriptor & lhs,const SkDescriptor & rhs)313 static bool loose_compare(const SkDescriptor& lhs, const SkDescriptor& rhs) {
314 uint32_t size;
315 auto ptr = lhs.findEntry(kRec_SkDescriptorTag, &size);
316 SkScalerContextRec lhsRec;
317 std::memcpy(&lhsRec, ptr, size);
318
319 ptr = rhs.findEntry(kRec_SkDescriptorTag, &size);
320 SkScalerContextRec rhsRec;
321 std::memcpy(&rhsRec, ptr, size);
322
323 // If these don't match, there's no way we can use these strikes interchangeably.
324 // Note that a typeface from each renderer maps to a unique proxy typeface on the GPU,
325 // keyed in the glyph cache using fontID in the SkDescriptor. By limiting this search
326 // to descriptors with the same fontID, we ensure that a renderer never uses glyphs
327 // generated by a different renderer.
328 return
329 lhsRec.fFontID == rhsRec.fFontID &&
330 lhsRec.fTextSize == rhsRec.fTextSize &&
331 lhsRec.fPreScaleX == rhsRec.fPreScaleX &&
332 lhsRec.fPreSkewX == rhsRec.fPreSkewX &&
333 lhsRec.fPost2x2[0][0] == rhsRec.fPost2x2[0][0] &&
334 lhsRec.fPost2x2[0][1] == rhsRec.fPost2x2[0][1] &&
335 lhsRec.fPost2x2[1][0] == rhsRec.fPost2x2[1][0] &&
336 lhsRec.fPost2x2[1][1] == rhsRec.fPost2x2[1][1];
337 }
338
desperationSearchForImage(const SkDescriptor & desc,SkGlyph * glyph,SkStrike * targetCache)339 bool SkStrikeCache::desperationSearchForImage(const SkDescriptor& desc, SkGlyph* glyph,
340 SkStrike* targetCache) {
341 SkAutoExclusive ac(fLock);
342
343 SkGlyphID glyphID = glyph->getGlyphID();
344 SkFixed targetSubX = glyph->getSubXFixed(),
345 targetSubY = glyph->getSubYFixed();
346
347 for (Node* node = internalGetHead(); node != nullptr; node = node->fNext) {
348 if (loose_compare(node->fCache.getDescriptor(), desc)) {
349 auto targetGlyphID = SkPackedGlyphID(glyphID, targetSubX, targetSubY);
350 if (node->fCache.isGlyphCached(glyphID, targetSubX, targetSubY)) {
351 SkGlyph* fallback = node->fCache.getRawGlyphByID(targetGlyphID);
352 // This desperate-match node may disappear as soon as we drop fLock, so we
353 // need to copy the glyph from node into this strike, including a
354 // deep copy of the mask.
355 targetCache->initializeGlyphFromFallback(glyph, *fallback);
356 return true;
357 }
358
359 // Look for any sub-pixel pos for this glyph, in case there is a pos mismatch.
360 if (const auto* fallback = node->fCache.getCachedGlyphAnySubPix(glyphID)) {
361 targetCache->initializeGlyphFromFallback(glyph, *fallback);
362 return true;
363 }
364 }
365 }
366
367 return false;
368 }
369
desperationSearchForPath(const SkDescriptor & desc,SkGlyphID glyphID,SkPath * path)370 bool SkStrikeCache::desperationSearchForPath(
371 const SkDescriptor& desc, SkGlyphID glyphID, SkPath* path) {
372 SkAutoExclusive ac(fLock);
373
374 // The following is wrong there is subpixel positioning with paths...
375 // Paths are only ever at sub-pixel position (0,0), so we can just try that directly rather
376 // than try our packed position first then search all others on failure like for masks.
377 //
378 // This will have to search the sub-pixel positions too.
379 // There is also a problem with accounting for cache size with shared path data.
380 for (Node* node = internalGetHead(); node != nullptr; node = node->fNext) {
381 if (loose_compare(node->fCache.getDescriptor(), desc)) {
382 if (node->fCache.isGlyphCached(glyphID, 0, 0)) {
383 SkGlyph* from = node->fCache.getRawGlyphByID(SkPackedGlyphID(glyphID));
384 if (from->fPathData != nullptr) {
385 // We can just copy the path out by value here, so no need to worry
386 // about the lifetime of this desperate-match node.
387 *path = from->fPathData->fPath;
388 return true;
389 }
390 }
391 }
392 }
393 return false;
394 }
395
CreateStrikeExclusive(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,SkFontMetrics * maybeMetrics,std::unique_ptr<SkStrikePinner> pinner)396 SkExclusiveStrikePtr SkStrikeCache::CreateStrikeExclusive(
397 const SkDescriptor& desc,
398 std::unique_ptr<SkScalerContext> scaler,
399 SkFontMetrics* maybeMetrics,
400 std::unique_ptr<SkStrikePinner> pinner)
401 {
402 return GlobalStrikeCache()->createStrikeExclusive(
403 desc, std::move(scaler), maybeMetrics, std::move(pinner));
404 }
405
createStrikeExclusive(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,SkFontMetrics * maybeMetrics,std::unique_ptr<SkStrikePinner> pinner)406 SkExclusiveStrikePtr SkStrikeCache::createStrikeExclusive(
407 const SkDescriptor& desc,
408 std::unique_ptr<SkScalerContext> scaler,
409 SkFontMetrics* maybeMetrics,
410 std::unique_ptr<SkStrikePinner> pinner)
411 {
412 return SkExclusiveStrikePtr(
413 this->createStrike(desc, std::move(scaler), maybeMetrics, std::move(pinner)));
414 }
415
createStrike(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,SkFontMetrics * maybeMetrics,std::unique_ptr<SkStrikePinner> pinner)416 auto SkStrikeCache::createStrike(
417 const SkDescriptor& desc,
418 std::unique_ptr<SkScalerContext> scaler,
419 SkFontMetrics* maybeMetrics,
420 std::unique_ptr<SkStrikePinner> pinner) -> Node* {
421 SkFontMetrics fontMetrics;
422 if (maybeMetrics != nullptr) {
423 fontMetrics = *maybeMetrics;
424 } else {
425 scaler->getFontMetrics(&fontMetrics);
426 }
427
428 return new Node{this, desc, std::move(scaler), fontMetrics, std::move(pinner)};
429 }
430
purgeAll()431 void SkStrikeCache::purgeAll() {
432 SkAutoExclusive ac(fLock);
433 this->internalPurge(fTotalMemoryUsed);
434 }
435
getTotalMemoryUsed() const436 size_t SkStrikeCache::getTotalMemoryUsed() const {
437 SkAutoExclusive ac(fLock);
438 return fTotalMemoryUsed;
439 }
440
getCacheCountUsed() const441 int SkStrikeCache::getCacheCountUsed() const {
442 SkAutoExclusive ac(fLock);
443 return fCacheCount;
444 }
445
getCacheCountLimit() const446 int SkStrikeCache::getCacheCountLimit() const {
447 SkAutoExclusive ac(fLock);
448 return fCacheCountLimit;
449 }
450
setCacheSizeLimit(size_t newLimit)451 size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) {
452 static const size_t minLimit = 256 * 1024;
453 if (newLimit < minLimit) {
454 newLimit = minLimit;
455 }
456
457 SkAutoExclusive ac(fLock);
458
459 size_t prevLimit = fCacheSizeLimit;
460 fCacheSizeLimit = newLimit;
461 this->internalPurge();
462 return prevLimit;
463 }
464
getCacheSizeLimit() const465 size_t SkStrikeCache::getCacheSizeLimit() const {
466 SkAutoExclusive ac(fLock);
467 return fCacheSizeLimit;
468 }
469
setCacheCountLimit(int newCount)470 int SkStrikeCache::setCacheCountLimit(int newCount) {
471 if (newCount < 0) {
472 newCount = 0;
473 }
474
475 SkAutoExclusive ac(fLock);
476
477 int prevCount = fCacheCountLimit;
478 fCacheCountLimit = newCount;
479 this->internalPurge();
480 return prevCount;
481 }
482
getCachePointSizeLimit() const483 int SkStrikeCache::getCachePointSizeLimit() const {
484 SkAutoExclusive ac(fLock);
485 return fPointSizeLimit;
486 }
487
setCachePointSizeLimit(int newLimit)488 int SkStrikeCache::setCachePointSizeLimit(int newLimit) {
489 if (newLimit < 0) {
490 newLimit = 0;
491 }
492
493 SkAutoExclusive ac(fLock);
494
495 int prevLimit = fPointSizeLimit;
496 fPointSizeLimit = newLimit;
497 return prevLimit;
498 }
499
forEachStrike(std::function<void (const SkStrike &)> visitor) const500 void SkStrikeCache::forEachStrike(std::function<void(const SkStrike&)> visitor) const {
501 SkAutoExclusive ac(fLock);
502
503 this->validate();
504
505 for (Node* node = this->internalGetHead(); node != nullptr; node = node->fNext) {
506 visitor(node->fCache);
507 }
508 }
509
internalPurge(size_t minBytesNeeded)510 size_t SkStrikeCache::internalPurge(size_t minBytesNeeded) {
511 this->validate();
512
513 size_t bytesNeeded = 0;
514 if (fTotalMemoryUsed > fCacheSizeLimit) {
515 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
516 }
517 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
518 if (bytesNeeded) {
519 // no small purges!
520 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
521 }
522
523 int countNeeded = 0;
524 if (fCacheCount > fCacheCountLimit) {
525 countNeeded = fCacheCount - fCacheCountLimit;
526 // no small purges!
527 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
528 }
529
530 // early exit
531 if (!countNeeded && !bytesNeeded) {
532 return 0;
533 }
534
535 size_t bytesFreed = 0;
536 int countFreed = 0;
537
538 // Start at the tail and proceed backwards deleting; the list is in LRU
539 // order, with unimportant entries at the tail.
540 Node* node = this->internalGetTail();
541 while (node != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
542 Node* prev = node->fPrev;
543
544 // Only delete if the strike is not pinned.
545 if (node->fPinner == nullptr || node->fPinner->canDelete()) {
546 bytesFreed += node->fCache.getMemoryUsed();
547 countFreed += 1;
548 this->internalDetachCache(node);
549 delete node;
550 }
551 node = prev;
552 }
553
554 this->validate();
555
556 #ifdef SPEW_PURGE_STATUS
557 if (countFreed) {
558 SkDebugf("purging %dK from font cache [%d entries]\n",
559 (int)(bytesFreed >> 10), countFreed);
560 }
561 #endif
562
563 return bytesFreed;
564 }
565
internalAttachToHead(Node * node)566 void SkStrikeCache::internalAttachToHead(Node* node) {
567 SkASSERT(nullptr == node->fPrev && nullptr == node->fNext);
568 if (fHead) {
569 fHead->fPrev = node;
570 node->fNext = fHead;
571 }
572 fHead = node;
573
574 if (fTail == nullptr) {
575 fTail = node;
576 }
577
578 fCacheCount += 1;
579 fTotalMemoryUsed += node->fCache.getMemoryUsed();
580 }
581
internalDetachCache(Node * node)582 void SkStrikeCache::internalDetachCache(Node* node) {
583 SkASSERT(fCacheCount > 0);
584 fCacheCount -= 1;
585 fTotalMemoryUsed -= node->fCache.getMemoryUsed();
586
587 if (node->fPrev) {
588 node->fPrev->fNext = node->fNext;
589 } else {
590 fHead = node->fNext;
591 }
592 if (node->fNext) {
593 node->fNext->fPrev = node->fPrev;
594 } else {
595 fTail = node->fPrev;
596 }
597 node->fPrev = node->fNext = nullptr;
598 }
599
ValidateGlyphCacheDataSize()600 void SkStrikeCache::ValidateGlyphCacheDataSize() {
601 #ifdef SK_DEBUG
602 GlobalStrikeCache()->validateGlyphCacheDataSize();
603 #endif
604 }
605
606 #ifdef SK_DEBUG
validateGlyphCacheDataSize() const607 void SkStrikeCache::validateGlyphCacheDataSize() const {
608 this->forEachStrike(
609 [](const SkStrike& cache) { cache.forceValidate();
610 });
611 }
612 #endif
613
614 #ifdef SK_DEBUG
validate() const615 void SkStrikeCache::validate() const {
616 size_t computedBytes = 0;
617 int computedCount = 0;
618
619 const Node* node = fHead;
620 while (node != nullptr) {
621 computedBytes += node->fCache.getMemoryUsed();
622 computedCount += 1;
623 node = node->fNext;
624 }
625
626 SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d", fCacheCount,
627 computedCount);
628 SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computedBytes: %d",
629 fTotalMemoryUsed, computedBytes);
630 }
631 #endif
632
633 ////////////////////////////////////////////////////////////////////////////////////////////////////
634