Put SubRuns in an alloc on GrTextBlob

This improves performance when there are multiple runs.

Bug: chromium:1029972

Change-Id: If32ddb2baf974ee1af7833710bd10a60e39c0169
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/258736
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp
index e8f08bb..7ae8d52 100644
--- a/src/gpu/text/GrTextBlob.cpp
+++ b/src/gpu/text/GrTextBlob.cpp
@@ -20,10 +20,6 @@
 
 #include <new>
 
-template <size_t N> static size_t sk_align(size_t s) {
-    return ((s + (N-1)) / N) * N;
-}
-
 static void calculate_translation(bool applyVM,
                                   const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY,
                                   const SkMatrix& currentViewMatrix, SkScalar currentX,
@@ -82,6 +78,7 @@
         , fY{textBlob->fInitialOrigin.y()}
         , fCurrentViewMatrix{textBlob->fInitialViewMatrix} {
     SkASSERT(type != kTransformedPath);
+    textBlob->insertSubRun(this);
 }
 
 GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec)
@@ -95,7 +92,9 @@
         , fStrikeSpec{strikeSpec}
         , fStrike{nullptr}
         , fColor{textBlob->fColor}
-        , fPaths{} { }
+        , fPaths{} {
+    textBlob->insertSubRun(this);
+}
 
 void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
     GrTextStrike* grStrike = fStrike.get();
@@ -223,32 +222,34 @@
                                    bool forceWForDistanceFields) {
 
     static_assert(sizeof(ARGB2DVertex) <= sizeof(Mask2DVertex));
-    size_t vertexSize = sizeof(Mask2DVertex);
+    static_assert(alignof(ARGB2DVertex) <= alignof(Mask2DVertex));
+    size_t quadSize = sizeof(Mask2DVertex) * kVerticesPerGlyph;
     if (viewMatrix.hasPerspective() || forceWForDistanceFields) {
         static_assert(sizeof(ARGB3DVertex) <= sizeof(SDFT3DVertex));
-        vertexSize = sizeof(SDFT3DVertex);
+        static_assert(alignof(ARGB3DVertex) <= alignof(SDFT3DVertex));
+        quadSize = sizeof(SDFT3DVertex) * kVerticesPerGlyph;
     }
 
-    size_t quadSize = kVerticesPerGlyph * vertexSize;
+    // We can use the alignment of SDFT3DVertex as a proxy for all Vertex alignments.
+    static_assert(alignof(SDFT3DVertex) >= alignof(Mask2DVertex));
 
-    size_t glyphCount = glyphRunList.totalGlyphCount();
-    // We allocate size for the GrTextBlob itself, plus size for the vertices array,
-    // and size for the glyphIds array.
-    size_t verticesCount = glyphCount * quadSize;
+    size_t subRunsOffset = sizeof(GrTextBlob);
+    size_t subRunsSize = glyphRunList.runCount() * sizeof(SubRun);
+    static_assert(alignof(SubRun) >= alignof(GrGlyph*));
+    size_t glyphsOffset = subRunsOffset + subRunsSize;
+    static_assert(alignof(GrGlyph*) >= alignof(SDFT3DVertex));
+    size_t vertexOffset = glyphsOffset + sizeof(GrGlyph*) * glyphRunList.totalGlyphCount();
+    size_t allocationSize = vertexOffset + quadSize * glyphRunList.totalGlyphCount();
 
-    size_t blobStart = 0;
-    size_t vertex = sk_align<alignof(char)>     (blobStart + sizeof(GrTextBlob) * 1);
-    size_t glyphs = sk_align<alignof(GrGlyph*)> (vertex + sizeof(char) * verticesCount);
-    size_t   size =                             (glyphs + sizeof(GrGlyph*) * glyphCount);
-
-    void* allocation = ::operator new (size);
+    void* allocation = ::operator new (allocationSize);
 
     sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{
-        size, strikeCache, viewMatrix, glyphRunList.origin(), color, forceWForDistanceFields}};
+            subRunsSize, strikeCache, viewMatrix, glyphRunList.origin(),
+            color, forceWForDistanceFields}};
 
     // setup offsets for vertices / glyphs
-    blob->fVertices = SkTAddOffset<char>(blob.get(), vertex);
-    blob->fGlyphs   = SkTAddOffset<GrGlyph*>(blob.get(), glyphs);
+    blob->fVertices = SkTAddOffset<char>(blob.get(), vertexOffset);
+    blob->fGlyphs   = SkTAddOffset<GrGlyph*>(blob.get(), glyphsOffset);
 
     return blob;
 }
@@ -286,7 +287,9 @@
 const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; }
 uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); }
 
-bool GrTextBlob::hasDistanceField() const { return SkToBool(fTextType & kHasDistanceField_TextType); }
+bool GrTextBlob::hasDistanceField() const {
+    return SkToBool(fTextType & kHasDistanceField_TextType);
+}
 bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
 bool GrTextBlob::hasPerspective() const { return fInitialViewMatrix.hasPerspective(); }
 
@@ -401,10 +404,10 @@
                        const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
                        const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
 
-    for (auto& subRun : fSubRuns) {
-        if (subRun.drawAsPaths()) {
+    for (SubRun* subRun = fFirstSubRun; subRun != nullptr; subRun = subRun->fNextSubRun) {
+        if (subRun->drawAsPaths()) {
             SkPaint runPaint{paint};
-            runPaint.setAntiAlias(subRun.isAntiAliased());
+            runPaint.setAntiAlias(subRun->isAntiAliased());
             // If there are shaders, blurs or styles, the path must be scaled into source
             // space independently of the CTM. This allows the CTM to be correct for the
             // different effects.
@@ -417,9 +420,10 @@
             // The origin for the blob may have changed, so figure out the delta.
             SkVector originShift = SkPoint{x, y} - fInitialOrigin;
 
-            for (const auto& pathGlyph : subRun.fPaths) {
+            for (const auto& pathGlyph : subRun->fPaths) {
                 SkMatrix ctm{viewMatrix};
-                SkMatrix pathMatrix = SkMatrix::MakeScale(subRun.fStrikeSpec.strikeToSourceRatio());
+                SkMatrix pathMatrix = SkMatrix::MakeScale(
+                        subRun->fStrikeSpec.strikeToSourceRatio());
                 // Shift the original glyph location in source space to the position of the new
                 // blob.
                 pathMatrix.postTranslate(originShift.x() + pathGlyph.fOrigin.x(),
@@ -447,7 +451,7 @@
                 target->drawShape(clip, runPaint, ctm, shape);
             }
         } else {
-            int glyphCount = subRun.glyphCount();
+            int glyphCount = subRun->glyphCount();
             if (0 == glyphCount) {
                 continue;
             }
@@ -460,13 +464,13 @@
             GrAA aa;
             // We can clip geometrically if we're not using SDFs or transformed glyphs,
             // and we have an axis-aligned rectangular non-AA clip
-            if (!subRun.drawAsDistanceFields() && !subRun.needsTransform() &&
+            if (!subRun->drawAsDistanceFields() && !subRun->needsTransform() &&
                 clip.isRRect(rtBounds, &clipRRect, &aa) &&
                 clipRRect.isRect() && GrAA::kNo == aa) {
                 skipClip = true;
                 // We only need to do clipping work if the subrun isn't contained by the clip
                 SkRect subRunBounds;
-                this->computeSubRunBounds(&subRunBounds, subRun, viewMatrix, x, y, false);
+                this->computeSubRunBounds(&subRunBounds, *subRun, viewMatrix, x, y, false);
                 if (!clipRRect.getBounds().contains(subRunBounds)) {
                     // If the subrun is completely outside, don't add an op for it
                     if (!clipRRect.getBounds().intersects(subRunBounds)) {
@@ -479,7 +483,7 @@
             }
 
             if (submitOp) {
-                auto op = this->makeOp(subRun, glyphCount, viewMatrix, x, y,
+                auto op = this->makeOp(*subRun, glyphCount, viewMatrix, x, y,
                                        clipRect, paint, filteredColor, props, distanceAdjustTable,
                                        target);
                 if (op) {
@@ -538,9 +542,9 @@
         SkScalar x, SkScalar y, const SkPaint& paint, const SkPMColor4f& filteredColor,
         const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
         GrTextTarget* target) {
-    GrTextBlob::SubRun& info = fSubRuns[0];
+    SubRun* info = fFirstSubRun;
     SkIRect emptyRect = SkIRect::MakeEmpty();
-    return this->makeOp(info, glyphCount, viewMatrix, x, y, emptyRect,
+    return this->makeOp(*info, glyphCount, viewMatrix, x, y, emptyRect,
                         paint, filteredColor, props, distanceAdjustTable, target);
 }
 
@@ -573,12 +577,12 @@
 
     sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
 
-    SubRun& subRun = fSubRuns.emplace_back(
+    SubRun* subRun = fAlloc.make<SubRun>(
             type, this, strikeSpec, format, bufferSpec, std::move(grStrike));
 
-    subRun.appendGlyphs(drawables);
+    subRun->appendGlyphs(drawables);
 
-    return &subRun;
+    return subRun;
 }
 
 void GrTextBlob::addSingleMaskFormat(
@@ -627,21 +631,32 @@
     subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
 }
 
-GrTextBlob::GrTextBlob(size_t size,
+GrTextBlob::GrTextBlob(size_t allocSize,
                        GrStrikeCache* strikeCache,
                        const SkMatrix& viewMatrix,
                        SkPoint origin,
                        GrColor color,
                        bool forceWForDistanceFields)
-        : fSize{size}
+        : fSize{allocSize}
         , fStrikeCache{strikeCache}
         , fInitialViewMatrix{viewMatrix}
         , fInitialViewMatrixInverse{make_inverse(viewMatrix)}
         , fInitialOrigin{origin}
         , fForceWForDistanceFields{forceWForDistanceFields}
-        , fColor{color} { }
+        , fColor{color}
+        , fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { }
 
-inline std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
+void GrTextBlob::insertSubRun(SubRun* subRun) {
+    if (fFirstSubRun == nullptr) {
+        fFirstSubRun = subRun;
+        fLastSubRun = subRun;
+    } else {
+        fLastSubRun->fNextSubRun = subRun;
+        fLastSubRun = subRun;
+    }
+}
+
+std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
         SubRun& info, int glyphCount,
         const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
         const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props,
@@ -682,10 +697,10 @@
                                     const SkFont& runFont,
                                     const SkStrikeSpec& strikeSpec) {
     this->setHasBitmap();
-    SubRun& subRun = fSubRuns.emplace_back(this, strikeSpec);
-    subRun.setAntiAliased(runFont.hasSomeAntiAliasing());
+    SubRun* subRun = fAlloc.make<SubRun>(this, strikeSpec);
+    subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
     for (auto [variant, pos] : drawables) {
-        subRun.fPaths.emplace_back(*variant.path(), pos);
+        subRun->fPaths.emplace_back(*variant.path(), pos);
     }
 }
 
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index 3ecfd12..cd5cc25 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -138,6 +138,7 @@
 
         const SkStrikeSpec& strikeSpec() const;
 
+        SubRun* fNextSubRun{nullptr};
         const SubRunType fType;
         GrTextBlob* const fBlob;
         const GrMaskFormat fMaskFormat;
@@ -292,13 +293,15 @@
         SkPaint::Join fJoin;
     };
 
-    GrTextBlob(size_t size,
+    GrTextBlob(size_t allocSize,
                GrStrikeCache* strikeCache,
                const SkMatrix& viewMatrix,
                SkPoint origin,
                GrColor color,
                bool forceWForDistanceFields);
 
+    void insertSubRun(SubRun* subRun);
+
     std::unique_ptr<GrAtlasTextOp> makeOp(
             SubRun& info, int glyphCount,
             const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
@@ -352,9 +355,6 @@
     // Number of glyphs stored in fGlyphs while accumulating SubRuns.
     uint32_t fGlyphsCursor{0};
 
-    // Assume one run per text blob.
-    SkSTArray<1, SubRun> fSubRuns;
-
     SkMaskFilterBase::BlurRec fBlurRec;
     StrokeInfo fStrokeInfo;
     Key fKey;
@@ -367,6 +367,10 @@
     SkScalar fMinMaxScale{SK_ScalarMax};
 
     uint8_t fTextType{0};
+
+    SubRun* fFirstSubRun{nullptr};
+    SubRun* fLastSubRun{nullptr};
+    SkArenaAlloc fAlloc;
 };
 
 /**