blob: 6aa14e69daf271fd05f049c9acbb76fb820c78c4 [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 */
27class GrBatchTextStrike {
28public:
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
55 static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) {
56 return *(ts.fFontScalerKey);
57 }
58 static uint32_t Hash(const GrFontDescKey& key) {
59 return key.getHash();
60 }
61
62private:
63 SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
64 SkAutoTUnref<const GrFontDescKey> fFontScalerKey;
65 SkVarAlloc fPool;
66
67 GrBatchFontCache* fBatchFontCache;
68 int fAtlasedGlyphs;
69
70 GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
71
72 friend class GrBatchFontCache;
73};
74
75/*
76 * GrBatchFontCache manages strikes which are indexed by a GrFontScaler. These strikes can then be
77 * used to individual Glyph Masks. The GrBatchFontCache also manages GrBatchAtlases, though this is
joshualitt62db8ba2015-04-09 08:22:37 -070078 * more or less transparent to the client(aside from atlasGeneration, described below).
79 * Note - we used to initialize the backing atlas for the GrBatchFontCache at initialization time.
80 * However, this caused a regression, even when the GrBatchFontCache was unused. We now initialize
81 * the backing atlases lazily. Its not immediately clear why this improves the situation.
joshualitt7c3a2f82015-03-31 13:32:05 -070082 */
83class GrBatchFontCache {
84public:
joshualitt62db8ba2015-04-09 08:22:37 -070085 GrBatchFontCache(GrContext*);
joshualitt7c3a2f82015-03-31 13:32:05 -070086 ~GrBatchFontCache();
87
joshualitt7c3a2f82015-03-31 13:32:05 -070088 inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) {
joshualitt7c3a2f82015-03-31 13:32:05 -070089 GrBatchTextStrike* strike = fCache.find(*(scaler->getKey()));
90 if (NULL == strike) {
91 strike = this->generateStrike(scaler);
92 }
93 return strike;
94 }
95
joshualitt62db8ba2015-04-09 08:22:37 -070096 void freeAll();
97
98 // if getTexture returns NULL, the client must not try to use other functions on the
99 // GrBatchFontCache which use the atlas. This function *must* be called first, before other
100 // functions which use the atlas.
101 GrTexture* getTexture(GrMaskFormat format) {
102 if (this->initAtlas(format)) {
103 return this->getAtlas(format)->getTexture();
104 }
105 return NULL;
106 }
107
108 bool hasGlyph(GrGlyph* glyph) {
109 SkASSERT(glyph);
110 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
111 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700112
113 // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store,
joshualittb4c507e2015-04-08 08:07:59 -0700114 // the client must pass in the currentToken from the GrBatchTarget along with the GrGlyph.
115 // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas.
116 // For convenience, this function will also set the use token for the current glyph if required
117 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration
joshualitt62db8ba2015-04-09 08:22:37 -0700118 void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater,
119 GrGlyph* glyph, GrBatchAtlas::BatchToken token) {
120 SkASSERT(glyph);
121 updater->add(glyph->fID);
122 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
123 }
joshualittb4c507e2015-04-08 08:07:59 -0700124
joshualitt62db8ba2015-04-09 08:22:37 -0700125 void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater,
126 GrBatchAtlas::BatchToken token,
127 GrMaskFormat format) {
128 this->getAtlas(format)->setLastUseTokenBulk(updater, token);
129 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700130
131 // add to texture atlas that matches this format
joshualitt62db8ba2015-04-09 08:22:37 -0700132 bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id,
133 GrBatchTarget* batchTarget,
134 GrMaskFormat format, int width, int height, const void* image,
135 SkIPoint16* loc) {
136 fPreserveStrike = strike;
137 return this->getAtlas(format)->addToAtlas(id, batchTarget, width, height, image, loc);
138 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700139
140 // Some clients may wish to verify the integrity of the texture backing store of the
141 // GrBatchAtlas. The atlasGeneration returned below is a monitonically increasing number which
142 // changes everytime something is removed from the texture backing store.
joshualitt62db8ba2015-04-09 08:22:37 -0700143 uint64_t atlasGeneration(GrMaskFormat format) const {
144 return this->getAtlas(format)->atlasGeneration();
145 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700146
joshualitt7c3a2f82015-03-31 13:32:05 -0700147 GrPixelConfig getPixelConfig(GrMaskFormat) const;
148
149 void dump() const;
150
151private:
152 // There is a 1:1 mapping between GrMaskFormats and atlas indices
joshualitt62db8ba2015-04-09 08:22:37 -0700153 static int MaskFormatToAtlasIndex(GrMaskFormat format) {
154 static const int sAtlasIndices[] = {
155 kA8_GrMaskFormat,
156 kA565_GrMaskFormat,
157 kARGB_GrMaskFormat,
158 };
159 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
joshualitt7c3a2f82015-03-31 13:32:05 -0700160
joshualitt62db8ba2015-04-09 08:22:37 -0700161 SkASSERT(sAtlasIndices[format] < kMaskFormatCount);
162 return sAtlasIndices[format];
163 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700164
joshualitt62db8ba2015-04-09 08:22:37 -0700165 bool initAtlas(GrMaskFormat);
166
167 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) {
168 GrBatchTextStrike* strike = SkNEW_ARGS(GrBatchTextStrike, (this, scaler->getKey()));
169 fCache.add(strike);
170 return strike;
171 }
172
173 GrBatchAtlas* getAtlas(GrMaskFormat format) const {
174 int atlasIndex = MaskFormatToAtlasIndex(format);
175 SkASSERT(fAtlases[atlasIndex]);
176 return fAtlases[atlasIndex];
177 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700178
179 static void HandleEviction(GrBatchAtlas::AtlasID, void*);
180
joshualitt62db8ba2015-04-09 08:22:37 -0700181 GrContext* fContext;
joshualitt7c3a2f82015-03-31 13:32:05 -0700182 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache;
joshualitt7c3a2f82015-03-31 13:32:05 -0700183 GrBatchAtlas* fAtlases[kMaskFormatCount];
184 GrBatchTextStrike* fPreserveStrike;
185};
186
187#endif