• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef GrTextBlobCache_DEFINED
9 #define GrTextBlobCache_DEFINED
10 
11 #include "include/core/SkRefCnt.h"
12 #include "include/private/SkTArray.h"
13 #include "include/private/SkTHash.h"
14 #include "src/core/SkMessageBus.h"
15 #include "src/core/SkTextBlobPriv.h"
16 #include "src/gpu/text/GrTextBlob.h"
17 
18 class GrTextBlobCache {
19 public:
20     /**
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 
GrTextBlobCache(PFOverBudgetCB cb,void * data,uint32_t uniqueID)26     GrTextBlobCache(PFOverBudgetCB cb, void* data, uint32_t uniqueID)
27         : fCallback(cb)
28         , fData(data)
29         , fSizeBudget(kDefaultBudget)
30         , fUniqueID(uniqueID)
31         , fPurgeBlobInbox(uniqueID) {
32         SkASSERT(cb && data);
33     }
34     ~GrTextBlobCache();
35 
makeBlob(const SkGlyphRunList & glyphRunList,GrColor color,GrStrikeCache * strikeCache)36     sk_sp<GrTextBlob> makeBlob(const SkGlyphRunList& glyphRunList,
37                                GrColor color,
38                                GrStrikeCache* strikeCache) {
39         return GrTextBlob::Make(
40                 glyphRunList.totalGlyphCount(), glyphRunList.size(), color, strikeCache);
41     }
42 
makeCachedBlob(const SkGlyphRunList & glyphRunList,const GrTextBlob::Key & key,const SkMaskFilterBase::BlurRec & blurRec,const SkPaint & paint,GrColor color,GrStrikeCache * strikeCache)43     sk_sp<GrTextBlob> makeCachedBlob(const SkGlyphRunList& glyphRunList,
44                                      const GrTextBlob::Key& key,
45                                      const SkMaskFilterBase::BlurRec& blurRec,
46                                      const SkPaint& paint,
47                                      GrColor color,
48                                      GrStrikeCache* strikeCache) {
49         sk_sp<GrTextBlob> cacheBlob(makeBlob(glyphRunList, color, strikeCache));
50         cacheBlob->setupKey(key, blurRec, paint);
51         this->add(cacheBlob);
52         glyphRunList.temporaryShuntBlobNotifyAddedToCache(fUniqueID);
53         return cacheBlob;
54     }
55 
find(const GrTextBlob::Key & key)56     sk_sp<GrTextBlob> find(const GrTextBlob::Key& key) const {
57         const auto* idEntry = fBlobIDCache.find(key.fUniqueID);
58         return idEntry ? idEntry->find(key) : nullptr;
59     }
60 
remove(GrTextBlob * blob)61     void remove(GrTextBlob* blob) {
62         auto  id      = GrTextBlob::GetKey(*blob).fUniqueID;
63         auto* idEntry = fBlobIDCache.find(id);
64         SkASSERT(idEntry);
65 
66         fCurrentSize -= blob->size();
67         fBlobList.remove(blob);
68         idEntry->removeBlob(blob);
69         if (idEntry->fBlobs.empty()) {
70             fBlobIDCache.remove(id);
71         }
72     }
73 
makeMRU(GrTextBlob * blob)74     void makeMRU(GrTextBlob* blob) {
75         if (fBlobList.head() == blob) {
76             return;
77         }
78 
79         fBlobList.remove(blob);
80         fBlobList.addToHead(blob);
81     }
82 
83     void freeAll();
84 
85     // TODO move to SkTextBlob
BlobGlyphCount(int * glyphCount,int * runCount,const SkTextBlob * blob)86     static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
87         SkTextBlobRunIterator itCounter(blob);
88         for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
89             *glyphCount += itCounter.glyphCount();
90         }
91     }
92 
setBudget(size_t budget)93     void setBudget(size_t budget) {
94         fSizeBudget = budget;
95         this->checkPurge();
96     }
97 
98     struct PurgeBlobMessage {
PurgeBlobMessagePurgeBlobMessage99         PurgeBlobMessage(uint32_t blobID, uint32_t contextUniqueID)
100                 : fBlobID(blobID), fContextID(contextUniqueID) {}
101 
102         uint32_t fBlobID;
103         uint32_t fContextID;
104     };
105 
106     static void PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID);
107 
108     void purgeStaleBlobs();
109 
usedBytes()110     size_t usedBytes() const { return fCurrentSize; }
111 
112 private:
113     using BitmapBlobList = SkTInternalLList<GrTextBlob>;
114 
115     struct BlobIDCacheEntry {
BlobIDCacheEntryBlobIDCacheEntry116         BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
BlobIDCacheEntryBlobIDCacheEntry117         explicit BlobIDCacheEntry(uint32_t id) : fID(id) {}
118 
GetKeyBlobIDCacheEntry119         static uint32_t GetKey(const BlobIDCacheEntry& entry) {
120             return entry.fID;
121         }
122 
addBlobBlobIDCacheEntry123         void addBlob(sk_sp<GrTextBlob> blob) {
124             SkASSERT(blob);
125             SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID);
126             SkASSERT(!this->find(GrTextBlob::GetKey(*blob)));
127 
128             fBlobs.emplace_back(std::move(blob));
129         }
130 
removeBlobBlobIDCacheEntry131         void removeBlob(GrTextBlob* blob) {
132             SkASSERT(blob);
133             SkASSERT(GrTextBlob::GetKey(*blob).fUniqueID == fID);
134 
135             auto index = this->findBlobIndex(GrTextBlob::GetKey(*blob));
136             SkASSERT(index >= 0);
137 
138             fBlobs.removeShuffle(index);
139         }
140 
findBlobIDCacheEntry141         sk_sp<GrTextBlob> find(const GrTextBlob::Key& key) const {
142             auto index = this->findBlobIndex(key);
143             return index < 0 ? nullptr : fBlobs[index];
144         }
145 
findBlobIndexBlobIDCacheEntry146         int findBlobIndex(const GrTextBlob::Key& key) const{
147             for (int i = 0; i < fBlobs.count(); ++i) {
148                 if (GrTextBlob::GetKey(*fBlobs[i]) == key) {
149                     return i;
150                 }
151             }
152             return -1;
153         }
154 
155         uint32_t                             fID;
156         // Current clients don't generate multiple GrAtlasTextBlobs per SkTextBlob, so an array w/
157         // linear search is acceptable.  If usage changes, we should re-evaluate this structure.
158         SkSTArray<1, sk_sp<GrTextBlob>, true> fBlobs;
159     };
160 
add(sk_sp<GrTextBlob> blob)161     void add(sk_sp<GrTextBlob> blob) {
162         auto  id      = GrTextBlob::GetKey(*blob).fUniqueID;
163         auto* idEntry = fBlobIDCache.find(id);
164         if (!idEntry) {
165             idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
166         }
167 
168         // Safe to retain a raw ptr temporarily here, because the cache will hold a ref.
169         GrTextBlob* rawBlobPtr = blob.get();
170         fBlobList.addToHead(rawBlobPtr);
171         fCurrentSize += blob->size();
172         idEntry->addBlob(std::move(blob));
173 
174         this->checkPurge(rawBlobPtr);
175     }
176 
177     void checkPurge(GrTextBlob* blob = nullptr);
178 
179     static const int kMinGrowthSize = 1 << 16;
180     static const int kDefaultBudget = 1 << 22;
181     BitmapBlobList fBlobList;
182     SkTHashMap<uint32_t, BlobIDCacheEntry> fBlobIDCache;
183     PFOverBudgetCB fCallback;
184     void* fData;
185     size_t fSizeBudget;
186     size_t fCurrentSize{0};
187     uint32_t fUniqueID;      // unique id to use for messaging
188     SkMessageBus<PurgeBlobMessage>::Inbox fPurgeBlobInbox;
189 };
190 
191 #endif
192