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