blob: e3b2ca73fb622bb0c73bfc1e3b6049c60a8546cc [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"
halcanary33779752015-10-27 14:01:05 -070013#include "SkTextBlobRunIterator.h"
joshualittb7133be2015-04-08 09:08:31 -070014
15class GrTextBlobCache {
16public:
joshualitt0db6dfa2015-04-10 07:01:30 -070017 /**
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
23 GrTextBlobCache(PFOverBudgetCB cb, void* data)
24 : fPool(kPreAllocSize, kMinGrowthSize)
25 , fCallback(cb)
joshualitt17d833b2015-08-03 10:17:44 -070026 , fData(data)
27 , fBudget(kDefaultBudget) {
joshualitt0db6dfa2015-04-10 07:01:30 -070028 SkASSERT(cb && data);
29 }
joshualittb7133be2015-04-08 09:08:31 -070030 ~GrTextBlobCache();
31
32 // creates an uncached blob
joshualitt92303772016-02-10 11:55:52 -080033 GrAtlasTextBlob* createBlob(int glyphCount, int runCount) {
34 return GrAtlasTextBlob::Create(&fPool, glyphCount, runCount);
35 }
36 GrAtlasTextBlob* createBlob(const SkTextBlob* blob) {
joshualitt2a0e9f32015-04-13 06:12:21 -070037 int glyphCount = 0;
38 int runCount = 0;
39 BlobGlyphCount(&glyphCount, &runCount, blob);
joshualitt92303772016-02-10 11:55:52 -080040 GrAtlasTextBlob* cacheBlob = GrAtlasTextBlob::Create(&fPool, glyphCount, runCount);
joshualitt2a0e9f32015-04-13 06:12:21 -070041 return cacheBlob;
42 }
joshualittb7133be2015-04-08 09:08:31 -070043
joshualitt259fbf12015-07-21 11:39:34 -070044 GrAtlasTextBlob* createCachedBlob(const SkTextBlob* blob,
45 const GrAtlasTextBlob::Key& key,
46 const SkMaskFilter::BlurRec& blurRec,
joshualitt92303772016-02-10 11:55:52 -080047 const SkPaint& paint) {
joshualitt259fbf12015-07-21 11:39:34 -070048 int glyphCount = 0;
49 int runCount = 0;
50 BlobGlyphCount(&glyphCount, &runCount, blob);
joshualitt92303772016-02-10 11:55:52 -080051 GrAtlasTextBlob* cacheBlob = GrAtlasTextBlob::Create(&fPool, glyphCount, runCount);
52 cacheBlob->setupKey(key, blurRec, paint);
joshualittb7133be2015-04-08 09:08:31 -070053 this->add(cacheBlob);
54 return cacheBlob;
55 }
56
joshualitt374b2f72015-07-21 08:05:03 -070057 GrAtlasTextBlob* find(const GrAtlasTextBlob::Key& key) {
joshualitt53b5f442015-04-13 06:33:59 -070058 return fCache.find(key);
joshualittb7133be2015-04-08 09:08:31 -070059 }
60
joshualitt374b2f72015-07-21 08:05:03 -070061 void remove(GrAtlasTextBlob* blob) {
joshualitt92303772016-02-10 11:55:52 -080062 fCache.remove(blob->key());
joshualittb7133be2015-04-08 09:08:31 -070063 fBlobList.remove(blob);
64 blob->unref();
65 }
66
joshualitt374b2f72015-07-21 08:05:03 -070067 void add(GrAtlasTextBlob* blob) {
joshualittb7133be2015-04-08 09:08:31 -070068 fCache.add(blob);
69 fBlobList.addToHead(blob);
70
joshualitt17d833b2015-08-03 10:17:44 -070071 this->checkPurge(blob);
joshualittb7133be2015-04-08 09:08:31 -070072 }
73
joshualitt374b2f72015-07-21 08:05:03 -070074 void makeMRU(GrAtlasTextBlob* blob) {
joshualittb7133be2015-04-08 09:08:31 -070075 if (fBlobList.head() == blob) {
76 return;
77 }
78
79 fBlobList.remove(blob);
80 fBlobList.addToHead(blob);
81 }
82
joshualitt26ffc002015-04-16 11:24:04 -070083 void freeAll();
84
joshualittb7133be2015-04-08 09:08:31 -070085 // TODO move to SkTextBlob
joshualitt259fbf12015-07-21 11:39:34 -070086 static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
halcanary33779752015-10-27 14:01:05 -070087 SkTextBlobRunIterator itCounter(blob);
joshualittb7133be2015-04-08 09:08:31 -070088 for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
89 *glyphCount += itCounter.glyphCount();
90 }
91 }
92
joshualitt17d833b2015-08-03 10:17:44 -070093 void setBudget(size_t budget) {
94 fBudget = budget;
95 this->checkPurge();
96 }
97
joshualitt259fbf12015-07-21 11:39:34 -070098private:
joshualitt374b2f72015-07-21 08:05:03 -070099 typedef SkTInternalLList<GrAtlasTextBlob> BitmapBlobList;
joshualittb7133be2015-04-08 09:08:31 -0700100
halcanary96fcdcc2015-08-27 07:41:13 -0700101 void checkPurge(GrAtlasTextBlob* blob = nullptr) {
joshualitt17d833b2015-08-03 10:17:44 -0700102 // 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);
halcanary96fcdcc2015-08-27 07:41:13 -0700106 GrAtlasTextBlob* lruBlob = nullptr;
joshualitt17d833b2015-08-03 10:17:44 -0700107 while (fPool.size() > fBudget && (lruBlob = iter.get()) && lruBlob != blob) {
joshualitt92303772016-02-10 11:55:52 -0800108 fCache.remove(lruBlob->key());
joshualitt17d833b2015-08-03 10:17:44 -0700109
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
joshualittb7133be2015-04-08 09:08:31 -0700131 // 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;
joshualitt17d833b2015-08-03 10:17:44 -0700135 static const int kDefaultBudget = 1 << 22;
joshualittb7133be2015-04-08 09:08:31 -0700136 BitmapBlobList fBlobList;
joshualitt374b2f72015-07-21 08:05:03 -0700137 SkTDynamicHash<GrAtlasTextBlob, GrAtlasTextBlob::Key> fCache;
joshualittb7133be2015-04-08 09:08:31 -0700138 GrMemoryPool fPool;
joshualitt0db6dfa2015-04-10 07:01:30 -0700139 PFOverBudgetCB fCallback;
140 void* fData;
joshualitt17d833b2015-08-03 10:17:44 -0700141 size_t fBudget;
joshualittb7133be2015-04-08 09:08:31 -0700142};
143
144#endif