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