Refactor GrPipeline dynamic state.

Remove scissor rect from GrPipeline.

Draws can specify "fixed dynamic state" which doesn't use the dynamism at
all or can specify dynamic state arrays with an entry per GrMesh.

When we state other than scissor rects this will allow the caller to
use a mix of truly dynamic and fixed dynamic state. So a caller that
only has dynamic scissor rects doesn't need to store its remaining
unvarying state in an array.

Change-Id: I8fcc07eb600c72a26cc712b185755c2116021a8a
Reviewed-on: https://skia-review.googlesource.com/137223
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index 566eebd..37cb23b 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -807,7 +807,7 @@
         GrAAConvexTessellator tess;
 
         int instanceCount = fPaths.count();
-        const GrPipeline* pipeline = fHelper.makePipeline(target);
+        auto pipe = fHelper.makePipeline(target);
         for (int i = 0; i < instanceCount; i++) {
             tess.rewind();
 
@@ -843,7 +843,7 @@
             mesh.setIndexed(indexBuffer, tess.numIndices(), firstIndex, 0, tess.numPts() - 1,
                             GrPrimitiveRestart::kNo);
             mesh.setVertexData(vertexBuffer, firstVertex);
-            target->draw(gp.get(), pipeline, mesh);
+            target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
         }
     }
 
@@ -854,7 +854,7 @@
             return;
         }
 #endif
-        const GrPipeline* pipeline = fHelper.makePipeline(target);
+        auto pipe = fHelper.makePipeline(target);
         int instanceCount = fPaths.count();
 
         SkMatrix invert;
@@ -931,7 +931,7 @@
                 mesh.setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0, draw.fVertexCnt - 1,
                                 GrPrimitiveRestart::kNo);
                 mesh.setVertexData(vertexBuffer, firstVertex);
-                target->draw(quadProcessor.get(), pipeline, mesh);
+                target->draw(quadProcessor.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
                 firstIndex += draw.fIndexCnt;
                 firstVertex += draw.fVertexCnt;
             }
diff --git a/src/gpu/ops/GrAAFillRectOp.cpp b/src/gpu/ops/GrAAFillRectOp.cpp
index 76bc812..cbd1ad5 100644
--- a/src/gpu/ops/GrAAFillRectOp.cpp
+++ b/src/gpu/ops/GrAAFillRectOp.cpp
@@ -291,7 +291,8 @@
                                            fHelper.compatibleWithAlphaAsCoverage(), localMatrix);
             info = this->next(info);
         }
-        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index 89dedc4..af5cb3d 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -939,7 +939,7 @@
         return;
     }
 
-    const GrPipeline* pipeline = fHelper.makePipeline(target);
+    auto pipe = fHelper.makePipeline(target);
     // do lines first
     if (lineCount) {
         sk_sp<GrGeometryProcessor> lineGP;
@@ -977,7 +977,7 @@
         mesh.setIndexedPatterned(linesIndexBuffer.get(), kIdxsPerLineSeg, kLineSegNumVertices,
                                  lineCount, kLineSegsNumInIdxBuffer);
         mesh.setVertexData(vertexBuffer, firstVertex);
-        target->draw(lineGP.get(), pipeline, mesh);
+        target->draw(lineGP.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
     if (quadCount || conicCount) {
@@ -1032,7 +1032,7 @@
             mesh.setIndexedPatterned(quadsIndexBuffer.get(), kIdxsPerQuad, kQuadNumVertices,
                                      quadCount, kQuadsNumInIdxBuffer);
             mesh.setVertexData(vertexBuffer, firstVertex);
-            target->draw(quadGP.get(), pipeline, mesh);
+            target->draw(quadGP.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
             firstVertex += quadCount * kQuadNumVertices;
         }
 
@@ -1041,7 +1041,7 @@
             mesh.setIndexedPatterned(quadsIndexBuffer.get(), kIdxsPerQuad, kQuadNumVertices,
                                      conicCount, kQuadsNumInIdxBuffer);
             mesh.setVertexData(vertexBuffer, firstVertex);
-            target->draw(conicGP.get(), pipeline, mesh);
+            target->draw(conicGP.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
         }
     }
 }
diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
index b6b7077..1876d6c 100644
--- a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
@@ -206,9 +206,9 @@
     }
 
 private:
