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