blob: 72743f84baf524519741beb4c3645d904d3f20ee [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
Herb Derby26cbe512018-05-24 14:39:01 -040011#include "GrTextContext.h"
Florin Malita4a01ac92017-03-13 16:45:28 -040012#include "SkMessageBus.h"
Florin Malitac337c9e2017-03-10 18:02:29 +000013#include "SkRefCnt.h"
Florin Malita33fdb8d2017-03-07 16:51:57 -050014#include "SkTArray.h"
halcanary33779752015-10-27 14:01:05 -070015#include "SkTextBlobRunIterator.h"
Florin Malita33fdb8d2017-03-07 16:51:57 -050016#include "SkTHash.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
36 // creates an uncached blob
Herb Derby86240592018-05-24 16:12:31 -040037 sk_sp<GrTextBlob> makeBlob(int glyphCount, int runCount) {
38 return GrTextBlob::Make(glyphCount, runCount);
Florin Malitadb3ceb82017-03-09 14:21:44 -050039 }
40
Herb Derby86240592018-05-24 16:12:31 -040041 sk_sp<GrTextBlob> makeBlob(const SkTextBlob* blob) {
Florin Malita3304c442017-03-09 22:42:58 +000042 int glyphCount = 0;
43 int runCount = 0;
44 BlobGlyphCount(&glyphCount, &runCount, blob);
Herb Derby86240592018-05-24 16:12:31 -040045 return GrTextBlob::Make(glyphCount, runCount);
Florin Malitac337c9e2017-03-10 18:02:29 +000046 }
47
Herb Derby86240592018-05-24 16:12:31 -040048 sk_sp<GrTextBlob> makeCachedBlob(const SkTextBlob* blob,
49 const GrTextBlob::Key& key,
Mike Reed80747ef2018-01-23 15:29:32 -050050 const SkMaskFilterBase::BlurRec& blurRec,
Florin Malitac337c9e2017-03-10 18:02:29 +000051 const SkPaint& paint) {
Herb Derby86240592018-05-24 16:12:31 -040052 sk_sp<GrTextBlob> cacheBlob(this->makeBlob(blob));
joshualitt92303772016-02-10 11:55:52 -080053 cacheBlob->setupKey(key, blurRec, paint);
joshualittb7133be2015-04-08 09:08:31 -070054 this->add(cacheBlob);
Jim Van Verth474d6872017-12-14 13:00:05 -050055 blob->notifyAddedToCache(fUniqueID);
joshualittb7133be2015-04-08 09:08:31 -070056 return cacheBlob;
57 }
58
Herb Derbycddab252018-07-16 11:19:04 -040059 sk_sp<GrTextBlob> makeBlob(SkGlyphRunList* glyphRunList) {
60 return GrTextBlob::Make(glyphRunList->totalGlyphCount(), glyphRunList->size());
61 }
62
63 sk_sp<GrTextBlob> makeCachedBlob(SkGlyphRunList* glyphRunList,
64 const GrTextBlob::Key& key,
65 const SkMaskFilterBase::BlurRec& blurRec,
66 const SkPaint& paint) {
67 sk_sp<GrTextBlob> cacheBlob(makeBlob(glyphRunList));
68 cacheBlob->setupKey(key, blurRec, paint);
69 this->add(cacheBlob);
70 glyphRunList->temporaryShuntBlobNotifyAddedToCache(fUniqueID);
71 return cacheBlob;
72 }
73
Herb Derby86240592018-05-24 16:12:31 -040074 sk_sp<GrTextBlob> find(const GrTextBlob::Key& key) const {
Florin Malita33fdb8d2017-03-07 16:51:57 -050075 const auto* idEntry = fBlobIDCache.find(key.fUniqueID);
76 return idEntry ? idEntry->find(key) : nullptr;
joshualittb7133be2015-04-08 09:08:31 -070077 }
78
Herb Derby86240592018-05-24 16:12:31 -040079 void remove(GrTextBlob* blob) {
80 auto id = GrTextBlob::GetKey(*blob).fUniqueID;
Florin Malita33fdb8d2017-03-07 16:51:57 -050081 auto* idEntry = fBlobIDCache.find(id);
82 SkASSERT(idEntry);
83
Herb Derbyb12175f2018-05-23 16:38:09 -040084 fCurrentSize -= blob->size();
Florin Malitac337c9e2017-03-10 18:02:29 +000085 fBlobList.remove(blob);
Florin Malita33fdb8d2017-03-07 16:51:57 -050086 idEntry->removeBlob(blob);
87 if (idEntry->fBlobs.empty()) {
88 fBlobIDCache.remove(id);
89 }
joshualittb7133be2015-04-08 09:08:31 -070090 }
91
Herb Derby86240592018-05-24 16:12:31 -040092 void makeMRU(GrTextBlob* blob) {
joshualittb7133be2015-04-08 09:08:31 -070093 if (fBlobList.head() == blob) {
94 return;
95 }
96
97 fBlobList.remove(blob);
98 fBlobList.addToHead(blob);
99 }
100
joshualitt26ffc002015-04-16 11:24:04 -0700101 void freeAll();
102
joshualittb7133be2015-04-08 09:08:31 -0700103 // TODO move to SkTextBlob
joshualitt259fbf12015-07-21 11:39:34 -0700104 static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
halcanary33779752015-10-27 14:01:05 -0700105 SkTextBlobRunIterator itCounter(blob);
joshualittb7133be2015-04-08 09:08:31 -0700106 for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
107 *glyphCount += itCounter.glyphCount();
108 }
109 }
110
joshualitt17d833b2015-08-03 10:17:44 -0700111 void setBudget(size_t budget) {
Herb Derbyb12175f2018-05-23 16:38:09 -0400112 fSizeBudget = budget;
joshualitt17d833b2015-08-03 10:17:44 -0700113 this->checkPurge();
114 }
115
Florin Malita4a01ac92017-03-13 16:45:28 -0400116 struct PurgeBlobMessage {
Brian Salomon238069b2018-07-11 15:58:57 -0400117 PurgeBlobMessage(uint32_t blobID, uint32_t contextUniqueID)
118 : fBlobID(blobID), fContextID(contextUniqueID) {}
119 bool shouldSend(uint32_t inboxID) const { return fContextID == inboxID; }
120
121 uint32_t fBlobID;
122 uint32_t fContextID;
Florin Malita4a01ac92017-03-13 16:45:28 -0400123 };
124
Jim Van Verth474d6872017-12-14 13:00:05 -0500125 static void PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID);
Florin Malita4a01ac92017-03-13 16:45:28 -0400126
Jim Van Verth76d917c2017-12-13 09:26:37 -0500127 void purgeStaleBlobs();
128
joshualitt259fbf12015-07-21 11:39:34 -0700129private:
Herb Derby86240592018-05-24 16:12:31 -0400130 using BitmapBlobList = SkTInternalLList<GrTextBlob>;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500131
132 struct BlobIDCacheEntry {
133 BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
134 explicit BlobIDCacheEntry(uint32_t id) : fID(id) {}
135
136 static uint32_t GetKey(const BlobIDCacheEntry& entry) {
137 return entry.fID;
138 }
139
Herb Derby86240592018-05-24 16:12:31 -0400140 void addBlob(sk_sp<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);
143 SkASSERT(!this->find(GrTextBlob::GetKey(*blob)));
Florin Malita33fdb8d2017-03-07 16:51:57 -0500144
Florin Malitac337c9e2017-03-10 18:02:29 +0000145 fBlobs.emplace_back(std::move(blob));
Florin Malita33fdb8d2017-03-07 16:51:57 -0500146 }
147
Herb Derby86240592018-05-24 16:12:31 -0400148 void removeBlob(GrTextBlob* blob) {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500149 SkASSERT(blob);
Herb Derby86240592018-05-24 16:12:31 -0400150 SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID);
Florin Malita33fdb8d2017-03-07 16:51:57 -0500151
Herb Derby86240592018-05-24 16:12:31 -0400152 auto index = this->findBlobIndex(GrTextBlob::GetKey(*blob));
Florin Malita33fdb8d2017-03-07 16:51:57 -0500153 SkASSERT(index >= 0);
154
155 fBlobs.removeShuffle(index);
156 }
157
Herb Derby86240592018-05-24 16:12:31 -0400158 sk_sp<GrTextBlob> find(const GrTextBlob::Key& key) const {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500159 auto index = this->findBlobIndex(key);
160 return index < 0 ? nullptr : fBlobs[index];
161 }
162
Herb Derby86240592018-05-24 16:12:31 -0400163 int findBlobIndex(const GrTextBlob::Key& key) const{
Florin Malita33fdb8d2017-03-07 16:51:57 -0500164 for (int i = 0; i < fBlobs.count(); ++i) {
Herb Derby86240592018-05-24 16:12:31 -0400165 if (GrTextBlob::GetKey(*fBlobs[i]) == key) {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500166 return i;
167 }
168 }
169 return -1;
170 }
171
172 uint32_t fID;
173 // Current clients don't generate multiple GrAtlasTextBlobs per SkTextBlob, so an array w/
174 // linear search is acceptable. If usage changes, we should re-evaluate this structure.
Herb Derby86240592018-05-24 16:12:31 -0400175 SkSTArray<1, sk_sp<GrTextBlob>, true> fBlobs;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500176 };
joshualittb7133be2015-04-08 09:08:31 -0700177
Herb Derby86240592018-05-24 16:12:31 -0400178 void add(sk_sp<GrTextBlob> blob) {
179 auto id = GrTextBlob::GetKey(*blob).fUniqueID;
Florin Malitac337c9e2017-03-10 18:02:29 +0000180 auto* idEntry = fBlobIDCache.find(id);
181 if (!idEntry) {
182 idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
183 }
184
185 // Safe to retain a raw ptr temporarily here, because the cache will hold a ref.
Herb Derby86240592018-05-24 16:12:31 -0400186 GrTextBlob* rawBlobPtr = blob.get();
Florin Malitac337c9e2017-03-10 18:02:29 +0000187 fBlobList.addToHead(rawBlobPtr);
Herb Derbyb12175f2018-05-23 16:38:09 -0400188 fCurrentSize += blob->size();
Florin Malitac337c9e2017-03-10 18:02:29 +0000189 idEntry->addBlob(std::move(blob));
190
191 this->checkPurge(rawBlobPtr);
192 }
193
Herb Derby86240592018-05-24 16:12:31 -0400194 void checkPurge(GrTextBlob* blob = nullptr);
joshualitt17d833b2015-08-03 10:17:44 -0700195
Florin Malita012893b2017-07-11 09:31:22 -0400196 static const int kMinGrowthSize = 1 << 16;
joshualitt17d833b2015-08-03 10:17:44 -0700197 static const int kDefaultBudget = 1 << 22;
joshualittb7133be2015-04-08 09:08:31 -0700198 BitmapBlobList fBlobList;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500199 SkTHashMap<uint32_t, BlobIDCacheEntry> fBlobIDCache;
joshualitt0db6dfa2015-04-10 07:01:30 -0700200 PFOverBudgetCB fCallback;
201 void* fData;
Herb Derbyb12175f2018-05-23 16:38:09 -0400202 size_t fSizeBudget;
203 size_t fCurrentSize{0};
Jim Van Verth474d6872017-12-14 13:00:05 -0500204 uint32_t fUniqueID; // unique id to use for messaging
Florin Malita4a01ac92017-03-13 16:45:28 -0400205 SkMessageBus<PurgeBlobMessage>::Inbox fPurgeBlobInbox;
joshualittb7133be2015-04-08 09:08:31 -0700206};
207
208#endif