• 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 "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 
GrRenderTargetOpList(GrRenderTargetProxy * proxy,GrResourceProvider * resourceProvider,GrAuditTrail * auditTrail)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 
~GrRenderTargetOpList()35 GrRenderTargetOpList::~GrRenderTargetOpList() {
36 }
37 
38 ////////////////////////////////////////////////////////////////////////////////
39 
40 #ifdef SK_DEBUG
dump() const41 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 
visitProxies_debugOnly(const GrOp::VisitProxyFunc & func) const60 void GrRenderTargetOpList::visitProxies_debugOnly(const GrOp::VisitProxyFunc& func) const {
61     for (const RecordedOp& recordedOp : fRecordedOps) {
62         recordedOp.visitProxies(func);
63     }
64 }
65 #endif
66 
onPrepare(GrOpFlushState * flushState)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 
create_command_buffer(GrGpu * gpu,GrRenderTarget * rt,GrSurfaceOrigin origin,GrLoadOp colorLoadOp,GrColor loadClearColor,GrLoadOp stencilLoadOp)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 
finish_command_buffer(GrGpuRTCommandBuffer * buffer)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.
onExecute(GrOpFlushState * flushState)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 
endFlush()183 void GrRenderTargetOpList::endFlush() {
184     fLastClipStackGenID = SK_InvalidUniqueID;
185     fRecordedOps.reset();
186     fClipAllocator.reset();
187     INHERITED::endFlush();
188 }
189 
discard()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 
fullClear(const GrCaps & caps,GrColor color)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
copySurface(const GrCaps & caps,GrSurfaceProxy * dst,GrSurfaceProxy * src,const SkIRect & srcRect,const SkIPoint & dstPoint)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 
purgeOpsWithUninstantiatedProxies()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 
gatherProxyIntervals(GrResourceAllocator * alloc) const258 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 
can_reorder(const SkRect & a,const SkRect & b)294 static inline bool can_reorder(const SkRect& a, const SkRect& b) { return !GrRectsOverlap(a, b); }
295 
combineIfPossible(const RecordedOp & a,GrOp * b,const GrAppliedClip * bClip,const DstProxy * bDstProxy,const GrCaps & caps)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 
recordOp(std::unique_ptr<GrOp> op,const GrCaps & caps,GrAppliedClip * clip,const DstProxy * dstProxy)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 
forwardCombine(const GrCaps & caps)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