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 18class GrBatchFontCache; 19class 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 */ 25class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> { 26public: 27 GrBatchTextStrike(GrBatchFontCache*, const GrFontDescKey* fontScalerKey); 28 ~GrBatchTextStrike(); 29 30 const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; } 31 GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; } 32 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 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 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 76 bool isAbandoned() const { return fIsAbandoned; } 77 78 static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) { 79 return *(ts.fFontScalerKey); 80 } 81 static uint32_t Hash(const GrFontDescKey& key) { 82 return key.getHash(); 83 } 84 85private: 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 */ 107class GrBatchFontCache { 108public: 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. 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. 128 GrTexture* getTexture(GrMaskFormat format) { 129 if (this->initAtlas(format)) { 130 return this->getAtlas(format)->getTexture(); 131 } 132 return nullptr; 133 } 134 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 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 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 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. 170 uint64_t atlasGeneration(GrMaskFormat format) const { 171 return this->getAtlas(format)->atlasGeneration(); 172 } 173 174 int log2Width(GrMaskFormat format) { return fAtlasConfigs[format].fLog2Width; } 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 183private: 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 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 210 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) { 211 GrBatchTextStrike* strike = new GrBatchTextStrike(this, scaler->getKey()); 212 fCache.add(strike); 213 return strike; 214 } 215 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