Robert Phillips | 5af44de | 2017-07-18 14:49:38 -0400 | [diff] [blame] | 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 "GrSurfaceProxy.h" |
| 11 | #include "GrSurfaceProxyPriv.h" |
| 12 | |
| 13 | void GrResourceAllocator::addInterval(GrSurfaceProxy* proxy, |
| 14 | unsigned int start, unsigned int end) { |
| 15 | SkASSERT(start <= end); |
| 16 | SkASSERT(!fAssigned); // We shouldn't be adding any intervals after (or during) assignment |
| 17 | |
| 18 | if (Interval* intvl = fIntvlHash.find(proxy->uniqueID().asUInt())) { |
| 19 | // Revise the interval for an existing use |
| 20 | SkASSERT(intvl->fEnd < start); |
| 21 | intvl->fEnd = end; |
| 22 | return; |
| 23 | } |
| 24 | |
| 25 | // TODO: given the usage pattern an arena allocation scheme would work well here |
| 26 | Interval* newIntvl = new Interval(proxy, start, end); |
| 27 | |
| 28 | fIntvlList.insertByIncreasingStart(newIntvl); |
| 29 | fIntvlHash.add(newIntvl); |
| 30 | } |
| 31 | |
| 32 | GrResourceAllocator::Interval* GrResourceAllocator::IntervalList::popHead() { |
| 33 | Interval* temp = fHead; |
| 34 | if (temp) { |
| 35 | fHead = temp->fNext; |
| 36 | } |
| 37 | return temp; |
| 38 | } |
| 39 | |
| 40 | // TODO: fuse this with insertByIncreasingEnd |
| 41 | void GrResourceAllocator::IntervalList::insertByIncreasingStart(Interval* intvl) { |
| 42 | if (!fHead) { |
| 43 | intvl->fNext = nullptr; |
| 44 | fHead = intvl; |
| 45 | } else if (intvl->fStart <= fHead->fStart) { |
| 46 | intvl->fNext = fHead; |
| 47 | fHead = intvl; |
| 48 | } else { |
| 49 | Interval* prev = fHead; |
| 50 | Interval* next = prev->fNext; |
| 51 | for (; next && intvl->fStart > next->fStart; prev = next, next = next->fNext) { |
| 52 | } |
| 53 | intvl->fNext = next; |
| 54 | prev->fNext = intvl; |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | // TODO: fuse this with insertByIncreasingStart |
| 59 | void GrResourceAllocator::IntervalList::insertByIncreasingEnd(Interval* intvl) { |
| 60 | if (!fHead) { |
| 61 | intvl->fNext = nullptr; |
| 62 | fHead = intvl; |
| 63 | } else if (intvl->fEnd <= fHead->fEnd) { |
| 64 | intvl->fNext = fHead; |
| 65 | fHead = intvl; |
| 66 | } else { |
| 67 | Interval* prev = fHead; |
| 68 | Interval* next = prev->fNext; |
| 69 | for (; next && intvl->fEnd > next->fEnd; prev = next, next = next->fNext) { |
| 70 | } |
| 71 | intvl->fNext = next; |
| 72 | prev->fNext = intvl; |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | // 'surface' can be reused. Add it back to the free pool. |
| 77 | void GrResourceAllocator::freeUpSurface(GrSurface* surface) { |
Robert Phillips | 57aa367 | 2017-07-21 11:38:13 -0400 | [diff] [blame] | 78 | const GrScratchKey &key = surface->resourcePriv().getScratchKey(); |
| 79 | |
| 80 | if (!key.isValid()) { |
| 81 | return; // can't do it w/o a valid scratch key |
| 82 | } |
| 83 | |
| 84 | // TODO: fix this insertion so we get a more LRU-ish behavior |
| 85 | fFreePool.insert(key, surface); |
Robert Phillips | 5af44de | 2017-07-18 14:49:38 -0400 | [diff] [blame] | 86 | } |
| 87 | |
| 88 | // First try to reuse one of the recently allocated/used GrSurfaces in the free pool. |
| 89 | // If we can't find a useable one, create a new one. |
| 90 | // TODO: handle being overbudget |
| 91 | sk_sp<GrSurface> GrResourceAllocator::findSurfaceFor(GrSurfaceProxy* proxy) { |
Robert Phillips | 57aa367 | 2017-07-21 11:38:13 -0400 | [diff] [blame] | 92 | // First look in the free pool |
| 93 | GrScratchKey key; |
Robert Phillips | 5af44de | 2017-07-18 14:49:38 -0400 | [diff] [blame] | 94 | |
Robert Phillips | 57aa367 | 2017-07-21 11:38:13 -0400 | [diff] [blame] | 95 | proxy->priv().computeScratchKey(&key); |
| 96 | |
| 97 | GrSurface* surface = fFreePool.find(key); |
| 98 | if (surface) { |
| 99 | return sk_ref_sp(surface); |
| 100 | } |
| 101 | |
| 102 | // Failing that, try to grab a new one from the resource cache |
Robert Phillips | 5af44de | 2017-07-18 14:49:38 -0400 | [diff] [blame] | 103 | return proxy->priv().createSurface(fResourceProvider); |
| 104 | } |
| 105 | |
| 106 | // Remove any intervals that end before the current index. Return their GrSurfaces |
| 107 | // to the free pool. |
| 108 | void GrResourceAllocator::expire(unsigned int curIndex) { |
| 109 | while (!fActiveIntvls.empty() && fActiveIntvls.peekHead()->fEnd < curIndex) { |
| 110 | Interval* temp = fActiveIntvls.popHead(); |
| 111 | this->freeUpSurface(temp->fProxy->priv().peekSurface()); |
| 112 | delete temp; |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | void GrResourceAllocator::assign() { |
| 117 | fIntvlHash.reset(); // we don't need this anymore |
| 118 | SkDEBUGCODE(fAssigned = true;) |
| 119 | |
| 120 | while (Interval* cur = fIntvlList.popHead()) { |
| 121 | this->expire(cur->fStart); |
Robert Phillips | 57aa367 | 2017-07-21 11:38:13 -0400 | [diff] [blame] | 122 | |
| 123 | if (cur->fProxy->priv().isInstantiated()) { |
| 124 | fActiveIntvls.insertByIncreasingEnd(cur); |
| 125 | continue; |
| 126 | } |
| 127 | |
Robert Phillips | 5af44de | 2017-07-18 14:49:38 -0400 | [diff] [blame] | 128 | // TODO: add over budget handling here? |
| 129 | sk_sp<GrSurface> surface = this->findSurfaceFor(cur->fProxy); |
| 130 | if (surface) { |
| 131 | cur->fProxy->priv().assign(std::move(surface)); |
| 132 | } |
| 133 | // TODO: handle resouce allocation failure upstack |
| 134 | fActiveIntvls.insertByIncreasingEnd(cur); |
| 135 | } |
| 136 | } |