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