aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrTextBlobCache.h
blob: 0d0320e00b81b06294b25fb2c1c0373aed01a813 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrTextBlobCache_DEFINED
#define GrTextBlobCache_DEFINED

#include "GrAtlasTextContext.h"
#include "SkTDynamicHash.h"
#include "SkTextBlob.h"

class GrTextBlobCache {
public:
    typedef GrAtlasTextContext::BitmapTextBlob BitmapTextBlob;

    /**
     * The callback function used by the cache when it is still over budget after a purge. The
     * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
     */
    typedef void (*PFOverBudgetCB)(void* data);

    GrTextBlobCache(PFOverBudgetCB cb, void* data)
        : fPool(kPreAllocSize, kMinGrowthSize)
        , fCallback(cb)
        , fData(data) {
        SkASSERT(cb && data);
    }
    ~GrTextBlobCache();

    // creates an uncached blob
    BitmapTextBlob* createBlob(int glyphCount, int runCount, size_t maxVASize);
    BitmapTextBlob* createBlob(const SkTextBlob* blob, size_t maxVAStride) {
        int glyphCount = 0;
        int runCount = 0;
        BlobGlyphCount(&glyphCount, &runCount, blob);
        BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
        return cacheBlob;
    }

    BitmapTextBlob* createCachedBlob(const SkTextBlob* blob, size_t maxVAStride) {
        int glyphCount = 0;
        int runCount = 0;
        BlobGlyphCount(&glyphCount, &runCount, blob);
        BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
        cacheBlob->fUniqueID = blob->uniqueID();
        this->add(cacheBlob);
        return cacheBlob;
    }

    BitmapTextBlob* find(uint32_t uniqueID) {
        return fCache.find(uniqueID);
    }

    void remove(BitmapTextBlob* blob) {
        fCache.remove(blob->fUniqueID);
        fBlobList.remove(blob);
        blob->unref();
    }

    void add(BitmapTextBlob* blob) {
        fCache.add(blob);
        fBlobList.addToHead(blob);

        // If we are overbudget, then unref until we are below budget again
        if (fPool.size() > kBudget) {
            BitmapBlobList::Iter iter;
            iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart);
            BitmapTextBlob* lruBlob = iter.get();
            SkASSERT(lruBlob);
            while (fPool.size() > kBudget && (lruBlob = iter.get()) && lruBlob != blob) {
                fCache.remove(lruBlob->fUniqueID);

                // Backup the iterator before removing and unrefing the blob
                iter.prev();
                fBlobList.remove(lruBlob);
                lruBlob->unref();
            }

            // If we break out of the loop with lruBlob == blob, then we haven't purged enough
            // use the call back and try to free some more.  If we are still overbudget after this,
            // then this single textblob is over our budget
            if (lruBlob == blob) {
                (*fCallback)(fData);
            }

#ifdef SK_DEBUG
            if (fPool.size() > kBudget) {
                SkDebugf("Single textblob is larger than our whole budget");
            }
#endif
        }
    }

    void makeMRU(BitmapTextBlob* blob) {
        if (fBlobList.head() == blob) {
            return;
        }

        fBlobList.remove(blob);
        fBlobList.addToHead(blob);
    }

private:
    // TODO move to SkTextBlob
    void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
        SkTextBlob::RunIterator itCounter(blob);
        for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
            *glyphCount += itCounter.glyphCount();
        }
    }

    typedef SkTInternalLList<BitmapTextBlob> BitmapBlobList;

    // Budget was chosen to be ~4 megabytes.  The min alloc and pre alloc sizes in the pool are
    // based off of the largest cached textblob I have seen in the skps(a couple of kilobytes).
    static const int kPreAllocSize = 1 << 17;
    static const int kMinGrowthSize = 1 << 17;
    static const int kBudget = 1 << 22;
    BitmapBlobList fBlobList;
    SkTDynamicHash<BitmapTextBlob, uint32_t> fCache;
    GrMemoryPool fPool;
    PFOverBudgetCB fCallback;
    void* fData;
};

#endif