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