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