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