1b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt/*
2b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt * Copyright 2015 Google Inc.
3b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt *
4b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt * Use of this source code is governed by a BSD-style license that can be
5b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt * found in the LICENSE file.
6b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt */
7b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
8b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt#ifndef GrTextBlobCache_DEFINED
9b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt#define GrTextBlobCache_DEFINED
10b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
11b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt#include "GrAtlasTextContext.h"
12b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt#include "SkTDynamicHash.h"
13b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt#include "SkTextBlob.h"
14b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
15b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualittclass GrTextBlobCache {
16b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualittpublic:
17b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    typedef GrAtlasTextContext::BitmapTextBlob BitmapTextBlob;
18b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
190db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt    /**
200db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt     * The callback function used by the cache when it is still over budget after a purge. The
210db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt     * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
220db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt     */
230db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt    typedef void (*PFOverBudgetCB)(void* data);
240db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt
250db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt    GrTextBlobCache(PFOverBudgetCB cb, void* data)
260db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt        : fPool(kPreAllocSize, kMinGrowthSize)
270db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt        , fCallback(cb)
280db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt        , fData(data) {
290db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt        SkASSERT(cb && data);
300db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt    }
31b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    ~GrTextBlobCache();
32b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
33b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    // creates an uncached blob
34b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    BitmapTextBlob* createBlob(int glyphCount, int runCount, size_t maxVASize);
352a0e9f36c6e823d2c0472ef23b4c5a247427807fjoshualitt    BitmapTextBlob* createBlob(const SkTextBlob* blob, size_t maxVAStride) {
362a0e9f36c6e823d2c0472ef23b4c5a247427807fjoshualitt        int glyphCount = 0;
372a0e9f36c6e823d2c0472ef23b4c5a247427807fjoshualitt        int runCount = 0;
382a0e9f36c6e823d2c0472ef23b4c5a247427807fjoshualitt        BlobGlyphCount(&glyphCount, &runCount, blob);
392a0e9f36c6e823d2c0472ef23b4c5a247427807fjoshualitt        BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
402a0e9f36c6e823d2c0472ef23b4c5a247427807fjoshualitt        return cacheBlob;
412a0e9f36c6e823d2c0472ef23b4c5a247427807fjoshualitt    }
42b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
4353b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt    BitmapTextBlob* createCachedBlob(const SkTextBlob* blob,
4453b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt                                     const BitmapTextBlob::Key& key,
4553b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt                                     const SkMaskFilter::BlurRec& blurRec,
4653b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt                                     const SkPaint& paint,
4753b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt                                     size_t maxVAStride) {
48b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        int glyphCount = 0;
49b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        int runCount = 0;
50b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        BlobGlyphCount(&glyphCount, &runCount, blob);
51b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
5253b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt        cacheBlob->fKey = key;
5353b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt        if (key.fHasBlur) {
5453b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt            cacheBlob->fBlurRec = blurRec;
5553b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt        }
5653b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt        if (key.fStyle != SkPaint::kFill_Style) {
5753b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt            cacheBlob->fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
5853b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt            cacheBlob->fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
5953b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt            cacheBlob->fStrokeInfo.fJoin = paint.getStrokeJoin();
6053b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt        }
61b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        this->add(cacheBlob);
62b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        return cacheBlob;
63b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    }
64b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
6553b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt    BitmapTextBlob* find(const BitmapTextBlob::Key& key) {
6653b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt        return fCache.find(key);
67b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    }
68b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
69b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    void remove(BitmapTextBlob* blob) {
7053b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt        fCache.remove(blob->fKey);
71b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        fBlobList.remove(blob);
72b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        blob->unref();
73b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    }
74b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
75b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    void add(BitmapTextBlob* blob) {
76b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        fCache.add(blob);
77b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        fBlobList.addToHead(blob);
78b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
79b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        // If we are overbudget, then unref until we are below budget again
80b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        if (fPool.size() > kBudget) {
81b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt            BitmapBlobList::Iter iter;
82b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt            iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart);
83b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt            BitmapTextBlob* lruBlob = iter.get();
84b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt            SkASSERT(lruBlob);
850db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            while (fPool.size() > kBudget && (lruBlob = iter.get()) && lruBlob != blob) {
8653b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt                fCache.remove(lruBlob->fKey);
87f6e97e6f62c08042886dc50850c1d8c929418602joshualitt
88f6e97e6f62c08042886dc50850c1d8c929418602joshualitt                // Backup the iterator before removing and unrefing the blob
89b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt                iter.prev();
90f6e97e6f62c08042886dc50850c1d8c929418602joshualitt                fBlobList.remove(lruBlob);
910db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt                lruBlob->unref();
920db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            }
930db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt
940db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            // If we break out of the loop with lruBlob == blob, then we haven't purged enough
950db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            // use the call back and try to free some more.  If we are still overbudget after this,
960db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            // then this single textblob is over our budget
970db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            if (lruBlob == blob) {
980db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt                (*fCallback)(fData);
990db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            }
1000db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt
1010db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt#ifdef SK_DEBUG
1020db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            if (fPool.size() > kBudget) {
1030db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt                SkDebugf("Single textblob is larger than our whole budget");
1040db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt            }
1050db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt#endif
106b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        }
107b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    }
108b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
109b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    void makeMRU(BitmapTextBlob* blob) {
110b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        if (fBlobList.head() == blob) {
111b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt            return;
112b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        }
113b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
114b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        fBlobList.remove(blob);
115b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        fBlobList.addToHead(blob);
116b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    }
117b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
11826ffc00bfa09fe85c22ddcbeb0fc54c0eacb7859joshualitt    void freeAll();
11926ffc00bfa09fe85c22ddcbeb0fc54c0eacb7859joshualitt
120b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualittprivate:
121b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    // TODO move to SkTextBlob
122b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
123b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        SkTextBlob::RunIterator itCounter(blob);
124b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
125b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt            *glyphCount += itCounter.glyphCount();
126b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt        }
127b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    }
128b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
129b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    typedef SkTInternalLList<BitmapTextBlob> BitmapBlobList;
130b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
131b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    // Budget was chosen to be ~4 megabytes.  The min alloc and pre alloc sizes in the pool are
132b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    // based off of the largest cached textblob I have seen in the skps(a couple of kilobytes).
133b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    static const int kPreAllocSize = 1 << 17;
134b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    static const int kMinGrowthSize = 1 << 17;
1350db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt    static const int kBudget = 1 << 22;
136b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    BitmapBlobList fBlobList;
13753b5f4488b05c40254b24c718c2df9724a13c54ajoshualitt    SkTDynamicHash<BitmapTextBlob, BitmapTextBlob::Key> fCache;
138b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt    GrMemoryPool fPool;
1390db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt    PFOverBudgetCB fCallback;
1400db6dfaeb2d1376ad393516fb22af7ecf62718fajoshualitt    void* fData;
141b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt};
142b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt
143b7133bed55af8dd4ca9427892bb1a5623dbaccf0joshualitt#endif
144