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