aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrResourceCache2.h
blob: 9331e9dfd3f73af46bd9dbdd0b833c9ea3762d5a (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrResourceCache2_DEFINED
#define GrResourceCache2_DEFINED

#include "GrGpuResource.h"
#include "GrGpuResourceCacheAccess.h"
#include "GrResourceKey.h"
#include "SkRefCnt.h"
#include "SkTInternalLList.h"
#include "SkTMultiMap.h"

/**
 * Manages the lifetime of all GrGpuResource instances.
 *
 * Resources may have optionally have two types of keys:
 *      1) A scratch key. This is for resources whose allocations are cached but not their contents.
 *         Multiple resources can share the same scratch key. This is so a caller can have two
 *         resource instances with the same properties (e.g. multipass rendering that ping-pongs
 *         between two temporary surfaces. The scratch key is set at resource creation time and
 *         should never change. Resources need not have a scratch key.
 *      2) A content key. This key represents the contents of the resource rather than just its
 *         allocation properties. They may not collide. The content key can be set after resource
 *         creation. Currently it may only be set once and cannot be cleared. This restriction will
 *         be removed.
 * If a resource has neither key type then it will be deleted as soon as the last reference to it
 * is dropped. If a key has both keys the content key takes precedence.
 */
class GrResourceCache2 {
public:
    GrResourceCache2();
    ~GrResourceCache2();

    /** Used to access functionality needed by GrGpuResource for lifetime management. */
    class ResourceAccess;
    ResourceAccess resourceAccess();

    /**
     * Sets the cache limits in terms of number of resources and max gpu memory byte size.
     */
    void setLimits(int count, size_t bytes);

    /**
     * Returns the number of resources.
     */
    int getResourceCount() const { return fCount; }

    /**
     * Returns the number of resources that count against the budget.
     */
    int getBudgetedResourceCount() const { return fBudgetedCount; }

    /**
     * Returns the number of bytes consumed by resources.
     */
    size_t getResourceBytes() const { return fBytes; }

    /**
     * Returns the number of bytes consumed by budgeted resources.
     */
    size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }

    /**
     * Returns the cached resources count budget.
     */
    int getMaxResourceCount() const { return fMaxCount; }

    /**
     * Returns the number of bytes consumed by cached resources.
     */
    size_t getMaxResourceBytes() const { return fMaxBytes; }

    /**
     * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
     * the cache.
     */
    void abandonAll();

    /**
     * Releases the backend API resources owned by all GrGpuResource objects and removes them from
     * the cache.
     */
    void releaseAll();

    enum {
        /** Preferentially returns scratch resources with no pending IO. */
        kPreferNoPendingIO_ScratchFlag = 0x1,
        /** Will not return any resources that match but have pending IO. */
        kRequireNoPendingIO_ScratchFlag = 0x2,
    };

    /**
     * Find a resource that matches a scratch key.
     */
    GrGpuResource* findAndRefScratchResource(const GrResourceKey& scratchKey, uint32_t flags = 0);
    
#ifdef SK_DEBUG
    // This is not particularly fast and only used for validation, so debug only.
    int countScratchEntriesForKey(const GrResourceKey& scratchKey) const {
        SkASSERT(scratchKey.isScratch());
        return fScratchMap.countForKey(scratchKey);
    }
#endif

    /**
     * Find a resource that matches a content key.
     */
    GrGpuResource* findAndRefContentResource(const GrResourceKey& contentKey) {
        SkASSERT(!contentKey.isScratch());
        GrGpuResource* resource = fContentHash.find(contentKey);
        if (resource) {
            resource->ref();
            this->makeResourceMRU(resource);
        }
        return resource;
    }

    /**
     * Query whether a content key exists in the cache.
     */
    bool hasContentKey(const GrResourceKey& contentKey) const {
        SkASSERT(!contentKey.isScratch());
        return SkToBool(fContentHash.find(contentKey));
    }

    /** Purges all resources that don't have external owners. */
    void purgeAllUnlocked();

    /**
     * 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);

    /**
     * Set the callback the cache should use when it is still over budget after a purge. The 'data'
     * provided here will be passed back to the callback. Note that the cache will attempt to purge
     * any resources newly freed by the callback.
     */
    void setOverBudgetCallback(PFOverBudgetCB overBudgetCB, void* data) {
        fOverBudgetCB = overBudgetCB;
        fOverBudgetData = data;
    }

#if GR_GPU_STATS
    void printStats() const;
#endif

private:
    ///////////////////////////////////////////////////////////////////////////
    /// @name Methods accessible via ResourceAccess
    ////
    void insertResource(GrGpuResource*);
    void removeResource(GrGpuResource*);
    void notifyPurgable(GrGpuResource*);
    void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
    bool didSetContentKey(GrGpuResource*);
    void didChangeBudgetStatus(GrGpuResource*);
    void makeResourceMRU(GrGpuResource*);
    /// @}

    void purgeAsNeeded() {
        if (fPurging || (fBudgetedCount <= fMaxCount && fBudgetedBytes < fMaxBytes)) {
            return;
        }
        this->internalPurgeAsNeeded();
    }

    void internalPurgeAsNeeded();

#ifdef SK_DEBUG
    bool isInCache(const GrGpuResource* r) const { return fResources.isInList(r); }
    void validate() const;
#else
    void validate() const {}
#endif

    class AutoValidate;

    class AvailableForScratchUse;

    struct ScratchMapTraits {
        static const GrResourceKey& GetKey(const GrGpuResource& r) {
            return r.cacheAccess().getScratchKey();
        }

        static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); }
    };
    typedef SkTMultiMap<GrGpuResource, GrResourceKey, ScratchMapTraits> ScratchMap;

    struct ContentHashTraits {
        static const GrResourceKey& GetKey(const GrGpuResource& r) {
            return *r.cacheAccess().getContentKey();
        }

        static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); }
    };
    typedef SkTDynamicHash<GrGpuResource, GrResourceKey, ContentHashTraits> ContentHash;

    typedef SkTInternalLList<GrGpuResource> ResourceList;

    ResourceList                        fResources;
    // This map holds all resources that can be used as scratch resources.
    ScratchMap                          fScratchMap;
    // This holds all resources that have content keys.
    ContentHash                         fContentHash;

    // our budget, used in purgeAsNeeded()
    int                                 fMaxCount;
    size_t                              fMaxBytes;

#if GR_CACHE_STATS
    int                                 fHighWaterCount;
    size_t                              fHighWaterBytes;
    int                                 fBudgetedHighWaterCount;
    size_t                              fBudgetedHighWaterBytes;
#endif

    // our current stats for all resources
    int                                 fCount;
    size_t                              fBytes;

    // our current stats for resources that count against the budget
    int                                 fBudgetedCount;
    size_t                              fBudgetedBytes;

    // prevents recursive purging
    bool                                fPurging;
    bool                                fNewlyPurgableResourceWhilePurging;

    PFOverBudgetCB                      fOverBudgetCB;
    void*                               fOverBudgetData;

};

class GrResourceCache2::ResourceAccess {
private:
    ResourceAccess(GrResourceCache2* cache) : fCache(cache) { }
    ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
    ResourceAccess& operator=(const ResourceAccess&); // unimpl

    /**
     * Insert a resource into the cache.
     */
    void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }

    /**
     * Removes a resource from the cache.
     */
    void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }

    /**
     * Called by GrGpuResources when they detects that they are newly purgable.
     */
    void notifyPurgable(GrGpuResource* resource) { fCache->notifyPurgable(resource); }

    /**
     * Called by GrGpuResources when their sizes change.
     */
    void didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
        fCache->didChangeGpuMemorySize(resource, oldSize);
    }

    /**
     * Called by GrGpuResources when their content keys change.
     *
     * This currently returns a bool and fails when an existing resource has a key that collides
     * with the new content key. In the future it will null out the content key for the existing
     * resource. The failure is a temporary measure taken because duties are split between two
     * cache objects currently.
     */
    bool didSetContentKey(GrGpuResource* resource) { return fCache->didSetContentKey(resource); }


    /**
     * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
     */
    void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }

    // No taking addresses of this type.
    const ResourceAccess* operator&() const;
    ResourceAccess* operator&();

    GrResourceCache2* fCache;

    friend class GrGpuResource; // To access all the proxy inline methods.
    friend class GrResourceCache2; // To create this type.
};

inline GrResourceCache2::ResourceAccess GrResourceCache2::resourceAccess() {
    return ResourceAccess(this);
}

#endif