blob: dc7e4c58442b7c81a9e51e6e1f5714164dc74e14 [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
11#include "GrAtlasTextContext.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
26 GrTextBlobCache(PFOverBudgetCB cb, void* data)
Florin Malita012893b2017-07-11 09:31:22 -040027 : fPool(0u, kMinGrowthSize)
joshualitt0db6dfa2015-04-10 07:01:30 -070028 , fCallback(cb)
joshualitt17d833b2015-08-03 10:17:44 -070029 , fData(data)
30 , fBudget(kDefaultBudget) {
joshualitt0db6dfa2015-04-10 07:01:30 -070031 SkASSERT(cb && data);
32 }
joshualittb7133be2015-04-08 09:08:31 -070033 ~GrTextBlobCache();
34
35 // creates an uncached blob
Florin Malitac337c9e2017-03-10 18:02:29 +000036 sk_sp<GrAtlasTextBlob> makeBlob(int glyphCount, int runCount) {
37 return GrAtlasTextBlob::Make(&fPool, glyphCount, runCount);
Florin Malitadb3ceb82017-03-09 14:21:44 -050038 }
39
Florin Malitac337c9e2017-03-10 18:02:29 +000040 sk_sp<GrAtlasTextBlob> makeBlob(const SkTextBlob* blob) {
Florin Malita3304c442017-03-09 22:42:58 +000041 int glyphCount = 0;
42 int runCount = 0;
43 BlobGlyphCount(&glyphCount, &runCount, blob);
Florin Malitac337c9e2017-03-10 18:02:29 +000044 return GrAtlasTextBlob::Make(&fPool, glyphCount, runCount);
45 }
46
47 sk_sp<GrAtlasTextBlob> makeCachedBlob(const SkTextBlob* blob,
48 const GrAtlasTextBlob::Key& key,
49 const SkMaskFilter::BlurRec& blurRec,
50 const SkPaint& paint) {
51 sk_sp<GrAtlasTextBlob> cacheBlob(this->makeBlob(blob));
joshualitt92303772016-02-10 11:55:52 -080052 cacheBlob->setupKey(key, blurRec, paint);
joshualittb7133be2015-04-08 09:08:31 -070053 this->add(cacheBlob);
Florin Malita4a01ac92017-03-13 16:45:28 -040054 blob->notifyAddedToCache();
joshualittb7133be2015-04-08 09:08:31 -070055 return cacheBlob;
56 }
57
Florin Malitac337c9e2017-03-10 18:02:29 +000058 sk_sp<GrAtlasTextBlob> find(const GrAtlasTextBlob::Key& key) const {
Florin Malita33fdb8d2017-03-07 16:51:57 -050059 const auto* idEntry = fBlobIDCache.find(key.fUniqueID);
60 return idEntry ? idEntry->find(key) : nullptr;
joshualittb7133be2015-04-08 09:08:31 -070061 }
62
joshualitt374b2f72015-07-21 08:05:03 -070063 void remove(GrAtlasTextBlob* blob) {
Florin Malita33fdb8d2017-03-07 16:51:57 -050064 auto id = GrAtlasTextBlob::GetKey(*blob).fUniqueID;
65 auto* idEntry = fBlobIDCache.find(id);
66 SkASSERT(idEntry);
67
Florin Malitac337c9e2017-03-10 18:02:29 +000068 fBlobList.remove(blob);
Florin Malita33fdb8d2017-03-07 16:51:57 -050069 idEntry->removeBlob(blob);
70 if (idEntry->fBlobs.empty()) {
71 fBlobIDCache.remove(id);
72 }
joshualittb7133be2015-04-08 09:08:31 -070073 }
74
joshualitt374b2f72015-07-21 08:05:03 -070075 void makeMRU(GrAtlasTextBlob* blob) {
joshualittb7133be2015-04-08 09:08:31 -070076 if (fBlobList.head() == blob) {
77 return;
78 }
79
80 fBlobList.remove(blob);
81 fBlobList.addToHead(blob);
82 }
83
joshualitt26ffc002015-04-16 11:24:04 -070084 void freeAll();
85
joshualittb7133be2015-04-08 09:08:31 -070086 // TODO move to SkTextBlob
joshualitt259fbf12015-07-21 11:39:34 -070087 static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
halcanary33779752015-10-27 14:01:05 -070088 SkTextBlobRunIterator itCounter(blob);
joshualittb7133be2015-04-08 09:08:31 -070089 for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
90 *glyphCount += itCounter.glyphCount();
91 }
92 }
93
joshualitt17d833b2015-08-03 10:17:44 -070094 void setBudget(size_t budget) {
95 fBudget = budget;
96 this->checkPurge();
97 }
98
Florin Malita4a01ac92017-03-13 16:45:28 -040099 struct PurgeBlobMessage {
100 uint32_t fID;
101 };
102
103 static void PostPurgeBlobMessage(uint32_t);
104
Jim Van Verth76d917c2017-12-13 09:26:37 -0500105 void purgeStaleBlobs();
106
joshualitt259fbf12015-07-21 11:39:34 -0700107private:
Florin Malita33fdb8d2017-03-07 16:51:57 -0500108 using BitmapBlobList = SkTInternalLList<GrAtlasTextBlob>;
109
110 struct BlobIDCacheEntry {
111 BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
112 explicit BlobIDCacheEntry(uint32_t id) : fID(id) {}
113
114 static uint32_t GetKey(const BlobIDCacheEntry& entry) {
115 return entry.fID;
116 }
117
Florin Malitac337c9e2017-03-10 18:02:29 +0000118 void addBlob(sk_sp<GrAtlasTextBlob> blob) {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500119 SkASSERT(blob);
120 SkASSERT(GrAtlasTextBlob::GetKey(*blob).fUniqueID == fID);
121 SkASSERT(!this->find(GrAtlasTextBlob::GetKey(*blob)));
122
Florin Malitac337c9e2017-03-10 18:02:29 +0000123 fBlobs.emplace_back(std::move(blob));
Florin Malita33fdb8d2017-03-07 16:51:57 -0500124 }
125
126 void removeBlob(GrAtlasTextBlob* blob) {
127 SkASSERT(blob);
128 SkASSERT(GrAtlasTextBlob::GetKey(*blob).fUniqueID == fID);
129
130 auto index = this->findBlobIndex(GrAtlasTextBlob::GetKey(*blob));
131 SkASSERT(index >= 0);
132
133 fBlobs.removeShuffle(index);
134 }
135
Florin Malitac337c9e2017-03-10 18:02:29 +0000136 sk_sp<GrAtlasTextBlob> find(const GrAtlasTextBlob::Key& key) const {
Florin Malita33fdb8d2017-03-07 16:51:57 -0500137 auto index = this->findBlobIndex(key);
138 return index < 0 ? nullptr : fBlobs[index];
139 }
140
141 int findBlobIndex(const GrAtlasTextBlob::Key& key) const{
142 for (int i = 0; i < fBlobs.count(); ++i) {
143 if (GrAtlasTextBlob::GetKey(*fBlobs[i]) == key) {
144 return i;
145 }
146 }
147 return -1;
148 }
149
150 uint32_t fID;
151 // Current clients don't generate multiple GrAtlasTextBlobs per SkTextBlob, so an array w/
152 // linear search is acceptable. If usage changes, we should re-evaluate this structure.
Florin Malitac337c9e2017-03-10 18:02:29 +0000153 SkSTArray<1, sk_sp<GrAtlasTextBlob>, true> fBlobs;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500154 };
joshualittb7133be2015-04-08 09:08:31 -0700155
Florin Malitac337c9e2017-03-10 18:02:29 +0000156 void add(sk_sp<GrAtlasTextBlob> blob) {
157 auto id = GrAtlasTextBlob::GetKey(*blob).fUniqueID;
158 auto* idEntry = fBlobIDCache.find(id);
159 if (!idEntry) {
160 idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
161 }
162
163 // Safe to retain a raw ptr temporarily here, because the cache will hold a ref.
164 GrAtlasTextBlob* rawBlobPtr = blob.get();
165 fBlobList.addToHead(rawBlobPtr);
166 idEntry->addBlob(std::move(blob));
167
168 this->checkPurge(rawBlobPtr);
169 }
170
Jim Van Verth76d917c2017-12-13 09:26:37 -0500171 void checkPurge(GrAtlasTextBlob* blob = nullptr);
joshualitt17d833b2015-08-03 10:17:44 -0700172
Florin Malita012893b2017-07-11 09:31:22 -0400173 static const int kMinGrowthSize = 1 << 16;
joshualitt17d833b2015-08-03 10:17:44 -0700174 static const int kDefaultBudget = 1 << 22;
Florin Malitac337c9e2017-03-10 18:02:29 +0000175 GrMemoryPool fPool;
joshualittb7133be2015-04-08 09:08:31 -0700176 BitmapBlobList fBlobList;
Florin Malita33fdb8d2017-03-07 16:51:57 -0500177 SkTHashMap<uint32_t, BlobIDCacheEntry> fBlobIDCache;
joshualitt0db6dfa2015-04-10 07:01:30 -0700178 PFOverBudgetCB fCallback;
179 void* fData;
joshualitt17d833b2015-08-03 10:17:44 -0700180 size_t fBudget;
Florin Malita4a01ac92017-03-13 16:45:28 -0400181 SkMessageBus<PurgeBlobMessage>::Inbox fPurgeBlobInbox;
joshualittb7133be2015-04-08 09:08:31 -0700182};
183
184#endif