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 |