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