Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkStrikeCache.cpp
Go to the documentation of this file.
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
9
17#include "src/core/SkStrike.h"
19
20#include <algorithm>
21#include <utility>
22
23class SkScalerContext;
24struct SkFontMetrics;
25
26using namespace sktext;
27
29
32 static thread_local auto* cache = new SkStrikeCache;
33 return cache;
34 }
35 static auto* cache = new SkStrikeCache;
36 return cache;
37}
38
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
52
56
58 SkDebugf("GlyphCache [ used budget ]\n");
59 SkDebugf(" bytes [ %8zu %8zu ]\n",
61 SkDebugf(" count [ %8d %8d ]\n",
63
64 auto visitor = [](const SkStrike& strike) {
65 strike.dump();
66 };
67
68 GlobalStrikeCache()->forEachStrike(visitor);
69}
70
72 dump->dumpNumericValue(kGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
73 dump->dumpNumericValue(kGlyphCacheDumpName, "budget_size", "bytes",
75 dump->dumpNumericValue(kGlyphCacheDumpName, "glyph_count", "objects",
77 dump->dumpNumericValue(kGlyphCacheDumpName, "budget_glyph_count", "objects",
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
93 SkAutoMutexExclusive ac(fLock);
94 sk_sp<SkStrike> result = this->internalFindStrikeOrNull(desc);
95 this->internalPurge();
96 return result;
97}
98
99auto 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
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
133auto 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
144void SkStrikeCache::purgePinned(size_t minBytesNeeded) {
145 SkAutoMutexExclusive ac(fLock);
146 this->internalPurge(minBytesNeeded, /* checkPinners= */ true);
147}
148
150 SkAutoMutexExclusive ac(fLock);
151 this->internalPurge(fTotalMemoryUsed, /* checkPinners= */ true);
152}
153
155 SkAutoMutexExclusive ac(fLock);
156 return fTotalMemoryUsed;
157}
158
160 SkAutoMutexExclusive ac(fLock);
161 return fCacheCount;
162}
163
165 SkAutoMutexExclusive ac(fLock);
166 return fCacheCountLimit;
167}
168
169size_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
179 SkAutoMutexExclusive ac(fLock);
180 return fCacheSizeLimit;
181}
182
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
196void 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
206size_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
267void 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
289void 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
311void 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
335const SkDescriptor& SkStrikeCache::StrikeTraits::GetKey(const sk_sp<SkStrike>& strike) {
336 return strike->getDescriptor();
337}
338
339uint32_t SkStrikeCache::StrikeTraits::Hash(const SkDescriptor& descriptor) {
340 return descriptor.getChecksum();
341}
342
343
static float prev(float f)
#define SK_ABORT(message,...)
Definition SkAssert.h:70
#define SkASSERT(cond)
Definition SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental
uint32_t getChecksum() const
static int GetFontCacheCountLimit()
static size_t GetFontCacheLimit()
static int GetFontCacheCountUsed()
static size_t GetFontCacheUsed()
static void DumpMemoryStatistics(SkTraceMemoryDump *dump)
size_t getCacheSizeLimit() const SK_EXCLUDES(fLock)
size_t setCacheSizeLimit(size_t limit) SK_EXCLUDES(fLock)
int setCacheCountLimit(int limit) SK_EXCLUDES(fLock)
sk_sp< SkStrike > createStrike(const SkStrikeSpec &strikeSpec, SkFontMetrics *maybeMetrics=nullptr, std::unique_ptr< SkStrikePinner >=nullptr) SK_EXCLUDES(fLock)
static void PurgeAll()
sk_sp< SkStrike > findOrCreateStrike(const SkStrikeSpec &strikeSpec) SK_EXCLUDES(fLock)
static void Dump()
sk_sp< SkStrike > findStrike(const SkDescriptor &desc) SK_EXCLUDES(fLock)
void purgePinned(size_t minBytesNeeded=0) SK_EXCLUDES(fLock)
void purgeAll() SK_EXCLUDES(fLock)
sk_sp< sktext::StrikeForGPU > findOrCreateScopedStrike(const SkStrikeSpec &strikeSpec) override SK_EXCLUDES(fLock)
int getCacheCountLimit() const SK_EXCLUDES(fLock)
size_t getTotalMemoryUsed() const SK_EXCLUDES(fLock)
int getCacheCountUsed() const SK_EXCLUDES(fLock)
SkStrikeCache()=default
static SkStrikeCache * GlobalStrikeCache()
const SkDescriptor & getDescriptor() const override
Definition SkStrike.h:103
T * get() const
Definition SkRefCnt.h:303
GAsyncResult * result