Reland "Stack-allocate pipelines for GrMeshDrawOp"

This is a reland of dfe5000a5ff2cabb50ddc139882dc1f3005fa429

Original change's description:
> Stack-allocate pipelines for GrMeshDrawOp
>
> Stack-allocates the pipelines in onExecute. This saves us from having
> to store the pipelines on the heap, as well as delaying the need to
> detach processors until onExecute. The delay is an improvement because
> it allows us to keep visiting proxies after onPrepare. (Previously,
> they were moved out of GrProcessorSet and into a pipeline during
> onPrepare, so visiting proxies was impossible after that point.)
>
> Bug: skia:8731
> Change-Id: Idc05063fb0dfbfed42b434e429fa5a497097bdae
> Reviewed-on: https://skia-review.googlesource.com/c/193368
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>

TBR=robertphillips@google.com

Bug: skia:8731
Change-Id: I32def1a35bb0593470fa672691a9e697dc6d9680
Reviewed-on: https://skia-review.googlesource.com/c/195261
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp
index 7b983c4..a02b2f4 100644
--- a/src/gpu/GrOpFlushState.cpp
+++ b/src/gpu/GrOpFlushState.cpp
@@ -32,8 +32,19 @@
     return fCommandBuffer->asRTCommandBuffer();
 }
 
-void GrOpFlushState::executeDrawsAndUploadsForMeshDrawOp(const GrOp* op, const SkRect& opBounds) {
+void GrOpFlushState::executeDrawsAndUploadsForMeshDrawOp(
+        const GrOp* op, const SkRect& chainBounds, GrProcessorSet&& processorSet,
+        uint32_t pipelineFlags, const GrUserStencilSettings* stencilSettings) {
     SkASSERT(this->rtCommandBuffer());
+
+    GrPipeline::InitArgs pipelineArgs;
+    pipelineArgs.fFlags = pipelineFlags;
+    pipelineArgs.fDstProxy = this->dstProxy();
+    pipelineArgs.fCaps = &this->caps();
+    pipelineArgs.fResourceProvider = this->resourceProvider();
+    pipelineArgs.fUserStencil = stencilSettings;
+    GrPipeline pipeline(pipelineArgs, std::move(processorSet), this->detachAppliedClip());
+
     while (fCurrDraw != fDraws.end() && fCurrDraw->fOp == op) {
         GrDeferredUploadToken drawToken = fTokenTracker->nextTokenToFlush();
         while (fCurrUpload != fInlineUploads.end() &&
@@ -41,9 +52,10 @@
             this->rtCommandBuffer()->inlineUpload(this, fCurrUpload->fUpload);
             ++fCurrUpload;
         }
-        this->rtCommandBuffer()->draw(*fCurrDraw->fGeometryProcessor, *fCurrDraw->fPipeline,
-                                      fCurrDraw->fFixedDynamicState, fCurrDraw->fDynamicStateArrays,
-                                      fCurrDraw->fMeshes, fCurrDraw->fMeshCnt, opBounds);
+        this->rtCommandBuffer()->draw(
+                *fCurrDraw->fGeometryProcessor, pipeline, fCurrDraw->fFixedDynamicState,
+                fCurrDraw->fDynamicStateArrays, fCurrDraw->fMeshes, fCurrDraw->fMeshCnt,
+                chainBounds);
         fTokenTracker->flushToken();
         ++fCurrDraw;
     }
@@ -98,10 +110,10 @@
     return fTokenTracker->nextTokenToFlush();
 }
 
-void GrOpFlushState::draw(sk_sp<const GrGeometryProcessor> gp, const GrPipeline* pipeline,
-                          const GrPipeline::FixedDynamicState* fixedDynamicState,
-                          const GrPipeline::DynamicStateArrays* dynamicStateArrays,
-                          const GrMesh meshes[], int meshCnt) {
+void GrOpFlushState::recordDraw(
+        sk_sp<const GrGeometryProcessor> gp, const GrMesh meshes[], int meshCnt,
+        const GrPipeline::FixedDynamicState* fixedDynamicState,
+        const GrPipeline::DynamicStateArrays* dynamicStateArrays) {
     SkASSERT(fOpArgs);
     SkASSERT(fOpArgs->fOp);
     bool firstDraw = fDraws.begin() == fDraws.end();
@@ -119,7 +131,6 @@
         }
     }
     draw.fGeometryProcessor = std::move(gp);
-    draw.fPipeline = pipeline;
     draw.fFixedDynamicState = fixedDynamicState;
     draw.fDynamicStateArrays = dynamicStateArrays;
     draw.fMeshes = meshes;
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 54aa4dd..07983d4 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -41,7 +41,9 @@
     void doUpload(GrDeferredTextureUploadFn&);
 
     /** Called as ops are executed. Must be called in the same order as the ops were prepared. */
-    void executeDrawsAndUploadsForMeshDrawOp(const GrOp* op, const SkRect& opBounds);
+    void executeDrawsAndUploadsForMeshDrawOp(
+            const GrOp* op, const SkRect& chainBounds, GrProcessorSet&&, uint32_t pipelineFlags = 0,
+            const GrUserStencilSettings* = &GrUserStencilSettings::kUnused);
 
     GrGpuCommandBuffer* commandBuffer() { return fCommandBuffer; }
     // Helper function used by Ops that are only called via RenderTargetOpLists
@@ -79,12 +81,9 @@
     GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&&) final;
 
     /** Overrides of GrMeshDrawOp::Target. */
