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/GrTextBlobCache.h"
9
DECLARE_SKMESSAGEBUS_MESSAGE(GrTextBlobCache::PurgeBlobMessage,uint32_t,true)10 DECLARE_SKMESSAGEBUS_MESSAGE(GrTextBlobCache::PurgeBlobMessage, uint32_t, true)
11
12 // This function is captured by the above macro using implementations from SkMessageBus.h
13 static inline bool SkShouldPostMessageToBus(
14 const GrTextBlobCache::PurgeBlobMessage& msg, uint32_t msgBusUniqueID) {
15 return msg.fContextID == msgBusUniqueID;
16 }
17
GrTextBlobCache(uint32_t messageBusID)18 GrTextBlobCache::GrTextBlobCache(uint32_t messageBusID)
19 : fSizeBudget(kDefaultBudget)
20 , fMessageBusID(messageBusID)
21 , fPurgeBlobInbox(messageBusID) { }
22
addOrReturnExisting(const SkGlyphRunList & glyphRunList,sk_sp<GrTextBlob> blob)23 sk_sp<GrTextBlob> GrTextBlobCache::addOrReturnExisting(
24 const SkGlyphRunList& glyphRunList, sk_sp<GrTextBlob> blob) {
25 SkAutoSpinlock lock{fSpinLock};
26 blob = this->internalAdd(std::move(blob));
27 glyphRunList.temporaryShuntBlobNotifyAddedToCache(fMessageBusID);
28 return blob;
29 }
30
find(const GrTextBlob::Key & key)31 sk_sp<GrTextBlob> GrTextBlobCache::find(const GrTextBlob::Key& key) {
32 SkAutoSpinlock lock{fSpinLock};
33 const BlobIDCacheEntry* idEntry = fBlobIDCache.find(key.fUniqueID);
34 if (idEntry == nullptr) {
35 return nullptr;
36 }
37
38 sk_sp<GrTextBlob> blob = idEntry->find(key);
39 GrTextBlob* blobPtr = blob.get();
40 if (blobPtr != nullptr && blobPtr != fBlobList.head()) {
41 fBlobList.remove(blobPtr);
42 fBlobList.addToHead(blobPtr);
43 }
44 return blob;
45 }
46
remove(GrTextBlob * blob)47 void GrTextBlobCache::remove(GrTextBlob* blob) {
48 SkAutoSpinlock lock{fSpinLock};
49 this->internalRemove(blob);
50 }
51
internalRemove(GrTextBlob * blob)52 void GrTextBlobCache::internalRemove(GrTextBlob* blob) {
53 auto id = blob->key().fUniqueID;
54 auto* idEntry = fBlobIDCache.find(id);
55
56 if (idEntry != nullptr) {
57 sk_sp<GrTextBlob> stillExists = idEntry->find(blob->key());
58 if (blob == stillExists.get()) {
59 fCurrentSize -= blob->size();
60 fBlobList.remove(blob);
61 idEntry->removeBlob(blob);
62 if (idEntry->fBlobs.empty()) {
63 fBlobIDCache.remove(id);
64 }
65 }
66 }
67 }
68
freeAll()69 void GrTextBlobCache::freeAll() {
70 SkAutoSpinlock lock{fSpinLock};
71 fBlobIDCache.reset();
72 fBlobList.reset();
73 fCurrentSize = 0;
74 }
75
PostPurgeBlobMessage(uint32_t blobID,uint32_t cacheID)76 void GrTextBlobCache::PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID) {
77 SkASSERT(blobID != SK_InvalidGenID);
78 SkMessageBus<PurgeBlobMessage, uint32_t>::Post(PurgeBlobMessage(blobID, cacheID));
79 }
80
purgeStaleBlobs()81 void GrTextBlobCache::purgeStaleBlobs() {
82 SkAutoSpinlock lock{fSpinLock};
83 this->internalPurgeStaleBlobs();
84 }
85
internalPurgeStaleBlobs()86 void GrTextBlobCache::internalPurgeStaleBlobs() {
87 SkTArray<PurgeBlobMessage> msgs;
88 fPurgeBlobInbox.poll(&msgs);
89
90 for (const auto& msg : msgs) {
91 auto* idEntry = fBlobIDCache.find(msg.fBlobID);
92 if (!idEntry) {
93 // no cache entries for id
94 continue;
95 }
96
97 // remove all blob entries from the LRU list
98 for (const auto& blob : idEntry->fBlobs) {
99 fCurrentSize -= blob->size();
100 fBlobList.remove(blob.get());
101 }
102
103 // drop the idEntry itself (unrefs all blobs)
104 fBlobIDCache.remove(msg.fBlobID);
105 }
106 }
107
usedBytes() const108 size_t GrTextBlobCache::usedBytes() const {
109 SkAutoSpinlock lock{fSpinLock};
110 return fCurrentSize;
111 }
112
isOverBudget() const113 bool GrTextBlobCache::isOverBudget() const {
114 SkAutoSpinlock lock{fSpinLock};
115 return fCurrentSize > fSizeBudget;
116 }
117
internalCheckPurge(GrTextBlob * blob)118 void GrTextBlobCache::internalCheckPurge(GrTextBlob* blob) {
119 // First, purge all stale blob IDs.
120 this->internalPurgeStaleBlobs();
121
122 // If we are still over budget, then unref until we are below budget again
123 if (fCurrentSize > fSizeBudget) {
124 TextBlobList::Iter iter;
125 iter.init(fBlobList, TextBlobList::Iter::kTail_IterStart);
126 GrTextBlob* lruBlob = nullptr;
127 while (fCurrentSize > fSizeBudget && (lruBlob = iter.get()) && lruBlob != blob) {
128 // Backup the iterator before removing and unrefing the blob
129 iter.prev();
130
131 this->internalRemove(lruBlob);
132 }
133
134 #ifdef SPEW_BUDGET_MESSAGE
135 if (fCurrentSize > fSizeBudget) {
136 SkDebugf("Single textblob is larger than our whole budget");
137 }
138 #endif
139 }
140 }
141
internalAdd(sk_sp<GrTextBlob> blob)142 sk_sp<GrTextBlob> GrTextBlobCache::internalAdd(sk_sp<GrTextBlob> blob) {
143 auto id = blob->key().fUniqueID;
144 auto* idEntry = fBlobIDCache.find(id);
145 if (!idEntry) {
146 idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
147 }
148
149 if (sk_sp<GrTextBlob> alreadyIn = idEntry->find(blob->key()); alreadyIn) {
150 blob = std::move(alreadyIn);
151 } else {
152 fBlobList.addToHead(blob.get());
153 fCurrentSize += blob->size();
154 idEntry->addBlob(blob);
155 }
156
157 this->internalCheckPurge(blob.get());
158 return blob;
159 }
160
BlobIDCacheEntry()161 GrTextBlobCache::BlobIDCacheEntry::BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
162
BlobIDCacheEntry(uint32_t id)163 GrTextBlobCache::BlobIDCacheEntry::BlobIDCacheEntry(uint32_t id) : fID(id) {}
164
GetKey(const GrTextBlobCache::BlobIDCacheEntry & entry)165 uint32_t GrTextBlobCache::BlobIDCacheEntry::GetKey(const GrTextBlobCache::BlobIDCacheEntry& entry) {
166 return entry.fID;
167 }
168
addBlob(sk_sp<GrTextBlob> blob)169 void GrTextBlobCache::BlobIDCacheEntry::addBlob(sk_sp<GrTextBlob> blob) {
170 SkASSERT(blob);
171 SkASSERT(blob->key().fUniqueID == fID);
172 SkASSERT(!this->find(blob->key()));
173
174 fBlobs.emplace_back(std::move(blob));
175 }
176
removeBlob(GrTextBlob * blob)177 void GrTextBlobCache::BlobIDCacheEntry::removeBlob(GrTextBlob* blob) {
178 SkASSERT(blob);
179 SkASSERT(blob->key().fUniqueID == fID);
180
181 auto index = this->findBlobIndex(blob->key());
182 SkASSERT(index >= 0);
183
184 fBlobs.removeShuffle(index);
185 }
186
find(const GrTextBlob::Key & key) const187 sk_sp<GrTextBlob> GrTextBlobCache::BlobIDCacheEntry::find(const GrTextBlob::Key& key) const {
188 auto index = this->findBlobIndex(key);
189 return index < 0 ? nullptr : fBlobs[index];
190 }
191
findBlobIndex(const GrTextBlob::Key & key) const192 int GrTextBlobCache::BlobIDCacheEntry::findBlobIndex(const GrTextBlob::Key& key) const {
193 for (int i = 0; i < fBlobs.count(); ++i) {
194 if (fBlobs[i]->key() == key) {
195 return i;
196 }
197 }
198 return -1;
199 }
200