Update AAHairlineOp to surface its programInfos at record time

This CL is interesting bc the AAHairlineOp is the first one that requires multiple programInfos.
Correspondingly, it is also the first one that shares a pipeline between said multiple programInfos.

Bug: skia:9455
Change-Id: I2369abbdeaf4eac2bc9547ad36631beba29bd641
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/277203
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index b61f284..bff863c 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -70,7 +70,11 @@
     }
 
     void visitProxies(const VisitProxyFunc& func) const override {
-        fProcessorSet.visitProxies(func);
+        if (fProgramInfo) {
+            fProgramInfo->visitFPProxies(func);
+        } else {
+            fProcessorSet.visitProxies(func);
+        }
     }
 
 protected:
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index 620e0b4..08292e3 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -20,6 +20,7 @@
 #include "src/gpu/GrDrawOpTest.h"
 #include "src/gpu/GrOpFlushState.h"
 #include "src/gpu/GrProcessor.h"
+#include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrResourceProvider.h"
 #include "src/gpu/GrStyle.h"
 #include "src/gpu/effects/GrBezierEffect.h"
@@ -772,8 +773,6 @@
     return true;
 }
 
-namespace {
-
 class AAHairlineOp final : public GrMeshDrawOp {
 private:
     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
@@ -823,7 +822,18 @@
     const char* name() const override { return "AAHairlineOp"; }
 
     void visitProxies(const VisitProxyFunc& func) const override {
-        fHelper.visitProxies(func);
+
+        bool visited = false;
+        for (int i = 0; i < 3; ++i) {
+            if (fProgramInfos[i]) {
+                fProgramInfos[i]->visitFPProxies(func);
+                visited = true;
+            }
+        }
+
+        if (!visited) {
+            fHelper.visitProxies(func);
+        }
     }
 
 #ifdef SK_DEBUG
@@ -848,16 +858,26 @@
                                           nullptr);
     }
 
+    enum Program : uint8_t {
+        kNone_Program  = 0x0,
+        kLine_Program  = 0x1,
+        kQuad_Program  = 0x2,
+        kConic_Program = 0x4,
+    };
+
 private:
-    GrGeometryProcessor* makeLineGP(const GrCaps&, SkArenaAlloc*,
-                                    const SkMatrix* geometryProcessorViewM,
-                                    const SkMatrix* geometryProcessorLocalM);
-    GrGeometryProcessor* makeQuadGP(const GrCaps&, SkArenaAlloc*,
-                                    const SkMatrix* geometryProcessorViewM,
-                                    const SkMatrix* geometryProcessorLocalM);
-    GrGeometryProcessor* makeConicGP(const GrCaps&, SkArenaAlloc*,
-                                     const SkMatrix* geometryProcessorViewM,
-                                     const SkMatrix* geometryProcessorLocalM);
+    void makeLineProgramInfo(const GrCaps&, SkArenaAlloc*, const GrPipeline*,
+                             const GrSurfaceProxyView* outputView,
+                             const SkMatrix* geometryProcessorViewM,
+                             const SkMatrix* geometryProcessorLocalM);
+    void makeQuadProgramInfo(const GrCaps&, SkArenaAlloc*, const GrPipeline*,
+                             const GrSurfaceProxyView* outputView,
+                             const SkMatrix* geometryProcessorViewM,
+                             const SkMatrix* geometryProcessorLocalM);
+    void makeConicProgramInfo(const GrCaps&, SkArenaAlloc*, const GrPipeline*,
+                              const GrSurfaceProxyView* outputView,
+                              const SkMatrix* geometryProcessorViewM,
+                              const SkMatrix* geometryProcessorLocalM);
 
     GrProgramInfo* programInfo() override {
         // This Op has 3 programInfos and implements its own onPrePrepareDraws so this entry point
@@ -866,20 +886,18 @@
         return nullptr;
     }
 
+    Program predictPrograms(const GrCaps*) const;
+
     void onCreateProgramInfo(const GrCaps*,
                              SkArenaAlloc*,
                              const GrSurfaceProxyView* outputView,
                              GrAppliedClip&&,
-                             const GrXferProcessor::DstProxyView&) override {
-        // TODO [PI]: implement
-    }
+                             const GrXferProcessor::DstProxyView&) override;
 
     void onPrePrepareDraws(GrRecordingContext*,
                            const GrSurfaceProxyView* outputView,
                            GrAppliedClip*,
-                           const GrXferProcessor::DstProxyView&) override {
-        // TODO [PI]: implement
-    }
+                           const GrXferProcessor::DstProxyView&) override;
 
     void onPrepareDraws(Target*) override;
     void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
@@ -942,34 +960,55 @@
     SkPMColor4f fColor;
     uint8_t fCoverage;
 
+    Program        fCharacterization = kNone_Program;       // holds a mask of required programs
+    GrSimpleMesh*  fMeshes[3] = { nullptr };
+    GrProgramInfo* fProgramInfos[3] = { nullptr };
+
     typedef GrMeshDrawOp INHERITED;
 };
 
-}  // anonymous namespace
+GR_MAKE_BITFIELD_OPS(AAHairlineOp::Program)
 
-GrGeometryProcessor* AAHairlineOp::makeLineGP(const GrCaps& caps, SkArenaAlloc* arena,
-                                              const SkMatrix* geometryProcessorViewM,
-                                              const SkMatrix* geometryProcessorLocalM) {
-    using namespace GrDefaultGeoProcFactory;
+void AAHairlineOp::makeLineProgramInfo(const GrCaps& caps, SkArenaAlloc* arena,
+                                       const GrPipeline* pipeline,
+                                       const GrSurfaceProxyView* outputView,
+                                       const SkMatrix* geometryProcessorViewM,
+                                       const SkMatrix* geometryProcessorLocalM) {
+    if (fProgramInfos[0]) {
+        return;
+    }
 
-    Color color(this->color());
-    LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
-                                                      : LocalCoords::kUnused_Type);
-    localCoords.fMatrix = geometryProcessorLocalM;
+    GrGeometryProcessor* lineGP;
+    {
+        using namespace GrDefaultGeoProcFactory;
 
-    GrGeometryProcessor* lineGP = GrDefaultGeoProcFactory::Make(arena,
-                                                                color,
-                                                                Coverage::kAttribute_Type,
-                                                                localCoords,
-                                                                *geometryProcessorViewM);
-    SkASSERT(sizeof(LineVertex) == lineGP->vertexStride());
+        Color color(this->color());
+        LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
+                                                          : LocalCoords::kUnused_Type);
+        localCoords.fMatrix = geometryProcessorLocalM;
 
-    return lineGP;
+        lineGP = GrDefaultGeoProcFactory::Make(arena,
+                                               color,
+                                               Coverage::kAttribute_Type,
+                                               localCoords,
+                                               *geometryProcessorViewM);
+        SkASSERT(sizeof(LineVertex) == lineGP->vertexStride());
+    }
+
+    fProgramInfos[0] = GrSimpleMeshDrawOpHelper::CreateProgramInfo(arena, pipeline,
+                                                                   outputView, lineGP,
+                                                                   GrPrimitiveType::kTriangles);
 }
 
-GrGeometryProcessor* AAHairlineOp::makeQuadGP(const GrCaps& caps, SkArenaAlloc* arena,
-                                              const SkMatrix* geometryProcessorViewM,
-                                              const SkMatrix* geometryProcessorLocalM) {
+void AAHairlineOp::makeQuadProgramInfo(const GrCaps& caps, SkArenaAlloc* arena,
+                                       const GrPipeline* pipeline,
+                                       const GrSurfaceProxyView* outputView,
+                                       const SkMatrix* geometryProcessorViewM,
+                                       const SkMatrix* geometryProcessorLocalM) {
+    if (fProgramInfos[1]) {
+        return;
+    }
+
     GrGeometryProcessor* quadGP = GrQuadEffect::Make(arena,
                                                      this->color(),
                                                      *geometryProcessorViewM,
@@ -980,12 +1019,20 @@
                                                      this->coverage());
     SkASSERT(sizeof(BezierVertex) == quadGP->vertexStride());
 
-    return quadGP;
+    fProgramInfos[1] = GrSimpleMeshDrawOpHelper::CreateProgramInfo(arena, pipeline,
+                                                                   outputView, quadGP,
+                                                                   GrPrimitiveType::kTriangles);
 }
 
-GrGeometryProcessor* AAHairlineOp::makeConicGP(const GrCaps& caps, SkArenaAlloc* arena,
-                                               const SkMatrix* geometryProcessorViewM,
-                                               const SkMatrix* geometryProcessorLocalM) {
+void AAHairlineOp::makeConicProgramInfo(const GrCaps& caps, SkArenaAlloc* arena,
+                                        const GrPipeline* pipeline,
+                                        const GrSurfaceProxyView* outputView,
+                                        const SkMatrix* geometryProcessorViewM,
+                                        const SkMatrix* geometryProcessorLocalM) {
+    if (fProgramInfos[2]) {
+        return;
+    }
+
     GrGeometryProcessor* conicGP = GrConicEffect::Make(arena,
                                                        this->color(),
                                                        *geometryProcessorViewM,
@@ -996,10 +1043,42 @@
                                                        this->coverage());
     SkASSERT(sizeof(BezierVertex) == conicGP->vertexStride());
 
-    return conicGP;
+    fProgramInfos[2] = GrSimpleMeshDrawOpHelper::CreateProgramInfo(arena, pipeline,
+                                                                   outputView, conicGP,
+                                                                   GrPrimitiveType::kTriangles);
 }
 
-void AAHairlineOp::onPrepareDraws(Target* target) {
+AAHairlineOp::Program AAHairlineOp::predictPrograms(const GrCaps* caps) const {
+    bool convertConicsToQuads = !caps->shaderCaps()->floatIs32Bits();
+
+    // When predicting the programs we always include the lineProgram bc it is used as a fallback
+    // for quads and conics. In non-DDL mode there are cases where it sometimes isn't needed for a
+    // given path.
+    Program neededPrograms = kLine_Program;
+
+    for (int i = 0; i < fPaths.count(); i++) {
+        uint32_t mask = fPaths[i].fPath.getSegmentMasks();
+
+        if (mask & (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)) {
+            neededPrograms |= kQuad_Program;
+        }
+        if (mask & SkPath::kConic_SegmentMask) {
+            if (convertConicsToQuads) {
+                neededPrograms |= kQuad_Program;
+            } else {
+                neededPrograms |= kConic_Program;
+            }
+        }
+    }
+
+    return neededPrograms;
+}
+
+void AAHairlineOp::onCreateProgramInfo(const GrCaps* caps,
+                                       SkArenaAlloc* arena,
+                                       const GrSurfaceProxyView* outputView,
+                                       GrAppliedClip&& appliedClip,
+                                       const GrXferProcessor::DstProxyView& dstProxyView) {
     // Setup the viewmatrix and localmatrix for the GrGeometryProcessor.
     SkMatrix invert;
     if (!this->viewMatrix().invert(&invert)) {
@@ -1010,15 +1089,67 @@
     bool hasPerspective = this->viewMatrix().hasPerspective();
     const SkMatrix* geometryProcessorViewM = &SkMatrix::I();
     const SkMatrix* geometryProcessorLocalM = &invert;
-    const SkMatrix* toDevice = nullptr;
-    const SkMatrix* toSrc = nullptr;
     if (hasPerspective) {
         geometryProcessorViewM = &this->viewMatrix();
         geometryProcessorLocalM = &SkMatrix::I();
+    }
+
+    auto pipeline = fHelper.createPipelineWithStencil(caps, arena, outputView->swizzle(),
+                                                      std::move(appliedClip), dstProxyView);
+
+    if (fCharacterization & kLine_Program) {
+        this->makeLineProgramInfo(*caps, arena, pipeline, outputView,
+                                  geometryProcessorViewM, geometryProcessorLocalM);
+    }
+    if (fCharacterization & kQuad_Program) {
+        this->makeQuadProgramInfo(*caps, arena, pipeline, outputView,
+                                  geometryProcessorViewM, geometryProcessorLocalM);
+    }
+    if (fCharacterization & kConic_Program) {
+        this->makeConicProgramInfo(*caps, arena, pipeline, outputView,
+                                   geometryProcessorViewM, geometryProcessorLocalM);
+
+    }
+}
+
+void AAHairlineOp::onPrePrepareDraws(GrRecordingContext* context,
+                                     const GrSurfaceProxyView* outputView,
+                                     GrAppliedClip* clip,
+                                     const GrXferProcessor::DstProxyView& dstProxyView) {
+    SkArenaAlloc* arena = context->priv().recordTimeAllocator();
+    const GrCaps* caps = context->priv().caps();
+
+    // This is equivalent to a GrOpFlushState::detachAppliedClip
+    GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
+
+    // Conservatively predict which programs will be required
+    fCharacterization = this->predictPrograms(caps);
+
+    this->createProgramInfo(caps, arena, outputView, std::move(appliedClip), dstProxyView);
+
+    context->priv().recordProgramInfo(fProgramInfos[0]);
+    context->priv().recordProgramInfo(fProgramInfos[1]);
+    context->priv().recordProgramInfo(fProgramInfos[2]);
+}
+
+void AAHairlineOp::onPrepareDraws(Target* target) {
+    // Setup the viewmatrix and localmatrix for the GrGeometryProcessor.
+    SkMatrix invert;
+    if (!this->viewMatrix().invert(&invert)) {
+        return;
+    }
+
+    // we will transform to identity space if the viewmatrix does not have perspective
+    const SkMatrix* toDevice = nullptr;
+    const SkMatrix* toSrc = nullptr;
+    if (this->viewMatrix().hasPerspective()) {
         toDevice = &this->viewMatrix();
         toSrc = &invert;
     }
 
+    SkDEBUGCODE(Program predictedPrograms = this->predictPrograms(&target->caps()));
+    Program actualPrograms = kNone_Program;
+
     // This is hand inlined for maximum performance.
     PREALLOC_PTARRAY(128) lines;
     PREALLOC_PTARRAY(128) quads;
@@ -1048,6 +1179,9 @@
 
     // do lines first
     if (lineCount) {
+        SkASSERT(predictedPrograms & kLine_Program);
+        actualPrograms |= kLine_Program;
+
         sk_sp<const GrBuffer> linesIndexBuffer = get_lines_index_buffer(target->resourceProvider());
 
         GrMeshDrawOp::PatternHelper helper(target, GrPrimitiveType::kTriangles, sizeof(LineVertex),
@@ -1064,11 +1198,7 @@
             add_line(&lines[2*i], toSrc, this->coverage(), &verts);
         }
 
-        GrGeometryProcessor* lineGP = this->makeLineGP(target->caps(), target->allocator(),
-                                                       geometryProcessorViewM,
-                                                       geometryProcessorLocalM);
-
-        helper.recordDraw(target, lineGP);
+        fMeshes[0] = helper.mesh();
     }
 
     if (quadCount || conicCount) {
@@ -1101,36 +1231,43 @@
         }
 
         if (quadCount > 0) {
-            GrGeometryProcessor* quadGP = this->makeQuadGP(target->caps(), target->allocator(),
-                                                           geometryProcessorViewM,
-                                                           geometryProcessorLocalM);
+            SkASSERT(predictedPrograms & kQuad_Program);
+            actualPrograms |= kQuad_Program;
 
-            GrSimpleMesh* mesh = target->allocMesh();
-            mesh->setIndexedPatterned(quadsIndexBuffer, kIdxsPerQuad, quadCount,
-                                      kQuadsNumInIdxBuffer, vertexBuffer, kQuadNumVertices,
-                                      firstVertex);
-            target->recordDraw(quadGP, mesh, 1, GrPrimitiveType::kTriangles);
+            fMeshes[1] = target->allocMesh();
+            fMeshes[1]->setIndexedPatterned(quadsIndexBuffer, kIdxsPerQuad, quadCount,
+                                            kQuadsNumInIdxBuffer, vertexBuffer, kQuadNumVertices,
+                                            firstVertex);
             firstVertex += quadCount * kQuadNumVertices;
         }
 
         if (conicCount > 0) {
-            GrGeometryProcessor* conicGP = this->makeConicGP(target->caps(), target->allocator(),
-                                                             geometryProcessorViewM,
-                                                             geometryProcessorLocalM);
+            SkASSERT(predictedPrograms & kConic_Program);
+            actualPrograms |= kConic_Program;
 
-            GrSimpleMesh* mesh = target->allocMesh();
-            mesh->setIndexedPatterned(std::move(quadsIndexBuffer), kIdxsPerQuad, conicCount,
-                                      kQuadsNumInIdxBuffer, std::move(vertexBuffer),
-                                      kQuadNumVertices, firstVertex);
-            target->recordDraw(conicGP, mesh, 1, GrPrimitiveType::kTriangles);
+            fMeshes[2] = target->allocMesh();
+            fMeshes[2]->setIndexedPatterned(std::move(quadsIndexBuffer), kIdxsPerQuad, conicCount,
+                                            kQuadsNumInIdxBuffer, std::move(vertexBuffer),
+                                            kQuadNumVertices, firstVertex);
         }
     }
+
+    // In DDL mode this will replace the predicted program requirements with the actual ones.
+    // However, we will already have surfaced the predicted programs to the DDL.
+    fCharacterization = actualPrograms;
 }
 
 void AAHairlineOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
-    auto pipeline = fHelper.createPipelineWithStencil(flushState);
+    this->createProgramInfo(flushState);
 
-    flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline);
+    for (int i = 0; i < 3; ++i) {
+        if (fProgramInfos[i] && fMeshes[i]) {
+            flushState->bindPipelineAndScissorClip(*fProgramInfos[i], chainBounds);
+            flushState->bindTextures(fProgramInfos[i]->primProc(), nullptr,
+                                     fProgramInfos[i]->pipeline());
+            flushState->drawMesh(*fMeshes[i]);
+        }
+    }
 }
 
 bool GrAAHairLinePathRenderer::onDrawPath(const DrawPathArgs& args) {
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
index f18717c..259b878 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
@@ -110,7 +110,7 @@
 const GrPipeline* GrSimpleMeshDrawOpHelper::CreatePipeline(
                                                 const GrCaps* caps,
                                                 SkArenaAlloc* arena,
-                                                const GrSurfaceProxyView* outputView,
+                                                GrSwizzle outputViewSwizzle,
                                                 GrAppliedClip&& appliedClip,
                                                 const GrXferProcessor::DstProxyView& dstProxyView,
                                                 GrProcessorSet&& processorSet,
@@ -122,7 +122,7 @@
     pipelineArgs.fUserStencil = stencilSettings;
     pipelineArgs.fCaps = caps;
     pipelineArgs.fDstProxyView = dstProxyView;
-    pipelineArgs.fOutputSwizzle = outputView->swizzle();
+    pipelineArgs.fOutputSwizzle = outputViewSwizzle;
 
     return arena->make<GrPipeline>(pipelineArgs,
                                    std::move(processorSet),
@@ -136,7 +136,7 @@
                                                 const GrUserStencilSettings* stencilSettings) {
     return CreatePipeline(&flushState->caps(),
                           flushState->allocator(),
-                          flushState->outputView(),
+                          flushState->outputView()->swizzle(),
                           flushState->detachAppliedClip(),
                           flushState->dstProxyView(),
                           std::move(processorSet),
@@ -147,7 +147,7 @@
 const GrPipeline* GrSimpleMeshDrawOpHelper::createPipeline(GrOpFlushState* flushState) {
     return CreatePipeline(&flushState->caps(),
                           flushState->allocator(),
-                          flushState->outputView(),
+                          flushState->outputView()->swizzle(),
                           flushState->detachAppliedClip(),
                           flushState->dstProxyView(),
                           this->detachProcessorSet(),
@@ -167,13 +167,21 @@
             const GrUserStencilSettings* stencilSettings) {
     auto pipeline = CreatePipeline(caps,
                                    arena,
-                                   outputView,
+                                   outputView->swizzle(),
                                    std::move(appliedClip),
                                    dstProxyView,
                                    std::move(processorSet),
                                    pipelineFlags,
                                    stencilSettings);
 
+    return CreateProgramInfo(arena, pipeline, outputView, geometryProcessor, primitiveType);
+}
+
+GrProgramInfo* GrSimpleMeshDrawOpHelper::CreateProgramInfo(SkArenaAlloc* arena,
+                                                           const GrPipeline* pipeline,
+                                                           const GrSurfaceProxyView* outputView,
+                                                           GrGeometryProcessor* geometryProcessor,
+                                                           GrPrimitiveType primitiveType) {
     GrRenderTargetProxy* outputProxy = outputView->asRenderTargetProxy();
 
     auto tmp = arena->make<GrProgramInfo>(outputProxy->numSamples(),
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
index 16f45a8..4d78203 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
@@ -126,7 +126,7 @@
     static const GrPipeline* CreatePipeline(
                                 const GrCaps*,
                                 SkArenaAlloc*,
-                                const GrSurfaceProxyView* outputView,
+                                GrSwizzle outputViewSwizzle,
                                 GrAppliedClip&&,
                                 const GrXferProcessor::DstProxyView&,
                                 GrProcessorSet&&,
@@ -140,6 +140,12 @@
 
     const GrPipeline* createPipeline(GrOpFlushState* flushState);
 
+    static GrProgramInfo* CreateProgramInfo(SkArenaAlloc*,
+                                            const GrPipeline*,
+                                            const GrSurfaceProxyView* outputView,
+                                            GrGeometryProcessor*,
+                                            GrPrimitiveType);
+
     // Create a programInfo with the following properties:
     //     its primitive processor uses no textures
     //     it has no dynamic state besides the scissor clip
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.cpp b/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.cpp
index eaa8e66..92c4090 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.cpp
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.cpp
@@ -8,17 +8,30 @@
 #include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
 
 const GrPipeline* GrSimpleMeshDrawOpHelperWithStencil::createPipelineWithStencil(
-        GrOpFlushState* flushState) {
-    return GrSimpleMeshDrawOpHelper::CreatePipeline(&flushState->caps(),
-                                                    flushState->allocator(),
-                                                    flushState->outputView(),
-                                                    flushState->detachAppliedClip(),
-                                                    flushState->dstProxyView(),
+                                            const GrCaps* caps,
+                                            SkArenaAlloc* arena,
+                                            GrSwizzle outputViewSwizzle,
+                                            GrAppliedClip&& appliedClip,
+                                            const GrXferProcessor::DstProxyView& dstProxyView) {
+    return GrSimpleMeshDrawOpHelper::CreatePipeline(caps,
+                                                    arena,
+                                                    outputViewSwizzle,
+                                                    std::move(appliedClip),
+                                                    dstProxyView,
                                                     this->detachProcessorSet(),
                                                     this->pipelineFlags(),
                                                     this->stencilSettings());
 }
 
+const GrPipeline* GrSimpleMeshDrawOpHelperWithStencil::createPipelineWithStencil(
+        GrOpFlushState* flushState) {
+    return this->createPipelineWithStencil(&flushState->caps(),
+                                           flushState->allocator(),
+                                           flushState->outputView()->swizzle(),
+                                           flushState->detachAppliedClip(),
+                                           flushState->dstProxyView());
+}
+
 GrSimpleMeshDrawOpHelperWithStencil::GrSimpleMeshDrawOpHelperWithStencil(
         const MakeArgs& args, GrAAType aaType, const GrUserStencilSettings* stencilSettings,
         InputFlags inputFlags)
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h b/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h
index 496f38f..b5210c1 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h
@@ -22,6 +22,12 @@
 
     using GrSimpleMeshDrawOpHelper::visitProxies;
 
+    const GrPipeline* createPipelineWithStencil(const GrCaps*,
+                                                SkArenaAlloc*,
+                                                GrSwizzle outputViewSwizzle,
+                                                GrAppliedClip&&,
+                                                const GrXferProcessor::DstProxyView&);
+
     const GrPipeline* createPipelineWithStencil(GrOpFlushState* flushState);
 
     GrProgramInfo* createProgramInfoWithStencil(const GrCaps*,
diff --git a/tools/gpu/TestOps.cpp b/tools/gpu/TestOps.cpp
index a9fffbf..f575653 100644
--- a/tools/gpu/TestOps.cpp
+++ b/tools/gpu/TestOps.cpp
@@ -12,6 +12,7 @@
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrMemoryPool.h"
 #include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrVertexWriter.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
@@ -90,7 +91,11 @@
                                       GrClampType) override;
 
     void visitProxies(const VisitProxyFunc& func) const override {
-        fProcessorSet.visitProxies(func);
+        if (fProgramInfo) {
+            fProgramInfo->visitFPProxies(func);
+        } else {
+            fProcessorSet.visitProxies(func);
+        }
     }
 
 private: