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 GrStrikeCache_DEFINED 9 #define GrStrikeCache_DEFINED 10 11 #include "src/codec/SkMasks.h" 12 #include "src/core/SkArenaAlloc.h" 13 #include "src/core/SkStrike.h" 14 #include "src/core/SkTDynamicHash.h" 15 #include "src/gpu/GrDrawOpAtlas.h" 16 #include "src/gpu/GrGlyph.h" 17 18 class GrAtlasManager; 19 class GrGpu; 20 class GrStrikeCache; 21 22 /** 23 * The GrTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory 24 * is indexed by a PackedID and SkStrike. The SkStrike is what actually creates the mask. 25 * The GrTextStrike may outlive the generating SkStrike. However, it retains a copy 26 * of it's SkDescriptor as a key to access (or regenerate) the SkStrike. GrTextStrikes are 27 * created by and owned by a GrStrikeCache. 28 */ 29 class GrTextStrike : public SkNVRefCnt<GrTextStrike> { 30 public: 31 GrTextStrike(const SkDescriptor& fontScalerKey); 32 getGlyph(const SkGlyph & skGlyph)33 GrGlyph* getGlyph(const SkGlyph& skGlyph) { 34 GrGlyph* grGlyph = fCache.find(skGlyph.getPackedID()); 35 if (grGlyph == nullptr) { 36 grGlyph = fAlloc.make<GrGlyph>(skGlyph); 37 fCache.add(grGlyph); 38 } 39 return grGlyph; 40 } 41 42 // This variant of the above function is called by GrAtlasTextOp. 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(SkPackedGlyphID packed,SkStrike * skStrike)46 GrGlyph* getGlyph(SkPackedGlyphID packed, SkStrike* skStrike) { 47 GrGlyph* grGlyph = fCache.find(packed); 48 if (grGlyph == nullptr) { 49 // We could return this to the caller, but in practice it adds code complexity for 50 // potentially little benefit(ie, if the glyph is not in our font cache, then its not 51 // in the atlas and we're going to be doing a texture upload anyways). 52 grGlyph = fAlloc.make<GrGlyph>(*skStrike->glyph(packed)); 53 fCache.add(grGlyph); 54 } 55 return grGlyph; 56 } 57 58 // returns true if glyph successfully added to texture atlas, false otherwise. If the glyph's 59 // mask format has changed, then addGlyphToAtlas will draw a clear box. This will almost never 60 // happen. 61 // TODO we can handle some of these cases if we really want to, but the long term solution is to 62 // get the actual glyph image itself when we get the glyph metrics. 63 GrDrawOpAtlas::ErrorCode addGlyphToAtlas(GrResourceProvider*, GrDeferredUploadTarget*, 64 GrStrikeCache*, GrAtlasManager*, GrGlyph*, 65 SkStrike*, GrMaskFormat expectedMaskFormat, 66 bool isScaledGlyph); 67 68 // testing countGlyphs()69 int countGlyphs() const { return fCache.count(); } 70 71 // remove any references to this plot 72 void removeID(GrDrawOpAtlas::AtlasID); 73 74 // If a TextStrike is abandoned by the cache, then the caller must get a new strike isAbandoned()75 bool isAbandoned() const { return fIsAbandoned; } 76 GetKey(const GrTextStrike & strike)77 static const SkDescriptor& GetKey(const GrTextStrike& strike) { 78 return *strike.fFontScalerKey.getDesc(); 79 } 80 Hash(const SkDescriptor & desc)81 static uint32_t Hash(const SkDescriptor& desc) { return desc.getChecksum(); } 82 83 private: 84 SkTDynamicHash<GrGlyph, SkPackedGlyphID> fCache; 85 SkAutoDescriptor fFontScalerKey; 86 SkArenaAlloc fAlloc{512}; 87 88 int fAtlasedGlyphs{0}; 89 bool fIsAbandoned{false}; 90 91 friend class GrStrikeCache; 92 }; 93 94 /** 95 * GrStrikeCache manages strikes which are indexed by a SkStrike. These strikes can then be 96 * used to generate individual Glyph Masks. 97 */ 98 class GrStrikeCache { 99 public: 100 GrStrikeCache(const GrCaps* caps, size_t maxTextureBytes); 101 ~GrStrikeCache(); 102 setStrikeToPreserve(GrTextStrike * strike)103 void setStrikeToPreserve(GrTextStrike* strike) { fPreserveStrike = strike; } 104 105 // The user of the cache may hold a long-lived ref to the returned strike. However, actions by 106 // another client of the cache may cause the strike to be purged while it is still reffed. 107 // Therefore, the caller must check GrTextStrike::isAbandoned() if there are other 108 // interactions with the cache since the strike was received. getStrike(const SkDescriptor & desc)109 sk_sp<GrTextStrike> getStrike(const SkDescriptor& desc) { 110 sk_sp<GrTextStrike> strike = sk_ref_sp(fCache.find(desc)); 111 if (!strike) { 112 strike = this->generateStrike(desc); 113 } 114 return strike; 115 } 116 getMasks()117 const SkMasks& getMasks() const { return *f565Masks; } 118 119 void freeAll(); 120 121 static void HandleEviction(GrDrawOpAtlas::AtlasID, void*); 122 123 private: generateStrike(const SkDescriptor & desc)124 sk_sp<GrTextStrike> generateStrike(const SkDescriptor& desc) { 125 // 'fCache' get the construction ref 126 sk_sp<GrTextStrike> strike = sk_ref_sp(new GrTextStrike(desc)); 127 fCache.add(strike.get()); 128 return strike; 129 } 130 131 using StrikeHash = SkTDynamicHash<GrTextStrike, SkDescriptor>; 132 133 StrikeHash fCache; 134 GrTextStrike* fPreserveStrike; 135 std::unique_ptr<const SkMasks> f565Masks; 136 }; 137 138 #endif // GrStrikeCache_DEFINED 139