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 
findOrCreateStrike(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)34 auto SkStrikeCache::findOrCreateStrike(const SkDescriptor& desc,
35                                        const SkScalerContextEffects& effects,
36                                        const SkTypeface& typeface) -> sk_sp<Strike> {
37     SkAutoMutexExclusive ac(fLock);
38     sk_sp<Strike> strike = this->internalFindStrikeOrNull(desc);
39     if (strike == nullptr) {
40         auto scaler = typeface.createScalerContext(effects, &desc);
41         strike = this->internalCreateStrike(desc, std::move(scaler));
42     }
43     this->internalPurge();
44     return strike;
45 }
46 
findOrCreateScopedStrike(const SkDescriptor & desc,const SkScalerContextEffects & effects,const SkTypeface & typeface)47 SkScopedStrikeForGPU SkStrikeCache::findOrCreateScopedStrike(const SkDescriptor& desc,
48                                                              const SkScalerContextEffects& effects,
49                                                              const SkTypeface& typeface) {
50     return SkScopedStrikeForGPU{this->findOrCreateStrike(desc, effects, typeface).release()};
51 }
52 
PurgeAll()53 void SkStrikeCache::PurgeAll() {
54     GlobalStrikeCache()->purgeAll();
55 }
56 
Dump()57 void SkStrikeCache::Dump() {
58     SkDebugf("GlyphCache [     used    budget ]\n");
59     SkDebugf("    bytes  [ %8zu  %8zu ]\n",
60              SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
61     SkDebugf("    count  [ %8zu  %8zu ]\n",
62              SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());
63 
64     int counter = 0;
65 
66     auto visitor = [&counter](const Strike& strike) {
67         const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();
68 
69         SkDebugf("index %d\n", counter);
70         SkDebugf("%s", rec.dump().c_str());
71         counter += 1;
72     };
73 
74     GlobalStrikeCache()->forEachStrike(visitor);
75 }
76 
77 namespace {
78     const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
79 }  // namespace
80 
DumpMemoryStatistics(SkTraceMemoryDump * dump)81 void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
82     dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
83     dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
84                            SkGraphics::GetFontCacheLimit());
85     dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
86                            SkGraphics::GetFontCacheCountUsed());
87     dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
88                            SkGraphics::GetFontCacheCountLimit());
89 
90     if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
91         dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
92         return;
93     }
94 
95     auto visitor = [&dump](const Strike& strike) {
96         const SkTypeface* face = strike.fScalerCache.getScalerContext()->getTypeface();
97         const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();
98 
99         SkString fontName;
100         face->getFamilyName(&fontName);
101         // Replace all special characters with '_'.
102         for (size_t index = 0; index < fontName.size(); ++index) {
103             if (!std::isalnum(fontName[index])) {
104                 fontName[index] = '_';
105             }
106         }
107 
108         SkString dumpName = SkStringPrintf(
109                 "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &strike);
110 
111         dump->dumpNumericValue(dumpName.c_str(),
112                                "size", "bytes", strike.fMemoryUsed);
113         dump->dumpNumericValue(dumpName.c_str(),
114                                "glyph_count", "objects",
115                                strike.fScalerCache.countCachedGlyphs());
116         dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
117     };
118 
119     GlobalStrikeCache()->forEachStrike(visitor);
120 }
121 
findStrike(const SkDescriptor & desc)122 sk_sp<SkStrike> SkStrikeCache::findStrike(const SkDescriptor& desc) {
123     SkAutoMutexExclusive ac(fLock);
124     sk_sp<SkStrike> result = this->internalFindStrikeOrNull(desc);
125     this->internalPurge();
126     return result;
127 }
128 
internalFindStrikeOrNull(const SkDescriptor & desc)129 auto SkStrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) -> sk_sp<Strike> {
130 
131     // Check head because it is likely the strike we are looking for.
132     if (fHead != nullptr && fHead->getDescriptor() == desc) { return sk_ref_sp(fHead); }
133 
134     // Do the heavy search looking for the strike.
135     sk_sp<Strike>* strikeHandle = fStrikeLookup.find(desc);
136     if (strikeHandle == nullptr) { return nullptr; }
137     Strike* strikePtr = strikeHandle->get();
138     SkASSERT(strikePtr != nullptr);
139     if (fHead != strikePtr) {
140         // Make most recently used
141         strikePtr->fPrev->fNext = strikePtr->fNext;
142         if (strikePtr->fNext != nullptr) {
143             strikePtr->fNext->fPrev = strikePtr->fPrev;
144         } else {
145             fTail = strikePtr->fPrev;
146         }
147         fHead->fPrev = strikePtr;
148         strikePtr->fNext = fHead;
149         strikePtr->fPrev = nullptr;
150         fHead = strikePtr;
151     }
152     return sk_ref_sp(strikePtr);
153 }
154 
createStrike(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,SkFontMetrics * maybeMetrics,std::unique_ptr<SkStrikePinner> pinner)155 sk_sp<SkStrike> SkStrikeCache::createStrike(
156         const SkDescriptor& desc,
157         std::unique_ptr<SkScalerContext> scaler,
158         SkFontMetrics* maybeMetrics,
159         std::unique_ptr<SkStrikePinner> pinner) {
160     SkAutoMutexExclusive ac(fLock);
161     return this->internalCreateStrike(desc, std::move(scaler), maybeMetrics, std::move(pinner));
162 }
163 
internalCreateStrike(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,SkFontMetrics * maybeMetrics,std::unique_ptr<SkStrikePinner> pinner)164 auto SkStrikeCache::internalCreateStrike(
165         const SkDescriptor& desc,
166         std::unique_ptr<SkScalerContext> scaler,
167         SkFontMetrics* maybeMetrics,
168         std::unique_ptr<SkStrikePinner> pinner) -> sk_sp<Strike> {
169     auto strike =
170             sk_make_sp<Strike>(this, desc, std::move(scaler), maybeMetrics, std::move(pinner));
171     this->internalAttachToHead(strike);
172     return strike;
173 }
174 
purgeAll()175 void SkStrikeCache::purgeAll() {
176     SkAutoMutexExclusive ac(fLock);
177     this->internalPurge(fTotalMemoryUsed);
178 }
179 
getTotalMemoryUsed() const180 size_t SkStrikeCache::getTotalMemoryUsed() const {
181     SkAutoMutexExclusive ac(fLock);
182     return fTotalMemoryUsed;
183 }
184 
getCacheCountUsed() const185 int SkStrikeCache::getCacheCountUsed() const {
186     SkAutoMutexExclusive ac(fLock);
187     return fCacheCount;
188 }
189 
getCacheCountLimit() const190 int SkStrikeCache::getCacheCountLimit() const {
191     SkAutoMutexExclusive ac(fLock);
192     return fCacheCountLimit;
193 }
194 
setCacheSizeLimit(size_t newLimit)195 size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) {
196     SkAutoMutexExclusive ac(fLock);
197 
198     size_t prevLimit = fCacheSizeLimit;
199     fCacheSizeLimit = newLimit;
200     this->internalPurge();
201     return prevLimit;
202 }
203 
getCacheSizeLimit() const204 size_t  SkStrikeCache::getCacheSizeLimit() const {
205     SkAutoMutexExclusive ac(fLock);
206     return fCacheSizeLimit;
207 }
208 
setCacheCountLimit(int newCount)209 int SkStrikeCache::setCacheCountLimit(int newCount) {
210     if (newCount < 0) {
211         newCount = 0;
212     }
213 
214     SkAutoMutexExclusive ac(fLock);
215 
216     int prevCount = fCacheCountLimit;
217     fCacheCountLimit = newCount;
218     this->internalPurge();
219     return prevCount;
220 }
221 
forEachStrike(std::function<void (const Strike &)> visitor) const222 void SkStrikeCache::forEachStrike(std::function<void(const Strike&)> visitor) const {
223     SkAutoMutexExclusive ac(fLock);
224 
225     this->validate();
226 
227     for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
228         visitor(*strike);
229     }
230 }
231 
internalPurge(size_t minBytesNeeded)232 size_t SkStrikeCache::internalPurge(size_t minBytesNeeded) {
233     size_t bytesNeeded = 0;
234     if (fTotalMemoryUsed > fCacheSizeLimit) {
235         bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
236     }
237     bytesNeeded = std::max(bytesNeeded, minBytesNeeded);
238     if (bytesNeeded) {
239         // no small purges!
240         bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2);
241     }
242 
243     int countNeeded = 0;
244     if (fCacheCount > fCacheCountLimit) {
245         countNeeded = fCacheCount - fCacheCountLimit;
246         // no small purges!
247         countNeeded = std::max(countNeeded, fCacheCount >> 2);
248     }
249 
250     // early exit
251     if (!countNeeded && !bytesNeeded) {
252         return 0;
253     }
254 
255     size_t  bytesFreed = 0;
256     int     countFreed = 0;
257 
258     // Start at the tail and proceed backwards deleting; the list is in LRU
259     // order, with unimportant entries at the tail.
260     Strike* strike = fTail;
261     while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
262         Strike* prev = strike->fPrev;
263 
264         // Only delete if the strike is not pinned.
265         if (strike->fPinner == nullptr || strike->fPinner->canDelete()) {
266             bytesFreed += strike->fMemoryUsed;
267             countFreed += 1;
268             this->internalRemoveStrike(strike);
269         }
270         strike = prev;
271     }
272 
273     this->validate();
274 
275 #ifdef SPEW_PURGE_STATUS
276     if (countFreed) {
277         SkDebugf("purging %dK from font cache [%d entries]\n",
278                  (int)(bytesFreed >> 10), countFreed);
279     }
280 #endif
281 
282     return bytesFreed;
283 }
284 
internalAttachToHead(sk_sp<Strike> strike)285 void SkStrikeCache::internalAttachToHead(sk_sp<Strike> strike) {
286     SkASSERT(fStrikeLookup.find(strike->getDescriptor()) == nullptr);
287     Strike* strikePtr = strike.get();
288     fStrikeLookup.set(std::move(strike));
289     SkASSERT(nullptr == strikePtr->fPrev && nullptr == strikePtr->fNext);
290 
291     fCacheCount += 1;
292     fTotalMemoryUsed += strikePtr->fMemoryUsed;
293 
294     if (fHead != nullptr) {
295         fHead->fPrev = strikePtr;
296         strikePtr->fNext = fHead;
297     }
298 
299     if (fTail == nullptr) {
300         fTail = strikePtr;
301     }
302 
303     fHead = strikePtr; // Transfer ownership of strike to the cache list.
304 }
305 
internalRemoveStrike(Strike * strike)306 void SkStrikeCache::internalRemoveStrike(Strike* strike) {
307     SkASSERT(fCacheCount > 0);
308     fCacheCount -= 1;
309     fTotalMemoryUsed -= strike->fMemoryUsed;
310 
311     if (strike->fPrev) {
312         strike->fPrev->fNext = strike->fNext;
313     } else {
314         fHead = strike->fNext;
315     }
316     if (strike->fNext) {
317         strike->fNext->fPrev = strike->fPrev;
318     } else {
319         fTail = strike->fPrev;
320     }
321 
322     strike->fPrev = strike->fNext = nullptr;
323     strike->fRemoved = true;
324     fStrikeLookup.remove(strike->getDescriptor());
325 }
326 
validate() const327 void SkStrikeCache::validate() const {
328 #ifdef SK_DEBUG
329     size_t computedBytes = 0;
330     int computedCount = 0;
331 
332     const Strike* strike = fHead;
333     while (strike != nullptr) {
334         computedBytes += strike->fMemoryUsed;
335         computedCount += 1;
336         SkASSERT(fStrikeLookup.findOrNull(strike->getDescriptor()) != nullptr);
337         strike = strike->fNext;
338     }
339 
340     if (fCacheCount != computedCount) {
341         SkDebugf("fCacheCount: %d, computedCount: %d", fCacheCount, computedCount);
342         SK_ABORT("fCacheCount != computedCount");
343     }
344     if (fTotalMemoryUsed != computedBytes) {
345         SkDebugf("fTotalMemoryUsed: %d, computedBytes: %d", fTotalMemoryUsed, computedBytes);
346         SK_ABORT("fTotalMemoryUsed == computedBytes");
347     }
348 #endif
349 }
350 
updateDelta(size_t increase)351 void SkStrikeCache::Strike::updateDelta(size_t increase) {
352     if (increase != 0) {
353         SkAutoMutexExclusive lock{fStrikeCache->fLock};
354         fMemoryUsed += increase;
355         if (!fRemoved) {
356             fStrikeCache->fTotalMemoryUsed += increase;
357         }
358     }
359 }
360