blob: 2b625291994ff2610f804e222d5879cc4695e25d [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/**
22 * The GrBatchTextStrike manages a pool of CPU backing memory for Glyph Masks. This backing memory
23 * is abstracted by GrGlyph, and indexed by a PackedID and GrFontScaler. The GrFontScaler is what
24 * actually creates the mask.
25 */
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
34 inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
35 GrGlyph* glyph = fCache.find(packed);
36 if (NULL == glyph) {
37 glyph = this->generateGlyph(packed, scaler);
38 }
39 return glyph;
40 }
41
joshualitt7c3a2f82015-03-31 13:32:05 -070042 // returns true if glyph successfully added to texture atlas, false otherwise
43 bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*);
44
45 // testing
46 int countGlyphs() const { return fCache.count(); }
47
48 // remove any references to this plot
49 void removeID(GrBatchAtlas::AtlasID);
50
joshualittae32c102015-04-21 09:37:57 -070051 // If a TextStrike is abandoned by the cache, then the caller must get a new strike
52 bool isAbandoned() const { return fIsAbandoned; }
53
joshualitt7c3a2f82015-03-31 13:32:05 -070054 static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) {
55 return *(ts.fFontScalerKey);
56 }
57 static uint32_t Hash(const GrFontDescKey& key) {
58 return key.getHash();
59 }
60
61private:
62 SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
63 SkAutoTUnref<const GrFontDescKey> fFontScalerKey;
64 SkVarAlloc fPool;
65
66 GrBatchFontCache* fBatchFontCache;
67 int fAtlasedGlyphs;
joshualittae32c102015-04-21 09:37:57 -070068 bool fIsAbandoned;
joshualitt7c3a2f82015-03-31 13:32:05 -070069
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();
joshualittae32c102015-04-21 09:37:57 -070087 // The user of the cache may hold a long-lived ref to the returned strike. However, actions by
88 // another client of the cache may cause the strike to be purged while it is still reffed.
89 // Therefore, the caller must check GrBatchTextStrike::isAbandoned() if there are other
90 // interactions with the cache since the strike was received.
joshualitt7c3a2f82015-03-31 13:32:05 -070091 inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) {
joshualitt7c3a2f82015-03-31 13:32:05 -070092 GrBatchTextStrike* strike = fCache.find(*(scaler->getKey()));
93 if (NULL == strike) {
94 strike = this->generateStrike(scaler);
95 }
96 return strike;
97 }
98
joshualitt62db8ba2015-04-09 08:22:37 -070099 void freeAll();
100
101 // if getTexture returns NULL, the client must not try to use other functions on the
102 // GrBatchFontCache which use the atlas. This function *must* be called first, before other
103 // functions which use the atlas.
104 GrTexture* getTexture(GrMaskFormat format) {
105 if (this->initAtlas(format)) {
106 return this->getAtlas(format)->getTexture();
107 }
108 return NULL;
109 }
110
111 bool hasGlyph(GrGlyph* glyph) {
112 SkASSERT(glyph);
113 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
114 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700115
116 // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store,
joshualittb4c507e2015-04-08 08:07:59 -0700117 // the client must pass in the currentToken from the GrBatchTarget along with the GrGlyph.
118 // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas.
119 // For convenience, this function will also set the use token for the current glyph if required
120 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration
joshualitt62db8ba2015-04-09 08:22:37 -0700121 void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater,
122 GrGlyph* glyph, GrBatchAtlas::BatchToken token) {
123 SkASSERT(glyph);
124 updater->add(glyph->fID);
125 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
126 }
joshualittb4c507e2015-04-08 08:07:59 -0700127
joshualitt62db8ba2015-04-09 08:22:37 -0700128 void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater,
129 GrBatchAtlas::BatchToken token,
130 GrMaskFormat format) {
131 this->getAtlas(format)->setLastUseTokenBulk(updater, token);
132 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700133
134 // add to texture atlas that matches this format
joshualitt62db8ba2015-04-09 08:22:37 -0700135 bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id,
136 GrBatchTarget* batchTarget,
137 GrMaskFormat format, int width, int height, const void* image,
138 SkIPoint16* loc) {
139 fPreserveStrike = strike;
140 return this->getAtlas(format)->addToAtlas(id, batchTarget, width, height, image, loc);
141 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700142
143 // Some clients may wish to verify the integrity of the texture backing store of the
144 // GrBatchAtlas. The atlasGeneration returned below is a monitonically increasing number which
145 // changes everytime something is removed from the texture backing store.
joshualitt62db8ba2015-04-09 08:22:37 -0700146 uint64_t atlasGeneration(GrMaskFormat format) const {
147 return this->getAtlas(format)->atlasGeneration();
148 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700149
joshualitt7c3a2f82015-03-31 13:32:05 -0700150 void dump() const;
151
152private:
bsalomon265697d2015-07-22 10:17:26 -0700153 static GrPixelConfig MaskFormatToPixelConfig(GrMaskFormat format) {
154 static const GrPixelConfig kPixelConfigs[] = {
155 kAlpha_8_GrPixelConfig,
156 kRGB_565_GrPixelConfig,
157 kSkia8888_GrPixelConfig
158 };
159 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, array_size_mismatch);
160
161 return kPixelConfigs[format];
162 }
163
joshualitt7c3a2f82015-03-31 13:32:05 -0700164 // There is a 1:1 mapping between GrMaskFormats and atlas indices
joshualitt62db8ba2015-04-09 08:22:37 -0700165 static int MaskFormatToAtlasIndex(GrMaskFormat format) {
166 static const int sAtlasIndices[] = {
167 kA8_GrMaskFormat,
168 kA565_GrMaskFormat,
169 kARGB_GrMaskFormat,
170 };
171 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
joshualitt7c3a2f82015-03-31 13:32:05 -0700172
joshualitt62db8ba2015-04-09 08:22:37 -0700173 SkASSERT(sAtlasIndices[format] < kMaskFormatCount);
174 return sAtlasIndices[format];
175 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700176
joshualitt62db8ba2015-04-09 08:22:37 -0700177 bool initAtlas(GrMaskFormat);
178
179 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) {
180 GrBatchTextStrike* strike = SkNEW_ARGS(GrBatchTextStrike, (this, scaler->getKey()));
181 fCache.add(strike);
182 return strike;
183 }
184
185 GrBatchAtlas* getAtlas(GrMaskFormat format) const {
186 int atlasIndex = MaskFormatToAtlasIndex(format);
187 SkASSERT(fAtlases[atlasIndex]);
188 return fAtlases[atlasIndex];
189 }
joshualitt7c3a2f82015-03-31 13:32:05 -0700190
191 static void HandleEviction(GrBatchAtlas::AtlasID, void*);
192
joshualitt62db8ba2015-04-09 08:22:37 -0700193 GrContext* fContext;
joshualitt7c3a2f82015-03-31 13:32:05 -0700194 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache;
joshualitt7c3a2f82015-03-31 13:32:05 -0700195 GrBatchAtlas* fAtlases[kMaskFormatCount];
196 GrBatchTextStrike* fPreserveStrike;
197};
198
199#endif