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 "SkTDynamicHash.h" 13 #include "SkTextBlobRunIterator.h" 14 15 class GrTextBlobCache { 16 public: 17 /** 18 * The callback function used by the cache when it is still over budget after a purge. The 19 * passed in 'data' is the same 'data' handed to setOverbudgetCallback. 20 */ 21 typedef void (*PFOverBudgetCB)(void* data); 22 GrTextBlobCache(PFOverBudgetCB cb,void * data)23 GrTextBlobCache(PFOverBudgetCB cb, void* data) 24 : fPool(kPreAllocSize, kMinGrowthSize) 25 , fCallback(cb) 26 , fData(data) 27 , fBudget(kDefaultBudget) { 28 SkASSERT(cb && data); 29 } 30 ~GrTextBlobCache(); 31 32 // creates an uncached blob createBlob(int glyphCount,int runCount)33 GrAtlasTextBlob* createBlob(int glyphCount, int runCount) { 34 return GrAtlasTextBlob::Create(&fPool, glyphCount, runCount); 35 } createBlob(const SkTextBlob * blob)36 GrAtlasTextBlob* createBlob(const SkTextBlob* blob) { 37 int glyphCount = 0; 38 int runCount = 0; 39 BlobGlyphCount(&glyphCount, &runCount, blob); 40 GrAtlasTextBlob* cacheBlob = GrAtlasTextBlob::Create(&fPool, glyphCount, runCount); 41 return cacheBlob; 42 } 43 createCachedBlob(const SkTextBlob * blob,const GrAtlasTextBlob::Key & key,const SkMaskFilter::BlurRec & blurRec,const SkPaint & paint)44 GrAtlasTextBlob* createCachedBlob(const SkTextBlob* blob, 45 const GrAtlasTextBlob::Key& key, 46 const SkMaskFilter::BlurRec& blurRec, 47 const SkPaint& paint) { 48 int glyphCount = 0; 49 int runCount = 0; 50 BlobGlyphCount(&glyphCount, &runCount, blob); 51 GrAtlasTextBlob* cacheBlob = GrAtlasTextBlob::Create(&fPool, glyphCount, runCount); 52 cacheBlob->setupKey(key, blurRec, paint); 53 this->add(cacheBlob); 54 return cacheBlob; 55 } 56 find(const GrAtlasTextBlob::Key & key)57 GrAtlasTextBlob* find(const GrAtlasTextBlob::Key& key) { 58 return fCache.find(key); 59 } 60 remove(GrAtlasTextBlob * blob)61 void remove(GrAtlasTextBlob* blob) { 62 fCache.remove(blob->key()); 63 fBlobList.remove(blob); 64 blob->unref(); 65 } 66 add(GrAtlasTextBlob * blob)67 void add(GrAtlasTextBlob* blob) { 68 fCache.add(blob); 69 fBlobList.addToHead(blob); 70 71 this->checkPurge(blob); 72 } 73 makeMRU(GrAtlasTextBlob * blob)74 void makeMRU(GrAtlasTextBlob* 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 fBudget = budget; 95 this->checkPurge(); 96 } 97 98 private: 99 typedef SkTInternalLList<GrAtlasTextBlob> BitmapBlobList; 100 101 void checkPurge(GrAtlasTextBlob* blob = nullptr) { 102 // If we are overbudget, then unref until we are below budget again 103 if (fPool.size() > fBudget) { 104 BitmapBlobList::Iter iter; 105 iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart); 106 GrAtlasTextBlob* lruBlob = nullptr; 107 while (fPool.size() > fBudget && (lruBlob = iter.get()) && lruBlob != blob) { 108 fCache.remove(lruBlob->key()); 109 110 // Backup the iterator before removing and unrefing the blob 111 iter.prev(); 112 fBlobList.remove(lruBlob); 113 lruBlob->unref(); 114 } 115 116 // If we break out of the loop with lruBlob == blob, then we haven't purged enough 117 // use the call back and try to free some more. If we are still overbudget after this, 118 // then this single textblob is over our budget 119 if (blob && lruBlob == blob) { 120 (*fCallback)(fData); 121 } 122 123 #ifdef SPEW_BUDGET_MESSAGE 124 if (fPool.size() > fBudget) { 125 SkDebugf("Single textblob is larger than our whole budget"); 126 } 127 #endif 128 } 129 } 130 131 // Budget was chosen to be ~4 megabytes. The min alloc and pre alloc sizes in the pool are 132 // based off of the largest cached textblob I have seen in the skps(a couple of kilobytes). 133 static const int kPreAllocSize = 1 << 17; 134 static const int kMinGrowthSize = 1 << 17; 135 static const int kDefaultBudget = 1 << 22; 136 BitmapBlobList fBlobList; 137 SkTDynamicHash<GrAtlasTextBlob, GrAtlasTextBlob::Key> fCache; 138 GrMemoryPool fPool; 139 PFOverBudgetCB fCallback; 140 void* fData; 141 size_t fBudget; 142 }; 143 144 #endif 145