-    void draw(sk_sp<const GrGeometryProcessor>,
-              const GrPipeline*,
-              const GrPipeline::FixedDynamicState*,
-              const GrPipeline::DynamicStateArrays*,
-              const GrMesh[],
-              int meshCnt) final;
+    void recordDraw(
+            sk_sp<const GrGeometryProcessor>, const GrMesh[], int meshCnt,
+            const GrPipeline::FixedDynamicState*, const GrPipeline::DynamicStateArrays*) final;
     void* makeVertexSpace(size_t vertexSize, int vertexCount, sk_sp<const GrBuffer>*,
                           int* startVertex) final;
     uint16_t* makeIndexSpace(int indexCount, sk_sp<const GrBuffer>*, int* startIndex) final;
@@ -97,6 +96,7 @@
     void putBackIndices(int indexCount) final;
     void putBackVertices(int vertices, size_t vertexStride) final;
     GrRenderTargetProxy* proxy() const final { return fOpArgs->fProxy; }
+    const GrAppliedClip* appliedClip() final { return fOpArgs->fAppliedClip; }
     GrAppliedClip detachAppliedClip() final;
     const GrXferProcessor::DstProxy& dstProxy() const final { return fOpArgs->fDstProxy; }
     GrDeferredUploadTarget* deferredUploadTarget() final { return this; }
@@ -113,7 +113,7 @@
 
 private:
     /** GrMeshDrawOp::Target override. */
-    SkArenaAlloc* pipelineArena() override { return &fArena; }
+    SkArenaAlloc* allocator() override { return &fArena; }
 
     struct InlineUpload {
         InlineUpload(GrDeferredTextureUploadFn&& upload, GrDeferredUploadToken token)
@@ -129,7 +129,6 @@
     struct Draw {
         ~Draw();
         sk_sp<const GrGeometryProcessor> fGeometryProcessor;
-        const GrPipeline* fPipeline = nullptr;
         const GrPipeline::FixedDynamicState* fFixedDynamicState;
         const GrPipeline::DynamicStateArrays* fDynamicStateArrays;
         const GrMesh* fMeshes = nullptr;
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index cb9d364..1e44b87 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -713,7 +713,6 @@
 
 private:
     void onPrepareDraws(Target* target) override {
-        auto pipe = fHelper.makePipeline(target);
         int instanceCount = fPaths.count();
 
         SkMatrix invert;
@@ -794,11 +793,14 @@
                 firstIndex += draw.fIndexCnt;
                 firstVertex += draw.fVertexCnt;
             }
-            target->draw(quadProcessor, pipe.fPipeline, pipe.fFixedDynamicState, nullptr, meshes,
-                         draws.count());
+            target->recordDraw(quadProcessor, meshes, draws.count());
         }
     }
 
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
+    }
+
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         AAConvexPathOp* that = t->cast<AAConvexPathOp>();
         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index 877a522..0933a23 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -845,6 +845,7 @@
 
 private:
     void onPrepareDraws(Target*) override;
+    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
 
     typedef SkTArray<SkPoint, true> PtArray;
     typedef SkTArray<int, true> IntArray;
@@ -954,7 +955,6 @@
         return;
     }
 
-    auto pipe = fHelper.makePipeline(target);
     // do lines first
     if (lineCount) {
         sk_sp<GrGeometryProcessor> lineGP;
@@ -993,7 +993,7 @@
         mesh->setIndexedPatterned(std::move(linesIndexBuffer), kIdxsPerLineSeg, kLineSegNumVertices,
                                   lineCount, kLineSegsNumInIdxBuffer);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->draw(std::move(lineGP), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+        target->recordDraw(std::move(lineGP), mesh);
     }
 
     if (quadCount || conicCount) {
@@ -1048,7 +1048,7 @@
             mesh->setIndexedPatterned(quadsIndexBuffer, kIdxsPerQuad, kQuadNumVertices, quadCount,
                                       kQuadsNumInIdxBuffer);
             mesh->setVertexData(vertexBuffer, firstVertex);
-            target->draw(std::move(quadGP), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+            target->recordDraw(std::move(quadGP), mesh);
             firstVertex += quadCount * kQuadNumVertices;
         }
 
@@ -1057,11 +1057,15 @@
             mesh->setIndexedPatterned(std::move(quadsIndexBuffer), kIdxsPerQuad, kQuadNumVertices,
                                       conicCount, kQuadsNumInIdxBuffer);
             mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-            target->draw(std::move(conicGP), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+            target->recordDraw(std::move(conicGP), mesh);
         }
     }
 }
 
+void AAHairlineOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
+    fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
+}
+
 bool GrAAHairLinePathRenderer::onDrawPath(const DrawPathArgs& args) {
     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
                               "GrAAHairlinePathRenderer::onDrawPath");
diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
index 6205b84..4ade866 100644
--- a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
@@ -193,9 +193,8 @@
     }
 
 private:
-    void draw(Target* target, sk_sp<const GrGeometryProcessor> gp, const GrPipeline* pipeline,
-              const GrPipeline::FixedDynamicState* fixedDynamicState, int vertexCount,
-              size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
+    void recordDraw(Target* target, sk_sp<const GrGeometryProcessor> gp, int vertexCount,
+                    size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
         if (vertexCount == 0 || indexCount == 0) {
             return;
         }
@@ -221,11 +220,10 @@
         mesh->setIndexed(std::move(indexBuffer), indexCount, firstIndex, 0, vertexCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->draw(std::move(gp), pipeline, fixedDynamicState, mesh);
+        target->recordDraw(std::move(gp), mesh);
     }
 
     void onPrepareDraws(Target* target) override {
-        auto pipe = fHelper.makePipeline(target);
         // Setup GrGeometryProcessor
         sk_sp<GrGeometryProcessor> gp(create_lines_only_gp(target->caps().shaderCaps(),
                                                            fHelper.compatibleWithAlphaAsCoverage(),
@@ -259,8 +257,8 @@
             if (vertexCount + currentVertices > static_cast<int>(UINT16_MAX)) {
                 // if we added the current instance, we would overflow the indices we can store in a
                 // uint16_t. Draw what we've got so far and reset.
-                this->draw(target, gp, pipe.fPipeline, pipe.fFixedDynamicState, vertexCount,
-                           vertexStride, vertices, indexCount, indices);
+                this->recordDraw(
+                        target, gp, vertexCount, vertexStride, vertices, indexCount, indices);
                 vertexCount = 0;
                 indexCount = 0;
             }
@@ -291,13 +289,17 @@
             indexCount += currentIndices;
         }
         if (vertexCount <= SK_MaxS32 && indexCount <= SK_MaxS32) {
-            this->draw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, vertexCount,
-                       vertexStride, vertices, indexCount, indices);
+            this->recordDraw(target, std::move(gp), vertexCount, vertexStride, vertices, indexCount,
+                             indices);
         }
         sk_free(vertices);
         sk_free(indices);
     }
 
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
+    }
+
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>();
         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 8e231cb..fae54df 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -298,16 +298,13 @@
     GR_STATIC_ASSERT(GrDistanceFieldA8TextGeoProc::kMaxTextures == kMaxTextures);
     GR_STATIC_ASSERT(GrDistanceFieldLCDTextGeoProc::kMaxTextures == kMaxTextures);
 
-    static const uint32_t kPipelineFlags = 0;
-    auto pipe = target->makePipeline(kPipelineFlags, std::move(fProcessors),
-                                     target->detachAppliedClip(), kMaxTextures);
+    auto fixedDynamicState = target->makeFixedDynamicState(kMaxTextures);
     for (unsigned i = 0; i < numActiveProxies; ++i) {
-        pipe.fFixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[i].get();
+        fixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[i].get();
     }
 
     FlushInfo flushInfo;
-    flushInfo.fPipeline = pipe.fPipeline;
-    flushInfo.fFixedDynamicState = pipe.fFixedDynamicState;
+    flushInfo.fFixedDynamicState = fixedDynamicState;
 
     bool vmPerspective = fGeoData[0].fViewMatrix.hasPerspective();
     if (this->usesDistanceFields()) {
@@ -389,6 +386,12 @@
     this->flush(target, &flushInfo);
 }
 
+void GrAtlasTextOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
+    static const uint32_t kPipelineFlags = 0;
+    flushState->executeDrawsAndUploadsForMeshDrawOp(
+            this, chainBounds, std::move(fProcessors), kPipelineFlags);
+}
+
 void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
     if (!flushInfo->fGlyphsToFlush) {
         return;
@@ -428,8 +431,8 @@
     mesh->setIndexedPatterned(flushInfo->fIndexBuffer, kIndicesPerGlyph, kVerticesPerGlyph,
                               flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
     mesh->setVertexData(flushInfo->fVertexBuffer, flushInfo->fVertexOffset);
-    target->draw(flushInfo->fGeometryProcessor, flushInfo->fPipeline, flushInfo->fFixedDynamicState,
-                 mesh);
+    target->recordDraw(
+            flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fFixedDynamicState, nullptr);
     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
     flushInfo->fGlyphsToFlush = 0;
 }
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index d8d453a..8f5ff20 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -107,13 +107,13 @@
         sk_sp<const GrBuffer> fVertexBuffer;
         sk_sp<const GrBuffer> fIndexBuffer;
         sk_sp<GrGeometryProcessor> fGeometryProcessor;
-        const GrPipeline* fPipeline;
         GrPipeline::FixedDynamicState* fFixedDynamicState;
         int fGlyphsToFlush;
         int fVertexOffset;
     };
 
     void onPrepareDraws(Target*) override;
+    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
 
     GrMaskFormat maskFormat() const {
         switch (fMaskType) {
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index ca97f29..f912f59 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -617,13 +617,16 @@
             }
             rectIndex++;
         }
+        helper.recordDraw(target, std::move(gp));
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
         uint32_t pipelineFlags = 0;
         if (AAMode::kCoverageWithMSAA == fAAMode) {
             pipelineFlags |= GrPipeline::kHWAntialias_Flag;
         }
-        auto pipe = target->makePipeline(pipelineFlags, std::move(fProcessorSet),
-                                         target->detachAppliedClip());
-        helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
+        flushState->executeDrawsAndUploadsForMeshDrawOp(
+                this, chainBounds, std::move(fProcessorSet), pipelineFlags);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 7b50162..1ac80e6 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -66,14 +66,11 @@
 class PathGeoBuilder {
 public:
     PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
-                   sk_sp<const GrGeometryProcessor> geometryProcessor, const GrPipeline* pipeline,
-                   const GrPipeline::FixedDynamicState* fixedDynamicState)
+                   sk_sp<const GrGeometryProcessor> geometryProcessor)
             : fPrimitiveType(primitiveType)
             , fTarget(target)
             , fVertexStride(sizeof(SkPoint))
             , fGeometryProcessor(std::move(geometryProcessor))
-            , fPipeline(pipeline)
-            , fFixedDynamicState(fixedDynamicState)
             , fFirstIndex(0)
             , fIndicesInChunk(0)
             , fIndices(nullptr) {
@@ -279,7 +276,7 @@
                                  vertexCount - 1, GrPrimitiveRestart::kNo);
             }
             mesh->setVertexData(std::move(fVertexBuffer), fFirstVertex);
-            fTarget->draw(fGeometryProcessor, fPipeline, fFixedDynamicState, mesh);
+            fTarget->recordDraw(fGeometryProcessor, mesh);
         }
 
         fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
