• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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