| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "GrResourceAllocator.h" |
| |
| #include "GrSurfaceProxy.h" |
| #include "GrSurfaceProxyPriv.h" |
| |
| void GrResourceAllocator::addInterval(GrSurfaceProxy* proxy, |
| unsigned int start, unsigned int end) { |
| SkASSERT(start <= end); |
| SkASSERT(!fAssigned); // We shouldn't be adding any intervals after (or during) assignment |
| |
| if (Interval* intvl = fIntvlHash.find(proxy->uniqueID().asUInt())) { |
| // Revise the interval for an existing use |
| SkASSERT(intvl->fEnd < start); |
| intvl->fEnd = end; |
| return; |
| } |
| |
| // TODO: given the usage pattern an arena allocation scheme would work well here |
| Interval* newIntvl = new Interval(proxy, start, end); |
| |
| fIntvlList.insertByIncreasingStart(newIntvl); |
| fIntvlHash.add(newIntvl); |
| } |
| |
| GrResourceAllocator::Interval* GrResourceAllocator::IntervalList::popHead() { |
| Interval* temp = fHead; |
| if (temp) { |
| fHead = temp->fNext; |
| } |
| return temp; |
| } |
| |
| // TODO: fuse this with insertByIncreasingEnd |
| void GrResourceAllocator::IntervalList::insertByIncreasingStart(Interval* intvl) { |
| if (!fHead) { |
| intvl->fNext = nullptr; |
| fHead = intvl; |
| } else if (intvl->fStart <= fHead->fStart) { |
| intvl->fNext = fHead; |
| fHead = intvl; |
| } else { |
| Interval* prev = fHead; |
| Interval* next = prev->fNext; |
| for (; next && intvl->fStart > next->fStart; prev = next, next = next->fNext) { |
| } |
| intvl->fNext = next; |
| prev->fNext = intvl; |
| } |
| } |
| |
| // TODO: fuse this with insertByIncreasingStart |
| void GrResourceAllocator::IntervalList::insertByIncreasingEnd(Interval* intvl) { |
| if (!fHead) { |
| intvl->fNext = nullptr; |
| fHead = intvl; |
| } else if (intvl->fEnd <= fHead->fEnd) { |
| intvl->fNext = fHead; |
| fHead = intvl; |
| } else { |
| Interval* prev = fHead; |
| Interval* next = prev->fNext; |
| for (; next && intvl->fEnd > next->fEnd; prev = next, next = next->fNext) { |
| } |
| intvl->fNext = next; |
| prev->fNext = intvl; |
| } |
| } |
| |
| // 'surface' can be reused. Add it back to the free pool. |
| void GrResourceAllocator::freeUpSurface(GrSurface* surface) { |
| const GrScratchKey &key = surface->resourcePriv().getScratchKey(); |
| |
| if (!key.isValid()) { |
| return; // can't do it w/o a valid scratch key |
| } |
| |
| // TODO: fix this insertion so we get a more LRU-ish behavior |
| fFreePool.insert(key, surface); |
| } |
| |
| // First try to reuse one of the recently allocated/used GrSurfaces in the free pool. |
| // If we can't find a useable one, create a new one. |
| // TODO: handle being overbudget |
| sk_sp<GrSurface> GrResourceAllocator::findSurfaceFor(GrSurfaceProxy* proxy) { |
| // First look in the free pool |
| GrScratchKey key; |
| |
| proxy->priv().computeScratchKey(&key); |
| |
| GrSurface* surface = fFreePool.find(key); |
| if (surface) { |
| return sk_ref_sp(surface); |
| } |
| |
| // Failing that, try to grab a new one from the resource cache |
| return proxy->priv().createSurface(fResourceProvider); |
| } |
| |
| // Remove any intervals that end before the current index. Return their GrSurfaces |
| // to the free pool. |
| void GrResourceAllocator::expire(unsigned int curIndex) { |
| while (!fActiveIntvls.empty() && fActiveIntvls.peekHead()->fEnd < curIndex) { |
| Interval* temp = fActiveIntvls.popHead(); |
| this->freeUpSurface(temp->fProxy->priv().peekSurface()); |
| delete temp; |
| } |
| } |
| |
| void GrResourceAllocator::assign() { |
| fIntvlHash.reset(); // we don't need this anymore |
| SkDEBUGCODE(fAssigned = true;) |
| |
| while (Interval* cur = fIntvlList.popHead()) { |
| this->expire(cur->fStart); |
| |
| if (cur->fProxy->priv().isInstantiated()) { |
| fActiveIntvls.insertByIncreasingEnd(cur); |
| continue; |
| } |
| |
| // TODO: add over budget handling here? |
| sk_sp<GrSurface> surface = this->findSurfaceFor(cur->fProxy); |
| if (surface) { |
| cur->fProxy->priv().assign(std::move(surface)); |
| } |
| // TODO: handle resouce allocation failure upstack |
| fActiveIntvls.insertByIncreasingEnd(cur); |
| } |
| } |
| |
| #ifdef SK_DEBUG |
| void GrResourceAllocator::dump() { |
| unsigned int min = fNumOps+1; |
| unsigned int max = 0; |
| for(const Interval* cur = fIntvlList.peekHead(); cur; cur = cur->fNext) { |
| SkDebugf("{ %d,%d }: [%d, %d]\n", |
| cur->fProxy->uniqueID().asUInt(), cur->fProxy->underlyingUniqueID().asUInt(), |
| cur->fStart, cur->fEnd); |
| if (min > cur->fStart) { |
| min = cur->fStart; |
| } |
| if (max < cur->fEnd) { |
| max = cur->fEnd; |
| } |
| } |
| |
| for(const Interval* cur = fIntvlList.peekHead(); cur; cur = cur->fNext) { |
| SkDebugf("{ %3d,%3d }: ", |
| cur->fProxy->uniqueID().asUInt(), cur->fProxy->underlyingUniqueID().asUInt()); |
| for (unsigned int i = min; i <= max; ++i) { |
| if (i >= cur->fStart && i <= cur->fEnd) { |
| SkDebugf("x"); |
| } else { |
| SkDebugf(" "); |
| } |
| } |
| SkDebugf("\n"); |
| } |
| } |
| #endif |
| |