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