OLD | NEW |
| (Empty) |
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 GrBatchFontCache_DEFINED | |
9 #define GrBatchFontCache_DEFINED | |
10 | |
11 #include "GrBatchAtlas.h" | |
12 #include "GrFontScaler.h" | |
13 #include "GrGlyph.h" | |
14 #include "SkGlyph.h" | |
15 #include "SkTDynamicHash.h" | |
16 #include "SkVarAlloc.h" | |
17 | |
18 class GrBatchFontCache; | |
19 class GrGpu; | |
20 | |
21 /** | |
22 * The GrBatchTextStrike manages a pool of CPU backing memory for GrGlyphs. Th
is backing memory | |
23 * is indexed by a PackedID and GrFontScaler. The GrFontScaler is what actuall
y creates the mask. | |
24 */ | |
25 class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> { | |
26 public: | |
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(const SkGlyph& skGlyph, GrGlyph::PackedID packed, | |
34 GrFontScaler* scaler) { | |
35 GrGlyph* glyph = fCache.find(packed); | |
36 if (nullptr == glyph) { | |
37 glyph = this->generateGlyph(skGlyph, packed, scaler); | |
38 } | |
39 return glyph; | |
40 } | |
41 | |
42 // This variant of the above function is called by TextBatch. At this point
, it is possible | |
43 // that the maskformat of the glyph differs from what we expect. In these c
ases we will just | |
44 // draw a clear square. | |
45 // skbug:4143 crbug:510931 | |
46 inline GrGlyph* getGlyph(GrGlyph::PackedID packed, | |
47 GrMaskFormat expectedMaskFormat, | |
48 GrFontScaler* scaler) { | |
49 GrGlyph* glyph = fCache.find(packed); | |
50 if (nullptr == glyph) { | |
51 // We could return this to the caller, but in practice it adds code
complexity for | |
52 // potentially little benefit(ie, if the glyph is not in our font ca
che, then its not | |
53 // in the atlas and we're going to be doing a texture upload anyways
). | |
54 const SkGlyph& skGlyph = scaler->grToSkGlyph(packed); | |
55 glyph = this->generateGlyph(skGlyph, packed, scaler); | |
56 glyph->fMaskFormat = expectedMaskFormat; | |
57 } | |
58 return glyph; | |
59 } | |
60 | |
61 // returns true if glyph successfully added to texture atlas, false otherwis
e. If the glyph's | |
62 // mask format has changed, then addGlyphToAtlas will draw a clear box. Thi
s will almost never | |
63 // happen. | |
64 // TODO we can handle some of these cases if we really want to, but the long
term solution is to | |
65 // get the actual glyph image itself when we get the glyph metrics. | |
66 bool addGlyphToAtlas(GrDrawBatch::Target*, GrGlyph*, GrFontScaler*, | |
67 GrMaskFormat expectedMaskFormat); | |
68 | |
69 // testing | |
70 int countGlyphs() const { return fCache.count(); } | |
71 | |
72 // remove any references to this plot | |
73 void removeID(GrBatchAtlas::AtlasID); | |
74 | |
75 // If a TextStrike is abandoned by the cache, then the caller must get a new
strike | |
76 bool isAbandoned() const { return fIsAbandoned; } | |
77 | |
78 static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) { | |
79 return *(ts.fFontScalerKey); | |
80 } | |
81 static uint32_t Hash(const GrFontDescKey& key) { | |
82 return key.getHash(); | |
83 } | |
84 | |
85 private: | |
86 SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache; | |
87 SkAutoTUnref<const GrFontDescKey> fFontScalerKey; | |
88 SkVarAlloc fPool; | |
89 | |
90 GrBatchFontCache* fBatchFontCache; | |
91 int fAtlasedGlyphs; | |
92 bool fIsAbandoned; | |
93 | |
94 GrGlyph* generateGlyph(const SkGlyph&, GrGlyph::PackedID, GrFontScaler*); | |
95 | |
96 friend class GrBatchFontCache; | |
97 }; | |
98 | |
99 /* | |
100 * GrBatchFontCache manages strikes which are indexed by a GrFontScaler. These
strikes can then be | |
101 * used to individual Glyph Masks. The GrBatchFontCache also manages GrBatchAtl
ases, though this is | |
102 * more or less transparent to the client(aside from atlasGeneration, described
below). | |
103 * Note - we used to initialize the backing atlas for the GrBatchFontCache at in
itialization time. | |
104 * However, this caused a regression, even when the GrBatchFontCache was unused.
We now initialize | |
105 * the backing atlases lazily. Its not immediately clear why this improves the
situation. | |
106 */ | |
107 class GrBatchFontCache { | |
108 public: | |
109 GrBatchFontCache(GrContext*); | |
110 ~GrBatchFontCache(); | |
111 // The user of the cache may hold a long-lived ref to the returned strike. H
owever, actions by | |
112 // another client of the cache may cause the strike to be purged while it is
still reffed. | |
113 // Therefore, the caller must check GrBatchTextStrike::isAbandoned() if ther
e are other | |
114 // interactions with the cache since the strike was received. | |
115 inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) { | |
116 GrBatchTextStrike* strike = fCache.find(*(scaler->getKey())); | |
117 if (nullptr == strike) { | |
118 strike = this->generateStrike(scaler); | |
119 } | |
120 return strike; | |
121 } | |
122 | |
123 void freeAll(); | |
124 | |
125 // if getTexture returns nullptr, the client must not try to use other funct
ions on the | |
126 // GrBatchFontCache which use the atlas. This function *must* be called fir
st, before other | |
127 // functions which use the atlas. | |
128 GrTexture* getTexture(GrMaskFormat format) { | |
129 if (this->initAtlas(format)) { | |
130 return this->getAtlas(format)->getTexture(); | |
131 } | |
132 return nullptr; | |
133 } | |
134 | |
135 bool hasGlyph(GrGlyph* glyph) { | |
136 SkASSERT(glyph); | |
137 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID); | |
138 } | |
139 | |
140 // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture
backing store, | |
141 // the client must pass in the current batch token along with the GrGlyph. | |
142 // A BulkUseTokenUpdater is used to manage bulk last use token updating in t
he Atlas. | |
143 // For convenience, this function will also set the use token for the curren
t glyph if required | |
144 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGene
ration | |
145 void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater
, | |
146 GrGlyph* glyph, GrBatchToken token) { | |
147 SkASSERT(glyph); | |
148 updater->add(glyph->fID); | |
149 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token); | |
150 } | |
151 | |
152 void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater, | |
153 GrBatchToken token, | |
154 GrMaskFormat format) { | |
155 this->getAtlas(format)->setLastUseTokenBulk(updater, token); | |
156 } | |
157 | |
158 // add to texture atlas that matches this format | |
159 bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id, | |
160 GrDrawBatch::Target* target, | |
161 GrMaskFormat format, int width, int height, const void* imag
e, | |
162 SkIPoint16* loc) { | |
163 fPreserveStrike = strike; | |
164 return this->getAtlas(format)->addToAtlas(id, target, width, height, ima
ge, loc); | |
165 } | |
166 | |
167 // Some clients may wish to verify the integrity of the texture backing stor
e of the | |
168 // GrBatchAtlas. The atlasGeneration returned below is a monitonically incr
easing number which | |
169 // changes everytime something is removed from the texture backing store. | |
170 uint64_t atlasGeneration(GrMaskFormat format) const { | |
171 return this->getAtlas(format)->atlasGeneration(); | |
172 } | |
173 | |
174 /////////////////////////////////////////////////////////////////////////// | |
175 // Functions intended debug only | |
176 void dump() const; | |
177 | |
178 void setAtlasSizes_ForTesting(const GrBatchAtlasConfig configs[3]); | |
179 | |
180 private: | |
181 static GrPixelConfig MaskFormatToPixelConfig(GrMaskFormat format) { | |
182 static const GrPixelConfig kPixelConfigs[] = { | |
183 kAlpha_8_GrPixelConfig, | |
184 kRGB_565_GrPixelConfig, | |
185 kSkia8888_GrPixelConfig | |
186 }; | |
187 static_assert(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, "array_
size_mismatch"); | |
188 | |
189 return kPixelConfigs[format]; | |
190 } | |
191 | |
192 // There is a 1:1 mapping between GrMaskFormats and atlas indices | |
193 static int MaskFormatToAtlasIndex(GrMaskFormat format) { | |
194 static const int sAtlasIndices[] = { | |
195 kA8_GrMaskFormat, | |
196 kA565_GrMaskFormat, | |
197 kARGB_GrMaskFormat, | |
198 }; | |
199 static_assert(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, "array_
size_mismatch"); | |
200 | |
201 SkASSERT(sAtlasIndices[format] < kMaskFormatCount); | |
202 return sAtlasIndices[format]; | |
203 } | |
204 | |
205 bool initAtlas(GrMaskFormat); | |
206 | |
207 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) { | |
208 GrBatchTextStrike* strike = new GrBatchTextStrike(this, scaler->getKey()
); | |
209 fCache.add(strike); | |
210 return strike; | |
211 } | |
212 | |
213 GrBatchAtlas* getAtlas(GrMaskFormat format) const { | |
214 int atlasIndex = MaskFormatToAtlasIndex(format); | |
215 SkASSERT(fAtlases[atlasIndex]); | |
216 return fAtlases[atlasIndex]; | |
217 } | |
218 | |
219 static void HandleEviction(GrBatchAtlas::AtlasID, void*); | |
220 | |
221 GrContext* fContext; | |
222 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache; | |
223 GrBatchAtlas* fAtlases[kMaskFormatCount]; | |
224 GrBatchTextStrike* fPreserveStrike; | |
225 GrBatchAtlasConfig fAtlasConfigs[kMaskFormatCount]; | |
226 }; | |
227 | |
228 #endif | |
OLD | NEW |