blob: a110a031ab866d14a0f5d1fef8fddcc6d1295121 [file] [log] [blame]
joshualittb7133be2015-04-08 09:08:31 -07001/*
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
15class GrTextBlobCache {
16public:
17 typedef GrAtlasTextContext::BitmapTextBlob BitmapTextBlob;
18
joshualitt0db6dfa2015-04-10 07:01:30 -070019 /**
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
25 GrTextBlobCache(PFOverBudgetCB cb, void* data)
26 : fPool(kPreAllocSize, kMinGrowthSize)
27 , fCallback(cb)
28 , fData(data) {
29 SkASSERT(cb && data);
30 }
joshualittb7133be2015-04-08 09:08:31 -070031 ~GrTextBlobCache();
32
33 // creates an uncached blob
34 BitmapTextBlob* createBlob(int glyphCount, int runCount, size_t maxVASize);
joshualitt2a0e9f32015-04-13 06:12:21 -070035 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 }
joshualittb7133be2015-04-08 09:08:31 -070042
joshualitt53b5f442015-04-13 06:33:59 -070043 BitmapTextBlob* createCachedBlob(const SkTextBlob* blob,
44 const BitmapTextBlob::Key& key,
45 const SkMaskFilter::BlurRec& blurRec,
46 const SkPaint& paint,
47 size_t maxVAStride) {
joshualittb7133be2015-04-08 09:08:31 -070048 int glyphCount = 0;
49 int runCount = 0;
50 BlobGlyphCount(&glyphCount, &runCount, blob);
51 BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
joshualitt53b5f442015-04-13 06:33:59 -070052 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 }
joshualittb7133be2015-04-08 09:08:31 -070061 this->add(cacheBlob);
62 return cacheBlob;
63 }
64
joshualitt53b5f442015-04-13 06:33:59 -070065 BitmapTextBlob* find(const BitmapTextBlob::Key& key) {
66 return fCache.find(key);
joshualittb7133be2015-04-08 09:08:31 -070067 }
68
69 void remove(BitmapTextBlob* blob) {
joshualitt53b5f442015-04-13 06:33:59 -070070 fCache.remove(blob->fKey);
joshualittb7133be2015-04-08 09:08:31 -070071 fBlobList.remove(blob);
72 blob->unref();
73 }
74
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);
joshualitt0db6dfa2015-04-10 07:01:30 -070085 while (fPool.size() > kBudget && (lruBlob = iter.get()) && lruBlob != blob) {
joshualitt53b5f442015-04-13 06:33:59 -070086 fCache.remove(lruBlob->fKey);
joshualittf6e97e62015-04-10 07:23:29 -070087
88 // Backup the iterator before removing and unrefing the blob
joshualittb7133be2015-04-08 09:08:31 -070089 iter.prev();
joshualittf6e97e62015-04-10 07:23:29 -070090 fBlobList.remove(lruBlob);
joshualitt0db6dfa2015-04-10 07:01:30 -070091 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
joshualittb7133be2015-04-08 09:08:31 -0700106 }
107 }
108
109 void makeMRU(BitmapTextBlob* blob) {
110 if (fBlobList.head() == blob) {
111 return;
112 }
113
114 fBlobList.remove(blob);
115 fBlobList.addToHead(blob);
116 }
117
118private:
119 // TODO move to SkTextBlob
120 void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
121 SkTextBlob::RunIterator itCounter(blob);
122 for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
123 *glyphCount += itCounter.glyphCount();
124 }
125 }
126
127 typedef SkTInternalLList<BitmapTextBlob> BitmapBlobList;
128
129 // Budget was chosen to be ~4 megabytes. The min alloc and pre alloc sizes in the pool are
130 // based off of the largest cached textblob I have seen in the skps(a couple of kilobytes).
131 static const int kPreAllocSize = 1 << 17;
132 static const int kMinGrowthSize = 1 << 17;
joshualitt0db6dfa2015-04-10 07:01:30 -0700133 static const int kBudget = 1 << 22;
joshualittb7133be2015-04-08 09:08:31 -0700134 BitmapBlobList fBlobList;
joshualitt53b5f442015-04-13 06:33:59 -0700135 SkTDynamicHash<BitmapTextBlob, BitmapTextBlob::Key> fCache;
joshualittb7133be2015-04-08 09:08:31 -0700136 GrMemoryPool fPool;
joshualitt0db6dfa2015-04-10 07:01:30 -0700137 PFOverBudgetCB fCallback;
138 void* fData;
joshualittb7133be2015-04-08 09:08:31 -0700139};
140
141#endif