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
8 #include "src/gpu/text/GrTextBlobRedrawCoordinator.h"
9
10 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
11
DECLARE_SKMESSAGEBUS_MESSAGE(GrTextBlobRedrawCoordinator::PurgeBlobMessage,uint32_t,true)12 DECLARE_SKMESSAGEBUS_MESSAGE(GrTextBlobRedrawCoordinator::PurgeBlobMessage, uint32_t, true)
13
14 // This function is captured by the above macro using implementations from SkMessageBus.h
15 static inline bool SkShouldPostMessageToBus(
16 const GrTextBlobRedrawCoordinator::PurgeBlobMessage& msg, uint32_t msgBusUniqueID) {
17 return msg.fContextID == msgBusUniqueID;
18 }
19
GrTextBlobRedrawCoordinator(uint32_t messageBusID)20 GrTextBlobRedrawCoordinator::GrTextBlobRedrawCoordinator(uint32_t messageBusID)
21 : fSizeBudget(kDefaultBudget)
22 , fMessageBusID(messageBusID)
23 , fPurgeBlobInbox(messageBusID) { }
24
drawGlyphRunList(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc)25 void GrTextBlobRedrawCoordinator::drawGlyphRunList(SkCanvas* canvas,
26 const GrClip* clip,
27 const SkMatrixProvider& viewMatrix,
28 const SkGlyphRunList& glyphRunList,
29 const SkPaint& paint,
30 skgpu::v1::SurfaceDrawContext* sdc) {
31
32 SkMatrix positionMatrix{viewMatrix.localToDevice()};
33 positionMatrix.preTranslate(glyphRunList.origin().x(), glyphRunList.origin().y());
34
35 GrSDFTControl control =
36 sdc->recordingContext()->priv().getSDFTControl(
37 sdc->surfaceProps().isUseDeviceIndependentFonts());
38
39 auto [canCache, key] = GrTextBlob::Key::Make(glyphRunList,
40 paint,
41 sdc->surfaceProps(),
42 sdc->colorInfo(),
43 positionMatrix,
44 control);
45 sk_sp<GrTextBlob> blob;
46 if (canCache) {
47 blob = this->find(key);
48 }
49
50 if (blob == nullptr || !blob->canReuse(paint, positionMatrix)) {
51 if (blob != nullptr) {
52 // We have to remake the blob because changes may invalidate our masks.
53 this->remove(blob.get());
54 }
55
56 const bool padAtlas =
57 sdc->recordingContext()->priv().options().fSupportBilerpFromGlyphAtlas;
58 blob = GrTextBlob::Make(
59 glyphRunList, paint, positionMatrix, padAtlas, control, sdc->glyphRunPainter());
60
61 if (canCache) {
62 blob->addKey(key);
63 // The blob may already have been created on a different thread. Use the first one
64 // that was there.
65 blob = this->addOrReturnExisting(glyphRunList, blob);
66 }
67 }
68
69 blob->draw(canvas, clip, viewMatrix, glyphRunList.origin(), paint, sdc);
70 }
71
addOrReturnExisting(const SkGlyphRunList & glyphRunList,sk_sp<GrTextBlob> blob)72 sk_sp<GrTextBlob> GrTextBlobRedrawCoordinator::addOrReturnExisting(
73 const SkGlyphRunList& glyphRunList, sk_sp<GrTextBlob> blob) {
74 SkAutoSpinlock lock{fSpinLock};
75 blob = this->internalAdd(std::move(blob));
76 glyphRunList.temporaryShuntBlobNotifyAddedToCache(fMessageBusID);
77 return blob;
78 }
79
find(const GrTextBlob::Key & key)80 sk_sp<GrTextBlob> GrTextBlobRedrawCoordinator::find(const GrTextBlob::Key& key) {
81 SkAutoSpinlock lock{fSpinLock};
82 const BlobIDCacheEntry* idEntry = fBlobIDCache.find(key.fUniqueID);
83 if (idEntry == nullptr) {
84 return nullptr;
85 }
86
87 sk_sp<GrTextBlob> blob = idEntry->find(key);
88 GrTextBlob* blobPtr = blob.get();
89 if (blobPtr != nullptr && blobPtr != fBlobList.head()) {
90 fBlobList.remove(blobPtr);
91 fBlobList.addToHead(blobPtr);
92 }
93 return blob;
94 }
95
remove(GrTextBlob * blob)96 void GrTextBlobRedrawCoordinator::remove(GrTextBlob* blob) {
97 SkAutoSpinlock lock{fSpinLock};
98 this->internalRemove(blob);
99 }
100
internalRemove(GrTextBlob * blob)101 void GrTextBlobRedrawCoordinator::internalRemove(GrTextBlob* blob) {
102 auto id = blob->key().fUniqueID;
103 auto* idEntry = fBlobIDCache.find(id);
104
105 if (idEntry != nullptr) {
106 sk_sp<GrTextBlob> stillExists = idEntry->find(blob->key());
107 if (blob == stillExists.get()) {
108 fCurrentSize -= blob->size();
109 fBlobList.remove(blob);
110 idEntry->removeBlob(blob);
111 if (idEntry->fBlobs.empty()) {
112 fBlobIDCache.remove(id);
113 }
114 }
115 }
116 }
117
freeAll()118 void GrTextBlobRedrawCoordinator::freeAll() {
119 SkAutoSpinlock lock{fSpinLock};
120 fBlobIDCache.reset();
121 fBlobList.reset();
122 fCurrentSize = 0;
123 }
124
PostPurgeBlobMessage(uint32_t blobID,uint32_t cacheID)125 void GrTextBlobRedrawCoordinator::PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID) {
126 SkASSERT(blobID != SK_InvalidGenID);
127 SkMessageBus<PurgeBlobMessage, uint32_t>::Post(PurgeBlobMessage(blobID, cacheID));
128 }
129
purgeStaleBlobs()130 void GrTextBlobRedrawCoordinator::purgeStaleBlobs() {
131 SkAutoSpinlock lock{fSpinLock};
132 this->internalPurgeStaleBlobs();
133 }
134
internalPurgeStaleBlobs()135 void GrTextBlobRedrawCoordinator::internalPurgeStaleBlobs() {
136 SkTArray<PurgeBlobMessage> msgs;
137 fPurgeBlobInbox.poll(&msgs);
138
139 for (const auto& msg : msgs) {
140 auto* idEntry = fBlobIDCache.find(msg.fBlobID);
141 if (!idEntry) {
142 // no cache entries for id
143 continue;
144 }
145
146 // remove all blob entries from the LRU list
147 for (const auto& blob : idEntry->fBlobs) {
148 fCurrentSize -= blob->size();
149 fBlobList.remove(blob.get());
150 }
151
152 // drop the idEntry itself (unrefs all blobs)
153 fBlobIDCache.remove(msg.fBlobID);
154 }
155 }
156
usedBytes() const157 size_t GrTextBlobRedrawCoordinator::usedBytes() const {
158 SkAutoSpinlock lock{fSpinLock};
159 return fCurrentSize;
160 }
161
isOverBudget() const162 bool GrTextBlobRedrawCoordinator::isOverBudget() const {
163 SkAutoSpinlock lock{fSpinLock};
164 return fCurrentSize > fSizeBudget;
165 }
166
internalCheckPurge(GrTextBlob * blob)167 void GrTextBlobRedrawCoordinator::internalCheckPurge(GrTextBlob* blob) {
168 // First, purge all stale blob IDs.
169 this->internalPurgeStaleBlobs();
170
171 // If we are still over budget, then unref until we are below budget again
172 if (fCurrentSize > fSizeBudget) {
173 TextBlobList::Iter iter;
174 iter.init(fBlobList, TextBlobList::Iter::kTail_IterStart);
175 GrTextBlob* lruBlob = nullptr;
176 while (fCurrentSize > fSizeBudget && (lruBlob = iter.get()) && lruBlob != blob) {
177 // Backup the iterator before removing and unrefing the blob
178 iter.prev();
179
180 this->internalRemove(lruBlob);
181 }
182
183 #ifdef SPEW_BUDGET_MESSAGE
184 if (fCurrentSize > fSizeBudget) {
185 SkDebugf("Single textblob is larger than our whole budget");
186 }
187 #endif
188 }
189 }
190
internalAdd(sk_sp<GrTextBlob> blob)191 sk_sp<GrTextBlob> GrTextBlobRedrawCoordinator::internalAdd(sk_sp<GrTextBlob> blob) {
192 auto id = blob->key().fUniqueID;
193 auto* idEntry = fBlobIDCache.find(id);
194 if (!idEntry) {
195 idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
196 }
197
198 if (sk_sp<GrTextBlob> alreadyIn = idEntry->find(blob->key()); alreadyIn) {
199 blob = std::move(alreadyIn);
200 } else {
201 fBlobList.addToHead(blob.get());
202 fCurrentSize += blob->size();
203 idEntry->addBlob(blob);
204 }
205
206 this->internalCheckPurge(blob.get());
207 return blob;
208 }
209
BlobIDCacheEntry()210 GrTextBlobRedrawCoordinator::BlobIDCacheEntry::BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
211
BlobIDCacheEntry(uint32_t id)212 GrTextBlobRedrawCoordinator::BlobIDCacheEntry::BlobIDCacheEntry(uint32_t id) : fID(id) {}
213
GetKey(const GrTextBlobRedrawCoordinator::BlobIDCacheEntry & entry)214 uint32_t GrTextBlobRedrawCoordinator::BlobIDCacheEntry::GetKey(
215 const GrTextBlobRedrawCoordinator::BlobIDCacheEntry& entry) {
216 return entry.fID;
217 }
218
addBlob(sk_sp<GrTextBlob> blob)219 void GrTextBlobRedrawCoordinator::BlobIDCacheEntry::addBlob(sk_sp<GrTextBlob> blob) {
220 SkASSERT(blob);
221 SkASSERT(blob->key().fUniqueID == fID);
222 SkASSERT(!this->find(blob->key()));
223
224 fBlobs.emplace_back(std::move(blob));
225 }
226
removeBlob(GrTextBlob * blob)227 void GrTextBlobRedrawCoordinator::BlobIDCacheEntry::removeBlob(GrTextBlob* blob) {
228 SkASSERT(blob);
229 SkASSERT(blob->key().fUniqueID == fID);
230
231 auto index = this->findBlobIndex(blob->key());
232 SkASSERT(index >= 0);
233
234 fBlobs.removeShuffle(index);
235 }
236
237 sk_sp<GrTextBlob>
find(const GrTextBlob::Key & key) const238 GrTextBlobRedrawCoordinator::BlobIDCacheEntry::find(const GrTextBlob::Key& key) const {
239 auto index = this->findBlobIndex(key);
240 return index < 0 ? nullptr : fBlobs[index];
241 }
242
findBlobIndex(const GrTextBlob::Key & key) const243 int GrTextBlobRedrawCoordinator::BlobIDCacheEntry::findBlobIndex(const GrTextBlob::Key& key) const {
244 for (int i = 0; i < fBlobs.count(); ++i) {
245 if (fBlobs[i]->key() == key) {
246 return i;
247 }
248 }
249 return -1;
250 }
251