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