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