blob: 447fc3f99167e1174ba8189e15773ecac6a9d913 [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"
joshualitt6c2c2b02015-07-24 10:37:00 -070014#include "SkGlyph.h"
joshualitt7c3a2f82015-03-31 13:32:05 -070015#include "SkTDynamicHash.h"
16#include "SkVarAlloc.h"
17
18class GrBatchFontCache;
19class GrBatchTarget;
20class GrGpu;
21
22/**
joshualitt4765bdc2015-07-23 10:58:48 -070023 * The GrBatchTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory
24 * is indexed by a PackedID and GrFontScaler. The GrFontScaler is what actually creates the mask.
joshualitt7c3a2f82015-03-31 13:32:05 -070025 */
joshualittae32c102015-04-21 09:37:57 -070026class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> {
joshualitt7c3a2f82015-03-31 13:32:05 -070027public:
28 GrBatchTextStrike(GrBatchFontCache*, const GrFontDescKey* fontScalerKey);
29 ~GrBatchTextStrike();
30
31 const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
32 GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; }
33
joshualitt6c2c2b02015-07-24 10:37:00 -070034 inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
35 GrFontScaler* scaler) {
joshualitt7c3a2f82015-03-31 13:32:05 -070036 GrGlyph* glyph = fCache.find(packed);
37 if (NULL == glyph) {
joshualitt6c2c2b02015-07-24 10:37:00 -070038 glyph = this->generateGlyph(skGlyph, packed, scaler);
joshualitt7c3a2f82015-03-31 13:32:05 -070039 }
40 return glyph;
41 }
42
joshualitt4f19ca32015-07-30 07:59:20 -070043 // returns true if glyph successfully added to texture atlas, false otherwise. If the glyph's
44 // mask format has changed, then addGlyphToAtlas will draw a clear box. This will almost never
45 // happen.
46 // TODO we can handle some of these cases if we really want to, but the long term solution is to
47 // get the actual glyph image itself when we get the glyph metrics.
48 bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*, const SkGlyph&,
49 GrMaskFormat expectedMaskFormat);
joshualitt7c3a2f82015-03-31 13:32:05 -070050
51 // testing
52 int countGlyphs() const { return fCache.count(); }
53
54 // remove any references to this plot
55 void removeID(GrBatchAtlas::AtlasID);
56
joshualittae32c102015-04-21 09:37:57 -070057 // If a TextStrike is abandoned by the cache, then the caller must get a new strike
58 bool isAbandoned() const { return fIsAbandoned; }
59
joshualitt7c3a2f82015-03-31 13:32:05 -070060 static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) {
61 return *(ts.fFontScalerKey);
62 }
63 static uint32_t Hash(const GrFontDescKey& key) {
64 return key.getHash();
65 }
66
67private:
68 SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
69 SkAutoTUnref<const GrFontDescKey> fFontScalerKey;
70 SkVarAlloc fPool;
71
72 GrBatchFontCache* fBatchFontCache;
73 int fAtlasedGlyphs;
joshualittae32c102015-04-21 09:37:57 -070074 bool fIsAbandoned;
joshualitt7c3a2f82015-03-31 13:32:05 -070075
joshualitt6c2c2b02015-07-24 10:37:00 -070076 GrGlyph* generateGlyph(const SkGlyph&, GrGlyph::PackedID, GrFontScaler*);
joshualitt7c3a2f82015-03-31 13:32:05 -070077
78 friend class GrBatchFontCache;
79};
80
81/*
82 * GrBatchFontCache manages strikes which are indexed by a GrFontScaler. These strikes can then be
83 * used to individual Glyph Masks. The GrBatchFontCache also manages GrBatchAtlases, though this is
joshualitt62db8ba2015-04-09 08:22:37 -070084 * more or less transparent to the client(aside from atlasGeneration, described below).
85 * Note - we used to initialize the backing atlas for the GrBatchFontCache at initialization time.
86 * However, this caused a regression, even when the GrBatchFontCache was unused. We now initialize
87 * the backing atlases lazily. Its not immediately clear why this improves the situation.
joshualitt7c3a2f82015-03-31 13:32:05 -070088 */
89class GrBatchFontCache {
90public:
joshualitt62db8ba2015-04-09 08:22:37 -070091 GrBatchFontCache(GrContext*);
joshualitt7c3a2f82015-03-31 13:32:05 -070092 ~GrBatchFontCache();
joshualittae32c102015-04-21 09:37:57 -070093 // The user of the cache may hold a long-lived ref to the returned strike. However, actions by
94 // another client of the cache may cause the strike to be purged while it is still reffed.
95 // Therefore, the caller must check GrBatchTextStrike::isAbandoned() if there are other
96 // interactions with the cache since the strike was received.
joshualitt7c3a2f82015-03-31 13:32:05 -070097 inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) {
joshualitt7c3a2f82015-03-31 13:32:05 -070098 GrBatchTextStrike* strike = fCache.find(*(scaler->getKey()));
99 if (NULL == strike) {
100 strike = this->generateStrike(scaler);
101 }
102 return strike;
103 }
104
joshualitt62db8ba2015-04-09 08:22:37 -0700105 void freeAll();
106
107 // if getTexture returns NULL, the client must not try to use other functions on the
108 // GrBatchFontCache which use the atlas. This function *must* be called first, before other
109 // functions which use the atlas.
110 GrTexture* getTexture(GrMaskFormat format) {
111 if (this->initAtlas(format)) {
112 return this->getAtlas(format)->getTexture();
113 }
114 return NULL;
115 }
116
117 bool hasGlyph(GrGlyph* glyph) {
118 SkASSERT(glyph);
119 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
120 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700121
122 // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store,
joshualittb4c507e2015-04-08 08:07:59 -0700123 // the client must pass in the currentToken from the GrBatchTarget along with the GrGlyph.
124 // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas.
125 // For convenience, this function will also set the use token for the current glyph if required
126 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration
joshualitt62db8ba2015-04-09 08:22:37 -0700127 void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater,
128 GrGlyph* glyph, GrBatchAtlas::BatchToken token) {
129 SkASSERT(glyph);
130 updater->add(glyph->fID);
131 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
132 }
joshualittb4c507e2015-04-08 08:07:59 -0700133
joshualitt62db8ba2015-04-09 08:22:37 -0700134 void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater,
135 GrBatchAtlas::BatchToken token,
136 GrMaskFormat format) {
137 this->getAtlas(format)->setLastUseTokenBulk(updater, token);
138 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700139
140 // add to texture atlas that matches this format
joshualitt62db8ba2015-04-09 08:22:37 -0700141 bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id,
142 GrBatchTarget* batchTarget,
143 GrMaskFormat format, int width, int height, const void* image,
144 SkIPoint16* loc) {
145 fPreserveStrike = strike;
146 return this->getAtlas(format)->addToAtlas(id, batchTarget, width, height, image, loc);
147 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700148
149 // Some clients may wish to verify the integrity of the texture backing store of the
150 // GrBatchAtlas. The atlasGeneration returned below is a monitonically increasing number which
151 // changes everytime something is removed from the texture backing store.
joshualitt62db8ba2015-04-09 08:22:37 -0700152 uint64_t atlasGeneration(GrMaskFormat format) const {
153 return this->getAtlas(format)->atlasGeneration();
154 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700155
joshualitt7c3a2f82015-03-31 13:32:05 -0700156 void dump() const;
157
158private:
bsalomon265697d2015-07-22 10:17:26 -0700159 static GrPixelConfig MaskFormatToPixelConfig(GrMaskFormat format) {
160 static const GrPixelConfig kPixelConfigs[] = {
161 kAlpha_8_GrPixelConfig,
162 kRGB_565_GrPixelConfig,
163 kSkia8888_GrPixelConfig
164 };
165 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, array_size_mismatch);
166
167 return kPixelConfigs[format];
168 }
169
joshualitt7c3a2f82015-03-31 13:32:05 -0700170 // There is a 1:1 mapping between GrMaskFormats and atlas indices
joshualitt62db8ba2015-04-09 08:22:37 -0700171 static int MaskFormatToAtlasIndex(GrMaskFormat format) {
172 static const int sAtlasIndices[] = {
173 kA8_GrMaskFormat,
174 kA565_GrMaskFormat,
175 kARGB_GrMaskFormat,
176 };
177 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
joshualitt7c3a2f82015-03-31 13:32:05 -0700178
joshualitt62db8ba2015-04-09 08:22:37 -0700179 SkASSERT(sAtlasIndices[format] < kMaskFormatCount);
180 return sAtlasIndices[format];
181 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700182
joshualitt62db8ba2015-04-09 08:22:37 -0700183 bool initAtlas(GrMaskFormat);
184
185 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) {
186 GrBatchTextStrike* strike = SkNEW_ARGS(GrBatchTextStrike, (this, scaler->getKey()));
187 fCache.add(strike);
188 return strike;
189 }
190
191 GrBatchAtlas* getAtlas(GrMaskFormat format) const {
192 int atlasIndex = MaskFormatToAtlasIndex(format);
193 SkASSERT(fAtlases[atlasIndex]);
194 return fAtlases[atlasIndex];
195 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700196
197 static void HandleEviction(GrBatchAtlas::AtlasID, void*);
198
joshualitt62db8ba2015-04-09 08:22:37 -0700199 GrContext* fContext;
joshualitt7c3a2f82015-03-31 13:32:05 -0700200 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache;
joshualitt7c3a2f82015-03-31 13:32:05 -0700201 GrBatchAtlas* fAtlases[kMaskFormatCount];
202 GrBatchTextStrike* fPreserveStrike;
203};
204
205#endif