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