• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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