Move surface wait ops to GrRenderTasks.

Change-Id: Id989a666e67be85af6ed72447696657b8c11aaa5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/239443
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index ae3d584..6f3dfdd 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -33,6 +33,7 @@
 #include "src/gpu/GrTextureResolveRenderTask.h"
 #include "src/gpu/GrTracing.h"
 #include "src/gpu/GrTransferFromRenderTask.h"
+#include "src/gpu/GrWaitRenderTask.h"
 #include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
 #include "src/gpu/text/GrTextContext.h"
 #include "src/image/SkSurface_Gpu.h"
@@ -702,6 +703,81 @@
     return resolveTask;
 }
 
+void GrDrawingManager::newWaitRenderTask(sk_sp<GrSurfaceProxy> proxy,
+                                         std::unique_ptr<sk_sp<GrSemaphore>[]> semaphores,
+                                         int numSemaphores) {
+    SkDEBUGCODE(this->validate());
+    SkASSERT(fContext);
+
+    const GrCaps& caps = *fContext->priv().caps();
+
+    sk_sp<GrWaitRenderTask> waitTask = sk_make_sp<GrWaitRenderTask>(proxy, std::move(semaphores),
+                                                                    numSemaphores);
+    if (fReduceOpsTaskSplitting) {
+        GrRenderTask* lastTask = proxy->getLastRenderTask();
+        if (lastTask && !lastTask->isClosed()) {
+            // We directly make the currently open renderTask depend on waitTask instead of using
+            // the proxy version of addDependency. The waitTask will never need to trigger any
+            // resolves or mip map generation which is the main advantage of going through the proxy
+            // version. Additionally we would've had to temporarily set the wait task as the
+            // lastRenderTask on the proxy, add the dependency, and then reset the lastRenderTask to
+            // lastTask. Additionally we add all dependencies of lastTask to waitTask so that the
+            // waitTask doesn't get reordered before them and unnecessarily block those tasks.
+            // Note: Any previous Ops already in lastTask will get blocked by the wait semaphore
+            // even though they don't need to be for correctness.
+
+            // Make sure we add the dependencies of lastTask to waitTask first or else we'll get a
+            // circular self dependency of waitTask on waitTask.
+            waitTask->addDependenciesFromOtherTask(lastTask);
+            lastTask->addDependency(waitTask.get());
+        } else {
+            // If there is a last task we set the waitTask to depend on it so that it doesn't get
+            // reordered in front of the lastTask causing the lastTask to be blocked by the
+            // semaphore. Again we directly just go through adding the dependency to the task and
+            // not the proxy since we don't need to worry about resolving anything.
+            if (lastTask) {
+                waitTask->addDependency(lastTask);
+            }
+            proxy->setLastRenderTask(waitTask.get());
+        }
+        fDAG.add(waitTask);
+    } else {
+        if (fActiveOpsTask && (fActiveOpsTask->fTarget == proxy)) {
+            SkASSERT(proxy->getLastRenderTask() == fActiveOpsTask);
+            fDAG.addBeforeLast(waitTask);
+            // In this case we keep the current renderTask open but just insert the new waitTask
+            // before it in the list. The waitTask will never need to trigger any resolves or mip
+            // map generation which is the main advantage of going through the proxy version.
+            // Additionally we would've had to temporarily set the wait task as the lastRenderTask
+            // on the proxy, add the dependency, and then reset the lastRenderTask to
+            // fActiveOpsTask. Additionally we make the waitTask depend on all of fActiveOpsTask
+            // dependencies so that we don't unnecessarily reorder the waitTask before them.
+            // Note: Any previous Ops already in fActiveOpsTask will get blocked by the wait
+            // semaphore even though they don't need to be for correctness.
+
+            // Make sure we add the dependencies of fActiveOpsTask to waitTask first or else we'll
+            // get a circular self dependency of waitTask on waitTask.
+            waitTask->addDependenciesFromOtherTask(fActiveOpsTask);
+            fActiveOpsTask->addDependency(waitTask.get());
+        } else {
+            // In this case we just close the previous RenderTask and start and append the waitTask
+            // to the DAG. Since it is the last task now we call setLastRenderTask on the proxy. If
+            // there is a lastTask on the proxy we make waitTask depend on that task. This
+            // dependency isn't strictly needed but it does keep the DAG from reordering the
+            // waitTask earlier and blocking more tasks.
+            if (GrRenderTask* lastTask = proxy->getLastRenderTask()) {
+                waitTask->addDependency(lastTask);
+            }
+            proxy->setLastRenderTask(waitTask.get());
+            this->closeRenderTasksForNewRenderTask(proxy.get());
+            fDAG.add(waitTask);
+        }
+    }
+    waitTask->makeClosed(caps);
+
+    SkDEBUGCODE(this->validate());
+}
+
 void GrDrawingManager::newTransferFromRenderTask(sk_sp<GrSurfaceProxy> srcProxy,
                                                  const SkIRect& srcRect,
                                                  GrColorType surfaceColorType,
diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h
index a1e3c6d..6c6d669 100644
--- a/src/gpu/GrDrawingManager.h
+++ b/src/gpu/GrDrawingManager.h
@@ -56,6 +56,14 @@
     GrRenderTask* newTextureResolveRenderTask(
             sk_sp<GrSurfaceProxy>, GrSurfaceProxy::ResolveFlags, 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
+    // work (even to other proxies) that has already been recorded or will be recorded later. The
+    // only guarantee is that future work to the passed in proxy will wait on the semaphores to be
+    // signaled.
+    void newWaitRenderTask(sk_sp<GrSurfaceProxy> proxy, std::unique_ptr<sk_sp<GrSemaphore>[]>,
+                           int numSemaphores);
+
     // Create a new render task which copies the pixels from the srcProxy into the dstBuffer. This
     // is used to support the asynchronous readback API. The srcRect is the region of the srcProxy
     // to be copied. The surfaceColorType says how we should interpret the data when reading back
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 26813d6..dd3472c 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -60,7 +60,6 @@
 #include "src/gpu/ops/GrOp.h"
 #include "src/gpu/ops/GrOvalOpFactory.h"
 #include "src/gpu/ops/GrRegionOp.h"
-#include "src/gpu/ops/GrSemaphoreOp.h"
 #include "src/gpu/ops/GrShadowRRectOp.h"
 #include "src/gpu/ops/GrStencilPathOp.h"
 #include "src/gpu/ops/GrStrokeRectOp.h"
@@ -2002,15 +2001,14 @@
 
     auto resourceProvider = direct->priv().resourceProvider();
 
+    std::unique_ptr<sk_sp<GrSemaphore>[]> grSemaphores(new sk_sp<GrSemaphore>[numSemaphores]);
     for (int i = 0; i < numSemaphores; ++i) {
-        sk_sp<GrSemaphore> sema = resourceProvider->wrapBackendSemaphore(
+        grSemaphores[i] = resourceProvider->wrapBackendSemaphore(
                 waitSemaphores[i], GrResourceProvider::SemaphoreWrapType::kWillWait,
                 kAdopt_GrWrapOwnership);
-        std::unique_ptr<GrOp> waitOp(GrSemaphoreOp::MakeWait(fContext, std::move(sema),
-                                                             fRenderTargetProxy.get()));
-        this->getOpsTask()->addWaitOp(
-                std::move(waitOp), GrTextureResolveManager(this->drawingManager()), *this->caps());
     }
+    this->drawingManager()->newWaitRenderTask(this->asSurfaceProxyRef(), std::move(grSemaphores),
+                                              numSemaphores);
     return true;
 }
 
diff --git a/src/gpu/GrRenderTask.cpp b/src/gpu/GrRenderTask.cpp
index 91528f2..42906dd 100644
--- a/src/gpu/GrRenderTask.cpp
+++ b/src/gpu/GrRenderTask.cpp
@@ -86,6 +86,15 @@
     SkDEBUGCODE(this->validate());
 }
 
+void GrRenderTask::addDependenciesFromOtherTask(GrRenderTask* otherTask) {
+    SkASSERT(otherTask);
+    for (int i = 0; i < otherTask->fDependencies.count(); ++i) {
+        // The task should not be adding a dependency to itself.
+        SkASSERT(otherTask->fDependencies[i] != this);
+        this->addDependency(otherTask->fDependencies[i]);
+    }
+}
+
 // Convert from a GrSurface-based dependency to a GrRenderTask one
 void GrRenderTask::addDependency(GrSurfaceProxy* dependedOn, GrMipMapped mipMapped,
                                  GrTextureResolveManager textureResolveManager,
diff --git a/src/gpu/GrRenderTask.h b/src/gpu/GrRenderTask.h
index 918055f..2ad4656 100644
--- a/src/gpu/GrRenderTask.h
+++ b/src/gpu/GrRenderTask.h
@@ -46,6 +46,12 @@
                        const GrCaps& caps);
 
     /*
+     * Notify this GrRenderTask that it relies on the contents of all GrRenderTasks which otherTask
+     * depends on.
+     */
+    void addDependenciesFromOtherTask(GrRenderTask* otherTask);
+
+    /*
      * Does this renderTask depend on 'dependedOn'?
      */
     bool dependsOn(const GrRenderTask* dependedOn) const;
diff --git a/src/gpu/GrWaitRenderTask.cpp b/src/gpu/GrWaitRenderTask.cpp
new file mode 100644
index 0000000..a09a9a6
--- /dev/null
+++ b/src/gpu/GrWaitRenderTask.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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/GrWaitRenderTask.h"
+
+#include "src/gpu/GrGpu.h"
+#include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrResourceAllocator.h"
+
+void GrWaitRenderTask::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);
+    alloc->incOps();
+}
+
+bool GrWaitRenderTask::onExecute(GrOpFlushState* flushState) {
+    for (int i = 0; i < fNumSemaphores; ++i) {
+        flushState->gpu()->waitSemaphore(fSemaphores[i]);
+    }
+    return true;
+}
diff --git a/src/gpu/GrWaitRenderTask.h b/src/gpu/GrWaitRenderTask.h
new file mode 100644
index 0000000..fa6b02c
--- /dev/null
+++ b/src/gpu/GrWaitRenderTask.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrWaitRenderTask_DEFINED
+#define GrWaitRenderTask_DEFINED
+
+#include "src/gpu/GrRenderTask.h"
+#include "src/gpu/GrSemaphore.h"
+
+class GrWaitRenderTask final : public GrRenderTask {
+public:
+    GrWaitRenderTask(sk_sp<GrSurfaceProxy> proxy, std::unique_ptr<sk_sp<GrSemaphore>[]> semaphores,
+                     int numSemaphores)
+            : GrRenderTask(std::move(proxy))
+            , fSemaphores(std::move(semaphores))
+            , fNumSemaphores(numSemaphores){}
+
+private:
+    void onPrepare(GrOpFlushState*) override {}
+    bool onIsUsed(GrSurfaceProxy* proxy) const override {
+        SkASSERT(proxy != fTarget.get());  // This case should be handled by GrRenderTask.
+        return false;
+    }
+    void handleInternalAllocationFailure() override {}
+    void gatherProxyIntervals(GrResourceAllocator*) const override;
+
+    ExpectedOutcome onMakeClosed(const GrCaps&) override {
+        return ExpectedOutcome::kTargetUnchanged;
+    }
+
+    bool onExecute(GrOpFlushState*) override;
+
+#ifdef SK_DEBUG
+    // No non-dst proxies.
+    void visitProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const override {}
+#endif
+    std::unique_ptr<sk_sp<GrSemaphore>[]> fSemaphores;
+    int fNumSemaphores;
+};
+
+#endif
diff --git a/src/gpu/ops/GrSemaphoreOp.cpp b/src/gpu/ops/GrSemaphoreOp.cpp
deleted file mode 100644
index e13e244..0000000
--- a/src/gpu/ops/GrSemaphoreOp.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ops/GrSemaphoreOp.h"
-
-#include "include/private/GrRecordingContext.h"
-#include "src/gpu/GrGpu.h"
-#include "src/gpu/GrMemoryPool.h"
-#include "src/gpu/GrOpFlushState.h"
-#include "src/gpu/GrRecordingContextPriv.h"
-
-class GrWaitSemaphoreOp final : public GrSemaphoreOp {
-public:
-    DEFINE_OP_CLASS_ID
-
-    static std::unique_ptr<GrOp> Make(GrRecordingContext* context,
-                                      sk_sp<GrSemaphore> semaphore,
-                                      GrRenderTargetProxy* proxy) {
-        GrOpMemoryPool* pool = context->priv().opMemoryPool();
-
-        return pool->allocate<GrWaitSemaphoreOp>(std::move(semaphore), proxy);
-    }
-
-    const char* name() const override { return "WaitSemaphore"; }
-
-private:
-    friend class GrOpMemoryPool; // for ctor
-
-    explicit GrWaitSemaphoreOp(sk_sp<GrSemaphore> semaphore, GrRenderTargetProxy* proxy)
-            : INHERITED(ClassID(), std::move(semaphore), proxy) {}
-
-    void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
-        state->gpu()->waitSemaphore(fSemaphore);
-    }
-
-    typedef GrSemaphoreOp INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-std::unique_ptr<GrOp> GrSemaphoreOp::MakeWait(GrRecordingContext* context,
-                                              sk_sp<GrSemaphore> semaphore,
-                                              GrRenderTargetProxy* proxy) {
-    return GrWaitSemaphoreOp::Make(context, std::move(semaphore), proxy);
-}
-
-
diff --git a/src/gpu/ops/GrSemaphoreOp.h b/src/gpu/ops/GrSemaphoreOp.h
deleted file mode 100644
index 215fb22..0000000
--- a/src/gpu/ops/GrSemaphoreOp.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrSemaphoreOp_DEFINED
-#define GrSemaphoreOp_DEFINED
-
-#include "src/gpu/ops/GrOp.h"
-
-#include "include/core/SkRefCnt.h"
-#include "src/gpu/GrRenderTargetProxy.h"
-#include "src/gpu/GrSemaphore.h"
-
-class GrRecordingContext;
-
-class GrSemaphoreOp : public GrOp {
-public:
-    static std::unique_ptr<GrOp> MakeWait(GrRecordingContext*,
-                                          sk_sp<GrSemaphore>,
-                                          GrRenderTargetProxy*);
-
-protected:
-    GrSemaphoreOp(uint32_t classId, sk_sp<GrSemaphore> semaphore, GrRenderTargetProxy* proxy)
-            : INHERITED(classId)
-            , fSemaphore(std::move(semaphore)) {
-        this->makeFullScreen(proxy);
-    }
-
-    sk_sp<GrSemaphore> fSemaphore;
-
-private:
-    void onPrepare(GrOpFlushState*) override {}
-
-    typedef GrOp INHERITED;
-};
-
-#endif