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