| /* | |
| * Copyright 2019 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/ganesh/GrRenderTask.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTypes.h" | |
| #include "src/gpu/ganesh/GrDrawingManager.h" | |
| #include "src/gpu/ganesh/GrRenderTargetProxy.h" | |
| #include "src/gpu/ganesh/GrSurface.h" | |
| #include "src/gpu/ganesh/GrSurfaceProxy.h" | |
| #include "src/gpu/ganesh/GrTextureProxy.h" | |
| #include "src/gpu/ganesh/GrTextureProxyPriv.h" | |
| #include "src/gpu/ganesh/GrTextureResolveManager.h" | |
| #include "src/gpu/ganesh/GrTextureResolveRenderTask.h" | |
| #include <algorithm> | |
| #include <atomic> | |
| #include <utility> | |
| uint32_t GrRenderTask::CreateUniqueID() { | |
| static std::atomic<uint32_t> nextID{1}; | |
| uint32_t id; | |
| do { | |
| id = nextID.fetch_add(1, std::memory_order_relaxed); | |
| } while (id == SK_InvalidUniqueID); | |
| return id; | |
| } | |
| GrRenderTask::GrRenderTask() | |
| : fUniqueID(CreateUniqueID()) | |
| , fFlags(0) { | |
| } | |
| void GrRenderTask::disown(GrDrawingManager* drawingMgr) { | |
| SkASSERT(!fDrawingMgr || drawingMgr == fDrawingMgr); | |
| SkASSERT(this->isClosed()); | |
| if (this->isSetFlag(kDisowned_Flag)) { | |
| return; | |
| } | |
| SkDEBUGCODE(fDrawingMgr = nullptr); | |
| this->setFlag(kDisowned_Flag); | |
| for (const sk_sp<GrSurfaceProxy>& target : fTargets) { | |
| if (this == drawingMgr->getLastRenderTask(target.get())) { | |
| drawingMgr->setLastRenderTask(target.get(), nullptr); | |
| } | |
| } | |
| } | |
| void GrRenderTask::makeSkippable() { | |
| SkASSERT(this->isClosed()); | |
| if (!this->isSkippable()) { | |
| this->setFlag(kSkippable_Flag); | |
| this->onMakeSkippable(); | |
| } | |
| } | |
| #ifdef SK_DEBUG | |
| GrRenderTask::~GrRenderTask() { | |
| SkASSERT(this->isSetFlag(kDisowned_Flag)); | |
| } | |
| bool GrRenderTask::deferredProxiesAreInstantiated() const { | |
| for (int i = 0; i < fDeferredProxies.size(); ++i) { | |
| if (!fDeferredProxies[i]->isInstantiated()) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| #endif | |
| void GrRenderTask::makeClosed(GrRecordingContext* rContext) { | |
| if (this->isClosed()) { | |
| return; | |
| } | |
| SkIRect targetUpdateBounds; | |
| if (ExpectedOutcome::kTargetDirty == this->onMakeClosed(rContext, &targetUpdateBounds)) { | |
| GrSurfaceProxy* proxy = this->target(0); | |
| if (proxy->requiresManualMSAAResolve()) { | |
| SkASSERT(this->target(0)->asRenderTargetProxy()); | |
| this->target(0)->asRenderTargetProxy()->markMSAADirty(targetUpdateBounds); | |
| } | |
| GrTextureProxy* textureProxy = this->target(0)->asTextureProxy(); | |
| if (textureProxy && skgpu::Mipmapped::kYes == textureProxy->mipmapped()) { | |
| textureProxy->markMipmapsDirty(); | |
| } | |
| } | |
| if (fTextureResolveTask) { | |
| this->addDependency(fTextureResolveTask); | |
| fTextureResolveTask->makeClosed(rContext); | |
| fTextureResolveTask = nullptr; | |
| } | |
| this->setFlag(kClosed_Flag); | |
| } | |
| void GrRenderTask::prepare(GrOpFlushState* flushState) { | |
| for (int i = 0; i < fDeferredProxies.size(); ++i) { | |
| fDeferredProxies[i]->texPriv().scheduleUpload(flushState); | |
| } | |
| this->onPrepare(flushState); | |
| } | |
| // Add a GrRenderTask-based dependency | |
| void GrRenderTask::addDependency(GrRenderTask* dependedOn) { | |
| SkASSERT(!dependedOn->dependsOn(this)); // loops are bad | |
| SkASSERT(!this->dependsOn(dependedOn)); // caller should weed out duplicates | |
| fDependencies.push_back(dependedOn); | |
| dependedOn->addDependent(this); | |
| SkDEBUGCODE(this->validate()); | |
| } | |
| void GrRenderTask::addDependenciesFromOtherTask(GrRenderTask* otherTask) { | |
| SkASSERT(otherTask); | |
| for (GrRenderTask* task : otherTask->fDependencies) { | |
| // The task should not be adding a dependency to itself. | |
| SkASSERT(task != this); | |
| if (!this->dependsOn(task)) { | |
| this->addDependency(task); | |
| } | |
| } | |
| } | |
| // Convert from a GrSurface-based dependency to a GrRenderTask one | |
| void GrRenderTask::addDependency(GrDrawingManager* drawingMgr, | |
| GrSurfaceProxy* dependedOn, | |
| skgpu::Mipmapped mipmapped, | |
| GrTextureResolveManager textureResolveManager, | |
| const GrCaps& caps) { | |
| // If it is still receiving dependencies, this GrRenderTask shouldn't be closed | |
| SkASSERT(!this->isClosed()); | |
| GrRenderTask* dependedOnTask = drawingMgr->getLastRenderTask(dependedOn); | |
| if (dependedOnTask == this) { | |
| // self-read - presumably for dst reads. We don't need to do anything in this case. The | |
| // XferProcessor will detect what is happening and insert a texture barrier. | |
| SkASSERT(skgpu::Mipmapped::kNo == mipmapped); | |
| // We should never attempt a self-read on a surface that has a separate MSAA renderbuffer. | |
| SkASSERT(!dependedOn->requiresManualMSAAResolve()); | |
| SkASSERT(!dependedOn->asTextureProxy() || | |
| !dependedOn->asTextureProxy()->texPriv().isDeferred()); | |
| return; | |
| } | |
| bool alreadyDependent = false; | |
| if (dependedOnTask) { | |
| if (this->dependsOn(dependedOnTask) || fTextureResolveTask == dependedOnTask) { | |
| alreadyDependent = true; | |
| dependedOnTask = nullptr; // don't add duplicate dependencies | |
| } else if (!dependedOnTask->isSetFlag(kAtlas_Flag)) { | |
| // We are closing 'dependedOnTask' here bc the current contents of it are what 'this' | |
| // renderTask depends on. We need a break in 'dependedOnTask' so that the usage of | |
| // that state has a chance to execute. | |
| dependedOnTask->makeClosed(drawingMgr->getContext()); | |
| } | |
| } | |
| auto resolveFlags = GrSurfaceProxy::ResolveFlags::kNone; | |
| if (dependedOn->requiresManualMSAAResolve()) { | |
| auto* renderTargetProxy = dependedOn->asRenderTargetProxy(); | |
| SkASSERT(renderTargetProxy); | |
| if (renderTargetProxy->isMSAADirty()) { | |
| resolveFlags |= GrSurfaceProxy::ResolveFlags::kMSAA; | |
| } | |
| } | |
| GrTextureProxy* textureProxy = dependedOn->asTextureProxy(); | |
| if (skgpu::Mipmapped::kYes == mipmapped) { | |
| SkASSERT(textureProxy); | |
| if (skgpu::Mipmapped::kYes != textureProxy->mipmapped()) { | |
| // There are some cases where we might be given a non-mipmapped texture with a mipmap | |
| // filter. See skbug.com/40038328. | |
| mipmapped = skgpu::Mipmapped::kNo; | |
| } else if (textureProxy->mipmapsAreDirty()) { | |
| resolveFlags |= GrSurfaceProxy::ResolveFlags::kMipMaps; | |
| } | |
| } | |
| // Does this proxy have msaa to resolve and/or mipmaps to regenerate? | |
| if (GrSurfaceProxy::ResolveFlags::kNone != resolveFlags) { | |
| if (!fTextureResolveTask) { | |
| fTextureResolveTask = textureResolveManager.newTextureResolveRenderTask(caps); | |
| } | |
| fTextureResolveTask->addProxy(drawingMgr, sk_ref_sp(dependedOn), resolveFlags, caps); | |
| // addProxy() should have closed the texture proxy's previous task. | |
| SkASSERT(!dependedOnTask || dependedOnTask->isClosed()); | |
| SkASSERT(drawingMgr->getLastRenderTask(dependedOn) == fTextureResolveTask); | |
| #ifdef SK_DEBUG | |
| // addProxy() should have called addDependency (in this instance, recursively) on | |
| // fTextureResolveTask. | |
| if (dependedOnTask) { | |
| SkASSERT(fTextureResolveTask->dependsOn(dependedOnTask)); | |
| } | |
| if (textureProxy && textureProxy->texPriv().isDeferred()) { | |
| SkASSERT(fTextureResolveTask->fDeferredProxies.back() == textureProxy); | |
| } | |
| // The GrTextureResolveRenderTask factory should have also marked the proxy clean, set the | |
| // last renderTask on the textureProxy to textureResolveTask, and closed textureResolveTask. | |
| if (resolveFlags & GrSurfaceProxy::ResolveFlags::kMSAA) { | |
| if (GrRenderTargetProxy* renderTargetProxy = dependedOn->asRenderTargetProxy()) { | |
| SkASSERT(!renderTargetProxy->isMSAADirty()); | |
| } | |
| } | |
| if (textureProxy && (resolveFlags & GrSurfaceProxy::ResolveFlags::kMipMaps)) { | |
| SkASSERT(!textureProxy->mipmapsAreDirty()); | |
| } | |
| SkASSERT(drawingMgr->getLastRenderTask(dependedOn) == fTextureResolveTask); | |
| #endif | |
| return; | |
| } | |
| if (textureProxy && textureProxy->texPriv().isDeferred()) { | |
| if (alreadyDependent) { | |
| SkASSERT(std::find(fDeferredProxies.begin(), fDeferredProxies.end(), textureProxy) != | |
| fDeferredProxies.end()); | |
| } else { | |
| fDeferredProxies.push_back(textureProxy); | |
| } | |
| } | |
| if (dependedOnTask) { | |
| this->addDependency(dependedOnTask); | |
| } | |
| } | |
| void GrRenderTask::replaceDependency(const GrRenderTask* toReplace, GrRenderTask* replaceWith) { | |
| for (auto& target : fDependencies) { | |
| if (target == toReplace) { | |
| target = replaceWith; | |
| replaceWith->fDependents.push_back(this); | |
| break; | |
| } | |
| } | |
| } | |
| void GrRenderTask::replaceDependent(const GrRenderTask* toReplace, GrRenderTask* replaceWith) { | |
| for (auto& target : fDependents) { | |
| if (target == toReplace) { | |
| target = replaceWith; | |
| replaceWith->fDependencies.push_back(this); | |
| break; | |
| } | |
| } | |
| } | |
| bool GrRenderTask::dependsOn(const GrRenderTask* dependedOn) const { | |
| for (int i = 0; i < fDependencies.size(); ++i) { | |
| if (fDependencies[i] == dependedOn) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void GrRenderTask::addDependent(GrRenderTask* dependent) { | |
| fDependents.push_back(dependent); | |
| } | |
| #ifdef SK_DEBUG | |
| bool GrRenderTask::isDependent(const GrRenderTask* dependent) const { | |
| for (int i = 0; i < fDependents.size(); ++i) { | |
| if (fDependents[i] == dependent) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void GrRenderTask::validate() const { | |
| // TODO: check for loops and duplicates | |
| for (int i = 0; i < fDependencies.size(); ++i) { | |
| SkASSERT(fDependencies[i]->isDependent(this)); | |
| } | |
| } | |
| #endif | |
| bool GrRenderTask::isInstantiated() const { | |
| for (const sk_sp<GrSurfaceProxy>& target : fTargets) { | |
| GrSurfaceProxy* proxy = target.get(); | |
| if (!proxy->isInstantiated()) { | |
| return false; | |
| } | |
| GrSurface* surface = proxy->peekSurface(); | |
| if (surface->wasDestroyed()) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| void GrRenderTask::addTarget(GrDrawingManager* drawingMgr, sk_sp<GrSurfaceProxy> proxy) { | |
| SkASSERT(proxy); | |
| SkASSERT(!this->isClosed()); | |
| SkASSERT(!fDrawingMgr || drawingMgr == fDrawingMgr); | |
| SkDEBUGCODE(fDrawingMgr = drawingMgr); | |
| drawingMgr->setLastRenderTask(proxy.get(), this); | |
| proxy->isUsedAsTaskTarget(); | |
| fTargets.emplace_back(std::move(proxy)); | |
| } | |
| #if defined(GPU_TEST_UTILS) | |
| void GrRenderTask::dump(const SkString& label, | |
| SkString indent, | |
| bool printDependencies, | |
| bool close) const { | |
| SkDebugf("%s%s --------------------------------------------------------------\n", | |
| indent.c_str(), | |
| label.c_str()); | |
| SkDebugf("%s%s task - renderTaskID: %u\n", indent.c_str(), this->name(), fUniqueID); | |
| if (!fTargets.empty()) { | |
| SkDebugf("%sTargets: \n", indent.c_str()); | |
| for (const sk_sp<GrSurfaceProxy>& target : fTargets) { | |
| SkASSERT(target); | |
| SkString proxyStr = target->dump(); | |
| SkDebugf("%s%s\n", indent.c_str(), proxyStr.c_str()); | |
| } | |
| } | |
| if (printDependencies) { | |
| SkDebugf("%sI rely On (%d): ", indent.c_str(), fDependencies.size()); | |
| for (int i = 0; i < fDependencies.size(); ++i) { | |
| SkDebugf("%u, ", fDependencies[i]->fUniqueID); | |
| } | |
| SkDebugf("\n"); | |
| SkDebugf("%s(%d) Rely On Me: ", indent.c_str(), fDependents.size()); | |
| for (int i = 0; i < fDependents.size(); ++i) { | |
| SkDebugf("%u, ", fDependents[i]->fUniqueID); | |
| } | |
| SkDebugf("\n"); | |
| } | |
| if (close) { | |
| SkDebugf("%s--------------------------------------------------------------\n\n", | |
| indent.c_str()); | |
| } | |
| } | |
| #endif |