blob: 28aa0364103e240ec8576f056a8f7dd5d30dbf1a [file] [log] [blame]
joshualitt7c3a2f82015-03-31 13:32:05 -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 GrBatchFontCache_DEFINED
9#define GrBatchFontCache_DEFINED
10
11#include "GrBatchAtlas.h"
joshualitt7c3a2f82015-03-31 13:32:05 -070012#include "GrFontScaler.h"
13#include "GrGlyph.h"
14#include "SkTDynamicHash.h"
15#include "SkVarAlloc.h"
16
17class GrBatchFontCache;
18class GrBatchTarget;
19class GrGpu;
20
21/**
joshualitt4765bdc2015-07-23 10:58:48 -070022 * The GrBatchTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory
23 * is indexed by a PackedID and GrFontScaler. The GrFontScaler is what actually creates the mask.
joshualitt7c3a2f82015-03-31 13:32:05 -070024 */
joshualittae32c102015-04-21 09:37:57 -070025class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> {
joshualitt7c3a2f82015-03-31 13:32:05 -070026public:
27 GrBatchTextStrike(GrBatchFontCache*, const GrFontDescKey* fontScalerKey);
28 ~GrBatchTextStrike();
29
30 const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
31 GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; }
32
33 inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
34 GrGlyph* glyph = fCache.find(packed);
35 if (NULL == glyph) {
36 glyph = this->generateGlyph(packed, scaler);
37 }
38 return glyph;
39 }
40
joshualitt7c3a2f82015-03-31 13:32:05 -070041 // returns true if glyph successfully added to texture atlas, false otherwise
42 bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*);
43
44 // testing
45 int countGlyphs() const { return fCache.count(); }
46
47 // remove any references to this plot
48 void removeID(GrBatchAtlas::AtlasID);
49
joshualittae32c102015-04-21 09:37:57 -070050 // If a TextStrike is abandoned by the cache, then the caller must get a new strike
51 bool isAbandoned() const { return fIsAbandoned; }
52
joshualitt7c3a2f82015-03-31 13:32:05 -070053 static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) {
54 return *(ts.fFontScalerKey);
55 }
56 static uint32_t Hash(const GrFontDescKey& key) {
57 return key.getHash();
58 }
59
60private:
61 SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
62 SkAutoTUnref<const GrFontDescKey> fFontScalerKey;
63 SkVarAlloc fPool;
64
65 GrBatchFontCache* fBatchFontCache;
66 int fAtlasedGlyphs;
joshualittae32c102015-04-21 09:37:57 -070067 bool fIsAbandoned;
joshualitt7c3a2f82015-03-31 13:32:05 -070068
69 GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
70
71 friend class GrBatchFontCache;
72};
73
74/*
75 * GrBatchFontCache manages strikes which are indexed by a GrFontScaler. These strikes can then be
76 * used to individual Glyph Masks. The GrBatchFontCache also manages GrBatchAtlases, though this is
joshualitt62db8ba2015-04-09 08:22:37 -070077 * more or less transparent to the client(aside from atlasGeneration, described below).
78 * Note - we used to initialize the backing atlas for the GrBatchFontCache at initialization time.
79 * However, this caused a regression, even when the GrBatchFontCache was unused. We now initialize
80 * the backing atlases lazily. Its not immediately clear why this improves the situation.
joshualitt7c3a2f82015-03-31 13:32:05 -070081 */
82class GrBatchFontCache {
83public:
joshualitt62db8ba2015-04-09 08:22:37 -070084 GrBatchFontCache(GrContext*);
joshualitt7c3a2f82015-03-31 13:32:05 -070085 ~GrBatchFontCache();
joshualittae32c102015-04-21 09:37:57 -070086 // The user of the cache may hold a long-lived ref to the returned strike. However, actions by
87 // another client of the cache may cause the strike to be purged while it is still reffed.
88 // Therefore, the caller must check GrBatchTextStrike::isAbandoned() if there are other
89 // interactions with the cache since the strike was received.
joshualitt7c3a2f82015-03-31 13:32:05 -070090 inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) {
joshualitt7c3a2f82015-03-31 13:32:05 -070091 GrBatchTextStrike* strike = fCache.find(*(scaler->getKey()));
92 if (NULL == strike) {
93 strike = this->generateStrike(scaler);
94 }
95 return strike;
96 }
97
joshualitt62db8ba2015-04-09 08:22:37 -070098 void freeAll();
99
100 // if getTexture returns NULL, the client must not try to use other functions on the
101 // GrBatchFontCache which use the atlas. This function *must* be called first, before other
102 // functions which use the atlas.
103 GrTexture* getTexture(GrMaskFormat format) {
104 if (this->initAtlas(format)) {
105 return this->getAtlas(format)->getTexture();
106 }
107 return NULL;
108 }
109
110 bool hasGlyph(GrGlyph* glyph) {
111 SkASSERT(glyph);
112 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
113 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700114
115 // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store,
joshualittb4c507e2015-04-08 08:07:59 -0700116 // the client must pass in the currentToken from the GrBatchTarget along with the GrGlyph.
117 // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas.
118 // For convenience, this function will also set the use token for the current glyph if required
119 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration
joshualitt62db8ba2015-04-09 08:22:37 -0700120 void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater,
121 GrGlyph* glyph, GrBatchAtlas::BatchToken token) {
122 SkASSERT(glyph);
123 updater->add(glyph->fID);
124 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
125 }
joshualittb4c507e2015-04-08 08:07:59 -0700126
joshualitt62db8ba2015-04-09 08:22:37 -0700127 void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater,
128 GrBatchAtlas::BatchToken token,
129 GrMaskFormat format) {
130 this->getAtlas(format)->setLastUseTokenBulk(updater, token);
131 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700132
133 // add to texture atlas that matches this format
joshualitt62db8ba2015-04-09 08:22:37 -0700134 bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id,
135 GrBatchTarget* batchTarget,
136 GrMaskFormat format, int width, int height, const void* image,
137 SkIPoint16* loc) {
138 fPreserveStrike = strike;
139 return this->getAtlas(format)->addToAtlas(id, batchTarget, width, height, image, loc);
140 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700141
142 // Some clients may wish to verify the integrity of the texture backing store of the
143 // GrBatchAtlas. The atlasGeneration returned below is a monitonically increasing number which
144 // changes everytime something is removed from the texture backing store.
joshualitt62db8ba2015-04-09 08:22:37 -0700145 uint64_t atlasGeneration(GrMaskFormat format) const {
146 return this->getAtlas(format)->atlasGeneration();
147 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700148
joshualitt7c3a2f82015-03-31 13:32:05 -0700149 void dump() const;
150
151private:
bsalomon265697d2015-07-22 10:17:26 -0700152 static GrPixelConfig MaskFormatToPixelConfig(GrMaskFormat format) {
153 static const GrPixelConfig kPixelConfigs[] = {
154 kAlpha_8_GrPixelConfig,
155 kRGB_565_GrPixelConfig,
156 kSkia8888_GrPixelConfig
157 };
158 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, array_size_mismatch);
159
160 return kPixelConfigs[format];
161 }
162
joshualitt7c3a2f82015-03-31 13:32:05 -0700163 // There is a 1:1 mapping between GrMaskFormats and atlas indices
joshualitt62db8ba2015-04-09 08:22:37 -0700164 static int MaskFormatToAtlasIndex(GrMaskFormat format) {
165 static const int sAtlasIndices[] = {
166 kA8_GrMaskFormat,
167 kA565_GrMaskFormat,
168 kARGB_GrMaskFormat,
169 };
170 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
joshualitt7c3a2f82015-03-31 13:32:05 -0700171
joshualitt62db8ba2015-04-09 08:22:37 -0700172 SkASSERT(sAtlasIndices[format] < kMaskFormatCount);
173 return sAtlasIndices[format];
174 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700175
joshualitt62db8ba2015-04-09 08:22:37 -0700176 bool initAtlas(GrMaskFormat);
177
178 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) {
179 GrBatchTextStrike* strike = SkNEW_ARGS(GrBatchTextStrike, (this, scaler->getKey()));
180 fCache.add(strike);
181 return strike;
182 }
183
184 GrBatchAtlas* getAtlas(GrMaskFormat format) const {
185 int atlasIndex = MaskFormatToAtlasIndex(format);
186 SkASSERT(fAtlases[atlasIndex]);
187 return fAtlases[atlasIndex];
188 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700189
190 static void HandleEviction(GrBatchAtlas::AtlasID, void*);
191
joshualitt62db8ba2015-04-09 08:22:37 -0700192 GrContext* fContext;
joshualitt7c3a2f82015-03-31 13:32:05 -0700193 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache;
joshualitt7c3a2f82015-03-31 13:32:05 -0700194 GrBatchAtlas* fAtlases[kMaskFormatCount];
195 GrBatchTextStrike* fPreserveStrike;
196};
197
198#endif