introduce a subrun linked list

Create a simple linked list to sequence the subruns preparing
the way for a more focused allocator for managing subruns
at the end of the GrTextBlob.

Change-Id: I595e2ce2810d161332a23405e4615724d2953471
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/366957
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp
index 1a251b9..3e0da52 100644
--- a/bench/GlyphQuadFillBench.cpp
+++ b/bench/GlyphQuadFillBench.cpp
@@ -57,15 +57,15 @@
                     glyphRun, view, drawOrigin, drawPaint, props, false, options, fBlob.get());
         }
 
-        SkASSERT(fBlob->subRunList().head() != nullptr);
-        GrAtlasSubRun* subRun = fBlob->subRunList().head()->testingOnly_atlasSubRun();
+        SkASSERT(!fBlob->subRunList().isEmpty());
+        GrAtlasSubRun* subRun = fBlob->subRunList().front().testingOnly_atlasSubRun();
         SkASSERT(subRun);
         subRun->testingOnly_packedGlyphIDToGrGlyph(&fCache);
         fVertices.reset(new char[subRun->vertexStride(view) * subRun->glyphCount() * 4]);
     }
 
     void onDraw(int loops, SkCanvas* canvas) override {
-        GrAtlasSubRun* subRun = fBlob->subRunList().head()->testingOnly_atlasSubRun();
+        GrAtlasSubRun* subRun = fBlob->subRunList().front().testingOnly_atlasSubRun();
         SkASSERT(subRun);
 
         SkIRect clip = SkIRect::MakeEmpty();
diff --git a/src/gpu/GrSurfaceDrawContext.cpp b/src/gpu/GrSurfaceDrawContext.cpp
index a95319b..5f78d58 100644
--- a/src/gpu/GrSurfaceDrawContext.cpp
+++ b/src/gpu/GrSurfaceDrawContext.cpp
@@ -443,8 +443,8 @@
         }
     }
 
-    for (GrSubRun* subRun : blob->subRunList()) {
-        subRun->draw(clip, viewMatrix, glyphRunList, this);
+    for (GrSubRun& subRun : blob->subRunList()) {
+        subRun.draw(clip, viewMatrix, glyphRunList, this);
     }
 }
 
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 91fcf3f..3f217e7 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -470,11 +470,11 @@
             rtc->surfaceProps(),
             rContext->priv().caps()->shaderCaps()->supportsDistanceFieldText(),
             SDFOptions, blob.get());
-    if (!blob->subRunList().head()) {
+    if (blob->subRunList().isEmpty()) {
         return nullptr;
     }
 
-    GrAtlasSubRun* subRun = blob->subRunList().head()->testingOnly_atlasSubRun();
+    GrAtlasSubRun* subRun = blob->subRunList().front().testingOnly_atlasSubRun();
     SkASSERT(subRun);
     GrOp::Owner op;
     std::tie(std::ignore, op) = subRun->makeAtlasTextOp(nullptr, mtxProvider, glyphRunList, rtc);
diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp
index ceadf58..2bd4df5 100644
--- a/src/gpu/text/GrTextBlob.cpp
+++ b/src/gpu/text/GrTextBlob.cpp
@@ -1429,8 +1429,8 @@
         return false;
     }
 
