• 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 "src/gpu/ganesh/GrResourceAllocator.h"
9 
10 #include "src/gpu/ganesh/GrCaps.h"
11 #include "src/gpu/ganesh/GrDirectContextPriv.h"
12 #include "src/gpu/ganesh/GrGpuResourcePriv.h"
13 #include "src/gpu/ganesh/GrRenderTargetProxy.h"
14 #include "src/gpu/ganesh/GrResourceProvider.h"
15 #include "src/gpu/ganesh/GrSurfaceProxy.h"
16 #include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
17 #include "src/gpu/ganesh/GrTexture.h"
18 
19 #ifdef SK_DEBUG
20 #include <atomic>
21 
CreateUniqueID()22 uint32_t GrResourceAllocator::Interval::CreateUniqueID() {
23     static std::atomic<uint32_t> nextID{1};
24     uint32_t id;
25     do {
26         id = nextID.fetch_add(1, std::memory_order_relaxed);
27     } while (id == SK_InvalidUniqueID);
28     return id;
29 }
30 
CreateUniqueID()31 uint32_t GrResourceAllocator::Register::CreateUniqueID() {
32     static std::atomic<uint32_t> nextID{1};
33     uint32_t id;
34     do {
35         id = nextID.fetch_add(1, std::memory_order_relaxed);
36     } while (id == SK_InvalidUniqueID);
37     return id;
38 }
39 #endif
40 
~GrResourceAllocator()41 GrResourceAllocator::~GrResourceAllocator() {
42     SkASSERT(fFailedInstantiation || fIntvlList.empty());
43     SkASSERT(fActiveIntvls.empty());
44     SkASSERT(!fIntvlHash.count());
45 }
46 
47 void GrResourceAllocator::addInterval(GrSurfaceProxy* proxy, unsigned int start, unsigned int end,
48                                       ActualUse actualUse, AllowRecycling allowRecycling
49                                       SkDEBUGCODE(, bool isDirectDstRead)) {
50     SkASSERT(start <= end);
51     SkASSERT(!fAssigned);  // We shouldn't be adding any intervals after (or during) assignment
52 
53     if (proxy->canSkipResourceAllocator()) {
54         return;
55     }
56 
57     // If a proxy is read only it must refer to a texture with specific content that cannot be
58     // recycled. We don't need to assign a texture to it and no other proxy can be instantiated
59     // with the same texture.
60     if (proxy->readOnly()) {
61         auto resourceProvider = fDContext->priv().resourceProvider();
62         if (proxy->isLazy() && !proxy->priv().doLazyInstantiation(resourceProvider)) {
63             fFailedInstantiation = true;
64         } else {
65             // Since we aren't going to add an interval we won't revisit this proxy in assign(). So
66             // must already be instantiated or it must be a lazy proxy that we instantiated above.
67             SkASSERT(proxy->isInstantiated());
68         }
69         return;
70     }
71     uint32_t proxyID = proxy->uniqueID().asUInt();
72     if (Interval** intvlPtr = fIntvlHash.find(proxyID)) {
73         // Revise the interval for an existing use
74         Interval* intvl = *intvlPtr;
75 #ifdef SK_DEBUG
76         if (0 == start && 0 == end) {
77             // This interval is for the initial upload to a deferred proxy. Due to the vagaries
78             // of how deferred proxies are collected they can appear as uploads multiple times
79             // in a single opsTasks' list and as uploads in several opsTasks.
80             SkASSERT(0 == intvl->start());
81         } else if (isDirectDstRead) {
82             // Direct reads from the render target itself should occur w/in the existing
83             // interval
84             SkASSERT(intvl->start() <= start && intvl->end() >= end);
85         } else {
86             SkASSERT(intvl->end() <= start && intvl->end() <= end);
87         }
88 #endif
89         if (ActualUse::kYes == actualUse) {
90             intvl->addUse();
91         }
92         if (allowRecycling == AllowRecycling::kNo) {
93             // In this case, a preexisting interval is made non-reuseable since its proxy is sampled
94             // into a secondary command buffer.
95             intvl->disallowRecycling();
96         }
97         intvl->extendEnd(end);
98         return;
99     }
100     Interval* newIntvl = fInternalAllocator.make<Interval>(proxy, start, end);
101 
102     if (ActualUse::kYes == actualUse) {
103         newIntvl->addUse();
104     }
105     if (allowRecycling == AllowRecycling::kNo) {
106         newIntvl->disallowRecycling();
107     }
108     fIntvlList.insertByIncreasingStart(newIntvl);
109     fIntvlHash.set(proxyID, newIntvl);
110 }
111 
112 // Tragically we have cases where we always have to make new textures.
can_proxy_use_scratch(const GrCaps & caps,GrSurfaceProxy * proxy)113 static bool can_proxy_use_scratch(const GrCaps& caps, GrSurfaceProxy* proxy) {
114     return caps.reuseScratchTextures() || proxy->asRenderTargetProxy();
115 }
116 
Register(GrSurfaceProxy * originatingProxy,skgpu::ScratchKey scratchKey,GrResourceProvider * provider)117 GrResourceAllocator::Register::Register(GrSurfaceProxy* originatingProxy,
118                                         skgpu::ScratchKey scratchKey,
119                                         GrResourceProvider* provider)
120         : fOriginatingProxy(originatingProxy)
121         , fScratchKey(std::move(scratchKey)) {
122     SkASSERT(originatingProxy);
123     SkASSERT(!originatingProxy->isInstantiated());
124     SkASSERT(!originatingProxy->isLazy());
125     SkDEBUGCODE(fUniqueID = CreateUniqueID();)
126     if (fScratchKey.isValid()) {
127         if (can_proxy_use_scratch(*provider->caps(), originatingProxy)) {
128             fExistingSurface = provider->findAndRefScratchTexture(
129                     fScratchKey, /*label=*/"ResourceAllocatorRegister");
130         }
131     } else {
132         SkASSERT(this->uniqueKey().isValid());
133         fExistingSurface = provider->findByUniqueKey<GrSurface>(this->uniqueKey());
134     }
135 }
136 
isRecyclable(const GrCaps & caps,GrSurfaceProxy * proxy,int knownUseCount,AllowRecycling allowRecycling) const137 bool GrResourceAllocator::Register::isRecyclable(const GrCaps& caps,
138                                                  GrSurfaceProxy* proxy,
139                                                  int knownUseCount,
140                                                  AllowRecycling allowRecycling) const {
141     if (allowRecycling == AllowRecycling::kNo) {
142         return false;
143     }
144 
145     if (!can_proxy_use_scratch(caps, proxy)) {
146         return false;
147     }
148 
149     if (!this->scratchKey().isValid()) {
150         return false; // no scratch key, no free pool
151     }
152     if (this->uniqueKey().isValid()) {
153         return false; // rely on the resource cache to hold onto uniquely-keyed surfaces.
154     }
155     // If all the refs on the proxy are known to the resource allocator then no one
156     // should be holding onto it outside of Ganesh.
157     return !proxy->refCntGreaterThan(knownUseCount);
158 }
159 
instantiateSurface(GrSurfaceProxy * proxy,GrResourceProvider * resourceProvider)160 bool GrResourceAllocator::Register::instantiateSurface(GrSurfaceProxy* proxy,
161                                                        GrResourceProvider* resourceProvider) {
162     SkASSERT(!proxy->peekSurface());
163 
164     sk_sp<GrSurface> newSurface;
165     if (!fExistingSurface) {
166         if (proxy == fOriginatingProxy) {
167             newSurface = proxy->priv().createSurface(resourceProvider);
168         } else {
169             newSurface = sk_ref_sp(fOriginatingProxy->peekSurface());
170         }
171     }
172     if (!fExistingSurface && !newSurface) {
173         return false;
174     }
175 
176     GrSurface* surface = newSurface ? newSurface.get() : fExistingSurface.get();
177     // Make surface budgeted if this proxy is budgeted.
178     if (skgpu::Budgeted::kYes == proxy->isBudgeted() &&
179         GrBudgetedType::kBudgeted != surface->resourcePriv().budgetedType()) {
180         // This gets the job done but isn't quite correct. It would be better to try to
181         // match budgeted proxies w/ budgeted surfaces and unbudgeted w/ unbudgeted.
182         surface->resourcePriv().makeBudgeted();
183     }
184 
185     // Propagate the proxy unique key to the surface if we have one.
186     if (const auto& uniqueKey = proxy->getUniqueKey(); uniqueKey.isValid()) {
187         if (!surface->getUniqueKey().isValid()) {
188             resourceProvider->assignUniqueKeyToResource(uniqueKey, surface);
189         }
190         SkASSERT(surface->getUniqueKey() == uniqueKey);
191     }
192     proxy->priv().assign(fExistingSurface ? fExistingSurface : std::move(newSurface));
193     return true;
194 }
195 
popHead()196 GrResourceAllocator::Interval* GrResourceAllocator::IntervalList::popHead() {
197     SkDEBUGCODE(this->validate());
198 
199     Interval* temp = fHead;
200     if (temp) {
201         fHead = temp->next();
202         if (!fHead) {
203             fTail = nullptr;
204         }
205         temp->setNext(nullptr);
206     }
207 
208     SkDEBUGCODE(this->validate());
209     return temp;
210 }
211 
212 // TODO: fuse this with insertByIncreasingEnd
insertByIncreasingStart(Interval * intvl)213 void GrResourceAllocator::IntervalList::insertByIncreasingStart(Interval* intvl) {
214     SkDEBUGCODE(this->validate());
215     SkASSERT(!intvl->next());
216 
217     if (!fHead) {
218         // 14%
219         fHead = fTail = intvl;
220     } else if (intvl->start() <= fHead->start()) {
221         // 3%
222         intvl->setNext(fHead);
223         fHead = intvl;
224     } else if (fTail->start() <= intvl->start()) {
225         // 83%
226         fTail->setNext(intvl);
227         fTail = intvl;
228     } else {
229         // almost never
230         Interval* prev = fHead;
231         Interval* next = prev->next();
232         for (; intvl->start() > next->start(); prev = next, next = next->next()) {
233         }
234 
235         SkASSERT(next);
236         intvl->setNext(next);
237         prev->setNext(intvl);
238     }
239 
240     SkDEBUGCODE(this->validate());
241 }
242 
243 // TODO: fuse this with insertByIncreasingStart
insertByIncreasingEnd(Interval * intvl)244 void GrResourceAllocator::IntervalList::insertByIncreasingEnd(Interval* intvl) {
245     SkDEBUGCODE(this->validate());
246     SkASSERT(!intvl->next());
247 
248     if (!fHead) {
249         // 14%
250         fHead = fTail = intvl;
251     } else if (intvl->end() <= fHead->end()) {
252         // 64%
253         intvl->setNext(fHead);
254         fHead = intvl;
255     } else if (fTail->end() <= intvl->end()) {
256         // 3%
257         fTail->setNext(intvl);
258         fTail = intvl;
259     } else {
260         // 19% but 81% of those land right after the list's head
261         Interval* prev = fHead;
262         Interval* next = prev->next();
263         for (; intvl->end() > next->end(); prev = next, next = next->next()) {
264         }
265 
266         SkASSERT(next);
267         intvl->setNext(next);
268         prev->setNext(intvl);
269     }
270 
271     SkDEBUGCODE(this->validate());
272 }
273 
274 #ifdef SK_DEBUG
validate() const275 void GrResourceAllocator::IntervalList::validate() const {
276     SkASSERT(SkToBool(fHead) == SkToBool(fTail));
277 
278     Interval* prev = nullptr;
279     for (Interval* cur = fHead; cur; prev = cur, cur = cur->next()) {
280     }
281 
282     SkASSERT(fTail == prev);
283 }
284 #endif
285 
286 // First try to reuse one of the recently allocated/used registers in the free pool.
findOrCreateRegisterFor(GrSurfaceProxy * proxy)287 GrResourceAllocator::Register* GrResourceAllocator::findOrCreateRegisterFor(GrSurfaceProxy* proxy) {
288     auto resourceProvider = fDContext->priv().resourceProvider();
289     // Handle uniquely keyed proxies
290     if (const auto& uniqueKey = proxy->getUniqueKey(); uniqueKey.isValid()) {
291         if (auto p = fUniqueKeyRegisters.find(uniqueKey)) {
292             return *p;
293         }
294         // No need for a scratch key. These don't go in the free pool.
295         Register* r = fInternalAllocator.make<Register>(proxy,
296                                                         skgpu::ScratchKey(),
297                                                         resourceProvider);
298         fUniqueKeyRegisters.set(uniqueKey, r);
299         return r;
300     }
301 
302     // Then look in the free pool
303     skgpu::ScratchKey scratchKey;
304     proxy->priv().computeScratchKey(*fDContext->priv().caps(), &scratchKey);
305 
306     auto filter = [] (const Register* r) {
307         return true;
308     };
309     if (Register* r = fFreePool.findAndRemove(scratchKey, filter)) {
310         return r;
311     }
312 
313     return fInternalAllocator.make<Register>(proxy, std::move(scratchKey), resourceProvider);
314 }
315 
316 // Remove any intervals that end before the current index. Add their registers
317 // to the free pool if possible.
expire(unsigned int curIndex)318 void GrResourceAllocator::expire(unsigned int curIndex) {
319     while (!fActiveIntvls.empty() && fActiveIntvls.peekHead()->end() < curIndex) {
320         Interval* intvl = fActiveIntvls.popHead();
321         SkASSERT(!intvl->next());
322 
323         Register* r = intvl->getRegister();
324         if (r && r->isRecyclable(*fDContext->priv().caps(), intvl->proxy(), intvl->uses(),
325                                  intvl->allowRecycling())) {
326 #if GR_ALLOCATION_SPEW
327             SkDebugf("putting register %d back into pool\n", r->uniqueID());
328 #endif
329             // TODO: fix this insertion so we get a more LRU-ish behavior
330             fFreePool.insert(r->scratchKey(), r);
331         }
332         fFinishedIntvls.insertByIncreasingStart(intvl);
333     }
334 }
335 
planAssignment()336 bool GrResourceAllocator::planAssignment() {
337     fIntvlHash.reset(); // we don't need the interval hash anymore
338 
339     SkASSERT(!fPlanned && !fAssigned);
340     SkDEBUGCODE(fPlanned = true;)
341 
342 #if GR_ALLOCATION_SPEW
343     SkDebugf("assigning %d ops\n", fNumOps);
344     this->dumpIntervals();
345 #endif
346 
347     auto resourceProvider = fDContext->priv().resourceProvider();
348     while (Interval* cur = fIntvlList.popHead()) {
349         this->expire(cur->start());
350         fActiveIntvls.insertByIncreasingEnd(cur);
351 
352         // Already-instantiated proxies and lazy proxies don't use registers.
353         if (cur->proxy()->isInstantiated()) {
354             continue;
355         }
356 
357         // Instantiate fully-lazy proxies immediately. Ignore other lazy proxies at this stage.
358         if (cur->proxy()->isLazy()) {
359             if (cur->proxy()->isFullyLazy()) {
360                 fFailedInstantiation = !cur->proxy()->priv().doLazyInstantiation(resourceProvider);
361                 if (fFailedInstantiation) {
362                     break;
363                 }
364             }
365             continue;
366         }
367 
368         Register* r = this->findOrCreateRegisterFor(cur->proxy());
369 #if GR_ALLOCATION_SPEW
370         SkDebugf("Assigning register %d to %d\n",
371              r->uniqueID(),
372              cur->proxy()->uniqueID().asUInt());
373 #endif
374         SkASSERT(!cur->proxy()->peekSurface());
375         cur->setRegister(r);
376     }
377 
378     // expire all the remaining intervals to drain the active interval list
379     this->expire(std::numeric_limits<unsigned int>::max());
380     return !fFailedInstantiation;
381 }
382 
makeBudgetHeadroom()383 bool GrResourceAllocator::makeBudgetHeadroom() {
384     SkASSERT(fPlanned);
385     SkASSERT(!fFailedInstantiation);
386     size_t additionalBytesNeeded = 0;
387     for (Interval* cur = fFinishedIntvls.peekHead(); cur; cur = cur->next()) {
388         GrSurfaceProxy* proxy = cur->proxy();
389         if (skgpu::Budgeted::kNo == proxy->isBudgeted() || proxy->isInstantiated()) {
390             continue;
391         }
392 
393         // N.B Fully-lazy proxies were already instantiated in planAssignment
394         if (proxy->isLazy()) {
395             additionalBytesNeeded += proxy->gpuMemorySize();
396         } else {
397             Register* r = cur->getRegister();
398             SkASSERT(r);
399             if (!r->accountedForInBudget() && !r->existingSurface()) {
400                 additionalBytesNeeded += proxy->gpuMemorySize();
401             }
402             r->setAccountedForInBudget();
403         }
404     }
405     return fDContext->priv().getResourceCache()->purgeToMakeHeadroom(additionalBytesNeeded);
406 }
407 
reset()408 void GrResourceAllocator::reset() {
409     // NOTE: We do not reset the failedInstantiation flag because we currently do not attempt
410     // to recover from failed instantiations. The user is responsible for checking this flag and
411     // bailing early.
412     SkDEBUGCODE(fPlanned = false;)
413     SkDEBUGCODE(fAssigned = false;)
414     SkASSERT(fActiveIntvls.empty());
415     fFinishedIntvls = IntervalList();
416     fIntvlList = IntervalList();
417     fIntvlHash.reset();
418     fUniqueKeyRegisters.reset();
419     fFreePool.reset();
420     fInternalAllocator.reset();
421 }
422 
assign()423 bool GrResourceAllocator::assign() {
424     if (fFailedInstantiation) {
425         return false;
426     }
427     SkASSERT(fPlanned && !fAssigned);
428     SkDEBUGCODE(fAssigned = true;)
429     auto resourceProvider = fDContext->priv().resourceProvider();
430     while (Interval* cur = fFinishedIntvls.popHead()) {
431         if (fFailedInstantiation) {
432             break;
433         }
434         if (cur->proxy()->isInstantiated()) {
435             continue;
436         }
437         if (cur->proxy()->isLazy()) {
438             fFailedInstantiation = !cur->proxy()->priv().doLazyInstantiation(resourceProvider);
439             continue;
440         }
441         Register* r = cur->getRegister();
442         SkASSERT(r);
443         fFailedInstantiation = !r->instantiateSurface(cur->proxy(), resourceProvider);
444     }
445     return !fFailedInstantiation;
446 }
447 
448 #if GR_ALLOCATION_SPEW
dumpIntervals()449 void GrResourceAllocator::dumpIntervals() {
450     // Print all the intervals while computing their range
451     SkDebugf("------------------------------------------------------------\n");
452     unsigned int min = std::numeric_limits<unsigned int>::max();
453     unsigned int max = 0;
454     for(const Interval* cur = fIntvlList.peekHead(); cur; cur = cur->next()) {
455         SkDebugf("{ %3d,%3d }: [%2d, %2d] - refProxys:%d surfaceRefs:%d\n",
456                  cur->proxy()->uniqueID().asUInt(),
457                  cur->proxy()->isInstantiated() ? cur->proxy()->underlyingUniqueID().asUInt() : -1,
458                  cur->start(),
459                  cur->end(),
460                  cur->proxy()->priv().getProxyRefCnt(),
461                  cur->proxy()->testingOnly_getBackingRefCnt());
462         min = std::min(min, cur->start());
463         max = std::max(max, cur->end());
464     }
465 
466     // Draw a graph of the useage intervals
467     for(const Interval* cur = fIntvlList.peekHead(); cur; cur = cur->next()) {
468         SkDebugf("{ %3d,%3d }: ",
469                  cur->proxy()->uniqueID().asUInt(),
470                  cur->proxy()->isInstantiated() ? cur->proxy()->underlyingUniqueID().asUInt() : -1);
471         for (unsigned int i = min; i <= max; ++i) {
472             if (i >= cur->start() && i <= cur->end()) {
473                 SkDebugf("x");
474             } else {
475                 SkDebugf(" ");
476             }
477         }
478         SkDebugf("\n");
479     }
480 }
481 #endif
482