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