-    void draw(GrMeshDrawOp::Target* target, const GrGeometryProcessor* gp,
-              const GrPipeline* pipeline, int vertexCount, size_t vertexStride, void* vertices,
-              int indexCount, uint16_t* indices) const {
+    void draw(Target* target, const GrGeometryProcessor* gp, const GrPipeline* pipeline,
+              const GrPipeline::FixedDynamicState* fixedDynamicState, int vertexCount,
+              size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
         if (vertexCount == 0 || indexCount == 0) {
             return;
         }
@@ -234,12 +234,11 @@
         mesh.setIndexed(indexBuffer, indexCount, firstIndex, 0, vertexCount - 1,
                         GrPrimitiveRestart::kNo);
         mesh.setVertexData(vertexBuffer, firstVertex);
-        target->draw(gp, pipeline, mesh);
+        target->draw(gp, pipeline, fixedDynamicState, mesh);
     }
 
     void onPrepareDraws(Target* target) override {
-        const GrPipeline* pipeline = fHelper.makePipeline(target);
-
+        auto pipe = fHelper.makePipeline(target);
         // Setup GrGeometryProcessor
         sk_sp<GrGeometryProcessor> gp(create_lines_only_gp(fHelper.compatibleWithAlphaAsCoverage(),
                                                            this->viewMatrix(),
@@ -275,8 +274,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.get(), pipeline, vertexCount, vertexStride, vertices,
-                           indexCount, indices);
+                this->draw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, vertexCount,
+                           vertexStride, vertices, indexCount, indices);
                 vertexCount = 0;
                 indexCount = 0;
             }
@@ -307,8 +306,8 @@
             indexCount += currentIndices;
         }
         if (vertexCount <= SK_MaxS32 && indexCount <= SK_MaxS32) {
-            this->draw(target, gp.get(), pipeline, vertexCount, vertexStride, vertices, indexCount,
-                       indices);
+            this->draw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, vertexCount,
+                       vertexStride, vertices, indexCount, indices);
         }
         sk_free(vertices);
         sk_free(indices);
diff --git a/src/gpu/ops/GrAAStrokeRectOp.cpp b/src/gpu/ops/GrAAStrokeRectOp.cpp
index 3347227..651f839 100644
--- a/src/gpu/ops/GrAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrAAStrokeRectOp.cpp
@@ -304,7 +304,8 @@
                                            info.fDegenerate,
                                            fHelper.compatibleWithAlphaAsCoverage());
     }
-    helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+    auto pipe = fHelper.makePipeline(target);
+    helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
 }
 
 sk_sp<const GrBuffer> AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index fc5e580..29a5938 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -297,9 +297,13 @@
     }
     SkASSERT(proxies[0]);
 
-    FlushInfo flushInfo;
-    flushInfo.fPipeline =
+    auto pipe =
             target->makePipeline(fSRGBFlags, std::move(fProcessors), target->detachAppliedClip());
+
+    FlushInfo flushInfo;
+    flushInfo.fPipeline = pipe.fPipeline;
+    flushInfo.fFixedDynamicState = pipe.fFixedDynamicState;
+
     bool vmPerspective = fGeoData[0].fViewMatrix.hasPerspective();
     if (this->usesDistanceFields()) {
         flushInfo.fGeometryProcessor = this->setupDfProcessor(proxies, numActiveProxies);
@@ -419,7 +423,8 @@
     mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerGlyph, kVerticesPerGlyph,
                              flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
     mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
-    target->draw(flushInfo->fGeometryProcessor.get(), flushInfo->fPipeline, mesh);
+    target->draw(flushInfo->fGeometryProcessor.get(), flushInfo->fPipeline,
+                 flushInfo->fFixedDynamicState, mesh);
     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
     flushInfo->fGlyphsToFlush = 0;
 }
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 0318a90..a1f8f89 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -109,6 +109,7 @@
         sk_sp<const GrBuffer> fIndexBuffer;
         sk_sp<GrGeometryProcessor> fGeometryProcessor;
         const GrPipeline* fPipeline;
+        const GrPipeline::FixedDynamicState* fFixedDynamicState;
         int fGlyphsToFlush;
         int fVertexOffset;
     };
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index 968d169..d38bb9f 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -697,9 +697,9 @@
         if (fDisableSRGBOutputConversion) {
             pipelineFlags |= GrPipeline::kDisableOutputConversionToSRGB_Flag;
         }
-        const GrPipeline* pipeline = target->makePipeline(pipelineFlags, std::move(fProcessorSet),
-                                                          target->detachAppliedClip());
-        helper.recordDraw(target, gp.get(), pipeline);
+        auto pipe = target->makePipeline(pipelineFlags, std::move(fProcessorSet),
+                                         target->detachAppliedClip());
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index aa0963b..af4546c 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -63,12 +63,14 @@
 class PathGeoBuilder {
 public:
     PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
-                   GrGeometryProcessor* geometryProcessor, const GrPipeline* pipeline)
+                   GrGeometryProcessor* geometryProcessor, const GrPipeline* pipeline,
+                   const GrPipeline::FixedDynamicState* fixedDynamicState)
             : fMesh(primitiveType)
             , fTarget(target)
             , fVertexStride(sizeof(SkPoint))
             , fGeometryProcessor(geometryProcessor)
             , fPipeline(pipeline)
+            , fFixedDynamicState(fixedDynamicState)
             , fIndexBuffer(nullptr)
             , fFirstIndex(0)
             , fIndicesInChunk(0)
@@ -274,7 +276,7 @@
                                  GrPrimitiveRestart::kNo);
             }
             fMesh.setVertexData(fVertexBuffer, fFirstVertex);
-            fTarget->draw(fGeometryProcessor, fPipeline, fMesh);
+            fTarget->draw(fGeometryProcessor, fPipeline, fFixedDynamicState, fMesh);
         }
 
         fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
@@ -312,6 +314,7 @@
     size_t fVertexStride;
     GrGeometryProcessor* fGeometryProcessor;
     const GrPipeline* fPipeline;
+    const GrPipeline::FixedDynamicState* fFixedDynamicState;
 
     const GrBuffer* fVertexBuffer;
     int fFirstVertex;
@@ -419,9 +422,9 @@
         } else {
             primitiveType = GrPrimitiveType::kTriangles;
         }
-
-        PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(),
-                                      fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(), pipe.fPipeline,
+                                      pipe.fFixedDynamicState);
 
         // fill buffers
         for (int i = 0; i < instanceCount; i++) {
diff --git a/src/gpu/ops/GrDrawAtlasOp.cpp b/src/gpu/ops/GrDrawAtlasOp.cpp
index 47bad8b..4c25edc 100644
--- a/src/gpu/ops/GrDrawAtlasOp.cpp
+++ b/src/gpu/ops/GrDrawAtlasOp.cpp
@@ -142,7 +142,8 @@
         memcpy(vertPtr, args.fVerts.begin(), allocSize);
         vertPtr += allocSize;
     }
-    helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+    auto pipe = fHelper.makePipeline(target);
+    helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
 }
 
 bool GrDrawAtlasOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
index 185b3c0..98a4775 100644
--- a/src/gpu/ops/GrDrawPathOp.cpp
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -75,13 +75,16 @@
 }
 
 void GrDrawPathOp::onExecute(GrOpFlushState* state) {
+    GrAppliedClip appliedClip = state->detachAppliedClip();
+    GrPipeline::FixedDynamicState fixedDynamicState(appliedClip.scissorState().rect());
     GrPipeline pipeline(this->pipelineInitArgs(*state), this->detachProcessors(),
-                        state->detachAppliedClip());
+                        std::move(appliedClip));
     sk_sp<GrPathProcessor> pathProc(GrPathProcessor::Create(this->color(), this->viewMatrix()));
 
     GrStencilSettings stencil;
     init_stencil_pass_settings(*state, this->fillType(), &stencil);
-    state->gpu()->pathRendering()->drawPath(*pathProc, pipeline, stencil, fPath.get());
+    state->gpu()->pathRendering()->drawPath(*pathProc, pipeline, fixedDynamicState, stencil,
+                                            fPath.get());
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index 8f6ad97..ad5aba2 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -245,7 +245,8 @@
                         GrPrimitiveRestart::kNo);
     }
     mesh.setVertexData(vertexBuffer, firstVertex);
-    target->draw(gp.get(), fHelper.makePipeline(target), mesh);
+    auto pipe = fHelper.makePipeline(target);
+    target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
 }
 
 bool GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 7bfa72e..92bcb0c 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -280,7 +280,8 @@
                                                   kVertsPerRect * patch.fIter->numRectsToDraw());
             }
         }
-        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index 0bc782c..349f832 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -40,9 +40,10 @@
     return vertices;
 }
 
-void GrMeshDrawOp::PatternHelper::recordDraw(Target* target, const GrGeometryProcessor* gp,
-                                             const GrPipeline* pipeline) {
-    target->draw(gp, pipeline, fMesh);
+void GrMeshDrawOp::PatternHelper::recordDraw(
+        Target* target, const GrGeometryProcessor* gp, const GrPipeline* pipeline,
+        const GrPipeline::FixedDynamicState* fixedDynamicState) {
+    target->draw(gp, pipeline, fixedDynamicState, fMesh);
 }
 
 void* GrMeshDrawOp::QuadHelper::init(Target* target, size_t vertexStride, int quadsToDraw) {
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index cb2c332..ddc29f1 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -8,6 +8,7 @@
 #ifndef GrMeshDrawOp_DEFINED
 #define GrMeshDrawOp_DEFINED
 
+#include "GrAppliedClip.h"
 #include "GrDrawOp.h"
 #include "GrGeometryProcessor.h"
 #include "GrMesh.h"
@@ -40,7 +41,8 @@
                    int indicesPerRepetition, int repeatCount);
 
         /** Call after init() to issue draws to the GrMeshDrawOp::Target.*/
-        void recordDraw(Target*, const GrGeometryProcessor*, const GrPipeline*);
+        void recordDraw(Target*, const GrGeometryProcessor*, const GrPipeline*,
+                        const GrPipeline::FixedDynamicState*);
 
     private:
         GrMesh fMesh;
@@ -76,7 +78,8 @@
     virtual ~Target() {}
 
     /** Adds a draw of a mesh. */
-    virtual void draw(const GrGeometryProcessor*, const GrPipeline*, const GrMesh&) = 0;
+    virtual void draw(const GrGeometryProcessor*, const GrPipeline*,
+                      const GrPipeline::FixedDynamicState*, const GrMesh&) = 0;
 
     /**
      * Makes space for vertex data. The returned pointer is the location where vertex data
@@ -129,19 +132,35 @@
         return this->pipelineArena()->make<GrPipeline>(std::forward<Args>(args)...);
     }
 
+    template <typename... Args>
+    GrPipeline::FixedDynamicState* allocFixedDynamicState(Args&... args) {
+        return this->pipelineArena()->make<GrPipeline::FixedDynamicState>(
+                std::forward<Args>(args)...);
+    }
+
+    // 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;
+        const GrPipeline::FixedDynamicState* fFixedDynamicState;
+    };
+
     /**
      * Helper that makes a pipeline targeting the op's render target that incorporates the op's
-     * GrAppliedClip.
+     * GrAppliedClip and uses a fixed dynamic state.
      */
-    GrPipeline* makePipeline(uint32_t pipelineFlags, GrProcessorSet&& processorSet,
-                             GrAppliedClip&& clip) {
+    PipelineAndFixedDynamicState makePipeline(uint32_t pipelineFlags, GrProcessorSet&& processorSet,
+                                              GrAppliedClip&& clip) {
         GrPipeline::InitArgs pipelineArgs;
         pipelineArgs.fFlags = pipelineFlags;
         pipelineArgs.fProxy = this->proxy();
         pipelineArgs.fDstProxy = this->dstProxy();
         pipelineArgs.fCaps = &this->caps();
         pipelineArgs.fResourceProvider = this->resourceProvider();
-        return this->allocPipeline(pipelineArgs, std::move(processorSet), std::move(clip));
+        const auto* state = this->allocFixedDynamicState(clip.scissorState().rect());
+        return {this->allocPipeline(pipelineArgs, std::move(processorSet), std::move(clip)), state};
     }
 
     virtual GrRenderTargetProxy* proxy() const = 0;
diff --git a/src/gpu/ops/GrNonAAFillRectOp.cpp b/src/gpu/ops/GrNonAAFillRectOp.cpp
index d4ebedc..8af9651 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectOp.cpp
@@ -203,7 +203,8 @@
             tesselate(verts, kVertexStride, fRects[i].fColor, &fRects[i].fViewMatrix,
                       fRects[i].fRect, &fRects[i].fLocalQuad);
         }
-        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -339,7 +340,8 @@
                 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr);
             }
         }
-        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.cpp b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
index 11c2f8c..4090e34 100644
--- a/src/gpu/ops/GrNonAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
@@ -191,7 +191,8 @@
         GrMesh mesh(primType);
         mesh.setNonIndexedNonInstanced(vertexCount);
         mesh.setVertexData(vertexBuffer, firstVertex);
