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 GrTextBlobCache_DEFINED
      9 #define GrTextBlobCache_DEFINED
     10 
     11 #include "GrAtlasTextContext.h"
     12 #include "SkMessageBus.h"
     13 #include "SkRefCnt.h"
     14 #include "SkTArray.h"
     15 #include "SkTextBlobRunIterator.h"
     16 #include "SkTHash.h"
     17 
     18 class GrTextBlobCache {
     19 public:
     20     /**
     21      * The callback function used by the cache when it is still over budget after a purge. The
     22      * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
     23      */
     24     typedef void (*PFOverBudgetCB)(void* data);
     25 
     26     GrTextBlobCache(PFOverBudgetCB cb, void* data, uint32_t uniqueID)
     27         : fPool(0u, kMinGrowthSize)
     28         , fCallback(cb)
     29         , fData(data)
     30         , fBudget(kDefaultBudget)
     31         , fUniqueID(uniqueID)
     32         , fPurgeBlobInbox(uniqueID) {
     33         SkASSERT(cb && data);
     34     }
     35     ~GrTextBlobCache();
     36 
     37     // creates an uncached blob
     38     sk_sp<GrAtlasTextBlob> makeBlob(int glyphCount, int runCount) {
     39         return GrAtlasTextBlob::Make(&fPool, glyphCount, runCount);
     40     }
     41 
     42     sk_sp<GrAtlasTextBlob> makeBlob(const SkTextBlob* blob) {
     43         int glyphCount = 0;
     44         int runCount = 0;
     45         BlobGlyphCount(&glyphCount, &runCount, blob);
     46         return GrAtlasTextBlob::Make(&fPool, glyphCount, runCount);
     47     }
     48 
     49     sk_sp<GrAtlasTextBlob> makeCachedBlob(const SkTextBlob* blob,
     50                                           const GrAtlasTextBlob::Key& key,
     51                                           const SkMaskFilterBase::BlurRec& blurRec,
     52                                           const SkPaint& paint) {
     53         sk_sp<GrAtlasTextBlob> cacheBlob(this->makeBlob(blob));
     54         cacheBlob->setupKey(key, blurRec, paint);
     55         this->add(cacheBlob);
     56         blob->notifyAddedToCache(fUniqueID);
     57         return cacheBlob;
     58     }
     59 
     60     sk_sp<GrAtlasTextBlob> find(const GrAtlasTextBlob::Key& key) const {
     61         const auto* idEntry = fBlobIDCache.find(key.fUniqueID);
     62         return idEntry ? idEntry->find(key) : nullptr;
     63     }
     64 
     65     void remove(GrAtlasTextBlob* blob) {
     66         auto  id      = GrAtlasTextBlob::GetKey(*blob).fUniqueID;
     67         auto* idEntry = fBlobIDCache.find(id);
     68         SkASSERT(idEntry);
     69 
     70         fBlobList.remove(blob);
     71         idEntry->removeBlob(blob);
     72         if (idEntry->fBlobs.empty()) {
     73             fBlobIDCache.remove(id);
     74         }
     75     }
     76 
     77     void makeMRU(GrAtlasTextBlob* blob) {
     78         if (fBlobList.head() == blob) {
     79             return;
     80         }
     81 
     82         fBlobList.remove(blob);
     83         fBlobList.addToHead(blob);
     84     }
     85 
     86     void freeAll();
     87 
     88     // TODO move to SkTextBlob
     89     static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
     90         SkTextBlobRunIterator itCounter(blob);
     91         for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
     92             *glyphCount += itCounter.glyphCount();
     93         }
     94     }
     95 
     96     void setBudget(size_t budget) {
     97         fBudget = budget;
     98         this->checkPurge();
     99     }
    100 
    101     struct PurgeBlobMessage {
    102         uint32_t fID;
    103     };
    104 
    105     static void PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID);
    106 
    107     void purgeStaleBlobs();
    108 
    109 private:
    110     using BitmapBlobList = SkTInternalLList<GrAtlasTextBlob>;
    111 
    112     struct BlobIDCacheEntry {
    113         BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
    114         explicit BlobIDCacheEntry(uint32_t id) : fID(id) {}
    115 
    116         static uint32_t GetKey(const BlobIDCacheEntry& entry) {
    117             return entry.fID;
    118         }
    119 
    120         void addBlob(sk_sp<GrAtlasTextBlob> blob) {
    121             SkASSERT(blob);
    122             SkASSERT(GrAtlasTextBlob::GetKey(*blob).fUniqueID == fID);
    123             SkASSERT(!this->find(GrAtlasTextBlob::GetKey(*blob)));
    124 
    125             fBlobs.emplace_back(std::move(blob));
    126         }
    127 
    128         void removeBlob(GrAtlasTextBlob* blob) {
    129             SkASSERT(blob);
    130             SkASSERT(GrAtlasTextBlob::GetKey(*blob).fUniqueID == fID);
    131 
    132             auto index = this->findBlobIndex(GrAtlasTextBlob::GetKey(*blob));
    133             SkASSERT(index >= 0);
    134 
    135             fBlobs.removeShuffle(index);
    136         }
    137 
    138         sk_sp<GrAtlasTextBlob> find(const GrAtlasTextBlob::Key& key) const {
    139             auto index = this->findBlobIndex(key);
    140             return index < 0 ? nullptr : fBlobs[index];
    141         }
    142 
    143         int findBlobIndex(const GrAtlasTextBlob::Key& key) const{
    144             for (int i = 0; i < fBlobs.count(); ++i) {
    145                 if (GrAtlasTextBlob::GetKey(*fBlobs[i]) == key) {
    146                     return i;
    147                 }
    148             }
    149             return -1;
    150         }
    151 
    152         uint32_t                             fID;
    153         // Current clients don't generate multiple GrAtlasTextBlobs per SkTextBlob, so an array w/
    154         // linear search is acceptable.  If usage changes, we should re-evaluate this structure.
    155         SkSTArray<1, sk_sp<GrAtlasTextBlob>, true> fBlobs;
    156     };
    157 
    158     void add(sk_sp<GrAtlasTextBlob> blob) {
    159         auto  id      = GrAtlasTextBlob::GetKey(*blob).fUniqueID;
    160         auto* idEntry = fBlobIDCache.find(id);
    161         if (!idEntry) {
    162             idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
    163         }
    164 
    165         // Safe to retain a raw ptr temporarily here, because the cache will hold a ref.
    166         GrAtlasTextBlob* rawBlobPtr = blob.get();
    167         fBlobList.addToHead(rawBlobPtr);
    168         idEntry->addBlob(std::move(blob));
    169 
    170         this->checkPurge(rawBlobPtr);
    171     }
    172 
    173     void checkPurge(GrAtlasTextBlob* blob = nullptr);
    174 
    175     static const int kMinGrowthSize = 1 << 16;
    176     static const int kDefaultBudget = 1 << 22;
    177     GrMemoryPool fPool;
    178     BitmapBlobList fBlobList;
    179     SkTHashMap<uint32_t, BlobIDCacheEntry> fBlobIDCache;
    180     PFOverBudgetCB fCallback;
    181     void* fData;
    182     size_t fBudget;
    183     uint32_t fUniqueID;      // unique id to use for messaging
    184     SkMessageBus<PurgeBlobMessage>::Inbox fPurgeBlobInbox;
    185 };
    186 
    187 #endif
    188