• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/gpu/GrRenderTargetOpList.h"
9 
10 #include "include/private/GrRecordingContext.h"
11 #include "src/core/SkExchange.h"
12 #include "src/core/SkRectPriv.h"
13 #include "src/core/SkTraceEvent.h"
14 #include "src/gpu/GrAuditTrail.h"
15 #include "src/gpu/GrCaps.h"
16 #include "src/gpu/GrGpu.h"
17 #include "src/gpu/GrGpuCommandBuffer.h"
18 #include "src/gpu/GrMemoryPool.h"
19 #include "src/gpu/GrRecordingContextPriv.h"
20 #include "src/gpu/GrRenderTargetContext.h"
21 #include "src/gpu/GrResourceAllocator.h"
22 #include "src/gpu/GrTexturePriv.h"
23 #include "src/gpu/geometry/GrRect.h"
24 #include "src/gpu/ops/GrClearOp.h"
25 #include "src/gpu/ops/GrCopySurfaceOp.h"
26 #include "src/gpu/ops/GrTransferFromOp.h"
27 
28 ////////////////////////////////////////////////////////////////////////////////
29 
30 // Experimentally we have found that most combining occurs within the first 10 comparisons.
31 static const int kMaxOpMergeDistance = 10;
32 static const int kMaxOpChainDistance = 10;
33 
34 ////////////////////////////////////////////////////////////////////////////////
35 
36 using DstProxy = GrXferProcessor::DstProxy;
37 
38 ////////////////////////////////////////////////////////////////////////////////
39 
can_reorder(const SkRect & a,const SkRect & b)40 static inline bool can_reorder(const SkRect& a, const SkRect& b) { return !GrRectsOverlap(a, b); }
41 
42 ////////////////////////////////////////////////////////////////////////////////
43 
List(std::unique_ptr<GrOp> op)44 inline GrRenderTargetOpList::OpChain::List::List(std::unique_ptr<GrOp> op)
45         : fHead(std::move(op)), fTail(fHead.get()) {
46     this->validate();
47 }
48 
List(List && that)49 inline GrRenderTargetOpList::OpChain::List::List(List&& that) { *this = std::move(that); }
50 
operator =(List && that)51 inline GrRenderTargetOpList::OpChain::List& GrRenderTargetOpList::OpChain::List::operator=(
52         List&& that) {
53     fHead = std::move(that.fHead);
54     fTail = that.fTail;
55     that.fTail = nullptr;
56     this->validate();
57     return *this;
58 }
59 
popHead()60 inline std::unique_ptr<GrOp> GrRenderTargetOpList::OpChain::List::popHead() {
61     SkASSERT(fHead);
62     auto temp = fHead->cutChain();
63     std::swap(temp, fHead);
64     if (!fHead) {
65         SkASSERT(fTail == temp.get());
66         fTail = nullptr;
67     }
68     return temp;
69 }
70 
removeOp(GrOp * op)71 inline std::unique_ptr<GrOp> GrRenderTargetOpList::OpChain::List::removeOp(GrOp* op) {
72 #ifdef SK_DEBUG
73     auto head = op;
74     while (head->prevInChain()) { head = head->prevInChain(); }
75     SkASSERT(head == fHead.get());
76 #endif
77     auto prev = op->prevInChain();
78     if (!prev) {
79         SkASSERT(op == fHead.get());
80         return this->popHead();
81     }
82     auto temp = prev->cutChain();
83     if (auto next = temp->cutChain()) {
84         prev->chainConcat(std::move(next));
85     } else {
86         SkASSERT(fTail == op);
87         fTail = prev;
88     }
89     this->validate();
90     return temp;
91 }
92 
pushHead(std::unique_ptr<GrOp> op)93 inline void GrRenderTargetOpList::OpChain::List::pushHead(std::unique_ptr<GrOp> op) {
94     SkASSERT(op);
95     SkASSERT(op->isChainHead());
96     SkASSERT(op->isChainTail());
97     if (fHead) {
98         op->chainConcat(std::move(fHead));
99         fHead = std::move(op);
100     } else {
101         fHead = std::move(op);
102         fTail = fHead.get();
103     }
104 }
105 
pushTail(std::unique_ptr<GrOp> op)106 inline void GrRenderTargetOpList::OpChain::List::pushTail(std::unique_ptr<GrOp> op) {
107     SkASSERT(op->isChainTail());
108     fTail->chainConcat(std::move(op));
109     fTail = fTail->nextInChain();
110 }
111 
validate() const112 inline void GrRenderTargetOpList::OpChain::List::validate() const {
113 #ifdef SK_DEBUG
114     if (fHead) {
115         SkASSERT(fTail);
116         fHead->validateChain(fTail);
117     }
118 #endif
119 }
120 
121 ////////////////////////////////////////////////////////////////////////////////
122 
OpChain(std::unique_ptr<GrOp> op,GrProcessorSet::Analysis processorAnalysis,GrAppliedClip * appliedClip,const DstProxy * dstProxy)123 GrRenderTargetOpList::OpChain::OpChain(std::unique_ptr<GrOp> op,
124                                        GrProcessorSet::Analysis processorAnalysis,
125                                        GrAppliedClip* appliedClip, const DstProxy* dstProxy)
126         : fList{std::move(op)}
127         , fProcessorAnalysis(processorAnalysis)
128         , fAppliedClip(appliedClip) {
129     if (fProcessorAnalysis.requiresDstTexture()) {
130         SkASSERT(dstProxy && dstProxy->proxy());
131         fDstProxy = *dstProxy;
132     }
133     fBounds = fList.head()->bounds();
134 }
135 
visitProxies(const GrOp::VisitProxyFunc & func) const136 void GrRenderTargetOpList::OpChain::visitProxies(const GrOp::VisitProxyFunc& func) const {
137     if (fList.empty()) {
138         return;
139     }
140     for (const auto& op : GrOp::ChainRange<>(fList.head())) {
141         op.visitProxies(func);
142     }
143     if (fDstProxy.proxy()) {
144         func(fDstProxy.proxy(), GrMipMapped::kNo);
145     }
146     if (fAppliedClip) {
147         fAppliedClip->visitProxies(func);
148     }
149 }
150 
deleteOps(GrOpMemoryPool * pool)151 void GrRenderTargetOpList::OpChain::deleteOps(GrOpMemoryPool* pool) {
152     while (!fList.empty()) {
153         pool->release(fList.popHead());
154     }
155 }
156 
157 // Concatenates two op chains and attempts to merge ops across the chains. Assumes that we know that
158 // the two chains are chainable. Returns the new chain.
DoConcat(List chainA,List chainB,const GrCaps & caps,GrOpMemoryPool * pool,GrAuditTrail * auditTrail)159 GrRenderTargetOpList::OpChain::List GrRenderTargetOpList::OpChain::DoConcat(
160         List chainA, List chainB, const GrCaps& caps, GrOpMemoryPool* pool,
161         GrAuditTrail* auditTrail) {
162     // We process ops in chain b from head to tail. We attempt to merge with nodes in a, starting
163     // at chain a's tail and working toward the head. We produce one of the following outcomes:
164     // 1) b's head is merged into an op in a.
165     // 2) An op from chain a is merged into b's head. (In this case b's head gets processed again.)
166     // 3) b's head is popped from chain a and added at the tail of a.
167     // After result 3 we don't want to attempt to merge the next head of b with the new tail of a,
168     // as we assume merges were already attempted when chain b was created. So we keep track of the
169     // original tail of a and start our iteration of a there. We also track the bounds of the nodes
170     // appended to chain a that will be skipped for bounds testing. If the original tail of a is
171     // merged into an op in b (case 2) then we advance the "original tail" towards the head of a.
172     GrOp* origATail = chainA.tail();
173     SkRect skipBounds = SkRectPriv::MakeLargestInverted();
174     do {
175         int numMergeChecks = 0;
176         bool merged = false;
177         bool noSkip = (origATail == chainA.tail());
178         SkASSERT(noSkip == (skipBounds == SkRectPriv::MakeLargestInverted()));
179         bool canBackwardMerge = noSkip || can_reorder(chainB.head()->bounds(), skipBounds);
180         SkRect forwardMergeBounds = skipBounds;
181         GrOp* a = origATail;
182         while (a) {
183             bool canForwardMerge =
184                     (a == chainA.tail()) || can_reorder(a->bounds(), forwardMergeBounds);
185             if (canForwardMerge || canBackwardMerge) {
186                 auto result = a->combineIfPossible(chainB.head(), caps);
187                 SkASSERT(result != GrOp::CombineResult::kCannotCombine);
188                 merged = (result == GrOp::CombineResult::kMerged);
189                 GrOP_INFO("\t\t: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
190                           chainB.head()->name(), chainB.head()->uniqueID(), a->name(),
191                           a->uniqueID());
192             }
193             if (merged) {
194                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(auditTrail, a, chainB.head());
195                 if (canBackwardMerge) {
196                     pool->release(chainB.popHead());
197                 } else {
198                     // We merged the contents of b's head into a. We will replace b's head with a in
199                     // chain b.
200                     SkASSERT(canForwardMerge);
201                     if (a == origATail) {
202                         origATail = a->prevInChain();
203                     }
204                     std::unique_ptr<GrOp> detachedA = chainA.removeOp(a);
205                     pool->release(chainB.popHead());
206                     chainB.pushHead(std::move(detachedA));
207                     if (chainA.empty()) {
208                         // We merged all the nodes in chain a to chain b.
209                         return chainB;
210                     }
211                 }
212                 break;
213             } else {
214                 if (++numMergeChecks == kMaxOpMergeDistance) {
215                     break;
216                 }
217                 forwardMergeBounds.joinNonEmptyArg(a->bounds());
218                 canBackwardMerge =
219                         canBackwardMerge && can_reorder(chainB.head()->bounds(), a->bounds());
220                 a = a->prevInChain();
221             }
222         }
223         // If we weren't able to merge b's head then pop b's head from chain b and make it the new
224         // tail of a.
225         if (!merged) {
226             chainA.pushTail(chainB.popHead());
227             skipBounds.joinNonEmptyArg(chainA.tail()->bounds());
228         }
229     } while (!chainB.empty());
230     return chainA;
231 }
232 
233 // Attempts to concatenate the given chain onto our own and merge ops across the chains. Returns
234 // whether the operation succeeded. On success, the provided list will be returned empty.
tryConcat(List * list,GrProcessorSet::Analysis processorAnalysis,const DstProxy & dstProxy,const GrAppliedClip * appliedClip,const SkRect & bounds,const GrCaps & caps,GrOpMemoryPool * pool,GrAuditTrail * auditTrail)235 bool GrRenderTargetOpList::OpChain::tryConcat(
236         List* list, GrProcessorSet::Analysis processorAnalysis, const DstProxy& dstProxy,
237         const GrAppliedClip* appliedClip, const SkRect& bounds, const GrCaps& caps,
238         GrOpMemoryPool* pool, GrAuditTrail* auditTrail) {
239     SkASSERT(!fList.empty());
240     SkASSERT(!list->empty());
241     SkASSERT(fProcessorAnalysis.requiresDstTexture() == SkToBool(fDstProxy.proxy()));
242     SkASSERT(processorAnalysis.requiresDstTexture() == SkToBool(dstProxy.proxy()));
243     // All returns use explicit tuple constructor rather than {a, b} to work around old GCC bug.
244     if (fList.head()->classID() != list->head()->classID() ||
245         SkToBool(fAppliedClip) != SkToBool(appliedClip) ||
246         (fAppliedClip && *fAppliedClip != *appliedClip) ||
247         (fProcessorAnalysis.requiresNonOverlappingDraws() !=
248                 processorAnalysis.requiresNonOverlappingDraws()) ||
249         (fProcessorAnalysis.requiresNonOverlappingDraws() &&
250                 // Non-overlaping draws are only required when Ganesh will either insert a barrier,
251                 // or read back a new dst texture between draws. In either case, we can neither
252                 // chain nor combine overlapping Ops.
253                 GrRectsTouchOrOverlap(fBounds, bounds)) ||
254         (fProcessorAnalysis.requiresDstTexture() != processorAnalysis.requiresDstTexture()) ||
255         (fProcessorAnalysis.requiresDstTexture() && fDstProxy != dstProxy)) {
256         return false;
257     }
258 
259     SkDEBUGCODE(bool first = true;)
260     do {
261         switch (fList.tail()->combineIfPossible(list->head(), caps)) {
262             case GrOp::CombineResult::kCannotCombine:
263                 // If an op supports chaining then it is required that chaining is transitive and
264                 // that if any two ops in two different chains can merge then the two chains
265                 // may also be chained together. Thus, we should only hit this on the first
266                 // iteration.
267                 SkASSERT(first);
268                 return false;
269             case GrOp::CombineResult::kMayChain:
270                 fList = DoConcat(std::move(fList), skstd::exchange(*list, List()), caps, pool,
271                                  auditTrail);
272                 // The above exchange cleared out 'list'. The list needs to be empty now for the
273                 // loop to terminate.
274                 SkASSERT(list->empty());
275                 break;
276             case GrOp::CombineResult::kMerged: {
277                 GrOP_INFO("\t\t: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
278                           list->tail()->name(), list->tail()->uniqueID(), list->head()->name(),
279                           list->head()->uniqueID());
280                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(auditTrail, fList.tail(), list->head());
281                 pool->release(list->popHead());
282                 break;
283             }
284         }
285         SkDEBUGCODE(first = false);
286     } while (!list->empty());
287 
288     // The new ops were successfully merged and/or chained onto our own.
289     fBounds.joinPossiblyEmptyRect(bounds);
290     return true;
291 }
292 
prependChain(OpChain * that,const GrCaps & caps,GrOpMemoryPool * pool,GrAuditTrail * auditTrail)293 bool GrRenderTargetOpList::OpChain::prependChain(OpChain* that, const GrCaps& caps,
294                                                  GrOpMemoryPool* pool, GrAuditTrail* auditTrail) {
295     if (!that->tryConcat(
296             &fList, fProcessorAnalysis, fDstProxy, fAppliedClip, fBounds, caps, pool, auditTrail)) {
297         this->validate();
298         // append failed
299         return false;
300     }
301 
302     // 'that' owns the combined chain. Move it into 'this'.
303     SkASSERT(fList.empty());
304     fList = std::move(that->fList);
305     fBounds = that->fBounds;
306 
307     that->fDstProxy.setProxy(nullptr);
308     if (that->fAppliedClip) {
309         for (int i = 0; i < that->fAppliedClip->numClipCoverageFragmentProcessors(); ++i) {
310             that->fAppliedClip->detachClipCoverageFragmentProcessor(i);
311         }
312     }
313     this->validate();
314     return true;
315 }
316 
appendOp(std::unique_ptr<GrOp> op,GrProcessorSet::Analysis processorAnalysis,const DstProxy * dstProxy,const GrAppliedClip * appliedClip,const GrCaps & caps,GrOpMemoryPool * pool,GrAuditTrail * auditTrail)317 std::unique_ptr<GrOp> GrRenderTargetOpList::OpChain::appendOp(
318         std::unique_ptr<GrOp> op, GrProcessorSet::Analysis processorAnalysis,
319         const DstProxy* dstProxy, const GrAppliedClip* appliedClip, const GrCaps& caps,
320         GrOpMemoryPool* pool, GrAuditTrail* auditTrail) {
321     const GrXferProcessor::DstProxy noDstProxy;
322     if (!dstProxy) {
323         dstProxy = &noDstProxy;
324     }
325     SkASSERT(op->isChainHead() && op->isChainTail());
326     SkRect opBounds = op->bounds();
327     List chain(std::move(op));
328     if (!this->tryConcat(
329             &chain, processorAnalysis, *dstProxy, appliedClip, opBounds, caps, pool, auditTrail)) {
330         // append failed, give the op back to the caller.
331         this->validate();
332         return chain.popHead();
333     }
334 
335     SkASSERT(chain.empty());
336     this->validate();
337     return nullptr;
338 }
339 
validate() const340 inline void GrRenderTargetOpList::OpChain::validate() const {
341 #ifdef SK_DEBUG
342     fList.validate();
343     for (const auto& op : GrOp::ChainRange<>(fList.head())) {
344         // Not using SkRect::contains because we allow empty rects.
345         SkASSERT(fBounds.fLeft <= op.bounds().fLeft && fBounds.fTop <= op.bounds().fTop &&
346                  fBounds.fRight >= op.bounds().fRight && fBounds.fBottom >= op.bounds().fBottom);
347     }
348 #endif
349 }
350 
351 ////////////////////////////////////////////////////////////////////////////////
352 
GrRenderTargetOpList(sk_sp<GrOpMemoryPool> opMemoryPool,sk_sp<GrRenderTargetProxy> proxy,GrAuditTrail * auditTrail)353 GrRenderTargetOpList::GrRenderTargetOpList(sk_sp<GrOpMemoryPool> opMemoryPool,
354                                            sk_sp<GrRenderTargetProxy> proxy,
355                                            GrAuditTrail* auditTrail)
356         : INHERITED(std::move(opMemoryPool), std::move(proxy), auditTrail)
357         , fLastClipStackGenID(SK_InvalidUniqueID)
358         SkDEBUGCODE(, fNumClips(0)) {
359     if (GrTextureProxy* textureProxy = fTarget->asTextureProxy()) {
360         if (GrMipMapped::kYes == textureProxy->mipMapped()) {
361             textureProxy->markMipMapsDirty();
362         }
363     }
364     fTarget->setLastRenderTask(this);
365 }
366 
deleteOps()367 void GrRenderTargetOpList::deleteOps() {
368     for (auto& chain : fOpChains) {
369         chain.deleteOps(fOpMemoryPool.get());
370     }
371     fOpChains.reset();
372 }
373 
~GrRenderTargetOpList()374 GrRenderTargetOpList::~GrRenderTargetOpList() {
375     this->deleteOps();
376 }
377 
378 ////////////////////////////////////////////////////////////////////////////////
379 
380 #ifdef SK_DEBUG
dump(bool printDependencies) const381 void GrRenderTargetOpList::dump(bool printDependencies) const {
382     INHERITED::dump(printDependencies);
383 
384     SkDebugf("ops (%d):\n", fOpChains.count());
385     for (int i = 0; i < fOpChains.count(); ++i) {
386         SkDebugf("*******************************\n");
387         if (!fOpChains[i].head()) {
388             SkDebugf("%d: <combined forward or failed instantiation>\n", i);
389         } else {
390             SkDebugf("%d: %s\n", i, fOpChains[i].head()->name());
391             SkRect bounds = fOpChains[i].bounds();
392             SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", bounds.fLeft,
393                      bounds.fTop, bounds.fRight, bounds.fBottom);
394             for (const auto& op : GrOp::ChainRange<>(fOpChains[i].head())) {
395                 SkString info = SkTabString(op.dumpInfo(), 1);
396                 SkDebugf("%s\n", info.c_str());
397                 bounds = op.bounds();
398                 SkDebugf("\tClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", bounds.fLeft,
399                          bounds.fTop, bounds.fRight, bounds.fBottom);
400             }
401         }
402     }
403 }
404 
visitProxies_debugOnly(const GrOp::VisitProxyFunc & func) const405 void GrRenderTargetOpList::visitProxies_debugOnly(const GrOp::VisitProxyFunc& func) const {
406     for (const OpChain& chain : fOpChains) {
407         chain.visitProxies(func);
408     }
409 }
410 
411 #endif
412 
onPrepare(GrOpFlushState * flushState)413 void GrRenderTargetOpList::onPrepare(GrOpFlushState* flushState) {
414     SkASSERT(fTarget->peekRenderTarget());
415     SkASSERT(this->isClosed());
416 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
417     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
418 #endif
419 
420     // Loop over the ops that haven't yet been prepared.
421     for (const auto& chain : fOpChains) {
422         if (chain.head()) {
423 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
424             TRACE_EVENT0("skia.gpu", chain.head()->name());
425 #endif
426             GrOpFlushState::OpArgs opArgs = {
427                 chain.head(),
428                 fTarget->asRenderTargetProxy(),
429                 chain.appliedClip(),
430                 fTarget.get()->asRenderTargetProxy()->outputSwizzle(),
431                 chain.dstProxy()
432             };
433             flushState->setOpArgs(&opArgs);
434             chain.head()->prepare(flushState);
435             flushState->setOpArgs(nullptr);
436         }
437     }
438 }
439 
create_command_buffer(GrGpu * gpu,GrRenderTarget * rt,GrSurfaceOrigin origin,const SkRect & bounds,GrLoadOp colorLoadOp,const SkPMColor4f & loadClearColor,GrLoadOp stencilLoadOp)440 static GrGpuRTCommandBuffer* create_command_buffer(GrGpu* gpu,
441                                                    GrRenderTarget* rt,
442                                                    GrSurfaceOrigin origin,
443                                                    const SkRect& bounds,
444                                                    GrLoadOp colorLoadOp,
445                                                    const SkPMColor4f& loadClearColor,
446                                                    GrLoadOp stencilLoadOp) {
447     const GrGpuRTCommandBuffer::LoadAndStoreInfo kColorLoadStoreInfo {
448         colorLoadOp,
449         GrStoreOp::kStore,
450         loadClearColor
451     };
452 
453     // TODO:
454     // We would like to (at this level) only ever clear & discard. We would need
455     // to stop splitting up higher level opLists for copyOps to achieve that.
456     // Note: we would still need SB loads and stores but they would happen at a
457     // lower level (inside the VK command buffer).
458     const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo stencilLoadAndStoreInfo {
459         stencilLoadOp,
460         GrStoreOp::kStore,
461     };
462 
463     return gpu->getCommandBuffer(rt, origin, bounds, kColorLoadStoreInfo, stencilLoadAndStoreInfo);
464 }
465 
466 // TODO: this is where GrOp::renderTarget is used (which is fine since it
467 // is at flush time). However, we need to store the RenderTargetProxy in the
468 // Ops and instantiate them here.
onExecute(GrOpFlushState * flushState)469 bool GrRenderTargetOpList::onExecute(GrOpFlushState* flushState) {
470     // TODO: Forcing the execution of the discard here isn't ideal since it will cause us to do a
471     // discard and then store the data back in memory so that the load op on future draws doesn't
472     // think the memory is unitialized. Ideally we would want a system where we are tracking whether
473     // the proxy itself has valid data or not, and then use that as a signal on whether we should be
474     // loading or discarding. In that world we wouldni;t need to worry about executing oplists with
475     // no ops just to do a discard.
476     if (fOpChains.empty() && GrLoadOp::kClear != fColorLoadOp &&
477         GrLoadOp::kDiscard != fColorLoadOp) {
478         // TEMPORARY: We are in the process of moving GrMipMapsStatus from the texture to the proxy.
479         // During this time we want to assert that the proxy resolves mipmaps at the exact same
480         // times the old code would have. A null opList is very exceptional, and the proxy will have
481         // assumed mipmaps are dirty in this scenario. We mark them dirty here on the texture as
482         // well, in order to keep the assert passing.
483         GrTexture* tex = fTarget->peekTexture();
484         if (tex && GrMipMapped::kYes == tex->texturePriv().mipMapped()) {
485             tex->texturePriv().markMipMapsDirty();
486         }
487         return false;
488     }
489 
490     SkASSERT(fTarget->peekRenderTarget());
491     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
492 
493     // TODO: at the very least, we want the stencil store op to always be discard (at this
494     // level). In Vulkan, sub-command buffers would still need to load & store the stencil buffer.
495 
496     // Make sure load ops are not kClear if the GPU needs to use draws for clears
497     SkASSERT(fColorLoadOp != GrLoadOp::kClear ||
498              !flushState->gpu()->caps()->performColorClearsAsDraws());
499     SkASSERT(fStencilLoadOp != GrLoadOp::kClear ||
500              !flushState->gpu()->caps()->performStencilClearsAsDraws());
501     GrGpuRTCommandBuffer* commandBuffer = create_command_buffer(
502                                                     flushState->gpu(),
503                                                     fTarget->peekRenderTarget(),
504                                                     fTarget->origin(),
505                                                     fTarget->getBoundsRect(),
506                                                     fColorLoadOp,
507                                                     fLoadClearColor,
508                                                     fStencilLoadOp);
509     flushState->setCommandBuffer(commandBuffer);
510     commandBuffer->begin();
511 
512     // Draw all the generated geometry.
513     for (const auto& chain : fOpChains) {
514         if (!chain.head()) {
515             continue;
516         }
517 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
518         TRACE_EVENT0("skia.gpu", chain.head()->name());
519 #endif
520 
521         GrOpFlushState::OpArgs opArgs {
522             chain.head(),
523             fTarget->asRenderTargetProxy(),
524             chain.appliedClip(),
525             fTarget.get()->asRenderTargetProxy()->outputSwizzle(),
526             chain.dstProxy()
527         };
528 
529         flushState->setOpArgs(&opArgs);
530         chain.head()->execute(flushState, chain.bounds());
531         flushState->setOpArgs(nullptr);
532     }
533 
534     commandBuffer->end();
535     flushState->gpu()->submit(commandBuffer);
536     flushState->setCommandBuffer(nullptr);
537 
538     return true;
539 }
540 
endFlush()541 void GrRenderTargetOpList::endFlush() {
542     fLastClipStackGenID = SK_InvalidUniqueID;
543     this->deleteOps();
544     fClipAllocator.reset();
545     INHERITED::endFlush();
546 }
547 
discard()548 void GrRenderTargetOpList::discard() {
549     // Discard calls to in-progress opLists are ignored. Calls at the start update the
550     // opLists' color & stencil load ops.
551     if (this->isEmpty()) {
552         fColorLoadOp = GrLoadOp::kDiscard;
553         fStencilLoadOp = GrLoadOp::kDiscard;
554     }
555 }
556 
setColorLoadOp(GrLoadOp op,const SkPMColor4f & color)557 void GrRenderTargetOpList::setColorLoadOp(GrLoadOp op, const SkPMColor4f& color) {
558     fColorLoadOp = op;
559     fLoadClearColor = color;
560 }
561 
resetForFullscreenClear(CanDiscardPreviousOps canDiscardPreviousOps)562 bool GrRenderTargetOpList::resetForFullscreenClear(CanDiscardPreviousOps canDiscardPreviousOps) {
563     // Mark the color load op as discard (this may be followed by a clearColorOnLoad call to make
564     // the load op kClear, or it may be followed by an explicit op). In the event of an absClear()
565     // after a regular clear(), we could end up with a clear load op and a real clear op in the list
566     // if the load op were not reset here.
567     fColorLoadOp = GrLoadOp::kDiscard;
568 
569     // If we previously recorded a wait op, we cannot delete the wait op. Until we track the wait
570     // ops separately from normal ops, we have to avoid clearing out any ops in this case as well.
571     if (fHasWaitOp) {
572         canDiscardPreviousOps = CanDiscardPreviousOps::kNo;
573     }
574 
575     if (CanDiscardPreviousOps::kYes == canDiscardPreviousOps || this->isEmpty()) {
576         this->deleteOps();
577         fDeferredProxies.reset();
578 
579         // If the opList is using a render target which wraps a vulkan command buffer, we can't do a
580         // clear load since we cannot change the render pass that we are using. Thus we fall back to
581         // making a clear op in this case.
582         return !fTarget->asRenderTargetProxy()->wrapsVkSecondaryCB();
583     }
584 
585     // Could not empty the list, so an op must be added to handle the clear
586     return false;
587 }
588 
589 ////////////////////////////////////////////////////////////////////////////////
590 
591 // This closely parallels GrTextureOpList::copySurface but renderTargetOpLists
592 // also store the applied clip and dest proxy with the op
copySurface(GrRecordingContext * context,GrSurfaceProxy * src,const SkIRect & srcRect,const SkIPoint & dstPoint)593 bool GrRenderTargetOpList::copySurface(GrRecordingContext* context,
594                                        GrSurfaceProxy* src,
595                                        const SkIRect& srcRect,
596                                        const SkIPoint& dstPoint) {
597     std::unique_ptr<GrOp> op = GrCopySurfaceOp::Make(
598             context, fTarget.get(), src, srcRect, dstPoint);
599     if (!op) {
600         return false;
601     }
602 
603     this->addOp(std::move(op), GrTextureResolveManager(context->priv().drawingManager()),
604                 *context->priv().caps());
605     return true;
606 }
607 
transferFrom(GrRecordingContext * context,const SkIRect & srcRect,GrColorType surfaceColorType,GrColorType dstColorType,sk_sp<GrGpuBuffer> dst,size_t dstOffset)608 void GrRenderTargetOpList::transferFrom(GrRecordingContext* context,
609                                         const SkIRect& srcRect,
610                                         GrColorType surfaceColorType,
611                                         GrColorType dstColorType,
612                                         sk_sp<GrGpuBuffer> dst,
613                                         size_t dstOffset) {
614     auto op = GrTransferFromOp::Make(context, srcRect, surfaceColorType, dstColorType,
615                                      std::move(dst), dstOffset);
616     this->addOp(std::move(op), GrTextureResolveManager(context->priv().drawingManager()),
617                 *context->priv().caps());
618 }
619 
handleInternalAllocationFailure()620 void GrRenderTargetOpList::handleInternalAllocationFailure() {
621     bool hasUninstantiatedProxy = false;
622     auto checkInstantiation = [&hasUninstantiatedProxy](GrSurfaceProxy* p, GrMipMapped) {
623         if (!p->isInstantiated()) {
624             hasUninstantiatedProxy = true;
625         }
626     };
627     for (OpChain& recordedOp : fOpChains) {
628         hasUninstantiatedProxy = false;
629         recordedOp.visitProxies(checkInstantiation);
630         if (hasUninstantiatedProxy) {
631             // When instantiation of the proxy fails we drop the Op
632             recordedOp.deleteOps(fOpMemoryPool.get());
633         }
634     }
635 }
636 
onIsUsed(GrSurfaceProxy * proxyToCheck) const637 bool GrRenderTargetOpList::onIsUsed(GrSurfaceProxy* proxyToCheck) const {
638     bool used = false;
639 
640     auto visit = [ proxyToCheck, &used ] (GrSurfaceProxy* p, GrMipMapped) {
641         if (p == proxyToCheck) {
642             used = true;
643         }
644     };
645     for (const OpChain& recordedOp : fOpChains) {
646         recordedOp.visitProxies(visit);
647     }
648 
649     return used;
650 }
651 
gatherProxyIntervals(GrResourceAllocator * alloc) const652 void GrRenderTargetOpList::gatherProxyIntervals(GrResourceAllocator* alloc) const {
653 
654     for (int i = 0; i < fDeferredProxies.count(); ++i) {
655         SkASSERT(!fDeferredProxies[i]->isInstantiated());
656         // We give all the deferred proxies a write usage at the very start of flushing. This
657         // locks them out of being reused for the entire flush until they are read - and then
658         // they can be recycled. This is a bit unfortunate because a flush can proceed in waves
659         // with sub-flushes. The deferred proxies only need to be pinned from the start of
660         // the sub-flush in which they appear.
661         alloc->addInterval(fDeferredProxies[i], 0, 0, GrResourceAllocator::ActualUse::kNo);
662     }
663 
664     // Add the interval for all the writes to this opList's target
665     if (fOpChains.count()) {
666         unsigned int cur = alloc->curOp();
667 
668         alloc->addInterval(fTarget.get(), cur, cur + fOpChains.count() - 1,
669                            GrResourceAllocator::ActualUse::kYes);
670     } else {
671         // This can happen if there is a loadOp (e.g., a clear) but no other draws. In this case we
672         // still need to add an interval for the destination so we create a fake op# for
673         // the missing clear op.
674         alloc->addInterval(fTarget.get(), alloc->curOp(), alloc->curOp(),
675                            GrResourceAllocator::ActualUse::kYes);
676         alloc->incOps();
677     }
678 
679     auto gather = [ alloc SkDEBUGCODE(, this) ] (GrSurfaceProxy* p, GrMipMapped) {
680         alloc->addInterval(p, alloc->curOp(), alloc->curOp(), GrResourceAllocator::ActualUse::kYes
681                            SkDEBUGCODE(, fTarget.get() == p));
682     };
683     for (const OpChain& recordedOp : fOpChains) {
684         // only diff from the GrTextureOpList version
685         recordedOp.visitProxies(gather);
686 
687         // Even though the op may have been (re)moved we still need to increment the op count to
688         // keep all the math consistent.
689         alloc->incOps();
690     }
691 }
692 
recordOp(std::unique_ptr<GrOp> op,GrProcessorSet::Analysis processorAnalysis,GrAppliedClip * clip,const DstProxy * dstProxy,const GrCaps & caps)693 void GrRenderTargetOpList::recordOp(
694         std::unique_ptr<GrOp> op, GrProcessorSet::Analysis processorAnalysis, GrAppliedClip* clip,
695         const DstProxy* dstProxy, const GrCaps& caps) {
696     SkDEBUGCODE(op->validate();)
697     SkASSERT(processorAnalysis.requiresDstTexture() == (dstProxy && dstProxy->proxy()));
698     SkASSERT(fTarget);
699 
700     // A closed GrOpList should never receive new/more ops
701     SkASSERT(!this->isClosed());
702     if (!op->bounds().isFinite()) {
703         fOpMemoryPool->release(std::move(op));
704         return;
705     }
706 
707     // Check if there is an op we can combine with by linearly searching back until we either
708     // 1) check every op
709     // 2) intersect with something
710     // 3) find a 'blocker'
711     GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), fTarget->uniqueID());
712     GrOP_INFO("opList: %d Recording (%s, opID: %u)\n"
713               "\tBounds [L: %.2f, T: %.2f R: %.2f B: %.2f]\n",
714                this->uniqueID(),
715                op->name(),
716                op->uniqueID(),
717                op->bounds().fLeft, op->bounds().fTop,
718                op->bounds().fRight, op->bounds().fBottom);
719     GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str());
720     GrOP_INFO("\tOutcome:\n");
721     int maxCandidates = SkTMin(kMaxOpChainDistance, fOpChains.count());
722     if (maxCandidates) {
723         int i = 0;
724         while (true) {
725             OpChain& candidate = fOpChains.fromBack(i);
726             op = candidate.appendOp(std::move(op), processorAnalysis, dstProxy, clip, caps,
727                                     fOpMemoryPool.get(), fAuditTrail);
728             if (!op) {
729                 return;
730             }
731             // Stop going backwards if we would cause a painter's order violation.
732             if (!can_reorder(candidate.bounds(), op->bounds())) {
733                 GrOP_INFO("\t\tBackward: Intersects with chain (%s, head opID: %u)\n",
734                           candidate.head()->name(), candidate.head()->uniqueID());
735                 break;
736             }
737             if (++i == maxCandidates) {
738                 GrOP_INFO("\t\tBackward: Reached max lookback or beginning of op array %d\n", i);
739                 break;
740             }
741         }
742     } else {
743         GrOP_INFO("\t\tBackward: FirstOp\n");
744     }
745     if (clip) {
746         clip = fClipAllocator.make<GrAppliedClip>(std::move(*clip));
747         SkDEBUGCODE(fNumClips++;)
748     }
749     fOpChains.emplace_back(std::move(op), processorAnalysis, clip, dstProxy);
750 }
751 
forwardCombine(const GrCaps & caps)752 void GrRenderTargetOpList::forwardCombine(const GrCaps& caps) {
753     SkASSERT(!this->isClosed());
754     GrOP_INFO("opList: %d ForwardCombine %d ops:\n", this->uniqueID(), fOpChains.count());
755 
756     for (int i = 0; i < fOpChains.count() - 1; ++i) {
757         OpChain& chain = fOpChains[i];
758         int maxCandidateIdx = SkTMin(i + kMaxOpChainDistance, fOpChains.count() - 1);
759         int j = i + 1;
760         while (true) {
761             OpChain& candidate = fOpChains[j];
762             if (candidate.prependChain(&chain, caps, fOpMemoryPool.get(), fAuditTrail)) {
763                 break;
764             }
765             // Stop traversing if we would cause a painter's order violation.
766             if (!can_reorder(chain.bounds(), candidate.bounds())) {
767                 GrOP_INFO(
768                         "\t\t%d: chain (%s head opID: %u) -> "
769                         "Intersects with chain (%s, head opID: %u)\n",
770                         i, chain.head()->name(), chain.head()->uniqueID(), candidate.head()->name(),
771                         candidate.head()->uniqueID());
772                 break;
773             }
774             if (++j > maxCandidateIdx) {
775                 GrOP_INFO("\t\t%d: chain (%s opID: %u) -> Reached max lookahead or end of array\n",
776                           i, chain.head()->name(), chain.head()->uniqueID());
777                 break;
778             }
779         }
780     }
781 }
782 
783