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