blob: fae66579cf8ef6133e1327eac9e211260c90bad4 [file] [log] [blame]
Robert Phillips5af44de2017-07-18 14:49:38 -04001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/gpu/GrSurface.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "src/gpu/GrGpuResourcePriv.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040013#include "src/gpu/GrSurfaceProxy.h"
Robert Phillips8186cbe2017-11-01 17:32:39 -040014
Ben Wagner729a23f2019-05-17 16:29:34 -040015#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/core/SkTDynamicHash.h"
17#include "src/core/SkTMultiMap.h"
Robert Phillips5af44de2017-07-18 14:49:38 -040018
Brian Salomon876a0172019-03-08 11:12:14 -050019class GrDeinstantiateProxyTracker;
Robert Phillips5af44de2017-07-18 14:49:38 -040020class GrResourceProvider;
21
Robert Phillips715d08c2018-07-18 13:56:48 -040022// Print out explicit allocation information
23#define GR_ALLOCATION_SPEW 0
24
Robert Phillipsda1be462018-07-27 07:18:06 -040025// Print out information about interval creation
26#define GR_TRACK_INTERVAL_CREATION 0
27
Robert Phillips5af44de2017-07-18 14:49:38 -040028/*
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). When it comes time to allocate the resources it
33 * traverses the sorted list and:
34 * removes intervals from the active list that have completed (returning their GrSurfaces
35 * to the free pool)
36
37 * allocates a new resource (preferably from the free pool) for the new interval
38 * adds the new interval to the active list (that is sorted by increasing end index)
39 *
40 * Note: the op indices (used in the usage intervals) come from the order of the ops in
41 * their opLists after the opList DAG has been linearized.
Robert Phillips82774f82019-06-20 14:38:27 -040042 *
43 *************************************************************************************************
44 * How does instantiation failure handling work when explicitly allocating?
45 *
46 * In the gather usage intervals pass all the GrSurfaceProxies used in the flush should be
47 * gathered (i.e., in GrOpList::gatherProxyIntervals).
48 *
49 * The allocator will churn through this list but could fail anywhere.
50 *
51 * Allocation failure handling occurs at two levels:
52 *
53 * 1) If the GrSurface backing an opList fails to allocate then the entire opList is dropped.
54 *
55 * 2) If an individual GrSurfaceProxy fails to allocate then any ops that use it are dropped
56 * (via GrOpList::purgeOpsWithUninstantiatedProxies)
57 *
58 * The pass to determine which ops to drop is a bit laborious so we only check the opLists and
59 * individual ops when something goes wrong in allocation (i.e., when the return code from
60 * GrResourceAllocator::assign is bad)
61 *
62 * All together this means we should never attempt to draw an op which is missing some
63 * required GrSurface.
64 *
65 * One wrinkle in this plan is that promise images are fulfilled during the gather interval pass.
66 * If any of the promise images fail at this stage then the allocator is set into an error
67 * state and all allocations are then scanned for failures during the main allocation pass.
Robert Phillips5af44de2017-07-18 14:49:38 -040068 */
69class GrResourceAllocator {
70public:
Robert Phillipsc476e5d2019-03-26 14:50:08 -040071 GrResourceAllocator(GrResourceProvider* resourceProvider,
72 GrDeinstantiateProxyTracker* tracker
73 SkDEBUGCODE(, int numOpLists))
74 : fResourceProvider(resourceProvider)
75 , fDeinstantiateTracker(tracker)
76 SkDEBUGCODE(, fNumOpLists(numOpLists)) {
77 }
Robert Phillips5af44de2017-07-18 14:49:38 -040078
Robert Phillips5b65a842017-11-13 15:48:12 -050079 ~GrResourceAllocator();
80
Robert Phillips5af44de2017-07-18 14:49:38 -040081 unsigned int curOp() const { return fNumOps; }
82 void incOps() { fNumOps++; }
Robert Phillips5af44de2017-07-18 14:49:38 -040083
Robert Phillipsc73666f2019-04-24 08:49:48 -040084 /** Indicates whether a given call to addInterval represents an actual usage of the
85 * provided proxy. This is mainly here to accomodate deferred proxies attached to opLists.
86 * In that case we need to create an extra long interval for them (due to the upload) but
87 * don't want to count that usage/reference towards the proxy's recyclability.
88 */
89 enum class ActualUse : bool {
90 kNo = false,
91 kYes = true
92 };
93
Robert Phillipseafd48a2017-11-16 07:52:08 -050094 // Add a usage interval from 'start' to 'end' inclusive. This is usually used for renderTargets.
Robert Phillips5af44de2017-07-18 14:49:38 -040095 // If an existing interval already exists it will be expanded to include the new range.
Robert Phillipsc73666f2019-04-24 08:49:48 -040096 void addInterval(GrSurfaceProxy*, unsigned int start, unsigned int end, ActualUse actualUse
Chris Dalton8816b932017-11-29 16:48:25 -070097 SkDEBUGCODE(, bool isDirectDstRead = false));
Robert Phillips5af44de2017-07-18 14:49:38 -040098
Greg Danielaa3dfbe2018-01-29 10:34:25 -050099 enum class AssignError {
100 kNoError,
101 kFailedProxyInstantiation
102 };
103
Robert Phillipseafd48a2017-11-16 07:52:08 -0500104 // Returns true when the opLists from 'startIndex' to 'stopIndex' should be executed;
105 // false when nothing remains to be executed.
Greg Danielaa3dfbe2018-01-29 10:34:25 -0500106 // If any proxy fails to instantiate, the AssignError will be set to kFailedProxyInstantiation.
107 // If this happens, the caller should remove all ops which reference an uninstantiated proxy.
Robert Phillipseafd48a2017-11-16 07:52:08 -0500108 // This is used to execute a portion of the queued opLists in order to reduce the total
109 // amount of GPU resources required.
Brian Salomon577aa0f2018-11-30 13:32:23 -0500110 bool assign(int* startIndex, int* stopIndex, AssignError* outError);
Robert Phillipseafd48a2017-11-16 07:52:08 -0500111
Robert Phillipsc73666f2019-04-24 08:49:48 -0400112 void determineRecyclability();
Robert Phillipseafd48a2017-11-16 07:52:08 -0500113 void markEndOfOpList(int opListIndex);
Robert Phillips5af44de2017-07-18 14:49:38 -0400114
Robert Phillips715d08c2018-07-18 13:56:48 -0400115#if GR_ALLOCATION_SPEW
116 void dumpIntervals();
117#endif
118
Robert Phillips5af44de2017-07-18 14:49:38 -0400119private:
120 class Interval;
121
122 // Remove dead intervals from the active list
123 void expire(unsigned int curIndex);
124
Robert Phillipsc476e5d2019-03-26 14:50:08 -0400125 bool onOpListBoundary() const;
126 void forceIntermediateFlush(int* stopIndex);
127
Robert Phillips5af44de2017-07-18 14:49:38 -0400128 // These two methods wrap the interactions with the free pool
Robert Phillips715d08c2018-07-18 13:56:48 -0400129 void recycleSurface(sk_sp<GrSurface> surface);
Robert Phillipseafd48a2017-11-16 07:52:08 -0500130 sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy, bool needsStencil);
Robert Phillips5af44de2017-07-18 14:49:38 -0400131
Robert Phillips57aa3672017-07-21 11:38:13 -0400132 struct FreePoolTraits {
133 static const GrScratchKey& GetKey(const GrSurface& s) {
134 return s.resourcePriv().getScratchKey();
135 }
Robert Phillips5af44de2017-07-18 14:49:38 -0400136
Robert Phillips57aa3672017-07-21 11:38:13 -0400137 static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
Robert Phillipsf8e25022017-11-08 15:24:31 -0500138 static void OnFree(GrSurface* s) { s->unref(); }
Robert Phillips5af44de2017-07-18 14:49:38 -0400139 };
Robert Phillips57aa3672017-07-21 11:38:13 -0400140 typedef SkTMultiMap<GrSurface, GrScratchKey, FreePoolTraits> FreePoolMultiMap;
141
Robert Phillips5af44de2017-07-18 14:49:38 -0400142 typedef SkTDynamicHash<Interval, unsigned int> IntvlHash;
143
144 class Interval {
145 public:
146 Interval(GrSurfaceProxy* proxy, unsigned int start, unsigned int end)
147 : fProxy(proxy)
148 , fProxyID(proxy->uniqueID().asUInt())
149 , fStart(start)
150 , fEnd(end)
151 , fNext(nullptr) {
152 SkASSERT(proxy);
Robert Phillipsda1be462018-07-27 07:18:06 -0400153#if GR_TRACK_INTERVAL_CREATION
154 fUniqueID = CreateUniqueID();
155 SkDebugf("New intvl %d: proxyID: %d [ %d, %d ]\n",
156 fUniqueID, proxy->uniqueID().asUInt(), start, end);
157#endif
Robert Phillips5af44de2017-07-18 14:49:38 -0400158 }
159
Robert Phillips39667382019-04-17 16:03:30 -0400160 // Used when recycling an interval
Robert Phillips8186cbe2017-11-01 17:32:39 -0400161 void resetTo(GrSurfaceProxy* proxy, unsigned int start, unsigned int end) {
162 SkASSERT(proxy);
Robert Phillips39667382019-04-17 16:03:30 -0400163 SkASSERT(!fProxy && !fNext);
Robert Phillips8186cbe2017-11-01 17:32:39 -0400164
Robert Phillipsc73666f2019-04-24 08:49:48 -0400165 fUses = 0;
Robert Phillips8186cbe2017-11-01 17:32:39 -0400166 fProxy = proxy;
167 fProxyID = proxy->uniqueID().asUInt();
168 fStart = start;
169 fEnd = end;
170 fNext = nullptr;
Robert Phillipsda1be462018-07-27 07:18:06 -0400171#if GR_TRACK_INTERVAL_CREATION
172 fUniqueID = CreateUniqueID();
173 SkDebugf("New intvl %d: proxyID: %d [ %d, %d ]\n",
174 fUniqueID, proxy->uniqueID().asUInt(), start, end);
175#endif
Robert Phillips8186cbe2017-11-01 17:32:39 -0400176 }
177
Robert Phillips5b65a842017-11-13 15:48:12 -0500178 ~Interval() {
179 SkASSERT(!fAssignedSurface);
180 }
181
Robert Phillipsf8e25022017-11-08 15:24:31 -0500182 const GrSurfaceProxy* proxy() const { return fProxy; }
183 GrSurfaceProxy* proxy() { return fProxy; }
Robert Phillipsc73666f2019-04-24 08:49:48 -0400184
Robert Phillipsf8e25022017-11-08 15:24:31 -0500185 unsigned int start() const { return fStart; }
186 unsigned int end() const { return fEnd; }
Robert Phillipsc73666f2019-04-24 08:49:48 -0400187
188 void setNext(Interval* next) { fNext = next; }
Robert Phillipsf8e25022017-11-08 15:24:31 -0500189 const Interval* next() const { return fNext; }
190 Interval* next() { return fNext; }
191
Robert Phillipsc73666f2019-04-24 08:49:48 -0400192 void markAsRecyclable() { fIsRecyclable = true;}
193 bool isRecyclable() const { return fIsRecyclable; }
194
195 void addUse() { fUses++; }
196 int uses() { return fUses; }
Robert Phillipsf8e25022017-11-08 15:24:31 -0500197
198 void extendEnd(unsigned int newEnd) {
Chris Dalton8816b932017-11-29 16:48:25 -0700199 if (newEnd > fEnd) {
200 fEnd = newEnd;
Robert Phillipsda1be462018-07-27 07:18:06 -0400201#if GR_TRACK_INTERVAL_CREATION
202 SkDebugf("intvl %d: extending from %d to %d\n", fUniqueID, fEnd, newEnd);
203#endif
Chris Dalton8816b932017-11-29 16:48:25 -0700204 }
Robert Phillipsf8e25022017-11-08 15:24:31 -0500205 }
206
Robert Phillips5b65a842017-11-13 15:48:12 -0500207 void assign(sk_sp<GrSurface>);
Ben Wagner5d1adbf2018-05-28 13:35:39 -0400208 bool wasAssignedSurface() const { return fAssignedSurface != nullptr; }
Robert Phillips5b65a842017-11-13 15:48:12 -0500209 sk_sp<GrSurface> detachSurface() { return std::move(fAssignedSurface); }
210
Robert Phillips5af44de2017-07-18 14:49:38 -0400211 // for SkTDynamicHash
212 static const uint32_t& GetKey(const Interval& intvl) {
213 return intvl.fProxyID;
214 }
215 static uint32_t Hash(const uint32_t& key) { return key; }
216
Robert Phillipsf8e25022017-11-08 15:24:31 -0500217 private:
Robert Phillips5b65a842017-11-13 15:48:12 -0500218 sk_sp<GrSurface> fAssignedSurface;
219 GrSurfaceProxy* fProxy;
220 uint32_t fProxyID; // This is here b.c. DynamicHash requires a ref to the key
221 unsigned int fStart;
222 unsigned int fEnd;
223 Interval* fNext;
Robert Phillipsc73666f2019-04-24 08:49:48 -0400224 unsigned int fUses = 0;
225 bool fIsRecyclable = false;
Robert Phillipsda1be462018-07-27 07:18:06 -0400226
227#if GR_TRACK_INTERVAL_CREATION
228 uint32_t fUniqueID;
229
230 uint32_t CreateUniqueID();
231#endif
Robert Phillips5af44de2017-07-18 14:49:38 -0400232 };
233
234 class IntervalList {
235 public:
236 IntervalList() = default;
237 ~IntervalList() {
Robert Phillips8186cbe2017-11-01 17:32:39 -0400238 // The only time we delete an IntervalList is in the GrResourceAllocator dtor.
239 // Since the arena allocator will clean up for us we don't bother here.
Robert Phillips5af44de2017-07-18 14:49:38 -0400240 }
241
Robert Phillipsdf25e3a2018-08-08 12:48:40 -0400242 bool empty() const {
243 SkASSERT(SkToBool(fHead) == SkToBool(fTail));
244 return !SkToBool(fHead);
245 }
Robert Phillips5af44de2017-07-18 14:49:38 -0400246 const Interval* peekHead() const { return fHead; }
Robert Phillipsc73666f2019-04-24 08:49:48 -0400247 Interval* peekHead() { return fHead; }
Robert Phillips5af44de2017-07-18 14:49:38 -0400248 Interval* popHead();
249 void insertByIncreasingStart(Interval*);
250 void insertByIncreasingEnd(Interval*);
Robert Phillips4150eea2018-02-07 17:08:21 -0500251 Interval* detachAll();
Robert Phillips5af44de2017-07-18 14:49:38 -0400252
253 private:
Robert Phillipsdf25e3a2018-08-08 12:48:40 -0400254 SkDEBUGCODE(void validate() const;)
255
Robert Phillips5af44de2017-07-18 14:49:38 -0400256 Interval* fHead = nullptr;
Robert Phillipsdf25e3a2018-08-08 12:48:40 -0400257 Interval* fTail = nullptr;
Robert Phillips5af44de2017-07-18 14:49:38 -0400258 };
259
Brian Salomon309b0532018-10-10 17:22:00 -0400260 // Compositing use cases can create > 80 intervals.
261 static const int kInitialArenaSize = 128 * sizeof(Interval);
Robert Phillips8186cbe2017-11-01 17:32:39 -0400262
Brian Salomon577aa0f2018-11-30 13:32:23 -0500263 GrResourceProvider* fResourceProvider;
Brian Salomon876a0172019-03-08 11:12:14 -0500264 GrDeinstantiateProxyTracker* fDeinstantiateTracker;
Brian Salomon577aa0f2018-11-30 13:32:23 -0500265 FreePoolMultiMap fFreePool; // Recently created/used GrSurfaces
266 IntvlHash fIntvlHash; // All the intervals, hashed by proxyID
Robert Phillips5af44de2017-07-18 14:49:38 -0400267
Brian Salomon577aa0f2018-11-30 13:32:23 -0500268 IntervalList fIntvlList; // All the intervals sorted by increasing start
269 IntervalList fActiveIntvls; // List of live intervals during assignment
Robert Phillipsc476e5d2019-03-26 14:50:08 -0400270 // (sorted by increasing end)
271 unsigned int fNumOps = 0;
Brian Salomon577aa0f2018-11-30 13:32:23 -0500272 SkTArray<unsigned int> fEndOfOpListOpIndices;
273 int fCurOpListIndex = 0;
Robert Phillipsc476e5d2019-03-26 14:50:08 -0400274 SkDEBUGCODE(const int fNumOpLists = -1;)
Robert Phillips8186cbe2017-11-01 17:32:39 -0400275
Brian Salomon577aa0f2018-11-30 13:32:23 -0500276 SkDEBUGCODE(bool fAssigned = false;)
Robert Phillipseafd48a2017-11-16 07:52:08 -0500277
Brian Salomon577aa0f2018-11-30 13:32:23 -0500278 char fStorage[kInitialArenaSize];
279 SkArenaAlloc fIntervalAllocator{fStorage, kInitialArenaSize, kInitialArenaSize};
280 Interval* fFreeIntervalList = nullptr;
Robert Phillips82774f82019-06-20 14:38:27 -0400281 bool fLazyInstantiationError = false;
Robert Phillips5af44de2017-07-18 14:49:38 -0400282};
283
284#endif // GrResourceAllocator_DEFINED