1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10
11#ifndef GrResourceCache_DEFINED
12#define GrResourceCache_DEFINED
13
14#include "GrTypes.h"
15#include "GrTHashCache.h"
16
17class GrResource;
18
19// return true if a<b, or false if b<a
20//
21#define RET_IF_LT_OR_GT(a, b)   \
22    do {                        \
23        if ((a) < (b)) {        \
24            return true;        \
25        }                       \
26        if ((b) < (a)) {        \
27            return false;       \
28        }                       \
29    } while (0)
30
31/**
32 *  Helper class for GrResourceCache, the Key is used to identify src data for
33 *  a resource. It is identified by 2 32bit data fields which can hold any
34 *  data (uninterpreted by the cache) and a width/height.
35 */
36class GrResourceKey {
37public:
38    enum {
39        kHashBits   = 7,
40        kHashCount  = 1 << kHashBits,
41        kHashMask   = kHashCount - 1
42    };
43
44    GrResourceKey(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) {
45        fP[0] = p0;
46        fP[1] = p1;
47        fP[2] = p2;
48        fP[3] = p3;
49        this->computeHashIndex();
50    }
51
52    GrResourceKey(uint32_t v[4]) {
53        memcpy(fP, v, 4 * sizeof(uint32_t));
54        this->computeHashIndex();
55    }
56
57    GrResourceKey(const GrResourceKey& src) {
58        memcpy(fP, src.fP, 4 * sizeof(uint32_t));
59#if GR_DEBUG
60        this->computeHashIndex();
61        GrAssert(fHashIndex == src.fHashIndex);
62#endif
63        fHashIndex = src.fHashIndex;
64    }
65
66    //!< returns hash value [0..kHashMask] for the key
67    int hashIndex() const { return fHashIndex; }
68
69    friend bool operator==(const GrResourceKey& a, const GrResourceKey& b) {
70        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
71        return 0 == memcmp(a.fP, b.fP, 4 * sizeof(uint32_t));
72    }
73
74    friend bool operator!=(const GrResourceKey& a, const GrResourceKey& b) {
75        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
76        return !(a == b);
77    }
78
79    friend bool operator<(const GrResourceKey& a, const GrResourceKey& b) {
80        RET_IF_LT_OR_GT(a.fP[0], b.fP[0]);
81        RET_IF_LT_OR_GT(a.fP[1], b.fP[1]);
82        RET_IF_LT_OR_GT(a.fP[2], b.fP[2]);
83        return a.fP[3] < b.fP[3];
84    }
85
86    uint32_t getValue32(int i) const {
87        GrAssert(i >=0 && i < 4);
88        return fP[i];
89    }
90private:
91
92    static uint32_t rol(uint32_t x) {
93        return (x >> 24) | (x << 8);
94    }
95    static uint32_t ror(uint32_t x) {
96        return (x >> 8) | (x << 24);
97    }
98    static uint32_t rohalf(uint32_t x) {
99        return (x >> 16) | (x << 16);
100    }
101
102    void computeHashIndex() {
103        uint32_t hash = fP[0] ^ rol(fP[1]) ^ ror(fP[2]) ^ rohalf(fP[3]);
104        // this way to mix and reduce hash to its index may have to change
105        // depending on how many bits we allocate to the index
106        hash ^= hash >> 16;
107        hash ^= hash >> 8;
108        fHashIndex = hash & kHashMask;
109    }
110
111    uint32_t    fP[4];
112
113    // this is computed from the fP... fields
114    int         fHashIndex;
115
116    friend class GrContext;
117};
118
119///////////////////////////////////////////////////////////////////////////////
120
121class GrResourceEntry {
122public:
123    GrResource* resource() const { return fResource; }
124    const GrResourceKey& key() const { return fKey; }
125
126#if GR_DEBUG
127    GrResourceEntry* next() const { return fNext; }
128    GrResourceEntry* prev() const { return fPrev; }
129#endif
130
131#if GR_DEBUG
132    void validate() const;
133#else
134    void validate() const {}
135#endif
136
137private:
138    GrResourceEntry(const GrResourceKey& key, GrResource* resource);
139    ~GrResourceEntry();
140
141    bool isLocked() const { return fLockCount != 0; }
142    void lock() { ++fLockCount; }
143    void unlock() {
144        GrAssert(fLockCount > 0);
145        --fLockCount;
146    }
147
148    GrResourceKey    fKey;
149    GrResource*      fResource;
150
151    // track if we're in use, used when we need to purge
152    // we only purge unlocked entries
153    int fLockCount;
154
155    // we're a dlinklist
156    GrResourceEntry* fPrev;
157    GrResourceEntry* fNext;
158
159    friend class GrResourceCache;
160};
161
162///////////////////////////////////////////////////////////////////////////////
163
164#include "GrTHashCache.h"
165
166/**
167 *  Cache of GrResource objects.
168 *
169 *  These have a corresponding GrResourceKey, built from 128bits identifying the
170 *  resource.
171 *
172 *  The cache stores the entries in a double-linked list, which is its LRU.
173 *  When an entry is "locked" (i.e. given to the caller), it is moved to the
174 *  head of the list. If/when we must purge some of the entries, we walk the
175 *  list backwards from the tail, since those are the least recently used.
176 *
177 *  For fast searches, we maintain a sorted array (based on the GrResourceKey)
178 *  which we can bsearch. When a new entry is added, it is inserted into this
179 *  array.
180 *
181 *  For even faster searches, a hash is computed from the Key. If there is
182 *  a collision between two keys with the same hash, we fall back on the
183 *  bsearch, and update the hash to reflect the most recent Key requested.
184 */
185class GrResourceCache {
186public:
187    GrResourceCache(int maxCount, size_t maxBytes);
188    ~GrResourceCache();
189
190    /**
191     *  Return the current resource cache limits.
192     *
193     *  @param maxResource If non-null, returns maximum number of resources
194     *                     that can be held in the cache.
195     *  @param maxBytes    If non-null, returns maximum number of bytes of
196     *                         gpu memory that can be held in the cache.
197     */
198    void getLimits(int* maxResources, size_t* maxBytes) const;
199
200    /**
201     *  Specify the resource cache limits. If the current cache exceeds either
202     *  of these, it will be purged (LRU) to keep the cache within these limits.
203     *
204     *  @param maxResources The maximum number of resources that can be held in
205     *                      the cache.
206     *  @param maxBytes     The maximum number of bytes of resource memory that
207     *                      can be held in the cache.
208     */
209    void setLimits(int maxResource, size_t maxResourceBytes);
210
211    /**
212     * Returns the number of bytes consumed by cached resources.
213     */
214    size_t getCachedResourceBytes() const { return fEntryBytes; }
215
216    /**
217     * Controls whether locks should be nestable or not.
218     */
219    enum LockType {
220        kNested_LockType,
221        kSingle_LockType,
222    };
223
224    /**
225     *  Search for an entry with the same Key. If found, "lock" it and return it.
226     *  If not found, return null.
227     */
228    GrResourceEntry* findAndLock(const GrResourceKey&, LockType style);
229
230    /**
231     *  Create a new entry, based on the specified key and resource, and return
232     *  its "locked" entry.
233     *
234     *  Ownership of the resource is transferred to the Entry, which will unref()
235     *  it when we are purged or deleted.
236     */
237    GrResourceEntry* createAndLock(const GrResourceKey&, GrResource*);
238
239    /**
240     * Determines if the cache contains an entry matching a key. If a matching
241     * entry exists but was detached then it will not be found.
242     */
243    bool hasKey(const GrResourceKey& key) const;
244
245    /**
246     * Detach removes an entry from the cache. This prevents the entry from
247     * being found by a subsequent findAndLock() until it is reattached. The
248     * entry still counts against the cache's budget and should be reattached
249     * when exclusive access is no longer needed.
250     */
251    void detach(GrResourceEntry*);
252
253    /**
254     * Reattaches a resource to the cache and unlocks it. Allows it to be found
255     * by a subsequent findAndLock or be purged (provided its lock count is
256     * now 0.)
257     */
258    void reattachAndUnlock(GrResourceEntry*);
259
260    /**
261     *  When done with an entry, call unlock(entry) on it, which returns it to
262     *  a purgable state.
263     */
264    void unlock(GrResourceEntry*);
265
266    void removeAll();
267
268#if GR_DEBUG
269    void validate() const;
270#else
271    void validate() const {}
272#endif
273
274private:
275    void internalDetach(GrResourceEntry*, bool);
276    void attachToHead(GrResourceEntry*, bool);
277    void purgeAsNeeded();
278
279    class Key;
280    GrTHashTable<GrResourceEntry, Key, 8> fCache;
281
282    // manage the dlink list
283    GrResourceEntry* fHead;
284    GrResourceEntry* fTail;
285
286    // our budget, used in purgeAsNeeded()
287    int fMaxCount;
288    size_t fMaxBytes;
289
290    // our current stats, related to our budget
291    int fEntryCount;
292    int fUnlockedEntryCount;
293    size_t fEntryBytes;
294    int fClientDetachedCount;
295    size_t fClientDetachedBytes;
296
297    // prevents recursive purging
298    bool fPurging;
299};
300
301///////////////////////////////////////////////////////////////////////////////
302
303#if GR_DEBUG
304    class GrAutoResourceCacheValidate {
305    public:
306        GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
307            cache->validate();
308        }
309        ~GrAutoResourceCacheValidate() {
310            fCache->validate();
311        }
312    private:
313        GrResourceCache* fCache;
314    };
315#else
316    class GrAutoResourceCacheValidate {
317    public:
318        GrAutoResourceCacheValidate(GrResourceCache*) {}
319    };
320#endif
321
322#endif
323