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