• 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 GrDeinstantiateProxyTracker;
20 class GrResourceProvider;
21 
22 // Print out explicit allocation information
23 #define GR_ALLOCATION_SPEW 0
24 
25 // Print out information about interval creation
26 #define GR_TRACK_INTERVAL_CREATION 0
27 
28 /*
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.
42  *
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.
68  */
69 class GrResourceAllocator {
70 public:
71     GrResourceAllocator(GrResourceProvider* resourceProvider,
72                         GrDeinstantiateProxyTracker* tracker
73                         SkDEBUGCODE(, int numOpLists))
fResourceProvider(resourceProvider)74             : fResourceProvider(resourceProvider)
75             , fDeinstantiateTracker(tracker)
76             SkDEBUGCODE(, fNumOpLists(numOpLists)) {
77     }
78 
79     ~GrResourceAllocator();
80 
curOp()81     unsigned int curOp() const { return fNumOps; }
incOps()82     void incOps() { fNumOps++; }
83 
84     /** 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 
94     // Add a usage interval from 'start' to 'end' inclusive. This is usually used for renderTargets.
95     // If an existing interval already exists it will be expanded to include the new range.
96     void addInterval(GrSurfaceProxy*, unsigned int start, unsigned int end, ActualUse actualUse
97                      SkDEBUGCODE(, bool isDirectDstRead = false));
98 
99     enum class AssignError {
100         kNoError,
101         kFailedProxyInstantiation
102     };
103 
104     // Returns true when the opLists from 'startIndex' to 'stopIndex' should be executed;
105     // false when nothing remains to be executed.
106     // 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.
108     // This is used to execute a portion of the queued opLists in order to reduce the total
109     // amount of GPU resources required.
110     bool assign(int* startIndex, int* stopIndex, AssignError* outError);
111 
112     void determineRecyclability();
113     void markEndOfOpList(int opListIndex);
114 
115 #if GR_ALLOCATION_SPEW
116     void dumpIntervals();
117 #endif
118 
119 private:
120     class Interval;
121 
122     // Remove dead intervals from the active list
123     void expire(unsigned int curIndex);
124 
125     bool onOpListBoundary() const;
126     void forceIntermediateFlush(int* stopIndex);
127 
128     // These two methods wrap the interactions with the free pool
129     void recycleSurface(sk_sp<GrSurface> surface);
130     sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy, int minStencilSampleCount);
131 
132     struct FreePoolTraits {
GetKeyFreePoolTraits133         static const GrScratchKey& GetKey(const GrSurface& s) {
134             return s.resourcePriv().getScratchKey();
135         }
136 
HashFreePoolTraits137         static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
OnFreeFreePoolTraits138         static void OnFree(GrSurface* s) { s->unref(); }
139     };
140     typedef SkTMultiMap<GrSurface, GrScratchKey, FreePoolTraits> FreePoolMultiMap;
141 
142     typedef SkTDynamicHash<Interval, unsigned int> IntvlHash;
143 
144     class Interval {
145     public:
Interval(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)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);
153 #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
158         }
159 
160         // Used when recycling an interval
resetTo(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)161         void resetTo(GrSurfaceProxy* proxy, unsigned int start, unsigned int end) {
162             SkASSERT(proxy);
163             SkASSERT(!fProxy && !fNext);
164 
165             fUses = 0;
166             fProxy = proxy;
167             fProxyID = proxy->uniqueID().asUInt();
168             fStart = start;
169             fEnd = end;
170             fNext = nullptr;
171 #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
176         }
177 
~Interval()178         ~Interval() {
179             SkASSERT(!fAssignedSurface);
180         }
181 
proxy()182         const GrSurfaceProxy* proxy() const { return fProxy; }
proxy()183         GrSurfaceProxy* proxy() { return fProxy; }
184 
start()185         unsigned int start() const { return fStart; }
end()186         unsigned int end() const { return fEnd; }
187 
setNext(Interval * next)188         void setNext(Interval* next) { fNext = next; }
next()189         const Interval* next() const { return fNext; }
next()190         Interval* next() { return fNext; }
191 
markAsRecyclable()192         void markAsRecyclable() { fIsRecyclable = true;}
isRecyclable()193         bool isRecyclable() const { return fIsRecyclable; }
194 
addUse()195         void addUse() { fUses++; }
uses()196         int uses() { return fUses; }
197 
extendEnd(unsigned int newEnd)198         void extendEnd(unsigned int newEnd) {
199             if (newEnd > fEnd) {
200                 fEnd = newEnd;
201 #if GR_TRACK_INTERVAL_CREATION
202                 SkDebugf("intvl %d: extending from %d to %d\n", fUniqueID, fEnd, newEnd);
203 #endif
204             }
205         }
206 
207         void assign(sk_sp<GrSurface>);
wasAssignedSurface()208         bool wasAssignedSurface() const { return fAssignedSurface != nullptr; }
detachSurface()209         sk_sp<GrSurface> detachSurface() { return std::move(fAssignedSurface); }
210 
211         // for SkTDynamicHash
GetKey(const Interval & intvl)212         static const uint32_t& GetKey(const Interval& intvl) {
213             return intvl.fProxyID;
214         }
Hash(const uint32_t & key)215         static uint32_t Hash(const uint32_t& key) { return key; }
216 
217     private:
218         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;
224         unsigned int     fUses = 0;
225         bool             fIsRecyclable = false;
226 
227 #if GR_TRACK_INTERVAL_CREATION
228         uint32_t        fUniqueID;
229 
230         uint32_t CreateUniqueID();
231 #endif
232     };
233 
234     class IntervalList {
235     public:
236         IntervalList() = default;
~IntervalList()237         ~IntervalList() {
238             // 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.
240         }
241 
empty()242         bool empty() const {
243             SkASSERT(SkToBool(fHead) == SkToBool(fTail));
244             return !SkToBool(fHead);
245         }
peekHead()246         const Interval* peekHead() const { return fHead; }
peekHead()247         Interval* peekHead() { return fHead; }
248         Interval* popHead();
249         void insertByIncreasingStart(Interval*);
250         void insertByIncreasingEnd(Interval*);
251         Interval* detachAll();
252 
253     private:
254         SkDEBUGCODE(void validate() const;)
255 
256         Interval* fHead = nullptr;
257         Interval* fTail = nullptr;
258     };
259 
260     // Compositing use cases can create > 80 intervals.
261     static const int kInitialArenaSize = 128 * sizeof(Interval);
262 
263     GrResourceProvider*          fResourceProvider;
264     GrDeinstantiateProxyTracker* fDeinstantiateTracker;
265     FreePoolMultiMap             fFreePool;          // Recently created/used GrSurfaces
266     IntvlHash                    fIntvlHash;         // All the intervals, hashed by proxyID
267 
268     IntervalList                 fIntvlList;         // All the intervals sorted by increasing start
269     IntervalList                 fActiveIntvls;      // List of live intervals during assignment
270                                                      // (sorted by increasing end)
271     unsigned int                 fNumOps = 0;
272     SkTArray<unsigned int>       fEndOfOpListOpIndices;
273     int                          fCurOpListIndex = 0;
274     SkDEBUGCODE(const int        fNumOpLists = -1;)
275 
276     SkDEBUGCODE(bool             fAssigned = false;)
277 
278     char                         fStorage[kInitialArenaSize];
279     SkArenaAlloc                 fIntervalAllocator{fStorage, kInitialArenaSize, kInitialArenaSize};
280     Interval*                    fFreeIntervalList = nullptr;
281     bool                         fLazyInstantiationError = false;
282 };
283 
284 #endif // GrResourceAllocator_DEFINED
285