blob: f60e2c8ebfbb1b204d7fd85b9c363ab37123d374 [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"
12#include "GrDrawTarget.h"
13#include "GrFontScaler.h"
14#include "GrGlyph.h"
15#include "SkTDynamicHash.h"
16#include "SkVarAlloc.h"
17
18class GrBatchFontCache;
19class GrBatchTarget;
20class GrGpu;
21
22/**
23 * The GrBatchTextStrike manages a pool of CPU backing memory for Glyph Masks. This backing memory
24 * is abstracted by GrGlyph, and indexed by a PackedID and GrFontScaler. The GrFontScaler is what
25 * actually creates the mask.
26 */
joshualittae32c102015-04-21 09:37:57 -070027class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> {
joshualitt7c3a2f82015-03-31 13:32:05 -070028public:
29 GrBatchTextStrike(GrBatchFontCache*, const GrFontDescKey* fontScalerKey);
30 ~GrBatchTextStrike();
31
32 const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
33 GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; }
34
35 inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
36 GrGlyph* glyph = fCache.find(packed);
37 if (NULL == glyph) {
38 glyph = this->generateGlyph(packed, scaler);
39 }
40 return glyph;
41 }
42
43 // returns true if glyph (or glyph+padding for distance field)
44 // is too large to ever fit in texture atlas subregions (GrPlots)
45 bool glyphTooLargeForAtlas(GrGlyph*);
46 // returns true if glyph successfully added to texture atlas, false otherwise
47 bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*);
48
49 // testing
50 int countGlyphs() const { return fCache.count(); }
51
52 // remove any references to this plot
53 void removeID(GrBatchAtlas::AtlasID);
54
joshualittae32c102015-04-21 09:37:57 -070055 // If a TextStrike is abandoned by the cache, then the caller must get a new strike
56 bool isAbandoned() const { return fIsAbandoned; }
57
joshualitt7c3a2f82015-03-31 13:32:05 -070058 static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) {
59 return *(ts.fFontScalerKey);
60 }
61 static uint32_t Hash(const GrFontDescKey& key) {
62 return key.getHash();
63 }
64
65private:
66 SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
67 SkAutoTUnref<const GrFontDescKey> fFontScalerKey;
68 SkVarAlloc fPool;
69
70 GrBatchFontCache* fBatchFontCache;
71 int fAtlasedGlyphs;
joshualittae32c102015-04-21 09:37:57 -070072 bool fIsAbandoned;
joshualitt7c3a2f82015-03-31 13:32:05 -070073
74 GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
75
76 friend class GrBatchFontCache;
77};
78
79/*
80 * GrBatchFontCache manages strikes which are indexed by a GrFontScaler. These strikes can then be
81 * used to individual Glyph Masks. The GrBatchFontCache also manages GrBatchAtlases, though this is
joshualitt62db8ba2015-04-09 08:22:37 -070082 * more or less transparent to the client(aside from atlasGeneration, described below).
83 * Note - we used to initialize the backing atlas for the GrBatchFontCache at initialization time.
84 * However, this caused a regression, even when the GrBatchFontCache was unused. We now initialize
85 * the backing atlases lazily. Its not immediately clear why this improves the situation.
joshualitt7c3a2f82015-03-31 13:32:05 -070086 */
87class GrBatchFontCache {
88public:
joshualitt62db8ba2015-04-09 08:22:37 -070089 GrBatchFontCache(GrContext*);
joshualitt7c3a2f82015-03-31 13:32:05 -070090 ~GrBatchFontCache();
joshualittae32c102015-04-21 09:37:57 -070091 // The user of the cache may hold a long-lived ref to the returned strike. However, actions by
92 // another client of the cache may cause the strike to be purged while it is still reffed.
93 // Therefore, the caller must check GrBatchTextStrike::isAbandoned() if there are other
94 // interactions with the cache since the strike was received.
joshualitt7c3a2f82015-03-31 13:32:05 -070095 inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) {
joshualitt7c3a2f82015-03-31 13:32:05 -070096 GrBatchTextStrike* strike = fCache.find(*(scaler->getKey()));
97 if (NULL == strike) {
98 strike = this->generateStrike(scaler);
99 }
100 return strike;
101 }
102
joshualitt62db8ba2015-04-09 08:22:37 -0700103 void freeAll();
104
105 // if getTexture returns NULL, the client must not try to use other functions on the
106 // GrBatchFontCache which use the atlas. This function *must* be called first, before other
107 // functions which use the atlas.
108 GrTexture* getTexture(GrMaskFormat format) {
109 if (this->initAtlas(format)) {
110 return this->getAtlas(format)->getTexture();
111 }
112 return NULL;
113 }
114
115 bool hasGlyph(GrGlyph* glyph) {
116 SkASSERT(glyph);
117 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
118 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700119
120 // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store,
joshualittb4c507e2015-04-08 08:07:59 -0700121 // the client must pass in the currentToken from the GrBatchTarget along with the GrGlyph.
122 // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas.
123 // For convenience, this function will also set the use token for the current glyph if required
124 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration
joshualitt62db8ba2015-04-09 08:22:37 -0700125 void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater,
126 GrGlyph* glyph, GrBatchAtlas::BatchToken token) {
127 SkASSERT(glyph);
128 updater->add(glyph->fID);
129 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
130 }
joshualittb4c507e2015-04-08 08:07:59 -0700131
joshualitt62db8ba2015-04-09 08:22:37 -0700132 void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater,
133 GrBatchAtlas::BatchToken token,
134 GrMaskFormat format) {
135 this->getAtlas(format)->setLastUseTokenBulk(updater, token);
136 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700137
138 // add to texture atlas that matches this format
joshualitt62db8ba2015-04-09 08:22:37 -0700139 bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id,
140 GrBatchTarget* batchTarget,
141 GrMaskFormat format, int width, int height, const void* image,
142 SkIPoint16* loc) {
143 fPreserveStrike = strike;
144 return this->getAtlas(format)->addToAtlas(id, batchTarget, width, height, image, loc);
145 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700146
147 // Some clients may wish to verify the integrity of the texture backing store of the
148 // GrBatchAtlas. The atlasGeneration returned below is a monitonically increasing number which
149 // changes everytime something is removed from the texture backing store.
joshualitt62db8ba2015-04-09 08:22:37 -0700150 uint64_t atlasGeneration(GrMaskFormat format) const {
151 return this->getAtlas(format)->atlasGeneration();
152 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700153
joshualitt7c3a2f82015-03-31 13:32:05 -0700154 GrPixelConfig getPixelConfig(GrMaskFormat) const;
155
156 void dump() const;
157
158private:
159 // There is a 1:1 mapping between GrMaskFormats and atlas indices
joshualitt62db8ba2015-04-09 08:22:37 -0700160 static int MaskFormatToAtlasIndex(GrMaskFormat format) {
161 static const int sAtlasIndices[] = {
162 kA8_GrMaskFormat,
163 kA565_GrMaskFormat,
164 kARGB_GrMaskFormat,
165 };
166 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
joshualitt7c3a2f82015-03-31 13:32:05 -0700167
joshualitt62db8ba2015-04-09 08:22:37 -0700168 SkASSERT(sAtlasIndices[format] < kMaskFormatCount);
169 return sAtlasIndices[format];
170 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700171
joshualitt62db8ba2015-04-09 08:22:37 -0700172 bool initAtlas(GrMaskFormat);
173
174 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) {
175 GrBatchTextStrike* strike = SkNEW_ARGS(GrBatchTextStrike, (this, scaler->getKey()));
176 fCache.add(strike);
177 return strike;
178 }
179
180 GrBatchAtlas* getAtlas(GrMaskFormat format) const {
181 int atlasIndex = MaskFormatToAtlasIndex(format);
182 SkASSERT(fAtlases[atlasIndex]);
183 return fAtlases[atlasIndex];
184 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700185
186 static void HandleEviction(GrBatchAtlas::AtlasID, void*);
187
joshualitt62db8ba2015-04-09 08:22:37 -0700188 GrContext* fContext;
joshualitt7c3a2f82015-03-31 13:32:05 -0700189 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache;
joshualitt7c3a2f82015-03-31 13:32:05 -0700190 GrBatchAtlas* fAtlases[kMaskFormatCount];
191 GrBatchTextStrike* fPreserveStrike;
192};
193
194#endif