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