• 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/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