1/*
2 * Copyright 2010 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#ifndef SkStrikeCache_DEFINED
9#define SkStrikeCache_DEFINED
10
11#include <unordered_map>
12#include <unordered_set>
13
14#include "include/private/SkSpinlock.h"
15#include "include/private/SkTemplates.h"
16#include "src/core/SkDescriptor.h"
17#include "src/core/SkScalerCache.h"
18
19class SkTraceMemoryDump;
20
21#ifndef SK_DEFAULT_FONT_CACHE_COUNT_LIMIT
22 #define SK_DEFAULT_FONT_CACHE_COUNT_LIMIT 2048
23#endif
24
25#ifndef SK_DEFAULT_FONT_CACHE_LIMIT
26 #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024)
27#endif
28
29#ifndef SK_DEFAULT_FONT_CACHE_POINT_SIZE_LIMIT
30 #define SK_DEFAULT_FONT_CACHE_POINT_SIZE_LIMIT 256
31#endif
32
33///////////////////////////////////////////////////////////////////////////////
34
35class SkStrikePinner {
36public:
37 virtual ~SkStrikePinner() = default;
38 virtual bool canDelete() = 0;
39};
40
41class SkStrikeCache final : public SkStrikeForGPUCacheInterface {
42public:
43 SkStrikeCache() = default;
44
45 class Strike final : public SkRefCnt, public SkStrikeForGPU {
46 public:
47 Strike(SkStrikeCache* strikeCache,
48 const SkDescriptor& desc,
49 std::unique_ptr<SkScalerContext> scaler,
50 const SkFontMetrics* metrics,
51 std::unique_ptr<SkStrikePinner> pinner)
52 : fStrikeCache{strikeCache}
53 , fScalerCache{desc, std::move(scaler), metrics}
54 , fPinner{std::move(pinner)} {}
55
56 SkGlyph* mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& from) {
57 auto [glyph, increase] = fScalerCache.mergeGlyphAndImage(toID, from);
58 this->updateDelta(increase);
59 return glyph;
60 }
61
62 const SkPath* mergePath(SkGlyph* glyph, const SkPath* path) {
63 auto [glyphPath, increase] = fScalerCache.mergePath(glyph, path);
64 this->updateDelta(increase);
65 return glyphPath;
66 }
67
68 SkScalerContext* getScalerContext() const {
69 return fScalerCache.getScalerContext();
70 }
71
72 void findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
73 SkGlyph* glyph, SkScalar* array, int* count) {
74 fScalerCache.findIntercepts(bounds, scale, xPos, glyph, array, count);
75 }
76
77 const SkFontMetrics& getFontMetrics() const {
78 return fScalerCache.getFontMetrics();
79 }
80
81 SkSpan<const SkGlyph*> metrics(SkSpan<const SkGlyphID> glyphIDs,
82 const SkGlyph* results[]) {
83 auto [glyphs, increase] = fScalerCache.metrics(glyphIDs, results);
84 this->updateDelta(increase);
85 return glyphs;
86 }
87
88 SkSpan<const SkGlyph*> preparePaths(SkSpan<const SkGlyphID> glyphIDs,
89 const SkGlyph* results[]) {
90 auto [glyphs, increase] = fScalerCache.preparePaths(glyphIDs, results);
91 this->updateDelta(increase);
92 return glyphs;
93 }
94
95 SkSpan<const SkGlyph*> prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs,
96 const SkGlyph* results[]) {
97 auto [glyphs, increase] = fScalerCache.prepareImages(glyphIDs, results);
98 this->updateDelta(increase);
99 return glyphs;
100 }
101
102 void prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables) {
103 size_t increase = fScalerCache.prepareForDrawingMasksCPU(drawables);
104 this->updateDelta(increase);
105 }
106
107 const SkGlyphPositionRoundingSpec& roundingSpec() const override {
108 return fScalerCache.roundingSpec();
109 }
110
111 const SkDescriptor& getDescriptor() const override {
112 return fScalerCache.getDescriptor();
113 }
114
115 void prepareForMaskDrawing(
116 SkDrawableGlyphBuffer* drawbles, SkSourceGlyphBuffer* rejects) override {
117 size_t increase = fScalerCache.prepareForMaskDrawing(drawbles, rejects);
118 this->updateDelta(increase);
119 }
120
121 void prepareForSDFTDrawing(
122 SkDrawableGlyphBuffer* drawbles, SkSourceGlyphBuffer* rejects) override {
123 size_t increase = fScalerCache.prepareForSDFTDrawing(drawbles, rejects);
124 this->updateDelta(increase);
125 }
126
127 void prepareForPathDrawing(
128 SkDrawableGlyphBuffer* drawbles, SkSourceGlyphBuffer* rejects) override {
129 size_t increase = fScalerCache.prepareForPathDrawing(drawbles, rejects);
130 this->updateDelta(increase);
131 }
132
133 void onAboutToExitScope() override {
134 this->unref();
135 }
136
137 void updateDelta(size_t increase);
138
139 SkStrikeCache* const fStrikeCache;
140 Strike* fNext{nullptr};
141 Strike* fPrev{nullptr};
142 SkScalerCache fScalerCache;
143 std::unique_ptr<SkStrikePinner> fPinner;
144 size_t fMemoryUsed{sizeof(SkScalerCache)};
145 bool fRemoved{false};
146 }; // Strike
147
148 static SkStrikeCache* GlobalStrikeCache();
149
150 sk_sp<Strike> findStrike(const SkDescriptor& desc) SK_EXCLUDES(fLock);
151
152 sk_sp<Strike> createStrike(
153 const SkDescriptor& desc,
154 std::unique_ptr<SkScalerContext> scaler,
155 SkFontMetrics* maybeMetrics = nullptr,
156 std::unique_ptr<SkStrikePinner> = nullptr) SK_EXCLUDES(fLock);
157
158 sk_sp<Strike> findOrCreateStrike(
159 const SkDescriptor& desc,
160 const SkScalerContextEffects& effects,
161 const SkTypeface& typeface) SK_EXCLUDES(fLock);
162
163 SkScopedStrikeForGPU findOrCreateScopedStrike(
164 const SkDescriptor& desc,
165 const SkScalerContextEffects& effects,
166 const SkTypeface& typeface) override SK_EXCLUDES(fLock);
167
168 static void PurgeAll();
169 static void Dump();
170
171 // Dump memory usage statistics of all the attaches caches in the process using the
172 // SkTraceMemoryDump interface.
173 static void DumpMemoryStatistics(SkTraceMemoryDump* dump);
174
175 void purgeAll() SK_EXCLUDES(fLock); // does not change budget
176
177 int getCacheCountLimit() const SK_EXCLUDES(fLock);
178 int setCacheCountLimit(int limit) SK_EXCLUDES(fLock);
179 int getCacheCountUsed() const SK_EXCLUDES(fLock);
180
181 size_t getCacheSizeLimit() const SK_EXCLUDES(fLock);
182 size_t setCacheSizeLimit(size_t limit) SK_EXCLUDES(fLock);
183 size_t getTotalMemoryUsed() const SK_EXCLUDES(fLock);
184
185 int getCachePointSizeLimit() const SK_EXCLUDES(fLock);
186 int setCachePointSizeLimit(int limit) SK_EXCLUDES(fLock);
187
188private:
189 sk_sp<Strike> internalFindStrikeOrNull(const SkDescriptor& desc) SK_REQUIRES(fLock);
190 sk_sp<Strike> internalCreateStrike(
191 const SkDescriptor& desc,
192 std::unique_ptr<SkScalerContext> scaler,
193 SkFontMetrics* maybeMetrics = nullptr,
194 std::unique_ptr<SkStrikePinner> = nullptr) SK_REQUIRES(fLock);
195
196 // The following methods can only be called when mutex is already held.
197 void internalRemoveStrike(Strike* strike) SK_REQUIRES(fLock);
198 void internalAttachToHead(sk_sp<Strike> strike) SK_REQUIRES(fLock);
199
200 // Checkout budgets, modulated by the specified min-bytes-needed-to-purge,
201 // and attempt to purge caches to match.
202 // Returns number of bytes freed.
203 size_t internalPurge(size_t minBytesNeeded = 0) SK_REQUIRES(fLock);
204
205 // A simple accounting of what each glyph cache reports and the strike cache total.
206 void validate() const SK_REQUIRES(fLock);
207
208 void forEachStrike(std::function<void(const Strike&)> visitor) const SK_EXCLUDES(fLock);
209
210 mutable SkSpinlock fLock;
211 Strike* fHead SK_GUARDED_BY(fLock) {nullptr};
212 Strike* fTail SK_GUARDED_BY(fLock) {nullptr};
213 struct StrikeTraits {
214 static const SkDescriptor& GetKey(const sk_sp<Strike>& strike) {
215 return strike->getDescriptor();
216 }
217 static uint32_t Hash(const SkDescriptor& descriptor) {
218 return descriptor.getChecksum();
219 }
220 };
221 SkTHashTable<sk_sp<Strike>, SkDescriptor, StrikeTraits> fStrikeLookup SK_GUARDED_BY(fLock);
222
223 size_t fCacheSizeLimit{SK_DEFAULT_FONT_CACHE_LIMIT};
224 size_t fTotalMemoryUsed SK_GUARDED_BY(fLock) {0};
225 int32_t fCacheCountLimit{SK_DEFAULT_FONT_CACHE_COUNT_LIMIT};
226 int32_t fCacheCount SK_GUARDED_BY(fLock) {0};
227 int32_t fPointSizeLimit{SK_DEFAULT_FONT_CACHE_POINT_SIZE_LIMIT};
228};
229
230using SkStrike = SkStrikeCache::Strike;
231
232#endif // SkStrikeCache_DEFINED
233