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/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