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