-    for (GrSubRun* subRun : this->subRunList()) {
-        if (!subRun->canReuse(paint, drawMatrix)) {
+    for (GrSubRun& subRun : this->fSubRunList) {
+        if (!subRun.canReuse(paint, drawMatrix)) {
             return false;
         }
     }
@@ -1451,7 +1451,7 @@
     auto addSameFormat = [&](const SkZip<SkGlyphVariant, SkPoint>& drawable, GrMaskFormat format) {
         GrSubRun* subRun = addSingle(drawable, strikeSpec, format, this, &fAlloc);
         if (subRun != nullptr) {
-            this->insertSubRun(subRun);
+            fSubRunList.append(subRun);
         } else {
             fSomeGlyphsExcluded = true;
         }
@@ -1483,10 +1483,6 @@
         , fInitialLuminance{initialLuminance}
         , fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { }
 
-void GrTextBlob::insertSubRun(GrSubRun* subRun) {
-    fSubRunList.addToTail(subRun);
-}
-
 void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                     const SkStrikeSpec& strikeSpec) {
 
@@ -1501,7 +1497,7 @@
                                         strikeSpec,
                                         *this,
                                         &fAlloc);
-    this->insertSubRun(subRun);
+    fSubRunList.append(subRun);
 }
 
 void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
@@ -1511,7 +1507,7 @@
                                    SkScalar maxScale) {
     this->setMinAndMaxScale(minScale, maxScale);
     GrSubRun* subRun = SDFTSubRun::Make(drawables, runFont, strikeSpec, this, &fAlloc);
-    this->insertSubRun(subRun);
+    fSubRunList.append(subRun);
 }
 
 void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index 3807101..bdd59cb 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -37,125 +37,6 @@
 class SkTextBlob;
 class SkTextBlobRunIterator;
 
-// A GrTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
-// on the GPU.  These are initially created with valid positions and colors, but invalid
-// texture coordinates.
-//
-// A GrTextBlob contains a number of SubRuns that are created in the blob's arena. Each SubRun
-// tracks its own GrGlyph* and vertex data. The memory is organized in the arena in the following
-// way so that the pointers for the GrGlyph* and vertex data are known before creating the SubRun.
-//
-//  GrGlyph*... | vertexData... | SubRun | GrGlyph*... | vertexData... | SubRun  etc.
-//
-// In these classes, I'm trying to follow the convention about matrices and origins.
-// * draw Matrix|Origin    - describes the current draw command.
-// * initial Matrix - describes the combined initial matrix and origin the GrTextBlob was created
-//   with.
-//
-//
-class GrTextBlob final : public SkNVRefCnt<GrTextBlob>, public SkGlyphRunPainterInterface {
-public:
-    struct Key {
-        Key();
-        uint32_t fUniqueID;
-        // Color may affect the gamma of the mask we generate, but in a fairly limited way.
-        // Each color is assigned to on of a fixed number of buckets based on its
-        // luminance. For each luminance bucket there is a "canonical color" that
-        // represents the bucket.  This functionality is currently only supported for A8
-        SkColor fCanonicalColor;
-        SkPaint::Style fStyle;
-        SkScalar fFrameWidth;
-        SkScalar fMiterLimit;
-        SkPaint::Join fJoin;
-        SkPixelGeometry fPixelGeometry;
-        bool fHasBlur;
-        SkMaskFilterBase::BlurRec fBlurRec;
-        uint32_t fScalerContextFlags;
-
-        bool operator==(const Key& other) const;
-    };
-
-    SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrTextBlob);
-
-    // Change memory management to handle the data after GrTextBlob, but in the same allocation
-    // of memory. Only allow placement new.
-    void operator delete(void* p);
-    void* operator new(size_t);
-    void* operator new(size_t, void* p);
-
-    ~GrTextBlob() override;
-
-    // Make an empty GrTextBlob, with all the invariants set to make the right decisions when
-    // adding SubRuns.
-    static sk_sp<GrTextBlob> Make(const SkGlyphRunList& glyphRunList,
-                                  const SkMatrix& drawMatrix);
-
-    static const Key& GetKey(const GrTextBlob& blob);
-    static uint32_t Hash(const Key& key);
-
-    void addKey(const Key& key);
-    bool hasPerspective() const;
-    const SkMatrix& initialMatrix() const { return fInitialMatrix; }
-
-    void setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax);
-    std::tuple<SkScalar, SkScalar> scaleBounds() const {
-        return {fMaxMinScale, fMinMaxScale};
-    }
-
-    bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix);
-
-    const Key& key() const;
-    size_t size() const;
-
-    template<typename AddSingleMaskFormat>
-    void addMultiMaskFormat(
-            AddSingleMaskFormat addSingle,
-            const SkZip<SkGlyphVariant, SkPoint>& drawables,
-            const SkStrikeSpec& strikeSpec);
-
-    const SkTInternalLList<GrSubRun>& subRunList() const { return fSubRunList; }
-
-private:
-    GrTextBlob(size_t allocSize, const SkMatrix& drawMatrix, SkColor initialLuminance);
-
-    void insertSubRun(GrSubRun* subRun);
-
-    // Methods to satisfy SkGlyphRunPainterInterface
-    void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                            const SkStrikeSpec& strikeSpec) override;
-    void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                            const SkFont& runFont,
-                            const SkStrikeSpec& strikeSpec) override;
-    void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                           const SkStrikeSpec& strikeSpec,
-                           const SkFont& runFont,
-                           SkScalar minScale,
-                           SkScalar maxScale) override;
-    void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                            const SkStrikeSpec& strikeSpec) override;
-
-    // Overall size of this struct plus vertices and glyphs at the end.
-    const size_t fSize;
-
-    // The initial view matrix combined with the initial origin. Used to determine if a cached
-    // subRun can be used in this draw situation.
-    const SkMatrix fInitialMatrix;
-
-    const SkColor fInitialLuminance;
-
-    Key fKey;
-
-    // We can reuse distance field text, but only if the new view matrix would not result in
-    // a mip change.  Because there can be multiple runs in a blob, we track the overall
-    // maximum minimum scale, and minimum maximum scale, we can support before we need to regen
-    SkScalar fMaxMinScale{-SK_ScalarMax};
-    SkScalar fMinMaxScale{SK_ScalarMax};
-
-    bool fSomeGlyphsExcluded{false};
-    SkTInternalLList<GrSubRun> fSubRunList;
-    SkArenaAlloc fAlloc;
-};
-
 // -- GrAtlasSubRun --------------------------------------------------------------------------------
 // GrAtlasSubRun is the API that GrAtlasTextOp uses to generate vertex data for drawing.
 //     There are three different ways GrAtlasSubRun is specialized.
