Revert "Reland "Add ability to specify different GP textures for each mesh in a draw.""

This reverts commit 2d0a6a1f989dbe02e5eaff5b8ae9ed9dc216a3e6.

Revert "Add support for dynamic state GP textures in Vulkan backend."

This reverts commit 5e81a123f22c309ebe790904345d54de4cba2ab5.

Revert "Disable GrTextureOp Chaining"

This reverts commit fdec6f469a44b19e3d77ed313c5b79fb72982603.

Bug: chromium:877598
Bug: chromium:877602
Bug: chromium:877610
Change-Id: Iabf2971b27bcf7728785903f1d7f47d1148a2e63
Reviewed-on: https://skia-review.googlesource.com/150461
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index d700600..c700335 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -61,7 +61,6 @@
     fSampleShadingSupport = false;
     fFenceSyncSupport = false;
     fCrossContextTextureSupport = false;
-    fDynamicStateArrayGeometryProcessorTextureSupport = false;
 
     fBlendEquationSupport = kBasic_BlendEquationSupport;
     fAdvBlendEqBlacklist = 0;
@@ -173,8 +172,6 @@
     writer->appendBool("Sample shading support", fSampleShadingSupport);
     writer->appendBool("Fence sync support", fFenceSyncSupport);
     writer->appendBool("Cross context texture support", fCrossContextTextureSupport);
-    writer->appendBool("Specify GeometryProcessor textures as a dynamic state array",
-                       fDynamicStateArrayGeometryProcessorTextureSupport);
 
     writer->appendBool("Blacklist Coverage Counting Path Renderer [workaround]",
                        fBlacklistCoverageCounting);
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 3dadb1e..9e18c38 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -234,16 +234,13 @@
 
     bool fenceSyncSupport() const { return fFenceSyncSupport; }
     bool crossContextTextureSupport() const { return fCrossContextTextureSupport; }
+
     /**
      * Returns whether or not we will be able to do a copy given the passed in params
      */
     virtual bool canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                                 const SkIRect& srcRect, const SkIPoint& dstPoint) const = 0;
 
