| OLD | NEW |
| (Empty) |
| 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. Th
is backing memory | |
| 23 * is indexed by a PackedID and GrFontScaler. The GrFontScaler is what actuall
y creates the mask. | |
| 24 */ | |
| 25 class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> { | |
| 26 public: | |
| 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 c
ases 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 ca
che, 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 otherwis
e. If the glyph's | |
| 62 // mask format has changed, then addGlyphToAtlas will draw a clear box. Thi
s 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 | |
| 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 GrBatchAtl
ases, 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 in
itialization 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. H
owever, 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 ther
e 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 funct
ions on the | |
| 126 // GrBatchFontCache which use the atlas. This function *must* be called fir
st, 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 t
he Atlas. | |
| 143 // For convenience, this function will also set the use token for the curren
t glyph if required | |
| 144 // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGene
ration | |
| 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* imag
e, | |
| 162 SkIPoint16* loc) { | |
| 163 fPreserveStrike = strike; | |
| 164 return this->getAtlas(format)->addToAtlas(id, target, width, height, ima
ge, loc); | |
| 165 } | |
| 166 | |
| 167 // Some clients may wish to verify the integrity of the texture backing stor
e of the | |
| 168 // GrBatchAtlas. The atlasGeneration returned below is a monitonically incr
easing 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 /////////////////////////////////////////////////////////////////////////// | |
| 175 // Functions intended debug only | |
| 176 void dump() const; | |
| 177 | |
| 178 void setAtlasSizes_ForTesting(const GrBatchAtlasConfig configs[3]); | |
| 179 | |
| 180 private: | |
| 181 static GrPixelConfig MaskFormatToPixelConfig(GrMaskFormat format) { | |
| 182 static const GrPixelConfig kPixelConfigs[] = { | |
| 183 kAlpha_8_GrPixelConfig, | |
| 184 kRGB_565_GrPixelConfig, | |
| 185 kSkia8888_GrPixelConfig | |
| 186 }; | |
| 187 static_assert(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, "array_
size_mismatch"); | |
| 188 | |
| 189 return kPixelConfigs[format]; | |
| 190 } | |
| 191 | |
| 192 // There is a 1:1 mapping between GrMaskFormats and atlas indices | |
| 193 static int MaskFormatToAtlasIndex(GrMaskFormat format) { | |
| 194 static const int sAtlasIndices[] = { | |
| 195 kA8_GrMaskFormat, | |
| 196 kA565_GrMaskFormat, | |
| 197 kARGB_GrMaskFormat, | |
| 198 }; | |
| 199 static_assert(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, "array_
size_mismatch"); | |
| 200 | |
| 201 SkASSERT(sAtlasIndices[format] < kMaskFormatCount); | |
| 202 return sAtlasIndices[format]; | |
| 203 } | |
| 204 | |
| 205 bool initAtlas(GrMaskFormat); | |
| 206 | |
| 207 GrBatchTextStrike* generateStrike(GrFontScaler* scaler) { | |
| 208 GrBatchTextStrike* strike = new GrBatchTextStrike(this, scaler->getKey()
); | |
| 209 fCache.add(strike); | |
| 210 return strike; | |
| 211 } | |
| 212 | |
| 213 GrBatchAtlas* getAtlas(GrMaskFormat format) const { | |
| 214 int atlasIndex = MaskFormatToAtlasIndex(format); | |
| 215 SkASSERT(fAtlases[atlasIndex]); | |
| 216 return fAtlases[atlasIndex]; | |
| 217 } | |
| 218 | |
| 219 static void HandleEviction(GrBatchAtlas::AtlasID, void*); | |
| 220 | |
| 221 GrContext* fContext; | |
| 222 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache; | |
| 223 GrBatchAtlas* fAtlases[kMaskFormatCount]; | |
| 224 GrBatchTextStrike* fPreserveStrike; | |
| 225 GrBatchAtlasConfig fAtlasConfigs[kMaskFormatCount]; | |
| 226 }; | |
| 227 | |
| 228 #endif | |
| OLD | NEW |