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