blob: 90046c769d98ac260a37b358f21994232d7e0782 [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
8#ifndef GrTextBlobCache_DEFINED
9#define GrTextBlobCache_DEFINED
10
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkRefCnt.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/private/SkTArray.h"
13#include "include/private/SkTHash.h"
Ben Wagner21bca282019-05-15 10:15:52 -040014#include "src/core/SkMessageBus.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/core/SkTextBlobPriv.h"
16#include "src/gpu/text/GrTextBlob.h"
joshualittb7133be2015-04-08 09:08:31 -070017
18class GrTextBlobCache {
19public:
joshualitt0db6dfa2015-04-10 07:01:30 -070020 /**
21 * The callback function used by the cache when it is still over budget after a purge. The
22 * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
23 */
24 typedef void (*PFOverBudgetCB)(void* data);
25
Herb Derbyb12175f2018-05-23 16:38:09 -040026 GrTextBlobCache(PFOverBudgetCB cb, void* data, uint32_t uniqueID)
27 : fCallback(cb)
joshualitt17d833b2015-08-03 10:17:44 -070028 , fData(data)
Herb Derbyb12175f2018-05-23 16:38:09 -040029 , fSizeBudget(kDefaultBudget)
Jim Van Verth474d6872017-12-14 13:00:05 -050030 , fUniqueID(uniqueID)
31 , fPurgeBlobInbox(uniqueID) {
joshualitt0db6dfa2015-04-10 07:01:30 -070032 SkASSERT(cb && data);
33 }
joshualittb7133be2015-04-08 09:08:31 -070034 ~GrTextBlobCache();
35
Herb Derbya00da612019-03-04 17:10:01 -050036 sk_sp<GrTextBlob> makeBlob(const SkGlyphRunList& glyphRunList,
Herb Derbyeba195f2019-12-03 16:44:47 -050037 GrStrikeCache* strikeCache,
Herb Derbye9f691d2019-12-04 12:11:13 -050038 const SkMatrix& viewMatrix,
Herb Derbya00da612019-03-04 17:10:01 -050039 GrColor color,
Herb Derbyeba195f2019-12-03 16:44:47 -050040 bool forceW) {
41 return GrTextBlob::Make(
Herb Derbye9f691d2019-12-04 12:11:13 -050042 glyphRunList.totalGlyphCount(),
43 strikeCache,
44 viewMatrix,
45 glyphRunList.origin(),
46 color,
47 forceW);
Herb Derbycddab252018-07-16 11:19:04 -040048 }
49
Herb Derbyb935cf82018-07-26 16:54:18 -040050 sk_sp<GrTextBlob> makeCachedBlob(const SkGlyphRunList& glyphRunList,
Herb Derbyeba195f2019-12-03 16:44:47 -050051 GrStrikeCache* strikeCache,
Herb Derbycddab252018-07-16 11:19:04 -040052 const GrTextBlob::Key& key,
53 const SkMaskFilterBase::BlurRec& blurRec,
Herb Derbye9f691d2019-12-04 12:11:13 -050054 const SkMatrix& viewMatrix,
Herb Derbya00da612019-03-04 17:10:01 -050055 GrColor color,
Herb Derbyeba195f2019-12-03 16:44:47 -050056 bool forceW) {
57 sk_sp<GrTextBlob> cacheBlob(
Herb Derbye9f691d2019-12-04 12:11:13 -050058 this->makeBlob(glyphRunList, strikeCache, viewMatrix, color, forceW));
Herb Derbyeba195f2019-12-03 16:44:47 -050059 cacheBlob->setupKey(key, blurRec, glyphRunList.paint());
Herb Derbycddab252018-07-16 11:19:04 -040060 this->add(cacheBlob);
Herb Derbyb935cf82018-07-26 16:54:18 -040061 glyphRunList.temporaryShuntBlobNotifyAddedToCache(fUniqueID);
Herb Derbycddab252018-07-16 11:19:04 -040062 return cacheBlob;
63 }
64
Herb Derby86240592018-05-24 16:12:31 -040065 sk_sp<GrTextBlob> find(const GrTextBlob::Key& key) const {
Florin Malita33fdb8d2017-03-07 16:51:57 -050066 const auto* idEntry = fBlobIDCache.find(key.fUniqueID);
67 return idEntry ? idEntry->find(key) : nullptr;
joshualittb7133be2015-04-08 09:08:31 -070068 }
69
Herb Derby86240592018-05-24 16:12:31 -040070 void remove(GrTextBlob* blob) {
71 auto id = GrTextBlob::GetKey(*blob).fUniqueID;
Florin Malita33fdb8d2017-03-07 16:51:57 -050072 auto* idEntry = fBlobIDCache.find(id);
73 SkASSERT(idEntry);
74
Herb Derbyb12175f2018-05-23 16:38:09 -040075 fCurrentSize -= blob->size();
Florin Malitac337c9e2017-03-10 18:02:29 +000076 fBlobList.remove(blob);
Florin Malita33fdb8d2017-03-07 16:51:57 -050077 idEntry->removeBlob(blob);
78 if (idEntry->fBlobs.empty()) {
79 fBlobIDCache.remove(id);
80 }
joshualittb7133be2015-04-08 09:08:31 -070081 }
82
Herb Derby86240592018-05-24 16:12:31 -040083 void makeMRU(GrTextBlob* blob) {
joshualittb7133be2015-04-08 09:08:31 -070084 if (fBlobList.head() == blob) {
85 return;
86 }
87
88 fBlobList.remove(blob);
89 fBlobList.addToHead(blob);
90 }
91
joshualitt26ffc002015-04-16 11:24:04 -070092 void freeAll();
93
joshualittb7133be2015-04-08 09:08:31 -070094 // TODO move to SkTextBlob
joshualitt259fbf12015-07-21 11:39:34 -070095 static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
halcanary33779752015-10-27 14:01:05 -070096 SkTextBlobRunIterator itCounter(blob);
joshualittb7133be2015-04-08 09:08:31 -070097 for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
98 *glyphCount += itCounter.glyphCount();
99 }
100 }
101
joshualitt17d833b2015-08-03 10:17:44 -0700102 void setBudget(size_t budget) {
Herb Derbyb12175f2018-05-23 16:38:09 -0400103 fSizeBudget = budget;
joshualitt17d833b2015-08-03 10:17:44 -0700104 this->checkPurge();
105 }
106
Florin Malita4a01ac92017-03-13 16:45:28 -0400107 struct PurgeBlobMessage {
Brian Salomon238069b2018-07-11 15:58:57 -0400108 PurgeBlobMessage(uint32_t blobID, uint32_t contextUniqueID)
109 : fBlobID(blobID), fContextID(contextUniqueID) {}
Brian Salomon238069b2018-07-11 15:58:57 -0400110
111 uint32_t fBlobID;
112 uint32_t fContextID;
Florin Malita4a01ac92017-03-13 16:45:28 -0400113 };
114
Jim Van Verth474d6872017-12-14 13:00:05 -0500115 static void PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID);
Florin Malita4a01ac92017-03-13 16:45:28 -0400116
Jim Van Verth76d917c2017-12-13 09:26:37 -0500117 void purgeStaleBlobs();
118
Khushal71652e22018-10-29 13:05:36 -0700119 size_t usedBytes() const { return fCurrentSize; }
120
joshualitt259fbf12015-07-21 11:39:34 -0700121private:
Herb Derby86240592018-05-24 16:12:31 -0400122 using BitmapBlobList = SkTInternalLList<GrTextBlob>;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500123
124 struct BlobIDCacheEntry {
125 BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
126 explicit BlobIDCacheEntry(uint32_t id) : fID(id) {}
127
128 static uint32_t GetKey(const BlobIDCacheEntry& entry) {
129 return entry.fID;
130 }
131
Herb Derby86240592018-05-24 16:12:31 -0400132 void addBlob(sk_sp<GrTextBlob> blob) {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500133 SkASSERT(blob);
Herb Derby86240592018-05-24 16:12:31 -0400134 SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID);
135 SkASSERT(!this->find(GrTextBlob::GetKey(*blob)));
Florin Malita33fdb8d2017-03-07 16:51:57 -0500136
Florin Malitac337c9e2017-03-10 18:02:29 +0000137 fBlobs.emplace_back(std::move(blob));
Florin Malita33fdb8d2017-03-07 16:51:57 -0500138 }
139
Herb Derby86240592018-05-24 16:12:31 -0400140 void removeBlob(GrTextBlob* blob) {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500141 SkASSERT(blob);
Herb Derby86240592018-05-24 16:12:31 -0400142 SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID);
Florin Malita33fdb8d2017-03-07 16:51:57 -0500143
Herb Derby86240592018-05-24 16:12:31 -0400144 auto index = this->findBlobIndex(GrTextBlob::GetKey(*blob));
Florin Malita33fdb8d2017-03-07 16:51:57 -0500145 SkASSERT(index >= 0);
146
147 fBlobs.removeShuffle(index);
148 }
149
Herb Derby86240592018-05-24 16:12:31 -0400150 sk_sp<GrTextBlob> find(const GrTextBlob::Key& key) const {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500151 auto index = this->findBlobIndex(key);
152 return index < 0 ? nullptr : fBlobs[index];
153 }
154
Herb Derby86240592018-05-24 16:12:31 -0400155 int findBlobIndex(const GrTextBlob::Key& key) const{
Florin Malita33fdb8d2017-03-07 16:51:57 -0500156 for (int i = 0; i < fBlobs.count(); ++i) {
Herb Derby86240592018-05-24 16:12:31 -0400157 if (GrTextBlob::GetKey(*fBlobs[i]) == key) {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500158 return i;
159 }
160 }
161 return -1;
162 }
163
Mike Kleinba094e52019-12-05 10:45:58 -0600164 uint32_t fID;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500165 // Current clients don't generate multiple GrAtlasTextBlobs per SkTextBlob, so an array w/
166 // linear search is acceptable. If usage changes, we should re-evaluate this structure.
Mike Kleinba094e52019-12-05 10:45:58 -0600167 SkSTArray<1, sk_sp<GrTextBlob>> fBlobs;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500168 };
joshualittb7133be2015-04-08 09:08:31 -0700169
Herb Derby86240592018-05-24 16:12:31 -0400170 void add(sk_sp<GrTextBlob> blob) {
171 auto id = GrTextBlob::GetKey(*blob).fUniqueID;
Florin Malitac337c9e2017-03-10 18:02:29 +0000172 auto* idEntry = fBlobIDCache.find(id);
173 if (!idEntry) {
174 idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
175 }
176
177 // Safe to retain a raw ptr temporarily here, because the cache will hold a ref.
Herb Derby86240592018-05-24 16:12:31 -0400178 GrTextBlob* rawBlobPtr = blob.get();
Florin Malitac337c9e2017-03-10 18:02:29 +0000179 fBlobList.addToHead(rawBlobPtr);
Herb Derbyb12175f2018-05-23 16:38:09 -0400180 fCurrentSize += blob->size();
Florin Malitac337c9e2017-03-10 18:02:29 +0000181 idEntry->addBlob(std::move(blob));
182
183 this->checkPurge(rawBlobPtr);
184 }
185
Herb Derby86240592018-05-24 16:12:31 -0400186 void checkPurge(GrTextBlob* blob = nullptr);
joshualitt17d833b2015-08-03 10:17:44 -0700187
Florin Malita012893b2017-07-11 09:31:22 -0400188 static const int kMinGrowthSize = 1 << 16;
joshualitt17d833b2015-08-03 10:17:44 -0700189 static const int kDefaultBudget = 1 << 22;
joshualittb7133be2015-04-08 09:08:31 -0700190 BitmapBlobList fBlobList;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500191 SkTHashMap<uint32_t, BlobIDCacheEntry> fBlobIDCache;
joshualitt0db6dfa2015-04-10 07:01:30 -0700192 PFOverBudgetCB fCallback;
193 void* fData;
Herb Derbyb12175f2018-05-23 16:38:09 -0400194 size_t fSizeBudget;
195 size_t fCurrentSize{0};
Jim Van Verth474d6872017-12-14 13:00:05 -0500196 uint32_t fUniqueID; // unique id to use for messaging
Florin Malita4a01ac92017-03-13 16:45:28 -0400197 SkMessageBus<PurgeBlobMessage>::Inbox fPurgeBlobInbox;
joshualittb7133be2015-04-08 09:08:31 -0700198};
199
200#endif