@@ -316,8 +313,6 @@
     GrMeshDrawOp::Target* fTarget;
     size_t fVertexStride;
     sk_sp<const GrGeometryProcessor> fGeometryProcessor;
-    const GrPipeline* fPipeline;
-    const GrPipeline::FixedDynamicState* fFixedDynamicState;
 
     sk_sp<const GrBuffer> fVertexBuffer;
     int fFirstVertex;
@@ -430,9 +425,7 @@
         } else {
             primitiveType = GrPrimitiveType::kTriangles;
         }
-        auto pipe = fHelper.makePipeline(target);
-        PathGeoBuilder pathGeoBuilder(primitiveType, target, std::move(gp), pipe.fPipeline,
-                                      pipe.fFixedDynamicState);
+        PathGeoBuilder pathGeoBuilder(primitiveType, target, std::move(gp));
 
         // fill buffers
         for (int i = 0; i < instanceCount; i++) {
@@ -441,6 +434,10 @@
         }
     }
 
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
+    }
+
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         DefaultPathOp* that = t->cast<DefaultPathOp>();
         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
diff --git a/src/gpu/ops/GrDrawAtlasOp.cpp b/src/gpu/ops/GrDrawAtlasOp.cpp
index e37e960..6dfeb3b 100644
--- a/src/gpu/ops/GrDrawAtlasOp.cpp
+++ b/src/gpu/ops/GrDrawAtlasOp.cpp
@@ -48,6 +48,7 @@
 
 private:
     void onPrepareDraws(Target*) override;
+    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
 
     const SkPMColor4f& color() const { return fColor; }
     const SkMatrix& viewMatrix() const { return fViewMatrix; }
@@ -206,8 +207,11 @@
         memcpy(vertPtr, args.fVerts.begin(), allocSize);
         vertPtr += allocSize;
     }
-    auto pipe = fHelper.makePipeline(target);
-    helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
+    helper.recordDraw(target, std::move(gp));
+}
+
+void DrawAtlasOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
+    fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
 }
 
 GrOp::CombineResult DrawAtlasOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index ec82f87..f1589ed 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -47,6 +47,7 @@
     };
 
     void onPrepareDraws(Target*) override;
+    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
 
     void drawVolatile(Target*);
     void drawNonVolatile(Target*);
@@ -502,8 +503,11 @@
         mesh->setNonIndexedNonInstanced(fVertexCount);
     }
     mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-    auto pipe = fHelper.makePipeline(target);
-    target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+    target->recordDraw(std::move(gp), mesh);
+}
+
+void DrawVerticesOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
+    fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
 }
 
 GrOp::CombineResult DrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
diff --git a/src/gpu/ops/GrFillRectOp.cpp b/src/gpu/ops/GrFillRectOp.cpp
index 1d4e13c..1712433 100644
--- a/src/gpu/ops/GrFillRectOp.cpp
+++ b/src/gpu/ops/GrFillRectOp.cpp
@@ -227,10 +227,12 @@
             return;
         }
         mesh->setVertexData(std::move(vbuffer), vertexOffsetInBuffer);
+        target->recordDraw(std::move(gp), mesh);
+    }
 
-        auto pipe = fHelper.makePipeline(target);
-        target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
-   }
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
+    }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         TRACE_EVENT0("skia", TRACE_FUNC);
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index dcb88d3..4d49c6e 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -284,9 +284,13 @@
                                                   kVertsPerRect * patch.fIter->numRectsToDraw());
             }
         }
-        auto pipe = fHelper.makePipeline(target, 1);
-        pipe.fFixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy.get();
-        helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
+        auto fixedDynamicState = target->makeFixedDynamicState(1);
+        fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy.get();
+        helper.recordDraw(target, std::move(gp), fixedDynamicState);
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index ac019f3..a8ad863 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -14,10 +14,6 @@
 
 void GrMeshDrawOp::onPrepare(GrOpFlushState* state) { this->onPrepareDraws(state); }
 
