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 | |
10 | DECLARE_SKMESSAGEBUS_MESSAGE(GrTextBlobCache::PurgeBlobMessage) |
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 | |
18 | GrTextBlobCache::GrTextBlobCache(uint32_t messageBusID) |
19 | : fSizeBudget(kDefaultBudget) |
20 | , fMessageBusID(messageBusID) |
21 | , fPurgeBlobInbox(messageBusID) { } |
22 | |
23 | sk_sp<GrTextBlob> |
24 | GrTextBlobCache::makeCachedBlob(const SkGlyphRunList& glyphRunList, const GrTextBlob::Key& key, |
25 | const SkMaskFilterBase::BlurRec& blurRec, |
26 | const SkMatrix& viewMatrix) { |
27 | sk_sp<GrTextBlob> cacheBlob(GrTextBlob::Make(glyphRunList, viewMatrix)); |
28 | cacheBlob->setupKey(key, blurRec, glyphRunList.paint()); |
29 | SkAutoSpinlock lock{fSpinLock}; |
30 | this->internalAdd(cacheBlob); |
31 | glyphRunList.temporaryShuntBlobNotifyAddedToCache(fMessageBusID); |
32 | return cacheBlob; |
33 | } |
34 | |
35 | sk_sp<GrTextBlob> GrTextBlobCache::find(const GrTextBlob::Key& key) { |
36 | SkAutoSpinlock lock{fSpinLock}; |
37 | const BlobIDCacheEntry* idEntry = fBlobIDCache.find(key.fUniqueID); |
38 | if (idEntry == nullptr) { |
39 | return nullptr; |
40 | } |
41 | |
42 | sk_sp<GrTextBlob> blob = idEntry->find(key); |
43 | GrTextBlob* blobPtr = blob.get(); |
44 | if (blobPtr != nullptr && blobPtr != fBlobList.head()) { |
45 | fBlobList.remove(blobPtr); |
46 | fBlobList.addToHead(blobPtr); |
47 | } |
48 | return blob; |
49 | } |
50 | |
51 | void GrTextBlobCache::remove(GrTextBlob* blob) { |
52 | SkAutoSpinlock lock{fSpinLock}; |
53 | this->internalRemove(blob); |
54 | } |
55 | |
56 | void GrTextBlobCache::internalRemove(GrTextBlob* blob) { |
57 | auto id = GrTextBlob::GetKey(*blob).fUniqueID; |
58 | auto* idEntry = fBlobIDCache.find(id); |
59 | SkASSERT(idEntry); |
60 | |
61 | fCurrentSize -= blob->size(); |
62 | fBlobList.remove(blob); |
63 | idEntry->removeBlob(blob); |
64 | if (idEntry->fBlobs.empty()) { |
65 | fBlobIDCache.remove(id); |
66 | } |
67 | } |
68 | |
69 | void GrTextBlobCache::freeAll() { |
70 | SkAutoSpinlock lock{fSpinLock}; |
71 | fBlobIDCache.reset(); |
72 | fBlobList.reset(); |
73 | fCurrentSize = 0; |
74 | } |
75 | |
76 | void GrTextBlobCache::PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID) { |
77 | SkASSERT(blobID != SK_InvalidGenID); |
78 | SkMessageBus<PurgeBlobMessage>::Post(PurgeBlobMessage(blobID, cacheID)); |
79 | } |
80 | |
81 | void GrTextBlobCache::purgeStaleBlobs() { |
82 | SkAutoSpinlock lock{fSpinLock}; |
83 | this->internalPurgeStaleBlobs(); |
84 | } |
85 | |
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 | |
108 | size_t GrTextBlobCache::usedBytes() const { |
109 | SkAutoSpinlock lock{fSpinLock}; |
110 | return fCurrentSize; |
111 | } |
112 | |
113 | bool GrTextBlobCache::isOverBudget() const { |
114 | SkAutoSpinlock lock{fSpinLock}; |
115 | return fCurrentSize > fSizeBudget; |
116 | } |
117 | |
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 | |
142 | void GrTextBlobCache::internalAdd(sk_sp<GrTextBlob> blob) { |
143 | auto id = GrTextBlob::GetKey(*blob).fUniqueID; |
144 | auto* idEntry = fBlobIDCache.find(id); |
145 | if (!idEntry) { |
146 | idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id)); |
147 | } |
148 | |
149 | // Safe to retain a raw ptr temporarily here, because the cache will hold a ref. |
150 | GrTextBlob* rawBlobPtr = blob.get(); |
151 | fBlobList.addToHead(rawBlobPtr); |
152 | fCurrentSize += blob->size(); |
153 | idEntry->addBlob(std::move(blob)); |
154 | |
155 | this->internalCheckPurge(rawBlobPtr); |
156 | } |
157 | |
158 | GrTextBlobCache::BlobIDCacheEntry::BlobIDCacheEntry() : fID(SK_InvalidGenID) {} |
159 | |
160 | GrTextBlobCache::BlobIDCacheEntry::BlobIDCacheEntry(uint32_t id) : fID(id) {} |
161 | |
162 | uint32_t GrTextBlobCache::BlobIDCacheEntry::GetKey(const GrTextBlobCache::BlobIDCacheEntry& entry) { |
163 | return entry.fID; |
164 | } |
165 | |
166 | void GrTextBlobCache::BlobIDCacheEntry::addBlob(sk_sp<GrTextBlob> blob) { |
167 | SkASSERT(blob); |
168 | SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID); |
169 | SkASSERT(!this->find(GrTextBlob::GetKey(*blob))); |
170 | |
171 | fBlobs.emplace_back(std::move(blob)); |
172 | } |
173 | |
174 | void GrTextBlobCache::BlobIDCacheEntry::removeBlob(GrTextBlob* blob) { |
175 | SkASSERT(blob); |
176 | SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID); |
177 | |
178 | auto index = this->findBlobIndex(GrTextBlob::GetKey(*blob)); |
179 | SkASSERT(index >= 0); |
180 | |
181 | fBlobs.removeShuffle(index); |
182 | } |
183 | |
184 | sk_sp<GrTextBlob> GrTextBlobCache::BlobIDCacheEntry::find(const GrTextBlob::Key& key) const { |
185 | auto index = this->findBlobIndex(key); |
186 | return index < 0 ? nullptr : fBlobs[index]; |
187 | } |
188 | |
189 | int GrTextBlobCache::BlobIDCacheEntry::findBlobIndex(const GrTextBlob::Key& key) const { |
190 | for (int i = 0; i < fBlobs.count(); ++i) { |
191 | if (GrTextBlob::GetKey(*fBlobs[i]) == key) { |
192 | return i; |
193 | } |
194 | } |
195 | return -1; |
196 | } |
197 | |