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 "SkTextBlob.h" 14 15 class GrTextBlobCache { 16 public: 17 typedef GrAtlasTextContext::BitmapTextBlob BitmapTextBlob; 18 19 /** 20 * The callback function used by the cache when it is still over budget after a purge. The 21 * passed in 'data' is the same 'data' handed to setOverbudgetCallback. 22 */ 23 typedef void (*PFOverBudgetCB)(void* data); 24 GrTextBlobCache(PFOverBudgetCB cb,void * data)25 GrTextBlobCache(PFOverBudgetCB cb, void* data) 26 : fPool(kPreAllocSize, kMinGrowthSize) 27 , fCallback(cb) 28 , fData(data) { 29 SkASSERT(cb && data); 30 } 31 ~GrTextBlobCache(); 32 33 // creates an uncached blob 34 BitmapTextBlob* createBlob(int glyphCount, int runCount, size_t maxVASize); createBlob(const SkTextBlob * blob,size_t maxVAStride)35 BitmapTextBlob* createBlob(const SkTextBlob* blob, size_t maxVAStride) { 36 int glyphCount = 0; 37 int runCount = 0; 38 BlobGlyphCount(&glyphCount, &runCount, blob); 39 BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride); 40 return cacheBlob; 41 } 42 createCachedBlob(const SkTextBlob * blob,const BitmapTextBlob::Key & key,const SkMaskFilter::BlurRec & blurRec,const SkPaint & paint,size_t maxVAStride)43 BitmapTextBlob* createCachedBlob(const SkTextBlob* blob, 44 const BitmapTextBlob::Key& key, 45 const SkMaskFilter::BlurRec& blurRec, 46 const SkPaint& paint, 47 size_t maxVAStride) { 48 int glyphCount = 0; 49 int runCount = 0; 50 BlobGlyphCount(&glyphCount, &runCount, blob); 51 BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride); 52 cacheBlob->fKey = key; 53 if (key.fHasBlur) { 54 cacheBlob->fBlurRec = blurRec; 55 } 56 if (key.fStyle != SkPaint::kFill_Style) { 57 cacheBlob->fStrokeInfo.fFrameWidth = paint.getStrokeWidth(); 58 cacheBlob->fStrokeInfo.fMiterLimit = paint.getStrokeMiter(); 59 cacheBlob->fStrokeInfo.fJoin = paint.getStrokeJoin(); 60 } 61 this->add(cacheBlob); 62 return cacheBlob; 63 } 64 find(const BitmapTextBlob::Key & key)65 BitmapTextBlob* find(const BitmapTextBlob::Key& key) { 66 return fCache.find(key); 67 } 68 remove(BitmapTextBlob * blob)69 void remove(BitmapTextBlob* blob) { 70 fCache.remove(blob->fKey); 71 fBlobList.remove(blob); 72 blob->unref(); 73 } 74 add(BitmapTextBlob * blob)75 void add(BitmapTextBlob* blob) { 76 fCache.add(blob); 77 fBlobList.addToHead(blob); 78 79 // If we are overbudget, then unref until we are below budget again 80 if (fPool.size() > kBudget) { 81 BitmapBlobList::Iter iter; 82 iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart); 83 BitmapTextBlob* lruBlob = iter.get(); 84 SkASSERT(lruBlob); 85 while (fPool.size() > kBudget && (lruBlob = iter.get()) && lruBlob != blob) { 86 fCache.remove(lruBlob->fKey); 87 88 // Backup the iterator before removing and unrefing the blob 89 iter.prev(); 90 fBlobList.remove(lruBlob); 91 lruBlob->unref(); 92 } 93 94 // If we break out of the loop with lruBlob == blob, then we haven't purged enough 95 // use the call back and try to free some more. If we are still overbudget after this, 96 // then this single textblob is over our budget 97 if (lruBlob == blob) { 98 (*fCallback)(fData); 99 } 100 101 #ifdef SK_DEBUG 102 if (fPool.size() > kBudget) { 103 SkDebugf("Single textblob is larger than our whole budget"); 104 } 105 #endif 106 } 107 } 108 makeMRU(BitmapTextBlob * blob)109 void makeMRU(BitmapTextBlob* blob) { 110 if (fBlobList.head() == blob) { 111 return; 112 } 113 114 fBlobList.remove(blob); 115 fBlobList.addToHead(blob); 116 } 117 118 void freeAll(); 119 120 private: 121 // TODO move to SkTextBlob BlobGlyphCount(int * glyphCount,int * runCount,const SkTextBlob * blob)122 void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) { 123 SkTextBlob::RunIterator itCounter(blob); 124 for (; !itCounter.done(); itCounter.next(), (*runCount)++) { 125 *glyphCount += itCounter.glyphCount(); 126 } 127 } 128 129 typedef SkTInternalLList<BitmapTextBlob> BitmapBlobList; 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 kBudget = 1 << 22; 136 BitmapBlobList fBlobList; 137 SkTDynamicHash<BitmapTextBlob, BitmapTextBlob::Key> fCache; 138 GrMemoryPool fPool; 139 PFOverBudgetCB fCallback; 140 void* fData; 141 }; 142 143 #endif 144