1 /*
2 * Copyright 2015 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 #include "src/text/gpu/StrikeCache.h"
8
9 #include "include/private/base/SkAssert.h"
10 #include "include/private/base/SkDebug.h"
11 #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
12 #include "src/base/SkArenaAlloc.h"
13 #include "src/core/SkGlyph.h"
14 #include "src/core/SkReadBuffer.h"
15 #include "src/core/SkStrikeCache.h"
16 #include "src/core/SkStrikeSpec.h"
17 #include "src/core/SkTraceEvent.h"
18 #include "src/text/StrikeForGPU.h"
19 #include "src/text/gpu/Glyph.h"
20
21 #include <algorithm>
22 #include <optional>
23 #include <utility>
24
25 class SkStrike;
26
27 namespace sktext::gpu {
28
~StrikeCache()29 StrikeCache::~StrikeCache() {
30 this->freeAll();
31 }
32
freeAll()33 void StrikeCache::freeAll() {
34 this->internalPurge(fTotalMemoryUsed);
35 }
36
findOrCreateStrike(const SkStrikeSpec & strikeSpec)37 sk_sp<TextStrike> StrikeCache::findOrCreateStrike(const SkStrikeSpec& strikeSpec) {
38 if (sk_sp<TextStrike>* cached = fCache.find(strikeSpec.descriptor())) {
39 return *cached;
40 }
41 sk_sp<TextStrike> strike = this->generateStrike(strikeSpec);
42 this->internalPurge();
43
44 return strike;
45 }
46
internalFindStrikeOrNull(const SkDescriptor & desc)47 sk_sp<TextStrike> StrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) {
48 // Check head because it is likely the strike we are looking for.
49 if (fHead != nullptr && fHead->getDescriptor() == desc) { return sk_ref_sp(fHead); }
50
51 // Do the heavy search looking for the strike.
52 sk_sp<TextStrike>* strikeHandle = fCache.find(desc);
53 if (strikeHandle == nullptr) { return nullptr; }
54 TextStrike* strikePtr = strikeHandle->get();
55 SkASSERT(strikePtr != nullptr);
56 if (fHead != strikePtr) {
57 // Make most recently used
58 strikePtr->fPrev->fNext = strikePtr->fNext;
59 if (strikePtr->fNext != nullptr) {
60 strikePtr->fNext->fPrev = strikePtr->fPrev;
61 } else {
62 fTail = strikePtr->fPrev;
63 }
64 fHead->fPrev = strikePtr;
65 strikePtr->fNext = fHead;
66 strikePtr->fPrev = nullptr;
67 fHead = strikePtr;
68 }
69 return sk_ref_sp(strikePtr);
70 }
71
generateStrike(const SkStrikeSpec & strikeSpec)72 sk_sp<TextStrike> StrikeCache::generateStrike(const SkStrikeSpec& strikeSpec) {
73 sk_sp<TextStrike> strike = sk_make_sp<TextStrike>(this, strikeSpec);
74 this->internalAttachToHead(strike);
75 return strike;
76 }
77
internalPurge(size_t minBytesNeeded)78 size_t StrikeCache::internalPurge(size_t minBytesNeeded) {
79 size_t bytesNeeded = 0;
80 if (fTotalMemoryUsed > fCacheSizeLimit) {
81 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
82 }
83 bytesNeeded = std::max(bytesNeeded, minBytesNeeded);
84 if (bytesNeeded) {
85 // no small purges!
86 bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2);
87 }
88
89 int countNeeded = 0;
90 if (fCacheCount > fCacheCountLimit) {
91 countNeeded = fCacheCount - fCacheCountLimit;
92 // no small purges!
93 countNeeded = std::max(countNeeded, fCacheCount >> 2);
94 }
95
96 // early exit
97 if (!countNeeded && !bytesNeeded) {
98 return 0;
99 }
100
101 TRACE_EVENT2_ALWAYS("skia.gpu.cache", "StrikeCache::internalPurge",
102 "totalMemoryUsed", fTotalMemoryUsed, "cacheCount", fCacheCount);
103
104 size_t bytesFreed = 0;
105 int countFreed = 0;
106
107 // Start at the tail and proceed backwards deleting; the list is in LRU
108 // order, with unimportant entries at the tail.
109 TextStrike* strike = fTail;
110 while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
111 TextStrike* prev = strike->fPrev;
112
113 bytesFreed += strike->fMemoryUsed;
114 countFreed += 1;
115 this->internalRemoveStrike(strike);
116
117 strike = prev;
118 }
119
120 this->validate();
121
122 #ifdef SPEW_PURGE_STATUS
123 if (countFreed) {
124 SkDebugf("purging %dK from font cache [%d entries]\n",
125 (int)(bytesFreed >> 10), countFreed);
126 }
127 #endif
128
129 return bytesFreed;
130 }
131
internalAttachToHead(sk_sp<TextStrike> strike)132 void StrikeCache::internalAttachToHead(sk_sp<TextStrike> strike) {
133 SkASSERT(fCache.find(strike->getDescriptor()) == nullptr);
134 TextStrike* strikePtr = strike.get();
135 fCache.set(std::move(strike));
136 SkASSERT(nullptr == strikePtr->fPrev && nullptr == strikePtr->fNext);
137
138 fCacheCount += 1;
139 fTotalMemoryUsed += strikePtr->fMemoryUsed;
140
141 if (fHead != nullptr) {
142 fHead->fPrev = strikePtr;
143 strikePtr->fNext = fHead;
144 }
145
146 if (fTail == nullptr) {
147 fTail = strikePtr;
148 }
149
150 fHead = strikePtr; // Transfer ownership of strike to the cache list.
151 }
152
internalRemoveStrike(TextStrike * strike)153 void StrikeCache::internalRemoveStrike(TextStrike* strike) {
154 SkASSERT(fCacheCount > 0);
155 fCacheCount -= 1;
156 fTotalMemoryUsed -= strike->fMemoryUsed;
157
158 if (strike->fPrev) {
159 strike->fPrev->fNext = strike->fNext;
160 } else {
161 fHead = strike->fNext;
162 }
163 if (strike->fNext) {
164 strike->fNext->fPrev = strike->fPrev;
165 } else {
166 fTail = strike->fPrev;
167 }
168
169 strike->fPrev = strike->fNext = nullptr;
170 strike->fRemoved = true;
171 fCache.remove(strike->getDescriptor());
172 }
173
validate() const174 void StrikeCache::validate() const {
175 #ifdef SK_DEBUG
176 size_t computedBytes = 0;
177 int computedCount = 0;
178
179 const TextStrike* strike = fHead;
180 while (strike != nullptr) {
181 computedBytes += strike->fMemoryUsed;
182 computedCount += 1;
183 SkASSERT(fCache.findOrNull(strike->getDescriptor()) != nullptr);
184 strike = strike->fNext;
185 }
186
187 if (fCacheCount != computedCount) {
188 SkDebugf("fCacheCount: %d, computedCount: %d", fCacheCount, computedCount);
189 SK_ABORT("fCacheCount != computedCount");
190 }
191 if (fTotalMemoryUsed != computedBytes) {
192 SkDebugf("fTotalMemoryUsed: %zu, computedBytes: %zu", fTotalMemoryUsed, computedBytes);
193 SK_ABORT("fTotalMemoryUsed == computedBytes");
194 }
195 #endif
196 }
197
GetKey(const sk_sp<TextStrike> & strike)198 const SkDescriptor& StrikeCache::HashTraits::GetKey(const sk_sp<TextStrike>& strike) {
199 return strike->fStrikeSpec.descriptor();
200 }
201
Hash(const SkDescriptor & descriptor)202 uint32_t StrikeCache::HashTraits::Hash(const SkDescriptor& descriptor) {
203 return descriptor.getChecksum();
204 }
205
TextStrike(StrikeCache * strikeCache,const SkStrikeSpec & strikeSpec)206 TextStrike::TextStrike(StrikeCache* strikeCache, const SkStrikeSpec& strikeSpec)
207 : fStrikeCache(strikeCache)
208 , fStrikeSpec{strikeSpec} {}
209
getGlyph(SkPackedGlyphID packedGlyphID)210 Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID) {
211 Glyph* glyph = fCache.findOrNull(packedGlyphID);
212 if (glyph == nullptr) {
213 glyph = fAlloc.make<Glyph>(packedGlyphID);
214 fCache.set(glyph);
215 fMemoryUsed += sizeof(Glyph);
216 if (!fRemoved) {
217 fStrikeCache->fTotalMemoryUsed += sizeof(Glyph);
218 }
219 }
220 return glyph;
221 }
222
GetKey(const Glyph * glyph)223 const SkPackedGlyphID& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
224 return glyph->fPackedID;
225 }
226
Hash(SkPackedGlyphID key)227 uint32_t TextStrike::HashTraits::Hash(SkPackedGlyphID key) {
228 return key.hash();
229 }
230
231 } // namespace sktext::gpu
232
233 namespace sktext {
MakeFromBuffer(SkReadBuffer & buffer,const SkStrikeClient * client,SkStrikeCache * strikeCache)234 std::optional<SkStrikePromise> SkStrikePromise::MakeFromBuffer(
235 SkReadBuffer& buffer, const SkStrikeClient* client, SkStrikeCache* strikeCache) {
236 std::optional<SkAutoDescriptor> descriptor = SkAutoDescriptor::MakeFromBuffer(buffer);
237 if (!buffer.validate(descriptor.has_value())) {
238 return std::nullopt;
239 }
240
241 // If there is a client, then this from a different process. Translate the SkTypefaceID from
242 // the strike server (Renderer) process to strike client (GPU) process.
243 if (client != nullptr) {
244 if (!client->translateTypefaceID(&descriptor.value())) {
245 return std::nullopt;
246 }
247 }
248
249 sk_sp<SkStrike> strike = strikeCache->findStrike(*descriptor->getDesc());
250 SkASSERT(strike != nullptr);
251 if (!buffer.validate(strike != nullptr)) {
252 return std::nullopt;
253 }
254
255 return SkStrikePromise{std::move(strike)};
256 }
257 } // namespace sktext
258