blob: 37489c6b35281a1ddef5e2980e4ddcdbd7b684aa [file] [log] [blame]
joshualittb7133be2015-04-08 09:08:31 -07001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/text/GrTextBlobCache.h"
joshualittb7133be2015-04-08 09:08:31 -07009
Florin Malita4a01ac92017-03-13 16:45:28 -040010DECLARE_SKMESSAGEBUS_MESSAGE(GrTextBlobCache::PurgeBlobMessage)
11
Herb Derby7b4ea9b2020-05-20 17:31:36 -040012// This function is captured by the above macro using implementations from SkMessageBus.h
Chris Dalton9a986cf2018-10-18 15:27:59 -060013static inline bool SkShouldPostMessageToBus(
14 const GrTextBlobCache::PurgeBlobMessage& msg, uint32_t msgBusUniqueID) {
15 return msg.fContextID == msgBusUniqueID;
16}
17
Herb Derby7b4ea9b2020-05-20 17:31:36 -040018GrTextBlobCache::GrTextBlobCache(GrTextBlobCache::PFOverBudgetCB cb, void* data, uint32_t uniqueID)
19 : fCallback(cb)
20 , fData(data)
21 , fSizeBudget(kDefaultBudget)
22 , fUniqueID(uniqueID)
23 , fPurgeBlobInbox(uniqueID) {
24 SkASSERT(cb != nullptr && data != nullptr);
25}
26
joshualittb7133be2015-04-08 09:08:31 -070027GrTextBlobCache::~GrTextBlobCache() {
Robert Phillips303cd582018-02-14 18:54:01 -050028 this->freeAll();
joshualittb7133be2015-04-08 09:08:31 -070029}
30
Herb Derby7b4ea9b2020-05-20 17:31:36 -040031sk_sp<GrTextBlob>
32GrTextBlobCache::makeCachedBlob(const SkGlyphRunList& glyphRunList, const GrTextBlob::Key& key,
33 const SkMaskFilterBase::BlurRec& blurRec,
34 const SkMatrix& viewMatrix, GrColor color, bool forceW) {
35 sk_sp<GrTextBlob> cacheBlob(GrTextBlob::Make(glyphRunList, viewMatrix, color, forceW));
36 cacheBlob->setupKey(key, blurRec, glyphRunList.paint());
37 this->add(cacheBlob);
38 glyphRunList.temporaryShuntBlobNotifyAddedToCache(fUniqueID);
39 return cacheBlob;
40}
41
42sk_sp<GrTextBlob> GrTextBlobCache::find(const GrTextBlob::Key& key) const {
43 const auto* idEntry = fBlobIDCache.find(key.fUniqueID);
44 return idEntry ? idEntry->find(key) : nullptr;
45}
46
47void GrTextBlobCache::remove(GrTextBlob* blob) {
48 auto id = GrTextBlob::GetKey(*blob).fUniqueID;
49 auto* idEntry = fBlobIDCache.find(id);
50 SkASSERT(idEntry);
51
52 fCurrentSize -= blob->size();
53 fBlobList.remove(blob);
54 idEntry->removeBlob(blob);
55 if (idEntry->fBlobs.empty()) {
56 fBlobIDCache.remove(id);
57 }
58}
59
60void GrTextBlobCache::makeMRU(GrTextBlob* blob) {
61 if (fBlobList.head() == blob) {
62 return;
63 }
64
65 fBlobList.remove(blob);
66 fBlobList.addToHead(blob);
67}
68
joshualitt26ffc002015-04-16 11:24:04 -070069void GrTextBlobCache::freeAll() {
Florin Malita33fdb8d2017-03-07 16:51:57 -050070 fBlobIDCache.foreach([this](uint32_t, BlobIDCacheEntry* entry) {
Florin Malitac337c9e2017-03-10 18:02:29 +000071 for (const auto& blob : entry->fBlobs) {
72 fBlobList.remove(blob.get());
Florin Malita33fdb8d2017-03-07 16:51:57 -050073 }
74 });
75
76 fBlobIDCache.reset();
joshualitt20ccd402016-01-05 08:56:56 -080077
Herb Derbyb12175f2018-05-23 16:38:09 -040078 fCurrentSize = 0;
79
joshualitt20ccd402016-01-05 08:56:56 -080080 // There should be no allocations in the memory pool at this point
Florin Malita33fdb8d2017-03-07 16:51:57 -050081 SkASSERT(fBlobList.isEmpty());
joshualitt26ffc002015-04-16 11:24:04 -070082}
Florin Malita4a01ac92017-03-13 16:45:28 -040083
Herb Derby7b4ea9b2020-05-20 17:31:36 -040084void GrTextBlobCache::setBudget(size_t budget) {
85 fSizeBudget = budget;
86 this->checkPurge();
87}
88
Jim Van Verth474d6872017-12-14 13:00:05 -050089void GrTextBlobCache::PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID) {
90 SkASSERT(blobID != SK_InvalidGenID);
Brian Salomon238069b2018-07-11 15:58:57 -040091 SkMessageBus<PurgeBlobMessage>::Post(PurgeBlobMessage(blobID, cacheID));
Florin Malita4a01ac92017-03-13 16:45:28 -040092}
Jim Van Verth76d917c2017-12-13 09:26:37 -050093
94void GrTextBlobCache::purgeStaleBlobs() {
95 SkTArray<PurgeBlobMessage> msgs;
96 fPurgeBlobInbox.poll(&msgs);
97
98 for (const auto& msg : msgs) {
Brian Salomon238069b2018-07-11 15:58:57 -040099 auto* idEntry = fBlobIDCache.find(msg.fBlobID);
Jim Van Verth76d917c2017-12-13 09:26:37 -0500100 if (!idEntry) {
101 // no cache entries for id
102 continue;
103 }
104
105 // remove all blob entries from the LRU list
106 for (const auto& blob : idEntry->fBlobs) {
Herb Derbyb12175f2018-05-23 16:38:09 -0400107 fCurrentSize -= blob->size();
Jim Van Verth76d917c2017-12-13 09:26:37 -0500108 fBlobList.remove(blob.get());
109 }
110
111 // drop the idEntry itself (unrefs all blobs)
Brian Salomon238069b2018-07-11 15:58:57 -0400112 fBlobIDCache.remove(msg.fBlobID);
Jim Van Verth76d917c2017-12-13 09:26:37 -0500113 }
114}
115
Herb Derby86240592018-05-24 16:12:31 -0400116void GrTextBlobCache::checkPurge(GrTextBlob* blob) {
Jim Van Verth76d917c2017-12-13 09:26:37 -0500117 // First, purge all stale blob IDs.
118 this->purgeStaleBlobs();
119
120 // If we are still over budget, then unref until we are below budget again
Herb Derbyb12175f2018-05-23 16:38:09 -0400121 if (fCurrentSize > fSizeBudget) {
Herb Derby7b4ea9b2020-05-20 17:31:36 -0400122 TextBlobList::Iter iter;
123 iter.init(fBlobList, TextBlobList::Iter::kTail_IterStart);
Herb Derby86240592018-05-24 16:12:31 -0400124 GrTextBlob* lruBlob = nullptr;
Herb Derbyb12175f2018-05-23 16:38:09 -0400125 while (fCurrentSize > fSizeBudget && (lruBlob = iter.get()) && lruBlob != blob) {
Jim Van Verth76d917c2017-12-13 09:26:37 -0500126 // Backup the iterator before removing and unrefing the blob
127 iter.prev();
128
129 this->remove(lruBlob);
130 }
131
132 // If we break out of the loop with lruBlob == blob, then we haven't purged enough
133 // use the call back and try to free some more. If we are still overbudget after this,
134 // then this single textblob is over our budget
135 if (blob && lruBlob == blob) {
136 (*fCallback)(fData);
137 }
138
Herb Derby7b4ea9b2020-05-20 17:31:36 -0400139 #ifdef SPEW_BUDGET_MESSAGE
Herb Derbyb12175f2018-05-23 16:38:09 -0400140 if (fCurrentSize > fSizeBudget) {
Jim Van Verth76d917c2017-12-13 09:26:37 -0500141 SkDebugf("Single textblob is larger than our whole budget");
142 }
Herb Derby7b4ea9b2020-05-20 17:31:36 -0400143 #endif
Jim Van Verth76d917c2017-12-13 09:26:37 -0500144 }
145}
146
Herb Derby7b4ea9b2020-05-20 17:31:36 -0400147void GrTextBlobCache::add(sk_sp<GrTextBlob> blob) {
148 auto id = GrTextBlob::GetKey(*blob).fUniqueID;
149 auto* idEntry = fBlobIDCache.find(id);
150 if (!idEntry) {
151 idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
152 }
Jim Van Verth76d917c2017-12-13 09:26:37 -0500153
Herb Derby7b4ea9b2020-05-20 17:31:36 -0400154 // Safe to retain a raw ptr temporarily here, because the cache will hold a ref.
155 GrTextBlob* rawBlobPtr = blob.get();
156 fBlobList.addToHead(rawBlobPtr);
157 fCurrentSize += blob->size();
158 idEntry->addBlob(std::move(blob));
Jim Van Verth76d917c2017-12-13 09:26:37 -0500159
Herb Derby7b4ea9b2020-05-20 17:31:36 -0400160 this->checkPurge(rawBlobPtr);
161}
162
163GrTextBlobCache::BlobIDCacheEntry::BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
164
165GrTextBlobCache::BlobIDCacheEntry::BlobIDCacheEntry(uint32_t id) : fID(id) {}
166
167uint32_t GrTextBlobCache::BlobIDCacheEntry::GetKey(const GrTextBlobCache::BlobIDCacheEntry& entry) {
168 return entry.fID;
169}
170
171void GrTextBlobCache::BlobIDCacheEntry::addBlob(sk_sp<GrTextBlob> blob) {
172 SkASSERT(blob);
173 SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID);
174 SkASSERT(!this->find(GrTextBlob::GetKey(*blob)));
175
176 fBlobs.emplace_back(std::move(blob));
177}
178
179void GrTextBlobCache::BlobIDCacheEntry::removeBlob(GrTextBlob* blob) {
180 SkASSERT(blob);
181 SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID);
182
183 auto index = this->findBlobIndex(GrTextBlob::GetKey(*blob));
184 SkASSERT(index >= 0);
185
186 fBlobs.removeShuffle(index);
187}
188
189sk_sp<GrTextBlob> GrTextBlobCache::BlobIDCacheEntry::find(const GrTextBlob::Key& key) const {
190 auto index = this->findBlobIndex(key);
191 return index < 0 ? nullptr : fBlobs[index];
192}
193
194int GrTextBlobCache::BlobIDCacheEntry::findBlobIndex(const GrTextBlob::Key& key) const {
195 for (int i = 0; i < fBlobs.count(); ++i) {
196 if (GrTextBlob::GetKey(*fBlobs[i]) == key) {
197 return i;
198 }
199 }
200 return -1;
201}