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/gpu/GrSurface.h" 12 #include "src/gpu/GrGpuResourcePriv.h" 13 #include "src/gpu/GrSurfaceProxy.h" 14 15 #include "src/core/SkArenaAlloc.h" 16 #include "src/core/SkTDynamicHash.h" 17 #include "src/core/SkTMultiMap.h" 18 19 class GrResourceProvider; 20 21 // Print out explicit allocation information 22 #define GR_ALLOCATION_SPEW 0 23 24 // Print out information about interval creation 25 #define GR_TRACK_INTERVAL_CREATION 0 26 27 /* 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 40 * their opsTasks after the opsTask DAG has been linearized. 41 * 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 46 * gathered (i.e., in GrOpsTask::gatherProxyIntervals). 47 * 48 * The allocator will churn through this list but could fail anywhere. 49 * 50 * Allocation failure handling occurs at two levels: 51 * 52 * 1) If the GrSurface backing an opsTask fails to allocate then the entire opsTask is dropped. 53 * 54 * 2) If an individual GrSurfaceProxy fails to allocate then any ops that use it are dropped 55 * (via GrOpsTask::purgeOpsWithUninstantiatedProxies) 56 * 57 * The pass to determine which ops to drop is a bit laborious so we only check the opsTasks and 58 * 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. 67 */ 68 class GrResourceAllocator { 69 public: 70 GrResourceAllocator(GrResourceProvider* resourceProvider SkDEBUGCODE(, int numOpsTasks)) fResourceProvider(resourceProvider)71 : fResourceProvider(resourceProvider) SkDEBUGCODE(, fNumOpsTasks(numOpsTasks)) {} 72 73 ~GrResourceAllocator(); 74 curOp()75 unsigned int curOp() const { return fNumOps; } incOps()76 void incOps() { fNumOps++; } 77 78 /** Indicates whether a given call to addInterval represents an actual usage of the 79 * provided proxy. This is mainly here to accomodate 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 88 // Add a usage interval from 'start' to 'end' inclusive. This is usually used for renderTargets. 89 // If an existing interval already exists it will be expanded to include the new range. 90 void addInterval(GrSurfaceProxy*, unsigned int start, unsigned int end, ActualUse actualUse 91 SkDEBUGCODE(, bool isDirectDstRead = false)); 92 93 enum class AssignError { 94 kNoError, 95 kFailedProxyInstantiation 96 }; 97 98 // Returns true when the opsTasks from 'startIndex' to 'stopIndex' should be executed; 99 // false when nothing remains to be executed. 100 // If any proxy fails to instantiate, the AssignError will be set to kFailedProxyInstantiation. 101 // If this happens, the caller should remove all ops which reference an uninstantiated proxy. 102 // This is used to execute a portion of the queued opsTasks in order to reduce the total 103 // amount of GPU resources required. 104 bool assign(int* startIndex, int* stopIndex, AssignError* outError); 105 106 void determineRecyclability(); 107 void markEndOfOpsTask(int opsTaskIndex); 108 109 #if GR_ALLOCATION_SPEW 110 void dumpIntervals(); 111 #endif 112 113 private: 114 class Interval; 115 116 // Remove dead intervals from the active list 117 void expire(unsigned int curIndex); 118 119 bool onOpsTaskBoundary() const; 120 void forceIntermediateFlush(int* stopIndex); 121 122 // These two methods wrap the interactions with the free pool 123 void recycleSurface(sk_sp<GrSurface> surface); 124 sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy); 125 126 struct FreePoolTraits { GetKeyFreePoolTraits127 static const GrScratchKey& GetKey(const GrSurface& s) { 128 return s.resourcePriv().getScratchKey(); 129 } 130 HashFreePoolTraits131 static uint32_t Hash(const GrScratchKey& key) { return key.hash(); } OnFreeFreePoolTraits132 static void OnFree(GrSurface* s) { s->unref(); } 133 }; 134 typedef SkTMultiMap<GrSurface, GrScratchKey, FreePoolTraits> FreePoolMultiMap; 135 136 typedef SkTDynamicHash<Interval, unsigned int> IntvlHash; 137 138 class Interval { 139 public: Interval(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)140 Interval(GrSurfaceProxy* proxy, unsigned int start, unsigned int end) 141 : fProxy(proxy) 142 , fProxyID(proxy->uniqueID().asUInt()) 143 , fStart(start) 144 , fEnd(end) 145 , fNext(nullptr) { 146 SkASSERT(proxy); 147 #if GR_TRACK_INTERVAL_CREATION 148 fUniqueID = CreateUniqueID(); 149 SkDebugf("New intvl %d: proxyID: %d [ %d, %d ]\n", 150 fUniqueID, proxy->uniqueID().asUInt(), start, end); 151 #endif 152 } 153 154 // Used when recycling an interval resetTo(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)155 void resetTo(GrSurfaceProxy* proxy, unsigned int start, unsigned int end) { 156 SkASSERT(proxy); 157 SkASSERT(!fProxy && !fNext); 158 159 fUses = 0; 160 fProxy = proxy; 161 fProxyID = proxy->uniqueID().asUInt(); 162 fStart = start; 163 fEnd = end; 164 fNext = nullptr; 165 #if GR_TRACK_INTERVAL_CREATION 166 fUniqueID = CreateUniqueID(); 167 SkDebugf("New intvl %d: proxyID: %d [ %d, %d ]\n", 168 fUniqueID, proxy->uniqueID().asUInt(), start, end); 169 #endif 170 } 171 ~Interval()172 ~Interval() { 173 SkASSERT(!fAssignedSurface); 174 } 175 proxy()176 const GrSurfaceProxy* proxy() const { return fProxy; } proxy()177 GrSurfaceProxy* proxy() { return fProxy; } 178 start()179 unsigned int start() const { return fStart; } end()180 unsigned int end() const { return fEnd; } 181 setNext(Interval * next)182 void setNext(Interval* next) { fNext = next; } next()183 const Interval* next() const { return fNext; } next()184 Interval* next() { return fNext; } 185 markAsRecyclable()186 void markAsRecyclable() { fIsRecyclable = true;} isRecyclable()187 bool isRecyclable() const { return fIsRecyclable; } 188 addUse()189 void addUse() { fUses++; } uses()190 int uses() { return fUses; } 191 extendEnd(unsigned int newEnd)192 void extendEnd(unsigned int newEnd) { 193 if (newEnd > fEnd) { 194 fEnd = newEnd; 195 #if GR_TRACK_INTERVAL_CREATION 196 SkDebugf("intvl %d: extending from %d to %d\n", fUniqueID, fEnd, newEnd); 197 #endif 198 } 199 } 200 201 void assign(sk_sp<GrSurface>); wasAssignedSurface()202 bool wasAssignedSurface() const { return fAssignedSurface != nullptr; } detachSurface()203 sk_sp<GrSurface> detachSurface() { return std::move(fAssignedSurface); } 204 205 // for SkTDynamicHash GetKey(const Interval & intvl)206 static const uint32_t& GetKey(const Interval& intvl) { 207 return intvl.fProxyID; 208 } Hash(const uint32_t & key)209 static uint32_t Hash(const uint32_t& key) { return key; } 210 211 private: 212 sk_sp<GrSurface> fAssignedSurface; 213 GrSurfaceProxy* fProxy; 214 uint32_t fProxyID; // This is here b.c. DynamicHash requires a ref to the key 215 unsigned int fStart; 216 unsigned int fEnd; 217 Interval* fNext; 218 unsigned int fUses = 0; 219 bool fIsRecyclable = false; 220 221 #if GR_TRACK_INTERVAL_CREATION 222 uint32_t fUniqueID; 223 224 uint32_t CreateUniqueID(); 225 #endif 226 }; 227 228 class IntervalList { 229 public: 230 IntervalList() = default; ~IntervalList()231 ~IntervalList() { 232 // The only time we delete an IntervalList is in the GrResourceAllocator dtor. 233 // Since the arena allocator will clean up for us we don't bother here. 234 } 235 empty()236 bool empty() const { 237 SkASSERT(SkToBool(fHead) == SkToBool(fTail)); 238 return !SkToBool(fHead); 239 } peekHead()240 const Interval* peekHead() const { return fHead; } peekHead()241 Interval* peekHead() { return fHead; } 242 Interval* popHead(); 243 void insertByIncreasingStart(Interval*); 244 void insertByIncreasingEnd(Interval*); 245 Interval* detachAll(); 246 247 private: 248 SkDEBUGCODE(void validate() const;) 249 250 Interval* fHead = nullptr; 251 Interval* fTail = nullptr; 252 }; 253 254 // Compositing use cases can create > 80 intervals. 255 static const int kInitialArenaSize = 128 * sizeof(Interval); 256 257 GrResourceProvider* fResourceProvider; 258 FreePoolMultiMap fFreePool; // Recently created/used GrSurfaces 259 IntvlHash fIntvlHash; // All the intervals, hashed by proxyID 260 261 IntervalList fIntvlList; // All the intervals sorted by increasing start 262 IntervalList fActiveIntvls; // List of live intervals during assignment 263 // (sorted by increasing end) 264 unsigned int fNumOps = 0; 265 SkTArray<unsigned int> fEndOfOpsTaskOpIndices; 266 int fCurOpsTaskIndex = 0; 267 SkDEBUGCODE(const int fNumOpsTasks = -1;) 268 269 SkDEBUGCODE(bool fAssigned = false;) 270 271 char fStorage[kInitialArenaSize]; 272 SkArenaAlloc fIntervalAllocator{fStorage, kInitialArenaSize, kInitialArenaSize}; 273 Interval* fFreeIntervalList = nullptr; 274 bool fLazyInstantiationError = false; 275 }; 276 277 #endif // GrResourceAllocator_DEFINED 278