Add Metal support for dynamic texture state

Bug: skia:9604
Change-Id: I16f487137c79e37493cd24b63ee29ac2e22d6cec
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/254981
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index db8aeab..a8e6b34 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -293,6 +293,8 @@
 
     fCrossContextTextureSupport = false;
     fHalfFloatVertexAttributeSupport = true;
+
+    fDynamicStateArrayGeometryProcessorTextureSupport = true;
 }
 
 static bool format_is_srgb(MTLPixelFormat format) {
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.mm b/src/gpu/mtl/GrMtlOpsRenderPass.mm
index e60f655..39110df 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.mm
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.mm
@@ -89,6 +89,7 @@
                                 programInfo.pipeline().getXferProcessor());
 
     bool hasDynamicScissors = programInfo.hasDynamicScissors();
+    bool hasDynamicTextures = programInfo.hasDynamicPrimProcTextures();
 
     if (!programInfo.pipeline().isScissorEnabled()) {
         GrMtlPipelineState::SetDynamicScissorRectState(fActiveRenderCmdEncoder,
@@ -103,6 +104,10 @@
                                                        programInfo.fixedScissor());
     }
 
+    if (!hasDynamicTextures) {
+        pipelineState->bindTextures(fActiveRenderCmdEncoder);
+    }
+
     for (int i = 0; i < meshCount; ++i) {
         const GrMesh& mesh = meshes[i];
         SkASSERT(nil != fActiveRenderCmdEncoder);
@@ -113,6 +118,11 @@
                                                            fOrigin,
                                                            programInfo.dynamicScissor(i));
         }
+        if (hasDynamicTextures) {
+            auto meshProxies = programInfo.dynamicPrimProcTextures(i);
+            pipelineState->setTextures(programInfo, meshProxies);
+            pipelineState->bindTextures(fActiveRenderCmdEncoder);
+        }
 
         mesh.sendToGpu(this);
     }
diff --git a/src/gpu/mtl/GrMtlPipelineState.h b/src/gpu/mtl/GrMtlPipelineState.h
index e71aee2..a2e41f0 100644
--- a/src/gpu/mtl/GrMtlPipelineState.h
+++ b/src/gpu/mtl/GrMtlPipelineState.h
@@ -48,6 +48,10 @@
 
     void setData(const GrRenderTarget*, const GrProgramInfo&);
 
+    void setTextures(const GrProgramInfo& programInfo,
+                     const GrTextureProxy* const primProcTextures[]);
+    void bindTextures(id<MTLRenderCommandEncoder> renderCmdEncoder);
+
     void setDrawState(id<MTLRenderCommandEncoder>, const GrSwizzle& outputSwizzle,
                       const GrXferProcessor&);
 
@@ -97,7 +101,7 @@
 
     void setRenderTargetState(const GrRenderTarget*, GrSurfaceOrigin);
 
-    void bind(id<MTLRenderCommandEncoder>);
+    void bindUniforms(id<MTLRenderCommandEncoder>);
 
     void setBlendConstants(id<MTLRenderCommandEncoder>, const GrSwizzle&, const GrXferProcessor&);
 
diff --git a/src/gpu/mtl/GrMtlPipelineState.mm b/src/gpu/mtl/GrMtlPipelineState.mm
index fadaf4e6..cde1722 100644
--- a/src/gpu/mtl/GrMtlPipelineState.mm
+++ b/src/gpu/mtl/GrMtlPipelineState.mm
@@ -58,19 +58,33 @@
 
 void GrMtlPipelineState::setData(const GrRenderTarget* renderTarget,
                                  const GrProgramInfo& programInfo) {
-
-    // Note: the Metal backend currently only supports fixed primProc textures
-    SkASSERT(!programInfo.hasDynamicPrimProcTextures());
-    auto proxies = programInfo.hasFixedPrimProcTextures() ? programInfo.fixedPrimProcTextures()
-                                                          : nullptr;
-
     this->setRenderTargetState(renderTarget, programInfo.origin());
     fGeometryProcessor->setData(fDataManager, programInfo.primProc(),
                                 GrFragmentProcessor::CoordTransformIter(programInfo.pipeline()));
+
+    if (!programInfo.hasDynamicPrimProcTextures()) {
+        auto proxies = programInfo.hasFixedPrimProcTextures() ? programInfo.fixedPrimProcTextures()
+                                                              : nullptr;
+        this->setTextures(programInfo, proxies);
+    }
+    fDataManager.resetDirtyBits();
+
+#ifdef SK_DEBUG
+    if (programInfo.pipeline().isStencilEnabled()) {
+        SkASSERT(renderTarget->renderTargetPriv().getStencilAttachment());
+        SkASSERT(renderTarget->renderTargetPriv().numStencilBits() == 8);
+    }
+#endif
+
+    fStencil = programInfo.nonGLStencilSettings();
+}
+
+void GrMtlPipelineState::setTextures(const GrProgramInfo& programInfo,
+                                     const GrTextureProxy* const primProcTextures[]) {
     fSamplerBindings.reset();
     for (int i = 0; i < programInfo.primProc().numTextureSamplers(); ++i) {
         const auto& sampler = programInfo.primProc().textureSampler(i);
-        auto texture = static_cast<GrMtlTexture*>(proxies[i]->peekTexture());
+        auto texture = static_cast<GrMtlTexture*>(primProcTextures[i]->peekTexture());
         fSamplerBindings.emplace_back(sampler.samplerState(), texture, fGpu);
     }
 
@@ -104,31 +118,23 @@
     }
 
     SkASSERT(fNumSamplers == fSamplerBindings.count());
-    fDataManager.resetDirtyBits();
-
-#ifdef SK_DEBUG
-    if (programInfo.pipeline().isStencilEnabled()) {
-        SkASSERT(renderTarget->renderTargetPriv().getStencilAttachment());
-        SkASSERT(renderTarget->renderTargetPriv().numStencilBits() == 8);
-    }
-#endif
-
-    fStencil = programInfo.nonGLStencilSettings();
 }
 
 void GrMtlPipelineState::setDrawState(id<MTLRenderCommandEncoder> renderCmdEncoder,
                                       const GrSwizzle& outputSwizzle,
                                       const GrXferProcessor& xferProcessor) {
     [renderCmdEncoder pushDebugGroup:@"setDrawState"];
-    this->bind(renderCmdEncoder);
+    this->bindUniforms(renderCmdEncoder);
     this->setBlendConstants(renderCmdEncoder, outputSwizzle, xferProcessor);
     this->setDepthStencilState(renderCmdEncoder);
     [renderCmdEncoder popDebugGroup];
 }
 
-void GrMtlPipelineState::bind(id<MTLRenderCommandEncoder> renderCmdEncoder) {
+void GrMtlPipelineState::bindUniforms(id<MTLRenderCommandEncoder> renderCmdEncoder) {
     fDataManager.uploadAndBindUniformBuffers(fGpu, renderCmdEncoder);
+}
 
+void GrMtlPipelineState::bindTextures(id<MTLRenderCommandEncoder> renderCmdEncoder) {
     SkASSERT(fNumSamplers == fSamplerBindings.count());
     for (int index = 0; index < fNumSamplers; ++index) {
         [renderCmdEncoder setFragmentTexture: fSamplerBindings[index].fTexture