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