-        target->draw(gp.get(), fHelper.makePipeline(target), mesh);
+        auto pipe = fHelper.makePipeline(target);
+        target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps&) override {
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index 1655b76..ca209e0 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -1445,7 +1445,8 @@
         mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
                         GrPrimitiveRestart::kNo);
         mesh.setVertexData(vertexBuffer, firstVertex);
-        target->draw(gp.get(),  fHelper.makePipeline(target), mesh);
+        auto pipe = fHelper.makePipeline(target);
+        target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -1761,7 +1762,8 @@
         mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
                         GrPrimitiveRestart::kNo);
         mesh.setVertexData(vertexBuffer, firstVertex);
-        target->draw(gp.get(), fHelper.makePipeline(target), mesh);
+        auto pipe = fHelper.makePipeline(target);
+        target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -2005,7 +2007,8 @@
 
             verts += kVerticesPerQuad;
         }
-        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -2239,7 +2242,8 @@
 
             verts += kVerticesPerQuad;
         }
-        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -2695,7 +2699,8 @@
         mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
                         GrPrimitiveRestart::kNo);
         mesh.setVertexData(vertexBuffer, firstVertex);
-        target->draw(gp.get(), fHelper.makePipeline(target), mesh);
+        auto pipe = fHelper.makePipeline(target);
+        target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -2953,7 +2958,8 @@
                 verts++;
             }
         }
-        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index 9a9814e..cd51152 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -142,7 +142,8 @@
             int numRectsInRegion = fRegions[i].fRegion.computeRegionComplexity();
             verts += numRectsInRegion * kVertsPerInstance * kVertexStride;
         }
-        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+        auto pipe = fHelper.makePipeline(target);
+        helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index afb084d..220556f 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -625,14 +625,14 @@
         }
 
         static const uint32_t kPipelineFlags = 0;
-        const GrPipeline* pipeline = target->makePipeline(
-                kPipelineFlags, GrProcessorSet::MakeEmptySet(), target->detachAppliedClip());
+        auto pipe = target->makePipeline(kPipelineFlags, GrProcessorSet::MakeEmptySet(),
+                                         target->detachAppliedClip());
 
         GrMesh mesh(GrPrimitiveType::kTriangles);
         mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
                         GrPrimitiveRestart::kNo);
         mesh.setVertexData(vertexBuffer, firstVertex);
-        target->draw(gp.get(), pipeline, mesh);
+        target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
index 8b83271..fc54475 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
@@ -134,17 +134,21 @@
     return args;
 }
 
-GrPipeline* GrSimpleMeshDrawOpHelper::internalMakePipeline(GrMeshDrawOp::Target* target,
-                                                           const GrPipeline::InitArgs& args) {
+auto GrSimpleMeshDrawOpHelper::internalMakePipeline(GrMeshDrawOp::Target* target,
+                                                    const GrPipeline::InitArgs& args)
+        -> 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();
+    auto* dynamicState = target->allocFixedDynamicState(clip.scissorState().rect());
     if (fProcessors) {
-        return target->allocPipeline(args, std::move(*fProcessors), target->detachAppliedClip());
+        return {target->allocPipeline(args, std::move(*fProcessors), std::move(clip)),
+                dynamicState};
     } else {
-        return target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
-                                     target->detachAppliedClip());
+        return {target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip)),
+                dynamicState};
     }
 }
 
@@ -169,8 +173,8 @@
            fStencilSettings == that.fStencilSettings;
 }
 
