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