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 "GrRect.h"
     14 #include "GrRenderTargetContext.h"
     15 #include "GrResourceAllocator.h"
     16 #include "ops/GrClearOp.h"
     17 #include "ops/GrCopySurfaceOp.h"
     18 #include "SkTraceEvent.h"
     19 
     20 
     21 ////////////////////////////////////////////////////////////////////////////////
     22 
     23 // Experimentally we have found that most combining occurs within the first 10 comparisons.
     24 static const int kMaxOpLookback = 10;
     25 static const int kMaxOpLookahead = 10;
     26 
     27 GrRenderTargetOpList::GrRenderTargetOpList(GrRenderTargetProxy* proxy,
     28                                            GrResourceProvider* resourceProvider,
     29                                            GrAuditTrail* auditTrail)
     30         : INHERITED(resourceProvider, proxy, auditTrail)
     31         , fLastClipStackGenID(SK_InvalidUniqueID)
     32         SkDEBUGCODE(, fNumClips(0)) {
     33 }
     34 
     35 GrRenderTargetOpList::~GrRenderTargetOpList() {
     36 }
     37 
     38 ////////////////////////////////////////////////////////////////////////////////
     39 
     40 #ifdef SK_DEBUG
     41 void GrRenderTargetOpList::dump() const {
     42     INHERITED::dump();
     43 
     44     SkDebugf("ops (%d):\n", fRecordedOps.count());
     45     for (int i = 0; i < fRecordedOps.count(); ++i) {
     46         SkDebugf("*******************************\n");
     47         if (!fRecordedOps[i].fOp) {
     48             SkDebugf("%d: <combined forward or failed instantiation>\n", i);
     49         } else {
     50             SkDebugf("%d: %s\n", i, fRecordedOps[i].fOp->name());
     51             SkString str = fRecordedOps[i].fOp->dumpInfo();
     52             SkDebugf("%s\n", str.c_str());
     53             const SkRect& bounds = fRecordedOps[i].fOp->bounds();
     54             SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", bounds.fLeft,
     55                      bounds.fTop, bounds.fRight, bounds.fBottom);
     56         }
     57     }
     58 }
     59 
     60 void GrRenderTargetOpList::visitProxies_debugOnly(const GrOp::VisitProxyFunc& func) const {
     61     for (const RecordedOp& recordedOp : fRecordedOps) {
     62         recordedOp.visitProxies(func);
     63     }
     64 }
     65 #endif
     66 
     67 void GrRenderTargetOpList::onPrepare(GrOpFlushState* flushState) {
     68     SkASSERT(fTarget.get()->priv().peekRenderTarget());
     69     SkASSERT(this->isClosed());
     70 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     71     TRACE_EVENT0("skia", TRACE_FUNC);
     72 #endif
     73 
     74     // Loop over the ops that haven't yet been prepared.
     75     for (int i = 0; i < fRecordedOps.count(); ++i) {
     76         if (fRecordedOps[i].fOp) {
     77 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     78             TRACE_EVENT0("skia", fRecordedOps[i].fOp->name());
     79 #endif
     80             GrOpFlushState::OpArgs opArgs = {
     81                 fRecordedOps[i].fOp.get(),
     82                 fTarget.get()->asRenderTargetProxy(),
     83                 fRecordedOps[i].fAppliedClip,
     84                 fRecordedOps[i].fDstProxy
     85             };
     86 
     87             flushState->setOpArgs(&opArgs);
     88             fRecordedOps[i].fOp->prepare(flushState);
     89             flushState->setOpArgs(nullptr);
     90         }
     91     }
     92 }
     93 
     94 static std::unique_ptr<GrGpuRTCommandBuffer> create_command_buffer(GrGpu* gpu,
     95                                                                    GrRenderTarget* rt,
     96                                                                    GrSurfaceOrigin origin,
     97                                                                    GrLoadOp colorLoadOp,
     98                                                                    GrColor loadClearColor,
     99                                                                    GrLoadOp stencilLoadOp) {
    100     const GrGpuRTCommandBuffer::LoadAndStoreInfo kColorLoadStoreInfo {
    101         colorLoadOp,
    102         GrStoreOp::kStore,
    103         loadClearColor
    104     };
    105 
    106     // TODO:
    107     // We would like to (at this level) only ever clear & discard. We would need
    108     // to stop splitting up higher level opLists for copyOps to achieve that.
    109     // Note: we would still need SB loads and stores but they would happen at a
    110     // lower level (inside the VK command buffer).
    111     const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo stencilLoadAndStoreInfo {
    112         stencilLoadOp,
    113         GrStoreOp::kStore,
    114     };
    115 
    116     std::unique_ptr<GrGpuRTCommandBuffer> buffer(
    117                             gpu->createCommandBuffer(rt, origin,
    118                                                      kColorLoadStoreInfo,
    119                                                      stencilLoadAndStoreInfo));
    120     return buffer;
    121 }
    122 
    123 static inline void finish_command_buffer(GrGpuRTCommandBuffer* buffer) {
    124     if (!buffer) {
    125         return;
    126     }
    127 
    128     buffer->end();
    129     buffer->submit();
    130 }
    131 
    132 // TODO: this is where GrOp::renderTarget is used (which is fine since it
    133 // is at flush time). However, we need to store the RenderTargetProxy in the
    134 // Ops and instantiate them here.
    135 bool GrRenderTargetOpList::onExecute(GrOpFlushState* flushState) {
    136     if (0 == fRecordedOps.count() && GrLoadOp::kClear != fColorLoadOp) {
    137         return false;
    138     }
    139 
    140     SkASSERT(fTarget.get()->priv().peekRenderTarget());
    141 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
    142     TRACE_EVENT0("skia", TRACE_FUNC);
    143 #endif
    144 
    145     // TODO: at the very least, we want the stencil store op to always be discard (at this
    146     // level). In Vulkan, sub-command buffers would still need to load & store the stencil buffer.
    147     std::unique_ptr<GrGpuRTCommandBuffer> commandBuffer = create_command_buffer(
    148                                                     flushState->gpu(),
    149                                                     fTarget.get()->priv().peekRenderTarget(),
    150                                                     fTarget.get()->origin(),
    151                                                     fColorLoadOp, fLoadClearColor,
    152                                                     fStencilLoadOp);
    153     flushState->setCommandBuffer(commandBuffer.get());
    154     commandBuffer->begin();
    155 
    156     // Draw all the generated geometry.
    157     for (int i = 0; i < fRecordedOps.count(); ++i) {
    158         if (!fRecordedOps[i].fOp) {
    159             continue;
    160         }
    161 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
    162         TRACE_EVENT0("skia", fRecordedOps[i].fOp->name());
    163 #endif
    164 
    165         GrOpFlushState::OpArgs opArgs {
    166             fRecordedOps[i].fOp.get(),
    167             fTarget.get()->asRenderTargetProxy(),
    168             fRecordedOps[i].fAppliedClip,
    169             fRecordedOps[i].fDstProxy
    170         };
    171 
    172         flushState->setOpArgs(&opArgs);
    173         fRecordedOps[i].fOp->execute(flushState);
    174         flushState->setOpArgs(nullptr);
    175     }
    176 
    177     finish_command_buffer(commandBuffer.get());
    178     flushState->setCommandBuffer(nullptr);
    179 
    180     return true;
    181 }
    182 
    183 void GrRenderTargetOpList::endFlush() {
    184     fLastClipStackGenID = SK_InvalidUniqueID;
    185     fRecordedOps.reset();
    186     fClipAllocator.reset();
    187     INHERITED::endFlush();
    188 }
    189 
    190 void GrRenderTargetOpList::discard() {
    191     // Discard calls to in-progress opLists are ignored. Calls at the start update the
    192     // opLists' color & stencil load ops.
    193     if (this->isEmpty()) {
    194         fColorLoadOp = GrLoadOp::kDiscard;
    195         fStencilLoadOp = GrLoadOp::kDiscard;
    196     }
    197 }
    198 
    199 void GrRenderTargetOpList::fullClear(const GrCaps& caps, GrColor color) {
    200 
    201     // This is conservative. If the opList is marked as needing a stencil buffer then there
    202     // may be a prior op that writes to the stencil buffer. Although the clear will ignore the
    203     // stencil buffer, following draw ops may not so we can't get rid of all the preceding ops.
    204     // Beware! If we ever add any ops that have a side effect beyond modifying the stencil
    205     // buffer we will need a more elaborate tracking system (skbug.com/7002).
    206     if (this->isEmpty() || !fTarget.get()->asRenderTargetProxy()->needsStencil()) {
    207         fRecordedOps.reset();
    208         fDeferredProxies.reset();
    209         fColorLoadOp = GrLoadOp::kClear;
    210         fLoadClearColor = color;
    211         return;
    212     }
    213 
    214     std::unique_ptr<GrClearOp> op(GrClearOp::Make(GrFixedClip::Disabled(), color, fTarget.get()));
    215     if (!op) {
    216         return;
    217     }
    218 
    219     this->recordOp(std::move(op), caps);
    220 }
    221 
    222 ////////////////////////////////////////////////////////////////////////////////
    223 
    224 // This closely parallels GrTextureOpList::copySurface but renderTargetOpLists
    225 // also store the applied clip and dest proxy with the op
    226 bool GrRenderTargetOpList::copySurface(const GrCaps& caps,
    227                                        GrSurfaceProxy* dst,
    228                                        GrSurfaceProxy* src,
    229                                        const SkIRect& srcRect,
    230                                        const SkIPoint& dstPoint) {
    231     SkASSERT(dst->asRenderTargetProxy() == fTarget.get());
    232     std::unique_ptr<GrOp> op = GrCopySurfaceOp::Make(dst, src, srcRect, dstPoint);
    233     if (!op) {
    234         return false;
    235     }
    236 
    237     this->addOp(std::move(op), caps);
    238     return true;
    239 }
    240 
    241 void GrRenderTargetOpList::purgeOpsWithUninstantiatedProxies() {
    242     bool hasUninstantiatedProxy = false;
    243     auto checkInstantiation = [ &hasUninstantiatedProxy ] (GrSurfaceProxy* p) {
    244         if (!p->priv().isInstantiated()) {
    245             hasUninstantiatedProxy = true;
    246         }
    247     };
    248     for (RecordedOp& recordedOp : fRecordedOps) {
    249         hasUninstantiatedProxy = false;
    250         recordedOp.visitProxies(checkInstantiation);
    251         if (hasUninstantiatedProxy) {
    252             // When instantiation of the proxy fails we drop the Op
    253             recordedOp.fOp = nullptr;
    254         }
    255     }
    256 }
    257 
    258 void GrRenderTargetOpList::gatherProxyIntervals(GrResourceAllocator* alloc) const {
    259     unsigned int cur = alloc->numOps();
    260 
    261     for (int i = 0; i < fDeferredProxies.count(); ++i) {
    262         SkASSERT(!fDeferredProxies[i]->priv().isInstantiated());
    263         // We give all the deferred proxies a write usage at the very start of flushing. This
    264         // locks them out of being reused for the entire flush until they are read - and then
    265         // they can be recycled. This is a bit unfortunate because a flush can proceed in waves
    266         // with sub-flushes. The deferred proxies only need to be pinned from the start of
    267         // the sub-flush in which they appear.
    268         alloc->addInterval(fDeferredProxies[i], 0, 0);
    269     }
    270 
    271     // Add the interval for all the writes to this opList's target
    272     if (fRecordedOps.count()) {
    273         alloc->addInterval(fTarget.get(), cur, cur+fRecordedOps.count()-1);
    274     } else {
    275         // This can happen if there is a loadOp (e.g., a clear) but no other draws. In this case we
    276         // still need to add an interval for the destination so we create a fake op# for
    277         // the missing clear op.
    278         alloc->addInterval(fTarget.get());
    279         alloc->incOps();
    280     }
    281 
    282     auto gather = [ alloc SkDEBUGCODE(, this) ] (GrSurfaceProxy* p) {
    283         alloc->addInterval(p SkDEBUGCODE(, fTarget.get() == p));
    284     };
    285     for (const RecordedOp& recordedOp : fRecordedOps) {
    286         recordedOp.visitProxies(gather); // only diff from the GrTextureOpList version
    287 
    288         // Even though the op may have been moved we still need to increment the op count to
    289         // keep all the math consistent.
    290         alloc->incOps();
    291     }
    292 }
    293 
    294 static inline bool can_reorder(const SkRect& a, const SkRect& b) { return !GrRectsOverlap(a, b); }
    295 
    296 bool GrRenderTargetOpList::combineIfPossible(const RecordedOp& a, GrOp* b,
    297                                              const GrAppliedClip* bClip,
    298                                              const DstProxy* bDstProxy,
    299                                              const GrCaps& caps) {
    300     if (a.fAppliedClip) {
    301         if (!bClip) {
    302             return false;
    303         }
    304         if (*a.fAppliedClip != *bClip) {
    305             return false;
    306         }
    307     } else if (bClip) {
    308         return false;
    309     }
    310     if (bDstProxy) {
    311         if (a.fDstProxy != *bDstProxy) {
    312             return false;
    313         }
    314     } else if (a.fDstProxy.proxy()) {
    315         return false;
    316     }
    317     return a.fOp->combineIfPossible(b, caps);
    318 }
    319 
    320 void GrRenderTargetOpList::recordOp(std::unique_ptr<GrOp> op,
    321                                     const GrCaps& caps,
    322                                     GrAppliedClip* clip,
    323                                     const DstProxy* dstProxy) {
    324     SkASSERT(fTarget.get());
    325 
    326     // A closed GrOpList should never receive new/more ops
    327     SkASSERT(!this->isClosed());
    328 
    329     // Check if there is an op we can combine with by linearly searching back until we either
    330     // 1) check every op
    331     // 2) intersect with something
    332     // 3) find a 'blocker'
    333     GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), fTarget.get()->uniqueID());
    334     GrOP_INFO("opList: %d Recording (%s, opID: %u)\n"
    335               "\tBounds [L: %.2f, T: %.2f R: %.2f B: %.2f]\n",
    336                this->uniqueID(),
    337                op->name(),
    338                op->uniqueID(),
    339                op->bounds().fLeft, op->bounds().fTop,
    340                op->bounds().fRight, op->bounds().fBottom);
    341     GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str());
    342     GrOP_INFO("\tOutcome:\n");
    343     int maxCandidates = SkTMin(kMaxOpLookback, fRecordedOps.count());
    344     // If we don't have a valid destination render target then we cannot reorder.
    345     if (maxCandidates) {
    346         int i = 0;
    347         while (true) {
    348             const RecordedOp& candidate = fRecordedOps.fromBack(i);
    349 
    350             if (this->combineIfPossible(candidate, op.get(), clip, dstProxy, caps)) {
    351                 GrOP_INFO("\t\tBackward: Combining with (%s, opID: %u)\n", candidate.fOp->name(),
    352                           candidate.fOp->uniqueID());
    353                 GrOP_INFO("\t\t\tBackward: Combined op info:\n");
    354                 GrOP_INFO(SkTabString(candidate.fOp->dumpInfo(), 4).c_str());
    355                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, candidate.fOp.get(), op.get());
    356                 return;
    357             }
    358             // Stop going backwards if we would cause a painter's order violation.
    359             if (!can_reorder(fRecordedOps.fromBack(i).fOp->bounds(), op->bounds())) {
    360                 GrOP_INFO("\t\tBackward: Intersects with (%s, opID: %u)\n", candidate.fOp->name(),
    361                           candidate.fOp->uniqueID());
    362                 break;
    363             }
    364             ++i;
    365             if (i == maxCandidates) {
    366                 GrOP_INFO("\t\tBackward: Reached max lookback or beginning of op array %d\n", i);
    367                 break;
    368             }
    369         }
    370     } else {
    371         GrOP_INFO("\t\tBackward: FirstOp\n");
    372     }
    373     GR_AUDIT_TRAIL_OP_RESULT_NEW(fAuditTrail, op);
    374     if (clip) {
    375         clip = fClipAllocator.make<GrAppliedClip>(std::move(*clip));
    376         SkDEBUGCODE(fNumClips++;)
    377     }
    378     fRecordedOps.emplace_back(std::move(op), clip, dstProxy);
    379     fRecordedOps.back().fOp->wasRecorded(this);
    380 }
    381 
    382 void GrRenderTargetOpList::forwardCombine(const GrCaps& caps) {
    383     SkASSERT(!this->isClosed());
    384 
    385     GrOP_INFO("opList: %d ForwardCombine %d ops:\n", this->uniqueID(), fRecordedOps.count());
    386 
    387     for (int i = 0; i < fRecordedOps.count() - 1; ++i) {
    388         GrOp* op = fRecordedOps[i].fOp.get();
    389 
    390         int maxCandidateIdx = SkTMin(i + kMaxOpLookahead, fRecordedOps.count() - 1);
    391         int j = i + 1;
    392         while (true) {
    393             const RecordedOp& candidate = fRecordedOps[j];
    394 
    395             if (this->combineIfPossible(fRecordedOps[i], candidate.fOp.get(),
    396                                         candidate.fAppliedClip, &candidate.fDstProxy, caps)) {
    397                 GrOP_INFO("\t\t%d: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
    398                           i, op->name(), op->uniqueID(),
    399                           candidate.fOp->name(), candidate.fOp->uniqueID());
    400                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, op, candidate.fOp.get());
    401                 fRecordedOps[j].fOp = std::move(fRecordedOps[i].fOp);
    402                 break;
    403             }
    404             // Stop traversing if we would cause a painter's order violation.
    405             if (!can_reorder(fRecordedOps[j].fOp->bounds(), op->bounds())) {
    406                 GrOP_INFO("\t\t%d: (%s opID: %u) -> Intersects with (%s, opID: %u)\n",
    407                           i, op->name(), op->uniqueID(),
    408                           candidate.fOp->name(), candidate.fOp->uniqueID());
    409                 break;
    410             }
    411             ++j;
    412             if (j > maxCandidateIdx) {
    413                 GrOP_INFO("\t\t%d: (%s opID: %u) -> Reached max lookahead or end of array\n",
    414                           i, op->name(), op->uniqueID());
    415                 break;
    416             }
    417         }
    418     }
    419 }
    420 
    421