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