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 "GrConfig.h"
15#include "GrTypes.h"
16#include "GrTMultiMap.h"
17#include "GrBinHashKey.h"
18#include "SkMessageBus.h"
19#include "SkTInternalLList.h"
20
21class GrCacheable;
22class GrResourceCache;
23class GrResourceCacheEntry;
24
25class GrResourceKey {
26public:
27    static GrCacheID::Domain ScratchDomain() {
28        static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain();
29        return gDomain;
30    }
31
32    /** Uniquely identifies the GrCacheable subclass in the key to avoid collisions
33        across resource types. */
34    typedef uint8_t ResourceType;
35
36    /** Flags set by the GrCacheable subclass. */
37    typedef uint8_t ResourceFlags;
38
39    /** Generate a unique ResourceType */
40    static ResourceType GenerateResourceType();
41
42    /** Creates a key for resource */
43    GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
44        this->init(id.getDomain(), id.getKey(), type, flags);
45    };
46
47    GrResourceKey(const GrResourceKey& src) {
48        fKey = src.fKey;
49    }
50
51    GrResourceKey() {
52        fKey.reset();
53    }
54
55    void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
56        this->init(id.getDomain(), id.getKey(), type, flags);
57    }
58
59    uint32_t getHash() const {
60        return fKey.getHash();
61    }
62
63    bool isScratch() const {
64        return ScratchDomain() ==
65            *reinterpret_cast<const GrCacheID::Domain*>(fKey.getData() +
66                                                        kCacheIDDomainOffset);
67    }
68
69    ResourceType getResourceType() const {
70        return *reinterpret_cast<const ResourceType*>(fKey.getData() +
71                                                      kResourceTypeOffset);
72    }
73
74    ResourceFlags getResourceFlags() const {
75        return *reinterpret_cast<const ResourceFlags*>(fKey.getData() +
76                                                       kResourceFlagsOffset);
77    }
78
79    bool operator==(const GrResourceKey& other) const { return fKey == other.fKey; }
80
81private:
82    enum {
83        kCacheIDKeyOffset = 0,
84        kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key),
85        kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain),
86        kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType),
87        kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags),
88        kKeySize = SkAlign4(kPadOffset),
89        kPadSize = kKeySize - kPadOffset
90    };
91
92    void init(const GrCacheID::Domain domain,
93              const GrCacheID::Key& key,
94              ResourceType type,
95              ResourceFlags flags) {
96        union {
97            uint8_t  fKey8[kKeySize];
98            uint32_t fKey32[kKeySize / 4];
99        } keyData;
100
101        uint8_t* k = keyData.fKey8;
102        memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key));
103        memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain));
104        memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType));
105        memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags));
106        memset(k + kPadOffset, 0, kPadSize);
107        fKey.setKeyData(keyData.fKey32);
108    }
109    GrBinHashKey<kKeySize> fKey;
110};
111
112// The cache listens for these messages to purge junk resources proactively.
113struct GrResourceInvalidatedMessage {
114    GrResourceKey key;
115};
116
117///////////////////////////////////////////////////////////////////////////////
118
119class GrResourceCacheEntry {
120public:
121    GrCacheable* resource() const { return fResource; }
122    const GrResourceKey& key() const { return fKey; }
123
124    static const GrResourceKey& GetKey(const GrResourceCacheEntry& e) { return e.key(); }
125    static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); }
126#ifdef SK_DEBUG
127    void validate() const;
128#else
129    void validate() const {}
130#endif
131
132    /**
133     *  Update the cached size for this entry and inform the resource cache that
134     *  it has changed. Usually invoked from GrCacheable::didChangeGpuMemorySize,
135     *  not directly from here.
136     */
137    void didChangeResourceSize();
138
139private:
140    GrResourceCacheEntry(GrResourceCache* resourceCache,
141                         const GrResourceKey& key,
142                         GrCacheable* resource);
143    ~GrResourceCacheEntry();
144
145    GrResourceCache* fResourceCache;
146    GrResourceKey    fKey;
147    GrCacheable*     fResource;
148    size_t           fCachedSize;
149    bool             fIsExclusive;
150
151    // Linked list for the LRU ordering.
152    SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceCacheEntry);
153
154    friend class GrResourceCache;
155};
156
157///////////////////////////////////////////////////////////////////////////////
158
159/**
160 *  Cache of GrCacheable objects.
161 *
162 *  These have a corresponding GrResourceKey, built from 128bits identifying the
163 *  resource. Multiple resources can map to same GrResourceKey.
164 *
165 *  The cache stores the entries in a double-linked list, which is its LRU.
166 *  When an entry is "locked" (i.e. given to the caller), it is moved to the
167 *  head of the list. If/when we must purge some of the entries, we walk the
168 *  list backwards from the tail, since those are the least recently used.
169 *
170 *  For fast searches, we maintain a hash map based on the GrResourceKey.
171 *
172 *  It is a goal to make the GrResourceCache the central repository and bookkeeper
173 *  of all resources. It should replace the linked list of GrGpuObjects that
174 *  GrGpu uses to call abandon/release.
175 */
176class GrResourceCache {
177public:
178    GrResourceCache(int maxCount, size_t maxBytes);
179    ~GrResourceCache();
180
181    /**
182     *  Return the current resource cache limits.
183     *
184     *  @param maxResource If non-null, returns maximum number of resources
185     *                     that can be held in the cache.
186     *  @param maxBytes    If non-null, returns maximum number of bytes of
187     *                     gpu memory that can be held in the cache.
188     */
189    void getLimits(int* maxResources, size_t* maxBytes) const;
190
191    /**
192     *  Specify the resource cache limits. If the current cache exceeds either
193     *  of these, it will be purged (LRU) to keep the cache within these limits.
194     *
195     *  @param maxResources The maximum number of resources that can be held in
196     *                      the cache.
197     *  @param maxBytes     The maximum number of bytes of resource memory that
198     *                      can be held in the cache.
199     */
200    void setLimits(int maxResources, size_t maxResourceBytes);
201
202    /**
203     *  The callback function used by the cache when it is still over budget
204     *  after a purge. The passed in 'data' is the same 'data' handed to
205     *  setOverbudgetCallback. The callback returns true if some resources
206     *  have been freed.
207     */
208    typedef bool (*PFOverbudgetCB)(void* data);
209
210    /**
211     *  Set the callback the cache should use when it is still over budget
212     *  after a purge. The 'data' provided here will be passed back to the
213     *  callback. Note that the cache will attempt to purge any resources newly
214     *  freed by the callback.
215     */
216    void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) {
217        fOverbudgetCB = overbudgetCB;
218        fOverbudgetData = data;
219    }
220
221    /**
222     * Returns the number of bytes consumed by cached resources.
223     */
224    size_t getCachedResourceBytes() const { return fEntryBytes; }
225
226    /**
227     * Returns the number of cached resources.
228     */
229    int getCachedResourceCount() const { return fEntryCount; }
230
231    // For a found or added resource to be completely exclusive to the caller
232    // both the kNoOtherOwners and kHide flags need to be specified
233    enum OwnershipFlags {
234        kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners
235        kHide_OwnershipFlag = 0x2  // found/added resource is hidden from future 'find's
236    };
237
238    /**
239     *  Search for an entry with the same Key. If found, return it.
240     *  If not found, return null.
241     *  If ownershipFlags includes kNoOtherOwners and a resource is returned
242     *  then that resource has no other refs to it.
243     *  If ownershipFlags includes kHide and a resource is returned then that
244     *  resource will not be returned from future 'find' calls until it is
245     *  'freed' (and recycled) or makeNonExclusive is called.
246     *  For a resource to be completely exclusive to a caller both kNoOtherOwners
247     *  and kHide must be specified.
248     */
249    GrCacheable* find(const GrResourceKey& key,
250                      uint32_t ownershipFlags = 0);
251
252    /**
253     *  Add the new resource to the cache (by creating a new cache entry based
254     *  on the provided key and resource).
255     *
256     *  Ownership of the resource is transferred to the resource cache,
257     *  which will unref() it when it is purged or deleted.
258     *
259     *  If ownershipFlags includes kHide, subsequent calls to 'find' will not
260     *  return 'resource' until it is 'freed' (and recycled) or makeNonExclusive
261     *  is called.
262     */
263    void addResource(const GrResourceKey& key,
264                     GrCacheable* resource,
265                     uint32_t ownershipFlags = 0);
266
267    /**
268     * Determines if the cache contains an entry matching a key. If a matching
269     * entry exists but was detached then it will not be found.
270     */
271    bool hasKey(const GrResourceKey& key) const { return NULL != fCache.find(key); }
272
273    /**
274     * Hide 'entry' so that future searches will not find it. Such
275     * hidden entries will not be purged. The entry still counts against
276     * the cache's budget and should be made non-exclusive when exclusive access
277     * is no longer needed.
278     */
279    void makeExclusive(GrResourceCacheEntry* entry);
280
281    /**
282     * Restore 'entry' so that it can be found by future searches. 'entry'
283     * will also be purgeable (provided its lock count is now 0.)
284     */
285    void makeNonExclusive(GrResourceCacheEntry* entry);
286
287    /**
288     * Notify the cache that the size of a resource has changed.
289     */
290    void didIncreaseResourceSize(const GrResourceCacheEntry*, size_t amountInc);
291    void didDecreaseResourceSize(const GrResourceCacheEntry*, size_t amountDec);
292
293    /**
294     * Remove a resource from the cache and delete it!
295     */
296    void deleteResource(GrResourceCacheEntry* entry);
297
298    /**
299     * Removes every resource in the cache that isn't locked.
300     */
301    void purgeAllUnlocked();
302
303    /**
304     * Allow cache to purge unused resources to obey resource limitations
305     * Note: this entry point will be hidden (again) once totally ref-driven
306     * cache maintenance is implemented. Note that the overbudget callback
307     * will be called if the initial purge doesn't get the cache under
308     * its budget.
309     *
310     * extraCount and extraBytes are added to the current resource allocation
311     * to make sure enough room is available for future additions (e.g,
312     * 10MB across 10 textures is about to be added).
313     */
314    void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0);
315
316#ifdef SK_DEBUG
317    void validate() const;
318#else
319    void validate() const {}
320#endif
321
322#if GR_CACHE_STATS
323    void printStats();
324#endif
325
326private:
327    enum BudgetBehaviors {
328        kAccountFor_BudgetBehavior,
329        kIgnore_BudgetBehavior
330    };
331
332    void internalDetach(GrResourceCacheEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
333    void attachToHead(GrResourceCacheEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
334
335    void removeInvalidResource(GrResourceCacheEntry* entry);
336
337    GrTMultiMap<GrResourceCacheEntry, GrResourceKey> fCache;
338
339    // We're an internal doubly linked list
340    typedef SkTInternalLList<GrResourceCacheEntry> EntryList;
341    EntryList      fList;
342
343#ifdef SK_DEBUG
344    // These objects cannot be returned by a search
345    EntryList      fExclusiveList;
346#endif
347
348    // our budget, used in purgeAsNeeded()
349    int            fMaxCount;
350    size_t         fMaxBytes;
351
352    // our current stats, related to our budget
353#if GR_CACHE_STATS
354    int            fHighWaterEntryCount;
355    size_t         fHighWaterEntryBytes;
356    int            fHighWaterClientDetachedCount;
357    size_t         fHighWaterClientDetachedBytes;
358#endif
359
360    int            fEntryCount;
361    size_t         fEntryBytes;
362    int            fClientDetachedCount;
363    size_t         fClientDetachedBytes;
364
365    // prevents recursive purging
366    bool           fPurging;
367
368    PFOverbudgetCB fOverbudgetCB;
369    void*          fOverbudgetData;
370
371    void internalPurge(int extraCount, size_t extraBytes);
372
373    // Listen for messages that a resource has been invalidated and purge cached junk proactively.
374    SkMessageBus<GrResourceInvalidatedMessage>::Inbox fInvalidationInbox;
375    void purgeInvalidated();
376
377#ifdef SK_DEBUG
378    static size_t countBytes(const SkTInternalLList<GrResourceCacheEntry>& list);
379#endif
380};
381
382///////////////////////////////////////////////////////////////////////////////
383
384#ifdef SK_DEBUG
385    class GrAutoResourceCacheValidate {
386    public:
387        GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
388            cache->validate();
389        }
390        ~GrAutoResourceCacheValidate() {
391            fCache->validate();
392        }
393    private:
394        GrResourceCache* fCache;
395    };
396#else
397    class GrAutoResourceCacheValidate {
398    public:
399        GrAutoResourceCacheValidate(GrResourceCache*) {}
400    };
401#endif
402
403#endif
404