Make DefaultPathOp surface its programInfo at record time

Here is another Op w/ multiple GrMeshes drawn w/ a single programInfo (cf., AAConvexPathOp).

Bug: skia:9455
Change-Id: I3d1eec03d1d9d4fc8e117aa2960472027ea96105
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/276220
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 0bb6e05..bbd9e40 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -19,6 +19,7 @@
 #include "src/gpu/GrFixedClip.h"
 #include "src/gpu/GrMesh.h"
 #include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/GrStyle.h"
 #include "src/gpu/GrSurfaceContextPriv.h"
@@ -66,20 +67,21 @@
 
 class PathGeoBuilder {
 public:
-    PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
-                   const GrGeometryProcessor* geometryProcessor)
+    PathGeoBuilder(GrPrimitiveType primitiveType,
+                   GrMeshDrawOp::Target* target,
+                   SkTDArray<GrMesh*>* meshes)
             : fPrimitiveType(primitiveType)
             , fTarget(target)
             , fVertexStride(sizeof(SkPoint))
-            , fGeometryProcessor(geometryProcessor)
             , fFirstIndex(0)
             , fIndicesInChunk(0)
-            , fIndices(nullptr) {
+            , fIndices(nullptr)
+            , fMeshes(meshes) {
         this->allocNewBuffers();
     }
 
     ~PathGeoBuilder() {
-        this->emitMeshAndPutBackReserve();
+        this->createMeshAndPutBackReserve();
     }
 
     /**
@@ -262,14 +264,15 @@
     }
 
     // Emits a single draw with all accumulated vertex/index data
-    void emitMeshAndPutBackReserve() {
+    void createMeshAndPutBackReserve() {
         int vertexCount = fCurVert - fVertices;
         int indexCount = fCurIdx - fIndices;
         SkASSERT(vertexCount <= fVerticesInChunk);
         SkASSERT(indexCount <= fIndicesInChunk);
 
+        GrMesh* mesh = nullptr;
         if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
-            GrMesh* mesh = fTarget->allocMesh();
+            mesh = fTarget->allocMesh();
             if (!this->isIndexed()) {
                 mesh->setNonIndexedNonInstanced(vertexCount);
             } else {
@@ -277,11 +280,14 @@
                                  vertexCount - 1, GrPrimitiveRestart::kNo);
             }
             mesh->setVertexData(std::move(fVertexBuffer), fFirstVertex);
-            fTarget->recordDraw(fGeometryProcessor, mesh, 1, fPrimitiveType);
         }
 
         fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
         fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
+
+        if (mesh) {
+            fMeshes->push_back(mesh);
+        }
     }
 
     void needSpace(int vertsNeeded, int indicesNeeded = 0) {
@@ -297,7 +303,7 @@
             SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
 
             // Draw the mesh we've accumulated, and put back any unused space
-            this->emitMeshAndPutBackReserve();
+            this->createMeshAndPutBackReserve();
 
             // Get new buffers
             this->allocNewBuffers();
@@ -313,7 +319,6 @@
     GrPrimitiveType fPrimitiveType;
     GrMeshDrawOp::Target* fTarget;
     size_t fVertexStride;
-    const GrGeometryProcessor* fGeometryProcessor;
 
     sk_sp<const GrBuffer> fVertexBuffer;
     int fFirstVertex;
@@ -327,6 +332,8 @@
     uint16_t* fIndices;
     uint16_t* fCurIdx;
     uint16_t fSubpathIndexStart;
+
+    SkTDArray<GrMesh*>* fMeshes;
 };
 
 class DefaultPathOp final : public GrMeshDrawOp {
@@ -354,7 +361,11 @@
     const char* name() const override { return "DefaultPathOp"; }
 
     void visitProxies(const VisitProxyFunc& func) const override {
-        fHelper.visitProxies(func);
+        if (fProgramInfo) {
+            fProgramInfo->visitProxies(func);
+        } else {
+            fHelper.visitProxies(func);
+        }
     }
 
 #ifdef SK_DEBUG
@@ -401,7 +412,25 @@
     }
 
 private:
-    void onPrepareDraws(Target* target) override {
+    GrPrimitiveType primType() const {
+        if (this->isHairline()) {
+            int instanceCount = fPaths.count();
+
+            // We avoid indices when we have a single hairline contour.
+            bool isIndexed = instanceCount > 1 ||
+                                PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
+
+            return isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
+        }
+
+        return GrPrimitiveType::kTriangles;
+    }
+
+    GrProgramInfo* createProgramInfo(const GrCaps* caps,
+                                     SkArenaAlloc* arena,
+                                     const GrSurfaceProxyView* outputView,
+                                     GrAppliedClip&& appliedClip,
+                                     const GrXferProcessor::DstProxyView& dstProxyView) {
         GrGeometryProcessor* gp;
         {
             using namespace GrDefaultGeoProcFactory;
@@ -409,8 +438,8 @@
             Coverage coverage(this->coverage());
             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
                                                               : LocalCoords::kUnused_Type);
-            gp = GrDefaultGeoProcFactory::Make(target->allocator(),
-                                               target->caps().shaderCaps(),
+            gp = GrDefaultGeoProcFactory::Make(arena,
+                                               caps->shaderCaps(),
                                                color,
                                                coverage,
                                                localCoords,
@@ -419,32 +448,57 @@
 
         SkASSERT(gp->vertexStride() == sizeof(SkPoint));
 
-        int instanceCount = fPaths.count();
+        return fHelper.createProgramInfoWithStencil(caps, arena, outputView, std::move(appliedClip),
+                                                    dstProxyView, gp, this->primType());
 
-        // We avoid indices when we have a single hairline contour.
-        bool isIndexed = !this->isHairline() || instanceCount > 1 ||
-                         PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
+    }
 
-        // determine primitiveType
-        GrPrimitiveType primitiveType;
-        if (this->isHairline()) {
-            primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
-        } else {
-            primitiveType = GrPrimitiveType::kTriangles;
-        }
-        PathGeoBuilder pathGeoBuilder(primitiveType, target, gp);
+    GrProgramInfo* createProgramInfo(Target* target) {
+        return this->createProgramInfo(&target->caps(),
+                                       target->allocator(),
+                                       target->outputView(),
+                                       target->detachAppliedClip(),
+                                       target->dstProxyView());
+    }
+
+    void onPrePrepareDraws(GrRecordingContext* context,
+                           const GrSurfaceProxyView* outputView,
+                           GrAppliedClip* clip,
+                           const GrXferProcessor::DstProxyView& dstProxyView) override {
+        SkArenaAlloc* arena = context->priv().recordTimeAllocator();
+
+        // This is equivalent to a GrOpFlushState::detachAppliedClip
+        GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
+
+        fProgramInfo = this->createProgramInfo(context->priv().caps(), arena, outputView,
+                                               std::move(appliedClip), dstProxyView);
+
+        context->priv().recordProgramInfo(fProgramInfo);
+    }
+
+    void onPrepareDraws(Target* target) override {
+        PathGeoBuilder pathGeoBuilder(this->primType(), target, &fMeshes);
 
         // fill buffers
-        for (int i = 0; i < instanceCount; i++) {
+        for (int i = 0; i < fPaths.count(); i++) {
             const PathData& args = fPaths[i];
             pathGeoBuilder.addPath(args.fPath, args.fTolerance);
         }
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
-        auto pipeline = fHelper.createPipelineWithStencil(flushState);
+        if (!fProgramInfo) {
+            fProgramInfo = this->createProgramInfo(flushState);
+        }
 
-        flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline);
+        if (!fProgramInfo || !fMeshes.count()) {
+            return;
+        }
+
+        flushState->opsRenderPass()->bindPipeline(*fProgramInfo, chainBounds);
+        for (int i = 0; i < fMeshes.count(); ++i) {
+            flushState->opsRenderPass()->drawMeshes(*fProgramInfo, fMeshes[i], 1);
+        }
     }
 
     CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
@@ -491,6 +545,9 @@
     SkMatrix fViewMatrix;
     bool fIsHairline;
 
+    SkTDArray<GrMesh*> fMeshes;
+    GrProgramInfo*     fProgramInfo = nullptr;
+
     typedef GrMeshDrawOp INHERITED;
 };