-    bool dynamicStateArrayGeometryProcessorTextureSupport() const {
-        return fDynamicStateArrayGeometryProcessorTextureSupport;
-    }
-
     /**
      * This is can be called before allocating a texture to be a dst for copySurface. This is only
      * used for doing dst copies needed in blends, thus the src is always a GrRenderTargetProxy. It
@@ -330,12 +327,9 @@
     // TODO: this may need to be an enum to support different fence types
     bool fFenceSyncSupport                           : 1;
 
-    // Requires fence sync support in GL.
+    // Vulkan doesn't support this (yet) and some drivers have issues, too
     bool fCrossContextTextureSupport                 : 1;
 
-    // Not (yet) implemented in VK backend.
-    bool fDynamicStateArrayGeometryProcessorTextureSupport : 1;
-
     BlendEquationSupport fBlendEquationSupport;
     uint32_t fAdvBlendEqBlacklist;
     GR_STATIC_ASSERT(kLast_GrBlendEquation < 32);
diff --git a/src/gpu/GrGpuCommandBuffer.cpp b/src/gpu/GrGpuCommandBuffer.cpp
index 421d057..d66f24c 100644
--- a/src/gpu/GrGpuCommandBuffer.cpp
+++ b/src/gpu/GrGpuCommandBuffer.cpp
@@ -47,20 +47,9 @@
     if (pipeline.isBad()) {
         return false;
     }
-    if (fixedDynamicState && fixedDynamicState->fPrimitiveProcessorTextures) {
-        for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
-            if (!fixedDynamicState->fPrimitiveProcessorTextures[i]->instantiate(resourceProvider)) {
-                return false;
-            }
-        }
-    }
-    if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
-        int n = primProc.numTextureSamplers() * meshCount;
-        const auto* textures = dynamicStateArrays->fPrimitiveProcessorTextures;
-        for (int i = 0; i < n; ++i) {
-            if (!textures[i]->instantiate(resourceProvider)) {
-                return false;
-            }
+    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+        if (!fixedDynamicState->fPrimitiveProcessorTextures[i]->instantiate(resourceProvider)) {
+            return false;
         }
     }
 
diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp
index a1d9229..6385c9f 100644
--- a/src/gpu/GrOpFlushState.cpp
+++ b/src/gpu/GrOpFlushState.cpp
@@ -102,28 +102,19 @@
 
 void GrOpFlushState::draw(sk_sp<const GrGeometryProcessor> gp, const GrPipeline* pipeline,
                           const GrPipeline::FixedDynamicState* fixedDynamicState,
-                          const GrPipeline::DynamicStateArrays* dynamicStateArrays,
                           const GrMesh meshes[], int meshCnt) {
     SkASSERT(fOpArgs);
     SkASSERT(fOpArgs->fOp);
     bool firstDraw = fDraws.begin() == fDraws.end();
     auto& draw = fDraws.append(&fArena);
     GrDeferredUploadToken token = fTokenTracker->issueDrawToken();
-    if (fixedDynamicState && fixedDynamicState->fPrimitiveProcessorTextures) {
-        for (int i = 0; i < gp->numTextureSamplers(); ++i) {
-            fixedDynamicState->fPrimitiveProcessorTextures[i]->addPendingRead();
-        }
-    }
-    if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
-        int n = gp->numTextureSamplers() * meshCnt;
-        for (int i = 0; i < n; ++i) {
-            dynamicStateArrays->fPrimitiveProcessorTextures[i]->addPendingRead();
-        }
+    for (int i = 0; i < gp->numTextureSamplers(); ++i) {
+        fixedDynamicState->fPrimitiveProcessorTextures[i]->addPendingRead();
     }
     draw.fGeometryProcessor = std::move(gp);
     draw.fPipeline = pipeline;
     draw.fFixedDynamicState = fixedDynamicState;
-    draw.fDynamicStateArrays = dynamicStateArrays;
+    draw.fDynamicStateArrays = nullptr;
     draw.fMeshes = meshes;
     draw.fMeshCnt = meshCnt;
     draw.fOpID = fOpArgs->fOp->uniqueID();
@@ -174,20 +165,3 @@
 GrAtlasManager* GrOpFlushState::atlasManager() const {
     return fGpu->getContext()->contextPriv().getAtlasManager();
 }
-
-//////////////////////////////////////////////////////////////////////////////
-
-GrOpFlushState::Draw::~Draw() {
-    if (fFixedDynamicState && fFixedDynamicState->fPrimitiveProcessorTextures) {
-        for (int i = 0; i < fGeometryProcessor->numTextureSamplers(); ++i) {
-            fFixedDynamicState->fPrimitiveProcessorTextures[i]->completedRead();
-        }
-    }
-    if (fDynamicStateArrays && fDynamicStateArrays->fPrimitiveProcessorTextures) {
-        int n = fGeometryProcessor->numTextureSamplers() * fMeshCnt;
-        const auto* textures = fDynamicStateArrays->fPrimitiveProcessorTextures;
-        for (int i = 0; i < n; ++i) {
-            textures[i]->completedRead();
-        }
-    }
-}
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 4746aa2..0a46ed2 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -76,9 +76,8 @@
     void draw(sk_sp<const GrGeometryProcessor>,
               const GrPipeline*,
               const GrPipeline::FixedDynamicState*,
-              const GrPipeline::DynamicStateArrays*,
               const GrMesh[],
-              int meshCnt) final;
+              int meshCount) final;
     void* makeVertexSpace(size_t vertexSize, int vertexCount, const GrBuffer**,
                           int* startVertex) final;
     uint16_t* makeIndexSpace(int indexCount, const GrBuffer**, int* startIndex) final;
@@ -121,7 +120,11 @@
     // that share a geometry processor into a Draw is that it allows the Gpu object to setup
     // the shared state once and then issue draws for each mesh.
     struct Draw {
-        ~Draw();
+        ~Draw() {
+            for (int i = 0; i < fGeometryProcessor->numTextureSamplers(); ++i) {
+                fFixedDynamicState->fPrimitiveProcessorTextures[i]->completedRead();
+            }
+        }
         sk_sp<const GrGeometryProcessor> fGeometryProcessor;
         const GrPipeline* fPipeline = nullptr;
         const GrPipeline::FixedDynamicState* fFixedDynamicState;
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index 5e75c65..5817ce8 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -73,8 +73,7 @@
         explicit FixedDynamicState(const SkIRect& scissorRect) : fScissorRect(scissorRect) {}
         FixedDynamicState() = default;
         SkIRect fScissorRect = SkIRect::EmptyIRect();
-        // Must have GrPrimitiveProcessor::numTextureSamplers() entries. Can be null if no samplers
-        // or textures are passed using DynamicStateArrays.
+        // Must have GrPrimitiveProcessor::numTextureSamplers() entries. Can be null if no samplers.
         GrTextureProxy** fPrimitiveProcessorTextures = nullptr;
     };
 
@@ -84,10 +83,6 @@
      */
     struct DynamicStateArrays {
         const SkIRect* fScissorRects = nullptr;
-        // Must have GrPrimitiveProcessor::numTextureSamplers() * num_meshes entries.
-        // Can be null if no samplers or to use the same textures for all meshes via'
-        // FixedDynamicState.
-        GrTextureProxy** fPrimitiveProcessorTextures = nullptr;
     };
 
     /**
diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h
index e74a25c..c15c054 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -179,7 +179,7 @@
     int                            fLastClipNumAnalyticFPs;
 
     // For ops/opList we have mean: 5 stdDev: 28
-    SkSTArray<25, RecordedOp, true> fRecordedOps;
+    SkSTArray<5, RecordedOp, true> fRecordedOps;
 
     // MDB TODO: 4096 for the first allocation of the clip space will be huge overkill.
     // Gather statistics to determine the correct size.
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index d53c861..5e5cd23 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -571,8 +571,6 @@
     // Safely moving textures between contexts requires fences.
     fCrossContextTextureSupport = fFenceSyncSupport;
 
-    fDynamicStateArrayGeometryProcessorTextureSupport = true;
-
     if (kGL_GrGLStandard == standard) {
         if (version >= GR_GL_VER(4, 1)) {
             fProgramBinarySupport = true;
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 0e32a20..2bb3983 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1672,11 +1672,9 @@
     }
 }
 
-void GrGLGpu::resolveAndGenerateMipMapsForProcessorTextures(
-        const GrPrimitiveProcessor& primProc,
-        const GrPipeline& pipeline,
-        const GrTextureProxy* const primProcTextures[],
-        int numPrimitiveProcessorTextureSets) {
+void GrGLGpu::generateMipmapsForProcessorTextures(const GrPrimitiveProcessor& primProc,
+                                                  const GrPipeline& pipeline,
+                                                  const GrTextureProxy* const primProcTextures[]) {
     auto genLevelsIfNeeded = [this](GrTexture* tex, const GrSamplerState& sampler) {
         SkASSERT(tex);
         if (sampler.filter() == GrSamplerState::Filter::kMipMap &&
@@ -1684,19 +1682,12 @@
             tex->texturePriv().mipMapsAreDirty()) {
             SkASSERT(this->caps()->mipMapSupport());
             this->regenerateMipMapLevels(static_cast<GrGLTexture*>(tex));
-            SkASSERT(!tex->asRenderTarget() || !tex->asRenderTarget()->needsResolve());
-        } else if (auto* rt = tex->asRenderTarget()) {
-            if (rt->needsResolve()) {
-                this->resolveRenderTarget(rt);
-            }
         }
     };
 
-    for (int set = 0, tex = 0; set < numPrimitiveProcessorTextureSets; ++set) {
-        for (int sampler = 0; sampler < primProc.numTextureSamplers(); ++sampler, ++tex) {
-            GrTexture* texture = primProcTextures[tex]->peekTexture();
-            genLevelsIfNeeded(texture, primProc.textureSampler(sampler).samplerState());
-        }
+    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+        GrTexture* tex = primProcTextures[i]->peekTexture();
+        genLevelsIfNeeded(tex, primProc.textureSampler(i).samplerState());
     }
 
     GrFragmentProcessor::Iter iter(pipeline);
@@ -1711,26 +1702,17 @@
 bool GrGLGpu::flushGLState(const GrPrimitiveProcessor& primProc,
                            const GrPipeline& pipeline,
                            const GrPipeline::FixedDynamicState* fixedDynamicState,
-                           const GrPipeline::DynamicStateArrays* dynamicStateArrays,
-                           int dynamicStateArraysLength,
                            bool willDrawPoints) {
     sk_sp<GrGLProgram> program(fProgramCache->refProgram(this, primProc, pipeline, willDrawPoints));
     if (!program) {
         GrCapsDebugf(this->caps(), "Failed to create program!\n");
         return false;
     }
-    const GrTextureProxy* const* primProcProxiesForMipRegen = nullptr;
-    const GrTextureProxy* const* primProcProxiesToBind = nullptr;
-    int numPrimProcTextureSets = 1;  // number of texture per prim proc sampler.
-    if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
-        primProcProxiesForMipRegen = dynamicStateArrays->fPrimitiveProcessorTextures;
-        numPrimProcTextureSets = dynamicStateArraysLength;
-    } else if (fixedDynamicState && fixedDynamicState->fPrimitiveProcessorTextures) {
-        primProcProxiesForMipRegen = fixedDynamicState->fPrimitiveProcessorTextures;
-        primProcProxiesToBind = fixedDynamicState->fPrimitiveProcessorTextures;
+    const GrTextureProxy* const* primProcProxies = nullptr;
+    if (fixedDynamicState) {
+        primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
     }
-    this->resolveAndGenerateMipMapsForProcessorTextures(
-            primProc, pipeline, primProcProxiesForMipRegen, numPrimProcTextureSets);
+    this->generateMipmapsForProcessorTextures(primProc, pipeline, primProcProxies);
 
     GrXferProcessor::BlendInfo blendInfo;
     pipeline.getXferProcessor().getBlendInfo(&blendInfo);
@@ -1747,7 +1729,7 @@
         this->flushBlend(blendInfo, swizzle);
     }
 
-    fHWProgram->updateUniformsAndTextureBindings(primProc, pipeline, primProcProxiesToBind);
+    fHWProgram->updateUniformsAndTextureBindings(primProc, pipeline, primProcProxies);
 
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(pipeline.renderTarget());
     GrStencilSettings stencil;
@@ -2270,40 +2252,30 @@
             break;
         }
     }
-    if (!this->flushGLState(primProc, pipeline, fixedDynamicState, dynamicStateArrays, meshCount,
-                            hasPoints)) {
+    if (!this->flushGLState(primProc, pipeline, fixedDynamicState, hasPoints)) {
         return;
     }
 
-    bool dynamicScissor = false;
-    bool dynamicPrimProcTextures = false;
-    if (dynamicStateArrays) {
-        dynamicScissor = pipeline.isScissorEnabled() && dynamicStateArrays->fScissorRects;
-        dynamicPrimProcTextures = dynamicStateArrays->fPrimitiveProcessorTextures;
-    }
-    for (int m = 0; m < meshCount; ++m) {
+    bool dynamicScissor =
+            pipeline.isScissorEnabled() && dynamicStateArrays && dynamicStateArrays->fScissorRects;
+    for (int i = 0; i < meshCount; ++i) {
         if (GrXferBarrierType barrierType = pipeline.xferBarrierType(*this->caps())) {
             this->xferBarrier(pipeline.renderTarget(), barrierType);
         }
 
         if (dynamicScissor) {
             GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(pipeline.renderTarget());
-            this->flushScissor(GrScissorState(dynamicStateArrays->fScissorRects[m]),
+            this->flushScissor(GrScissorState(dynamicStateArrays->fScissorRects[i]),
                                glRT->getViewport(), pipeline.proxy()->origin());
         }
-        if (dynamicPrimProcTextures) {
-            auto texProxyArray = dynamicStateArrays->fPrimitiveProcessorTextures +
-                                 m * primProc.numTextureSamplers();
-            fHWProgram->updatePrimitiveProcessorTextureBindings(primProc, texProxyArray);
-        }
         if (this->glCaps().requiresCullFaceEnableDisableWhenDrawingLinesAfterNonLines() &&
-            GrIsPrimTypeLines(meshes[m].primitiveType()) &&
+            GrIsPrimTypeLines(meshes[i].primitiveType()) &&
             !GrIsPrimTypeLines(fLastPrimitiveType)) {
             GL_CALL(Enable(GR_GL_CULL_FACE));
             GL_CALL(Disable(GR_GL_CULL_FACE));
         }
-        meshes[m].sendToGpu(this);
-        fLastPrimitiveType = meshes[m].primitiveType();
+        meshes[i].sendToGpu(this);
+        fLastPrimitiveType = meshes[i].primitiveType();
     }
 
 #if SWAP_PER_DRAW
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 295ca42..6b01d65 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -251,22 +251,14 @@
 
     void setTextureSwizzle(int unitIdx, GrGLenum target, const GrGLenum swizzle[]);
 
-    /**
-     * primitiveProcessorTextures must contain GrPrimitiveProcessor::numTextureSamplers() *
-     * numPrimitiveProcessorTextureSets entries.
-     */
-    void resolveAndGenerateMipMapsForProcessorTextures(
+    void generateMipmapsForProcessorTextures(
             const GrPrimitiveProcessor&, const GrPipeline&,
-            const GrTextureProxy* const primitiveProcessorTextures[],
-            int numPrimitiveProcessorTextureSets);
+            const GrTextureProxy* const primitiveProcessorTextures[]);
 
     // Flushes state from GrPipeline to GL. Returns false if the state couldn't be set.
     // willDrawPoints must be true if point primitives will be rendered after setting the GL state.
