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