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 "instanced/InstancedRendering.h" 16 #include "ops/GrClearOp.h" 17 #include "ops/GrCopySurfaceOp.h" 18 19 using gr_instanced::InstancedRendering; 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, GrGpu* gpu, 28 GrAuditTrail* auditTrail) 29 : INHERITED(gpu->getContext()->resourceProvider(), proxy, auditTrail) 30 , fLastClipStackGenID(SK_InvalidUniqueID) 31 SkDEBUGCODE(, fNumClips(0)) { 32 if (GrCaps::InstancedSupport::kNone != gpu->caps()->instancedSupport()) { 33 fInstancedRendering.reset(gpu->createInstancedRendering()); 34 } 35 } 36 37 GrRenderTargetOpList::~GrRenderTargetOpList() { 38 } 39 40 //////////////////////////////////////////////////////////////////////////////// 41 42 #ifdef SK_DEBUG 43 void GrRenderTargetOpList::dump() const { 44 INHERITED::dump(); 45 46 SkDebugf("ops (%d):\n", fRecordedOps.count()); 47 for (int i = 0; i < fRecordedOps.count(); ++i) { 48 SkDebugf("*******************************\n"); 49 if (!fRecordedOps[i].fOp) { 50 SkDebugf("%d: <combined forward>\n", i); 51 } else { 52 SkDebugf("%d: %s\n", i, fRecordedOps[i].fOp->name()); 53 SkString str = fRecordedOps[i].fOp->dumpInfo(); 54 SkDebugf("%s\n", str.c_str()); 55 const SkRect& bounds = fRecordedOps[i].fOp->bounds(); 56 SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", bounds.fLeft, 57 bounds.fTop, bounds.fRight, bounds.fBottom); 58 } 59 } 60 } 61 #endif 62 63 void GrRenderTargetOpList::prepareOps(GrOpFlushState* flushState) { 64 SkASSERT(fTarget.get()->priv().peekRenderTarget()); 65 SkASSERT(this->isClosed()); 66 67 // Loop over the ops that haven't yet been prepared. 68 for (int i = 0; i < fRecordedOps.count(); ++i) { 69 if (fRecordedOps[i].fOp) { 70 GrOpFlushState::DrawOpArgs opArgs = { 71 fTarget.get()->priv().peekRenderTarget(), 72 fRecordedOps[i].fAppliedClip, 73 fRecordedOps[i].fDstProxy 74 }; 75 76 flushState->setDrawOpArgs(&opArgs); 77 fRecordedOps[i].fOp->prepare(flushState); 78 flushState->setDrawOpArgs(nullptr); 79 } 80 } 81 82 if (fInstancedRendering) { 83 fInstancedRendering->beginFlush(flushState->resourceProvider()); 84 } 85 } 86 87 static std::unique_ptr<GrGpuCommandBuffer> create_command_buffer(GrGpu* gpu) { 88 static const GrGpuCommandBuffer::LoadAndStoreInfo kBasicLoadStoreInfo { 89 GrGpuCommandBuffer::LoadOp::kLoad, 90 GrGpuCommandBuffer::StoreOp::kStore, 91 GrColor_ILLEGAL 92 }; 93 94 std::unique_ptr<GrGpuCommandBuffer> buffer( 95 gpu->createCommandBuffer(kBasicLoadStoreInfo, // Color 96 kBasicLoadStoreInfo)); // Stencil 97 return buffer; 98 } 99 100 static inline void finish_command_buffer(GrGpuCommandBuffer* buffer) { 101 if (!buffer) { 102 return; 103 } 104 105 buffer->end(); 106 buffer->submit(); 107 } 108 109 // TODO: this is where GrOp::renderTarget is used (which is fine since it 110 // is at flush time). However, we need to store the RenderTargetProxy in the 111 // Ops and instantiate them here. 112 bool GrRenderTargetOpList::executeOps(GrOpFlushState* flushState) { 113 if (0 == fRecordedOps.count()) { 114 return false; 115 } 116 117 SkASSERT(fTarget.get()->priv().peekRenderTarget()); 118 119 std::unique_ptr<GrGpuCommandBuffer> commandBuffer = create_command_buffer(flushState->gpu()); 120 flushState->setCommandBuffer(commandBuffer.get()); 121 122 // Draw all the generated geometry. 123 for (int i = 0; i < fRecordedOps.count(); ++i) { 124 if (!fRecordedOps[i].fOp) { 125 continue; 126 } 127 128 if (fRecordedOps[i].fOp->needsCommandBufferIsolation()) { 129 // This op is a special snowflake and must occur between command buffers 130 // TODO: make this go through the command buffer 131 finish_command_buffer(commandBuffer.get()); 132 133 commandBuffer.reset(); 134 flushState->setCommandBuffer(commandBuffer.get()); 135 } else if (!commandBuffer) { 136 commandBuffer = create_command_buffer(flushState->gpu()); 137 flushState->setCommandBuffer(commandBuffer.get()); 138 } 139 140 GrOpFlushState::DrawOpArgs opArgs { 141 fTarget.get()->priv().peekRenderTarget(), 142 fRecordedOps[i].fAppliedClip, 143 fRecordedOps[i].fDstProxy 144 }; 145 146 flushState->setDrawOpArgs(&opArgs); 147 fRecordedOps[i].fOp->execute(flushState); 148 flushState->setDrawOpArgs(nullptr); 149 } 150 151 finish_command_buffer(commandBuffer.get()); 152 flushState->setCommandBuffer(nullptr); 153 154 return true; 155 } 156 157 void GrRenderTargetOpList::reset() { 158 fLastFullClearOp = nullptr; 159 fLastClipStackGenID = SK_InvalidUniqueID; 160 fRecordedOps.reset(); 161 if (fInstancedRendering) { 162 fInstancedRendering->endFlush(); 163 fInstancedRendering = nullptr; 164 } 165 166 INHERITED::reset(); 167 } 168 169 void GrRenderTargetOpList::abandonGpuResources() { 170 if (fInstancedRendering) { 171 fInstancedRendering->resetGpuResources(InstancedRendering::ResetType::kAbandon); 172 } 173 } 174 175 void GrRenderTargetOpList::freeGpuResources() { 176 if (fInstancedRendering) { 177 fInstancedRendering->resetGpuResources(InstancedRendering::ResetType::kDestroy); 178 } 179 } 180 181 void GrRenderTargetOpList::fullClear(const GrCaps& caps, GrColor color) { 182 // Currently this just inserts or updates the last clear op. However, once in MDB this can 183 // remove all the previously recorded ops and change the load op to clear with supplied 184 // color. 185 if (fLastFullClearOp) { 186 // As currently implemented, fLastFullClearOp should be the last op because we would 187 // have cleared it when another op was recorded. 188 SkASSERT(fRecordedOps.back().fOp.get() == fLastFullClearOp); 189 GrOP_INFO("opList: %d Fusing clears (opID: %d Color: 0x%08x -> 0x%08x)\n", 190 this->uniqueID(), 191 fLastFullClearOp->uniqueID(), 192 fLastFullClearOp->color(), color); 193 fLastFullClearOp->setColor(color); 194 return; 195 } 196 std::unique_ptr<GrClearOp> op(GrClearOp::Make(GrFixedClip::Disabled(), color, fTarget.get())); 197 if (!op) { 198 return; 199 } 200 201 if (GrOp* clearOp = this->recordOp(std::move(op), caps)) { 202 // This is either the clear op we just created or another one that it combined with. 203 fLastFullClearOp = static_cast<GrClearOp*>(clearOp); 204 } 205 } 206 207 //////////////////////////////////////////////////////////////////////////////// 208 209 // This closely parallels GrTextureOpList::copySurface but renderTargetOpLists 210 // also store the applied clip and dest proxy with the op 211 bool GrRenderTargetOpList::copySurface(const GrCaps& caps, 212 GrSurfaceProxy* dst, 213 GrSurfaceProxy* src, 214 const SkIRect& srcRect, 215 const SkIPoint& dstPoint) { 216 SkASSERT(dst->asRenderTargetProxy() == fTarget.get()); 217 std::unique_ptr<GrOp> op = GrCopySurfaceOp::Make(dst, src, srcRect, dstPoint); 218 if (!op) { 219 return false; 220 } 221 #ifdef ENABLE_MDB 222 this->addDependency(src); 223 #endif 224 225 this->recordOp(std::move(op), caps); 226 return true; 227 } 228 229 static inline bool can_reorder(const SkRect& a, const SkRect& b) { return !GrRectsOverlap(a, b); } 230 231 bool GrRenderTargetOpList::combineIfPossible(const RecordedOp& a, GrOp* b, 232 const GrAppliedClip* bClip, 233 const DstProxy* bDstProxy, 234 const GrCaps& caps) { 235 if (a.fAppliedClip) { 236 if (!bClip) { 237 return false; 238 } 239 if (*a.fAppliedClip != *bClip) { 240 return false; 241 } 242 } else if (bClip) { 243 return false; 244 } 245 if (bDstProxy) { 246 if (a.fDstProxy != *bDstProxy) { 247 return false; 248 } 249 } else if (a.fDstProxy.proxy()) { 250 return false; 251 } 252 return a.fOp->combineIfPossible(b, caps); 253 } 254 255 GrOp* GrRenderTargetOpList::recordOp(std::unique_ptr<GrOp> op, 256 const GrCaps& caps, 257 GrAppliedClip* clip, 258 const DstProxy* dstProxy) { 259 SkASSERT(fTarget.get()); 260 261 // A closed GrOpList should never receive new/more ops 262 SkASSERT(!this->isClosed()); 263 264 // Check if there is an op we can combine with by linearly searching back until we either 265 // 1) check every op 266 // 2) intersect with something 267 // 3) find a 'blocker' 268 GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), fTarget.get()->uniqueID()); 269 GrOP_INFO("opList: %d Recording (%s, opID: %u)\n" 270 "\tBounds [L: %.2f, T: %.2f R: %.2f B: %.2f]\n", 271 this->uniqueID(), 272 op->name(), 273 op->uniqueID(), 274 op->bounds().fLeft, op->bounds().fTop, 275 op->bounds().fRight, op->bounds().fBottom); 276 GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str()); 277 GrOP_INFO("\tOutcome:\n"); 278 int maxCandidates = SkTMin(kMaxOpLookback, fRecordedOps.count()); 279 // If we don't have a valid destination render target then we cannot reorder. 280 if (maxCandidates) { 281 int i = 0; 282 while (true) { 283 const RecordedOp& candidate = fRecordedOps.fromBack(i); 284 285 if (this->combineIfPossible(candidate, op.get(), clip, dstProxy, caps)) { 286 GrOP_INFO("\t\tBackward: Combining with (%s, opID: %u)\n", candidate.fOp->name(), 287 candidate.fOp->uniqueID()); 288 GrOP_INFO("\t\t\tBackward: Combined op info:\n"); 289 GrOP_INFO(SkTabString(candidate.fOp->dumpInfo(), 4).c_str()); 290 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, candidate.fOp.get(), op.get()); 291 return candidate.fOp.get(); 292 } 293 // Stop going backwards if we would cause a painter's order violation. 294 if (!can_reorder(fRecordedOps.fromBack(i).fOp->bounds(), op->bounds())) { 295 GrOP_INFO("\t\tBackward: Intersects with (%s, opID: %u)\n", candidate.fOp->name(), 296 candidate.fOp->uniqueID()); 297 break; 298 } 299 ++i; 300 if (i == maxCandidates) { 301 GrOP_INFO("\t\tBackward: Reached max lookback or beginning of op array %d\n", i); 302 break; 303 } 304 } 305 } else { 306 GrOP_INFO("\t\tBackward: FirstOp\n"); 307 } 308 GR_AUDIT_TRAIL_OP_RESULT_NEW(fAuditTrail, op); 309 if (clip) { 310 clip = fClipAllocator.make<GrAppliedClip>(std::move(*clip)); 311 SkDEBUGCODE(fNumClips++;) 312 } 313 fRecordedOps.emplace_back(std::move(op), clip, dstProxy); 314 fRecordedOps.back().fOp->wasRecorded(this); 315 fLastFullClearOp = nullptr; 316 return fRecordedOps.back().fOp.get(); 317 } 318 319 void GrRenderTargetOpList::forwardCombine(const GrCaps& caps) { 320 SkASSERT(!this->isClosed()); 321 322 GrOP_INFO("opList: %d ForwardCombine %d ops:\n", this->uniqueID(), fRecordedOps.count()); 323 324 for (int i = 0; i < fRecordedOps.count() - 1; ++i) { 325 GrOp* op = fRecordedOps[i].fOp.get(); 326 327 int maxCandidateIdx = SkTMin(i + kMaxOpLookahead, fRecordedOps.count() - 1); 328 int j = i + 1; 329 while (true) { 330 const RecordedOp& candidate = fRecordedOps[j]; 331 332 if (this->combineIfPossible(fRecordedOps[i], candidate.fOp.get(), 333 candidate.fAppliedClip, &candidate.fDstProxy, caps)) { 334 GrOP_INFO("\t\t%d: (%s opID: %u) -> Combining with (%s, opID: %u)\n", 335 i, op->name(), op->uniqueID(), 336 candidate.fOp->name(), candidate.fOp->uniqueID()); 337 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, op, candidate.fOp.get()); 338 fRecordedOps[j].fOp = std::move(fRecordedOps[i].fOp); 339 break; 340 } 341 // Stop traversing if we would cause a painter's order violation. 342 if (!can_reorder(fRecordedOps[j].fOp->bounds(), op->bounds())) { 343 GrOP_INFO("\t\t%d: (%s opID: %u) -> Intersects with (%s, opID: %u)\n", 344 i, op->name(), op->uniqueID(), 345 candidate.fOp->name(), candidate.fOp->uniqueID()); 346 break; 347 } 348 ++j; 349 if (j > maxCandidateIdx) { 350 GrOP_INFO("\t\t%d: (%s opID: %u) -> Reached max lookahead or end of array\n", 351 i, op->name(), op->uniqueID()); 352 break; 353 } 354 } 355 } 356 } 357 358