• 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 "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:
GrResourceAllocator(GrResourceProvider * resourceProvider)38     GrResourceAllocator(GrResourceProvider* resourceProvider)
39             : fResourceProvider(resourceProvider) {
40     }
41 
42     ~GrResourceAllocator();
43 
curOp()44     unsigned int curOp() const { return fNumOps; }
incOps()45     void incOps() { fNumOps++; }
numOps()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 {
GetKeyFreePoolTraits86         static const GrScratchKey& GetKey(const GrSurface& s) {
87             return s.resourcePriv().getScratchKey();
88         }
89 
HashFreePoolTraits90         static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
OnFreeFreePoolTraits91         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:
Interval(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)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 
resetTo(GrSurfaceProxy * proxy,unsigned int start,unsigned int end)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 
~Interval()118         ~Interval() {
119             SkASSERT(!fAssignedSurface);
120         }
121 
proxy()122         const GrSurfaceProxy* proxy() const { return fProxy; }
proxy()123         GrSurfaceProxy* proxy() { return fProxy; }
start()124         unsigned int start() const { return fStart; }
end()125         unsigned int end() const { return fEnd; }
next()126         const Interval* next() const { return fNext; }
next()127         Interval* next() { return fNext; }
128 
setNext(Interval * next)129         void setNext(Interval* next) { fNext = next; }
130 
extendEnd(unsigned int newEnd)131         void extendEnd(unsigned int newEnd) {
132             if (newEnd > fEnd) {
133                 fEnd = newEnd;
134             }
135         }
136 
137         void assign(sk_sp<GrSurface>);
wasAssignedSurface()138         bool wasAssignedSurface() const { return fAssignedSurface; }
detachSurface()139         sk_sp<GrSurface> detachSurface() { return std::move(fAssignedSurface); }
140 
141         // for SkTDynamicHash
GetKey(const Interval & intvl)142         static const uint32_t& GetKey(const Interval& intvl) {
143             return intvl.fProxyID;
144         }
Hash(const uint32_t & key)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;
~IntervalList()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 
empty()164         bool empty() const { return !SkToBool(fHead); }
peekHead()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