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