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