-void GrMeshDrawOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
-    state->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds);
-}
-
 //////////////////////////////////////////////////////////////////////////////
 
 GrMeshDrawOp::PatternHelper::PatternHelper(Target* target, GrPrimitiveType primitiveType,
@@ -54,9 +50,14 @@
 }
 
 void GrMeshDrawOp::PatternHelper::recordDraw(
-        Target* target, sk_sp<const GrGeometryProcessor> gp, const GrPipeline* pipeline,
+        Target* target, sk_sp<const GrGeometryProcessor> gp) const {
+    target->recordDraw(std::move(gp), fMesh);
+}
+
+void GrMeshDrawOp::PatternHelper::recordDraw(
+        Target* target, sk_sp<const GrGeometryProcessor> gp,
         const GrPipeline::FixedDynamicState* fixedDynamicState) const {
-    target->draw(std::move(gp), pipeline, fixedDynamicState, fMesh);
+    target->recordDraw(std::move(gp), fMesh, 1, fixedDynamicState, nullptr);
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -73,45 +74,32 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-GrPipeline::FixedDynamicState* GrMeshDrawOp::Target::allocFixedDynamicState(
-        const SkIRect& rect, int numPrimitiveProcessorTextures) {
-    auto result = this->pipelineArena()->make<GrPipeline::FixedDynamicState>(rect);
-    if (numPrimitiveProcessorTextures) {
-        result->fPrimitiveProcessorTextures =
-                this->allocPrimitiveProcessorTextureArray(numPrimitiveProcessorTextures);
-    }
-    return result;
-}
-
 GrPipeline::DynamicStateArrays* GrMeshDrawOp::Target::allocDynamicStateArrays(
         int numMeshes, int numPrimitiveProcessorTextures, bool allocScissors) {
-    auto result = this->pipelineArena()->make<GrPipeline::DynamicStateArrays>();
+    auto result = this->allocator()->make<GrPipeline::DynamicStateArrays>();
     if (allocScissors) {
-        result->fScissorRects = this->pipelineArena()->makeArray<SkIRect>(numMeshes);
+        result->fScissorRects = this->allocator()->makeArray<SkIRect>(numMeshes);
     }
     if (numPrimitiveProcessorTextures) {
-        result->fPrimitiveProcessorTextures = this->allocPrimitiveProcessorTextureArray(
-                numPrimitiveProcessorTextures * numMeshes);
+        result->fPrimitiveProcessorTextures =
+                this->allocator()->makeArrayDefault<GrTextureProxy*>(
+                        numPrimitiveProcessorTextures * numMeshes);
     }
     return result;
 }
 
-GrMeshDrawOp::Target::PipelineAndFixedDynamicState GrMeshDrawOp::Target::makePipeline(
-        uint32_t pipelineFlags, GrProcessorSet&& processorSet, GrAppliedClip&& clip,
+GrPipeline::FixedDynamicState* GrMeshDrawOp::Target::makeFixedDynamicState(
         int numPrimProcTextures) {
-    GrPipeline::InitArgs pipelineArgs;
-    pipelineArgs.fFlags = pipelineFlags;
-    pipelineArgs.fDstProxy = this->dstProxy();
-    pipelineArgs.fCaps = &this->caps();
-    pipelineArgs.fResourceProvider = this->resourceProvider();
-    GrPipeline::FixedDynamicState* fixedDynamicState = nullptr;
-    if (clip.scissorState().enabled() || numPrimProcTextures) {
-        fixedDynamicState = this->allocFixedDynamicState(clip.scissorState().rect());
+    const GrAppliedClip* clip = this->appliedClip();
+    if ((clip && clip->scissorState().enabled()) || numPrimProcTextures) {
+        const SkIRect& scissor = (clip) ? clip->scissorState().rect() : SkIRect::MakeEmpty();
+        auto fixedDynamicState =
+                this->allocator()->make<GrPipeline::FixedDynamicState>(scissor);
         if (numPrimProcTextures) {
             fixedDynamicState->fPrimitiveProcessorTextures =
-                    this->allocPrimitiveProcessorTextureArray(numPrimProcTextures);
+                    this->allocator()->makeArrayDefault<GrTextureProxy*>(numPrimProcTextures);
         }
+        return fixedDynamicState;
     }
-    return {this->allocPipeline(pipelineArgs, std::move(processorSet), std::move(clip)),
-            fixedDynamicState};
+    return nullptr;
 }
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 170fee0..f0c11a4 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -39,7 +39,8 @@
                       int indicesPerRepetition, int repeatCount);
 
         /** Called to issue draws to the GrMeshDrawOp::Target.*/
-        void recordDraw(Target*, sk_sp<const GrGeometryProcessor>, const GrPipeline*,
+        void recordDraw(Target*, sk_sp<const GrGeometryProcessor>) const;
+        void recordDraw(Target*, sk_sp<const GrGeometryProcessor>,
                         const GrPipeline::FixedDynamicState*) const;
 
         void* vertices() const { return fVertices; }
@@ -72,7 +73,6 @@
 
 private:
     void onPrepare(GrOpFlushState* state) final;
-    void onExecute(GrOpFlushState* state, const SkRect& chainBounds) final;
     virtual void onPrepareDraws(Target*) = 0;
     typedef GrDrawOp INHERITED;
 };
@@ -82,18 +82,18 @@
     virtual ~Target() {}
 
     /** Adds a draw of a mesh. */
-    virtual void draw(sk_sp<const GrGeometryProcessor>,
-                      const GrPipeline*,
-                      const GrPipeline::FixedDynamicState*,
-                      const GrPipeline::DynamicStateArrays*,
-                      const GrMesh[],
-                      int meshCount) = 0;
-    /** Helper for drawing a single GrMesh. */
-    void draw(sk_sp<const GrGeometryProcessor> gp,
-              const GrPipeline* pipeline,
-              const GrPipeline::FixedDynamicState* fixedDynamicState,
-              const GrMesh* mesh) {
-        this->draw(std::move(gp), pipeline, fixedDynamicState, nullptr, mesh, 1);
+    virtual void recordDraw(
+            sk_sp<const GrGeometryProcessor>, const GrMesh[], int meshCnt,
+            const GrPipeline::FixedDynamicState*, const GrPipeline::DynamicStateArrays*) = 0;
+
+    /**
+     * Helper for drawing GrMesh(es) with zero primProc textures and no dynamic state besides the
+     * scissor clip.
+     */
+    void recordDraw(sk_sp<const GrGeometryProcessor> gp, const GrMesh meshes[], int meshCnt = 1) {
+        static constexpr int kZeroPrimProcTextures = 0;
+        auto fixedDynamicState = this->makeFixedDynamicState(kZeroPrimProcTextures);
+        this->recordDraw(std::move(gp), meshes, meshCnt, fixedDynamicState, nullptr);
     }
 
     /**
@@ -135,55 +135,21 @@
     virtual void putBackIndices(int indices) = 0;
     virtual void putBackVertices(int vertices, size_t vertexStride) = 0;
 
-    /**
-     * Allocate space for a pipeline. The target ensures this pipeline lifetime is at least
-     * as long as any deferred execution of draws added via draw().
-     * @tparam Args
-     * @param args
-     * @return
-     */
-    template <typename... Args>
-    GrPipeline* allocPipeline(Args&&... args) {
-        return this->pipelineArena()->make<GrPipeline>(std::forward<Args>(args)...);
-    }
-
     GrMesh* allocMesh(GrPrimitiveType primitiveType) {
-        return this->pipelineArena()->make<GrMesh>(primitiveType);
+        return this->allocator()->make<GrMesh>(primitiveType);
     }
 
-    GrMesh* allocMeshes(int n) { return this->pipelineArena()->makeArray<GrMesh>(n); }
-
-    GrPipeline::FixedDynamicState* allocFixedDynamicState(const SkIRect& rect,
-                                                          int numPrimitiveProcessorTextures = 0);
+    GrMesh* allocMeshes(int n) { return this->allocator()->makeArray<GrMesh>(n); }
 
     GrPipeline::DynamicStateArrays* allocDynamicStateArrays(int numMeshes,
                                                             int numPrimitiveProcessorTextures,
                                                             bool allocScissors);
 
-    GrTextureProxy** allocPrimitiveProcessorTextureArray(int n) {
-        SkASSERT(n > 0);
-        return this->pipelineArena()->makeArrayDefault<GrTextureProxy*>(n);
-    }
-
-    // Once we have C++17 structured bindings make this just be a tuple because then we can do:
-    //      auto [pipeline, fixedDynamicState] = target->makePipeline(...);
-    // in addition to:
-    //      std::tie(flushInfo.fPipeline, flushInfo.fFixedState) = target->makePipeline(...);
-    struct PipelineAndFixedDynamicState {
-        const GrPipeline* fPipeline;
-        GrPipeline::FixedDynamicState* fFixedDynamicState;
-    };
-
-    /**
-     * Helper that makes a pipeline targeting the op's render target that incorporates the op's
-     * GrAppliedClip and uses a fixed dynamic state.
-     */
-    PipelineAndFixedDynamicState makePipeline(uint32_t pipelineFlags, GrProcessorSet&&,
-                                              GrAppliedClip&&,
-                                              int numPrimitiveProcessorTextures = 0);
+    GrPipeline::FixedDynamicState* makeFixedDynamicState(int numPrimitiveProcessorTextures);
 
     virtual GrRenderTargetProxy* proxy() const = 0;
 
+    virtual const GrAppliedClip* appliedClip() = 0;
     virtual GrAppliedClip detachAppliedClip() = 0;
 
     virtual const GrXferProcessor::DstProxy& dstProxy() const = 0;
@@ -199,7 +165,7 @@
     virtual GrDeferredUploadTarget* deferredUploadTarget() = 0;
 
 private:
-    virtual SkArenaAlloc* pipelineArena() = 0;
+    virtual SkArenaAlloc* allocator() = 0;
 };
 
 #endif
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index 315455f..3c44259 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -1289,8 +1289,11 @@
         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        auto pipe = fHelper.makePipeline(target);
-        target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+        target->recordDraw(std::move(gp), mesh);
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -1573,8 +1576,11 @@
         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        auto pipe = fHelper.makePipeline(target);
-        target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+        target->recordDraw(std::move(gp), mesh);
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -1806,8 +1812,11 @@
                             origin_centered_tri_strip(xMaxOffset, yMaxOffset),
                             invRadii);
         }
