• 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 #include "GrResourceAllocator.h"
9 
10 #include "GrGpuResourcePriv.h"
11 #include "GrOpList.h"
12 #include "GrRenderTargetProxy.h"
13 #include "GrResourceCache.h"
14 #include "GrResourceProvider.h"
15 #include "GrSurfacePriv.h"
16 #include "GrSurfaceProxy.h"
17 #include "GrSurfaceProxyPriv.h"
18 #include "GrTextureProxy.h"
19 
assign(sk_sp<GrSurface> s)20 void GrResourceAllocator::Interval::assign(sk_sp<GrSurface> s) {
21     SkASSERT(!fAssignedSurface);
22     fAssignedSurface = s;
23     fProxy->priv().assign(std::move(s));
24 }
25 
26 
markEndOfOpList(int opListIndex)27 void GrResourceAllocator::markEndOfOpList(int opListIndex) {
28     SkASSERT(!fAssigned);      // We shouldn't be adding any opLists after (or during) assignment
29 
30     SkASSERT(fEndOfOpListOpIndices.count() == opListIndex);
31     if (!fEndOfOpListOpIndices.empty()) {
32         SkASSERT(fEndOfOpListOpIndices.back() < this->curOp());
33     }
34 
35     fEndOfOpListOpIndices.push_back(this->curOp()); // This is the first op index of the next opList
36 }
37 
~GrResourceAllocator()38 GrResourceAllocator::~GrResourceAllocator() {
39 #ifndef SK_DISABLE_EXPLICIT_GPU_RESOURCE_ALLOCATION
40     SkASSERT(fIntvlList.empty());
41     SkASSERT(fActiveIntvls.empty());
42     SkASSERT(!fIntvlHash.count());
43 #endif
44 }
45 
46 void GrResourceAllocator::addInterval(GrSurfaceProxy* proxy, unsigned int start, unsigned int end
47                                       SkDEBUGCODE(, bool isDirectDstRead)) {
48     SkASSERT(start <= end);
49     SkASSERT(!fAssigned);      // We shouldn't be adding any intervals after (or during) assignment
50 
51     if (Interval* intvl = fIntvlHash.find(proxy->uniqueID().asUInt())) {
52         // Revise the interval for an existing use
53 #ifdef SK_DEBUG
54         if (0 == start && 0 == end) {
55             // This interval is for the initial upload to a deferred proxy. Due to the vagaries
56             // of how deferred proxies are collected they can appear as uploads multiple times in a
57             // single opLists' list and as uploads in several opLists.
58             SkASSERT(0 == intvl->start());
59         } else if (isDirectDstRead) {
60             // Direct reads from the render target itself should occur w/in the existing interval
61             SkASSERT(intvl->start() <= start && intvl->end() >= end);
62         } else {
63             SkASSERT(intvl->end() <= start && intvl->end() <= end);
64         }
65 #endif
66         intvl->extendEnd(end);
67         return;
68     }
69 
70     Interval* newIntvl;
71     if (fFreeIntervalList) {
72         newIntvl = fFreeIntervalList;
73         fFreeIntervalList = newIntvl->next();
74         newIntvl->resetTo(proxy, start, end);
75     } else {
76         newIntvl = fIntervalAllocator.make<Interval>(proxy, start, end);
77     }
78 
79     fIntvlList.insertByIncreasingStart(newIntvl);
80     fIntvlHash.add(newIntvl);
81 
82 #ifdef SK_DISABLE_EXPLICIT_GPU_RESOURCE_ALLOCATION
83     // FIXME: remove this once we can do the lazy instantiation from assign instead.
84     if (GrSurfaceProxy::LazyState::kNot != proxy->lazyInstantiationState()) {
85         proxy->priv().doLazyInstantiation(fResourceProvider);
86     }
87 #endif
88 }
89 
popHead()90 GrResourceAllocator::Interval* GrResourceAllocator::IntervalList::popHead() {
91     Interval* temp = fHead;
92     if (temp) {
93         fHead = temp->next();
94     }
95     return temp;
96 }
97 
98 // TODO: fuse this with insertByIncreasingEnd
insertByIncreasingStart(Interval * intvl)99 void GrResourceAllocator::IntervalList::insertByIncreasingStart(Interval* intvl) {
100     if (!fHead) {
101         intvl->setNext(nullptr);
102         fHead = intvl;
103     } else if (intvl->start() <= fHead->start()) {
104         intvl->setNext(fHead);
105         fHead = intvl;
106     } else {
107         Interval* prev = fHead;
108         Interval* next = prev->next();
109         for (; next && intvl->start() > next->start(); prev = next, next = next->next()) {
110         }
111         intvl->setNext(next);
112         prev->setNext(intvl);
113     }
114 }
115 
116 // TODO: fuse this with insertByIncreasingStart
insertByIncreasingEnd(Interval * intvl)117 void GrResourceAllocator::IntervalList::insertByIncreasingEnd(Interval* intvl) {
118     if (!fHead) {
119         intvl->setNext(nullptr);
120         fHead = intvl;
121     } else if (intvl->end() <= fHead->end()) {
122         intvl->setNext(fHead);
123         fHead = intvl;
124     } else {
125         Interval* prev = fHead;
126         Interval* next = prev->next();
127         for (; next && intvl->end() > next->end(); prev = next, next = next->next()) {
128         }
129         intvl->setNext(next);
130         prev->setNext(intvl);
131     }
132 }
133 
134 // 'surface' can be reused. Add it back to the free pool.
freeUpSurface(sk_sp<GrSurface> surface)135 void GrResourceAllocator::freeUpSurface(sk_sp<GrSurface> surface) {
136     const GrScratchKey &key = surface->resourcePriv().getScratchKey();
137 
138     if (!key.isValid()) {
139         return; // can't do it w/o a valid scratch key
140     }
141 
142     if (surface->getUniqueKey().isValid()) {
143         // If the surface has a unique key we throw it back into the resource cache.
144         // If things get really tight 'findSurfaceFor' may pull it back out but there is
145         // no need to have it in tight rotation.
146         return;
147     }
148 
149     // TODO: fix this insertion so we get a more LRU-ish behavior
150     fFreePool.insert(key, surface.release());
151 }
152 
153 // First try to reuse one of the recently allocated/used GrSurfaces in the free pool.
154 // If we can't find a useable one, create a new one.
findSurfaceFor(const GrSurfaceProxy * proxy,bool needsStencil)155 sk_sp<GrSurface> GrResourceAllocator::findSurfaceFor(const GrSurfaceProxy* proxy,
156                                                      bool needsStencil) {
157     // First look in the free pool
158     GrScratchKey key;
159 
160     proxy->priv().computeScratchKey(&key);
161 
162     auto filter = [&] (const GrSurface* s) {
163         return !proxy->priv().requiresNoPendingIO() || !s->surfacePriv().hasPendingIO();
164     };
165     sk_sp<GrSurface> surface(fFreePool.findAndRemove(key, filter));
166     if (surface) {
167         if (SkBudgeted::kYes == proxy->isBudgeted() &&
168             SkBudgeted::kNo == surface->resourcePriv().isBudgeted()) {
169             // This gets the job done but isn't quite correct. It would be better to try to
170             // match budgeted proxies w/ budgeted surface and unbudgeted w/ unbudgeted.
171             surface->resourcePriv().makeBudgeted();
172         }
173 
174         GrSurfaceProxyPriv::AttachStencilIfNeeded(fResourceProvider, surface.get(), needsStencil);
175         return surface;
176     }
177 
178     // Failing that, try to grab a new one from the resource cache
179     return proxy->priv().createSurface(fResourceProvider);
180 }
181 
182 // Remove any intervals that end before the current index. Return their GrSurfaces
183 // to the free pool.
expire(unsigned int curIndex)184 void GrResourceAllocator::expire(unsigned int curIndex) {
185     while (!fActiveIntvls.empty() && fActiveIntvls.peekHead()->end() < curIndex) {
186         Interval* temp = fActiveIntvls.popHead();
187 
188         if (temp->wasAssignedSurface()) {
189             this->freeUpSurface(temp->detachSurface());
190         }
191 
192         // Add temp to the free interval list so it can be reused
193         temp->setNext(fFreeIntervalList);
194         fFreeIntervalList = temp;
195     }
196 }
197 
assign(int * startIndex,int * stopIndex,AssignError * outError)198 bool GrResourceAllocator::assign(int* startIndex, int* stopIndex, AssignError* outError) {
199     SkASSERT(outError);
200     *outError = AssignError::kNoError;
201 
202     fIntvlHash.reset(); // we don't need the interval hash anymore
203     if (fIntvlList.empty()) {
204         return false;          // nothing to render
205     }
206 
207     *startIndex = fCurOpListIndex;
208     *stopIndex = fEndOfOpListOpIndices.count();
209 
210     SkDEBUGCODE(fAssigned = true;)
211 
212     while (Interval* cur = fIntvlList.popHead()) {
213         if (fEndOfOpListOpIndices[fCurOpListIndex] < cur->start()) {
214             fCurOpListIndex++;
215         }
216 
217         this->expire(cur->start());
218 
219         bool needsStencil = cur->proxy()->asRenderTargetProxy()
220                                             ? cur->proxy()->asRenderTargetProxy()->needsStencil()
221                                             : false;
222 
223         if (cur->proxy()->priv().isInstantiated()) {
224             GrSurfaceProxyPriv::AttachStencilIfNeeded(fResourceProvider,
225                                                       cur->proxy()->priv().peekSurface(),
226                                                       needsStencil);
227 
228             fActiveIntvls.insertByIncreasingEnd(cur);
229 
230             if (fResourceProvider->overBudget()) {
231                 // Only force intermediate draws on opList boundaries
232                 if (!fIntvlList.empty() &&
233                     fEndOfOpListOpIndices[fCurOpListIndex] < fIntvlList.peekHead()->start()) {
234                     *stopIndex = fCurOpListIndex+1;
235                     return true;
236                 }
237             }
238 
239             continue;
240         }
241 
242         if (GrSurfaceProxy::LazyState::kNot != cur->proxy()->lazyInstantiationState()) {
243             if (!cur->proxy()->priv().doLazyInstantiation(fResourceProvider)) {
244                 *outError = AssignError::kFailedProxyInstantiation;
245             }
246         } else if (sk_sp<GrSurface> surface = this->findSurfaceFor(cur->proxy(), needsStencil)) {
247             // TODO: make getUniqueKey virtual on GrSurfaceProxy
248             GrTextureProxy* tex = cur->proxy()->asTextureProxy();
249             if (tex && tex->getUniqueKey().isValid()) {
250                 fResourceProvider->assignUniqueKeyToResource(tex->getUniqueKey(), surface.get());
251                 SkASSERT(surface->getUniqueKey() == tex->getUniqueKey());
252             }
253 
254             cur->assign(std::move(surface));
255         } else {
256             SkASSERT(!cur->proxy()->priv().isInstantiated());
257             *outError = AssignError::kFailedProxyInstantiation;
258         }
259 
260         fActiveIntvls.insertByIncreasingEnd(cur);
261 
262         if (fResourceProvider->overBudget()) {
263             // Only force intermediate draws on opList boundaries
264             if (!fIntvlList.empty() &&
265                 fEndOfOpListOpIndices[fCurOpListIndex] < fIntvlList.peekHead()->start()) {
266                 *stopIndex = fCurOpListIndex+1;
267                 return true;
268             }
269         }
270     }
271 
272     // expire all the remaining intervals to drain the active interval list
273     this->expire(std::numeric_limits<unsigned int>::max());
274     return true;
275 }
276