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 "GrDrawTarget.h"
13 #include "GrFontScaler.h"
14 #include "GrGlyph.h"
15 #include "SkTDynamicHash.h"
16 #include "SkVarAlloc.h"
17 
18 class GrBatchFontCache;
19 class GrBatchTarget;
20 class 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  */
27 class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> {
28 public:
29     GrBatchTextStrike(GrBatchFontCache*, const GrFontDescKey* fontScalerKey);
30     ~GrBatchTextStrike();
31 
getFontScalerKey()32     const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
getBatchFontCache()33     GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; }
34 
getGlyph(GrGlyph::PackedID packed,GrFontScaler * scaler)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 successfully added to texture atlas, false otherwise
44     bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*);
45 
46     // testing
countGlyphs()47     int countGlyphs() const { return fCache.count(); }
48 
49     // remove any references to this plot
50     void removeID(GrBatchAtlas::AtlasID);
51 
52     // If a TextStrike is abandoned by the cache, then the caller must get a new strike
isAbandoned()53     bool isAbandoned() const { return fIsAbandoned; }
54 
GetKey(const GrBatchTextStrike & ts)55     static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) {
56         return *(ts.fFontScalerKey);
57     }
Hash(const GrFontDescKey & key)58     static uint32_t Hash(const GrFontDescKey& key) {
59         return key.getHash();
60     }
61 
62 private:
63     SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
64     SkAutoTUnref<const GrFontDescKey> fFontScalerKey;
65     SkVarAlloc fPool;
66 
67     GrBatchFontCache* fBatchFontCache;
68     int fAtlasedGlyphs;
69     bool fIsAbandoned;
70 
71     GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
72 
73     friend class GrBatchFontCache;
74 };
75 
76 /*
77  * GrBatchFontCache manages strikes which are indexed by a GrFontScaler.  These strikes can then be
78  * used to individual Glyph Masks.  The GrBatchFontCache also manages GrBatchAtlases, though this is
79  * more or less transparent to the client(aside from atlasGeneration, described below).
80  * Note - we used to initialize the backing atlas for the GrBatchFontCache at initialization time.
81  * However, this caused a regression, even when the GrBatchFontCache was unused.  We now initialize
82  * the backing atlases lazily.  Its not immediately clear why this improves the situation.
83  */
84 class GrBatchFontCache {
85 public:
86     GrBatchFontCache(GrContext*);
87     ~GrBatchFontCache();
88     // The user of the cache may hold a long-lived ref to the returned strike. However, actions by
89     // another client of the cache may cause the strike to be purged while it is still reffed.
90     // Therefore, the caller must check GrBatchTextStrike::isAbandoned() if there are other
91     // interactions with the cache since the strike was received.
getStrike(GrFontScaler * scaler)92     inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) {
93         GrBatchTextStrike* strike = fCache.find(*(scaler->getKey()));
94         if (NULL == strike) {
95             strike = this->generateStrike(scaler);
96         }
97         return strike;
98     }
99 
100     void freeAll();
101 
102     // if getTexture returns NULL, the client must not try to use other functions on the
103     // GrBatchFontCache which use the atlas.  This function *must* be called first, before other
104     // functions which use the atlas.
getTexture(GrMaskFormat format)105     GrTexture* getTexture(GrMaskFormat format) {
106         if (this->initAtlas(format)) {
107             return this->getAtlas(format)->getTexture();
108         }
109         return NULL;
110     }
111 
hasGlyph(GrGlyph * glyph)112     bool hasGlyph(GrGlyph* glyph) {
113         SkASSERT(glyph);
114         return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
115     }
116 
117     // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store,
118     // the client must pass in the currentToken from the GrBatchTarget along with the GrGlyph.
119     // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas.
120     // For convenience, this function will also set the use token for the current glyph if required
121     // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration
addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater * updater,GrGlyph * glyph,GrBatchAtlas::BatchToken token)122     void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater,
123                                       GrGlyph* glyph, GrBatchAtlas::BatchToken token) {
124         SkASSERT(glyph);
125         updater->add(glyph->fID);
126         this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
127     }
128 
setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater & updater,GrBatchAtlas::BatchToken token,GrMaskFormat format)129     void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater,
130                          GrBatchAtlas::BatchToken token,
131                          GrMaskFormat format) {
132         this->getAtlas(format)->setLastUseTokenBulk(updater, token);
133     }
134 
135     // add to texture atlas that matches this format
addToAtlas(GrBatchTextStrike * strike,GrBatchAtlas::AtlasID * id,GrBatchTarget * batchTarget,GrMaskFormat format,int width,int height,const void * image,SkIPoint16 * loc)136     bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id,
137                     GrBatchTarget* batchTarget,
138                     GrMaskFormat format, int width, int height, const void* image,
139                     SkIPoint16* loc) {
140         fPreserveStrike = strike;
141         return this->getAtlas(format)->addToAtlas(id, batchTarget, width, height, image, loc);
142     }
143 
144     // Some clients may wish to verify the integrity of the texture backing store of the
145     // GrBatchAtlas.  The atlasGeneration returned below is a monitonically increasing number which
146     // changes everytime something is removed from the texture backing store.
atlasGeneration(GrMaskFormat format)147     uint64_t atlasGeneration(GrMaskFormat format) const {
148         return this->getAtlas(format)->atlasGeneration();
149     }
150 
151     GrPixelConfig getPixelConfig(GrMaskFormat) const;
152 
153     void dump() const;
154 
155 private:
156     // There is a 1:1 mapping between GrMaskFormats and atlas indices
MaskFormatToAtlasIndex(GrMaskFormat format)157     static int MaskFormatToAtlasIndex(GrMaskFormat format) {
158         static const int sAtlasIndices[] = {
159             kA8_GrMaskFormat,
160             kA565_GrMaskFormat,
161             kARGB_GrMaskFormat,
162         };
163         SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
164 
165         SkASSERT(sAtlasIndices[format] < kMaskFormatCount);
166         return sAtlasIndices[format];
167     }
168 
169     bool initAtlas(GrMaskFormat);
170 
generateStrike(GrFontScaler * scaler)171     GrBatchTextStrike* generateStrike(GrFontScaler* scaler) {
172         GrBatchTextStrike* strike = SkNEW_ARGS(GrBatchTextStrike, (this, scaler->getKey()));
173         fCache.add(strike);
174         return strike;
175     }
176 
getAtlas(GrMaskFormat format)177     GrBatchAtlas* getAtlas(GrMaskFormat format) const {
178         int atlasIndex = MaskFormatToAtlasIndex(format);
179         SkASSERT(fAtlases[atlasIndex]);
180         return fAtlases[atlasIndex];
181     }
182 
183     static void HandleEviction(GrBatchAtlas::AtlasID, void*);
184 
185     GrContext* fContext;
186     SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache;
187     GrBatchAtlas* fAtlases[kMaskFormatCount];
188     GrBatchTextStrike* fPreserveStrike;
189 };
190 
191 #endif
192