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