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 "GrFontScaler.h" 13 #include "GrGlyph.h" 14 #include "SkGlyph.h" 15 #include "SkTDynamicHash.h" 16 #include "SkVarAlloc.h" 17 18 class GrBatchFontCache; 19 class GrGpu; 20 21 /** 22 * The GrBatchTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory 23 * is indexed by a PackedID and GrFontScaler. The GrFontScaler is what actually creates the mask. 24 */ 25 class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> { 26 public: 27 GrBatchTextStrike(GrBatchFontCache*, const GrFontDescKey* fontScalerKey); 28 ~GrBatchTextStrike(); 29 getFontScalerKey()30 const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; } getBatchFontCache()31 GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; } 32 getGlyph(const SkGlyph & skGlyph,GrGlyph::PackedID packed,GrFontScaler * scaler)33 inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed, 34 GrFontScaler* scaler) { 35 GrGlyph* glyph = fCache.find(packed); 36 if (nullptr == glyph) { 37 glyph = this->generateGlyph(skGlyph, packed, scaler); 38 } 39 return glyph; 40 } 41 42 // This variant of the above function is called by TextBatch. At this point, it is possible 43 // that the maskformat of the glyph differs from what we expect. In these cases we will just 44 // draw a clear square. 45 // skbug:4143 crbug:510931 getGlyph(GrGlyph::PackedID packed,GrMaskFormat expectedMaskFormat,GrFontScaler * scaler)46 inline GrGlyph* getGlyph(GrGlyph::PackedID packed, 47 GrMaskFormat expectedMaskFormat, 48 GrFontScaler* scaler) { 49 GrGlyph* glyph = fCache.find(packed); 50 if (nullptr == glyph) { 51 // We could return this to the caller, but in practice it adds code complexity for 52 // potentially little benefit(ie, if the glyph is not in our font cache, then its not 53 // in the atlas and we're going to be doing a texture upload anyways). 54 const SkGlyph& skGlyph = scaler->grToSkGlyph(packed); 55 glyph = this->generateGlyph(skGlyph, packed, scaler); 56 glyph->fMaskFormat = expectedMaskFormat; 57 } 58 return glyph; 59 } 60 61 // returns true if glyph successfully added to texture atlas, false otherwise. If the glyph's 62 // mask format has changed, then addGlyphToAtlas will draw a clear box. This will almost never 63 // happen. 64 // TODO we can handle some of these cases if we really want to, but the long term solution is to 65 // get the actual glyph image itself when we get the glyph metrics. 66 bool addGlyphToAtlas(GrDrawBatch::Target*, GrGlyph*, GrFontScaler*, 67 GrMaskFormat expectedMaskFormat); 68 69 // testing countGlyphs()70 int countGlyphs() const { return fCache.count(); } 71 72 // remove any references to this plot 73 void removeID(GrBatchAtlas::AtlasID); 74 75 // If a TextStrike is abandoned by the cache, then the caller must get a new strike isAbandoned()76 bool isAbandoned() const { return fIsAbandoned; } 77 GetKey(const GrBatchTextStrike & ts)78 static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) { 79 return *(ts.fFontScalerKey); 80 } Hash(const GrFontDescKey & key)81 static uint32_t Hash(const GrFontDescKey& key) { 82 return key.getHash(); 83 } 84 85 private: 86 SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache; 87 SkAutoTUnref<const GrFontDescKey> fFontScalerKey; 88 SkVarAlloc fPool; 89 90 GrBatchFontCache* fBatchFontCache; 91 int fAtlasedGlyphs; 92 bool fIsAbandoned; 93 94 GrGlyph* generateGlyph(const SkGlyph&, GrGlyph::PackedID, GrFontScaler*); 95 96 friend class GrBatchFontCache; 97 }; 98 99 /* 100 * GrBatchFontCache manages strikes which are indexed by a GrFontScaler. These strikes can then be 101 * used to individual Glyph Masks. The GrBatchFontCache also manages GrBatchAtlases, though this is 102 * more or less transparent to the client(aside from atlasGeneration, described below). 103 * Note - we used to initialize the backing atlas for the GrBatchFontCache at initialization time. 104 * However, this caused a regression, even when the GrBatchFontCache was unused. We now initialize 105 * the backing atlases lazily. Its not immediately clear why this improves the situation. 106 */ 107 class GrBatchFontCache { 108 public: 109 GrBatchFontCache(GrContext*); 110 ~GrBatchFontCache(); 111 // The user of the cache may hold a long-lived ref to the returned strike. However, actions by 112 // another client of the cache may cause the strike to be purged while it is still reffed. 113 // Therefore, the caller must check GrBatchTextStrike::isAbandoned() if there are other 114 // interactions with the cache since the strike was received. getStrike(GrFontScaler * scaler)115 inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) { 116 GrBatchTextStrike* strike = fCache.find(*(scaler->getKey())); 117 if (nullptr == strike) { 118 strike = this->generateStrike(scaler); 119 } 120 return strike; 121 } 122 123 void freeAll(); 124 125 // if getTexture returns nullptr, the client must not try to use other functions on the 126 // GrBatchFontCache which use the atlas. This function *must* be called first, before other 127 // functions which use the atlas. getTexture(GrMaskFormat format)128 GrTexture* getTexture(GrMaskFormat format) { 129 if (this->initAtlas(format)) { 130 return this->getAtlas(format)->getTexture(); 131 } 132 return nullptr; 133 } 134 hasGlyph(GrGlyph * glyph)135 bool hasGlyph(GrGlyph* glyph) { 136 SkASSERT(glyph); 137 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID); 138 } 139 140 // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store, 141 // the client must pass in the current batch token along with the GrGlyph. 142 // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas. 143 // For convenience, this function will also set the use token for the current glyph if required 144 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater * updater,GrGlyph * glyph,GrBatchToken token)145 void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater, 146 GrGlyph* glyph, GrBatchToken token) { 147 SkASSERT(glyph); 148 updater->add(glyph->fID); 149 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token); 150 } 151 setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater & updater,GrBatchToken token,GrMaskFormat format)152 void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater, 153 GrBatchToken token, 154 GrMaskFormat format) { 155 this->getAtlas(format)->setLastUseTokenBulk(updater, token); 156 } 157 158 // add to texture atlas that matches this format addToAtlas(GrBatchTextStrike * strike,GrBatchAtlas::AtlasID * id,GrDrawBatch::Target * target,GrMaskFormat format,int width,int height,const void * image,SkIPoint16 * loc)159 bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id, 160 GrDrawBatch::Target* target, 161 GrMaskFormat format, int width, int height, const void* image, 162 SkIPoint16* loc) { 163 fPreserveStrike = strike; 164 return this->getAtlas(format)->addToAtlas(id, target, width, height, image, loc); 165 } 166 167 // Some clients may wish to verify the integrity of the texture backing store of the 168 // GrBatchAtlas. The atlasGeneration returned below is a monitonically increasing number which 169 // changes everytime something is removed from the texture backing store. atlasGeneration(GrMaskFormat format)170 uint64_t atlasGeneration(GrMaskFormat format) const { 171 return this->getAtlas(format)->atlasGeneration(); 172 } 173 log2Width(GrMaskFormat format)174 int log2Width(GrMaskFormat format) { return fAtlasConfigs[format].fLog2Width; } log2Height(GrMaskFormat format)175 int log2Height(GrMaskFormat format) { return fAtlasConfigs[format].fLog2Height; } 176 177 /////////////////////////////////////////////////////////////////////////// 178 // Functions intended debug only 179 void dump() const; 180 181 void setAtlasSizes_ForTesting(const GrBatchAtlasConfig configs[3]); 182 183 private: MaskFormatToPixelConfig(GrMaskFormat format)184 static GrPixelConfig MaskFormatToPixelConfig(GrMaskFormat format) { 185 static const GrPixelConfig kPixelConfigs[] = { 186 kAlpha_8_GrPixelConfig, 187 kRGB_565_GrPixelConfig, 188 kSkia8888_GrPixelConfig 189 }; 190 static_assert(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, "array_size_mismatch"); 191 192 return kPixelConfigs[format]; 193 } 194 195 // There is a 1:1 mapping between GrMaskFormats and atlas indices MaskFormatToAtlasIndex(GrMaskFormat format)196 static int MaskFormatToAtlasIndex(GrMaskFormat format) { 197 static const int sAtlasIndices[] = { 198 kA8_GrMaskFormat, 199 kA565_GrMaskFormat, 200 kARGB_GrMaskFormat, 201 }; 202 static_assert(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, "array_size_mismatch"); 203 204 SkASSERT(sAtlasIndices[format] < kMaskFormatCount); 205 return sAtlasIndices[format]; 206 } 207 208 bool initAtlas(GrMaskFormat); 209 generateStrike(GrFontScaler * scaler)210 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) { 211 GrBatchTextStrike* strike = new GrBatchTextStrike(this, scaler->getKey()); 212 fCache.add(strike); 213 return strike; 214 } 215 getAtlas(GrMaskFormat format)216 GrBatchAtlas* getAtlas(GrMaskFormat format) const { 217 int atlasIndex = MaskFormatToAtlasIndex(format); 218 SkASSERT(fAtlases[atlasIndex]); 219 return fAtlases[atlasIndex]; 220 } 221 222 static void HandleEviction(GrBatchAtlas::AtlasID, void*); 223 224 GrContext* fContext; 225 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache; 226 GrBatchAtlas* fAtlases[kMaskFormatCount]; 227 GrBatchTextStrike* fPreserveStrike; 228 GrBatchAtlasConfig fAtlasConfigs[kMaskFormatCount]; 229 }; 230 231 #endif 232