/src/skia/src/core/SkStrikeCache.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
30 | 121k | SkStrikeCache* SkStrikeCache::GlobalStrikeCache() { |
31 | 121k | if (gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental) { |
32 | 0 | static thread_local auto* cache = new SkStrikeCache; |
33 | 0 | return cache; |
34 | 0 | } |
35 | 121k | static auto* cache = new SkStrikeCache; |
36 | 121k | return cache; |
37 | 121k | } |
38 | | |
39 | 127k | auto SkStrikeCache::findOrCreateStrike(const SkStrikeSpec& strikeSpec) -> sk_sp<SkStrike> { |
40 | 127k | SkAutoMutexExclusive ac(fLock); |
41 | 127k | sk_sp<SkStrike> strike = this->internalFindStrikeOrNull(strikeSpec.descriptor()); |
42 | 127k | if (strike == nullptr) { |
43 | 23.3k | strike = this->internalCreateStrike(strikeSpec); |
44 | 23.3k | } |
45 | 127k | this->internalPurge(); |
46 | 127k | return strike; |
47 | 127k | } |
48 | | |
49 | 7.59k | sk_sp<StrikeForGPU> SkStrikeCache::findOrCreateScopedStrike(const SkStrikeSpec& strikeSpec) { |
50 | 7.59k | return this->findOrCreateStrike(strikeSpec); |
51 | 7.59k | } |
52 | | |
53 | 0 | void SkStrikeCache::PurgeAll() { |
54 | 0 | GlobalStrikeCache()->purgeAll(); |
55 | 0 | } |
56 | | |
57 | 0 | void SkStrikeCache::Dump() { |
58 | 0 | SkDebugf("GlyphCache [ used budget ]\n"); |
59 | 0 | SkDebugf(" bytes [ %8zu %8zu ]\n", |
60 | 0 | SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit()); |
61 | 0 | SkDebugf(" count [ %8d %8d ]\n", |
62 | 0 | SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit()); |
63 | |
|
64 | 0 | auto visitor = [](const SkStrike& strike) { |
65 | 0 | strike.dump(); |
66 | 0 | }; |
67 | |
|
68 | 0 | GlobalStrikeCache()->forEachStrike(visitor); |
69 | 0 | } |
70 | | |
71 | 0 | void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) { |
72 | 0 | dump->dumpNumericValue(kGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed()); |
73 | 0 | dump->dumpNumericValue(kGlyphCacheDumpName, "budget_size", "bytes", |
74 | 0 | SkGraphics::GetFontCacheLimit()); |
75 | 0 | dump->dumpNumericValue(kGlyphCacheDumpName, "glyph_count", "objects", |
76 | 0 | SkGraphics::GetFontCacheCountUsed()); |
77 | 0 | dump->dumpNumericValue(kGlyphCacheDumpName, "budget_glyph_count", "objects", |
78 | 0 | SkGraphics::GetFontCacheCountLimit()); |
79 | |
|
80 | 0 | if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) { |
81 | 0 | dump->setMemoryBacking(kGlyphCacheDumpName, "malloc", nullptr); |
82 | 0 | return; |
83 | 0 | } |
84 | | |
85 | 0 | auto visitor = [&](const SkStrike& strike) { |
86 | 0 | strike.dumpMemoryStatistics(dump); |
87 | 0 | }; |
88 | |
|
89 | 0 | GlobalStrikeCache()->forEachStrike(visitor); |
90 | 0 | } |
91 | | |
92 | 0 | sk_sp<SkStrike> SkStrikeCache::findStrike(const SkDescriptor& desc) { |
93 | 0 | SkAutoMutexExclusive ac(fLock); |
94 | 0 | sk_sp<SkStrike> result = this->internalFindStrikeOrNull(desc); |
95 | 0 | this->internalPurge(); |
96 | 0 | return result; |
97 | 0 | } |
98 | | |
99 | 127k | auto SkStrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) -> sk_sp<SkStrike> { |
100 | | |
101 | | // Check head because it is likely the strike we are looking for. |
102 | 127k | if (fHead != nullptr && fHead->getDescriptor() == desc) { return sk_ref_sp(fHead); } |
103 | | |
104 | | // Do the heavy search looking for the strike. |
105 | 56.5k | sk_sp<SkStrike>* strikeHandle = fStrikeLookup.find(desc); |
106 | 56.5k | if (strikeHandle == nullptr) { return nullptr; } |
107 | 33.1k | SkStrike* strikePtr = strikeHandle->get(); |
108 | 33.1k | SkASSERT(strikePtr != nullptr); |
109 | 33.1k | if (fHead != strikePtr) { |
110 | | // Make most recently used |
111 | 33.1k | strikePtr->fPrev->fNext = strikePtr->fNext; |
112 | 33.1k | if (strikePtr->fNext != nullptr) { |
113 | 32.6k | strikePtr->fNext->fPrev = strikePtr->fPrev; |
114 | 32.6k | } else { |
115 | 517 | fTail = strikePtr->fPrev; |
116 | 517 | } |
117 | 33.1k | fHead->fPrev = strikePtr; |
118 | 33.1k | strikePtr->fNext = fHead; |
119 | 33.1k | strikePtr->fPrev = nullptr; |
120 | 33.1k | fHead = strikePtr; |
121 | 33.1k | } |
122 | 33.1k | return sk_ref_sp(strikePtr); |
123 | 56.5k | } |
124 | | |
125 | | sk_sp<SkStrike> SkStrikeCache::createStrike( |
126 | | const SkStrikeSpec& strikeSpec, |
127 | | SkFontMetrics* maybeMetrics, |
128 | 0 | std::unique_ptr<SkStrikePinner> pinner) { |
129 | 0 | SkAutoMutexExclusive ac(fLock); |
130 | 0 | return this->internalCreateStrike(strikeSpec, maybeMetrics, std::move(pinner)); |
131 | 0 | } |
132 | | |
133 | | auto SkStrikeCache::internalCreateStrike( |
134 | | const SkStrikeSpec& strikeSpec, |
135 | | SkFontMetrics* maybeMetrics, |
136 | 23.3k | std::unique_ptr<SkStrikePinner> pinner) -> sk_sp<SkStrike> { |
137 | 23.3k | std::unique_ptr<SkScalerContext> scaler = strikeSpec.createScalerContext(); |
138 | 23.3k | auto strike = |
139 | 23.3k | sk_make_sp<SkStrike>(this, strikeSpec, std::move(scaler), maybeMetrics, std::move(pinner)); |
140 | 23.3k | this->internalAttachToHead(strike); |
141 | 23.3k | return strike; |
142 | 23.3k | } |
143 | | |
144 | 0 | void SkStrikeCache::purgePinned(size_t minBytesNeeded) { |
145 | 0 | SkAutoMutexExclusive ac(fLock); |
146 | 0 | this->internalPurge(minBytesNeeded, /* checkPinners= */ true); |
147 | 0 | } |
148 | | |
149 | 0 | void SkStrikeCache::purgeAll() { |
150 | 0 | SkAutoMutexExclusive ac(fLock); |
151 | 0 | this->internalPurge(fTotalMemoryUsed, /* checkPinners= */ true); |
152 | 0 | } |
153 | | |
154 | 0 | size_t SkStrikeCache::getTotalMemoryUsed() const { |
155 | 0 | SkAutoMutexExclusive ac(fLock); |
156 | 0 | return fTotalMemoryUsed; |
157 | 0 | } |
158 | | |
159 | 0 | int SkStrikeCache::getCacheCountUsed() const { |
160 | 0 | SkAutoMutexExclusive ac(fLock); |
161 | 0 | return fCacheCount; |
162 | 0 | } |
163 | | |
164 | 0 | int SkStrikeCache::getCacheCountLimit() const { |
165 | 0 | SkAutoMutexExclusive ac(fLock); |
166 | 0 | return fCacheCountLimit; |
167 | 0 | } |
168 | | |
169 | 0 | size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) { |
170 | 0 | SkAutoMutexExclusive ac(fLock); |
171 | |
|
172 | 0 | size_t prevLimit = fCacheSizeLimit; |
173 | 0 | fCacheSizeLimit = newLimit; |
174 | 0 | this->internalPurge(); |
175 | 0 | return prevLimit; |
176 | 0 | } |
177 | | |
178 | 0 | size_t SkStrikeCache::getCacheSizeLimit() const { |
179 | 0 | SkAutoMutexExclusive ac(fLock); |
180 | 0 | return fCacheSizeLimit; |
181 | 0 | } |
182 | | |
183 | 0 | int SkStrikeCache::setCacheCountLimit(int newCount) { |
184 | 0 | if (newCount < 0) { |
185 | 0 | newCount = 0; |
186 | 0 | } |
187 | |
|
188 | 0 | SkAutoMutexExclusive ac(fLock); |
189 | |
|
190 | 0 | int prevCount = fCacheCountLimit; |
191 | 0 | fCacheCountLimit = newCount; |
192 | 0 | this->internalPurge(); |
193 | 0 | return prevCount; |
194 | 0 | } |
195 | | |
196 | 0 | void SkStrikeCache::forEachStrike(std::function<void(const SkStrike&)> visitor) const { |
197 | 0 | SkAutoMutexExclusive ac(fLock); |
198 | |
|
199 | 0 | this->validate(); |
200 | |
|
201 | 0 | for (SkStrike* strike = fHead; strike != nullptr; strike = strike->fNext) { |
202 | 0 | visitor(*strike); |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | 127k | size_t SkStrikeCache::internalPurge(size_t minBytesNeeded, bool checkPinners) { |
207 | 127k | #ifndef SK_STRIKE_CACHE_DOESNT_AUTO_CHECK_PINNERS |
208 | | // Temporarily default to checking pinners, for staging. |
209 | 127k | checkPinners = true; |
210 | 127k | #endif |
211 | | |
212 | 127k | if (fPinnerCount == fCacheCount && !checkPinners) |
213 | 0 | return 0; |
214 | | |
215 | 127k | size_t bytesNeeded = 0; |
216 | 127k | if (fTotalMemoryUsed > fCacheSizeLimit) { |
217 | 2.06k | bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit; |
218 | 2.06k | } |
219 | 127k | bytesNeeded = std::max(bytesNeeded, minBytesNeeded); |
220 | 127k | if (bytesNeeded) { |
221 | | // no small purges! |
222 | 2.06k | bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2); |
223 | 2.06k | } |
224 | | |
225 | 127k | int countNeeded = 0; |
226 | 127k | if (fCacheCount > fCacheCountLimit) { |
227 | 6 | countNeeded = fCacheCount - fCacheCountLimit; |
228 | | // no small purges! |
229 | 6 | countNeeded = std::max(countNeeded, fCacheCount >> 2); |
230 | 6 | } |
231 | | |
232 | | // early exit |
233 | 127k | if (!countNeeded && !bytesNeeded) { |
234 | 125k | return 0; |
235 | 125k | } |
236 | | |
237 | 2.06k | size_t bytesFreed = 0; |
238 | 2.06k | 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 | 2.06k | SkStrike* strike = fTail; |
243 | 21.3k | while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) { |
244 | 19.3k | SkStrike* prev = strike->fPrev; |
245 | | |
246 | | // Only delete if the strike is not pinned. |
247 | 19.3k | if (strike->fPinner == nullptr || (checkPinners && strike->fPinner->canDelete())) { |
248 | 19.3k | bytesFreed += strike->fMemoryUsed; |
249 | 19.3k | countFreed += 1; |
250 | 19.3k | this->internalRemoveStrike(strike); |
251 | 19.3k | } |
252 | 19.3k | strike = prev; |
253 | 19.3k | } |
254 | | |
255 | 2.06k | 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 | 2.06k | return bytesFreed; |
265 | 127k | } |
266 | | |
267 | 23.3k | void SkStrikeCache::internalAttachToHead(sk_sp<SkStrike> strike) { |
268 | 23.3k | SkASSERT(fStrikeLookup.find(strike->getDescriptor()) == nullptr); |
269 | 23.3k | SkStrike* strikePtr = strike.get(); |
270 | 23.3k | fStrikeLookup.set(std::move(strike)); |
271 | 23.3k | SkASSERT(nullptr == strikePtr->fPrev && nullptr == strikePtr->fNext); |
272 | | |
273 | 23.3k | fCacheCount += 1; |
274 | 23.3k | fPinnerCount += strikePtr->fPinner != nullptr ? 1 : 0; |
275 | 23.3k | fTotalMemoryUsed += strikePtr->fMemoryUsed; |
276 | | |
277 | 23.3k | if (fHead != nullptr) { |
278 | 22.0k | fHead->fPrev = strikePtr; |
279 | 22.0k | strikePtr->fNext = fHead; |
280 | 22.0k | } |
281 | | |
282 | 23.3k | if (fTail == nullptr) { |
283 | 1.30k | fTail = strikePtr; |
284 | 1.30k | } |
285 | | |
286 | 23.3k | fHead = strikePtr; // Transfer ownership of strike to the cache list. |
287 | 23.3k | } |
288 | | |
289 | 19.3k | void SkStrikeCache::internalRemoveStrike(SkStrike* strike) { |
290 | 19.3k | SkASSERT(fCacheCount > 0); |
291 | 19.3k | fCacheCount -= 1; |
292 | 19.3k | fPinnerCount -= strike->fPinner != nullptr ? 1 : 0; |
293 | 19.3k | fTotalMemoryUsed -= strike->fMemoryUsed; |
294 | | |
295 | 19.3k | if (strike->fPrev) { |
296 | 18.0k | strike->fPrev->fNext = strike->fNext; |
297 | 18.0k | } else { |
298 | 1.29k | fHead = strike->fNext; |
299 | 1.29k | } |
300 | 19.3k | if (strike->fNext) { |
301 | 0 | strike->fNext->fPrev = strike->fPrev; |
302 | 19.3k | } else { |
303 | 19.3k | fTail = strike->fPrev; |
304 | 19.3k | } |
305 | | |
306 | 19.3k | strike->fPrev = strike->fNext = nullptr; |
307 | 19.3k | strike->fRemoved = true; |
308 | 19.3k | fStrikeLookup.remove(strike->getDescriptor()); |
309 | 19.3k | } SkStrikeCache::internalRemoveStrike(SkStrike*) Line | Count | Source | 289 | 19.3k | void SkStrikeCache::internalRemoveStrike(SkStrike* strike) { | 290 | 19.3k | SkASSERT(fCacheCount > 0); | 291 | 19.3k | fCacheCount -= 1; | 292 | 19.3k | fPinnerCount -= strike->fPinner != nullptr ? 1 : 0; | 293 | 19.3k | fTotalMemoryUsed -= strike->fMemoryUsed; | 294 | | | 295 | 19.3k | if (strike->fPrev) { | 296 | 18.0k | strike->fPrev->fNext = strike->fNext; | 297 | 18.0k | } else { | 298 | 1.29k | fHead = strike->fNext; | 299 | 1.29k | } | 300 | 19.3k | if (strike->fNext) { | 301 | 0 | strike->fNext->fPrev = strike->fPrev; | 302 | 19.3k | } else { | 303 | 19.3k | fTail = strike->fPrev; | 304 | 19.3k | } | 305 | | | 306 | 19.3k | strike->fPrev = strike->fNext = nullptr; | 307 | 19.3k | strike->fRemoved = true; | 308 | 19.3k | fStrikeLookup.remove(strike->getDescriptor()); | 309 | 19.3k | } |
Unexecuted instantiation: SkStrikeCache::internalRemoveStrike(SkStrike*) |
310 | | |
311 | 2.06k | 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 | 2.06k | } |
334 | | |
335 | 114k | const SkDescriptor& SkStrikeCache::StrikeTraits::GetKey(const sk_sp<SkStrike>& strike) { |
336 | 114k | return strike->getDescriptor(); |
337 | 114k | } |
338 | | |
339 | 137k | uint32_t SkStrikeCache::StrikeTraits::Hash(const SkDescriptor& descriptor) { |
340 | 137k | return descriptor.getChecksum(); |
341 | 137k | } |
342 | | |
343 | | |