Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2010 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 "GrRenderTargetOpList.h"
      9 #include "GrAuditTrail.h"
     10 #include "GrCaps.h"
     11 #include "GrGpu.h"
     12 #include "GrGpuCommandBuffer.h"
     13 #include "GrRenderTarget.h"
     14 #include "GrRenderTargetContext.h"
     15 #include "GrResourceProvider.h"
     16 #include "ops/GrClearOp.h"
     17 #include "ops/GrClearStencilClipOp.h"
     18 #include "ops/GrCopySurfaceOp.h"
     19 #include "ops/GrDiscardOp.h"
     20 #include "instanced/InstancedRendering.h"
     21 
     22 using gr_instanced::InstancedRendering;
     23 
     24 ////////////////////////////////////////////////////////////////////////////////
     25 
     26 // Experimentally we have found that most combining occurs within the first 10 comparisons.
     27 static const int kDefaultMaxOpLookback = 10;
     28 static const int kDefaultMaxOpLookahead = 10;
     29 
     30 GrRenderTargetOpList::GrRenderTargetOpList(GrRenderTargetProxy* rtp, GrGpu* gpu,
     31                                            GrResourceProvider* resourceProvider,
     32                                            GrAuditTrail* auditTrail, const Options& options)
     33         : INHERITED(rtp, auditTrail)
     34         , fGpu(SkRef(gpu))
     35         , fResourceProvider(resourceProvider)
     36         , fLastClipStackGenID(SK_InvalidUniqueID)
     37         , fClipAllocator(fClipAllocatorStorage, sizeof(fClipAllocatorStorage),
     38                          sizeof(fClipAllocatorStorage)) {
     39 
     40     fMaxOpLookback = (options.fMaxOpCombineLookback < 0) ? kDefaultMaxOpLookback
     41                                                          : options.fMaxOpCombineLookback;
     42     fMaxOpLookahead = (options.fMaxOpCombineLookahead < 0) ? kDefaultMaxOpLookahead
     43                                                            : options.fMaxOpCombineLookahead;
     44 
     45     if (GrCaps::InstancedSupport::kNone != this->caps()->instancedSupport()) {
     46         fInstancedRendering.reset(fGpu->createInstancedRendering());
     47     }
     48 }
     49 
     50 GrRenderTargetOpList::~GrRenderTargetOpList() {
     51     fGpu->unref();
     52 }
     53 
     54 ////////////////////////////////////////////////////////////////////////////////
     55 
     56 #ifdef SK_DEBUG
     57 void GrRenderTargetOpList::dump() const {
     58     INHERITED::dump();
     59 
     60     SkDebugf("ops (%d):\n", fRecordedOps.count());
     61     for (int i = 0; i < fRecordedOps.count(); ++i) {
     62         SkDebugf("*******************************\n");
     63         if (!fRecordedOps[i].fOp) {
     64             SkDebugf("%d: <combined forward>\n", i);
     65         } else {
     66             SkDebugf("%d: %s\n", i, fRecordedOps[i].fOp->name());
     67             SkString str = fRecordedOps[i].fOp->dumpInfo();
     68             SkDebugf("%s\n", str.c_str());
     69             const SkRect& bounds = fRecordedOps[i].fOp->bounds();
     70             SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", bounds.fLeft,
     71                      bounds.fTop, bounds.fRight, bounds.fBottom);
     72         }
     73     }
     74 }
     75 
     76 void GrRenderTargetOpList::validateTargetsSingleRenderTarget() const {
     77     GrRenderTarget* rt = nullptr;
     78     for (int i = 0; i < fRecordedOps.count(); ++i) {
     79         if (!fRecordedOps[i].fOp) {
     80             continue;       // combined forward
     81         }
     82 
     83         if (!rt) {
     84             rt = fRecordedOps[i].fRenderTarget.get();
     85         } else {
     86             SkASSERT(fRecordedOps[i].fRenderTarget.get() == rt);
     87         }
     88     }
     89 }
     90 #endif
     91 
     92 void GrRenderTargetOpList::prepareOps(GrOpFlushState* flushState) {
     93     // MDB TODO: add SkASSERT(this->isClosed());
     94 
     95     // Loop over the ops that haven't yet been prepared.
     96     for (int i = 0; i < fRecordedOps.count(); ++i) {
     97         if (fRecordedOps[i].fOp) {
     98             GrOpFlushState::DrawOpArgs opArgs;
     99             if (fRecordedOps[i].fRenderTarget) {
    100                 opArgs = {
    101                     fRecordedOps[i].fRenderTarget.get(),
    102                     fRecordedOps[i].fAppliedClip,
    103                     fRecordedOps[i].fDstTexture
    104                 };
    105             }
    106             flushState->setDrawOpArgs(&opArgs);
    107             fRecordedOps[i].fOp->prepare(flushState);
    108             flushState->setDrawOpArgs(nullptr);
    109         }
    110     }
    111 
    112     if (fInstancedRendering) {
    113         fInstancedRendering->beginFlush(flushState->resourceProvider());
    114     }
    115 }
    116 
    117 // TODO: this is where GrOp::renderTarget is used (which is fine since it
    118 // is at flush time). However, we need to store the RenderTargetProxy in the
    119 // Ops and instantiate them here.
    120 bool GrRenderTargetOpList::executeOps(GrOpFlushState* flushState) {
    121     if (0 == fRecordedOps.count()) {
    122         return false;
    123     }
    124     // Draw all the generated geometry.
    125     SkRandom random;
    126     const GrRenderTarget* currentRenderTarget = nullptr;
    127     std::unique_ptr<GrGpuCommandBuffer> commandBuffer;
    128     for (int i = 0; i < fRecordedOps.count(); ++i) {
    129         if (!fRecordedOps[i].fOp) {
    130             continue;
    131         }
    132         if (fRecordedOps[i].fRenderTarget.get() != currentRenderTarget) {
    133             if (commandBuffer) {
    134                 commandBuffer->end();
    135                 commandBuffer->submit();
    136                 commandBuffer.reset();
    137             }
    138             currentRenderTarget = fRecordedOps[i].fRenderTarget.get();
    139             if (currentRenderTarget) {
    140                 static const GrGpuCommandBuffer::LoadAndStoreInfo kBasicLoadStoreInfo
    141                     { GrGpuCommandBuffer::LoadOp::kLoad,GrGpuCommandBuffer::StoreOp::kStore,
    142                       GrColor_ILLEGAL };
    143                 commandBuffer.reset(fGpu->createCommandBuffer(kBasicLoadStoreInfo,   // Color
    144                                                               kBasicLoadStoreInfo)); // Stencil
    145             }
    146             flushState->setCommandBuffer(commandBuffer.get());
    147         }
    148         GrOpFlushState::DrawOpArgs opArgs;
    149         if (fRecordedOps[i].fRenderTarget) {
    150             opArgs = {
    151                 fRecordedOps[i].fRenderTarget.get(),
    152                 fRecordedOps[i].fAppliedClip,
    153                 fRecordedOps[i].fDstTexture
    154             };
    155             flushState->setDrawOpArgs(&opArgs);
    156         }
    157         fRecordedOps[i].fOp->execute(flushState);
    158         flushState->setDrawOpArgs(nullptr);
    159     }
    160     if (commandBuffer) {
    161         commandBuffer->end();
    162         commandBuffer->submit();
    163         flushState->setCommandBuffer(nullptr);
    164     }
    165 
    166     fGpu->finishOpList();
    167     return true;
    168 }
    169 
    170 void GrRenderTargetOpList::reset() {
    171     fLastFullClearOp = nullptr;
    172     fLastFullClearRenderTargetID.makeInvalid();
    173     fRecordedOps.reset();
    174     if (fInstancedRendering) {
    175         fInstancedRendering->endFlush();
    176     }
    177 }
    178 
    179 void GrRenderTargetOpList::abandonGpuResources() {
    180     if (GrCaps::InstancedSupport::kNone != this->caps()->instancedSupport()) {
    181         InstancedRendering* ir = this->instancedRendering();
    182         ir->resetGpuResources(InstancedRendering::ResetType::kAbandon);
    183     }
    184 }
    185 
    186 void GrRenderTargetOpList::freeGpuResources() {
    187     if (GrCaps::InstancedSupport::kNone != this->caps()->instancedSupport()) {
    188         InstancedRendering* ir = this->instancedRendering();
    189         ir->resetGpuResources(InstancedRendering::ResetType::kDestroy);
    190     }
    191 }
    192 
    193 void GrRenderTargetOpList::fullClear(GrRenderTargetContext* renderTargetContext, GrColor color) {
    194     GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
    195     // Currently this just inserts or updates the last clear op. However, once in MDB this can
    196     // remove all the previously recorded ops and change the load op to clear with supplied
    197     // color.
    198     // TODO: this needs to be updated to use GrSurfaceProxy::UniqueID
    199     if (fLastFullClearRenderTargetID == renderTarget->uniqueID()) {
    200         // As currently implemented, fLastFullClearOp should be the last op because we would
    201         // have cleared it when another op was recorded.
    202         SkASSERT(fRecordedOps.back().fOp.get() == fLastFullClearOp);
    203         fLastFullClearOp->setColor(color);
    204         return;
    205     }
    206     std::unique_ptr<GrClearOp> op(GrClearOp::Make(GrFixedClip::Disabled(), color, renderTarget));
    207     if (GrOp* clearOp = this->recordOp(std::move(op), renderTargetContext)) {
    208         // This is either the clear op we just created or another one that it combined with.
    209         fLastFullClearOp = static_cast<GrClearOp*>(clearOp);
    210         fLastFullClearRenderTargetID = renderTarget->uniqueID();
    211     }
    212 }
    213 
    214 void GrRenderTargetOpList::discard(GrRenderTargetContext* renderTargetContext) {
    215     // Currently this just inserts a discard op. However, once in MDB this can remove all the
    216     // previously recorded ops and change the load op to discard.
    217     if (this->caps()->discardRenderTargetSupport()) {
    218         this->recordOp(GrDiscardOp::Make(renderTargetContext->accessRenderTarget()),
    219                        renderTargetContext);
    220     }
    221 }
    222 
    223 ////////////////////////////////////////////////////////////////////////////////
    224 
    225 bool GrRenderTargetOpList::copySurface(GrSurface* dst,
    226                                        GrSurface* src,
    227                                        const SkIRect& srcRect,
    228                                        const SkIPoint& dstPoint) {
    229     std::unique_ptr<GrOp> op = GrCopySurfaceOp::Make(dst, src, srcRect, dstPoint);
    230     if (!op) {
    231         return false;
    232     }
    233 #ifdef ENABLE_MDB
    234     this->addDependency(src);
    235 #endif
    236 
    237     // Copy surface doesn't work through a GrGpuCommandBuffer. By passing nullptr for the context we
    238     // force this to occur between command buffers and execute directly on GrGpu. This workaround
    239     // goes away with MDB.
    240     this->recordOp(std::move(op), nullptr);
    241     return true;
    242 }
    243 
    244 static inline bool can_reorder(const SkRect& a, const SkRect& b) {
    245     return a.fRight <= b.fLeft || a.fBottom <= b.fTop ||
    246            b.fRight <= a.fLeft || b.fBottom <= a.fTop;
    247 }
    248 
    249 bool GrRenderTargetOpList::combineIfPossible(const RecordedOp& a, GrOp* b,
    250                                              const GrAppliedClip* bClip,
    251                                              const DstTexture* bDstTexture) {
    252     if (a.fAppliedClip) {
    253         if (!bClip) {
    254             return false;
    255         }
    256         if (*a.fAppliedClip != *bClip) {
    257             return false;
    258         }
    259     } else if (bClip) {
    260         return false;
    261     }
    262     if (bDstTexture) {
    263         if (a.fDstTexture != *bDstTexture) {
    264             return false;
    265         }
    266     } else if (a.fDstTexture.texture()) {
    267         return false;
    268     }
    269     return a.fOp->combineIfPossible(b, *this->caps());
    270 }
    271 
    272 GrOp* GrRenderTargetOpList::recordOp(std::unique_ptr<GrOp> op,
    273                                      GrRenderTargetContext* renderTargetContext,
    274                                      GrAppliedClip* clip,
    275                                      const DstTexture* dstTexture) {
    276     GrRenderTarget* renderTarget =
    277             renderTargetContext ? renderTargetContext->accessRenderTarget()
    278                                 : nullptr;
    279 
    280     // A closed GrOpList should never receive new/more ops
    281     SkASSERT(!this->isClosed());
    282 
    283     // Check if there is an op we can combine with by linearly searching back until we either
    284     // 1) check every op
    285     // 2) intersect with something
    286     // 3) find a 'blocker'
    287     GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), renderTarget->uniqueID());
    288     GrOP_INFO("Recording (%s, B%u)\n"
    289               "\tBounds LRTB (%f, %f, %f, %f)\n",
    290                op->name(),
    291                op->uniqueID(),
    292                op->bounds().fLeft, op->bounds().fRight,
    293                op->bounds().fTop, op->bounds().fBottom);
    294     GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str());
    295     GrOP_INFO("\tClipped Bounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", op->bounds().fLeft,
    296               op->bounds().fTop, op->bounds().fRight, op->bounds().fBottom);
    297     GrOP_INFO("\tOutcome:\n");
    298     int maxCandidates = SkTMin(fMaxOpLookback, fRecordedOps.count());
    299     // If we don't have a valid destination render target then we cannot reorder.
    300     if (maxCandidates && renderTarget) {
    301         int i = 0;
    302         while (true) {
    303             const RecordedOp& candidate = fRecordedOps.fromBack(i);
    304             // We cannot continue to search backwards if the render target changes
    305             if (candidate.fRenderTarget.get() != renderTarget) {
    306                 GrOP_INFO("\t\tBreaking because of (%s, B%u) Rendertarget\n", candidate.fOp->name(),
    307                           candidate.fOp->uniqueID());
    308                 break;
    309             }
    310             if (this->combineIfPossible(candidate, op.get(), clip, dstTexture)) {
    311                 GrOP_INFO("\t\tCombining with (%s, B%u)\n", candidate.fOp->name(),
    312                           candidate.fOp->uniqueID());
    313                 GrOP_INFO("\t\t\tCombined op info:\n");
    314                 GrOP_INFO(SkTabString(candidate.fOp->dumpInfo(), 4).c_str());
    315                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, candidate.fOp.get(), op.get());
    316                 return candidate.fOp.get();
    317             }
    318             // Stop going backwards if we would cause a painter's order violation.
    319             if (!can_reorder(fRecordedOps.fromBack(i).fOp->bounds(), op->bounds())) {
    320                 GrOP_INFO("\t\tIntersects with (%s, B%u)\n", candidate.fOp->name(),
    321                           candidate.fOp->uniqueID());
    322                 break;
    323             }
    324             ++i;
    325             if (i == maxCandidates) {
    326                 GrOP_INFO("\t\tReached max lookback or beginning of op array %d\n", i);
    327                 break;
    328             }
    329         }
    330     } else {
    331         GrOP_INFO("\t\tFirstOp\n");
    332     }
    333     GR_AUDIT_TRAIL_OP_RESULT_NEW(fAuditTrail, op);
    334     if (clip) {
    335         clip = fClipAllocator.make<GrAppliedClip>(std::move(*clip));
    336     }
    337     fRecordedOps.emplace_back(std::move(op), renderTarget, clip, dstTexture);
    338     fRecordedOps.back().fOp->wasRecorded();
    339     fLastFullClearOp = nullptr;
    340     fLastFullClearRenderTargetID.makeInvalid();
    341     return fRecordedOps.back().fOp.get();
    342 }
    343 
    344 void GrRenderTargetOpList::forwardCombine() {
    345     if (fMaxOpLookahead <= 0) {
    346         return;
    347     }
    348     for (int i = 0; i < fRecordedOps.count() - 1; ++i) {
    349         GrOp* op = fRecordedOps[i].fOp.get();
    350         GrRenderTarget* renderTarget = fRecordedOps[i].fRenderTarget.get();
    351         // If we don't have a valid destination render target ID then we cannot reorder.
    352         if (!renderTarget) {
    353             continue;
    354         }
    355         int maxCandidateIdx = SkTMin(i + fMaxOpLookahead, fRecordedOps.count() - 1);
    356         int j = i + 1;
    357         while (true) {
    358             const RecordedOp& candidate = fRecordedOps[j];
    359             // We cannot continue to search if the render target changes
    360             if (candidate.fRenderTarget.get() != renderTarget) {
    361                 GrOP_INFO("\t\tBreaking because of (%s, B%u) Rendertarget\n", candidate.fOp->name(),
    362                           candidate.fOp->uniqueID());
    363                 break;
    364             }
    365             if (this->combineIfPossible(fRecordedOps[i], candidate.fOp.get(),
    366                                         candidate.fAppliedClip, &candidate.fDstTexture)) {
    367                 GrOP_INFO("\t\tCombining with (%s, B%u)\n", candidate.fOp->name(),
    368                           candidate.fOp->uniqueID());
    369                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, op, candidate.fOp.get());
    370                 fRecordedOps[j].fOp = std::move(fRecordedOps[i].fOp);
    371                 break;
    372             }
    373             // Stop going traversing if we would cause a painter's order violation.
    374             if (!can_reorder(fRecordedOps[j].fOp->bounds(), op->bounds())) {
    375                 GrOP_INFO("\t\tIntersects with (%s, B%u)\n", candidate.fOp->name(),
    376                           candidate.fOp->uniqueID());
    377                 break;
    378             }
    379             ++j;
    380             if (j > maxCandidateIdx) {
    381                 GrOP_INFO("\t\tReached max lookahead or end of op array %d\n", i);
    382                 break;
    383             }
    384         }
    385     }
    386 }
    387 
    388 ///////////////////////////////////////////////////////////////////////////////
    389 
    390 void GrRenderTargetOpList::clearStencilClip(const GrFixedClip& clip,
    391                                             bool insideStencilMask,
    392                                             GrRenderTargetContext* renderTargetContext) {
    393     this->recordOp(GrClearStencilClipOp::Make(clip, insideStencilMask,
    394                                               renderTargetContext->accessRenderTarget()),
    395                    renderTargetContext);
    396 }
    397