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 "src/core/SkStrikeCache.h"
9
10 #include <cctype>
11
12 #include "include/core/SkGraphics.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkTraceMemoryDump.h"
15 #include "include/core/SkTypeface.h"
16 #include "include/private/SkMutex.h"
17 #include "include/private/SkTemplates.h"
18 #include "src/core/SkGlyphRunPainter.h"
19 #include "src/core/SkScalerCache.h"
20
21 bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental = false;
22
GlobalStrikeCache()23 SkStrikeCache* SkStrikeCache::GlobalStrikeCache() {
24 #if !defined(SK_BUILD_FOR_IOS)
25 if (gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental) {
26 static thread_local auto* cache = new SkStrikeCache;
27 return cache;
28 }
29 #endif
30 static auto* cache = new SkStrikeCache;
31 return cache;
32 }
33
ExclusiveStrikePtr(sk_sp<Strike> strike)34 SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr(sk_sp<Strike> strike)
35 : fStrike{std::move(strike)} {}
36
ExclusiveStrikePtr()37 SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr()
38 : fStrike{nullptr} {}
39
ExclusiveStrikePtr(ExclusiveStrikePtr && o)40 SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr(ExclusiveStrikePtr&& o)
41 : fStrike{std::move(o.fStrike)} {
42 o.fStrike = nullptr;
43 }
44
45 SkStrikeCache::ExclusiveStrikePtr&
operator =(ExclusiveStrikePtr && that)46 SkStrikeCache::ExclusiveStrikePtr::operator = (ExclusiveStrikePtr&& that) {
47 fStrike = std::move(that.fStrike);
48 return *this;
49 }
50
get() const51 SkStrike* SkStrikeCache::ExclusiveStrikePtr::get() const {
52 return fStrike.get();
53 }
54
operator ->() const55 SkStrike* SkStrikeCache::ExclusiveStrikePtr::operator -> () const {
56 return this->get();
57 }
58
operator *() const59 SkStrike& SkStrikeCache::ExclusiveStrikePtr::operator * () const {
60 return *this->get();
61 }
62
operator bool() const63 SkStrikeCache::ExclusiveStrikePtr::operator bool () const {
64 return fStrike != nullptr;
65 }
66
operator ==(const SkStrikeCache::ExclusiveStrikePtr & lhs,const SkStrikeCache::ExclusiveStrikePtr & rhs)67 bool operator == (const SkStrikeCache::ExclusiveStrikePtr& lhs,
68 const SkStrikeCache::ExclusiveStrikePtr& rhs) {
69 return lhs.fStrike == rhs.fStrike;
70 }
71
operator ==(const SkStrikeCache::ExclusiveStrikePtr & lhs,decltype(nullptr) )72 bool operator == (const SkStrikeCache::ExclusiveStrikePtr& lhs, decltype(nullptr)) {
73 return lhs.fStrike == nullptr;
74 }
75
operator ==(decltype(nullptr) ,const SkStrikeCache::ExclusiveStrikePtr & rhs)76 bool operator == (decltype(nullptr), const SkStrikeCache::ExclusiveStrikePtr& rhs) {
77 return nullptr == rhs.fStrike;
78 }
79
~SkStrikeCache()80 SkStrikeCache::~SkStrikeCache() {
81 Strike* strike = fHead;
82 while (strike) {
83 Strike* next = strike->fNext;
84 strike->unref();
85 strike = next;
86 }
87 }
88
findOrCreateStrikeExclusive(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)89 SkExclusiveStrikePtr SkStrikeCache::findOrCreateStrikeExclusive(
90 const SkDescriptor& desc, const SkScalerContextEffects& effects, const SkTypeface& typeface)
91 {
92 return SkExclusiveStrikePtr(this->findOrCreateStrike(desc, effects, typeface));
93 }
94
findOrCreateStrike(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)95 auto SkStrikeCache::findOrCreateStrike(const SkDescriptor& desc,
96 const SkScalerContextEffects& effects,
97 const SkTypeface& typeface) -> sk_sp<Strike> {
98 SkAutoSpinlock ac(fLock);
99 sk_sp<Strike> strike = this->internalFindStrikeOrNull(desc);
100 if (strike == nullptr) {
101 auto scaler = typeface.createScalerContext(effects, &desc);
102 strike = this->internalCreateStrike(desc, std::move(scaler));
103 }
104 this->internalPurge();
105 return strike;
106 }
107
findOrCreateScopedStrike(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)108 SkScopedStrikeForGPU SkStrikeCache::findOrCreateScopedStrike(const SkDescriptor& desc,
109 const SkScalerContextEffects& effects,
110 const SkTypeface& typeface) {
111 return SkScopedStrikeForGPU{this->findOrCreateStrike(desc, effects, typeface).release()};
112 }
113
PurgeAll()114 void SkStrikeCache::PurgeAll() {
115 GlobalStrikeCache()->purgeAll();
116 }
117
Dump()118 void SkStrikeCache::Dump() {
119 SkDebugf("GlyphCache [ used budget ]\n");
120 SkDebugf(" bytes [ %8zu %8zu ]\n",
121 SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
122 SkDebugf(" count [ %8zu %8zu ]\n",
123 SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());
124
125 int counter = 0;
126
127 auto visitor = [&counter](const Strike& strike) {
128 const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();
129
130 SkDebugf("index %d\n", counter);
131 SkDebugf("%s", rec.dump().c_str());
132 counter += 1;
133 };
134
135 GlobalStrikeCache()->forEachStrike(visitor);
136 }
137
138 namespace {
139 const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
140 } // namespace
141
DumpMemoryStatistics(SkTraceMemoryDump * dump)142 void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
143 dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
144 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
145 SkGraphics::GetFontCacheLimit());
146 dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
147 SkGraphics::GetFontCacheCountUsed());
148 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
149 SkGraphics::GetFontCacheCountLimit());
150
151 if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
152 dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
153 return;
154 }
155
156 auto visitor = [&dump](const Strike& strike) {
157 const SkTypeface* face = strike.fScalerCache.getScalerContext()->getTypeface();
158 const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();
159
160 SkString fontName;
161 face->getFamilyName(&fontName);
162 // Replace all special characters with '_'.
163 for (size_t index = 0; index < fontName.size(); ++index) {
164 if (!std::isalnum(fontName[index])) {
165 fontName[index] = '_';
166 }
167 }
168
169 SkString dumpName = SkStringPrintf(
170 "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &strike);
171
172 dump->dumpNumericValue(dumpName.c_str(),
173 "size", "bytes", strike.fMemoryUsed);
174 dump->dumpNumericValue(dumpName.c_str(),
175 "glyph_count", "objects",
176 strike.fScalerCache.countCachedGlyphs());
177 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
178 };
179
180 GlobalStrikeCache()->forEachStrike(visitor);
181 }
182
findStrikeExclusive(const SkDescriptor & desc)183 SkExclusiveStrikePtr SkStrikeCache::findStrikeExclusive(const SkDescriptor& desc) {
184 SkAutoSpinlock ac(fLock);
185 sk_sp<SkStrike> result = this->internalFindStrikeOrNull(desc);
186 this->internalPurge();
187 return SkExclusiveStrikePtr(result);
188 }
189
internalFindStrikeOrNull(const SkDescriptor & desc)190 auto SkStrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) -> sk_sp<Strike> {
191 for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
192 if (strike->fScalerCache.getDescriptor() == desc) {
193 if (fHead != strike) {
194 // Make most recently used
195 strike->fPrev->fNext = strike->fNext;
196 if (strike->fNext != nullptr) {
197 strike->fNext->fPrev = strike->fPrev;
198 } else {
199 fTail = strike->fPrev;
200 }
201 fHead->fPrev = strike;
202 strike->fNext = fHead;
203 strike->fPrev = nullptr;
204 fHead = strike;
205 }
206
207 return sk_ref_sp(strike);
208 }
209 }
210
211 return nullptr;
212 }
213
createStrikeExclusive(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,SkFontMetrics * maybeMetrics,std::unique_ptr<SkStrikePinner> pinner)214 SkExclusiveStrikePtr SkStrikeCache::createStrikeExclusive(
215 const SkDescriptor& desc,
216 std::unique_ptr<SkScalerContext> scaler,
217 SkFontMetrics* maybeMetrics,
218 std::unique_ptr<SkStrikePinner> pinner)
219 {
220 SkAutoSpinlock ac(fLock);
221 return SkExclusiveStrikePtr(
222 this->internalCreateStrike(desc, std::move(scaler), maybeMetrics, std::move(pinner)));
223 }
224
internalCreateStrike(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,SkFontMetrics * maybeMetrics,std::unique_ptr<SkStrikePinner> pinner)225 auto SkStrikeCache::internalCreateStrike(
226 const SkDescriptor& desc,
227 std::unique_ptr<SkScalerContext> scaler,
228 SkFontMetrics* maybeMetrics,
229 std::unique_ptr<SkStrikePinner> pinner) -> sk_sp<Strike> {
230 auto strike =
231 sk_make_sp<Strike>(this, desc, std::move(scaler), maybeMetrics, std::move(pinner));
232 this->internalAttachToHead(strike);
233 return strike;
234 }
235
purgeAll()236 void SkStrikeCache::purgeAll() {
237 SkAutoSpinlock ac(fLock);
238 this->internalPurge(fTotalMemoryUsed);
239 }
240
getTotalMemoryUsed() const241 size_t SkStrikeCache::getTotalMemoryUsed() const {
242 SkAutoSpinlock ac(fLock);
243 return fTotalMemoryUsed;
244 }
245
getCacheCountUsed() const246 int SkStrikeCache::getCacheCountUsed() const {
247 SkAutoSpinlock ac(fLock);
248 return fCacheCount;
249 }
250
getCacheCountLimit() const251 int SkStrikeCache::getCacheCountLimit() const {
252 SkAutoSpinlock ac(fLock);
253 return fCacheCountLimit;
254 }
255
setCacheSizeLimit(size_t newLimit)256 size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) {
257 SkAutoSpinlock ac(fLock);
258
259 size_t prevLimit = fCacheSizeLimit;
260 fCacheSizeLimit = newLimit;
261 this->internalPurge();
262 return prevLimit;
263 }
264
getCacheSizeLimit() const265 size_t SkStrikeCache::getCacheSizeLimit() const {
266 SkAutoSpinlock ac(fLock);
267 return fCacheSizeLimit;
268 }
269
setCacheCountLimit(int newCount)270 int SkStrikeCache::setCacheCountLimit(int newCount) {
271 if (newCount < 0) {
272 newCount = 0;
273 }
274
275 SkAutoSpinlock ac(fLock);
276
277 int prevCount = fCacheCountLimit;
278 fCacheCountLimit = newCount;
279 this->internalPurge();
280 return prevCount;
281 }
282
getCachePointSizeLimit() const283 int SkStrikeCache::getCachePointSizeLimit() const {
284 SkAutoSpinlock ac(fLock);
285 return fPointSizeLimit;
286 }
287
setCachePointSizeLimit(int newLimit)288 int SkStrikeCache::setCachePointSizeLimit(int newLimit) {
289 if (newLimit < 0) {
290 newLimit = 0;
291 }
292
293 SkAutoSpinlock ac(fLock);
294
295 int prevLimit = fPointSizeLimit;
296 fPointSizeLimit = newLimit;
297 return prevLimit;
298 }
299
forEachStrike(std::function<void (const Strike &)> visitor) const300 void SkStrikeCache::forEachStrike(std::function<void(const Strike&)> visitor) const {
301 SkAutoSpinlock ac(fLock);
302
303 this->validate();
304
305 for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
306 visitor(*strike);
307 }
308 }
309
internalPurge(size_t minBytesNeeded)310 size_t SkStrikeCache::internalPurge(size_t minBytesNeeded) {
311 this->validate();
312
313 size_t bytesNeeded = 0;
314 if (fTotalMemoryUsed > fCacheSizeLimit) {
315 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
316 }
317 bytesNeeded = std::max(bytesNeeded, minBytesNeeded);
318 if (bytesNeeded) {
319 // no small purges!
320 bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2);
321 }
322
323 int countNeeded = 0;
324 if (fCacheCount > fCacheCountLimit) {
325 countNeeded = fCacheCount - fCacheCountLimit;
326 // no small purges!
327 countNeeded = std::max(countNeeded, fCacheCount >> 2);
328 }
329
330 // early exit
331 if (!countNeeded && !bytesNeeded) {
332 return 0;
333 }
334
335 size_t bytesFreed = 0;
336 int countFreed = 0;
337
338 // Start at the tail and proceed backwards deleting; the list is in LRU
339 // order, with unimportant entries at the tail.
340 Strike* strike = fTail;
341 while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
342 Strike* prev = strike->fPrev;
343
344 // Only delete if the strike is not pinned.
345 if (strike->fPinner == nullptr || strike->fPinner->canDelete()) {
346 bytesFreed += strike->fMemoryUsed;
347 countFreed += 1;
348 this->internalRemoveStrike(strike);
349 }
350 strike = prev;
351 }
352
353 this->validate();
354
355 #ifdef SPEW_PURGE_STATUS
356 if (countFreed) {
357 SkDebugf("purging %dK from font cache [%d entries]\n",
358 (int)(bytesFreed >> 10), countFreed);
359 }
360 #endif
361
362 return bytesFreed;
363 }
364
internalAttachToHead(sk_sp<Strike> strike)365 void SkStrikeCache::internalAttachToHead(sk_sp<Strike> strike) {
366 SkASSERT(nullptr == strike->fPrev && nullptr == strike->fNext);
367
368 fCacheCount += 1;
369 fTotalMemoryUsed += strike->fMemoryUsed;
370
371 if (fHead) {
372 fHead->fPrev = strike.get();
373 strike->fNext = fHead;
374 }
375
376 if (fTail == nullptr) {
377 fTail = strike.get();
378 }
379
380 fHead = strike.release(); // Transfer ownership of strike to the cache list.
381 }
382
internalRemoveStrike(Strike * strike)383 void SkStrikeCache::internalRemoveStrike(Strike* strike) {
384 SkASSERT(fCacheCount > 0);
385 fCacheCount -= 1;
386 fTotalMemoryUsed -= strike->fMemoryUsed;
387
388 if (strike->fPrev) {
389 strike->fPrev->fNext = strike->fNext;
390 } else {
391 fHead = strike->fNext;
392 }
393 if (strike->fNext) {
394 strike->fNext->fPrev = strike->fPrev;
395 } else {
396 fTail = strike->fPrev;
397 }
398 strike->fPrev = strike->fNext = nullptr;
399 strike->fRemoved = true;
400 strike->unref();
401 }
402
validate() const403 void SkStrikeCache::validate() const {
404 #ifdef SK_DEBUG
405 size_t computedBytes = 0;
406 int computedCount = 0;
407
408 const Strike* strike = fHead;
409 while (strike != nullptr) {
410 computedBytes += strike->fMemoryUsed;
411 computedCount += 1;
412 strike = strike->fNext;
413 }
414
415 // Can't use SkASSERTF because it looses thread annotations.
416 if (fCacheCount != computedCount) {
417 SkDebugf("fCacheCount: %d, computedCount: %d", fCacheCount, computedCount);
418 SK_ABORT("fCacheCount != computedCount");
419 }
420 if (fTotalMemoryUsed != computedBytes) {
421 SkDebugf("fTotalMemoryUsed: %d, computedBytes: %d", fTotalMemoryUsed, computedBytes);
422 SK_ABORT("fTotalMemoryUsed == computedBytes");
423 }
424 #endif
425 }
426
updateDelta(size_t increase)427 void SkStrikeCache::Strike::updateDelta(size_t increase) {
428 if (increase != 0) {
429 SkAutoSpinlock lock{fStrikeCache->fLock};
430 fMemoryUsed += increase;
431 if (!fRemoved) {
432 fStrikeCache->fTotalMemoryUsed += increase;
433 }
434 }
435 }
436