• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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/GrOpsTask.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/GrMemoryPool.h"
18 #include "src/gpu/GrOpFlushState.h"
19 #include "src/gpu/GrOpsRenderPass.h"
20 #include "src/gpu/GrRecordingContextPriv.h"
21 #include "src/gpu/GrRenderTarget.h"
22 #include "src/gpu/GrRenderTargetContext.h"
23 #include "src/gpu/GrRenderTargetPriv.h"
24 #include "src/gpu/GrResourceAllocator.h"
25 #include "src/gpu/GrStencilAttachment.h"
26 #include "src/gpu/GrTexturePriv.h"
27 #include "src/gpu/geometry/GrRect.h"
28 #include "src/gpu/ops/GrClearOp.h"
29 
30 ////////////////////////////////////////////////////////////////////////////////
31 
32 // Experimentally we have found that most combining occurs within the first 10 comparisons.
33 static const int kMaxOpMergeDistance = 10;
34 static const int kMaxOpChainDistance = 10;
35 
36 ////////////////////////////////////////////////////////////////////////////////
37 
38 using DstProxyView = GrXferProcessor::DstProxyView;
39 
40 ////////////////////////////////////////////////////////////////////////////////
41 
can_reorder(const SkRect & a,const SkRect & b)42 static inline bool can_reorder(const SkRect& a, const SkRect& b) { return !GrRectsOverlap(a, b); }
43 
44 ////////////////////////////////////////////////////////////////////////////////
45 
List(std::unique_ptr<GrOp> op)46 inline GrOpsTask::OpChain::List::List(std::unique_ptr<GrOp> op)
47         : fHead(std::move(op)), fTail(fHead.get()) {
48     this->validate();
49 }
50 
List(List && that)51 inline GrOpsTask::OpChain::List::List(List&& that) { *this = std::move(that); }
52 
operator =(List && that)53 inline GrOpsTask::OpChain::List& GrOpsTask::OpChain::List::operator=(List&& that) {
54     fHead = std::move(that.fHead);
55     fTail = that.fTail;
56     that.fTail = nullptr;
57     this->validate();
58     return *this;
59 }
60 
popHead()61 inline std::unique_ptr<GrOp> GrOpsTask::OpChain::List::popHead() {
62     SkASSERT(fHead);
63     auto temp = fHead->cutChain();
64     std::swap(temp, fHead);
65     if (!fHead) {
66         SkASSERT(fTail == temp.get());
67         fTail = nullptr;
68     }
69     return temp;
70 }
71 
removeOp(GrOp * op)72 inline std::unique_ptr<GrOp> GrOpsTask::OpChain::List::removeOp(GrOp* op) {
73 #ifdef SK_DEBUG
74     auto head = op;
75     while (head->prevInChain()) { head = head->prevInChain(); }
76     SkASSERT(head == fHead.get());
77 #endif
78     auto prev = op->prevInChain();
79     if (!prev) {
80         SkASSERT(op == fHead.get());
81         return this->popHead();
82     }
83     auto temp = prev->cutChain();
84     if (auto next = temp->cutChain()) {
85         prev->chainConcat(std::move(next));
86     } else {
87         SkASSERT(fTail == op);
88         fTail = prev;
89     }
90     this->validate();
91     return temp;
92 }
93 
pushHead(std::unique_ptr<GrOp> op)94 inline void GrOpsTask::OpChain::List::pushHead(std::unique_ptr<GrOp> op) {
95     SkASSERT(op);
96     SkASSERT(op->isChainHead());
97     SkASSERT(op->isChainTail());
98     if (fHead) {
99         op->chainConcat(std::move(fHead));
100         fHead = std::move(op);
101     } else {
102         fHead = std::move(op);
103         fTail = fHead.get();
104     }
105 }
106 
pushTail(std::unique_ptr<GrOp> op)107 inline void GrOpsTask::OpChain::List::pushTail(std::unique_ptr<GrOp> op) {
108     SkASSERT(op->isChainTail());
109     fTail->chainConcat(std::move(op));
110     fTail = fTail->nextInChain();
111 }
112 
validate() const113 inline void GrOpsTask::OpChain::List::validate() const {
114 #ifdef SK_DEBUG
115     if (fHead) {
116         SkASSERT(fTail);
117         fHead->validateChain(fTail);
118     }
119 #endif
120 }
121 
122 ////////////////////////////////////////////////////////////////////////////////
123 
OpChain(std::unique_ptr<GrOp> op,GrProcessorSet::Analysis processorAnalysis,GrAppliedClip * appliedClip,const DstProxyView * dstProxyView)124 GrOpsTask::OpChain::OpChain(std::unique_ptr<GrOp> op,
125                             GrProcessorSet::Analysis processorAnalysis,
126                             GrAppliedClip* appliedClip, const DstProxyView* dstProxyView)
127         : fList{std::move(op)}
128         , fProcessorAnalysis(processorAnalysis)
129         , fAppliedClip(appliedClip) {
130     if (fProcessorAnalysis.requiresDstTexture()) {
131         SkASSERT(dstProxyView && dstProxyView->proxy());
132         fDstProxyView = *dstProxyView;
133     }
134     fBounds = fList.head()->bounds();
135 }
136 
visitProxies(const GrOp::VisitProxyFunc & func) const137 void GrOpsTask::OpChain::visitProxies(const GrOp::VisitProxyFunc& func) const {
138     if (fList.empty()) {
139         return;
140     }
141     for (const auto& op : GrOp::ChainRange<>(fList.head())) {
142         op.visitProxies(func);
143     }
144     if (fDstProxyView.proxy()) {
145         func(fDstProxyView.proxy(), GrMipMapped::kNo);
146     }
147     if (fAppliedClip) {
148         fAppliedClip->visitProxies(func);
149     }
150 }
151 
deleteOps(GrOpMemoryPool * pool)152 void GrOpsTask::OpChain::deleteOps(GrOpMemoryPool* pool) {
153     while (!fList.empty()) {
154         pool->release(fList.popHead());
155     }
156 }
157 
158 // Concatenates two op chains and attempts to merge ops across the chains. Assumes that we know that
159 // the two chains are chainable. Returns the new chain.
DoConcat(List chainA,List chainB,const GrCaps & caps,GrRecordingContext::Arenas * arenas,GrAuditTrail * auditTrail)160 GrOpsTask::OpChain::List GrOpsTask::OpChain::DoConcat(
161         List chainA, List chainB, const GrCaps& caps, GrRecordingContext::Arenas* arenas,
162         GrAuditTrail* auditTrail) {
163     // We process ops in chain b from head to tail. We attempt to merge with nodes in a, starting
164     // at chain a's tail and working toward the head. We produce one of the following outcomes:
165     // 1) b's head is merged into an op in a.
166     // 2) An op from chain a is merged into b's head. (In this case b's head gets processed again.)
167     // 3) b's head is popped from chain a and added at the tail of a.
168     // After result 3 we don't want to attempt to merge the next head of b with the new tail of a,
169     // as we assume merges were already attempted when chain b was created. So we keep track of the
170     // original tail of a and start our iteration of a there. We also track the bounds of the nodes
171     // appended to chain a that will be skipped for bounds testing. If the original tail of a is
172     // merged into an op in b (case 2) then we advance the "original tail" towards the head of a.
173     GrOp* origATail = chainA.tail();
174     SkRect skipBounds = SkRectPriv::MakeLargestInverted();
175     do {
176         int numMergeChecks = 0;
177         bool merged = false;
178         bool noSkip = (origATail == chainA.tail());
179         SkASSERT(noSkip == (skipBounds == SkRectPriv::MakeLargestInverted()));
180         bool canBackwardMerge = noSkip || can_reorder(chainB.head()->bounds(), skipBounds);
181         SkRect forwardMergeBounds = skipBounds;
182         GrOp* a = origATail;
183         while (a) {
184             bool canForwardMerge =
185                     (a == chainA.tail()) || can_reorder(a->bounds(), forwardMergeBounds);
186             if (canForwardMerge || canBackwardMerge) {
187                 auto result = a->combineIfPossible(chainB.head(), arenas, caps);
188                 SkASSERT(result != GrOp::CombineResult::kCannotCombine);
189                 merged = (result == GrOp::CombineResult::kMerged);
190                 GrOP_INFO("\t\t: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
191                           chainB.head()->name(), chainB.head()->uniqueID(), a->name(),
192                           a->uniqueID());
193             }
194             if (merged) {
195                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(auditTrail, a, chainB.head());
196                 if (canBackwardMerge) {
197                     arenas->opMemoryPool()->release(chainB.popHead());
198                 } else {
199                     // We merged the contents of b's head into a. We will replace b's head with a in
200                     // chain b.
201                     SkASSERT(canForwardMerge);
202                     if (a == origATail) {
203                         origATail = a->prevInChain();
204                     }
205                     std::unique_ptr<GrOp> detachedA = chainA.removeOp(a);
206                     arenas->opMemoryPool()->release(chainB.popHead());
207                     chainB.pushHead(std::move(detachedA));
208                     if (chainA.empty()) {
209                         // We merged all the nodes in chain a to chain b.
210                         return chainB;
211                     }
212                 }
213                 break;
214             } else {
215                 if (++numMergeChecks == kMaxOpMergeDistance) {
216                     break;
217                 }
218                 forwardMergeBounds.joinNonEmptyArg(a->bounds());
219                 canBackwardMerge =
220                         canBackwardMerge && can_reorder(chainB.head()->bounds(), a->bounds());
221                 a = a->prevInChain();
222             }
223         }
224         // If we weren't able to merge b's head then pop b's head from chain b and make it the new
225         // tail of a.
226         if (!merged) {
227             chainA.pushTail(chainB.popHead());
228             skipBounds.joinNonEmptyArg(chainA.tail()->bounds());
229         }
230     } while (!chainB.empty());
231     return chainA;
232 }
233 
234 // Attempts to concatenate the given chain onto our own and merge ops across the chains. Returns
235 // whether the operation succeeded. On success, the provided list will be returned empty.
tryConcat(List * list,GrProcessorSet::Analysis processorAnalysis,const DstProxyView & dstProxyView,const GrAppliedClip * appliedClip,const SkRect & bounds,const GrCaps & caps,GrRecordingContext::Arenas * arenas,GrAuditTrail * auditTrail)236 bool GrOpsTask::OpChain::tryConcat(
237         List* list, GrProcessorSet::Analysis processorAnalysis, const DstProxyView& dstProxyView,
238         const GrAppliedClip* appliedClip, const SkRect& bounds, const GrCaps& caps,
239         GrRecordingContext::Arenas* arenas, GrAuditTrail* auditTrail) {
240     SkASSERT(!fList.empty());
241     SkASSERT(!list->empty());
242     SkASSERT(fProcessorAnalysis.requiresDstTexture() == SkToBool(fDstProxyView.proxy()));
243     SkASSERT(processorAnalysis.requiresDstTexture() == SkToBool(dstProxyView.proxy()));
244     // All returns use explicit tuple constructor rather than {a, b} to work around old GCC bug.
245     if (fList.head()->classID() != list->head()->classID() ||
246         SkToBool(fAppliedClip) != SkToBool(appliedClip) ||
247         (fAppliedClip && *fAppliedClip != *appliedClip) ||
248         (fProcessorAnalysis.requiresNonOverlappingDraws() !=
249                 processorAnalysis.requiresNonOverlappingDraws()) ||
250         (fProcessorAnalysis.requiresNonOverlappingDraws() &&
251                 // Non-overlaping draws are only required when Ganesh will either insert a barrier,
252                 // or read back a new dst texture between draws. In either case, we can neither
253                 // chain nor combine overlapping Ops.
254                 GrRectsTouchOrOverlap(fBounds, bounds)) ||
255         (fProcessorAnalysis.requiresDstTexture() != processorAnalysis.requiresDstTexture()) ||
256         (fProcessorAnalysis.requiresDstTexture() && fDstProxyView != dstProxyView)) {
257         return false;
258     }
259 
260     SkDEBUGCODE(bool first = true;)
261     do {
262         switch (fList.tail()->combineIfPossible(list->head(), arenas, caps)) {
263             case GrOp::CombineResult::kCannotCombine:
264                 // If an op supports chaining then it is required that chaining is transitive and
265                 // that if any two ops in two different chains can merge then the two chains
266                 // may also be chained together. Thus, we should only hit this on the first
267                 // iteration.
268                 SkASSERT(first);
269                 return false;
270             case GrOp::CombineResult::kMayChain:
271                 fList = DoConcat(std::move(fList), skstd::exchange(*list, List()), caps, arenas,
272                                  auditTrail);
273                 // The above exchange cleared out 'list'. The list needs to be empty now for the
274                 // loop to terminate.
275                 SkASSERT(list->empty());
276                 break;
277             case GrOp::CombineResult::kMerged: {
278                 GrOP_INFO("\t\t: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
279                           list->tail()->name(), list->tail()->uniqueID(), list->head()->name(),
280                           list->head()->uniqueID());
281                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(auditTrail, fList.tail(), list->head());
282                 arenas->opMemoryPool()->release(list->popHead());
283                 break;
284             }
285         }
286         SkDEBUGCODE(first = false);
287     } while (!list->empty());
288 
289     // The new ops were successfully merged and/or chained onto our own.
290     fBounds.joinPossiblyEmptyRect(bounds);
291     return true;
292 }
293 
prependChain(OpChain * that,const GrCaps & caps,GrRecordingContext::Arenas * arenas,GrAuditTrail * auditTrail)294 bool GrOpsTask::OpChain::prependChain(OpChain* that, const GrCaps& caps,
295                                       GrRecordingContext::Arenas* arenas,
296                                       GrAuditTrail* auditTrail) {
297     if (!that->tryConcat(&fList, fProcessorAnalysis, fDstProxyView, fAppliedClip, fBounds, caps,
298                          arenas, auditTrail)) {
299         this->validate();
300         // append failed
301         return false;
302     }
303 
304     // 'that' owns the combined chain. Move it into 'this'.
305     SkASSERT(fList.empty());
306     fList = std::move(that->fList);
307     fBounds = that->fBounds;
308 
309     that->fDstProxyView.setProxyView({});
310     if (that->fAppliedClip) {
311         for (int i = 0; i < that->fAppliedClip->numClipCoverageFragmentProcessors(); ++i) {
312             that->fAppliedClip->detachClipCoverageFragmentProcessor(i);
313         }
314     }
315     this->validate();
316     return true;
317 }
318 
appendOp(std::unique_ptr<GrOp> op,GrProcessorSet::Analysis processorAnalysis,const DstProxyView * dstProxyView,const GrAppliedClip * appliedClip,const GrCaps & caps,GrRecordingContext::Arenas * arenas,GrAuditTrail * auditTrail)319 std::unique_ptr<GrOp> GrOpsTask::OpChain::appendOp(
320         std::unique_ptr<GrOp> op, GrProcessorSet::Analysis processorAnalysis,
321         const DstProxyView* dstProxyView, const GrAppliedClip* appliedClip, const GrCaps& caps,
322         GrRecordingContext::Arenas* arenas, GrAuditTrail* auditTrail) {
323     const GrXferProcessor::DstProxyView noDstProxyView;
324     if (!dstProxyView) {
325         dstProxyView = &noDstProxyView;
326     }
327     SkASSERT(op->isChainHead() && op->isChainTail());
328     SkRect opBounds = op->bounds();
329     List chain(std::move(op));
330     if (!this->tryConcat(
331             &chain, processorAnalysis, *dstProxyView, appliedClip, opBounds, caps,
332             arenas, auditTrail)) {
333         // append failed, give the op back to the caller.
334         this->validate();
335         return chain.popHead();
336     }
337 
338     SkASSERT(chain.empty());
339     this->validate();
340     return nullptr;
341 }
342 
validate() const343 inline void GrOpsTask::OpChain::validate() const {
344 #ifdef SK_DEBUG
345     fList.validate();
346     for (const auto& op : GrOp::ChainRange<>(fList.head())) {
347         // Not using SkRect::contains because we allow empty rects.
348         SkASSERT(fBounds.fLeft <= op.bounds().fLeft && fBounds.fTop <= op.bounds().fTop &&
349                  fBounds.fRight >= op.bounds().fRight && fBounds.fBottom >= op.bounds().fBottom);
350     }
351 #endif
352 }
353 
354 ////////////////////////////////////////////////////////////////////////////////
355 
GrOpsTask(GrRecordingContext::Arenas arenas,GrSurfaceProxyView view,GrAuditTrail * auditTrail)356 GrOpsTask::GrOpsTask(GrRecordingContext::Arenas arenas,
357                      GrSurfaceProxyView view,
358                      GrAuditTrail* auditTrail)
359         : GrRenderTask(std::move(view))
360         , fArenas(arenas)
361         , fAuditTrail(auditTrail)
362         , fLastClipStackGenID(SK_InvalidUniqueID)
363         SkDEBUGCODE(, fNumClips(0)) {
364     fTargetView.proxy()->setLastRenderTask(this);
365 }
366 
deleteOps()367 void GrOpsTask::deleteOps() {
368     for (auto& chain : fOpChains) {
369         chain.deleteOps(fArenas.opMemoryPool());
370     }
371     fOpChains.reset();
372 }
373 
~GrOpsTask()374 GrOpsTask::~GrOpsTask() {
375     this->deleteOps();
376 }
377 
378 ////////////////////////////////////////////////////////////////////////////////
379 
endFlush()380 void GrOpsTask::endFlush() {
381     fLastClipStackGenID = SK_InvalidUniqueID;
382     this->deleteOps();
383     fClipAllocator.reset();
384 
385     GrSurfaceProxy* proxy = fTargetView.proxy();
386     if (proxy && this == proxy->getLastRenderTask()) {
387         proxy->setLastRenderTask(nullptr);
388     }
389 
390     fTargetView.reset();
391     fDeferredProxies.reset();
392     fSampledProxies.reset();
393     fAuditTrail = nullptr;
394 }
395 
onPrePrepare(GrRecordingContext * context)396 void GrOpsTask::onPrePrepare(GrRecordingContext* context) {
397     SkASSERT(this->isClosed());
398 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
399     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
400 #endif
401     // TODO: remove the check for discard here once reduced op splitting is turned on. Currently we
402     // can end up with GrOpsTasks that only have a discard load op and no ops. For vulkan validation
403     // we need to keep that discard and not drop it. Once we have reduce op list splitting enabled
404     // we shouldn't end up with GrOpsTasks with only discard.
405     if (this->isNoOp() || (fClippedContentBounds.isEmpty() && fColorLoadOp != GrLoadOp::kDiscard)) {
406         return;
407     }
408 
409     for (const auto& chain : fOpChains) {
410         if (chain.shouldExecute()) {
411             chain.head()->prePrepare(context,
412                                      &fTargetView,
413                                      chain.appliedClip(),
414                                      chain.dstProxyView());
415         }
416     }
417 }
418 
onPrepare(GrOpFlushState * flushState)419 void GrOpsTask::onPrepare(GrOpFlushState* flushState) {
420     SkASSERT(fTargetView.proxy()->peekRenderTarget());
421     SkASSERT(this->isClosed());
422 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
423     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
424 #endif
425     // TODO: remove the check for discard here once reduced op splitting is turned on. Currently we
426     // can end up with GrOpsTasks that only have a discard load op and no ops. For vulkan validation
427     // we need to keep that discard and not drop it. Once we have reduce op list splitting enabled
428     // we shouldn't end up with GrOpsTasks with only discard.
429     if (this->isNoOp() || (fClippedContentBounds.isEmpty() && fColorLoadOp != GrLoadOp::kDiscard)) {
430         return;
431     }
432 
433     flushState->setSampledProxyArray(&fSampledProxies);
434     // Loop over the ops that haven't yet been prepared.
435     for (const auto& chain : fOpChains) {
436         if (chain.shouldExecute()) {
437 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
438             TRACE_EVENT0("skia.gpu", chain.head()->name());
439 #endif
440             GrOpFlushState::OpArgs opArgs(chain.head(),
441                                           &fTargetView,
442                                           chain.appliedClip(),
443                                           chain.dstProxyView());
444 
445             flushState->setOpArgs(&opArgs);
446 
447             // Temporary debugging helper: for debugging prePrepare w/o going through DDLs
448             // Delete once most of the GrOps have an onPrePrepare.
449             // chain.head()->prePrepare(flushState->gpu()->getContext(), &fTargetView,
450             //                          chain.appliedClip());
451 
452             // GrOp::prePrepare may or may not have been called at this point
453             chain.head()->prepare(flushState);
454             flushState->setOpArgs(nullptr);
455         }
456     }
457     flushState->setSampledProxyArray(nullptr);
458 }
459 
create_render_pass(GrGpu * gpu,GrRenderTarget * rt,GrSurfaceOrigin origin,const SkIRect & bounds,GrLoadOp colorLoadOp,const SkPMColor4f & loadClearColor,GrLoadOp stencilLoadOp,GrStoreOp stencilStoreOp,const SkTArray<GrSurfaceProxy *,true> & sampledProxies)460 static GrOpsRenderPass* create_render_pass(
461         GrGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
462         GrLoadOp colorLoadOp, const SkPMColor4f& loadClearColor, GrLoadOp stencilLoadOp,
463         GrStoreOp stencilStoreOp, const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
464     const GrOpsRenderPass::LoadAndStoreInfo kColorLoadStoreInfo {
465         colorLoadOp,
466         GrStoreOp::kStore,
467         loadClearColor
468     };
469 
470     // TODO:
471     // We would like to (at this level) only ever clear & discard. We would need
472     // to stop splitting up higher level OpsTasks for copyOps to achieve that.
473     // Note: we would still need SB loads and stores but they would happen at a
474     // lower level (inside the VK command buffer).
475     const GrOpsRenderPass::StencilLoadAndStoreInfo stencilLoadAndStoreInfo {
476         stencilLoadOp,
477         stencilStoreOp,
478     };
479 
480     return gpu->getOpsRenderPass(rt, origin, bounds, kColorLoadStoreInfo, stencilLoadAndStoreInfo,
481                                  sampledProxies);
482 }
483 
484 // TODO: this is where GrOp::renderTarget is used (which is fine since it
485 // is at flush time). However, we need to store the RenderTargetProxy in the
486 // Ops and instantiate them here.
onExecute(GrOpFlushState * flushState)487 bool GrOpsTask::onExecute(GrOpFlushState* flushState) {
488     // TODO: remove the check for discard here once reduced op splitting is turned on. Currently we
489     // can end up with GrOpsTasks that only have a discard load op and no ops. For vulkan validation
490     // we need to keep that discard and not drop it. Once we have reduce op list splitting enabled
491     // we shouldn't end up with GrOpsTasks with only discard.
492     if (this->isNoOp() || (fClippedContentBounds.isEmpty() && fColorLoadOp != GrLoadOp::kDiscard)) {
493         return false;
494     }
495 
496     SkASSERT(fTargetView.proxy());
497     GrRenderTargetProxy* proxy = fTargetView.proxy()->asRenderTargetProxy();
498     SkASSERT(proxy);
499     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
500 
501     // Make sure load ops are not kClear if the GPU needs to use draws for clears
502     SkASSERT(fColorLoadOp != GrLoadOp::kClear ||
503              !flushState->gpu()->caps()->performColorClearsAsDraws());
504 
505     const GrCaps& caps = *flushState->gpu()->caps();
506     GrRenderTarget* renderTarget = proxy->peekRenderTarget();
507     SkASSERT(renderTarget);
508 
509     GrStencilAttachment* stencil = nullptr;
510     if (int numStencilSamples = proxy->numStencilSamples()) {
511         if (!flushState->resourceProvider()->attachStencilAttachment(
512                 renderTarget, numStencilSamples)) {
513             SkDebugf("WARNING: failed to attach a stencil buffer. Rendering will be skipped.\n");
514             return false;
515         }
516         stencil = renderTarget->renderTargetPriv().getStencilAttachment();
517     }
518 
519     SkASSERT(!stencil || stencil->numSamples() == proxy->numStencilSamples());
520 
521     GrLoadOp stencilLoadOp;
522     switch (fInitialStencilContent) {
523         case StencilContent::kDontCare:
524             stencilLoadOp = GrLoadOp::kDiscard;
525             break;
526         case StencilContent::kUserBitsCleared:
527             SkASSERT(!caps.performStencilClearsAsDraws());
528             SkASSERT(stencil);
529             if (caps.discardStencilValuesAfterRenderPass()) {
530                 // Always clear the stencil if it is being discarded after render passes. This is
531                 // also an optimization because we are on a tiler and it avoids loading the values
532                 // from memory.
533                 stencilLoadOp = GrLoadOp::kClear;
534                 break;
535             }
536             if (!stencil->hasPerformedInitialClear()) {
537                 stencilLoadOp = GrLoadOp::kClear;
538                 stencil->markHasPerformedInitialClear();
539                 break;
540             }
541             // renderTargetContexts are required to leave the user stencil bits in a cleared state
542             // once finished, meaning the stencil values will always remain cleared after the
543             // initial clear. Just fall through to reloading the existing (cleared) stencil values
544             // from memory.
545         case StencilContent::kPreserved:
546             SkASSERT(stencil);
547             stencilLoadOp = GrLoadOp::kLoad;
548             break;
549     }
550 
551     // NOTE: If fMustPreserveStencil is set, then we are executing a renderTargetContext that split
552     // its opsTask.
553     //
554     // FIXME: We don't currently flag render passes that don't use stencil at all. In that case
555     // their store op might be "discard", and we currently make the assumption that a discard will
556     // not invalidate what's already in main memory. This is probably ok for now, but certainly
557     // something we want to address soon.
558     GrStoreOp stencilStoreOp = (caps.discardStencilValuesAfterRenderPass() && !fMustPreserveStencil)
559             ? GrStoreOp::kDiscard
560             : GrStoreOp::kStore;
561 
562     GrOpsRenderPass* renderPass = create_render_pass(
563             flushState->gpu(), proxy->peekRenderTarget(), fTargetView.origin(),
564             fClippedContentBounds, fColorLoadOp, fLoadClearColor, stencilLoadOp, stencilStoreOp,
565             fSampledProxies);
566     if (!renderPass) {
567         return false;
568     }
569     flushState->setOpsRenderPass(renderPass);
570     renderPass->begin();
571 
572     // Draw all the generated geometry.
573     for (const auto& chain : fOpChains) {
574         if (!chain.shouldExecute()) {
575             continue;
576         }
577 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
578         TRACE_EVENT0("skia.gpu", chain.head()->name());
579 #endif
580 
581         GrOpFlushState::OpArgs opArgs(chain.head(),
582                                       &fTargetView,
583                                       chain.appliedClip(),
584                                       chain.dstProxyView());
585 
586         flushState->setOpArgs(&opArgs);
587         chain.head()->execute(flushState, chain.bounds());
588         flushState->setOpArgs(nullptr);
589     }
590 
591     renderPass->end();
592     flushState->gpu()->submit(renderPass);
593     flushState->setOpsRenderPass(nullptr);
594 
595     return true;
596 }
597 
setColorLoadOp(GrLoadOp op,const SkPMColor4f & color)598 void GrOpsTask::setColorLoadOp(GrLoadOp op, const SkPMColor4f& color) {
599     fColorLoadOp = op;
600     fLoadClearColor = color;
601     if (GrLoadOp::kClear == fColorLoadOp) {
602         GrSurfaceProxy* proxy = fTargetView.proxy();
603         SkASSERT(proxy);
604         fTotalBounds = proxy->getBoundsRect();
605     }
606 }
607 
resetForFullscreenClear(CanDiscardPreviousOps canDiscardPreviousOps)608 bool GrOpsTask::resetForFullscreenClear(CanDiscardPreviousOps canDiscardPreviousOps) {
609     // If we previously recorded a wait op, we cannot delete the wait op. Until we track the wait
610     // ops separately from normal ops, we have to avoid clearing out any ops in this case as well.
611     if (fHasWaitOp) {
612         canDiscardPreviousOps = CanDiscardPreviousOps::kNo;
613     }
614 
615     if (CanDiscardPreviousOps::kYes == canDiscardPreviousOps || this->isEmpty()) {
616         this->deleteOps();
617         fDeferredProxies.reset();
618         fSampledProxies.reset();
619 
620         // If the opsTask is using a render target which wraps a vulkan command buffer, we can't do
621         // a clear load since we cannot change the render pass that we are using. Thus we fall back
622         // to making a clear op in this case.
623         return !fTargetView.asRenderTargetProxy()->wrapsVkSecondaryCB();
624     }
625 
626     // Could not empty the task, so an op must be added to handle the clear
627     return false;
628 }
629 
discard()630 void GrOpsTask::discard() {
631     // Discard calls to in-progress opsTasks are ignored. Calls at the start update the
632     // opsTasks' color & stencil load ops.
633     if (this->isEmpty()) {
634         fColorLoadOp = GrLoadOp::kDiscard;
635         fInitialStencilContent = StencilContent::kDontCare;
636         fTotalBounds.setEmpty();
637     }
638 }
639 
640 ////////////////////////////////////////////////////////////////////////////////
641 
642 #ifdef SK_DEBUG
dump(bool printDependencies) const643 void GrOpsTask::dump(bool printDependencies) const {
644     GrRenderTask::dump(printDependencies);
645 
646     SkDebugf("fColorLoadOp: ");
647     switch (fColorLoadOp) {
648         case GrLoadOp::kLoad:
649             SkDebugf("kLoad\n");
650             break;
651         case GrLoadOp::kClear:
652             SkDebugf("kClear (0x%x)\n", fLoadClearColor.toBytes_RGBA());
653             break;
654         case GrLoadOp::kDiscard:
655             SkDebugf("kDiscard\n");
656             break;
657     }
658 
659     SkDebugf("fInitialStencilContent: ");
660     switch (fInitialStencilContent) {
661         case StencilContent::kDontCare:
662             SkDebugf("kDontCare\n");
663             break;
664         case StencilContent::kUserBitsCleared:
665             SkDebugf("kUserBitsCleared\n");
666             break;
667         case StencilContent::kPreserved:
668             SkDebugf("kPreserved\n");
669             break;
670     }
671 
672     SkDebugf("ops (%d):\n", fOpChains.count());
673     for (int i = 0; i < fOpChains.count(); ++i) {
674         SkDebugf("*******************************\n");
675         if (!fOpChains[i].head()) {
676             SkDebugf("%d: <combined forward or failed instantiation>\n", i);
677         } else {
678             SkDebugf("%d: %s\n", i, fOpChains[i].head()->name());
679             SkRect bounds = fOpChains[i].bounds();
680             SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", bounds.fLeft,
681                      bounds.fTop, bounds.fRight, bounds.fBottom);
682             for (const auto& op : GrOp::ChainRange<>(fOpChains[i].head())) {
683                 SkString info = SkTabString(op.dumpInfo(), 1);
684                 SkDebugf("%s\n", info.c_str());
685                 bounds = op.bounds();
686                 SkDebugf("\tClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", bounds.fLeft,
687                          bounds.fTop, bounds.fRight, bounds.fBottom);
688             }
689         }
690     }
691 }
692 
visitProxies_debugOnly(const GrOp::VisitProxyFunc & func) const693 void GrOpsTask::visitProxies_debugOnly(const GrOp::VisitProxyFunc& func) const {
694     auto textureFunc = [ func ] (GrSurfaceProxy* tex, GrMipMapped mipmapped) {
695         func(tex, mipmapped);
696     };
697 
698     for (const OpChain& chain : fOpChains) {
699         chain.visitProxies(textureFunc);
700     }
701 }
702 
703 #endif
704 
705 ////////////////////////////////////////////////////////////////////////////////
706 
onIsUsed(GrSurfaceProxy * proxyToCheck) const707 bool GrOpsTask::onIsUsed(GrSurfaceProxy* proxyToCheck) const {
708     bool used = false;
709 
710     auto visit = [ proxyToCheck, &used ] (GrSurfaceProxy* p, GrMipMapped) {
711         if (p == proxyToCheck) {
712             used = true;
713         }
714     };
715     for (const OpChain& recordedOp : fOpChains) {
716         recordedOp.visitProxies(visit);
717     }
718 
719     return used;
720 }
721 
handleInternalAllocationFailure()722 void GrOpsTask::handleInternalAllocationFailure() {
723     bool hasUninstantiatedProxy = false;
724     auto checkInstantiation = [&hasUninstantiatedProxy](GrSurfaceProxy* p, GrMipMapped) {
725         if (!p->isInstantiated()) {
726             hasUninstantiatedProxy = true;
727         }
728     };
729     for (OpChain& recordedOp : fOpChains) {
730         hasUninstantiatedProxy = false;
731         recordedOp.visitProxies(checkInstantiation);
732         if (hasUninstantiatedProxy) {
733             recordedOp.setSkipExecuteFlag();
734         }
735     }
736 }
737 
gatherProxyIntervals(GrResourceAllocator * alloc) const738 void GrOpsTask::gatherProxyIntervals(GrResourceAllocator* alloc) const {
739     for (int i = 0; i < fDeferredProxies.count(); ++i) {
740         SkASSERT(!fDeferredProxies[i]->isInstantiated());
741         // We give all the deferred proxies a write usage at the very start of flushing. This
742         // locks them out of being reused for the entire flush until they are read - and then
743         // they can be recycled. This is a bit unfortunate because a flush can proceed in waves
744         // with sub-flushes. The deferred proxies only need to be pinned from the start of
745         // the sub-flush in which they appear.
746         alloc->addInterval(fDeferredProxies[i], 0, 0, GrResourceAllocator::ActualUse::kNo);
747     }
748 
749     GrSurfaceProxy* targetProxy = fTargetView.proxy();
750 
751     // Add the interval for all the writes to this GrOpsTasks's target
752     if (fOpChains.count()) {
753         unsigned int cur = alloc->curOp();
754 
755         alloc->addInterval(targetProxy, cur, cur + fOpChains.count() - 1,
756                            GrResourceAllocator::ActualUse::kYes);
757     } else {
758         // This can happen if there is a loadOp (e.g., a clear) but no other draws. In this case we
759         // still need to add an interval for the destination so we create a fake op# for
760         // the missing clear op.
761         alloc->addInterval(targetProxy, alloc->curOp(), alloc->curOp(),
762                            GrResourceAllocator::ActualUse::kYes);
763         alloc->incOps();
764     }
765 
766     auto gather = [ alloc SkDEBUGCODE(, this) ] (GrSurfaceProxy* p, GrMipMapped) {
767         alloc->addInterval(p, alloc->curOp(), alloc->curOp(), GrResourceAllocator::ActualUse::kYes
768                            SkDEBUGCODE(, fTargetView.proxy() == p));
769     };
770     for (const OpChain& recordedOp : fOpChains) {
771         recordedOp.visitProxies(gather);
772 
773         // Even though the op may have been (re)moved we still need to increment the op count to
774         // keep all the math consistent.
775         alloc->incOps();
776     }
777 }
778 
recordOp(std::unique_ptr<GrOp> op,GrProcessorSet::Analysis processorAnalysis,GrAppliedClip * clip,const DstProxyView * dstProxyView,const GrCaps & caps)779 void GrOpsTask::recordOp(
780         std::unique_ptr<GrOp> op, GrProcessorSet::Analysis processorAnalysis, GrAppliedClip* clip,
781         const DstProxyView* dstProxyView, const GrCaps& caps) {
782     SkDEBUGCODE(op->validate();)
783     SkASSERT(processorAnalysis.requiresDstTexture() == (dstProxyView && dstProxyView->proxy()));
784     GrSurfaceProxy* proxy = fTargetView.proxy();
785     SkASSERT(proxy);
786 
787     // A closed GrOpsTask should never receive new/more ops
788     SkASSERT(!this->isClosed());
789     if (!op->bounds().isFinite()) {
790         fArenas.opMemoryPool()->release(std::move(op));
791         return;
792     }
793 
794     // Account for this op's bounds before we attempt to combine.
795     // NOTE: The caller should have already called "op->setClippedBounds()" by now, if applicable.
796     fTotalBounds.join(op->bounds());
797 
798     // Check if there is an op we can combine with by linearly searching back until we either
799     // 1) check every op
800     // 2) intersect with something
801     // 3) find a 'blocker'
802     GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), proxy->uniqueID());
803     GrOP_INFO("opsTask: %d Recording (%s, opID: %u)\n"
804               "\tBounds [L: %.2f, T: %.2f R: %.2f B: %.2f]\n",
805                this->uniqueID(),
806                op->name(),
807                op->uniqueID(),
808                op->bounds().fLeft, op->bounds().fTop,
809                op->bounds().fRight, op->bounds().fBottom);
810     GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str());
811     GrOP_INFO("\tOutcome:\n");
812     int maxCandidates = std::min(kMaxOpChainDistance, fOpChains.count());
813     if (maxCandidates) {
814         int i = 0;
815         while (true) {
816             OpChain& candidate = fOpChains.fromBack(i);
817             op = candidate.appendOp(std::move(op), processorAnalysis, dstProxyView, clip, caps,
818                                     &fArenas, fAuditTrail);
819             if (!op) {
820                 return;
821             }
822             // Stop going backwards if we would cause a painter's order violation.
823             if (!can_reorder(candidate.bounds(), op->bounds())) {
824                 GrOP_INFO("\t\tBackward: Intersects with chain (%s, head opID: %u)\n",
825                           candidate.head()->name(), candidate.head()->uniqueID());
826                 break;
827             }
828             if (++i == maxCandidates) {
829                 GrOP_INFO("\t\tBackward: Reached max lookback or beginning of op array %d\n", i);
830                 break;
831             }
832         }
833     } else {
834         GrOP_INFO("\t\tBackward: FirstOp\n");
835     }
836     if (clip) {
837         clip = fClipAllocator.make<GrAppliedClip>(std::move(*clip));
838         SkDEBUGCODE(fNumClips++;)
839     }
840     fOpChains.emplace_back(std::move(op), processorAnalysis, clip, dstProxyView);
841 }
842 
forwardCombine(const GrCaps & caps)843 void GrOpsTask::forwardCombine(const GrCaps& caps) {
844     SkASSERT(!this->isClosed());
845     GrOP_INFO("opsTask: %d ForwardCombine %d ops:\n", this->uniqueID(), fOpChains.count());
846 
847     for (int i = 0; i < fOpChains.count() - 1; ++i) {
848         OpChain& chain = fOpChains[i];
849         int maxCandidateIdx = std::min(i + kMaxOpChainDistance, fOpChains.count() - 1);
850         int j = i + 1;
851         while (true) {
852             OpChain& candidate = fOpChains[j];
853             if (candidate.prependChain(&chain, caps, &fArenas, fAuditTrail)) {
854                 break;
855             }
856             // Stop traversing if we would cause a painter's order violation.
857             if (!can_reorder(chain.bounds(), candidate.bounds())) {
858                 GrOP_INFO(
859                         "\t\t%d: chain (%s head opID: %u) -> "
860                         "Intersects with chain (%s, head opID: %u)\n",
861                         i, chain.head()->name(), chain.head()->uniqueID(), candidate.head()->name(),
862                         candidate.head()->uniqueID());
863                 break;
864             }
865             if (++j > maxCandidateIdx) {
866                 GrOP_INFO("\t\t%d: chain (%s opID: %u) -> Reached max lookahead or end of array\n",
867                           i, chain.head()->name(), chain.head()->uniqueID());
868                 break;
869             }
870         }
871     }
872 }
873 
onMakeClosed(const GrCaps & caps,SkIRect * targetUpdateBounds)874 GrRenderTask::ExpectedOutcome GrOpsTask::onMakeClosed(
875         const GrCaps& caps, SkIRect* targetUpdateBounds) {
876     this->forwardCombine(caps);
877     if (!this->isNoOp()) {
878         GrSurfaceProxy* proxy = fTargetView.proxy();
879         SkRect clippedContentBounds = proxy->getBoundsRect();
880         // TODO: If we can fix up GLPrograms test to always intersect the fTargetView proxy bounds
881         // then we can simply assert here that the bounds intersect.
882         if (clippedContentBounds.intersect(fTotalBounds)) {
883             clippedContentBounds.roundOut(&fClippedContentBounds);
884             *targetUpdateBounds = fClippedContentBounds;
885             return ExpectedOutcome::kTargetDirty;
886         }
887     }
888     return ExpectedOutcome::kTargetUnchanged;
889 }
890