• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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