Move GrTessellator::VertexAllocator to GrEagerVertexAllocator

Moves the interface up to Ganesh level and starts using it from other
locations.

Change-Id: I939d2b357d3ae8551976d0d71b877b72da403712
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266063
Reviewed-by: Stephen White <senorblanco@chromium.org>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index a2e3839..0dcd690 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -89,6 +89,7 @@
   "$_src/gpu/GrDrawOpTest.cpp",
   "$_src/gpu/GrDrawOpTest.h",
   "$_src/gpu/GrDriverBugWorkarounds.cpp",
+  "$_src/gpu/GrEagerVertexAllocator.h",
   "$_src/gpu/GrFixedClip.cpp",
   "$_src/gpu/GrFixedClip.h",
   "$_src/gpu/GrFragmentProcessor.cpp",
diff --git a/src/gpu/GrEagerVertexAllocator.h b/src/gpu/GrEagerVertexAllocator.h
new file mode 100644
index 0000000..2bf23d0
--- /dev/null
+++ b/src/gpu/GrEagerVertexAllocator.h
@@ -0,0 +1,84 @@
+/*
+ * 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 GrEagerVertexAllocator_DEFINED
+#define GrEagerVertexAllocator_DEFINED
+
+#include "src/gpu/ops/GrMeshDrawOp.h"
+
+// This interface is used to allocate and map GPU vertex data before the exact number of required
+// vertices is known. Usage pattern:
+//
+//   1. Call lock(eagerCount) with an upper bound on the number of required vertices.
+//   2. Compute and write vertex data to the returned pointer (if not null).
+//   3. Call unlock(actualCount) and provide the actual number of vertices written during step #2.
+//
+// On step #3, the implementation will attempt to shrink the underlying GPU memory slot to fit the
+// actual vertex count.
+class GrEagerVertexAllocator {
+public:
+    template<typename T> T* lock(int eagerCount) {
+        return static_cast<T*>(this->lock(sizeof(T), eagerCount));
+    }
+    virtual void* lock(size_t stride, int eagerCount) = 0;
+
+    virtual void unlock(int actualCount) = 0;
+
+    virtual ~GrEagerVertexAllocator() {}
+};
+
+// GrEagerVertexAllocator implementation that uses GrMeshDrawOp::Target::makeVertexSpace and
+// GrMeshDrawOp::Target::putBackVertices.
+class GrEagerDynamicVertexAllocator : public GrEagerVertexAllocator {
+public:
+    GrEagerDynamicVertexAllocator(GrMeshDrawOp::Target* target,
+                                   sk_sp<const GrBuffer>* vertexBuffer, int* baseVertex)
+            : fTarget(target)
+            , fVertexBuffer(vertexBuffer)
+            , fBaseVertex(baseVertex) {
+    }
+
+#ifdef SK_DEBUG
+    ~GrEagerDynamicVertexAllocator() override {
+        SkASSERT(!fLockCount);
+    }
+#endif
+
+    void* lock(size_t stride, int eagerCount) override {
+        SkASSERT(!fLockCount);
+        SkASSERT(eagerCount);
+        if (void* data = fTarget->makeVertexSpace(stride, eagerCount, fVertexBuffer, fBaseVertex)) {
+            fLockStride = stride;
+            fLockCount = eagerCount;
+            return data;
+        }
+        fVertexBuffer->reset();
+        *fBaseVertex = 0;
+        return nullptr;
+    }
+
+    void unlock(int actualCount) override {
+        SkASSERT(fLockCount);
+        SkASSERT(actualCount <= fLockCount);
+        fTarget->putBackVertices(fLockCount - actualCount, fLockStride);
+        if (!actualCount) {
+            fVertexBuffer->reset();
+            *fBaseVertex = 0;
+        }
+        fLockCount = 0;
+    }
+
+private:
+    GrMeshDrawOp::Target* const fTarget;
+    sk_sp<const GrBuffer>* const fVertexBuffer;
+    int* const fBaseVertex;
+
+    size_t fLockStride;
+    int fLockCount = 0;
+};
+
+#endif
diff --git a/src/gpu/GrTessellator.cpp b/src/gpu/GrTessellator.cpp
index d6149af..40b73c9 100644
--- a/src/gpu/GrTessellator.cpp
+++ b/src/gpu/GrTessellator.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/GrTessellator.h"
 
 #include "src/gpu/GrDefaultGeoProcFactory.h"
+#include "src/gpu/GrEagerVertexAllocator.h"
 #include "src/gpu/GrVertexWriter.h"
 #include "src/gpu/geometry/GrPathUtils.h"
 
@@ -2358,7 +2359,7 @@
 // Stage 6: Triangulate the monotone polygons into a vertex buffer.
 
 int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
-                    VertexAllocator* vertexAllocator, bool antialias, bool* isLinear) {
+                    GrEagerVertexAllocator* vertexAllocator, bool antialias, bool* isLinear) {
     int contourCnt = get_contour_count(path, tolerance);
     if (contourCnt <= 0) {
         *isLinear = true;
@@ -2378,7 +2379,8 @@
     }
     int count = count64;
 
-    void* verts = vertexAllocator->lock(count);
+    size_t vertexStride = GetVertexStride(antialias);
+    void* verts = vertexAllocator->lock(vertexStride, count);
     if (!verts) {
         SkDebugf("Could not allocate vertices\n");
         return 0;
@@ -2389,7 +2391,7 @@
     end = outer_mesh_to_triangles(outerMesh, true, end);
 
     int actualCount = static_cast<int>((static_cast<uint8_t*>(end) - static_cast<uint8_t*>(verts))
-                                       / vertexAllocator->stride());
+                                       / vertexStride);
     SkASSERT(actualCount <= count);
     vertexAllocator->unlock(actualCount);
     return actualCount;
diff --git a/src/gpu/GrTessellator.h b/src/gpu/GrTessellator.h
index e892a19..6e89698 100644
--- a/src/gpu/GrTessellator.h
+++ b/src/gpu/GrTessellator.h
@@ -12,6 +12,7 @@
 #include "include/private/SkColorData.h"
 #include "src/gpu/GrColor.h"
 
+class GrEagerVertexAllocator;
 class SkPath;
 struct SkRect;
 
@@ -23,17 +24,6 @@
 
 namespace GrTessellator {
 
-class VertexAllocator {
-public:
-    VertexAllocator(size_t stride) : fStride(stride) {}
-    virtual ~VertexAllocator() {}
-    virtual void* lock(int vertexCount) = 0;
-    virtual void unlock(int actualCount) = 0;
-    size_t stride() const { return fStride; }
-private:
-    size_t fStride;
-};
-
 struct WindingVertex {
     SkPoint fPos;
     int fWinding;
@@ -46,8 +36,12 @@
 int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
                    WindingVertex** verts);
 
+constexpr size_t GetVertexStride(bool antialias) {
+    return sizeof(SkPoint) + ((antialias) ? sizeof(float) : 0);
+}
+
 int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
-                    VertexAllocator*, bool antialias, bool *isLinear);
+                    GrEagerVertexAllocator*, bool antialias, bool *isLinear);
 }
 
 #endif
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index ee4d91d..49f677b 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -13,6 +13,7 @@
 #include "src/gpu/GrClip.h"
 #include "src/gpu/GrDefaultGeoProcFactory.h"
 #include "src/gpu/GrDrawOpTest.h"
+#include "src/gpu/GrEagerVertexAllocator.h"
 #include "src/gpu/GrMesh.h"
 #include "src/gpu/GrOpFlushState.h"
 #include "src/gpu/GrResourceCache.h"
@@ -68,16 +69,22 @@
     return false;
 }
 
-class StaticVertexAllocator : public GrTessellator::VertexAllocator {
+class StaticVertexAllocator : public GrEagerVertexAllocator {
 public:
-    StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
-      : VertexAllocator(stride)
-      , fResourceProvider(resourceProvider)
+    StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB)
+      : fResourceProvider(resourceProvider)
       , fCanMapVB(canMapVB)
       , fVertices(nullptr) {
     }
-    void* lock(int vertexCount) override {
-        size_t size = vertexCount * stride();
+#ifdef SK_DEBUG
+    ~StaticVertexAllocator() override {
+        SkASSERT(!fLockStride);
+    }
+#endif
+    void* lock(size_t stride, int eagerCount) override {
+        SkASSERT(!fLockStride);
+        SkASSERT(stride);
+        size_t size = eagerCount * stride;
         fVertexBuffer = fResourceProvider->createBuffer(size, GrGpuBufferType::kVertex,
                                                         kStatic_GrAccessPattern);
         if (!fVertexBuffer.get()) {
@@ -86,18 +93,21 @@
         if (fCanMapVB) {
             fVertices = fVertexBuffer->map();
         } else {
-            fVertices = sk_malloc_throw(vertexCount * stride());
+            fVertices = sk_malloc_throw(eagerCount * stride);
         }
+        fLockStride = stride;
         return fVertices;
     }
     void unlock(int actualCount) override {
+        SkASSERT(fLockStride);
         if (fCanMapVB) {
             fVertexBuffer->unmap();
         } else {
-            fVertexBuffer->updateData(fVertices, actualCount * stride());
+            fVertexBuffer->updateData(fVertices, actualCount * fLockStride);
             sk_free(fVertices);
         }
         fVertices = nullptr;
+        fLockStride = 0;
     }
     sk_sp<GrGpuBuffer> detachVertexBuffer() { return std::move(fVertexBuffer); }
 
@@ -106,33 +116,7 @@
     GrResourceProvider* fResourceProvider;
     bool fCanMapVB;
     void* fVertices;
-};
-
-class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
-public:
-    DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
-            : VertexAllocator(stride)
-            , fTarget(target)
-            , fVertexBuffer(nullptr)
-            , fVertices(nullptr) {}
-    void* lock(int vertexCount) override {
-        fVertexCount = vertexCount;
-        fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
-        return fVertices;
-    }
-    void unlock(int actualCount) override {
-        fTarget->putBackVertices(fVertexCount - actualCount, stride());
-        fVertices = nullptr;
-    }
-    sk_sp<const GrBuffer> detachVertexBuffer() const { return std::move(fVertexBuffer); }
-    int firstVertex() const { return fFirstVertex; }
-
-private:
-    GrMeshDrawOp::Target* fTarget;
-    sk_sp<const GrBuffer> fVertexBuffer;
-    int fVertexCount;
-    int fFirstVertex;
-    void* fVertices;
+    size_t fLockStride = 0;
 };
 
 }  // namespace
@@ -255,7 +239,7 @@
         return path;
     }
 
-    void draw(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
+    void draw(Target* target, const GrGeometryProcessor* gp) {
         SkASSERT(!fAntiAlias);
         GrResourceProvider* rp = target->resourceProvider();
         bool inverseFill = fShape.inverseFilled();
@@ -292,7 +276,7 @@
         vmi.mapRect(&clipBounds);
         bool isLinear;
         bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
-        StaticVertexAllocator allocator(vertexStride, rp, canMapVB);
+        StaticVertexAllocator allocator(rp, canMapVB);
         int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator, false,
                                                    &isLinear);
         if (count == 0) {
@@ -309,7 +293,7 @@
         this->drawVertices(target, gp, std::move(vb), 0, count);
     }
 
-    void drawAA(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
+    void drawAA(Target* target, const GrGeometryProcessor* gp) {
         SkASSERT(fAntiAlias);
         SkPath path = getPath();
         if (path.isEmpty()) {
@@ -318,15 +302,16 @@
         SkRect clipBounds = SkRect::Make(fDevClipBounds);
         path.transform(fViewMatrix);
         SkScalar tol = GrPathUtils::kDefaultTolerance;
+        sk_sp<const GrBuffer> vertexBuffer;
+        int firstVertex;
         bool isLinear;
-        DynamicVertexAllocator allocator(vertexStride, target);
+        GrEagerDynamicVertexAllocator allocator(target, &vertexBuffer, &firstVertex);
         int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, true,
                                                    &isLinear);
         if (count == 0) {
             return;
         }
-        this->drawVertices(target, gp, allocator.detachVertexBuffer(),
-                           allocator.firstVertex(), count);
+        this->drawVertices(target, gp, std::move(vertexBuffer), firstVertex, count);
     }
 
     void onPrepareDraws(Target* target) override {
@@ -362,11 +347,11 @@
         if (!gp) {
             return;
         }
-        size_t vertexStride = gp->vertexStride();
+        SkASSERT(GrTessellator::GetVertexStride(fAntiAlias) == gp->vertexStride());
         if (fAntiAlias) {
-            this->drawAA(target, gp, vertexStride);
+            this->drawAA(target, gp);
         } else {
-            this->draw(target, gp, vertexStride);
+            this->draw(target, gp);
         }
     }
 
diff --git a/src/gpu/tessellate/GrPathParser.cpp b/src/gpu/tessellate/GrPathParser.cpp
index c6c61ac..a660a0d 100644
--- a/src/gpu/tessellate/GrPathParser.cpp
+++ b/src/gpu/tessellate/GrPathParser.cpp
@@ -9,6 +9,7 @@
 
 #include "include/private/SkTArray.h"
 #include "src/core/SkPathPriv.h"
+#include "src/gpu/GrEagerVertexAllocator.h"
 
 static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
     SkASSERT(1 != T);  // The below does not guarantee lerp(a, b, 1) === b.
@@ -76,7 +77,19 @@
     int fMidpointWeight;
 };
 
-int GrPathParser::EmitCenterWedgePatches(const SkPath& path, SkPoint* patchData) {
+constexpr int max_wedge_vertex_count(int numPathVerbs) {
+    // No initial moveTo, one wedge per verb, plus an implicit close at the end.
+    // Each wedge has 5 vertices.
+    return (numPathVerbs + 1) * 5;
+}
+
+int GrPathParser::EmitCenterWedgePatches(const SkPath& path, GrEagerVertexAllocator* vertexAlloc) {
+    int maxVertices = max_wedge_vertex_count(path.countVerbs());
+    auto* vertexData = vertexAlloc->lock<SkPoint>(maxVertices);
+    if (!vertexData) {
+        return 0;
+    }
+
     int vertexCount = 0;
     MidpointContourParser parser(path);
     while (parser.parseNextContour()) {
@@ -88,7 +101,7 @@
                 case SkPathVerb::kDone:
                     if (parser.startPoint() != lastPoint) {
                         lastPoint = write_line_as_cubic(
-                                patchData + vertexCount, lastPoint, parser.startPoint());
+                                vertexData + vertexCount, lastPoint, parser.startPoint());
                         break;
                     }  // fallthru
                 default:
@@ -99,29 +112,29 @@
                     continue;
 
                 case SkPathVerb::kLine:
-                    lastPoint = write_line_as_cubic(patchData + vertexCount, lastPoint,
+                    lastPoint = write_line_as_cubic(vertexData + vertexCount, lastPoint,
                                                     parser.atPoint(ptsIdx));
                     ++ptsIdx;
                     break;
                 case SkPathVerb::kQuad:
-                    lastPoint = write_quadratic_as_cubic(patchData + vertexCount, lastPoint,
+                    lastPoint = write_quadratic_as_cubic(vertexData + vertexCount, lastPoint,
                                                          parser.atPoint(ptsIdx),
                                                          parser.atPoint(ptsIdx + 1));
                     ptsIdx += 2;
                     break;
                 case SkPathVerb::kCubic:
-                    lastPoint = write_cubic(patchData + vertexCount, lastPoint,
+                    lastPoint = write_cubic(vertexData + vertexCount, lastPoint,
                                             parser.atPoint(ptsIdx), parser.atPoint(ptsIdx + 1),
                                             parser.atPoint(ptsIdx + 2));
                     ptsIdx += 3;
                     break;
             }
-            patchData[vertexCount + 4] = parser.midpoint();
+            vertexData[vertexCount + 4] = parser.midpoint();
             vertexCount += 5;
         }
     }
 
-    SkASSERT(vertexCount <= MaxWedgeVertices(path));
+    vertexAlloc->unlock(vertexCount);
     return vertexCount;
 }
 
@@ -147,8 +160,9 @@
 
 class InnerPolygonContourParser : public SkTPathContourParser<InnerPolygonContourParser> {
 public:
-    InnerPolygonContourParser(const SkPath& path) : SkTPathContourParser(path) {
-        fPolyPoints.reserve(GrPathParser::MaxInnerPolygonVertices(path));
+    InnerPolygonContourParser(const SkPath& path, int vertexReserveCount)
+            : SkTPathContourParser(path)
+            , fPolyPoints(vertexReserveCount) {
     }
 
     int emitInnerPolygon(SkPoint* vertexData) {
@@ -197,36 +211,51 @@
     int fNumCurves;
 };
 
-int GrPathParser::EmitInnerPolygonTriangles(const SkPath& path, SkPoint* vertexData,
-                                            int* numCurves) {
-    *numCurves = 0;
-    int vertexCount = 0;
-    InnerPolygonContourParser parser(path);
-    while (parser.parseNextContour()) {
-        vertexCount += parser.emitInnerPolygon(vertexData + vertexCount);
-        *numCurves += parser.numCurves();
+constexpr int max_inner_poly_vertex_count(int numPathVerbs) {
+    // No initial moveTo, plus an implicit close at the end; n-2 trianles fill an n-gon.
+    // Each triangle has 3 vertices.
+    return (numPathVerbs - 1) * 3;
+}
+
+int GrPathParser::EmitInnerPolygonTriangles(const SkPath& path,
+                                            GrEagerVertexAllocator* vertexAlloc) {
+    int maxVertices = max_inner_poly_vertex_count(path.countVerbs());
+    InnerPolygonContourParser parser(path, maxVertices);
+    auto* vertexData = vertexAlloc->lock<SkPoint>(maxVertices);
+    if (!vertexData) {
+        return 0;
     }
 
-    SkASSERT(vertexCount <= MaxInnerPolygonVertices(path));
+    int vertexCount = 0;
+    while (parser.parseNextContour()) {
+        vertexCount += parser.emitInnerPolygon(vertexData + vertexCount);
+    }
+
+    vertexAlloc->unlock(vertexCount);
     return vertexCount;
 }
 
-int GrPathParser::EmitCubicInstances(const SkPath& path, SkPoint* vertexData) {
+int GrPathParser::EmitCubicInstances(const SkPath& path, GrEagerVertexAllocator* vertexAlloc) {
+    auto* instanceData = vertexAlloc->lock<std::array<SkPoint, 4>>(path.countVerbs());
+    if (!instanceData) {
+        return 0;
+    }
+
     int instanceCount = 0;
     SkPath::Iter iter(path, false);
     SkPath::Verb verb;
     SkPoint pts[4];
     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
         if (SkPath::kQuad_Verb == verb) {
-            write_quadratic_as_cubic(vertexData + (instanceCount * 4), pts[0], pts[1], pts[2]);
-            ++instanceCount;
+            write_quadratic_as_cubic(instanceData[instanceCount++].data(), pts[0], pts[1], pts[2]);
             continue;
         }
         if (SkPath::kCubic_Verb == verb) {
-            write_cubic(vertexData + (instanceCount * 4), pts[0], pts[1], pts[2], pts[3]);
-            ++instanceCount;
+            instanceData[instanceCount++] = {pts[0], pts[1], pts[2], pts[3]};
             continue;
         }
     }
+
+    vertexAlloc->unlock(instanceCount);
     return instanceCount;
 }
diff --git a/src/gpu/tessellate/GrPathParser.h b/src/gpu/tessellate/GrPathParser.h
index 76f61cf..61c1128 100644
--- a/src/gpu/tessellate/GrPathParser.h
+++ b/src/gpu/tessellate/GrPathParser.h
@@ -10,48 +10,35 @@
 
 #include "include/core/SkPath.h"
 
+class GrEagerVertexAllocator;
+
 namespace GrPathParser {
 
-// Returns the maximum number of vertices that can be written by EmitCenterWedges() for the given
-// path.
-inline int MaxWedgeVertices(const SkPath& path) {
-    // No initial moveTo, one wedge per verb, plus an implicit close at the end.
-    // Each wedge has 5 vertices.
-    return (path.countVerbs() + 1) * 5;
-}
-
 // Writes an array of cubic "wedges" from an SkPath, converting any lines or quadratics to cubics.
-// These wedges can then be fed into GrTessellateWedgeShader to stencil the path. A wedge is a
-// 5-point tessellation patch consisting of 4 cubic control points, plus an anchor point fanning
-// from the center of the curve's resident contour.
+// These wedges can then be fed into GrStencilWedgeShader to stencil the path. A wedge is a 5-point
+// tessellation patch consisting of 4 cubic control points, plus an anchor point fanning from the
+// center of the curve's resident contour.
 //
 // TODO: Eventually we want to use rational cubic wedges in order to support conics.
 //
 // Returns the number of vertices written to the array.
-//
-// The incoming patchData array must have at least MaxWedgeVertices() elements.
-int EmitCenterWedgePatches(const SkPath&, SkPoint* patchData);
+int EmitCenterWedgePatches(const SkPath&, GrEagerVertexAllocator*);
 
-// Returns the maximum number of vertices required to triangulate the given path's inner polygon(s).
-inline int MaxInnerPolygonVertices(const SkPath& path) {
-    // No initial moveTo, plus an implicit close at the end; n-2 trianles fill an n-gon.
-    // Each triangle has 3 vertices.
-    return (path.countVerbs() - 1) * 3;
-}
-
-// Triangulates the path's inner polygon(s) and writes the result to "vertexData". The inner
-// polygons connect the endpoints of each verb. (i.e., they are the path that would result from
-// collapsing all curves to single lines.)
+// Triangulates and writes an SkPath's inner polygon(s). The inner polygons connect the endpoints of
+// each verb. (i.e., they are the path that would result from collapsing all curves to single
+// lines.)
 //
 // This method works by recursively subdividing the path rather than emitting a linear triangle fan
 // or strip. This can reduce the load on the rasterizer by a great deal on complex paths.
 //
 // Returns the number of vertices written to the array.
-//
-// The incoming vertexData array must have at least MaxInnerPolygonVertices() elements.
-int EmitInnerPolygonTriangles(const SkPath&, SkPoint* vertexData, int* numCurves);
+int EmitInnerPolygonTriangles(const SkPath&, GrEagerVertexAllocator*);
 
-int EmitCubicInstances(const SkPath&, SkPoint* vertexData);
+// Writes out an array of cubics from an SkPath as 4-point instances, converting any quadratics to
+// cubics.
+//
+// Returns the number of *instances* written to the array.
+int EmitCubicInstances(const SkPath&, GrEagerVertexAllocator*);
 
 }  // namespace
 
diff --git a/src/gpu/tessellate/GrTessellatePathOp.cpp b/src/gpu/tessellate/GrTessellatePathOp.cpp
index 7b03923..a4ce3f1 100644
--- a/src/gpu/tessellate/GrTessellatePathOp.cpp
+++ b/src/gpu/tessellate/GrTessellatePathOp.cpp
@@ -7,6 +7,7 @@
 
 #include "src/gpu/tessellate/GrTessellatePathOp.h"
 
+#include "src/gpu/GrEagerVertexAllocator.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrOpFlushState.h"
 #include "src/gpu/GrOpsRenderPass.h"
@@ -24,6 +25,10 @@
 }
 
 void GrTessellatePathOp::onPrepare(GrOpFlushState* state) {
+    GrEagerDynamicVertexAllocator pathVertexAllocator(state, &fPathVertexBuffer, &fBasePathVertex);
+    GrEagerDynamicVertexAllocator cubicInstanceAllocator(state, &fCubicInstanceBuffer,
+                                                         &fBaseCubicInstance);
+
     // First see if we should split up inner polygon triangles and curves, and triangulate the inner
     // polygon(s) more efficiently. This causes greater CPU overhead due to the extra shaders and
     // draw calls, but the better triangulation can reduce the rasterizer load by a great deal on
@@ -33,39 +38,15 @@
     // Raster-edge work is 1-dimensional, so we sum height and width rather than multiplying them.
     float rasterEdgeWork = (bounds.height() + bounds.width()) * scale * fPath.countVerbs();
     if (rasterEdgeWork > 1000 * 1000) {
-        int numCurves = 0;
-        int maxVertexCount = GrPathParser::MaxInnerPolygonVertices(fPath);
-        if (auto* triangleData = (SkPoint*)state->makeVertexSpace(
-                sizeof(SkPoint), maxVertexCount, &fPathVertexBuffer, &fBasePathVertex)) {
-            if ((fPathVertexCount =
-                     GrPathParser::EmitInnerPolygonTriangles(fPath, triangleData, &numCurves))) {
-                fPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
-            } else {
-                fPathVertexBuffer.reset();
-            }
-            state->putBackVertices(maxVertexCount - fPathVertexCount, sizeof(SkPoint));
-        }
-        if (numCurves) {
-            if (auto* cubicData = (SkPoint*)state->makeVertexSpace(
-                    sizeof(SkPoint) * 4, numCurves, &fCubicInstanceBuffer, &fBaseCubicInstance)) {
-                fCubicInstanceCount = GrPathParser::EmitCubicInstances(fPath, cubicData);
-                SkASSERT(fCubicInstanceCount == numCurves);
-            }
-        }
+        fPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
+        fPathVertexCount = GrPathParser::EmitInnerPolygonTriangles(fPath, &pathVertexAllocator);
+        fCubicInstanceCount = GrPathParser::EmitCubicInstances(fPath, &cubicInstanceAllocator);
         return;
     }
 
     // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
-    int maxVertexCount = GrPathParser::MaxWedgeVertices(fPath);
-    if (auto* patchData = (SkPoint*)state->makeVertexSpace(
-            sizeof(SkPoint), maxVertexCount, &fPathVertexBuffer, &fBasePathVertex)) {
-        if ((fPathVertexCount = GrPathParser::EmitCenterWedgePatches(fPath, patchData))) {
-            fPathShader = state->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
-        } else {
-            fPathVertexBuffer.reset();
-        }
-        state->putBackVertices(maxVertexCount - fPathVertexCount, sizeof(SkPoint));
-    }
+    fPathShader = state->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
+    fPathVertexCount = GrPathParser::EmitCenterWedgePatches(fPath, &pathVertexAllocator);
 }
 
 void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
diff --git a/src/gpu/tessellate/GrTessellatePathOp.h b/src/gpu/tessellate/GrTessellatePathOp.h
index 0e17c41..cd0e37b 100644
--- a/src/gpu/tessellate/GrTessellatePathOp.h
+++ b/src/gpu/tessellate/GrTessellatePathOp.h
@@ -67,6 +67,9 @@
     SkPMColor4f fColor;
     GrProcessorSet fProcessors;
 
+    // The "path shader" draws the below path geometry.
+    GrStencilPathShader* fPathShader;
+
     // The "path vertex buffer" is made up of either inner polygon triangles (see
     // GrPathParser::EmitInnerPolygonTriangles) or cubic wedge patches (see
     // GrPathParser::EmitCenterWedgePatches).
@@ -74,9 +77,6 @@
     int fBasePathVertex;
     int fPathVertexCount;
 
-    // The "path shader" draws the above path geometry.
-    GrStencilPathShader* fPathShader;
-
     // The cubic instance buffer defines standalone cubics to tessellate into the stencil buffer, in
     // addition to the above path geometry.
     sk_sp<const GrBuffer> fCubicInstanceBuffer;