-    // If DynamicStateArrays is not null then dynamicStateArraysLength is the number of dynamic
-    // state entries in each array.
     bool flushGLState(const GrPrimitiveProcessor&, const GrPipeline&,
-                      const GrPipeline::FixedDynamicState*, const GrPipeline::DynamicStateArrays*,
-                      int dynamicStateArraysLength, bool willDrawPoints);
+                      const GrPipeline::FixedDynamicState*, bool willDrawPoints);
 
     void flushProgram(sk_sp<GrGLProgram>);
 
diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp
index 1b3cfbd..8bd4c9c 100644
--- a/src/gpu/gl/GrGLPathRendering.cpp
+++ b/src/gpu/gl/GrGLPathRendering.cpp
@@ -116,7 +116,7 @@
                                    const GrPipeline::FixedDynamicState& fixedDynamicState,
                                    const GrStencilSettings& stencilPassSettings,
                                    const GrPath* path) {
-    if (!this->gpu()->flushGLState(primProc, pipeline, &fixedDynamicState, nullptr, 1, false)) {
+    if (!this->gpu()->flushGLState(primProc, pipeline, &fixedDynamicState, false)) {
         return;
     }
     const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 8a5308f..06aaade 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -75,6 +75,7 @@
 void GrGLProgram::updateUniformsAndTextureBindings(const GrPrimitiveProcessor& primProc,
                                                    const GrPipeline& pipeline,
                                                    const GrTextureProxy* const primProcTextures[]) {
+    SkASSERT(primProcTextures || !primProc.numTextureSamplers());
     this->setRenderTargetState(primProc, pipeline.proxy());
 
     // we set the textures, and uniforms for installed processors in a generic way, but subclasses
@@ -83,12 +84,13 @@
     // We must bind to texture units in the same order in which we set the uniforms in
     // GrGLProgramDataManager. That is, we bind textures for processors in this order:
     // primProc, fragProcs, XP.
+    int nextTexSamplerIdx = 0;
     fPrimitiveProcessor->setData(fProgramDataManager, primProc,
                                  GrFragmentProcessor::CoordTransformIter(pipeline));
-    if (primProcTextures) {
-        this->updatePrimitiveProcessorTextureBindings(primProc, primProcTextures);
+    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+        auto* tex = static_cast<GrGLTexture*>(primProcTextures[i]->peekTexture());
+        fGpu->bindTexture(nextTexSamplerIdx++, primProc.textureSampler(i).samplerState(), tex);
     }
-    int nextTexSamplerIdx = primProc.numTextureSamplers();
 
     this->setFragmentData(pipeline, &nextTexSamplerIdx);
 
@@ -104,14 +106,6 @@
     SkASSERT(nextTexSamplerIdx == fNumTextureSamplers);
 }
 
-void GrGLProgram::updatePrimitiveProcessorTextureBindings(const GrPrimitiveProcessor& primProc,
-                                                          const GrTextureProxy* const proxies[]) {
-    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
-        auto* tex = static_cast<GrGLTexture*>(proxies[i]->peekTexture());
-        fGpu->bindTexture(i, primProc.textureSampler(i).samplerState(), tex);
-    }
-}
-
 void GrGLProgram::setFragmentData(const GrPipeline& pipeline, int* nextTexSamplerIdx) {
     GrFragmentProcessor::Iter iter(pipeline);
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 1a2ef20..ca9253c 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -112,17 +112,13 @@
 
     /**
      * This function uploads uniforms, calls each GrGLSL*Processor's setData. It binds all fragment
-     * processor textures. Primitive process textures can be bound using this function or by
-     * calling updatePrimitiveProcessorTextureBindings.
+     * processor textures. Primitive process textures are also bound here but are passed separately.
      *
      * It is the caller's responsibility to ensure the program is bound before calling.
      */
     void updateUniformsAndTextureBindings(const GrPrimitiveProcessor&, const GrPipeline&,
                                           const GrTextureProxy* const primitiveProcessorTextures[]);
 
-    void updatePrimitiveProcessorTextureBindings(const GrPrimitiveProcessor&,
-                                                 const GrTextureProxy* const[]);
-
     int vertexStride() const { return fVertexStride; }
     int instanceStride() const { return fInstanceStride; }
 
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index 837f405..e9a612f 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -938,7 +938,7 @@
                 firstIndex += draw.fIndexCnt;
                 firstVertex += draw.fVertexCnt;
             }
-            target->draw(quadProcessor, pipe.fPipeline, pipe.fFixedDynamicState, nullptr, meshes,
+            target->draw(quadProcessor, pipe.fPipeline, pipe.fFixedDynamicState, meshes,
                          draws.count());
         }
     }
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index 54d2165..620ea47 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -73,29 +73,6 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-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>();
-    if (allocScissors) {
-        result->fScissorRects = this->pipelineArena()->makeArray<SkIRect>(numMeshes);
-    }
-    if (numPrimitiveProcessorTextures) {
-        result->fPrimitiveProcessorTextures = this->allocPrimitiveProcessorTextureArray(
-                numPrimitiveProcessorTextures * numMeshes);
-    }
-    return result;
-}
-
 GrMeshDrawOp::Target::PipelineAndFixedDynamicState GrMeshDrawOp::Target::makePipeline(
         uint32_t pipelineFlags, GrProcessorSet&& processorSet, GrAppliedClip&& clip,
         int numPrimProcTextures) {
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 808a1d4..a6e7da5 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -83,15 +83,15 @@
     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);
