/* * 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 "GrResourceAllocator.h" #include "GrTextureProxy.h" #include "SkStringUtils.h" #include "ops/GrCopySurfaceOp.h" //////////////////////////////////////////////////////////////////////////////// GrTextureOpList::GrTextureOpList(GrResourceProvider* resourceProvider, sk_sp 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: \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(GrContext* context, GrSurfaceProxy* dst, GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { SkASSERT(dst == fTarget.get()); std::unique_ptr op = GrCopySurfaceOp::Make(context, dst, src, srcRect, dstPoint); if (!op) { return false; } const GrCaps* caps = context->contextPriv().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 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)); }