Move GrAtlasRenderTask to gpu/ops and skgpu::v1 namespace
Bug: skia:11837
Change-Id: I29352c0e2c9e987b28be983c3d519a88b589baf1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/442022
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/ops/AtlasPathRenderer.cpp b/src/gpu/ops/AtlasPathRenderer.cpp
index a6b6187..3283896 100644
--- a/src/gpu/ops/AtlasPathRenderer.cpp
+++ b/src/gpu/ops/AtlasPathRenderer.cpp
@@ -14,9 +14,9 @@
#include "src/gpu/GrVx.h"
#include "src/gpu/effects/GrModulateAtlasCoverageEffect.h"
#include "src/gpu/geometry/GrStyledShape.h"
+#include "src/gpu/ops/AtlasRenderTask.h"
#include "src/gpu/ops/GrDrawAtlasPathOp.h"
#include "src/gpu/ops/TessellationPathRenderer.h"
-#include "src/gpu/tessellate/GrAtlasRenderTask.h"
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
#include "src/gpu/v1/SurfaceDrawContext_v1.h"
@@ -63,10 +63,10 @@
// Ensures the atlas dependencies are set up such that each atlas will be totally out of service
// before we render the next one in line. This means there will only ever be one atlas active at a
// time and that they can all share the same texture.
-void validate_atlas_dependencies(const SkTArray<sk_sp<GrAtlasRenderTask>>& atlasTasks) {
+void validate_atlas_dependencies(const SkTArray<sk_sp<skgpu::v1::AtlasRenderTask>>& atlasTasks) {
for (int i = atlasTasks.count() - 1; i >= 1; --i) {
- GrAtlasRenderTask* atlasTask = atlasTasks[i].get();
- GrAtlasRenderTask* previousAtlasTask = atlasTasks[i - 1].get();
+ auto atlasTask = atlasTasks[i].get();
+ auto previousAtlasTask = atlasTasks[i - 1].get();
// Double check that atlasTask depends on every dependent of its previous atlas. If this
// fires it might mean previousAtlasTask gained a new dependent after atlasTask came into
// service (maybe by an op that hadn't yet been added to an opsTask when we registered the
@@ -223,8 +223,8 @@
!fAtlasRenderTasks.back()->addPath(viewMatrix, path, devIBounds->topLeft(), widthInAtlas,
heightInAtlas, *transposedInAtlas, locationInAtlas)) {
// We either don't have an atlas yet or the current one is full. Try to replace it.
- GrAtlasRenderTask* currentAtlasTask = (!fAtlasRenderTasks.empty())
- ? fAtlasRenderTasks.back().get() : nullptr;
+ auto currentAtlasTask = (!fAtlasRenderTasks.empty()) ? fAtlasRenderTasks.back().get()
+ : nullptr;
if (currentAtlasTask &&
drawRefsAtlasCallback &&
drawRefsAtlasCallback(currentAtlasTask->atlasProxy())) {
@@ -237,9 +237,9 @@
kAtlasAlpha8Type, GrDynamicAtlas::InternalMultisample::kYes,
SkISize{fAtlasInitialSize, fAtlasInitialSize}, fAtlasMaxSize,
*rContext->priv().caps(), kAtlasAlgorithm);
- auto newAtlasTask = sk_make_sp<GrAtlasRenderTask>(rContext,
- sk_make_sp<GrArenas>(),
- std::move(dynamicAtlas));
+ auto newAtlasTask = sk_make_sp<AtlasRenderTask>(rContext,
+ sk_make_sp<GrArenas>(),
+ std::move(dynamicAtlas));
rContext->priv().drawingManager()->addAtlasTask(newAtlasTask, currentAtlasTask);
SkAssertResult(newAtlasTask->addPath(viewMatrix, path, devIBounds->topLeft(), widthInAtlas,
heightInAtlas, *transposedInAtlas, locationInAtlas));
@@ -403,7 +403,7 @@
GrTexture* firstAtlasTexture = fAtlasRenderTasks[0]->atlasProxy()->peekTexture();
SkASSERT(firstAtlasTexture);
for (int i = 1; i < fAtlasRenderTasks.count(); ++i) {
- GrAtlasRenderTask* atlasTask = fAtlasRenderTasks[i].get();
+ auto atlasTask = fAtlasRenderTasks[i].get();
if (atlasTask->atlasProxy()->backingStoreDimensions() == firstAtlasTexture->dimensions()) {
atlasTask->instantiate(onFlushRP, sk_ref_sp(firstAtlasTexture));
} else {
diff --git a/src/gpu/ops/AtlasPathRenderer.h b/src/gpu/ops/AtlasPathRenderer.h
index 026b7b1..2cf48ad 100644
--- a/src/gpu/ops/AtlasPathRenderer.h
+++ b/src/gpu/ops/AtlasPathRenderer.h
@@ -16,12 +16,13 @@
#include "src/gpu/GrOnFlushResourceProvider.h"
#include "src/gpu/v1/PathRenderer.h"
-class GrAtlasRenderTask;
class GrOp;
class GrRecordingContext;
namespace skgpu::v1 {
+class AtlasRenderTask;
+
// Draws paths by first rendering their coverage mask into an offscreen atlas.
class AtlasPathRenderer final : public PathRenderer, public GrOnFlushCallbackObject {
public:
@@ -94,7 +95,7 @@
// A collection of all atlases we've created and used since the last flush. We instantiate these
// at flush time during preFlush().
- SkSTArray<4, sk_sp<GrAtlasRenderTask>> fAtlasRenderTasks;
+ SkSTArray<4, sk_sp<AtlasRenderTask>> fAtlasRenderTasks;
// This simple cache remembers the locations of cacheable path masks in the most recent atlas.
// Its main motivation is for clip paths.
diff --git a/src/gpu/ops/AtlasRenderTask.cpp b/src/gpu/ops/AtlasRenderTask.cpp
new file mode 100644
index 0000000..5e50eb9
--- /dev/null
+++ b/src/gpu/ops/AtlasRenderTask.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2021 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/AtlasRenderTask.h"
+
+#include "src/core/SkBlendModePriv.h"
+#include "src/core/SkIPoint16.h"
+#include "src/gpu/GrGpu.h"
+#include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrOpsTypes.h"
+#include "src/gpu/ops/GrFillRectOp.h"
+#include "src/gpu/ops/PathStencilCoverOp.h"
+
+namespace skgpu::v1 {
+
+AtlasRenderTask::AtlasRenderTask(GrRecordingContext* rContext,
+ sk_sp<GrArenas> arenas,
+ std::unique_ptr<GrDynamicAtlas> dynamicAtlas)
+ : OpsTask(rContext->priv().drawingManager(),
+ dynamicAtlas->writeView(*rContext->priv().caps()),
+ rContext->priv().auditTrail(),
+ std::move(arenas))
+ , fDynamicAtlas(std::move(dynamicAtlas)) {
+}
+
+bool AtlasRenderTask::addPath(const SkMatrix& viewMatrix, const SkPath& path,
+ SkIPoint pathDevTopLeft, int widthInAtlas, int heightInAtlas,
+ bool transposedInAtlas, SkIPoint16* locationInAtlas) {
+ SkASSERT(!this->isClosed());
+ SkASSERT(this->isEmpty());
+ SkASSERT(!fDynamicAtlas->isInstantiated()); // Paths can't be added after instantiate().
+
+ if (!fDynamicAtlas->addRect(widthInAtlas, heightInAtlas, locationInAtlas)) {
+ return false;
+ }
+
+ SkMatrix pathToAtlasMatrix = viewMatrix;
+ if (transposedInAtlas) {
+ std::swap(pathToAtlasMatrix[0], pathToAtlasMatrix[3]);
+ std::swap(pathToAtlasMatrix[1], pathToAtlasMatrix[4]);
+ float tx=pathToAtlasMatrix.getTranslateX(), ty=pathToAtlasMatrix.getTranslateY();
+ pathToAtlasMatrix.setTranslateX(ty - pathDevTopLeft.y() + locationInAtlas->x());
+ pathToAtlasMatrix.setTranslateY(tx - pathDevTopLeft.x() + locationInAtlas->y());
+ } else {
+ pathToAtlasMatrix.postTranslate(locationInAtlas->x() - pathDevTopLeft.x(),
+ locationInAtlas->y() - pathDevTopLeft.y());
+ }
+
+ if (GrFillRuleForSkPath(path) == GrFillRule::kNonzero) {
+ fWindingPathList.add(&fPathDrawAllocator, pathToAtlasMatrix, path);
+ } else {
+ fEvenOddPathList.add(&fPathDrawAllocator, pathToAtlasMatrix, path);
+ }
+ return true;
+}
+
+GrRenderTask::ExpectedOutcome AtlasRenderTask::onMakeClosed(GrRecordingContext* rContext,
+ SkIRect* targetUpdateBounds) {
+ // We don't add our ops until now, at which point we know the atlas is done being built.
+ SkASSERT(this->isEmpty());
+ SkASSERT(!fDynamicAtlas->isInstantiated()); // Instantiation happens after makeClosed().
+
+ const GrCaps& caps = *rContext->priv().caps();
+
+ // Set our dimensions now. OpsTask will need them when we add our ops.
+ this->target(0)->priv().setLazyDimensions(fDynamicAtlas->drawBounds());
+ this->target(0)->asRenderTargetProxy()->setNeedsStencil();
+ SkRect drawRect = target(0)->getBoundsRect();
+
+ // Clear the atlas.
+ if (caps.performColorClearsAsDraws() || caps.performStencilClearsAsDraws()) {
+ this->setColorLoadOp(GrLoadOp::kDiscard);
+ this->setInitialStencilContent(StencilContent::kDontCare);
+
+ constexpr static GrUserStencilSettings kClearStencil(
+ GrUserStencilSettings::StaticInit<
+ 0x0000,
+ GrUserStencilTest::kAlways,
+ 0xffff,
+ GrUserStencilOp::kReplace,
+ GrUserStencilOp::kReplace,
+ 0xffff>());
+
+ this->stencilAtlasRect(rContext, drawRect, SK_PMColor4fTRANSPARENT, &kClearStencil);
+ } else {
+ this->setColorLoadOp(GrLoadOp::kClear);
+ this->setInitialStencilContent(StencilContent::kUserBitsCleared);
+ }
+
+ // Add ops to stencil the atlas paths.
+ for (const auto* pathList : {&fWindingPathList, &fEvenOddPathList}) {
+ if (pathList->pathCount() > 0) {
+ auto op = GrOp::Make<GrPathStencilCoverOp>(
+ rContext,
+ pathList->pathDrawList(),
+ pathList->totalCombinedPathVerbCnt(),
+ pathList->pathCount(),
+ GrPaint(),
+ GrAAType::kMSAA,
+ GrTessellationPathFlags::kStencilOnly,
+ drawRect);
+ this->addAtlasDrawOp(std::move(op), caps);
+ }
+ }
+
+ // Finally, draw a fullscreen rect to cover our stencilled paths.
+ const GrUserStencilSettings* stencil;
+ if (caps.discardStencilValuesAfterRenderPass()) {
+ constexpr static GrUserStencilSettings kTestStencil(
+ GrUserStencilSettings::StaticInit<
+ 0x0000,
+ GrUserStencilTest::kNotEqual,
+ 0xffff,
+ GrUserStencilOp::kKeep,
+ GrUserStencilOp::kKeep,
+ 0xffff>());
+
+ // This is the final op in the task. Since Ganesh is planning to discard the stencil values
+ // anyway, there is no need to reset the stencil values back to 0.
+ stencil = &kTestStencil;
+ } else {
+ constexpr static GrUserStencilSettings kTestAndResetStencil(
+ GrUserStencilSettings::StaticInit<
+ 0x0000,
+ GrUserStencilTest::kNotEqual,
+ 0xffff,
+ GrUserStencilOp::kZero,
+ GrUserStencilOp::kKeep,
+ 0xffff>());
+
+ // Outset the cover rect to make extra sure we clear every stencil value touched by the
+ // atlas.
+ drawRect.outset(1, 1);
+ stencil = &kTestAndResetStencil;
+ }
+ this->stencilAtlasRect(rContext, drawRect, SK_PMColor4fWHITE, stencil);
+
+ this->OpsTask::onMakeClosed(rContext, targetUpdateBounds);
+
+ // Don't mark msaa dirty. Since this op defers being closed, the drawing manager's dirty
+ // tracking doesn't work anyway. We will just resolve msaa manually during onExecute.
+ return ExpectedOutcome::kTargetUnchanged;
+}
+
+void AtlasRenderTask::stencilAtlasRect(GrRecordingContext* rContext, const SkRect& rect,
+ const SkPMColor4f& color,
+ const GrUserStencilSettings* stencil) {
+ GrPaint paint;
+ paint.setColor4f(color);
+ paint.setXPFactory(SkBlendMode_AsXPFactory(SkBlendMode::kSrc));
+ GrQuad quad(rect);
+ DrawQuad drawQuad{quad, quad, GrQuadAAFlags::kAll};
+ auto op = GrFillRectOp::Make(rContext, std::move(paint), GrAAType::kMSAA, &drawQuad, stencil);
+ this->addAtlasDrawOp(std::move(op), *rContext->priv().caps());
+}
+
+void AtlasRenderTask::addAtlasDrawOp(GrOp::Owner op, const GrCaps& caps) {
+ SkASSERT(!this->isClosed());
+
+ auto drawOp = static_cast<GrDrawOp*>(op.get());
+ SkDEBUGCODE(drawOp->fAddDrawOpCalled = true;)
+
+ auto processorAnalysis = drawOp->finalize(caps, nullptr,
+ GrColorTypeClampType(fDynamicAtlas->colorType()));
+ SkASSERT(!processorAnalysis.requiresDstTexture());
+ SkASSERT(!processorAnalysis.usesNonCoherentHWBlending());
+
+ drawOp->setClippedBounds(drawOp->bounds());
+ this->recordOp(std::move(op), true/*usesMSAA*/, processorAnalysis, nullptr, nullptr, caps);
+}
+
+bool AtlasRenderTask::onExecute(GrOpFlushState* flushState) {
+ if (!this->OpsTask::onExecute(flushState)) {
+ return false;
+ }
+ if (this->target(0)->requiresManualMSAAResolve()) {
+ // Since atlases don't get closed until they are done being built, the drawingManager
+ // doesn't detect that they need an MSAA resolve. Do it here manually.
+ auto nativeRect = GrNativeRect::MakeIRectRelativeTo(
+ GrDynamicAtlas::kTextureOrigin,
+ this->target(0)->backingStoreDimensions().height(),
+ SkIRect::MakeSize(fDynamicAtlas->drawBounds()));
+ flushState->gpu()->resolveRenderTarget(this->target(0)->peekRenderTarget(), nativeRect);
+ }
+ return true;
+}
+
+} // namespace skgpu::v1
diff --git a/src/gpu/ops/AtlasRenderTask.h b/src/gpu/ops/AtlasRenderTask.h
new file mode 100644
index 0000000..7846966
--- /dev/null
+++ b/src/gpu/ops/AtlasRenderTask.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef AtlasRenderTask_DEFINED
+#define AtlasRenderTask_DEFINED
+
+#include "include/core/SkPath.h"
+#include "src/core/SkTBlockList.h"
+#include "src/gpu/GrDynamicAtlas.h"
+#include "src/gpu/ops/OpsTask.h"
+#include "src/gpu/tessellate/GrPathTessellator.h"
+
+struct SkIPoint16;
+
+namespace skgpu::v1 {
+
+// Represents a GrRenderTask that draws paths into an atlas. This task gets added the DAG and left
+// open, lays out its atlas while future tasks call addPath(), and finally adds its internal draw
+// ops during onMakeClosed().
+//
+// The atlas texture does not get instantiated automatically. It is the creator's responsibility to
+// call instantiate() at flush time.
+class AtlasRenderTask : public OpsTask {
+public:
+ AtlasRenderTask(GrRecordingContext*,
+ sk_sp<GrArenas>,
+ std::unique_ptr<GrDynamicAtlas>);
+
+ const GrTextureProxy* atlasProxy() const { return fDynamicAtlas->textureProxy(); }
+ GrSurfaceProxyView readView(const GrCaps& caps) const { return fDynamicAtlas->readView(caps); }
+
+ // Allocates a rectangle for, and stages the given path to be rendered into the atlas. Returns
+ // false if there was not room in the atlas. On success, writes out the location of the path's
+ // upper-left corner to 'locationInAtlas'.
+ bool addPath(const SkMatrix&, const SkPath&, SkIPoint pathDevTopLeft, int widthInAtlas,
+ int heightInAtlas, bool transposedInAtlas, SkIPoint16* locationInAtlas);
+
+ // Must be called at flush time. The texture proxy is instantiated with 'backingTexture', if
+ // provided. See GrDynamicAtlas.
+ void instantiate(GrOnFlushResourceProvider* onFlushRP,
+ sk_sp<GrTexture> backingTexture = nullptr) {
+ SkASSERT(this->isClosed());
+ fDynamicAtlas->instantiate(onFlushRP, std::move(backingTexture));
+ }
+
+private:
+ // Adds internal ops to render the atlas before deferring to OpsTask::onMakeClosed.
+ ExpectedOutcome onMakeClosed(GrRecordingContext*, SkIRect* targetUpdateBounds) override;
+
+ void stencilAtlasRect(GrRecordingContext*, const SkRect&, const SkPMColor4f&,
+ const GrUserStencilSettings*);
+ void addAtlasDrawOp(GrOp::Owner, const GrCaps&);
+
+ // Executes the OpsTask and resolves msaa if needed.
+ bool onExecute(GrOpFlushState* flushState) override;
+
+ const std::unique_ptr<GrDynamicAtlas> fDynamicAtlas;
+
+ // Allocate enough inline entries for 16 atlas path draws, then spill to the heap.
+ using PathDrawAllocator = SkTBlockList<GrPathTessellator::PathDrawList, 16>;
+ PathDrawAllocator fPathDrawAllocator{64, SkBlockAllocator::GrowthPolicy::kFibonacci};
+
+ class AtlasPathList : SkNoncopyable {
+ public:
+ void add(PathDrawAllocator* alloc, const SkMatrix& pathMatrix, const SkPath& path) {
+ fPathDrawList = &alloc->emplace_back(pathMatrix, path, fPathDrawList);
+ if (path.isInverseFillType()) {
+ // The atlas never has inverse paths. The inversion happens later.
+ fPathDrawList->fPath.toggleInverseFillType();
+ }
+ fTotalCombinedPathVerbCnt += path.countVerbs();
+ ++fPathCount;
+ }
+ const GrPathTessellator::PathDrawList* pathDrawList() const { return fPathDrawList; }
+ int totalCombinedPathVerbCnt() const { return fTotalCombinedPathVerbCnt; }
+ int pathCount() const { return fPathCount; }
+
+ private:
+ GrPathTessellator::PathDrawList* fPathDrawList = nullptr;
+ int fTotalCombinedPathVerbCnt = 0;
+ int fPathCount = 0;
+ };
+
+ AtlasPathList fWindingPathList;
+ AtlasPathList fEvenOddPathList;
+};
+
+} // namespace skgpu::v1
+
+#endif // AtlasRenderTask_DEFINED