blob: 8b7071e76b909954f9c40bfdaf7e3e8de67a45a4 [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 "src/gpu/GrGpuResourcePriv.h"
Greg Daniel456f9b52020-03-05 19:14:18 +000012#include "src/gpu/GrSurface.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
19class GrResourceProvider;
20
Robert Phillips715d08c2018-07-18 13:56:48 -040021// Print out explicit allocation information
22#define GR_ALLOCATION_SPEW 0
23
Robert Phillipsda1be462018-07-27 07:18:06 -040024// Print out information about interval creation
25#define GR_TRACK_INTERVAL_CREATION 0
26
Robert Phillips5af44de2017-07-18 14:49:38 -040027/*
28 * The ResourceAllocator explicitly distributes GPU resources at flush time. It operates by
29 * being given the usage intervals of the various proxies. It keeps these intervals in a singly
30 * linked list sorted by increasing start index. (It also maintains a hash table from proxyID
31 * to interval to find proxy reuse). When it comes time to allocate the resources it
32 * traverses the sorted list and:
33 * removes intervals from the active list that have completed (returning their GrSurfaces
34 * to the free pool)
35
36 * allocates a new resource (preferably from the free pool) for the new interval
37 * adds the new interval to the active list (that is sorted by increasing end index)
38 *
39 * Note: the op indices (used in the usage intervals) come from the order of the ops in
Greg Danielf41b2bd2019-08-22 16:19:24 -040040 * their opsTasks after the opsTask DAG has been linearized.
Robert Phillips82774f82019-06-20 14:38:27 -040041 *
42 *************************************************************************************************
43 * How does instantiation failure handling work when explicitly allocating?
44 *
45 * In the gather usage intervals pass all the GrSurfaceProxies used in the flush should be
Greg Danielf41b2bd2019-08-22 16:19:24 -040046 * gathered (i.e., in GrOpsTask::gatherProxyIntervals).
Robert Phillips82774f82019-06-20 14:38:27 -040047 *
48 * The allocator will churn through this list but could fail anywhere.
49 *
50 * Allocation failure handling occurs at two levels:
51 *
Greg Danielf41b2bd2019-08-22 16:19:24 -040052 * 1) If the GrSurface backing an opsTask fails to allocate then the entire opsTask is dropped.
Robert Phillips82774f82019-06-20 14:38:27 -040053 *
54 * 2) If an individual GrSurfaceProxy fails to allocate then any ops that use it are dropped
Greg Danielf41b2bd2019-08-22 16:19:24 -040055 * (via GrOpsTask::purgeOpsWithUninstantiatedProxies)
Robert Phillips82774f82019-06-20 14:38:27 -040056 *
Greg Danielf41b2bd2019-08-22 16:19:24 -040057 * The pass to determine which ops to drop is a bit laborious so we only check the opsTasks and
Robert Phillips82774f82019-06-20 14:38:27 -040058 * individual ops when something goes wrong in allocation (i.e., when the return code from
59 * GrResourceAllocator::assign is bad)
60 *
61 * All together this means we should never attempt to draw an op which is missing some
62 * required GrSurface.
63 *
64 * One wrinkle in this plan is that promise images are fulfilled during the gather interval pass.
65 * If any of the promise images fail at this stage then the allocator is set into an error
66 * state and all allocations are then scanned for failures during the main allocation pass.
Robert Phillips5af44de2017-07-18 14:49:38 -040067 */
68class GrResourceAllocator {
69public:
Brian Salomonbeb7f522019-08-30 16:19:42 -040070 GrResourceAllocator(GrResourceProvider* resourceProvider SkDEBUGCODE(, int numOpsTasks))
Adlai Hollerc616e1c2021-02-11 15:18:17 -050071 : fResourceProvider(resourceProvider) {}
Robert Phillips5af44de2017-07-18 14:49:38 -040072
Robert Phillips5b65a842017-11-13 15:48:12 -050073 ~GrResourceAllocator();
74
Robert Phillips5af44de2017-07-18 14:49:38 -040075 unsigned int curOp() const { return fNumOps; }
76 void incOps() { fNumOps++; }
Robert Phillips5af44de2017-07-18 14:49:38 -040077
Adlai Holler7f7a5df2021-02-09 17:41:10 +000078 /** Indicates whether a given call to addInterval represents an actual usage of the
79 * provided proxy. This is mainly here to accommodate deferred proxies attached to opsTasks.
80 * In that case we need to create an extra long interval for them (due to the upload) but
81 * don't want to count that usage/reference towards the proxy's recyclability.
82 */
83 enum class ActualUse : bool {
84 kNo = false,
85 kYes = true
86 };
87
Robert Phillipseafd48a2017-11-16 07:52:08 -050088 // Add a usage interval from 'start' to 'end' inclusive. This is usually used for renderTargets.
Robert Phillips5af44de2017-07-18 14:49:38 -040089 // If an existing interval already exists it will be expanded to include the new range.
Adlai Holler7f7a5df2021-02-09 17:41:10 +000090 void addInterval(GrSurfaceProxy*, unsigned int start, unsigned int end, ActualUse actualUse
Chris Dalton8816b932017-11-29 16:48:25 -070091 SkDEBUGCODE(, bool isDirectDstRead = false));
Robert Phillips5af44de2017-07-18 14:49:38 -040092
Greg Danielaa3dfbe2018-01-29 10:34:25 -050093 enum class AssignError {
94 kNoError,
95 kFailedProxyInstantiation
96 };
97
Greg Danielaa3dfbe2018-01-29 10:34:25 -050098 // If any proxy fails to instantiate, the AssignError will be set to kFailedProxyInstantiation.
99 // If this happens, the caller should remove all ops which reference an uninstantiated proxy.
Adlai Hollerc616e1c2021-02-11 15:18:17 -0500100 void assign(AssignError* outError);
Robert Phillips5af44de2017-07-18 14:49:38 -0400101
Robert Phillips715d08c2018-07-18 13:56:48 -0400102#if GR_ALLOCATION_SPEW
103 void dumpIntervals();
104#endif
105
Robert Phillips5af44de2017-07-18 14:49:38 -0400106private:
107 class Interval;
108
109 // Remove dead intervals from the active list
110 void expire(unsigned int curIndex);
111
112 // These two methods wrap the interactions with the free pool
Robert Phillips715d08c2018-07-18 13:56:48 -0400113 void recycleSurface(sk_sp<GrSurface> surface);
Chris Dalton0b68dda2019-11-07 21:08:03 -0700114 sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy);
Robert Phillips5af44de2017-07-18 14:49:38 -0400115
Adlai Hollerc616e1c2021-02-11 15:18:17 -0500116 void determineRecyclability();
117
Robert Phillips57aa3672017-07-21 11:38:13 -0400118 struct FreePoolTraits {
119 static const GrScratchKey& GetKey(const GrSurface& s) {
120 return s.resourcePriv().getScratchKey();
121 }
Robert Phillips5af44de2017-07-18 14:49:38 -0400122
Robert Phillips57aa3672017-07-21 11:38:13 -0400123 static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
Robert Phillipsf8e25022017-11-08 15:24:31 -0500124 static void OnFree(GrSurface* s) { s->unref(); }
Robert Phillips5af44de2017-07-18 14:49:38 -0400125 };
Robert Phillips57aa3672017-07-21 11:38:13 -0400126 typedef SkTMultiMap<GrSurface, GrScratchKey, FreePoolTraits> FreePoolMultiMap;
127
Robert Phillips5af44de2017-07-18 14:49:38 -0400128 typedef SkTDynamicHash<Interval, unsigned int> IntvlHash;
129
130 class Interval {
131 public:
132 Interval(GrSurfaceProxy* proxy, unsigned int start, unsigned int end)
133 : fProxy(proxy)
134 , fProxyID(proxy->uniqueID().asUInt())
135 , fStart(start)
136 , fEnd(end)
137 , fNext(nullptr) {
138 SkASSERT(proxy);
Robert Phillipsda1be462018-07-27 07:18:06 -0400139#if GR_TRACK_INTERVAL_CREATION
140 fUniqueID = CreateUniqueID();
Robert Phillips047d5bb2021-01-08 13:39:19 -0500141 SkString proxyStr = proxy->dump();
142 SkDebugf("New intvl %d: %s [%d, %d]\n", fUniqueID, proxyStr.c_str(), start, end);
Robert Phillipsda1be462018-07-27 07:18:06 -0400143#endif
Robert Phillips5af44de2017-07-18 14:49:38 -0400144 }
145
Robert Phillips39667382019-04-17 16:03:30 -0400146 // Used when recycling an interval
Robert Phillips8186cbe2017-11-01 17:32:39 -0400147 void resetTo(GrSurfaceProxy* proxy, unsigned int start, unsigned int end) {
148 SkASSERT(proxy);
Robert Phillips39667382019-04-17 16:03:30 -0400149 SkASSERT(!fProxy && !fNext);
Robert Phillips8186cbe2017-11-01 17:32:39 -0400150
Robert Phillipsc73666f2019-04-24 08:49:48 -0400151 fUses = 0;
Robert Phillips8186cbe2017-11-01 17:32:39 -0400152 fProxy = proxy;
153 fProxyID = proxy->uniqueID().asUInt();
154 fStart = start;
155 fEnd = end;
156 fNext = nullptr;
Robert Phillipsda1be462018-07-27 07:18:06 -0400157#if GR_TRACK_INTERVAL_CREATION
158 fUniqueID = CreateUniqueID();
Robert Phillips047d5bb2021-01-08 13:39:19 -0500159 SkString proxyStr = proxy->dump();
160 SkDebugf("New intvl %d: %s [ %d, %d ]\n", fUniqueID, proxyStr.c_str(), start, end);
Robert Phillipsda1be462018-07-27 07:18:06 -0400161#endif
Robert Phillips8186cbe2017-11-01 17:32:39 -0400162 }
163
Robert Phillips5b65a842017-11-13 15:48:12 -0500164 ~Interval() {
165 SkASSERT(!fAssignedSurface);
166 }
167
Robert Phillipsf8e25022017-11-08 15:24:31 -0500168 const GrSurfaceProxy* proxy() const { return fProxy; }
169 GrSurfaceProxy* proxy() { return fProxy; }
Robert Phillipsc73666f2019-04-24 08:49:48 -0400170
Robert Phillipsf8e25022017-11-08 15:24:31 -0500171 unsigned int start() const { return fStart; }
172 unsigned int end() const { return fEnd; }
Robert Phillipsc73666f2019-04-24 08:49:48 -0400173
174 void setNext(Interval* next) { fNext = next; }
Robert Phillipsf8e25022017-11-08 15:24:31 -0500175 const Interval* next() const { return fNext; }
176 Interval* next() { return fNext; }
177
Robert Phillipsc73666f2019-04-24 08:49:48 -0400178 void markAsRecyclable() { fIsRecyclable = true;}
179 bool isRecyclable() const { return fIsRecyclable; }
180
181 void addUse() { fUses++; }
182 int uses() { return fUses; }
Robert Phillipsf8e25022017-11-08 15:24:31 -0500183
184 void extendEnd(unsigned int newEnd) {
Chris Dalton8816b932017-11-29 16:48:25 -0700185 if (newEnd > fEnd) {
186 fEnd = newEnd;
Robert Phillipsda1be462018-07-27 07:18:06 -0400187#if GR_TRACK_INTERVAL_CREATION
188 SkDebugf("intvl %d: extending from %d to %d\n", fUniqueID, fEnd, newEnd);
189#endif
Chris Dalton8816b932017-11-29 16:48:25 -0700190 }
Robert Phillipsf8e25022017-11-08 15:24:31 -0500191 }
192
Robert Phillips5b65a842017-11-13 15:48:12 -0500193 void assign(sk_sp<GrSurface>);
Ben Wagner5d1adbf2018-05-28 13:35:39 -0400194 bool wasAssignedSurface() const { return fAssignedSurface != nullptr; }
Robert Phillips5b65a842017-11-13 15:48:12 -0500195 sk_sp<GrSurface> detachSurface() { return std::move(fAssignedSurface); }
196
Robert Phillips5af44de2017-07-18 14:49:38 -0400197 // for SkTDynamicHash
198 static const uint32_t& GetKey(const Interval& intvl) {
199 return intvl.fProxyID;
200 }
201 static uint32_t Hash(const uint32_t& key) { return key; }
202
Robert Phillipsf8e25022017-11-08 15:24:31 -0500203 private:
Adlai Hollerc616e1c2021-02-11 15:18:17 -0500204 // TODO: Do we really need this variable?
Robert Phillips5b65a842017-11-13 15:48:12 -0500205 sk_sp<GrSurface> fAssignedSurface;
206 GrSurfaceProxy* fProxy;
207 uint32_t fProxyID; // This is here b.c. DynamicHash requires a ref to the key
208 unsigned int fStart;
209 unsigned int fEnd;
210 Interval* fNext;
Robert Phillipsc73666f2019-04-24 08:49:48 -0400211 unsigned int fUses = 0;
212 bool fIsRecyclable = false;
Robert Phillipsda1be462018-07-27 07:18:06 -0400213
214#if GR_TRACK_INTERVAL_CREATION
215 uint32_t fUniqueID;
216
217 uint32_t CreateUniqueID();
218#endif
Robert Phillips5af44de2017-07-18 14:49:38 -0400219 };
220
221 class IntervalList {
222 public:
223 IntervalList() = default;
224 ~IntervalList() {
Robert Phillips8186cbe2017-11-01 17:32:39 -0400225 // The only time we delete an IntervalList is in the GrResourceAllocator dtor.
226 // Since the arena allocator will clean up for us we don't bother here.
Robert Phillips5af44de2017-07-18 14:49:38 -0400227 }
228
Robert Phillipsdf25e3a2018-08-08 12:48:40 -0400229 bool empty() const {
230 SkASSERT(SkToBool(fHead) == SkToBool(fTail));
231 return !SkToBool(fHead);
232 }
Robert Phillips5af44de2017-07-18 14:49:38 -0400233 const Interval* peekHead() const { return fHead; }
Robert Phillipsc73666f2019-04-24 08:49:48 -0400234 Interval* peekHead() { return fHead; }
Robert Phillips5af44de2017-07-18 14:49:38 -0400235 Interval* popHead();
236 void insertByIncreasingStart(Interval*);
237 void insertByIncreasingEnd(Interval*);
Robert Phillips4150eea2018-02-07 17:08:21 -0500238 Interval* detachAll();
Robert Phillips5af44de2017-07-18 14:49:38 -0400239
240 private:
Robert Phillipsdf25e3a2018-08-08 12:48:40 -0400241 SkDEBUGCODE(void validate() const;)
242
Robert Phillips5af44de2017-07-18 14:49:38 -0400243 Interval* fHead = nullptr;
Robert Phillipsdf25e3a2018-08-08 12:48:40 -0400244 Interval* fTail = nullptr;
Robert Phillips5af44de2017-07-18 14:49:38 -0400245 };
246
Brian Salomon309b0532018-10-10 17:22:00 -0400247 // Compositing use cases can create > 80 intervals.
248 static const int kInitialArenaSize = 128 * sizeof(Interval);
Robert Phillips8186cbe2017-11-01 17:32:39 -0400249
Brian Salomon577aa0f2018-11-30 13:32:23 -0500250 GrResourceProvider* fResourceProvider;
Brian Salomon577aa0f2018-11-30 13:32:23 -0500251 FreePoolMultiMap fFreePool; // Recently created/used GrSurfaces
252 IntvlHash fIntvlHash; // All the intervals, hashed by proxyID
Robert Phillips5af44de2017-07-18 14:49:38 -0400253
Brian Salomon577aa0f2018-11-30 13:32:23 -0500254 IntervalList fIntvlList; // All the intervals sorted by increasing start
255 IntervalList fActiveIntvls; // List of live intervals during assignment
Robert Phillipsc476e5d2019-03-26 14:50:08 -0400256 // (sorted by increasing end)
257 unsigned int fNumOps = 0;
Robert Phillips8186cbe2017-11-01 17:32:39 -0400258
Brian Salomon577aa0f2018-11-30 13:32:23 -0500259 SkDEBUGCODE(bool fAssigned = false;)
Robert Phillipseafd48a2017-11-16 07:52:08 -0500260
Brian Salomon577aa0f2018-11-30 13:32:23 -0500261 char fStorage[kInitialArenaSize];
262 SkArenaAlloc fIntervalAllocator{fStorage, kInitialArenaSize, kInitialArenaSize};
263 Interval* fFreeIntervalList = nullptr;
Robert Phillips82774f82019-06-20 14:38:27 -0400264 bool fLazyInstantiationError = false;
Robert Phillips5af44de2017-07-18 14:49:38 -0400265};
266
267#endif // GrResourceAllocator_DEFINED