| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "GrTextureOpList.h" |
| |
| #include "GrAuditTrail.h" |
| #include "GrContext.h" |
| #include "GrContextPriv.h" |
| #include "GrGpu.h" |
| #include "GrMemoryPool.h" |
| #include "GrRecordingContext.h" |
| #include "GrRecordingContextPriv.h" |
| #include "GrResourceAllocator.h" |
| #include "GrTextureProxy.h" |
| #include "SkStringUtils.h" |
| #include "ops/GrCopySurfaceOp.h" |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| GrTextureOpList::GrTextureOpList(GrResourceProvider* resourceProvider, |
| sk_sp<GrOpMemoryPool> opMemoryPool, |
| GrTextureProxy* proxy, |
| GrAuditTrail* auditTrail) |
| : INHERITED(resourceProvider, std::move(opMemoryPool), proxy, auditTrail) { |
| SkASSERT(fOpMemoryPool); |
| SkASSERT(!proxy->readOnly()); |
| } |
| |
| void GrTextureOpList::deleteOp(int index) { |
| SkASSERT(index >= 0 && index < fRecordedOps.count()); |
| fOpMemoryPool->release(std::move(fRecordedOps[index])); |
| } |
| |
| void GrTextureOpList::deleteOps() { |
| for (int i = 0; i < fRecordedOps.count(); ++i) { |
| if (fRecordedOps[i]) { |
| fOpMemoryPool->release(std::move(fRecordedOps[i])); |
| } |
| } |
| fRecordedOps.reset(); |
| fOpMemoryPool = nullptr; |
| } |
| |
| GrTextureOpList::~GrTextureOpList() { |
| this->deleteOps(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| #ifdef SK_DEBUG |
| void GrTextureOpList::dump(bool printDependencies) const { |
| INHERITED::dump(printDependencies); |
| |
| SkDebugf("ops (%d):\n", fRecordedOps.count()); |
| for (int i = 0; i < fRecordedOps.count(); ++i) { |
| if (!fRecordedOps[i]) { |
| SkDebugf("%d: <failed instantiation>\n", i); |
| } else { |
| SkDebugf("*******************************\n"); |
| SkDebugf("%d: %s\n", i, fRecordedOps[i]->name()); |
| SkString str = fRecordedOps[i]->dumpInfo(); |
| SkDebugf("%s\n", str.c_str()); |
| const SkRect& clippedBounds = fRecordedOps[i]->bounds(); |
| SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", |
| clippedBounds.fLeft, clippedBounds.fTop, clippedBounds.fRight, |
| clippedBounds.fBottom); |
| } |
| } |
| } |
| |
| #endif |
| |
| void GrTextureOpList::onPrepare(GrOpFlushState* flushState) { |
| SkASSERT(fTarget.get()->peekTexture()); |
| SkASSERT(this->isClosed()); |
| |
| // Loop over the ops that haven't yet generated their geometry |
| for (int i = 0; i < fRecordedOps.count(); ++i) { |
| if (fRecordedOps[i]) { |
| SkASSERT(fRecordedOps[i]->isChainHead()); |
| GrOpFlushState::OpArgs opArgs = { |
| fRecordedOps[i].get(), |
| nullptr, |
| nullptr, |
| GrXferProcessor::DstProxy() |
| }; |
| flushState->setOpArgs(&opArgs); |
| fRecordedOps[i]->prepare(flushState); |
| flushState->setOpArgs(nullptr); |
| } |
| } |
| } |
| |
| bool GrTextureOpList::onExecute(GrOpFlushState* flushState) { |
| if (0 == fRecordedOps.count()) { |
| return false; |
| } |
| |
| SkASSERT(fTarget.get()->peekTexture()); |
| |
| GrGpuTextureCommandBuffer* commandBuffer( |
| flushState->gpu()->getCommandBuffer(fTarget.get()->peekTexture(), |
| fTarget.get()->origin())); |
| flushState->setCommandBuffer(commandBuffer); |
| |
| for (int i = 0; i < fRecordedOps.count(); ++i) { |
| if (!fRecordedOps[i]) { |
| continue; |
| } |
| SkASSERT(fRecordedOps[i]->isChainHead()); |
| GrOpFlushState::OpArgs opArgs = { |
| fRecordedOps[i].get(), |
| nullptr, |
| nullptr, |
| GrXferProcessor::DstProxy() |
| }; |
| flushState->setOpArgs(&opArgs); |
| fRecordedOps[i]->execute(flushState, fRecordedOps[i].get()->bounds()); |
| flushState->setOpArgs(nullptr); |
| } |
| |
| flushState->gpu()->submit(commandBuffer); |
| flushState->setCommandBuffer(nullptr); |
| |
| return true; |
| } |
| |
| void GrTextureOpList::endFlush() { |
| this->deleteOps(); |
| INHERITED::endFlush(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // This closely parallels GrRenderTargetOpList::copySurface but renderTargetOpList |
| // stores extra data with the op |
| bool GrTextureOpList::copySurface(GrRecordingContext* context, |
| GrSurfaceProxy* dst, |
| GrSurfaceProxy* src, |
| const SkIRect& srcRect, |
| const SkIPoint& dstPoint) { |
| SkASSERT(dst == fTarget.get()); |
| |
| std::unique_ptr<GrOp> op = GrCopySurfaceOp::Make(context, dst, src, srcRect, dstPoint); |
| if (!op) { |
| return false; |
| } |
| |
| const GrCaps* caps = context->priv().caps(); |
| auto addDependency = [ caps, this ] (GrSurfaceProxy* p) { |
| this->addDependency(p, *caps); |
| }; |
| op->visitProxies(addDependency); |
| |
| this->recordOp(std::move(op)); |
| return true; |
| } |
| |
| void GrTextureOpList::purgeOpsWithUninstantiatedProxies() { |
| bool hasUninstantiatedProxy = false; |
| auto checkInstantiation = [&hasUninstantiatedProxy](GrSurfaceProxy* p) { |
| if (!p->isInstantiated()) { |
| hasUninstantiatedProxy = true; |
| } |
| }; |
| for (int i = 0; i < fRecordedOps.count(); ++i) { |
| const GrOp* op = fRecordedOps[i].get(); // only diff from the GrRenderTargetOpList version |
| hasUninstantiatedProxy = false; |
| if (op) { |
| op->visitProxies(checkInstantiation); |
| } |
| if (hasUninstantiatedProxy) { |
| // When instantiation of the proxy fails we drop the Op |
| this->deleteOp(i); |
| } |
| } |
| } |
| |
| void GrTextureOpList::gatherProxyIntervals(GrResourceAllocator* alloc) const { |
| unsigned int cur = alloc->numOps(); |
| |
| // Add the interval for all the writes to this opList's target |
| if (fRecordedOps.count()) { |
| alloc->addInterval(fTarget.get(), cur, cur+fRecordedOps.count()-1); |
| } else { |
| // This can happen if there is a loadOp (e.g., a clear) but no other draws. In this case we |
| // still need to add an interval for the destination so we create a fake op# for |
| // the missing clear op. |
| alloc->addInterval(fTarget.get()); |
| alloc->incOps(); |
| } |
| |
| auto gather = [ alloc SkDEBUGCODE(, this) ] (GrSurfaceProxy* p) { |
| alloc->addInterval(p SkDEBUGCODE(, p == fTarget.get())); |
| }; |
| for (int i = 0; i < fRecordedOps.count(); ++i) { |
| const GrOp* op = fRecordedOps[i].get(); // only diff from the GrRenderTargetOpList version |
| if (op) { |
| op->visitProxies(gather, GrOp::VisitorType::kAllocatorGather); |
| } |
| |
| // Even though the op may have been moved we still need to increment the op count to |
| // keep all the math consistent. |
| alloc->incOps(); |
| } |
| } |
| |
| void GrTextureOpList::recordOp(std::unique_ptr<GrOp> op) { |
| SkASSERT(fTarget.get()); |
| // A closed GrOpList should never receive new/more ops |
| SkASSERT(!this->isClosed()); |
| |
| GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), fTarget.get()->uniqueID()); |
| GrOP_INFO("Re-Recording (%s, opID: %u)\n" |
| "\tBounds LRTB (%f, %f, %f, %f)\n", |
| op->name(), |
| op->uniqueID(), |
| op->bounds().fLeft, op->bounds().fRight, |
| op->bounds().fTop, op->bounds().fBottom); |
| GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str()); |
| |
| fRecordedOps.emplace_back(std::move(op)); |
| } |