Coverage Report

Created: 2024-05-20 07:14

/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