Delete GrSTArenaList

The intent of this class was to abstract the internal linked list used
by GrStrokeTessellateOp, but it seems to just make things more
complicated. We have a need now to iterate the list with more freedom
than is offered by GrSTArenaList, so it seems best to just use a plain
C-style linked list instead.

Bug: chromium:1172543
Change-Id: Ia76be83c523bd3c285200099a529ccd3818490b6
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/372656
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/bench/TessellateBench.cpp b/bench/TessellateBench.cpp
index b9d9a9d..14c7dd1 100644
--- a/bench/TessellateBench.cpp
+++ b/bench/TessellateBench.cpp
@@ -227,10 +227,11 @@
             SkDebugf("ERROR: could not create mock context.");
             return;
         }
+        SkMatrix matrix = SkMatrix::Scale(fMatrixScale, fMatrixScale);
+        GrStrokeTessellator::PathStrokeList pathStroke(fPath, fStrokeRec, SK_PMColor4fWHITE);
         for (int i = 0; i < loops; ++i) {
-            SkMatrix matrix = SkMatrix::Scale(fMatrixScale, fMatrixScale);
-            GrStrokeHardwareTessellator tessellator(ShaderFlags::kNone, {fPath, fStrokeRec,
-                                                    SK_PMColor4fWHITE}, fPath.countVerbs(),
+            GrStrokeHardwareTessellator tessellator(ShaderFlags::kNone, &pathStroke,
+                                                    fPath.countVerbs(),
                                                     *fTarget->caps().shaderCaps());
             tessellator.prepare(fTarget.get(), matrix);
         }
@@ -270,9 +271,10 @@
         }
         for (int i = 0; i < loops; ++i) {
             for (const SkPath& path : fPaths) {
+                GrStrokeTessellator::PathStrokeList pathStroke(path, fStrokeRec, SK_PMColor4fWHITE);
                 GrStrokeIndirectTessellator tessellator(ShaderFlags::kNone, SkMatrix::I(),
-                                                        {path, fStrokeRec, SK_PMColor4fWHITE},
-                                                        path.countVerbs(), fTarget->allocator());
+                                                        &pathStroke, path.countVerbs(),
+                                                        fTarget->allocator());
                 tessellator.prepare(fTarget.get(), SkMatrix::I());
             }
             fTarget->resetAllocator();
diff --git a/gn/gpu.gni b/gn/gpu.gni
index ce7c2813..d554c49 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -199,7 +199,6 @@
   "$_src/gpu/GrSPIRVUniformHandler.h",
   "$_src/gpu/GrSPIRVVaryingHandler.cpp",
   "$_src/gpu/GrSPIRVVaryingHandler.h",
-  "$_src/gpu/GrSTArenaList.h",
   "$_src/gpu/GrSWMaskHelper.cpp",
   "$_src/gpu/GrSWMaskHelper.h",
   "$_src/gpu/GrSamplePatternDictionary.cpp",
