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 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 50 GrRenderTargetOpList::~GrRenderTargetOpList() { 51 fGpu->unref(); 52 } 53 54 //////////////////////////////////////////////////////////////////////////////// 55 56 #ifdef SK_DEBUG 57 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 76 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 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. 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 170 void GrRenderTargetOpList::reset() { 171 fLastFullClearOp = nullptr; 172 fLastFullClearRenderTargetID.makeInvalid(); 173 fRecordedOps.reset(); 174 if (fInstancedRendering) { 175 fInstancedRendering->endFlush(); 176 } 177 } 178 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 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 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 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 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 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 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 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 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 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