-        auto pipe = fHelper.makePipeline(target);
-        helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
+        helper.recordDraw(target, std::move(gp));
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -2020,8 +2029,11 @@
                             origin_centered_tri_strip(innerRatioX + offsetDx,
                                                       innerRatioY + offsetDy));
         }
-        auto pipe = fHelper.makePipeline(target);
-        helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
+        helper.recordDraw(target, std::move(gp));
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -2448,8 +2460,11 @@
         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        auto pipe = fHelper.makePipeline(target);
-        target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+        target->recordDraw(std::move(gp), mesh);
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -2716,8 +2731,11 @@
                             reciprocalRadii);
             }
         }
-        auto pipe = fHelper.makePipeline(target);
-        helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
+        helper.recordDraw(target, std::move(gp));
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index c050142..a90bfb9 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -134,8 +134,11 @@
                 iter.next();
             }
         }
-        auto pipe = fHelper.makePipeline(target);
-        helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
+        helper.recordDraw(target, std::move(gp));
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index 0d99501..8cb87d6 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -589,15 +589,16 @@
             }
         }
 
-        static const uint32_t kPipelineFlags = 0;
-        auto pipe = target->makePipeline(kPipelineFlags, GrProcessorSet::MakeEmptySet(),
-                                         target->detachAppliedClip());
-
         GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+        target->recordDraw(std::move(gp), mesh);
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        flushState->executeDrawsAndUploadsForMeshDrawOp(
+                this, chainBounds, GrProcessorSet::MakeEmptySet());
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
index ad7fdf5..dcd5567 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
@@ -98,6 +98,17 @@
     return result;
 }
 
+void GrSimpleMeshDrawOpHelper::executeDrawsAndUploads(
+        const GrOp* op, GrOpFlushState* flushState, const SkRect& chainBounds) {
+    if (fProcessors) {
+        flushState->executeDrawsAndUploadsForMeshDrawOp(
+                op, chainBounds, std::move(*fProcessors), fPipelineFlags);
+    } else {
+        flushState->executeDrawsAndUploadsForMeshDrawOp(
+                op, chainBounds, GrProcessorSet::MakeEmptySet(), fPipelineFlags);
+    }
+}
+
 #ifdef SK_DEBUG
 SkString GrSimpleMeshDrawOpHelper::dumpInfo() const {
     const GrProcessorSet& processors = fProcessors ? *fProcessors : GrProcessorSet::EmptySet();
@@ -122,42 +133,6 @@
 }
 #endif
 
-GrPipeline::InitArgs GrSimpleMeshDrawOpHelper::pipelineInitArgs(
-        GrMeshDrawOp::Target* target) const {
-    GrPipeline::InitArgs args;
-    args.fFlags = this->pipelineFlags();
-    args.fDstProxy = target->dstProxy();
-    args.fCaps = &target->caps();
-    args.fResourceProvider = target->resourceProvider();
-    return args;
-}
-
-auto GrSimpleMeshDrawOpHelper::internalMakePipeline(GrMeshDrawOp::Target* target,
-                                                    const GrPipeline::InitArgs& args,
-                                                    int numPrimitiveProcessorProxies)
-        -> PipelineAndFixedDynamicState {
-    // A caller really should only call this once as the processor set and applied clip get
-    // moved into the GrPipeline.
-    SkASSERT(!fMadePipeline);
-    SkDEBUGCODE(fMadePipeline = true);
-    auto clip = target->detachAppliedClip();
-    GrPipeline::FixedDynamicState* fixedDynamicState = nullptr;
-    if (clip.scissorState().enabled() || numPrimitiveProcessorProxies) {
-        fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect());
-        if (numPrimitiveProcessorProxies) {
-            fixedDynamicState->fPrimitiveProcessorTextures =
-                    target->allocPrimitiveProcessorTextureArray(numPrimitiveProcessorProxies);
-        }
-    }
-    if (fProcessors) {
-        return {target->allocPipeline(args, std::move(*fProcessors), std::move(clip)),
-                fixedDynamicState};
-    } else {
-        return {target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip)),
-                fixedDynamicState};
-    }
-}
-
 GrSimpleMeshDrawOpHelperWithStencil::GrSimpleMeshDrawOpHelperWithStencil(
         const MakeArgs& args, GrAAType aaType, const GrUserStencilSettings* stencilSettings,
         Flags flags)
@@ -179,12 +154,15 @@
            fStencilSettings == that.fStencilSettings;
 }
 
-auto GrSimpleMeshDrawOpHelperWithStencil::makePipeline(GrMeshDrawOp::Target* target,
-                                                       int numPrimitiveProcessorTextures)
-        -> PipelineAndFixedDynamicState {
-    auto args = INHERITED::pipelineInitArgs(target);
-    args.fUserStencil = fStencilSettings;
-    return this->internalMakePipeline(target, args, numPrimitiveProcessorTextures);
+void GrSimpleMeshDrawOpHelperWithStencil::executeDrawsAndUploads(
+        const GrOp* op, GrOpFlushState* flushState, const SkRect& chainBounds) {
+    if (fProcessors) {
+        flushState->executeDrawsAndUploadsForMeshDrawOp(
+                op, chainBounds, std::move(*fProcessors), fPipelineFlags, fStencilSettings);
+    } else {
+        flushState->executeDrawsAndUploadsForMeshDrawOp(
+                op, chainBounds, GrProcessorSet::MakeEmptySet(), fPipelineFlags, fStencilSettings);
+    }
 }
 
 #ifdef SK_DEBUG
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
index 66b9b4c..8c37b48 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
@@ -92,14 +92,6 @@
 
     bool compatibleWithAlphaAsCoverage() const { return fCompatibleWithAlphaAsCoveage; }
 
-    using PipelineAndFixedDynamicState = GrOpFlushState::PipelineAndFixedDynamicState;
-    /** Makes a pipeline that consumes the processor set and the op's applied clip. */
-    PipelineAndFixedDynamicState makePipeline(GrMeshDrawOp::Target* target,
-                                              int numPrimitiveProcessorTextures = 0) {
-        return this->internalMakePipeline(target, this->pipelineInitArgs(target),
-                                          numPrimitiveProcessorTextures);
-    }
-
     struct MakeArgs {
     private:
         MakeArgs() = default;
@@ -124,16 +116,11 @@
       fAAType = static_cast<unsigned>(aaType);
     }
 
+    void executeDrawsAndUploads(const GrOp*, GrOpFlushState*, const SkRect& chainBounds);
+
 protected:
     uint32_t pipelineFlags() const { return fPipelineFlags; }
 
-    GrPipeline::InitArgs pipelineInitArgs(GrMeshDrawOp::Target* target) const;
-
-    PipelineAndFixedDynamicState internalMakePipeline(GrMeshDrawOp::Target*,
-                                                      const GrPipeline::InitArgs&,
-                                                      int numPrimitiveProcessorTextures);
-
-private:
     GrProcessorSet* fProcessors;
     unsigned fPipelineFlags : 8;
     unsigned fAAType : 2;
@@ -152,7 +139,6 @@
 public:
     using MakeArgs = GrSimpleMeshDrawOpHelper::MakeArgs;
     using Flags = GrSimpleMeshDrawOpHelper::Flags;
-    using PipelineAndFixedDynamicState = GrOpFlushState::PipelineAndFixedDynamicState;
 
     using GrSimpleMeshDrawOpHelper::visitProxies;
 
@@ -180,8 +166,7 @@
                       const SkRect& thisBounds, const SkRect& thatBounds,
                       bool noneAACompatibleWithCoverage = false) const;
 
-    PipelineAndFixedDynamicState makePipeline(GrMeshDrawOp::Target*,
-                                              int numPrimitiveProcessorTextures = 0);
+    void executeDrawsAndUploads(const GrOp*, GrOpFlushState*, const SkRect& chainBounds);
 
 #ifdef SK_DEBUG
     SkString dumpInfo() const;
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index bb55520..25791aa 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -308,7 +308,6 @@
         sk_sp<const GrBuffer> fVertexBuffer;
         sk_sp<const GrBuffer> fIndexBuffer;
         sk_sp<GrGeometryProcessor>   fGeometryProcessor;
