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 "SkTDynamicHash.h"
     13 #include "SkTextBlobRunIterator.h"
     14 
     15 class GrTextBlobCache {
     16 public:
     17     /**
     18      * The callback function used by the cache when it is still over budget after a purge. The
     19      * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
     20      */
     21     typedef void (*PFOverBudgetCB)(void* data);
     22 
     23     GrTextBlobCache(PFOverBudgetCB cb, void* data)
     24         : fPool(kPreAllocSize, kMinGrowthSize)
     25         , fCallback(cb)
     26         , fData(data)
     27         , fBudget(kDefaultBudget) {
     28         SkASSERT(cb && data);
     29     }
     30     ~GrTextBlobCache();
     31 
     32     // creates an uncached blob
     33     GrAtlasTextBlob* createBlob(int glyphCount, int runCount) {
     34         return GrAtlasTextBlob::Create(&fPool, glyphCount, runCount);
     35     }
     36     GrAtlasTextBlob* createBlob(const SkTextBlob* blob) {
     37         int glyphCount = 0;
     38         int runCount = 0;
     39         BlobGlyphCount(&glyphCount, &runCount, blob);
     40         GrAtlasTextBlob* cacheBlob = GrAtlasTextBlob::Create(&fPool, glyphCount, runCount);
     41         return cacheBlob;
     42     }
     43 
     44     GrAtlasTextBlob* createCachedBlob(const SkTextBlob* blob,
     45                                       const GrAtlasTextBlob::Key& key,
     46                                       const SkMaskFilter::BlurRec& blurRec,
     47                                       const SkPaint& paint) {
     48         int glyphCount = 0;
     49         int runCount = 0;
     50         BlobGlyphCount(&glyphCount, &runCount, blob);
     51         GrAtlasTextBlob* cacheBlob = GrAtlasTextBlob::Create(&fPool, glyphCount, runCount);
     52         cacheBlob->setupKey(key, blurRec, paint);
     53         this->add(cacheBlob);
     54         return cacheBlob;
     55     }
     56 
     57     GrAtlasTextBlob* find(const GrAtlasTextBlob::Key& key) {
     58         return fCache.find(key);
     59     }
     60 
     61     void remove(GrAtlasTextBlob* blob) {
     62         fCache.remove(blob->key());
     63         fBlobList.remove(blob);
     64         blob->unref();
     65     }
     66 
     67     void add(GrAtlasTextBlob* blob) {
     68         fCache.add(blob);
     69         fBlobList.addToHead(blob);
     70 
     71         this->checkPurge(blob);
     72     }
     73 
     74     void makeMRU(GrAtlasTextBlob* blob) {
     75         if (fBlobList.head() == blob) {
     76             return;
     77         }
     78 
     79         fBlobList.remove(blob);
     80         fBlobList.addToHead(blob);
     81     }
     82 
     83     void freeAll();
     84 
     85     // TODO move to SkTextBlob
     86     static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
     87         SkTextBlobRunIterator itCounter(blob);
     88         for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
     89             *glyphCount += itCounter.glyphCount();
     90         }
     91     }
     92 
     93     void setBudget(size_t budget) {
     94         fBudget = budget;
     95         this->checkPurge();
     96     }
     97 
     98 private:
     99     typedef SkTInternalLList<GrAtlasTextBlob> BitmapBlobList;
    100 
    101     void checkPurge(GrAtlasTextBlob* blob = nullptr) {
    102         // If we are overbudget, then unref until we are below budget again
    103         if (fPool.size() > fBudget) {
    104             BitmapBlobList::Iter iter;
    105             iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart);
    106             GrAtlasTextBlob* lruBlob = nullptr;
    107             while (fPool.size() > fBudget && (lruBlob = iter.get()) && lruBlob != blob) {
    108                 fCache.remove(lruBlob->key());
    109 
    110                 // Backup the iterator before removing and unrefing the blob
    111                 iter.prev();
    112                 fBlobList.remove(lruBlob);
    113                 lruBlob->unref();
    114             }
    115 
    116             // If we break out of the loop with lruBlob == blob, then we haven't purged enough
    117             // use the call back and try to free some more.  If we are still overbudget after this,
    118             // then this single textblob is over our budget
    119             if (blob && lruBlob == blob) {
    120                 (*fCallback)(fData);
    121             }
    122 
    123 #ifdef SPEW_BUDGET_MESSAGE
    124             if (fPool.size() > fBudget) {
    125                 SkDebugf("Single textblob is larger than our whole budget");
    126             }
    127 #endif
    128         }
    129     }
    130 
    131     // Budget was chosen to be ~4 megabytes.  The min alloc and pre alloc sizes in the pool are
    132     // based off of the largest cached textblob I have seen in the skps(a couple of kilobytes).
    133     static const int kPreAllocSize = 1 << 17;
    134     static const int kMinGrowthSize = 1 << 17;
    135     static const int kDefaultBudget = 1 << 22;
    136     BitmapBlobList fBlobList;
    137     SkTDynamicHash<GrAtlasTextBlob, GrAtlasTextBlob::Key> fCache;
    138     GrMemoryPool fPool;
    139     PFOverBudgetCB fCallback;
    140     void* fData;
    141     size_t fBudget;
    142 };
    143 
    144 #endif
    145