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