@@ -222,7 +103,158 @@
     // * Don't use this API. It is only to support testing.
     virtual GrAtlasSubRun* testingOnly_atlasSubRun() = 0;
 
+    GrSubRun* fNext{nullptr};
+};
+
+struct GrSubRunList {
+    class Iterator {
+    public:
+        using value_type = GrSubRun;
+        using difference_type = ptrdiff_t;
+        using pointer = value_type*;
+        using reference = value_type&;
+        using iterator_category = std::input_iterator_tag;
+        constexpr Iterator(GrSubRun* subRun) : fPtr{subRun} { }
+        constexpr Iterator& operator++() { fPtr = fPtr->fNext; return *this; }
+        constexpr Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
+        constexpr bool operator==(const Iterator& rhs) const { return fPtr == rhs.fPtr; }
+        constexpr bool operator!=(const Iterator& rhs) const { return fPtr != rhs.fPtr; }
+        constexpr reference operator*() { return *fPtr; }
+
+    private:
+        GrSubRun* fPtr;
+    };
+
+    void append(GrSubRun* subRun) {
+        GrSubRun** newTail = &subRun->fNext;
+        *fTail = subRun;
+        fTail = newTail;
+    }
+    bool isEmpty() const { return fHead == nullptr; }
+    Iterator begin() { return Iterator{fHead}; }
+    Iterator end() { return Iterator{nullptr}; }
+    GrSubRun& front() const {return *fHead; }
+
+    GrSubRun* fHead{nullptr};
+    GrSubRun** fTail{&fHead};
+};
+
+// A GrTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
+// on the GPU.  These are initially created with valid positions and colors, but invalid
+// texture coordinates.
+//
+// A GrTextBlob contains a number of SubRuns that are created in the blob's arena. Each SubRun
+// tracks its own GrGlyph* and vertex data. The memory is organized in the arena in the following
+// way so that the pointers for the GrGlyph* and vertex data are known before creating the SubRun.
+//
+//  GrGlyph*... | vertexData... | SubRun | GrGlyph*... | vertexData... | SubRun  etc.
+//
+// In these classes, I'm trying to follow the convention about matrices and origins.
+// * draw Matrix|Origin    - describes the current draw command.
+// * initial Matrix - describes the combined initial matrix and origin the GrTextBlob was created
+//   with.
+//
+//
+class GrTextBlob final : public SkNVRefCnt<GrTextBlob>, public SkGlyphRunPainterInterface {
+public:
+    struct Key {
+        Key();
+        uint32_t fUniqueID;
+        // Color may affect the gamma of the mask we generate, but in a fairly limited way.
+        // Each color is assigned to on of a fixed number of buckets based on its
+        // luminance. For each luminance bucket there is a "canonical color" that
+        // represents the bucket.  This functionality is currently only supported for A8
+        SkColor fCanonicalColor;
+        SkPaint::Style fStyle;
+        SkScalar fFrameWidth;
+        SkScalar fMiterLimit;
+        SkPaint::Join fJoin;
+        SkPixelGeometry fPixelGeometry;
+        bool fHasBlur;
+        SkMaskFilterBase::BlurRec fBlurRec;
+        uint32_t fScalerContextFlags;
+
+        bool operator==(const Key& other) const;
+    };
+
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrTextBlob);
+
+    // Make an empty GrTextBlob, with all the invariants set to make the right decisions when
+    // adding SubRuns.
+    static sk_sp<GrTextBlob> Make(const SkGlyphRunList& glyphRunList,
+                                  const SkMatrix& drawMatrix);
+
+    ~GrTextBlob() override;
+
+    // Change memory management to handle the data after GrTextBlob, but in the same allocation
+    // of memory. Only allow placement new.
+    void operator delete(void* p);
+    void* operator new(size_t);
+    void* operator new(size_t, void* p);
+
+    static const Key& GetKey(const GrTextBlob& blob);
+    static uint32_t Hash(const Key& key);
+
+    void addKey(const Key& key);
+    bool hasPerspective() const;
+    const SkMatrix& initialMatrix() const { return fInitialMatrix; }
+
+    void setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax);
+    std::tuple<SkScalar, SkScalar> scaleBounds() const {
+        return {fMaxMinScale, fMinMaxScale};
+    }
+
+    bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix);
+
+    const Key& key() const;
+    size_t size() const;
+
+    template<typename AddSingleMaskFormat>
+    void addMultiMaskFormat(
+            AddSingleMaskFormat addSingle,
+            const SkZip<SkGlyphVariant, SkPoint>& drawables,
+            const SkStrikeSpec& strikeSpec);
+
+    GrSubRunList& subRunList() {
+        return fSubRunList;
+    }
+
 private:
-    SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrSubRun);
+    GrTextBlob(size_t allocSize, const SkMatrix& drawMatrix, SkColor initialLuminance);
+
+    // Methods to satisfy SkGlyphRunPainterInterface
+    void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                            const SkStrikeSpec& strikeSpec) override;
+    void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                            const SkFont& runFont,
+                            const SkStrikeSpec& strikeSpec) override;
+    void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                           const SkStrikeSpec& strikeSpec,
+                           const SkFont& runFont,
+                           SkScalar minScale,
+                           SkScalar maxScale) override;
+    void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                            const SkStrikeSpec& strikeSpec) override;
+
+    // Overall size of this struct plus vertices and glyphs at the end.
+    const size_t fSize;
+
+    // The initial view matrix combined with the initial origin. Used to determine if a cached
+    // subRun can be used in this draw situation.
+    const SkMatrix fInitialMatrix;
+
+    const SkColor fInitialLuminance;
+
+    Key fKey;
+
+    // We can reuse distance field text, but only if the new view matrix would not result in
+    // a mip change.  Because there can be multiple runs in a blob, we track the overall
+    // maximum minimum scale, and minimum maximum scale, we can support before we need to regen
+    SkScalar fMaxMinScale{-SK_ScalarMax};
+    SkScalar fMinMaxScale{SK_ScalarMax};
+
+    bool fSomeGlyphsExcluded{false};
+    GrSubRunList fSubRunList;
+    SkArenaAlloc fAlloc;
 };
 #endif  // GrTextBlob_DEFINED