diff --git a/src/gpu/GrSTArenaList.h b/src/gpu/GrSTArenaList.h
deleted file mode 100644
index 5a4c530..0000000
--- a/src/gpu/GrSTArenaList.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrSTArenaList_DEFINED
-#define GrSTArenaList_DEFINED
-
-#include "src/core/SkArenaAlloc.h"
-
-// A singly-linked list whose head element is a "stack allocated" class member and whose subsequent
-// elements are allocated in an SkArenaAlloc.
-template<typename T> class GrSTArenaList {
-public:
-    struct Node {
-        template <typename... Args>
-        Node(Args&&... elementArgs) : fElement(std::forward<Args>(elementArgs)...) {}
-        T fElement;
-        Node* fNext = nullptr;
-    };
-
-    template <typename... Args>
-    GrSTArenaList(Args&&... headArgs) : fHead(std::forward<Args>(headArgs)...) {}
-
-    const T& head() const { return fHead.fElement; }
-    T& head() { return fHead.fElement; }
-
-    void concat(GrSTArenaList&& list, SkArenaAlloc* allocator) {
-        Node* listHeadCopy = allocator->make<Node>(std::move(list.fHead));
-        fTail->fNext = listHeadCopy;
-        // If the list's fTail pointed to its locally allocated head element, then point our fTail
-        // at the copy we just made in the arena. Otherwise the list's fTail already points at an
-        // arena-allocated element, so keep it.
-        fTail = (list.fTail == &list.fHead) ? listHeadCopy : list.fTail;
-    }
-
-    struct Iter {
-        bool operator!=(const Iter& it) const { return fCurr != it.fCurr; }
-        bool operator==(const Iter& it) const { return fCurr == it.fCurr; }
-        void operator++() { fCurr = fCurr->fNext; }
-        const T& operator*() { return fCurr->fElement; }
-        const Node* fCurr;
-    };
-
-    Iter begin() const { return Iter{&fHead}; }
-    Iter end() const { return Iter{nullptr}; }
-
-private:
-    Node fHead;
-    Node* fTail = &fHead;
-};
-
-#endif
diff --git a/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp b/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp
index c6897f7..4b122b3 100644
--- a/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp
+++ b/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp
@@ -655,8 +655,8 @@
     PatchWriter patchWriter(fShaderFlags, target, &fPatchChunks, fTotalCombinedVerbCnt);
     const SkStrokeRec* strokeForTolerances = nullptr;
 
-    for (const auto& pathStroke : fPathStrokeList) {
-        const SkStrokeRec& stroke = pathStroke.fStroke;
+    for (PathStrokeList* pathStroke = fPathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
+        const SkStrokeRec& stroke = pathStroke->fStroke;
         if (!strokeForTolerances || strokeForTolerances->getWidth() != stroke.getWidth() ||
             strokeForTolerances->getCap() != stroke.getCap()) {
             auto tolerances = Tolerances::MakePreTransform(matrixScales.data(), stroke.getWidth());
@@ -667,10 +667,10 @@
             patchWriter.updateDynamicStroke(stroke);
         }
         if (fShaderFlags & ShaderFlags::kDynamicColor) {
-            patchWriter.updateDynamicColor(pathStroke.fColor);
+            patchWriter.updateDynamicColor(pathStroke->fColor);
         }
 
-        const SkPath& path = pathStroke.fPath;
+        const SkPath& path = pathStroke->fPath;
         auto strokeJoinType = JoinType(stroke.getJoin());
         SkPathVerb previousVerb = SkPathVerb::kClose;
         for (auto [verb, p, w] : SkPathPriv::Iterate(path)) {
diff --git a/src/gpu/tessellate/GrStrokeHardwareTessellator.h b/src/gpu/tessellate/GrStrokeHardwareTessellator.h
index 37a4041..63f7fe0 100644
--- a/src/gpu/tessellate/GrStrokeHardwareTessellator.h
+++ b/src/gpu/tessellate/GrStrokeHardwareTessellator.h
@@ -26,8 +26,7 @@
         int fBasePatch;
     };
 
-    GrStrokeHardwareTessellator(ShaderFlags shaderFlags,
-                                GrSTArenaList<PathStroke>&& pathStrokeList,
+    GrStrokeHardwareTessellator(ShaderFlags shaderFlags, PathStrokeList* pathStrokeList,
                                 int totalCombinedVerbCnt, const GrShaderCaps& shaderCaps)
             : GrStrokeTessellator(shaderFlags, std::move(pathStrokeList))
             , fTotalCombinedVerbCnt(totalCombinedVerbCnt) {
diff --git a/src/gpu/tessellate/GrStrokeIndirectTessellator.cpp b/src/gpu/tessellate/GrStrokeIndirectTessellator.cpp
index b3ae838..799a65f 100644
--- a/src/gpu/tessellate/GrStrokeIndirectTessellator.cpp
+++ b/src/gpu/tessellate/GrStrokeIndirectTessellator.cpp
@@ -441,7 +441,7 @@
 
 GrStrokeIndirectTessellator::GrStrokeIndirectTessellator(ShaderFlags shaderFlags,
                                                          const SkMatrix& viewMatrix,
-                                                         GrSTArenaList<PathStroke>&& pathStrokeList,
+                                                         PathStrokeList* pathStrokeList,
                                                          int totalCombinedVerbCnt,
                                                          SkArenaAlloc* alloc)
         : GrStrokeTessellator(shaderFlags, std::move(pathStrokeList)) {
@@ -464,8 +464,8 @@
 
     float lastStrokeWidth = -1;
     SkPoint lastControlPoint = {0,0};
-    for (const auto& pathStroke : fPathStrokeList) {
-        const SkStrokeRec& stroke = pathStroke.fStroke;
+    for (PathStrokeList* pathStroke = fPathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
+        const SkStrokeRec& stroke = pathStroke->fStroke;
         SkASSERT(stroke.getWidth() >= 0);  // Otherwise we can't initialize lastStrokeWidth=-1.
         if (stroke.getWidth() != lastStrokeWidth ||
             (stroke.getJoin() == SkPaint::kRound_Join) != counter.isRoundJoin()) {
@@ -475,7 +475,7 @@
         fMaxNumExtraEdgesInJoin = std::max(fMaxNumExtraEdgesInJoin,
                 GrStrokeTessellateShader::NumExtraEdgesInIndirectJoin(stroke.getJoin()));
         // Iterate through each verb in the stroke, counting its resolveLevel(s).
-        GrStrokeIterator iter(pathStroke.fPath, &stroke, &viewMatrix);
+        GrStrokeIterator iter(pathStroke->fPath, &stroke, &viewMatrix);
         while (iter.next()) {
             using Verb = GrStrokeIterator::Verb;
             Verb verb = iter.verb();
@@ -816,17 +816,17 @@
     int8_t resolveLevel;
 
     // Now write out each instance to its resolveLevel's designated location in the instance buffer.
-    for (const auto& pathStroke : fPathStrokeList) {
-        const SkStrokeRec& stroke = pathStroke.fStroke;
+    for (PathStrokeList* pathStroke = fPathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
+        const SkStrokeRec& stroke = pathStroke->fStroke;
         SkASSERT(stroke.getJoin() != SkPaint::kMiter_Join || numExtraEdgesInJoin == 4);
         bool isRoundJoin = (stroke.getJoin() == SkPaint::kRound_Join);
         if (fShaderFlags & ShaderFlags::kDynamicStroke) {
             binningWriter.updateDynamicStroke(stroke);
         }
         if (fShaderFlags & ShaderFlags::kDynamicColor) {
-            binningWriter.updateDynamicColor(pathStroke.fColor);
+            binningWriter.updateDynamicColor(pathStroke->fColor);
         }
-        GrStrokeIterator iter(pathStroke.fPath, &stroke, &viewMatrix);
+        GrStrokeIterator iter(pathStroke->fPath, &stroke, &viewMatrix);
         bool hasLastControlPoint = false;
         while (iter.next()) {
             using Verb = GrStrokeIterator::Verb;
diff --git a/src/gpu/tessellate/GrStrokeIndirectTessellator.h b/src/gpu/tessellate/GrStrokeIndirectTessellator.h
index 9552ff3..8af6d6c 100644
--- a/src/gpu/tessellate/GrStrokeIndirectTessellator.h
+++ b/src/gpu/tessellate/GrStrokeIndirectTessellator.h
@@ -22,7 +22,7 @@
     // become an issue if we try to draw a stroke with an astronomically wide width.
     constexpr static int8_t kMaxResolveLevel = 15;
 
-    GrStrokeIndirectTessellator(ShaderFlags, const SkMatrix&, GrSTArenaList<PathStroke>&&,
+    GrStrokeIndirectTessellator(ShaderFlags, const SkMatrix&, PathStrokeList*,
                                 int totalCombinedVerbCnt, SkArenaAlloc*);
 
     // Adds the given tessellator to our chain. The chained tessellators all append to a shared
diff --git a/src/gpu/tessellate/GrStrokeTessellateOp.cpp b/src/gpu/tessellate/GrStrokeTessellateOp.cpp
index f8fbfea..a278b0b 100644
--- a/src/gpu/tessellate/GrStrokeTessellateOp.cpp
+++ b/src/gpu/tessellate/GrStrokeTessellateOp.cpp
@@ -63,7 +63,7 @@
                                                         bool hasMixedSampledCoverage,
                                                         GrClampType clampType) {
     // Make sure the finalize happens before combining. We might change fNeedsStencil here.
-    SkASSERT(fPathStrokeList.begin().fCurr->fNext == nullptr);
+    SkASSERT(fPathStrokeList.fNext == nullptr);
     SkASSERT(fAAType != GrAAType::kCoverage || hasMixedSampledCoverage);
     const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
             this->headColor(), GrProcessorAnalysisCoverage::kNone, clip,
@@ -120,9 +120,16 @@
         return CombineResult::kMayChain;
     }
 
-    fPathStrokeList.concat(std::move(op->fPathStrokeList), alloc);
-    fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
     fShaderFlags = combinedFlags;
+
+    // Concat the op's PathStrokeList. Since the head element is allocated inside the op, we need to
+    // copy it.
+    auto* headCopy = alloc->make<PathStrokeList>(std::move(op->fPathStrokeList));
+    *fPathStrokeTail = headCopy;
+    fPathStrokeTail = (op->fPathStrokeTail == &op->fPathStrokeList.fNext) ? &headCopy->fNext
+                                                                          : op->fPathStrokeTail;
+
+    fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
     return CombineResult::kMerged;
 }
 
@@ -163,8 +170,7 @@
     if (this->canUseHardwareTessellation(caps) &&
         ((fShaderFlags & ShaderFlags::kDynamicColor) || fTotalCombinedVerbCnt > 50)) {
         SkASSERT(!this->nextInChain());  // We never chain when hw tessellation is an option.
-        fTessellator = arena->make<GrStrokeHardwareTessellator>(fShaderFlags,
-                                                                std::move(fPathStrokeList),
+        fTessellator = arena->make<GrStrokeHardwareTessellator>(fShaderFlags, &fPathStrokeList,
                                                                 fTotalCombinedVerbCnt,
                                                                 *caps.shaderCaps());
         shaderMode = GrStrokeTessellateShader::Mode::kTessellation;
@@ -184,15 +190,14 @@
             }
         }
         auto* headTessellator = arena->make<GrStrokeIndirectTessellator>(
-                fShaderFlags, fViewMatrix, std::move(fPathStrokeList), fTotalCombinedVerbCnt,
-                arena);
+                fShaderFlags, fViewMatrix, &fPathStrokeList, fTotalCombinedVerbCnt, arena);
         // Make a tessellator for every chained op after us. These will all append to the head
         // tessellator's shared indirect-draw list during prepare().
         for (GrStrokeTessellateOp* op = this->nextInChain(); op; op = op->nextInChain()) {
             SkASSERT(fViewMatrix == op->fViewMatrix);
             auto* chainedTessellator = arena->make<GrStrokeIndirectTessellator>(
-                    fShaderFlags, fViewMatrix, std::move(op->fPathStrokeList),
-                    op->fTotalCombinedVerbCnt, arena);
+                    fShaderFlags, fViewMatrix, &op->fPathStrokeList, op->fTotalCombinedVerbCnt,
+                    arena);
             headTessellator->addToChain(chainedTessellator);
         }
         fTessellator = headTessellator;
diff --git a/src/gpu/tessellate/GrStrokeTessellateOp.h b/src/gpu/tessellate/GrStrokeTessellateOp.h
index 819d69e..feb2f78 100644
--- a/src/gpu/tessellate/GrStrokeTessellateOp.h
+++ b/src/gpu/tessellate/GrStrokeTessellateOp.h
@@ -9,7 +9,6 @@
 #define GrStrokeTessellateOp_DEFINED
 
 #include "include/core/SkStrokeRec.h"
-#include "src/gpu/GrSTArenaList.h"
 #include "src/gpu/ops/GrMeshDrawOp.h"
 #include "src/gpu/tessellate/GrPathShader.h"
 #include "src/gpu/tessellate/GrStrokeTessellateShader.h"
@@ -21,16 +20,17 @@
 public:
     using ShaderFlags = GrStrokeTessellateShader::ShaderFlags;
 
-    struct PathStroke {
-        PathStroke(const SkPath& path, const SkStrokeRec& stroke, const SkPMColor4f& color)
+    struct PathStrokeList {
+        PathStrokeList(const SkPath& path, const SkStrokeRec& stroke, const SkPMColor4f& color)
                 : fPath(path), fStroke(stroke), fColor(color) {}
         SkPath fPath;
         SkStrokeRec fStroke;
         SkPMColor4f fColor;
+        PathStrokeList* fNext = nullptr;
     };
 
-    GrStrokeTessellator(ShaderFlags shaderFlags, GrSTArenaList<PathStroke>&& pathStrokeList)
-            : fShaderFlags(shaderFlags), fPathStrokeList(std::move(pathStrokeList)) {}
+    GrStrokeTessellator(ShaderFlags shaderFlags, PathStrokeList* pathStrokeList)
+            : fShaderFlags(shaderFlags), fPathStrokeList(pathStrokeList) {}
 
     // Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
     virtual void prepare(GrMeshDrawOp::Target*, const SkMatrix&) = 0;
@@ -43,7 +43,7 @@
 
 protected:
     const ShaderFlags fShaderFlags;
-    const GrSTArenaList<PathStroke> fPathStrokeList;
+    PathStrokeList* fPathStrokeList;
 };
 
 // Renders strokes by linearizing them into sorted "parametric" and "radial" edges. See
@@ -54,11 +54,11 @@
 
 private:
     using ShaderFlags = GrStrokeTessellateShader::ShaderFlags;
-    using PathStroke = GrStrokeTessellator::PathStroke;
+    using PathStrokeList = GrStrokeTessellator::PathStrokeList;
     DEFINE_OP_CLASS_ID
 
-    SkStrokeRec& headStroke() { return fPathStrokeList.head().fStroke; }
-    SkPMColor4f& headColor() { return fPathStrokeList.head().fColor; }
+    SkStrokeRec& headStroke() { return fPathStrokeList.fStroke; }
+    SkPMColor4f& headColor() { return fPathStrokeList.fColor; }
     GrStrokeTessellateOp* nextInChain() const {
         return static_cast<GrStrokeTessellateOp*>(this->GrDrawOp::nextInChain());
     }
@@ -103,7 +103,8 @@
     const GrAAType fAAType;
     const SkMatrix fViewMatrix;
     ShaderFlags fShaderFlags = ShaderFlags::kNone;
-    GrSTArenaList<PathStroke> fPathStrokeList;
+    PathStrokeList fPathStrokeList;
+    PathStrokeList** fPathStrokeTail = &fPathStrokeList.fNext;
     int fTotalCombinedVerbCnt = 0;
     GrProcessorSet fProcessors;
     bool fNeedsStencil = false;
diff --git a/tests/StrokeIndirectTest.cpp b/tests/StrokeIndirectTest.cpp
index b920b77..9b67b66 100644
--- a/tests/StrokeIndirectTest.cpp
+++ b/tests/StrokeIndirectTest.cpp
@@ -41,9 +41,10 @@
         for (int i = 0; i < 16; ++i) {
             float scale = ldexpf(rand.nextF() + 1, i);
             auto matrix = SkMatrix::Scale(scale, scale);
+            GrStrokeTessellator::PathStrokeList pathStrokeList(path, stroke, SK_PMColor4fWHITE);
             GrStrokeIndirectTessellator tessellator(GrStrokeTessellateShader::ShaderFlags::kNone,
-                                                    matrix, {path, stroke, SK_PMColor4fWHITE},
-                                                    path.countVerbs(), target->allocator());
+                                                    matrix, &pathStrokeList, path.countVerbs(),
+                                                    target->allocator());
             tessellator.verifyResolveLevels(r, target, matrix, path, stroke);
             tessellator.prepare(target, matrix);
             tessellator.verifyBuffers(r, target, matrix, stroke);