-        const GrPipeline* fPipeline;
         GrPipeline::FixedDynamicState* fFixedDynamicState;
         int fVertexOffset;
         int fInstancesToFlush;
@@ -320,17 +319,14 @@
         static constexpr int kMaxTextures = GrDistanceFieldPathGeoProc::kMaxTextures;
         GR_STATIC_ASSERT(GrBitmapTextGeoProc::kMaxTextures == kMaxTextures);
 
-        auto pipe = fHelper.makePipeline(target, kMaxTextures);
+        FlushInfo flushInfo;
+        flushInfo.fFixedDynamicState = target->makeFixedDynamicState(kMaxTextures);
         int numActiveProxies = fAtlas->numActivePages();
         const auto proxies = fAtlas->getProxies();
         for (int i = 0; i < numActiveProxies; ++i) {
-            pipe.fFixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[i].get();
+            flushInfo.fFixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[i].get();
         }
 
-        FlushInfo flushInfo;
-        flushInfo.fPipeline = pipe.fPipeline;
-        flushInfo.fFixedDynamicState = pipe.fFixedDynamicState;
-
         // Setup GrGeometryProcessor
         const SkMatrix& ctm = fShapes[0].fViewMatrix;
         if (fUsesDistanceField) {
@@ -798,13 +794,17 @@
             mesh->setIndexedPatterned(flushInfo->fIndexBuffer, kIndicesPerQuad, kVerticesPerQuad,
                                       flushInfo->fInstancesToFlush, maxInstancesPerDraw);
             mesh->setVertexData(flushInfo->fVertexBuffer, flushInfo->fVertexOffset);
-            target->draw(flushInfo->fGeometryProcessor, flushInfo->fPipeline,
-                         flushInfo->fFixedDynamicState, mesh);
+            target->recordDraw(
+                    flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fFixedDynamicState, nullptr);
             flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
             flushInfo->fInstancesToFlush = 0;
         }
     }
 
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
+    }
+
     const SkPMColor4f& color() const { return fShapes[0].fColor; }
     bool usesDistanceField() const { return fUsesDistanceField; }
 
diff --git a/src/gpu/ops/GrStrokeRectOp.cpp b/src/gpu/ops/GrStrokeRectOp.cpp
index e50f918..71c5aec 100644
--- a/src/gpu/ops/GrStrokeRectOp.cpp
+++ b/src/gpu/ops/GrStrokeRectOp.cpp
@@ -217,8 +217,11 @@
         GrMesh* mesh = target->allocMesh(primType);
         mesh->setNonIndexedNonInstanced(vertexCount);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        auto pipe = fHelper.makePipeline(target);
-        target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+        target->recordDraw(std::move(gp), mesh);
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     // TODO: override onCombineIfPossible
@@ -412,6 +415,7 @@
 
 private:
     void onPrepareDraws(Target*) override;
+    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
 
     static const int kMiterIndexCnt = 3 * 24;
     static const int kMiterVertexCnt = 16;
@@ -500,8 +504,11 @@
                                            info.fDegenerate,
                                            fHelper.compatibleWithAlphaAsCoverage());
     }
-    auto pipe = fHelper.makePipeline(target);
-    helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
+    helper.recordDraw(target, std::move(gp));
+}
+
+void AAStrokeRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
+    fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
 }
 
 sk_sp<const GrGpuBuffer> AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index dc3f59f..e619f19 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -363,8 +363,11 @@
                                                                : GrPrimitiveType::kTriangles);
         mesh->setNonIndexedNonInstanced(count);
         mesh->setVertexData(std::move(vb), firstVertex);
-        auto pipe = fHelper.makePipeline(target);
-        target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+        target->recordDraw(std::move(gp), mesh);
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
     Helper fHelper;
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 5cf35ec..eb9ccee 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -453,28 +453,17 @@
                 textureType, config, samplerState, extraSamplerKey,
                 std::move(fTextureColorSpaceXform));
 
-        GrPipeline::InitArgs args;
-        args.fCaps = &target->caps();
-        args.fResourceProvider = target->resourceProvider();
-        args.fFlags = 0;
-        if (aaType == GrAAType::kMSAA) {
-            args.fFlags |= GrPipeline::kHWAntialias_Flag;
-        }
-
-        auto clip = target->detachAppliedClip();
         // We'll use a dynamic state array for the GP textures when there are multiple ops.
         // Otherwise, we use fixed dynamic state to specify the single op's proxy.
         GrPipeline::DynamicStateArrays* dynamicStateArrays = nullptr;
         GrPipeline::FixedDynamicState* fixedDynamicState;
         if (numProxies > 1) {
             dynamicStateArrays = target->allocDynamicStateArrays(numProxies, 1, false);
-            fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 0);
+            fixedDynamicState = target->makeFixedDynamicState(0);
         } else {
-            fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 1);
+            fixedDynamicState = target->makeFixedDynamicState(1);
             fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
         }
-        const auto* pipeline =
-                target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
 
         size_t vertexSize = gp->vertexStride();
 
@@ -525,8 +514,16 @@
         }
         SkASSERT(!numQuadVerticesLeft);
         SkASSERT(!numAllocatedVertices);
-        target->draw(std::move(gp), pipeline, fixedDynamicState, dynamicStateArrays, meshes,
-                     numProxies);
+        target->recordDraw(
+                std::move(gp), meshes, numProxies, fixedDynamicState, dynamicStateArrays);
+    }
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        auto pipelineFlags = (GrAAType::kMSAA == this->aaType())
+                ? GrPipeline::kHWAntialias_Flag
+                : 0;
+        flushState->executeDrawsAndUploadsForMeshDrawOp(
+                this, chainBounds, GrProcessorSet::MakeEmptySet(), pipelineFlags);
     }
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {