Reorder msaa and mipmap resolves to happen all at once
Makes it so every renderTask has only one textureResolveTask, and
modifies GrTextureResolveTask to perform multiple resolves
back-to-back.
Bug: skia:9406
Change-Id: I93566cf4b23764bd846a1e0a0848642c9b3a507a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/241936
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 6f3dfdd..42db1eb 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -626,7 +626,11 @@
for (int i = 0; i < fDAG.numRenderTasks(); ++i) {
if (fActiveOpsTask != fDAG.renderTask(i)) {
- SkASSERT(fDAG.renderTask(i)->isClosed());
+ // The resolveTask associated with the activeTask remains open for as long as the
+ // activeTask does.
+ bool isActiveResolveTask =
+ fActiveOpsTask && fActiveOpsTask->fTextureResolveTask == fDAG.renderTask(i);
+ SkASSERT(isActiveResolveTask || fDAG.renderTask(i)->isClosed());
}
}
@@ -679,12 +683,9 @@
return opsTask;
}
-GrRenderTask* GrDrawingManager::newTextureResolveRenderTask(
- sk_sp<GrSurfaceProxy> proxy, GrSurfaceProxy::ResolveFlags flags, const GrCaps& caps) {
- SkDEBUGCODE(auto* previousTaskBeforeMipsResolve = proxy->getLastRenderTask();)
-
+GrTextureResolveRenderTask* GrDrawingManager::newTextureResolveRenderTask(const GrCaps& caps) {
// Unlike in the "new opsTask" case, we do not want to close the active opsTask, nor (if we are
- // in sorting and opsTask reduction mode) the render tasks that depend on the proxy's current
+ // in sorting and opsTask reduction mode) the render tasks that depend on any proxy's current
// state. This is because those opsTasks can still receive new ops and because if they refer to
// the mipmapped version of 'proxy', they will then come to depend on the render task being
// created here.
@@ -692,15 +693,8 @@
// Add the new textureResolveTask before the fActiveOpsTask (if not in
// sorting/opsTask-splitting-reduction mode) because it will depend upon this resolve task.
// NOTE: Putting it here will also reduce the amount of work required by the topological sort.
- auto* resolveTask = static_cast<GrTextureResolveRenderTask*>(fDAG.addBeforeLast(
- sk_make_sp<GrTextureResolveRenderTask>(std::move(proxy), flags)));
- resolveTask->init(caps);
-
- // GrTextureResolveRenderTask::init should have closed the texture proxy's previous task.
- SkASSERT(!previousTaskBeforeMipsResolve || previousTaskBeforeMipsResolve->isClosed());
- SkASSERT(resolveTask->fTarget->getLastRenderTask() == resolveTask);
-
- return resolveTask;
+ return static_cast<GrTextureResolveRenderTask*>(fDAG.addBeforeLast(
+ sk_make_sp<GrTextureResolveRenderTask>()));
}
void GrDrawingManager::newWaitRenderTask(sk_sp<GrSurfaceProxy> proxy,
diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h
index 6c6d669..72f3211 100644
--- a/src/gpu/GrDrawingManager.h
+++ b/src/gpu/GrDrawingManager.h
@@ -27,6 +27,7 @@
class GrRenderTargetProxy;
class GrSoftwarePathRenderer;
class GrTextureContext;
+class GrTextureResolveRenderTask;
class SkDeferredDisplayList;
class GrDrawingManager {
@@ -49,12 +50,10 @@
// others). An unmanaged one is created and used by the onFlushCallback.
sk_sp<GrOpsTask> newOpsTask(sk_sp<GrRenderTargetProxy>, bool managedOpsTask);
- // Create a new, specialized, render task that will regenerate mipmap levels and/or resolve
- // MSAA (depending on ResolveFlags). This method will add the new render task to the list of
- // render tasks and make it depend on the target texture proxy. It is up to the caller to add
- // any dependencies on the new render task.
- GrRenderTask* newTextureResolveRenderTask(
- sk_sp<GrSurfaceProxy>, GrSurfaceProxy::ResolveFlags, const GrCaps&);
+ // Create a render task that can resolve MSAA and/or regenerate mipmap levels on proxies. This
+ // method will only add the new render task to the list. It is up to the caller to call
+ // addProxy() on the returned object.
+ GrTextureResolveRenderTask* newTextureResolveRenderTask(const GrCaps&);
// Create a new render task that will cause the gpu to wait on semaphores before executing any
// more RenderTasks that target proxy. It is possible for this wait to also block additional
diff --git a/src/gpu/GrOnFlushResourceProvider.cpp b/src/gpu/GrOnFlushResourceProvider.cpp
index 781df12..2cf4af8 100644
--- a/src/gpu/GrOnFlushResourceProvider.cpp
+++ b/src/gpu/GrOnFlushResourceProvider.cpp
@@ -49,8 +49,9 @@
renderTask->makeClosed(*this->caps());
}
auto task = static_cast<GrTextureResolveRenderTask*>(fDrawingMgr->fOnFlushRenderTasks.push_back(
- sk_make_sp<GrTextureResolveRenderTask>(std::move(textureProxy), resolveFlags)).get());
- task->init(*this->caps());
+ sk_make_sp<GrTextureResolveRenderTask>()).get());
+ task->addProxy(textureProxy, resolveFlags, *this->caps());
+ task->makeClosed(*this->caps());
}
bool GrOnFlushResourceProvider::assignUniqueKeyToProxy(const GrUniqueKey& key,
diff --git a/src/gpu/GrRenderTask.cpp b/src/gpu/GrRenderTask.cpp
index 42906dd..287fd5a 100644
--- a/src/gpu/GrRenderTask.cpp
+++ b/src/gpu/GrRenderTask.cpp
@@ -10,6 +10,7 @@
#include "src/gpu/GrRenderTargetPriv.h"
#include "src/gpu/GrStencilAttachment.h"
#include "src/gpu/GrTextureProxyPriv.h"
+#include "src/gpu/GrTextureResolveRenderTask.h"
uint32_t GrRenderTask::CreateUniqueID() {
static std::atomic<uint32_t> nextID{1};
@@ -61,6 +62,12 @@
}
}
+ if (fTextureResolveTask) {
+ this->addDependency(fTextureResolveTask);
+ fTextureResolveTask->makeClosed(caps);
+ fTextureResolveTask = nullptr;
+ }
+
this->setFlag(kClosed_Flag);
}
@@ -146,18 +153,23 @@
// Does this proxy have msaa to resolve and/or mipmaps to regenerate?
if (GrSurfaceProxy::ResolveFlags::kNone != resolveFlags) {
- // Create a renderTask that resolves the texture's mipmap data.
- GrRenderTask* textureResolveTask = textureResolveManager.newTextureResolveRenderTask(
- sk_ref_sp(dependedOn), resolveFlags, caps);
+ if (!fTextureResolveTask) {
+ fTextureResolveTask = textureResolveManager.newTextureResolveRenderTask(caps);
+ }
+ fTextureResolveTask->addProxy(sk_ref_sp(dependedOn), resolveFlags, caps);
+
+ // addProxy() should have closed the texture proxy's previous task.
+ SkASSERT(!dependedOnTask || dependedOnTask->isClosed());
+ SkASSERT(dependedOn->getLastRenderTask() == fTextureResolveTask);
#ifdef SK_DEBUG
- // GrTextureResolveRenderTask::init should have called addDependency (in this instance,
- // recursively) on the textureResolveTask.
+ // addProxy() should have called addDependency (in this instance, recursively) on
+ // fTextureResolveTask.
if (dependedOnTask) {
- SkASSERT(textureResolveTask->dependsOn(dependedOnTask));
+ SkASSERT(fTextureResolveTask->dependsOn(dependedOnTask));
}
if (textureProxy && textureProxy->texPriv().isDeferred()) {
- SkASSERT(textureResolveTask->fDeferredProxies.back() == textureProxy);
+ SkASSERT(fTextureResolveTask->fDeferredProxies.back() == textureProxy);
}
// The GrTextureResolveRenderTask factory should have also marked the proxy clean, set the
@@ -168,13 +180,12 @@
if (textureProxy) {
SkASSERT(!textureProxy->mipMapsAreDirty());
}
- SkASSERT(dependedOn->getLastRenderTask() == textureResolveTask);
- SkASSERT(textureResolveTask->isClosed());
+ SkASSERT(dependedOn->getLastRenderTask() == fTextureResolveTask);
#endif
+ return;
+ }
- // Fall through and add textureResolveTask as a dependency of "this".
- dependedOnTask = textureResolveTask;
- } else if (textureProxy && textureProxy->texPriv().isDeferred()) {
+ if (textureProxy && textureProxy->texPriv().isDeferred()) {
fDeferredProxies.push_back(textureProxy);
}
diff --git a/src/gpu/GrRenderTask.h b/src/gpu/GrRenderTask.h
index 2ad4656..0cb006f 100644
--- a/src/gpu/GrRenderTask.h
+++ b/src/gpu/GrRenderTask.h
@@ -17,6 +17,7 @@
class GrOpFlushState;
class GrOpsTask;
class GrResourceAllocator;
+class GrTextureResolveRenderTask;
// This class abstracts a task that targets a single GrSurfaceProxy, participates in the
// GrDrawingManager's DAG, and implements the onExecute method to modify its target proxy's
@@ -77,7 +78,9 @@
void visitTargetAndSrcProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const {
this->visitProxies_debugOnly(fn);
- fn(fTarget.get(), GrMipMapped::kNo);
+ if (fTarget) {
+ fn(fTarget.get(), GrMipMapped::kNo);
+ }
}
#endif
@@ -185,6 +188,11 @@
SkSTArray<1, GrRenderTask*, true> fDependencies;
// 'this' GrRenderTask's output is relied on by the GrRenderTasks in 'fDependents'
SkSTArray<1, GrRenderTask*, true> fDependents;
+
+ // For performance reasons, we should perform texture resolves back-to-back as much as possible.
+ // (http://skbug.com/9406). To accomplish this, we make and reuse one single resolve task for
+ // each render task, then add it as a dependency during makeClosed().
+ GrTextureResolveRenderTask* fTextureResolveTask = nullptr;
};
#endif
diff --git a/src/gpu/GrTextureResolveManager.h b/src/gpu/GrTextureResolveManager.h
index a9b0182..f610c6b 100644
--- a/src/gpu/GrTextureResolveManager.h
+++ b/src/gpu/GrTextureResolveManager.h
@@ -25,11 +25,9 @@
explicit GrTextureResolveManager(GrDrawingManager* drawingManager)
: fDrawingManager(drawingManager) {}
- GrRenderTask* newTextureResolveRenderTask(sk_sp<GrSurfaceProxy> proxy,
- GrSurfaceProxy::ResolveFlags resolveFlags,
- const GrCaps& caps) const {
+ GrTextureResolveRenderTask* newTextureResolveRenderTask(const GrCaps& caps) const {
SkASSERT(fDrawingManager);
- return fDrawingManager->newTextureResolveRenderTask(std::move(proxy), resolveFlags, caps);
+ return fDrawingManager->newTextureResolveRenderTask(caps);
}
private:
diff --git a/src/gpu/GrTextureResolveRenderTask.cpp b/src/gpu/GrTextureResolveRenderTask.cpp
index 0b77383..d97e5d5 100644
--- a/src/gpu/GrTextureResolveRenderTask.cpp
+++ b/src/gpu/GrTextureResolveRenderTask.cpp
@@ -14,58 +14,83 @@
#include "src/gpu/GrResourceAllocator.h"
#include "src/gpu/GrTexturePriv.h"
-void GrTextureResolveRenderTask::init(const GrCaps& caps) {
- if (GrSurfaceProxy::ResolveFlags::kMSAA & fResolveFlags) {
- GrRenderTargetProxy* renderTargetProxy = fTarget->asRenderTargetProxy();
+GrTextureResolveRenderTask::~GrTextureResolveRenderTask() {
+ for (const auto& resolve : fResolves) {
+ // Ensure the proxy doesn't keep hold of a dangling back pointer.
+ resolve.fProxy->setLastRenderTask(nullptr);
+ }
+}
+
+void GrTextureResolveRenderTask::addProxy(
+ sk_sp<GrSurfaceProxy> proxy, GrSurfaceProxy::ResolveFlags flags, const GrCaps& caps) {
+ // Ensure the last render task that operated on the proxy is closed. That's where msaa and
+ // mipmaps should have been marked dirty.
+ SkASSERT(!proxy->getLastRenderTask() || proxy->getLastRenderTask()->isClosed());
+ SkASSERT(GrSurfaceProxy::ResolveFlags::kNone != flags);
+
+ if (GrSurfaceProxy::ResolveFlags::kMSAA & flags) {
+ GrRenderTargetProxy* renderTargetProxy = proxy->asRenderTargetProxy();
SkASSERT(renderTargetProxy);
SkASSERT(renderTargetProxy->isMSAADirty());
renderTargetProxy->markMSAAResolved();
}
- if (GrSurfaceProxy::ResolveFlags::kMipMaps & fResolveFlags) {
- GrTextureProxy* textureProxy = fTarget->asTextureProxy();
+ if (GrSurfaceProxy::ResolveFlags::kMipMaps & flags) {
+ GrTextureProxy* textureProxy = proxy->asTextureProxy();
SkASSERT(GrMipMapped::kYes == textureProxy->mipMapped());
SkASSERT(textureProxy->mipMapsAreDirty());
textureProxy->markMipMapsClean();
}
- // Add the target as a dependency: We will read the existing contents of this texture while
+ // Add the proxy as a dependency: We will read the existing contents of this texture while
// generating mipmap levels and/or resolving MSAA.
- //
- // NOTE: This must be called before makeClosed.
- this->addDependency(fTarget.get(), GrMipMapped::kNo, GrTextureResolveManager(nullptr), caps);
- fTarget->setLastRenderTask(this);
+ this->addDependency(proxy.get(), GrMipMapped::kNo, GrTextureResolveManager(nullptr), caps);
+ proxy->setLastRenderTask(this);
- // We only resolve the texture; nobody should try to do anything else with this opsTask.
- this->makeClosed(caps);
+ fResolves.emplace_back(std::move(proxy), flags);
}
void GrTextureResolveRenderTask::gatherProxyIntervals(GrResourceAllocator* alloc) const {
- // This renderTask doesn't have "normal" ops. In this case we still need to add an interval (so
- // fEndOfOpsTaskOpIndices will remain in sync), so we create a fake op# to capture the fact that
- // we manipulate fTarget.
- alloc->addInterval(fTarget.get(), alloc->curOp(), alloc->curOp(),
- GrResourceAllocator::ActualUse::kYes);
+ // This renderTask doesn't have "normal" ops, however we still need to add intervals so
+ // fEndOfOpsTaskOpIndices will remain in sync. We create fake op#'s to capture the fact that we
+ // manipulate the resolve proxies.
+ auto fakeOp = alloc->curOp();
+ for (const auto& resolve : fResolves) {
+ alloc->addInterval(resolve.fProxy.get(), fakeOp, fakeOp,
+ GrResourceAllocator::ActualUse::kYes);
+ }
alloc->incOps();
}
bool GrTextureResolveRenderTask::onExecute(GrOpFlushState* flushState) {
- // Resolve msaa before regenerating mipmaps.
- if (GrSurfaceProxy::ResolveFlags::kMSAA & fResolveFlags) {
- GrRenderTarget* renderTarget = fTarget->peekRenderTarget();
- SkASSERT(renderTarget);
- if (renderTarget->needsResolve()) {
- flushState->gpu()->resolveRenderTarget(renderTarget);
+ // Resolve all msaa back-to-back, before regenerating mipmaps.
+ for (const auto& resolve : fResolves) {
+ if (GrSurfaceProxy::ResolveFlags::kMSAA & resolve.fFlags) {
+ GrRenderTarget* renderTarget = resolve.fProxy->peekRenderTarget();
+ SkASSERT(renderTarget);
+ if (renderTarget->needsResolve()) {
+ flushState->gpu()->resolveRenderTarget(renderTarget);
+ }
}
}
-
- if (GrSurfaceProxy::ResolveFlags::kMipMaps & fResolveFlags) {
- GrTexture* texture = fTarget->peekTexture();
- SkASSERT(texture);
- if (texture->texturePriv().mipMapsAreDirty()) {
- flushState->gpu()->regenerateMipMapLevels(texture);
+ // Regenerate all mipmaps back-to-back.
+ for (const auto& resolve : fResolves) {
+ if (GrSurfaceProxy::ResolveFlags::kMipMaps & resolve.fFlags) {
+ GrTexture* texture = resolve.fProxy->peekTexture();
+ SkASSERT(texture);
+ if (texture->texturePriv().mipMapsAreDirty()) {
+ flushState->gpu()->regenerateMipMapLevels(texture);
+ }
}
}
return true;
}
+
+#ifdef SK_DEBUG
+void GrTextureResolveRenderTask::visitProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const {
+ for (const auto& resolve : fResolves) {
+ fn(resolve.fProxy.get(), GrMipMapped::kNo);
+ }
+}
+#endif
diff --git a/src/gpu/GrTextureResolveRenderTask.h b/src/gpu/GrTextureResolveRenderTask.h
index 86365dc..b175b0b 100644
--- a/src/gpu/GrTextureResolveRenderTask.h
+++ b/src/gpu/GrTextureResolveRenderTask.h
@@ -12,17 +12,10 @@
class GrTextureResolveRenderTask final : public GrRenderTask {
public:
- GrTextureResolveRenderTask(sk_sp<GrSurfaceProxy> proxy,
- GrSurfaceProxy::ResolveFlags resolveFlags)
- : GrRenderTask(std::move(proxy))
- , fResolveFlags(resolveFlags) {
- // Ensure the last render task that operated on the target is closed. That's where msaa and
- // mipmaps should have been marked dirty.
- SkASSERT(!fTarget->getLastRenderTask() || fTarget->getLastRenderTask()->isClosed());
- SkASSERT(GrSurfaceProxy::ResolveFlags::kNone != fResolveFlags);
- }
+ GrTextureResolveRenderTask() : GrRenderTask(nullptr) {}
+ ~GrTextureResolveRenderTask() override;
- void init(const GrCaps&);
+ void addProxy(sk_sp<GrSurfaceProxy>, GrSurfaceProxy::ResolveFlags, const GrCaps&);
private:
void onPrepare(GrOpFlushState*) override {}
@@ -40,11 +33,17 @@
bool onExecute(GrOpFlushState*) override;
#ifdef SK_DEBUG
- // No non-dst proxies.
- void visitProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const override {}
+ SkDEBUGCODE(void visitProxies_debugOnly(const VisitSurfaceProxyFunc&) const override;)
#endif
- const GrSurfaceProxy::ResolveFlags fResolveFlags;
+ struct Resolve {
+ Resolve(sk_sp<GrSurfaceProxy> proxy, GrSurfaceProxy::ResolveFlags flags)
+ : fProxy(std::move(proxy)), fFlags(flags) {}
+ sk_sp<GrSurfaceProxy> fProxy;
+ GrSurfaceProxy::ResolveFlags fFlags;
+ };
+
+ SkSTArray<4, Resolve> fResolves;
};
#endif