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 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 35 GrRenderTargetOpList::~GrRenderTargetOpList() { 36 } 37 38 //////////////////////////////////////////////////////////////////////////////// 39 40 #ifdef SK_DEBUG 41 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 60 void GrRenderTargetOpList::visitProxies_debugOnly(const GrOp::VisitProxyFunc& func) const { 61 for (const RecordedOp& recordedOp : fRecordedOps) { 62 recordedOp.visitProxies(func); 63 } 64 } 65 #endif 66 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 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 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. 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 183 void GrRenderTargetOpList::endFlush() { 184 fLastClipStackGenID = SK_InvalidUniqueID; 185 fRecordedOps.reset(); 186 fClipAllocator.reset(); 187 INHERITED::endFlush(); 188 } 189 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 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 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 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 258 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 294 static inline bool can_reorder(const SkRect& a, const SkRect& b) { return !GrRectsOverlap(a, b); } 295 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 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 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