LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu/text - GrTextBlobCache.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 94 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 19 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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           0 :     GrTextBlobCache(PFOverBudgetCB cb, void* data)
      27           0 :         : fPool(kPreAllocSize, kMinGrowthSize)
      28             :         , fCallback(cb)
      29             :         , fData(data)
      30           0 :         , fBudget(kDefaultBudget) {
      31           0 :         SkASSERT(cb && data);
      32           0 :     }
      33             :     ~GrTextBlobCache();
      34             : 
      35             :     // creates an uncached blob
      36           0 :     sk_sp<GrAtlasTextBlob> makeBlob(int glyphCount, int runCount) {
      37           0 :         return GrAtlasTextBlob::Make(&fPool, glyphCount, runCount);
      38             :     }
      39             : 
      40           0 :     sk_sp<GrAtlasTextBlob> makeBlob(const SkTextBlob* blob) {
      41           0 :         int glyphCount = 0;
      42           0 :         int runCount = 0;
      43           0 :         BlobGlyphCount(&glyphCount, &runCount, blob);
      44           0 :         return GrAtlasTextBlob::Make(&fPool, glyphCount, runCount);
      45             :     }
      46             : 
      47           0 :     sk_sp<GrAtlasTextBlob> makeCachedBlob(const SkTextBlob* blob,
      48             :                                           const GrAtlasTextBlob::Key& key,
      49             :                                           const SkMaskFilter::BlurRec& blurRec,
      50             :                                           const SkPaint& paint) {
      51           0 :         sk_sp<GrAtlasTextBlob> cacheBlob(this->makeBlob(blob));
      52           0 :         cacheBlob->setupKey(key, blurRec, paint);
      53           0 :         this->add(cacheBlob);
      54           0 :         blob->notifyAddedToCache();
      55           0 :         return cacheBlob;
      56             :     }
      57             : 
      58           0 :     sk_sp<GrAtlasTextBlob> find(const GrAtlasTextBlob::Key& key) const {
      59           0 :         const auto* idEntry = fBlobIDCache.find(key.fUniqueID);
      60           0 :         return idEntry ? idEntry->find(key) : nullptr;
      61             :     }
      62             : 
      63           0 :     void remove(GrAtlasTextBlob* blob) {
      64           0 :         auto  id      = GrAtlasTextBlob::GetKey(*blob).fUniqueID;
      65           0 :         auto* idEntry = fBlobIDCache.find(id);
      66           0 :         SkASSERT(idEntry);
      67             : 
      68           0 :         fBlobList.remove(blob);
      69           0 :         idEntry->removeBlob(blob);
      70           0 :         if (idEntry->fBlobs.empty()) {
      71           0 :             fBlobIDCache.remove(id);
      72             :         }
      73           0 :     }
      74             : 
      75           0 :     void makeMRU(GrAtlasTextBlob* blob) {
      76           0 :         if (fBlobList.head() == blob) {
      77           0 :             return;
      78             :         }
      79             : 
      80           0 :         fBlobList.remove(blob);
      81           0 :         fBlobList.addToHead(blob);
      82             :     }
      83             : 
      84             :     void freeAll();
      85             : 
      86             :     // TODO move to SkTextBlob
      87           0 :     static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
      88           0 :         SkTextBlobRunIterator itCounter(blob);
      89           0 :         for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
      90           0 :             *glyphCount += itCounter.glyphCount();
      91             :         }
      92           0 :     }
      93             : 
      94             :     void setBudget(size_t budget) {
      95             :         fBudget = budget;
      96             :         this->checkPurge();
      97             :     }
      98             : 
      99             :     struct PurgeBlobMessage {
     100             :         uint32_t fID;
     101             :     };
     102             : 
     103             :     static void PostPurgeBlobMessage(uint32_t);
     104             : 
     105             : private:
     106             :     using BitmapBlobList = SkTInternalLList<GrAtlasTextBlob>;
     107             : 
     108           0 :     struct BlobIDCacheEntry {
     109           0 :         BlobIDCacheEntry() : fID(SK_InvalidGenID) {}
     110           0 :         explicit BlobIDCacheEntry(uint32_t id) : fID(id) {}
     111             : 
     112             :         static uint32_t GetKey(const BlobIDCacheEntry& entry) {
     113             :             return entry.fID;
     114             :         }
     115             : 
     116           0 :         void addBlob(sk_sp<GrAtlasTextBlob> blob) {
     117           0 :             SkASSERT(blob);
     118           0 :             SkASSERT(GrAtlasTextBlob::GetKey(*blob).fUniqueID == fID);
     119           0 :             SkASSERT(!this->find(GrAtlasTextBlob::GetKey(*blob)));
     120             : 
     121           0 :             fBlobs.emplace_back(std::move(blob));
     122           0 :         }
     123             : 
     124           0 :         void removeBlob(GrAtlasTextBlob* blob) {
     125           0 :             SkASSERT(blob);
     126           0 :             SkASSERT(GrAtlasTextBlob::GetKey(*blob).fUniqueID == fID);
     127             : 
     128           0 :             auto index = this->findBlobIndex(GrAtlasTextBlob::GetKey(*blob));
     129           0 :             SkASSERT(index >= 0);
     130             : 
     131           0 :             fBlobs.removeShuffle(index);
     132           0 :         }
     133             : 
     134           0 :         sk_sp<GrAtlasTextBlob> find(const GrAtlasTextBlob::Key& key) const {
     135           0 :             auto index = this->findBlobIndex(key);
     136           0 :             return index < 0 ? nullptr : fBlobs[index];
     137             :         }
     138             : 
     139           0 :         int findBlobIndex(const GrAtlasTextBlob::Key& key) const{
     140           0 :             for (int i = 0; i < fBlobs.count(); ++i) {
     141           0 :                 if (GrAtlasTextBlob::GetKey(*fBlobs[i]) == key) {
     142           0 :                     return i;
     143             :                 }
     144             :             }
     145           0 :             return -1;
     146             :         }
     147             : 
     148             :         uint32_t                             fID;
     149             :         // Current clients don't generate multiple GrAtlasTextBlobs per SkTextBlob, so an array w/
     150             :         // linear search is acceptable.  If usage changes, we should re-evaluate this structure.
     151             :         SkSTArray<1, sk_sp<GrAtlasTextBlob>, true> fBlobs;
     152             :     };
     153             : 
     154           0 :     void add(sk_sp<GrAtlasTextBlob> blob) {
     155           0 :         auto  id      = GrAtlasTextBlob::GetKey(*blob).fUniqueID;
     156           0 :         auto* idEntry = fBlobIDCache.find(id);
     157           0 :         if (!idEntry) {
     158           0 :             idEntry = fBlobIDCache.set(id, BlobIDCacheEntry(id));
     159             :         }
     160             : 
     161             :         // Safe to retain a raw ptr temporarily here, because the cache will hold a ref.
     162           0 :         GrAtlasTextBlob* rawBlobPtr = blob.get();
     163           0 :         fBlobList.addToHead(rawBlobPtr);
     164           0 :         idEntry->addBlob(std::move(blob));
     165             : 
     166           0 :         this->checkPurge(rawBlobPtr);
     167           0 :     }
     168             : 
     169           0 :     void checkPurge(GrAtlasTextBlob* blob = nullptr) {
     170             :         // First, purge all stale blob IDs.
     171             :         {
     172           0 :             SkTArray<PurgeBlobMessage> msgs;
     173           0 :             fPurgeBlobInbox.poll(&msgs);
     174             : 
     175           0 :             for (const auto& msg : msgs) {
     176           0 :                 auto* idEntry = fBlobIDCache.find(msg.fID);
     177           0 :                 if (!idEntry) {
     178             :                     // no cache entries for id
     179           0 :                     continue;
     180             :                 }
     181             : 
     182             :                 // remove all blob entries from the LRU list
     183           0 :                 for (const auto& blob : idEntry->fBlobs) {
     184           0 :                     fBlobList.remove(blob.get());
     185             :                 }
     186             : 
     187             :                 // drop the idEntry itself (unrefs all blobs)
     188           0 :                 fBlobIDCache.remove(msg.fID);
     189             :             }
     190             :         }
     191             : 
     192             :         // If we are still overbudget, then unref until we are below budget again
     193           0 :         if (fPool.size() > fBudget) {
     194           0 :             BitmapBlobList::Iter iter;
     195           0 :             iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart);
     196           0 :             GrAtlasTextBlob* lruBlob = nullptr;
     197           0 :             while (fPool.size() > fBudget && (lruBlob = iter.get()) && lruBlob != blob) {
     198             :                 // Backup the iterator before removing and unrefing the blob
     199           0 :                 iter.prev();
     200             : 
     201           0 :                 this->remove(lruBlob);
     202             :             }
     203             : 
     204             :             // If we break out of the loop with lruBlob == blob, then we haven't purged enough
     205             :             // use the call back and try to free some more.  If we are still overbudget after this,
     206             :             // then this single textblob is over our budget
     207           0 :             if (blob && lruBlob == blob) {
     208           0 :                 (*fCallback)(fData);
     209             :             }
     210             : 
     211             : #ifdef SPEW_BUDGET_MESSAGE
     212             :             if (fPool.size() > fBudget) {
     213             :                 SkDebugf("Single textblob is larger than our whole budget");
     214             :             }
     215             : #endif
     216             :         }
     217           0 :     }
     218             : 
     219             :     // Budget was chosen to be ~4 megabytes.  The min alloc and pre alloc sizes in the pool are
     220             :     // based off of the largest cached textblob I have seen in the skps(a couple of kilobytes).
     221             :     static const int kPreAllocSize = 1 << 17;
     222             :     static const int kMinGrowthSize = 1 << 17;
     223             :     static const int kDefaultBudget = 1 << 22;
     224             :     GrMemoryPool fPool;
     225             :     BitmapBlobList fBlobList;
     226             :     SkTHashMap<uint32_t, BlobIDCacheEntry> fBlobIDCache;
     227             :     PFOverBudgetCB fCallback;
     228             :     void* fData;
     229             :     size_t fBudget;
     230             :     SkMessageBus<PurgeBlobMessage>::Inbox fPurgeBlobInbox;
     231             : };
     232             : 
     233             : #endif

Generated by: LCOV version 1.13