Home | History | Annotate | Download | only in text
      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 
     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
     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
     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
     78     bool isAbandoned() const { return fIsAbandoned; }
     79 
     80     static const SkDescriptor& GetKey(const GrAtlasTextStrike& ts) {
     81         return *ts.fFontScalerKey.getDesc();
     82     }
     83 
     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 
     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.
    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.
    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 
    140     uint32_t getAtlasPageCount(GrMaskFormat format) {
    141         if (this->initAtlas(format)) {
    142             return this->getAtlas(format)->pageCount();
    143         }
    144         return 0;
    145     }
    146 
    147     SkScalar getGlyphSizeLimit() const { return fGlyphSizeLimit; }
    148 
    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
    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 
    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
    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.
    183     uint64_t atlasGeneration(GrMaskFormat format) const {
    184         return this->getAtlas(format)->atlasGeneration();
    185     }
    186 
    187     // GrOnFlushCallbackObject overrides
    188 
    189     void preFlush(GrOnFlushResourceProvider*, const uint32_t*, int,
    190                   SkTArray<sk_sp<GrRenderTargetContext>>*) override {}
    191 
    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
    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 
    213     GrContext* context() const { return fContext; }
    214 
    215 private:
    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
    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 
    245     GrAtlasTextStrike* generateStrike(const SkGlyphCache* cache) {
    246         GrAtlasTextStrike* strike = new GrAtlasTextStrike(this, cache->getDescriptor());
    247         fCache.add(strike);
    248         return strike;
    249     }
    250 
    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