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 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 class GrResourceAllocator {
44 public:
GrResourceAllocator(GrResourceProvider * resourceProvider,GrDeinstantiateProxyTracker * tracker)45     GrResourceAllocator(GrResourceProvider* resourceProvider, GrDeinstantiateProxyTracker* tracker)
46             : fResourceProvider(resourceProvider), fDeinstantiateTracker(tracker) {}
47 
48     ~GrResourceAllocator();
49 
curOp()50     unsigned int curOp() const { return fNumOps; }
incOps()51     void incOps() { fNumOps++; }
numOps()52     unsigned int numOps() const { return fNumOps; }
53 
54     // Add a usage interval from 'start' to 'end' inclusive. This is usually used for renderTargets.
55     // If an existing interval already exists it will be expanded to include the new range.
56     void addInterval(GrSurfaceProxy*, unsigned int start, unsigned int end
57                      SkDEBUGCODE(, bool isDirectDstRead = false));
58 
59     // Add an interval that spans just the current op. Usually this is for texture uses.
60     // If an existing interval already exists it will be expanded to include the new operation.
61     void addInterval(GrSurfaceProxy* proxy
62                      SkDEBUGCODE(, bool isDirectDstRead = false)) {
63         this->addInterval(proxy, fNumOps, fNumOps SkDEBUGCODE(, isDirectDstRead));
64     }
65 
66     enum class AssignError {
67         kNoError,
68         kFailedProxyInstantiation
69     };
70 
71     // Returns true when the opLists from 'startIndex' to 'stopIndex' should be executed;
72     // false when nothing remains to be executed.
73     // If any proxy fails to instantiate, the AssignError will be set to kFailedProxyInstantiation.
74     // If this happens, the caller should remove all ops which reference an uninstantiated proxy.
75     // This is used to execute a portion of the queued opLists in order to reduce the total
76     // amount of GPU resources required.
77     bool assign(int* startIndex, int* stopIndex, AssignError* outError);
78 
79     void markEndOfOpList(int opListIndex);
80 
81 #if GR_ALLOCATION_SPEW
82     void dumpIntervals();
83 #endif
84 
85 private:
86     class Interval;
87 
88     // Remove dead intervals from the active list
89     void expire(unsigned int curIndex);
90 
91     // These two methods wrap the interactions with the free pool
92     void recycleSurface(sk_sp<GrSurface> surface);
93     sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy, bool needsStencil);
94 
95     struct FreePoolTraits {
GetKeyFreePoolTraits96         static const GrScratchKey& GetKey(const GrSurface& s) {
97             return s.resourcePriv().getScratchKey();
98         }
99 
HashFreePoolTraits100         static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
OnFreeFreePoolTraits101         static void OnFree(GrSurface* s) { s->unref(); }
102     };
103     typedef SkTMultiMap<GrSurface, GrScratchKey, FreePoolTraits> FreePoolMultiMap;
104 
105     typedef SkTDynamicHash<Interval, unsigned int> IntvlHash;
106 
107     class Interval {
108     public:
Interval(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)109         Interval(GrSurfaceProxy* proxy, unsigned int start, unsigned int end)
110             : fProxy(proxy)
111             , fProxyID(proxy->uniqueID().asUInt())
112             , fStart(start)
113             , fEnd(end)
114             , fNext(nullptr) {
115             SkASSERT(proxy);
116 #if GR_TRACK_INTERVAL_CREATION
117             fUniqueID = CreateUniqueID();
118             SkDebugf("New intvl %d: proxyID: %d [ %d, %d ]\n",
119                      fUniqueID, proxy->uniqueID().asUInt(), start, end);
120 #endif
121         }
122 
resetTo(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)123         void resetTo(GrSurfaceProxy* proxy, unsigned int start, unsigned int end) {
124             SkASSERT(proxy);
125             SkASSERT(!fNext);
126 
127             fProxy = proxy;
128             fProxyID = proxy->uniqueID().asUInt();
129             fStart = start;
130             fEnd = end;
131             fNext = nullptr;
132 #if GR_TRACK_INTERVAL_CREATION
133             fUniqueID = CreateUniqueID();
134             SkDebugf("New intvl %d: proxyID: %d [ %d, %d ]\n",
135                      fUniqueID, proxy->uniqueID().asUInt(), start, end);
136 #endif
137         }
138 
~Interval()139         ~Interval() {
140             SkASSERT(!fAssignedSurface);
141         }
142 
proxy()143         const GrSurfaceProxy* proxy() const { return fProxy; }
proxy()144         GrSurfaceProxy* proxy() { return fProxy; }
start()145         unsigned int start() const { return fStart; }
end()146         unsigned int end() const { return fEnd; }
next()147         const Interval* next() const { return fNext; }
next()148         Interval* next() { return fNext; }
149 
setNext(Interval * next)150         void setNext(Interval* next) { fNext = next; }
151 
extendEnd(unsigned int newEnd)152         void extendEnd(unsigned int newEnd) {
153             if (newEnd > fEnd) {
154                 fEnd = newEnd;
155 #if GR_TRACK_INTERVAL_CREATION
156                 SkDebugf("intvl %d: extending from %d to %d\n", fUniqueID, fEnd, newEnd);
157 #endif
158             }
159         }
160 
161         void assign(sk_sp<GrSurface>);
wasAssignedSurface()162         bool wasAssignedSurface() const { return fAssignedSurface != nullptr; }
detachSurface()163         sk_sp<GrSurface> detachSurface() { return std::move(fAssignedSurface); }
164 
165         // for SkTDynamicHash
GetKey(const Interval & intvl)166         static const uint32_t& GetKey(const Interval& intvl) {
167             return intvl.fProxyID;
168         }
Hash(const uint32_t & key)169         static uint32_t Hash(const uint32_t& key) { return key; }
170 
171     private:
172         sk_sp<GrSurface> fAssignedSurface;
173         GrSurfaceProxy*  fProxy;
174         uint32_t         fProxyID; // This is here b.c. DynamicHash requires a ref to the key
175         unsigned int     fStart;
176         unsigned int     fEnd;
177         Interval*        fNext;
178 
179 #if GR_TRACK_INTERVAL_CREATION
180         uint32_t        fUniqueID;
181 
182         uint32_t CreateUniqueID();
183 #endif
184     };
185 
186     class IntervalList {
187     public:
188         IntervalList() = default;
~IntervalList()189         ~IntervalList() {
190             // The only time we delete an IntervalList is in the GrResourceAllocator dtor.
191             // Since the arena allocator will clean up for us we don't bother here.
192         }
193 
empty()194         bool empty() const {
195             SkASSERT(SkToBool(fHead) == SkToBool(fTail));
196             return !SkToBool(fHead);
197         }
peekHead()198         const Interval* peekHead() const { return fHead; }
199         Interval* popHead();
200         void insertByIncreasingStart(Interval*);
201         void insertByIncreasingEnd(Interval*);
202         Interval* detachAll();
203 
204     private:
205         SkDEBUGCODE(void validate() const;)
206 
207         Interval* fHead = nullptr;
208         Interval* fTail = nullptr;
209     };
210 
211     // Compositing use cases can create > 80 intervals.
212     static const int kInitialArenaSize = 128 * sizeof(Interval);
213 
214     GrResourceProvider*          fResourceProvider;
215     GrDeinstantiateProxyTracker* fDeinstantiateTracker;
216     FreePoolMultiMap             fFreePool;          // Recently created/used GrSurfaces
217     IntvlHash                    fIntvlHash;         // All the intervals, hashed by proxyID
218 
219     IntervalList                 fIntvlList;         // All the intervals sorted by increasing start
220     IntervalList                 fActiveIntvls;      // List of live intervals during assignment
221                                                // (sorted by increasing end)
222     unsigned int                 fNumOps = 1;        // op # 0 is reserved for uploads at the start
223                                                // of a flush
224     SkTArray<unsigned int>       fEndOfOpListOpIndices;
225     int                          fCurOpListIndex = 0;
226 
227     SkDEBUGCODE(bool             fAssigned = false;)
228 
229     char                         fStorage[kInitialArenaSize];
230     SkArenaAlloc                 fIntervalAllocator{fStorage, kInitialArenaSize, kInitialArenaSize};
231     Interval*                    fFreeIntervalList = nullptr;
232 };
233 
234 #endif // GrResourceAllocator_DEFINED
235