• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef GrResourceAllocator_DEFINED
9 #define GrResourceAllocator_DEFINED
10 
11 #include "include/private/SkTHash.h"
12 
13 #include "src/gpu/GrHashMapWithCache.h"
14 #include "src/gpu/GrSurface.h"
15 #include "src/gpu/GrSurfaceProxy.h"
16 
17 #include "src/core/SkArenaAlloc.h"
18 #include "src/core/SkTMultiMap.h"
19 
20 class GrDirectContext;
21 
22 // Print out explicit allocation information
23 #define GR_ALLOCATION_SPEW 0
24 
25 // Print out information about interval creation
26 #define GR_TRACK_INTERVAL_CREATION 0
27 
28 /*
29  * The ResourceAllocator explicitly distributes GPU resources at flush time. It operates by
30  * being given the usage intervals of the various proxies. It keeps these intervals in a singly
31  * linked list sorted by increasing start index. (It also maintains a hash table from proxyID
32  * to interval to find proxy reuse). The ResourceAllocator uses Registers (in the sense of register
33  * allocation) to represent a future surface that will be used for each proxy during
34  * `planAssignment`, and then assigns actual surfaces during `assign`.
35  *
36  * Note: the op indices (used in the usage intervals) come from the order of the ops in
37  * their opsTasks after the opsTask DAG has been linearized.
38  *
39  * The planAssignment method traverses the sorted list and:
40  *     moves intervals from the active list that have completed (returning their registers
41  *     to the free pool) into the finished list (sorted by increasing start)
42  *
43  *     allocates a new register (preferably from the free pool) for the new interval
44  *     adds the new interval to the active list (that is sorted by increasing end index)
45  *
46  * After assignment planning, the user can choose to call `makeBudgetHeadroom` which:
47  *     computes how much VRAM would be needed for new resources for all extant Registers
48  *
49  *     asks the resource cache to purge enough resources to get that much free space
50  *
51  *     if it's not possible, do nothing and return false. The user may opt to reset
52  *     the allocator and start over with a different DAG.
53  *
54  * If the user wants to commit to the current assignment plan, they call `assign` which:
55  *     instantiates lazy proxies
56  *
57  *     instantantiates new surfaces for all registers that need them
58  *
59  *     assigns the surface for each register to all the proxies that will use it
60  *
61  *************************************************************************************************
62  * How does instantiation failure handling work when explicitly allocating?
63  *
64  * In the gather usage intervals pass all the GrSurfaceProxies used in the flush should be
65  * gathered (i.e., in OpsTask::gatherProxyIntervals).
66  *
67  * During addInterval, read-only lazy proxies are instantiated. If that fails, the resource
68  * allocator will note the failure and ignore pretty much anything else until `reset`.
69  *
70  * During planAssignment, fully-lazy proxies are instantiated so that we can know their size for
71  * budgeting purposes. If this fails, return false.
72  *
73  * During assign, partially-lazy proxies are instantiated and new surfaces are created for all other
74  * proxies. If any of these fails, return false.
75  *
76  * The drawing manager will drop the flush if any proxies fail to instantiate.
77  */
78 class GrResourceAllocator {
79 public:
GrResourceAllocator(GrDirectContext * dContext)80     GrResourceAllocator(GrDirectContext* dContext)
81             : fDContext(dContext) {}
82 
83     ~GrResourceAllocator();
84 
curOp()85     unsigned int curOp() const { return fNumOps; }
incOps()86     void incOps() { fNumOps++; }
87 
88     /** Indicates whether a given call to addInterval represents an actual usage of the
89      *  provided proxy. This is mainly here to accommodate deferred proxies attached to opsTasks.
90      *  In that case we need to create an extra long interval for them (due to the upload) but
91      *  don't want to count that usage/reference towards the proxy's recyclability.
92      */
93     enum class ActualUse : bool {
94         kNo  = false,
95         kYes = true
96     };
97 
98     // Add a usage interval from 'start' to 'end' inclusive. This is usually used for renderTargets.
99     // If an existing interval already exists it will be expanded to include the new range.
100     void addInterval(GrSurfaceProxy*, unsigned int start, unsigned int end, ActualUse actualUse
101                      SkDEBUGCODE(, bool isDirectDstRead = false));
102 
failedInstantiation()103     bool failedInstantiation() const { return fFailedInstantiation; }
104 
105     // Generate an internal plan for resource allocation. After this you can optionally call
106     // `makeBudgetHeadroom` to check whether that plan would go over our memory budget.
107     // Fully-lazy proxies are also instantiated at this point so that their size can
108     // be known accurately. Returns false if any lazy proxy failed to instantiate, true otherwise.
109     bool planAssignment();
110 
111     // Figure out how much VRAM headroom this plan requires. If there's enough purgeable resources,
112     // purge them and return true. Otherwise return false.
113     bool makeBudgetHeadroom();
114 
115     // Clear all internal state in preparation for a new set of intervals.
116     void reset();
117 
118     // Instantiate and assign resources to all proxies.
119     bool assign();
120 
121 #if GR_ALLOCATION_SPEW
122     void dumpIntervals();
123 #endif
124 
125 private:
126     class Interval;
127     class Register;
128 
129     // Remove dead intervals from the active list
130     void expire(unsigned int curIndex);
131 
132     // These two methods wrap the interactions with the free pool
133     void recycleRegister(Register* r);
134     Register* findOrCreateRegisterFor(GrSurfaceProxy* proxy);
135 
136     struct FreePoolTraits {
GetKeyFreePoolTraits137         static const GrScratchKey& GetKey(const Register& r) {
138             return r.scratchKey();
139         }
140 
HashFreePoolTraits141         static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
OnFreeFreePoolTraits142         static void OnFree(Register* r) { }
143     };
144     typedef SkTMultiMap<Register, GrScratchKey, FreePoolTraits> FreePoolMultiMap;
145 
146     typedef SkTHashMap<uint32_t, Interval*, GrCheapHash>        IntvlHash;
147 
148     struct UniqueKeyHash {
operatorUniqueKeyHash149         uint32_t operator()(const GrUniqueKey& key) const { return key.hash(); }
150     };
151     typedef SkTHashMap<GrUniqueKey, Register*, UniqueKeyHash> UniqueKeyRegisterHash;
152 
153     // Each proxy – with some exceptions – is assigned a register. After all assignments are made,
154     // another pass is performed to instantiate and assign actual surfaces to the proxies. Right
155     // now these are performed in one call, but in the future they will be separable and the user
156     // will be able to query re: memory cost before committing to surface creation.
157     class Register {
158     public:
159         // It's OK to pass an invalid scratch key iff the proxy has a unique key.
160         Register(GrSurfaceProxy* originatingProxy, GrScratchKey, GrResourceProvider*);
161 
scratchKey()162         const GrScratchKey& scratchKey() const { return fScratchKey; }
uniqueKey()163         const GrUniqueKey& uniqueKey() const { return fOriginatingProxy->getUniqueKey(); }
164 
accountedForInBudget()165         bool accountedForInBudget() const { return fAccountedForInBudget; }
setAccountedForInBudget()166         void setAccountedForInBudget() { fAccountedForInBudget = true; }
167 
existingSurface()168         GrSurface* existingSurface() const { return fExistingSurface.get(); }
169 
170         // Can this register be used by other proxies after this one?
171         bool isRecyclable(const GrCaps&, GrSurfaceProxy* proxy, int knownUseCount) const;
172 
173         // Resolve the register allocation to an actual GrSurface. 'fOriginatingProxy'
174         // is used to cache the allocation when a given register is used by multiple
175         // proxies.
176         bool instantiateSurface(GrSurfaceProxy*, GrResourceProvider*);
177 
178         SkDEBUGCODE(uint32_t uniqueID() const { return fUniqueID; })
179 
180     private:
181         GrSurfaceProxy*  fOriginatingProxy;
182         GrScratchKey     fScratchKey; // free pool wants a reference to this.
183         sk_sp<GrSurface> fExistingSurface; // queried from resource cache. may be null.
184         bool             fAccountedForInBudget = false;
185 
186 #ifdef SK_DEBUG
187         uint32_t         fUniqueID;
188 
189         static uint32_t  CreateUniqueID();
190 #endif
191     };
192 
193     class Interval {
194     public:
Interval(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)195         Interval(GrSurfaceProxy* proxy, unsigned int start, unsigned int end)
196                 : fProxy(proxy)
197                 , fStart(start)
198                 , fEnd(end) {
199             SkASSERT(proxy);
200             SkDEBUGCODE(fUniqueID = CreateUniqueID());
201 #if GR_TRACK_INTERVAL_CREATION
202             SkString proxyStr = proxy->dump();
203             SkDebugf("New intvl %d: %s [%d, %d]\n", fUniqueID, proxyStr.c_str(), start, end);
204 #endif
205         }
206 
proxy()207         const GrSurfaceProxy* proxy() const { return fProxy; }
proxy()208         GrSurfaceProxy* proxy() { return fProxy; }
209 
start()210         unsigned int start() const { return fStart; }
end()211         unsigned int end() const { return fEnd; }
212 
setNext(Interval * next)213         void setNext(Interval* next) { fNext = next; }
next()214         const Interval* next() const { return fNext; }
next()215         Interval* next() { return fNext; }
216 
getRegister()217         Register* getRegister() const { return fRegister; }
setRegister(Register * r)218         void setRegister(Register* r) { fRegister = r; }
219 
addUse()220         void addUse() { fUses++; }
uses()221         int uses() const { return fUses; }
222 
extendEnd(unsigned int newEnd)223         void extendEnd(unsigned int newEnd) {
224             if (newEnd > fEnd) {
225                 fEnd = newEnd;
226 #if GR_TRACK_INTERVAL_CREATION
227                 SkDebugf("intvl %d: extending from %d to %d\n", fUniqueID, fEnd, newEnd);
228 #endif
229             }
230         }
231 
232         SkDEBUGCODE(uint32_t uniqueID() const { return fUniqueID; })
233 
234     private:
235         GrSurfaceProxy*  fProxy;
236         unsigned int     fStart;
237         unsigned int     fEnd;
238         Interval*        fNext = nullptr;
239         unsigned int     fUses = 0;
240         Register*        fRegister = nullptr;
241 
242 #ifdef SK_DEBUG
243         uint32_t        fUniqueID;
244 
245         static uint32_t CreateUniqueID();
246 #endif
247     };
248 
249     class IntervalList {
250     public:
251         IntervalList() = default;
252         // N.B. No need for a destructor – the arena allocator will clean up for us.
253 
empty()254         bool empty() const {
255             SkASSERT(SkToBool(fHead) == SkToBool(fTail));
256             return !SkToBool(fHead);
257         }
peekHead()258         const Interval* peekHead() const { return fHead; }
peekHead()259         Interval* peekHead() { return fHead; }
260         Interval* popHead();
261         void insertByIncreasingStart(Interval*);
262         void insertByIncreasingEnd(Interval*);
263 
264     private:
265         SkDEBUGCODE(void validate() const;)
266 
267         Interval* fHead = nullptr;
268         Interval* fTail = nullptr;
269     };
270 
271     // Compositing use cases can create > 80 intervals.
272     static const int kInitialArenaSize = 128 * sizeof(Interval);
273 
274     GrDirectContext*             fDContext;
275     FreePoolMultiMap             fFreePool;          // Recently created/used GrSurfaces
276     IntvlHash                    fIntvlHash;         // All the intervals, hashed by proxyID
277 
278     IntervalList                 fIntvlList;         // All the intervals sorted by increasing start
279     IntervalList                 fActiveIntvls;      // List of live intervals during assignment
280                                                      // (sorted by increasing end)
281     IntervalList                 fFinishedIntvls;    // All the completed intervals
282                                                      // (sorted by increasing start)
283     UniqueKeyRegisterHash        fUniqueKeyRegisters;
284     unsigned int                 fNumOps = 0;
285 
286     SkDEBUGCODE(bool             fPlanned = false;)
287     SkDEBUGCODE(bool             fAssigned = false;)
288 
289     SkSTArenaAllocWithReset<kInitialArenaSize>   fInternalAllocator; // intervals & registers
290     bool                                         fFailedInstantiation = false;
291 };
292 
293 #endif // GrResourceAllocator_DEFINED
294