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