+        this->draw(std::move(gp), pipeline, fixedDynamicState, mesh, 1);
     }
 
     /**
@@ -152,11 +152,14 @@
     GrMesh* allocMeshes(int n) { return this->pipelineArena()->makeArray<GrMesh>(n); }
 
     GrPipeline::FixedDynamicState* allocFixedDynamicState(const SkIRect& rect,
-                                                          int numPrimitiveProcessorTextures = 0);
-
-    GrPipeline::DynamicStateArrays* allocDynamicStateArrays(int numMeshes,
-                                                            int numPrimitiveProcessorTextures,
-                                                            bool allocScissors);
+                                                          int numPrimitiveProcessorTextures = 0) {
+        auto result = this->pipelineArena()->make<GrPipeline::FixedDynamicState>(rect);
+        if (numPrimitiveProcessorTextures) {
+            result->fPrimitiveProcessorTextures =
+                    this->allocPrimitiveProcessorTextureArray(numPrimitiveProcessorTextures);
+        }
+        return result;
+    }
 
     GrTextureProxy** allocPrimitiveProcessorTextureArray(int n) {
         SkASSERT(n > 0);
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index af53498..2ba3639 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -611,7 +611,7 @@
     }
 
     template <typename Pos, Domain D, GrAA AA>
-    void tess(void* v, const GrGeometryProcessor* gp) const {
+    void tess(void* v, const GrGeometryProcessor* gp) {
         using Vertex = TextureGeometryProcessor::Vertex<Pos, D, AA>;
         SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex));
         auto vertices = static_cast<Vertex*>(v);
@@ -628,25 +628,16 @@
     }
 
     void onPrepareDraws(Target* target) override {
-        bool hasPerspective = false;
-        Domain domain = Domain::kNo;
-        int numOps = 0;
-        for (const auto& op : ChainRange<TextureOp>(this)) {
-            ++numOps;
-            hasPerspective |= op.fPerspective;
-            if (op.fDomain) {
-                domain = Domain::kYes;
-            }
-            if (!op.fProxy->instantiate(target->resourceProvider())) {
-                return;
-            }
+        if (!fProxy->instantiate(target->resourceProvider())) {
+            return;
         }
 
+        Domain domain = fDomain ? Domain::kYes : Domain::kNo;
         bool coverageAA = GrAAType::kCoverage == this->aaType();
         sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
                 fProxy->textureType(), fProxy->config(), fFilter,
                 std::move(fTextureColorSpaceXform), std::move(fPaintColorSpaceXform), coverageAA,
-                hasPerspective, domain, *target->caps().shaderCaps());
+                fPerspective, domain, *target->caps().shaderCaps());
         GrPipeline::InitArgs args;
         args.fProxy = target->proxy();
         args.fCaps = &target->caps();
@@ -657,17 +648,8 @@
         }
 
         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 (numOps > 1) {
-            dynamicStateArrays = target->allocDynamicStateArrays(numOps, 1, false);
-            fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 0);
-        } else {
-            fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 1);
-            fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy;
-        }
+        auto* fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 1);
+        fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy;
         const auto* pipeline =
                 target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
         using TessFn = decltype(&TextureOp::tess<SkPoint, Domain::kNo, GrAA::kNo>);
@@ -691,45 +673,42 @@
         };
 #undef TESS_FN_AND_VERTEX_SIZE
         int tessFnIdx = 0;
-        tessFnIdx |= coverageAA               ? 0x1 : 0x0;
-        tessFnIdx |= (domain == Domain::kYes) ? 0x2 : 0x0;
-        tessFnIdx |= hasPerspective           ? 0x4 : 0x0;
+        tessFnIdx |= coverageAA   ? 0x1 : 0x0;
+        tessFnIdx |= fDomain      ? 0x2 : 0x0;
+        tessFnIdx |= fPerspective ? 0x4 : 0x0;
 
         SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride());
 
-        GrMesh* meshes = target->allocMeshes(numOps);
-        int i = 0;
-        for (const auto& op : ChainRange<TextureOp>(this)) {
-            int vstart;
-            const GrBuffer* vbuffer;
-            void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
-                                                  4 * op.fDraws.count(), &vbuffer, &vstart);
-            if (!vdata) {
-                SkDebugf("Could not allocate vertices\n");
-                return;
-            }
+        int vstart;
+        const GrBuffer* vbuffer;
+        void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
+                                              4 * fDraws.count(), &vbuffer, &vstart);
+        if (!vdata) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
+        }
 
-            (op.*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get());
+        (this->*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get());
 
-            meshes[i].setPrimitiveType(GrPrimitiveType::kTriangles);
+        GrPrimitiveType primitiveType =
+                fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
+        GrMesh* mesh = target->allocMesh(primitiveType);
+        if (fDraws.count() > 1) {
             sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
             if (!ibuffer) {
                 SkDebugf("Could not allocate quad indices\n");
                 return;
             }
-            meshes[i].setIndexedPatterned(ibuffer.get(), 6, 4, op.fDraws.count(),
-                                          GrResourceProvider::QuadCountOfQuadBuffer());
-            meshes[i].setVertexData(vbuffer, vstart);
-            if (dynamicStateArrays) {
-                dynamicStateArrays->fPrimitiveProcessorTextures[i] = op.fProxy;
-            }
-            ++i;
+            mesh->setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
+                                      GrResourceProvider::QuadCountOfQuadBuffer());
+        } else {
+            mesh->setNonIndexedNonInstanced(4);
         }
-        target->draw(std::move(gp), pipeline, fixedDynamicState, dynamicStateArrays, meshes,
-                     numOps);
+        mesh->setVertexData(vbuffer, vstart);
+        target->draw(std::move(gp), pipeline, fixedDynamicState, mesh);
     }
 
-    CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
+    CombineResult onCombineIfPossible(GrOp* t, const GrCaps&) override {
         const auto* that = t->cast<TextureOp>();
         if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
                                        that->fTextureColorSpaceXform.get())) {
@@ -742,20 +721,7 @@
         if (this->aaType() != that->aaType()) {
             return CombineResult::kCannotCombine;
         }
-        if (fFilter != that->fFilter) {
-            return CombineResult::kCannotCombine;
-        }
-        if (fProxy->uniqueID() != that->fProxy->uniqueID() || that->isChained()) {
-#if 0  // Disabled for crbug.com/877598 crbug.com/877610 and bad image-surface GM on S7 MaliT880
-       // bot.
-            // We can't merge across different proxies (and we're disallowed from merging when
-            // 'that' is chained. Check if we can be chained with 'that'.
-            if (fProxy->config() == that->fProxy->config() &&
-                fProxy->textureType() == that->fProxy->textureType() &&
-                caps.dynamicStateArrayGeometryProcessorTextureSupport()) {
-                return CombineResult::kMayChain;
-            }
-#endif
+        if (fProxy->uniqueID() != that->fProxy->uniqueID() || fFilter != that->fFilter) {
             return CombineResult::kCannotCombine;
         }
         fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 7c7cf75..75e9639 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -21,8 +21,8 @@
     : INHERITED(contextOptions) {
 
     /**************************************************************************
-     * GrCaps fields
-     **************************************************************************/
+    * GrDrawTargetCaps fields
+    **************************************************************************/
     fMipMapSupport = true;   // always available in Vulkan
     fSRGBSupport = true;   // always available in Vulkan
     fNPOTTextureTileSupport = true;  // always available in Vulkan
@@ -42,8 +42,6 @@
     fMaxRenderTargetSize = 4096; // minimum required by spec
     fMaxTextureSize = 4096; // minimum required by spec
 
-    fDynamicStateArrayGeometryProcessorTextureSupport = true;
-
     fShaderCaps.reset(new GrShaderCaps(contextOptions));
 
     this->init(contextOptions, vkInterface, physDev, features, extensions);
@@ -325,11 +323,6 @@
         fMaxVertexAttributes = SkTMin(fMaxVertexAttributes, 32);
     }
 
-    // The image-surface GM does not draw correctly on all tested NVIDIA systems.
-    if (kNvidia_VkVendor == properties.vendorID) {
-        fDynamicStateArrayGeometryProcessorTextureSupport = false;
-    }
-
     ////////////////////////////////////////////////////////////////////////////
     // GrShaderCaps workarounds
     ////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index 7b4a92d..caff3ca 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -243,6 +243,7 @@
                                                          dynamicOffsetCount,
                                                          dynamicOffsets));
     this->addRecordingResource(layout);
+    pipelineState->addUniformResources(*this);
 }
 
 void GrVkCommandBuffer::bindDescriptorSets(const GrVkGpu* gpu,
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 3ecb340..44cf980 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -605,20 +605,13 @@
     }
     fLastPipelineState = pipelineState;
 
-    pipelineState->bindPipeline(fGpu, cbInfo.currentCmdBuf());
-
-    pipelineState->setAndBindUniforms(fGpu, primProc, pipeline, cbInfo.currentCmdBuf());
-
-    // Check whether we need to bind textures between each GrMesh. If not we can bind them all now.
-    bool setTextures = !(dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures);
-    if (setTextures) {
-        const GrTextureProxy* const* primProcProxies = nullptr;
-        if (fixedDynamicState) {
-            primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
-        }
-        pipelineState->setAndBindTextures(fGpu, primProc, pipeline, primProcProxies,
-                                          cbInfo.currentCmdBuf());
+    const GrTextureProxy* const* primProcProxies = nullptr;
+    if (fixedDynamicState) {
+        primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
     }
+    pipelineState->setData(fGpu, primProc, pipeline, primProcProxies);
+
+    pipelineState->bind(fGpu, cbInfo.currentCmdBuf());
 
     GrRenderTarget* rt = pipeline.renderTarget();
 
@@ -673,18 +666,9 @@
         cbInfo.fSampledImages.push_back(vkTexture);
     };
 
-    if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
-        for (int m = 0, i = 0; m < meshCount; ++m) {
-            for (int s = 0; s < primProc.numTextureSamplers(); ++s, ++i) {
-                auto texture = dynamicStateArrays->fPrimitiveProcessorTextures[i]->peekTexture();
-                prepareSampledImage(texture, primProc.textureSampler(s).samplerState().filter());
-            }
-        }
-    } else {
-        for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
-            auto texture = fixedDynamicState->fPrimitiveProcessorTextures[i]->peekTexture();
-            prepareSampledImage(texture, primProc.textureSampler(i).samplerState().filter());
-        }
+    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+        auto texture = fixedDynamicState->fPrimitiveProcessorTextures[i]->peekTexture();
+        prepareSampledImage(texture, primProc.textureSampler(i).samplerState().filter());
     }
     GrFragmentProcessor::Iter iter(pipeline);
     while (const GrFragmentProcessor* fp = iter.next()) {
@@ -706,11 +690,14 @@
 
     bool dynamicScissor =
             pipeline.isScissorEnabled() && dynamicStateArrays && dynamicStateArrays->fScissorRects;
-    bool dynamicTextures = dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures;
 
     for (int i = 0; i < meshCount; ++i) {
         const GrMesh& mesh = meshes[i];
         if (mesh.primitiveType() != primitiveType) {
+            // Technically we don't have to call this here (since there is a safety check in
+            // pipelineState:setData but this will allow for quicker freeing of resources if the
+            // pipelineState sits in a cache for a while.
+            pipelineState->freeTempResources(fGpu);
             SkDEBUGCODE(pipelineState = nullptr);
             primitiveType = mesh.primitiveType();
             pipelineState = this->prepareDrawState(primProc, pipeline, fixedDynamicState,
@@ -725,18 +712,18 @@
                                                      pipeline.proxy()->origin(),
                                                      dynamicStateArrays->fScissorRects[i]);
         }
-        if (dynamicTextures) {
-            GrTextureProxy* const* meshProxies = dynamicStateArrays->fPrimitiveProcessorTextures +
-                                                 primProc.numTextureSamplers() * i;
-            pipelineState->setAndBindTextures(fGpu, primProc, pipeline, meshProxies,
-                                              cbInfo.currentCmdBuf());
-        }
+
         SkASSERT(pipelineState);
         mesh.sendToGpu(this);
     }
 
     cbInfo.fBounds.join(bounds);
     cbInfo.fIsEmpty = false;
+
+    // Technically we don't have to call this here (since there is a safety check in
+    // pipelineState:setData but this will allow for quicker freeing of resources if the
+    // pipelineState sits in a cache for a while.
+    pipelineState->freeTempResources(fGpu);
 }
 
 void GrVkGpuRTCommandBuffer::sendInstancedMeshToGpu(GrPrimitiveType,
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 694c846..c57fa1b 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -53,6 +53,10 @@
         , fFragmentProcessors(std::move(fragmentProcessors))
         , fFragmentProcessorCnt(fragmentProcessorCnt)
         , fDataManager(uniforms, geometryUniformSize, fragmentUniformSize) {
+    fSamplers.setReserve(numSamplers);
+    fTextureViews.setReserve(numSamplers);
+    fTextures.setReserve(numSamplers);
+
     fDescriptorSets[0] = VK_NULL_HANDLE;
     fDescriptorSets[1] = VK_NULL_HANDLE;
     fDescriptorSets[2] = VK_NULL_HANDLE;
@@ -67,6 +71,26 @@
     // Must have freed all GPU resources before this is destroyed
     SkASSERT(!fPipeline);
     SkASSERT(!fPipelineLayout);
+    SkASSERT(!fSamplers.count());
+    SkASSERT(!fTextureViews.count());
+    SkASSERT(!fTextures.count());
+}
+
+void GrVkPipelineState::freeTempResources(const GrVkGpu* gpu) {
+    for (int i = 0; i < fSamplers.count(); ++i) {
+        fSamplers[i]->unref(gpu);
+    }
+    fSamplers.rewind();
+
+    for (int i = 0; i < fTextureViews.count(); ++i) {
+        fTextureViews[i]->unref(gpu);
+    }
+    fTextureViews.rewind();
+
+    for (int i = 0; i < fTextures.count(); ++i) {
+        fTextures[i]->unref(gpu);
+    }
+    fTextures.rewind();
 }
 
 void GrVkPipelineState::freeGPUResources(const GrVkGpu* gpu) {
@@ -99,6 +123,8 @@
         fSamplerDescriptorSet->recycle(const_cast<GrVkGpu*>(gpu));
         fSamplerDescriptorSet = nullptr;
     }
+
+    this->freeTempResources(gpu);
 }
 
 void GrVkPipelineState::abandonGPUResources() {
@@ -122,6 +148,21 @@
         fFragmentUniformBuffer.reset();
     }
 
+    for (int i = 0; i < fSamplers.count(); ++i) {
+        fSamplers[i]->unrefAndAbandon();
+    }
+    fSamplers.rewind();
+
+    for (int i = 0; i < fTextureViews.count(); ++i) {
+        fTextureViews[i]->unrefAndAbandon();
+    }
+    fTextureViews.rewind();
+
+    for (int i = 0; i < fTextures.count(); ++i) {
+        fTextures[i]->unrefAndAbandon();
+    }
+    fTextures.rewind();
+
     if (fUniformDescriptorSet) {
         fUniformDescriptorSet->unrefAndAbandon();
         fUniformDescriptorSet = nullptr;
@@ -133,70 +174,17 @@
     }
 }
 
-void GrVkPipelineState::setAndBindUniforms(GrVkGpu* gpu,
-                                           const GrPrimitiveProcessor& primProc,
-                                           const GrPipeline& pipeline,
-                                           GrVkCommandBuffer* commandBuffer) {
+void GrVkPipelineState::setData(GrVkGpu* gpu,
+                                const GrPrimitiveProcessor& primProc,
+                                const GrPipeline& pipeline,
+                                const GrTextureProxy* const primProcTextures[]) {
+    SkASSERT(primProcTextures || !primProc.numTextureSamplers());
+    // This is here to protect against someone calling setData multiple times in a row without
+    // freeing the tempData between calls.
+    this->freeTempResources(gpu);
+
     this->setRenderTargetState(pipeline.proxy());
 
-    fGeometryProcessor->setData(fDataManager, primProc,
-                                GrFragmentProcessor::CoordTransformIter(pipeline));
-    GrFragmentProcessor::Iter iter(pipeline);
-    GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
-    const GrFragmentProcessor* fp = iter.next();
-    GrGLSLFragmentProcessor* glslFP = glslIter.next();
-    while (fp && glslFP) {
-        glslFP->setData(fDataManager, *fp);
-        fp = iter.next();
-        glslFP = glslIter.next();
-    }
-    SkASSERT(!fp && !glslFP);
-
-    {
-        SkIPoint offset;
-        GrTexture* dstTexture = pipeline.peekDstTexture(&offset);
-
-        fXferProcessor->setData(fDataManager, pipeline.getXferProcessor(), dstTexture, offset);
-    }
-
-    // Get new descriptor set
-    if (fGeometryUniformBuffer || fFragmentUniformBuffer) {
-        int uniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
-        if (fDataManager.uploadUniformBuffers(
-                    gpu, fGeometryUniformBuffer.get(), fFragmentUniformBuffer.get()) ||
-            !fUniformDescriptorSet) {
-            if (fUniformDescriptorSet) {
-                fUniformDescriptorSet->recycle(gpu);
-            }
-            fUniformDescriptorSet = gpu->resourceProvider().getUniformDescriptorSet();
-            fDescriptorSets[uniformDSIdx] = fUniformDescriptorSet->descriptorSet();
-            this->writeUniformBuffers(gpu);
-        }
-        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout, uniformDSIdx, 1,
-                                          &fDescriptorSets[uniformDSIdx], 0, nullptr);
-        if (fUniformDescriptorSet) {
-            commandBuffer->addRecycledResource(fUniformDescriptorSet);
-        }
-        if (fGeometryUniformBuffer) {
-            commandBuffer->addRecycledResource(fGeometryUniformBuffer->resource());
-        }
-        if (fFragmentUniformBuffer) {
-            commandBuffer->addRecycledResource(fFragmentUniformBuffer->resource());
-        }
-    }
-}
-
-void GrVkPipelineState::setAndBindTextures(GrVkGpu* gpu,
-                                           const GrPrimitiveProcessor& primProc,
-                                           const GrPipeline& pipeline,
-                                           const GrTextureProxy* const primProcTextures[],
-                                           GrVkCommandBuffer* commandBuffer) {
-    SkASSERT(primProcTextures || !primProc.numTextureSamplers());
-
-    struct SamplerBindings {
-        GrSamplerState fState;
-        GrVkTexture* fTexture;
-    };
     SkAutoSTMalloc<8, SamplerBindings> samplerBindings(fNumSamplers);
     int currTextureBinding = 0;
 
@@ -213,6 +201,7 @@
     const GrFragmentProcessor* fp = iter.next();
     GrGLSLFragmentProcessor* glslFP = glslIter.next();
     while (fp && glslFP) {
+        glslFP->setData(fDataManager, *fp);
         for (int i = 0; i < fp->numTextureSamplers(); ++i) {
             const auto& sampler = fp->textureSampler(i);
             samplerBindings[currTextureBinding++] =
@@ -223,13 +212,20 @@
     }
     SkASSERT(!fp && !glslFP);
 
+    {
+        SkIPoint offset;
+        GrTexture* dstTexture = pipeline.peekDstTexture(&offset);
+
+        fXferProcessor->setData(fDataManager, pipeline.getXferProcessor(), dstTexture, offset);
+    }
+
     if (GrTextureProxy* dstTextureProxy = pipeline.dstTextureProxy()) {
         samplerBindings[currTextureBinding++] = {
                 GrSamplerState::ClampNearest(),
                 static_cast<GrVkTexture*>(dstTextureProxy->peekTexture())};
     }
 
-    // Get new descriptor set
+    // Get new descriptor sets
     SkASSERT(fNumSamplers == currTextureBinding);
     if (fNumSamplers) {
         if (fSamplerDescriptorSet) {
@@ -238,44 +234,23 @@
         fSamplerDescriptorSet = gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle);
         int samplerDSIdx = GrVkUniformHandler::kSamplerDescSet;
         fDescriptorSets[samplerDSIdx] = fSamplerDescriptorSet->descriptorSet();
-        for (int i = 0; i < fNumSamplers; ++i) {
-            const GrSamplerState& state = samplerBindings[i].fState;
-            GrVkTexture* texture = samplerBindings[i].fTexture;
+        this->writeSamplers(gpu, samplerBindings.get());
+    }
 
-            const GrVkImageView* textureView = texture->textureView();
-            GrVkSampler* sampler = gpu->resourceProvider().findOrCreateCompatibleSampler(
-                    state, texture->texturePriv().maxMipMapLevel());
-
-            VkDescriptorImageInfo imageInfo;
-            memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
-            imageInfo.sampler = sampler->sampler();
-            imageInfo.imageView = textureView->imageView();
-            imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
-
-            VkWriteDescriptorSet writeInfo;
-            memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
-            writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-            writeInfo.pNext = nullptr;
-            writeInfo.dstSet = fDescriptorSets[GrVkUniformHandler::kSamplerDescSet];
-            writeInfo.dstBinding = i;
-            writeInfo.dstArrayElement = 0;
-            writeInfo.descriptorCount = 1;
-            writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-            writeInfo.pImageInfo = &imageInfo;
-            writeInfo.pBufferInfo = nullptr;
-            writeInfo.pTexelBufferView = nullptr;
-
-            GR_VK_CALL(gpu->vkInterface(),
-                       UpdateDescriptorSets(gpu->device(), 1, &writeInfo, 0, nullptr));
-            commandBuffer->addResource(sampler);
-            sampler->unref(gpu);
-            commandBuffer->addResource(samplerBindings[i].fTexture->textureView());
-            commandBuffer->addResource(samplerBindings[i].fTexture->resource());
+    if (fGeometryUniformBuffer || fFragmentUniformBuffer) {
+        if (fDataManager.uploadUniformBuffers(gpu,
+                                              fGeometryUniformBuffer.get(),
+                                              fFragmentUniformBuffer.get())
+            || !fUniformDescriptorSet)
+        {
+            if (fUniformDescriptorSet) {
+                fUniformDescriptorSet->recycle(gpu);
+            }
+            fUniformDescriptorSet = gpu->resourceProvider().getUniformDescriptorSet();
+            int uniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
+            fDescriptorSets[uniformDSIdx] = fUniformDescriptorSet->descriptorSet();
+            this->writeUniformBuffers(gpu);
         }
-
-        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout, samplerDSIdx, 1,
-                                          &fDescriptorSets[samplerDSIdx], 0, nullptr);
-        commandBuffer->addRecycledResource(fSamplerDescriptorSet);
     }
 }
 
@@ -337,6 +312,49 @@
     }
 }
 
+void GrVkPipelineState::writeSamplers(GrVkGpu* gpu, const SamplerBindings bindings[]) {
+    for (int i = 0; i < fNumSamplers; ++i) {
+        const GrSamplerState& state = bindings[i].fState;
+        GrVkTexture* texture = bindings[i].fTexture;
+
+        fSamplers.push_back(gpu->resourceProvider().findOrCreateCompatibleSampler(
+                state, texture->texturePriv().maxMipMapLevel()));
+
+        const GrVkResource* textureResource = texture->resource();
+        textureResource->ref();
+        fTextures.push_back(textureResource);
+
+        const GrVkImageView* textureView = texture->textureView();
+        textureView->ref();
+        fTextureViews.push_back(textureView);
+
+        VkDescriptorImageInfo imageInfo;
+        memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
+        imageInfo.sampler = fSamplers[i]->sampler();
+        imageInfo.imageView = textureView->imageView();
+        imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+        VkWriteDescriptorSet writeInfo;
+        memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
+        writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+        writeInfo.pNext = nullptr;
+        writeInfo.dstSet = fDescriptorSets[GrVkUniformHandler::kSamplerDescSet];
+        writeInfo.dstBinding = i;
+        writeInfo.dstArrayElement = 0;
+        writeInfo.descriptorCount = 1;
+        writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+        writeInfo.pImageInfo = &imageInfo;
+        writeInfo.pBufferInfo = nullptr;
+        writeInfo.pTexelBufferView = nullptr;
+
+        GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
+                                                            1,
+                                                            &writeInfo,
+                                                            0,
+                                                            nullptr));
+    }
+}
+
 void GrVkPipelineState::setRenderTargetState(const GrRenderTargetProxy* proxy) {
     GrRenderTarget* rt = proxy->peekRenderTarget();
 
@@ -361,6 +379,47 @@
     }
 }
 
-void GrVkPipelineState::bindPipeline(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer) {
+void GrVkPipelineState::bind(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer) {
     commandBuffer->bindPipeline(gpu, fPipeline);
+
+    if (fGeometryUniformBuffer || fFragmentUniformBuffer) {
+        int dsIndex = GrVkUniformHandler::kUniformBufferDescSet;
+        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout,
+                                          dsIndex, 1,
+                                          &fDescriptorSets[dsIndex], 0, nullptr);
+    }
+    if (fNumSamplers) {
+        int dsIndex = GrVkUniformHandler::kSamplerDescSet;
+        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout,
+                                          dsIndex, 1,
+                                          &fDescriptorSets[dsIndex], 0, nullptr);
+    }
+}
+
+void GrVkPipelineState::addUniformResources(GrVkCommandBuffer& commandBuffer) {
+    if (fUniformDescriptorSet) {
+        commandBuffer.addRecycledResource(fUniformDescriptorSet);
+    }
+    if (fSamplerDescriptorSet) {
+        commandBuffer.addRecycledResource(fSamplerDescriptorSet);
+    }
+
+    if (fGeometryUniformBuffer.get()) {
+        commandBuffer.addRecycledResource(fGeometryUniformBuffer->resource());
+    }
+    if (fFragmentUniformBuffer.get()) {
+        commandBuffer.addRecycledResource(fFragmentUniformBuffer->resource());
+    }
+
+    for (int i = 0; i < fSamplers.count(); ++i) {
+        commandBuffer.addResource(fSamplers[i]);
+    }
+
+    for (int i = 0; i < fTextureViews.count(); ++i) {
+        commandBuffer.addResource(fTextureViews[i]);
+    }
+
+    for (int i = 0; i < fTextures.count(); ++i) {
+        commandBuffer.addResource(fTextures[i]);
+    }
 }
diff --git a/src/gpu/vk/GrVkPipelineState.h b/src/gpu/vk/GrVkPipelineState.h
index 994d0a9..d61359c 100644
--- a/src/gpu/vk/GrVkPipelineState.h
+++ b/src/gpu/vk/GrVkPipelineState.h
@@ -56,32 +56,35 @@
 
     ~GrVkPipelineState();
 
-    void setAndBindUniforms(GrVkGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
-                            GrVkCommandBuffer*);
-    /**
-     * This must be called after setAndBindUniforms() since that function invalidates texture
-     * bindings.
-     */
-    void setAndBindTextures(GrVkGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
-                            const GrTextureProxy* const primitiveProcessorTextures[],
-                            GrVkCommandBuffer*);
+    void setData(GrVkGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
+                 const GrTextureProxy* const primitiveProcessorTextures[]);
 
-    void bindPipeline(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer);
+    void bind(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer);
 
-    void addUniformResources(GrVkCommandBuffer&, GrVkSampler*[], GrVkTexture*[], int numTextures);
+    void addUniformResources(GrVkCommandBuffer&);
 
     void freeGPUResources(const GrVkGpu* gpu);
 
+    // This releases resources that only a given instance of a GrVkPipelineState needs to hold onto
+    // and don't need to survive across new uses of the GrVkPipelineState.
+    void freeTempResources(const GrVkGpu* gpu);
+
     void abandonGPUResources();
 
 private:
     void writeUniformBuffers(const GrVkGpu* gpu);
 
+    struct SamplerBindings {
+        GrSamplerState fState;
+        GrVkTexture* fTexture;
+    };
+    void writeSamplers(GrVkGpu* gpu, const SamplerBindings[]);
+
     /**
-     * We use the RT's size and origin to adjust from Skia device space to vulkan normalized device
-     * space and to make device space positions have the correct origin for processors that require
-     * them.
-     */
+    * We use the RT's size and origin to adjust from Skia device space to vulkan normalized device
+    * space and to make device space positions have the correct origin for processors that require
+    * them.
+    */
     struct RenderTargetState {
         SkISize         fRenderTargetSize;
         GrSurfaceOrigin fRenderTargetOrigin;
@@ -138,6 +141,11 @@
     std::unique_ptr<GrVkUniformBuffer> fGeometryUniformBuffer;
     std::unique_ptr<GrVkUniformBuffer> fFragmentUniformBuffer;
 
+    // GrVkResources used for sampling textures
+    SkTDArray<GrVkSampler*> fSamplers;
+    SkTDArray<const GrVkImageView*> fTextureViews;
+    SkTDArray<const GrVkResource*> fTextures;
+
     // Tracks the current render target uniforms stored in the vertex buffer.
     RenderTargetState fRenderTargetState;
     GrGLSLBuiltinUniformHandles fBuiltinUniformHandles;
diff --git a/src/gpu/vk/GrVkUniformHandler.h b/src/gpu/vk/GrVkUniformHandler.h
index d79b1b3..bd8f8f3 100644
--- a/src/gpu/vk/GrVkUniformHandler.h
+++ b/src/gpu/vk/GrVkUniformHandler.h
@@ -17,12 +17,6 @@
     static const int kUniformsPerBlock = 8;
 
     enum {
-        /**
-         * Binding a descriptor set invalidates all higher index descriptor sets. We must bind
-         * in the order of this enumeration. Samplers are after Uniforms because GrOps can specify
-         * GP textures as dynamic state, meaning they get rebound for each GrMesh in a draw while
-         * uniforms are bound once before all the draws.
-         */
         kUniformBufferDescSet = 0,
         kSamplerDescSet = 1,
     };