-const GrPipeline* GrSimpleMeshDrawOpHelperWithStencil::makePipeline(
-        GrMeshDrawOp::Target* target) {
+auto GrSimpleMeshDrawOpHelperWithStencil::makePipeline(GrMeshDrawOp::Target* target)
+        -> PipelineAndFixedDynamicState {
     auto args = INHERITED::pipelineInitArgs(target);
     args.fUserStencil = fStencilSettings;
     return this->internalMakePipeline(target, args);
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
index e5229a7..cbf41a7 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
@@ -88,8 +88,9 @@
 
     bool compatibleWithAlphaAsCoverage() const { return fCompatibleWithAlphaAsCoveage; }
 
+    using PipelineAndFixedDynamicState = GrOpFlushState::PipelineAndFixedDynamicState;
     /** Makes a pipeline that consumes the processor set and the op's applied clip. */
-    GrPipeline* makePipeline(GrMeshDrawOp::Target* target) {
+    PipelineAndFixedDynamicState makePipeline(GrMeshDrawOp::Target* target) {
         return this->internalMakePipeline(target, this->pipelineInitArgs(target));
     }
 
@@ -117,7 +118,8 @@
 
     GrPipeline::InitArgs pipelineInitArgs(GrMeshDrawOp::Target* target) const;
 
-    GrPipeline* internalMakePipeline(GrMeshDrawOp::Target*, const GrPipeline::InitArgs&);
+    PipelineAndFixedDynamicState internalMakePipeline(GrMeshDrawOp::Target*,
+                                                      const GrPipeline::InitArgs&);
 
 private:
     GrProcessorSet* fProcessors;
@@ -139,6 +141,8 @@
 public:
     using MakeArgs = GrSimpleMeshDrawOpHelper::MakeArgs;
     using Flags = GrSimpleMeshDrawOpHelper::Flags;
+    using PipelineAndFixedDynamicState = GrOpFlushState::PipelineAndFixedDynamicState;
+
     using GrSimpleMeshDrawOpHelper::visitProxies;
 
     // using declarations can't be templated, so this is a pass through function instead.
@@ -161,7 +165,7 @@
     bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil& that, const GrCaps&,
                       const SkRect& thisBounds, const SkRect& thatBounds) const;
 
-    const GrPipeline* makePipeline(GrMeshDrawOp::Target*);
+    PipelineAndFixedDynamicState makePipeline(GrMeshDrawOp::Target*);
 
     SkString dumpInfo() const;
 
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index a0c9f9c..0cd99d7 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -303,6 +303,7 @@
         sk_sp<const GrBuffer> fIndexBuffer;
         sk_sp<GrGeometryProcessor>   fGeometryProcessor;
         const GrPipeline* fPipeline;
+        const GrPipeline::FixedDynamicState* fFixedDynamicState;
         int fVertexOffset;
         int fInstancesToFlush;
     };
@@ -310,8 +311,12 @@
     void onPrepareDraws(Target* target) override {
         int instanceCount = fShapes.count();
 
+        auto pipe = fHelper.makePipeline(target);
+
         FlushInfo flushInfo;
-        flushInfo.fPipeline = fHelper.makePipeline(target);
+        flushInfo.fPipeline = pipe.fPipeline;
+        flushInfo.fFixedDynamicState = pipe.fFixedDynamicState;
+
         // Setup GrGeometryProcessor
         const SkMatrix& ctm = fShapes[0].fViewMatrix;
         if (fUsesDistanceField) {
@@ -821,7 +826,8 @@
                                      kVerticesPerQuad, flushInfo->fInstancesToFlush,
                                      maxInstancesPerDraw);
             mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
-            target->draw(flushInfo->fGeometryProcessor.get(), flushInfo->fPipeline, mesh);
+            target->draw(flushInfo->fGeometryProcessor.get(), flushInfo->fPipeline,
+                         flushInfo->fFixedDynamicState, mesh);
             flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
             flushInfo->fInstancesToFlush = 0;
         }
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 4c436c2..ed59834 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -358,7 +358,8 @@
         GrMesh mesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines : GrPrimitiveType::kTriangles);
         mesh.setNonIndexedNonInstanced(count);
         mesh.setVertexData(vb, firstVertex);
-        target->draw(gp, fHelper.makePipeline(target), mesh);
+        auto pipe = fHelper.makePipeline(target);
+        target->draw(gp, pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
     bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index a895230..bced31d 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -793,8 +793,10 @@
             args.fFlags |= GrPipeline::kHWAntialias_Flag;
         }
 
-        const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
-                                                           target->detachAppliedClip());
+        auto clip = target->detachAppliedClip();
+        const auto* fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect());
+        const auto* pipeline =
+                target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
         using TessFn =
                 decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
 #define TESS_FN_AND_VERTEX_SIZE(Point, MT, Domain, AA)                          \
@@ -866,7 +868,7 @@
             mesh.setNonIndexedNonInstanced(4);
         }
         mesh.setVertexData(vbuffer, vstart);
-        target->draw(gp.get(), pipeline, mesh);
+        target->draw(gp.get(), pipeline, fixedDynamicState, mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {