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 "GrGpuResourcePriv.h" 12 #include "GrSurface.h" 13 #include "GrSurfaceProxy.h" 14 15 #include "SkArenaAlloc.h" 16 #include "SkTDynamicHash.h" 17 #include "SkTMultiMap.h" 18 19 class GrResourceProvider; 20 21 /* 22 * The ResourceAllocator explicitly distributes GPU resources at flush time. It operates by 23 * being given the usage intervals of the various proxies. It keeps these intervals in a singly 24 * linked list sorted by increasing start index. (It also maintains a hash table from proxyID 25 * to interval to find proxy reuse). When it comes time to allocate the resources it 26 * traverses the sorted list and: 27 * removes intervals from the active list that have completed (returning their GrSurfaces 28 * to the free pool) 29 30 * allocates a new resource (preferably from the free pool) for the new interval 31 * adds the new interval to the active list (that is sorted by increasing end index) 32 * 33 * Note: the op indices (used in the usage intervals) come from the order of the ops in 34 * their opLists after the opList DAG has been linearized. 35 */ 36 class GrResourceAllocator { 37 public: 38 GrResourceAllocator(GrResourceProvider* resourceProvider) 39 : fResourceProvider(resourceProvider) { 40 } 41 42 ~GrResourceAllocator(); 43 44 unsigned int curOp() const { return fNumOps; } 45 void incOps() { fNumOps++; } 46 unsigned int numOps() const { return fNumOps; } 47 48 // Add a usage interval from 'start' to 'end' inclusive. This is usually used for renderTargets. 49 // If an existing interval already exists it will be expanded to include the new range. 50 void addInterval(GrSurfaceProxy*, unsigned int start, unsigned int end 51 SkDEBUGCODE(, bool isDirectDstRead = false)); 52 53 // Add an interval that spans just the current op. Usually this is for texture uses. 54 // If an existing interval already exists it will be expanded to include the new operation. 55 void addInterval(GrSurfaceProxy* proxy 56 SkDEBUGCODE(, bool isDirectDstRead = false)) { 57 this->addInterval(proxy, fNumOps, fNumOps SkDEBUGCODE(, isDirectDstRead)); 58 } 59 60 enum class AssignError { 61 kNoError, 62 kFailedProxyInstantiation 63 }; 64 65 // Returns true when the opLists from 'startIndex' to 'stopIndex' should be executed; 66 // false when nothing remains to be executed. 67 // If any proxy fails to instantiate, the AssignError will be set to kFailedProxyInstantiation. 68 // If this happens, the caller should remove all ops which reference an uninstantiated proxy. 69 // This is used to execute a portion of the queued opLists in order to reduce the total 70 // amount of GPU resources required. 71 bool assign(int* startIndex, int* stopIndex, AssignError* outError); 72 73 void markEndOfOpList(int opListIndex); 74 75 private: 76 class Interval; 77 78 // Remove dead intervals from the active list 79 void expire(unsigned int curIndex); 80 81 // These two methods wrap the interactions with the free pool 82 void freeUpSurface(sk_sp<GrSurface> surface); 83 sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy, bool needsStencil); 84 85 struct FreePoolTraits { 86 static const GrScratchKey& GetKey(const GrSurface& s) { 87 return s.resourcePriv().getScratchKey(); 88 } 89 90 static uint32_t Hash(const GrScratchKey& key) { return key.hash(); } 91 static void OnFree(GrSurface* s) { s->unref(); } 92 }; 93 typedef SkTMultiMap<GrSurface, GrScratchKey, FreePoolTraits> FreePoolMultiMap; 94 95 typedef SkTDynamicHash<Interval, unsigned int> IntvlHash; 96 97 class Interval { 98 public: 99 Interval(GrSurfaceProxy* proxy, unsigned int start, unsigned int end) 100 : fProxy(proxy) 101 , fProxyID(proxy->uniqueID().asUInt()) 102 , fStart(start) 103 , fEnd(end) 104 , fNext(nullptr) { 105 SkASSERT(proxy); 106 } 107 108 void resetTo(GrSurfaceProxy* proxy, unsigned int start, unsigned int end) { 109 SkASSERT(proxy); 110 111 fProxy = proxy; 112 fProxyID = proxy->uniqueID().asUInt(); 113 fStart = start; 114 fEnd = end; 115 fNext = nullptr; 116 } 117 118 ~Interval() { 119 SkASSERT(!fAssignedSurface); 120 } 121 122 const GrSurfaceProxy* proxy() const { return fProxy; } 123 GrSurfaceProxy* proxy() { return fProxy; } 124 unsigned int start() const { return fStart; } 125 unsigned int end() const { return fEnd; } 126 const Interval* next() const { return fNext; } 127 Interval* next() { return fNext; } 128 129 void setNext(Interval* next) { fNext = next; } 130 131 void extendEnd(unsigned int newEnd) { 132 if (newEnd > fEnd) { 133 fEnd = newEnd; 134 } 135 } 136 137 void assign(sk_sp<GrSurface>); 138 bool wasAssignedSurface() const { return fAssignedSurface; } 139 sk_sp<GrSurface> detachSurface() { return std::move(fAssignedSurface); } 140 141 // for SkTDynamicHash 142 static const uint32_t& GetKey(const Interval& intvl) { 143 return intvl.fProxyID; 144 } 145 static uint32_t Hash(const uint32_t& key) { return key; } 146 147 private: 148 sk_sp<GrSurface> fAssignedSurface; 149 GrSurfaceProxy* fProxy; 150 uint32_t fProxyID; // This is here b.c. DynamicHash requires a ref to the key 151 unsigned int fStart; 152 unsigned int fEnd; 153 Interval* fNext; 154 }; 155 156 class IntervalList { 157 public: 158 IntervalList() = default; 159 ~IntervalList() { 160 // The only time we delete an IntervalList is in the GrResourceAllocator dtor. 161 // Since the arena allocator will clean up for us we don't bother here. 162 } 163 164 bool empty() const { return !SkToBool(fHead); } 165 const Interval* peekHead() const { return fHead; } 166 Interval* popHead(); 167 void insertByIncreasingStart(Interval*); 168 void insertByIncreasingEnd(Interval*); 169 Interval* detachAll(); 170 171 private: 172 Interval* fHead = nullptr; 173 }; 174 175 // Gathered statistics indicate that 99% of flushes will be covered by <= 12 Intervals 176 static const int kInitialArenaSize = 12 * sizeof(Interval); 177 178 GrResourceProvider* fResourceProvider; 179 FreePoolMultiMap fFreePool; // Recently created/used GrSurfaces 180 IntvlHash fIntvlHash; // All the intervals, hashed by proxyID 181 182 IntervalList fIntvlList; // All the intervals sorted by increasing start 183 IntervalList fActiveIntvls; // List of live intervals during assignment 184 // (sorted by increasing end) 185 unsigned int fNumOps = 1; // op # 0 is reserved for uploads at the start 186 // of a flush 187 SkTArray<unsigned int> fEndOfOpListOpIndices; 188 int fCurOpListIndex = 0; 189 190 SkDEBUGCODE(bool fAssigned = false;) 191 192 char fStorage[kInitialArenaSize]; 193 SkArenaAlloc fIntervalAllocator { fStorage, kInitialArenaSize, 0 }; 194 Interval* fFreeIntervalList = nullptr; 195 }; 196 197 #endif // GrResourceAllocator_DEFINED 198