Pass in actual sampled texture and sample params to gpu uniform builders.

To support external textures vulkan needs to know the actual sampler it
will use at pipeline creation time. Thus we need access to the actual
vkimage and sampler state to know how to make these samplers early.

Bug: skia:
Change-Id: I8492bb283932d6800dbfcc8bc86b3bba947397fe
Reviewed-on: https://skia-review.googlesource.com/c/166281
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/GrGpuCommandBuffer.cpp b/src/gpu/GrGpuCommandBuffer.cpp
index 8b5b128..a6a2f84 100644
--- a/src/gpu/GrGpuCommandBuffer.cpp
+++ b/src/gpu/GrGpuCommandBuffer.cpp
@@ -62,6 +62,24 @@
                 return false;
             }
         }
+#ifdef SK_DEBUG
+        SkASSERT(meshCount >= 1);
+        const GrTextureProxy* const* primProcProxies =
+                dynamicStateArrays->fPrimitiveProcessorTextures;
+        for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+            const GrBackendFormat& format = primProcProxies[i]->backendFormat();
+            GrTextureType type = primProcProxies[i]->textureType();
+            GrPixelConfig config = primProcProxies[i]->config();
+            for (int j = 1; j < meshCount; ++j) {
+                const GrTextureProxy* testProxy =
+                        primProcProxies[j*primProc.numTextureSamplers() + i];
+                SkASSERT(testProxy->backendFormat() == format);
+                SkASSERT(testProxy->textureType() == type);
+                SkASSERT(testProxy->config() == config);
+            }
+        }
+#endif
+
     }
 
     if (primProc.numVertexAttributes() > this->gpu()->caps()->maxVertexAttributes()) {
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index fb74efb..df10ffd 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1843,11 +1843,6 @@
                            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.
@@ -1858,6 +1853,15 @@
         primProcProxiesForMipRegen = fixedDynamicState->fPrimitiveProcessorTextures;
         primProcProxiesToBind = fixedDynamicState->fPrimitiveProcessorTextures;
     }
+
+    SkASSERT(SkToBool(primProcProxiesForMipRegen) == SkToBool(primProc.numTextureSamplers()));
+
+    sk_sp<GrGLProgram> program(fProgramCache->refProgram(this, primProc, primProcProxiesForMipRegen,
+                                                         pipeline, willDrawPoints));
+    if (!program) {
+        GrCapsDebugf(this->caps(), "Failed to create program!\n");
+        return false;
+    }
     this->resolveAndGenerateMipMapsForProcessorTextures(
             primProc, pipeline, primProcProxiesForMipRegen, numPrimProcTextureSets);
 
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index aea2e96..aa5d847 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -309,8 +309,9 @@
         ~ProgramCache();
 
         void abandon();
-        GrGLProgram* refProgram(const GrGLGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
-                                bool hasPointSize);
+        GrGLProgram* refProgram(const GrGLGpu*, const GrPrimitiveProcessor&,
+                                const GrTextureProxy* const primProcProxies[],
+                                const GrPipeline&, bool hasPointSize);
 
     private:
         // We may actually have kMaxEntries+1 shaders in the GL context because we create a new
diff --git a/src/gpu/gl/GrGLGpuProgramCache.cpp b/src/gpu/gl/GrGLGpuProgramCache.cpp
index e00ef29..fa738cd 100644
--- a/src/gpu/gl/GrGLGpuProgramCache.cpp
+++ b/src/gpu/gl/GrGLGpuProgramCache.cpp
@@ -70,6 +70,7 @@
 
 GrGLProgram* GrGLGpu::ProgramCache::refProgram(const GrGLGpu* gpu,
                                                const GrPrimitiveProcessor& primProc,
+                                               const GrTextureProxy* const primProcProxies[],
                                                const GrPipeline& pipeline,
                                                bool isPoints) {
 #ifdef PROGRAM_CACHE_STATS
@@ -96,7 +97,8 @@
 #ifdef PROGRAM_CACHE_STATS
         ++fCacheMisses;
 #endif
-        GrGLProgram* program = GrGLProgramBuilder::CreateProgram(primProc, pipeline, &desc, fGpu);
+        GrGLProgram* program = GrGLProgramBuilder::CreateProgram(primProc, primProcProxies,
+                                                                 pipeline, &desc, fGpu);
         if (nullptr == program) {
             return nullptr;
         }
diff --git a/src/gpu/gl/GrGLUniformHandler.cpp b/src/gpu/gl/GrGLUniformHandler.cpp
index 7551e9a..24f4ac9 100644
--- a/src/gpu/gl/GrGLUniformHandler.cpp
+++ b/src/gpu/gl/GrGLUniformHandler.cpp
@@ -7,6 +7,7 @@
 
 #include "gl/GrGLUniformHandler.h"
 
+#include "GrTexturePriv.h"
 #include "gl/GrGLCaps.h"
 #include "gl/GrGLGpu.h"
 #include "gl/builders/GrGLProgramBuilder.h"
@@ -61,16 +62,20 @@
     return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1);
 }
 
-GrGLSLUniformHandler::SamplerHandle GrGLUniformHandler::addSampler(GrSwizzle swizzle,
-                                                                   GrTextureType type,
-                                                                   GrSLPrecision precision,
-                                                                   const char* name) {
+GrGLSLUniformHandler::SamplerHandle GrGLUniformHandler::addSampler(const GrTexture* texture,
+                                                                   const GrSamplerState&,
+                                                                   const char* name,
+                                                                   const GrShaderCaps* shaderCaps) {
     SkASSERT(name && strlen(name));
 
     SkString mangleName;
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
+    GrSLPrecision precision = GrSLSamplerPrecision(texture->config());
+    GrSwizzle swizzle = shaderCaps->configTextureSwizzle(texture->config());
+    GrTextureType type = texture->texturePriv().textureType();
+
     UniformInfo& sampler = fSamplers.push_back();
     sampler.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type));
     sampler.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
diff --git a/src/gpu/gl/GrGLUniformHandler.h b/src/gpu/gl/GrGLUniformHandler.h
index 2442716..962beee 100644
--- a/src/gpu/gl/GrGLUniformHandler.h
+++ b/src/gpu/gl/GrGLUniformHandler.h
@@ -39,7 +39,8 @@
                                           int arrayCount,
                                           const char** outName) override;
 
-    SamplerHandle addSampler(GrSwizzle, GrTextureType, GrSLPrecision, const char* name) override;
+    SamplerHandle addSampler(const GrTexture*, const GrSamplerState&, const char* name,
+                             const GrShaderCaps*) override;
 
     const GrShaderVar& samplerVariable(SamplerHandle handle) const override {
         return fSamplers[handle.toIndex()].fVariable;
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 6c12738..c98d47a 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -30,6 +30,7 @@
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X)
 
 GrGLProgram* GrGLProgramBuilder::CreateProgram(const GrPrimitiveProcessor& primProc,
+                                               const GrTextureProxy* const primProcProxies[],
                                                const GrPipeline& pipeline,
                                                GrProgramDesc* desc,
                                                GrGLGpu* gpu) {
@@ -40,7 +41,7 @@
 
     // create a builder.  This will be handed off to effects so they can use it to add
     // uniforms, varyings, textures, etc
-    GrGLProgramBuilder builder(gpu, pipeline, primProc, desc);
+    GrGLProgramBuilder builder(gpu, pipeline, primProc, primProcProxies, desc);
 
     auto persistentCache = gpu->getContext()->contextPriv().getPersistentCache();
     if (persistentCache && gpu->glCaps().programBinarySupport()) {
@@ -61,8 +62,9 @@
 GrGLProgramBuilder::GrGLProgramBuilder(GrGLGpu* gpu,
                                        const GrPipeline& pipeline,
                                        const GrPrimitiveProcessor& primProc,
+                                       const GrTextureProxy* const primProcProxies[],
                                        GrProgramDesc* desc)
-        : INHERITED(primProc, pipeline, desc)
+        : INHERITED(primProc, primProcProxies, pipeline, desc)
         , fGpu(gpu)
         , fVaryingHandler(this)
         , fUniformHandler(this)
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index b601b6b..0ba7d49 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -36,6 +36,7 @@
      * @return true if generation was successful.
      */
     static GrGLProgram* CreateProgram(const GrPrimitiveProcessor&,
+                                      const GrTextureProxy* const primProcProxies[],
                                       const GrPipeline&,
                                       GrProgramDesc*,
                                       GrGLGpu*);
@@ -46,7 +47,7 @@
 
 private:
     GrGLProgramBuilder(GrGLGpu*, const GrPipeline&, const GrPrimitiveProcessor&,
-                       GrProgramDesc*);
+                       const GrTextureProxy* const primProcProxies[], GrProgramDesc*);
 
     void addInputVars(const SkSL::Program::Inputs& inputs);
     bool compileAndAttachShaders(const char* glsl,
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index f56f389..15be881 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -20,6 +20,7 @@
 const int GrGLSLProgramBuilder::kVarsPerBlock = 8;
 
 GrGLSLProgramBuilder::GrGLSLProgramBuilder(const GrPrimitiveProcessor& primProc,
+                                           const GrTextureProxy* const primProcProxies[],
                                            const GrPipeline& pipeline,
                                            GrProgramDesc* desc)
         : fVS(this)
@@ -28,6 +29,7 @@
         , fStageIndex(-1)
         , fPipeline(pipeline)
         , fPrimProc(primProc)
+        , fPrimProcProxies(primProcProxies)
         , fDesc(desc)
         , fGeometryProcessor(nullptr)
         , fXferProcessor(nullptr)
@@ -51,11 +53,9 @@
 bool GrGLSLProgramBuilder::emitAndInstallProcs() {
     // First we loop over all of the installed processors and collect coord transforms.  These will
     // be sent to the GrGLSLPrimitiveProcessor in its emitCode function
-    const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
-
     SkString inputColor;
     SkString inputCoverage;
-    this->emitAndInstallPrimProc(primProc, &inputColor, &inputCoverage);
+    this->emitAndInstallPrimProc(&inputColor, &inputCoverage);
     this->emitAndInstallFragProcs(&inputColor, &inputCoverage);
     this->emitAndInstallXferProc(inputColor, inputCoverage);
     this->emitFSOutputSwizzle(this->pipeline().getXferProcessor().hasSecondaryOutput());
@@ -63,9 +63,11 @@
     return this->checkSamplerCounts();
 }
 
-void GrGLSLProgramBuilder::emitAndInstallPrimProc(const GrPrimitiveProcessor& proc,
-                                                  SkString* outputColor,
+void GrGLSLProgramBuilder::emitAndInstallPrimProc(SkString* outputColor,
                                                   SkString* outputCoverage) {
+    const GrPrimitiveProcessor& proc = this->primitiveProcessor();
+    const GrTextureProxy* const* primProcProxies = this->primProcProxies();
+
     // Program builders have a bit of state we need to clear with each effect
     AutoStageAdvance adv(this);
     this->nameExpression(outputColor, "outputColor");
@@ -99,7 +101,12 @@
         SkString name;
         name.printf("TextureSampler_%d", i);
         const auto& sampler = proc.textureSampler(i);
-        texSamplers[i] = this->emitSampler(sampler.textureType(), sampler.config(), name.c_str());
+        const GrTexture* texture = primProcProxies[i]->peekTexture();
+        SkASSERT(sampler.textureType() == texture->texturePriv().textureType());
+        SkASSERT(sampler.config() == texture->config());
+        texSamplers[i] = this->emitSampler(texture,
+                                           sampler.samplerState(),
+                                           name.c_str());
     }
 
     GrGLSLPrimitiveProcessor::FPCoordTransformHandler transformHandler(fPipeline,
@@ -179,8 +186,8 @@
             SkString name;
             name.printf("TextureSampler_%d", samplerIdx++);
             const auto& sampler = subFP->textureSampler(i);
-            GrTextureType textureType = sampler.peekTexture()->texturePriv().textureType();
-            texSamplers.emplace_back(this->emitSampler(textureType, sampler.peekTexture()->config(),
+            texSamplers.emplace_back(this->emitSampler(sampler.peekTexture(),
+                                                       sampler.samplerState(),
                                                        name.c_str()));
         }
     }
@@ -237,8 +244,7 @@
         // GrProcessor::TextureSampler sampler(dstTexture);
         SkString name("DstTextureSampler");
         dstTextureSamplerHandle =
-                this->emitSampler(dstTexture->texturePriv().textureType(), dstTexture->config(),
-                                  "DstTextureSampler");
+                this->emitSampler(dstTexture, GrSamplerState(), "DstTextureSampler");
         dstTextureOrigin = fPipeline.dstTextureProxy()->origin();
         SkASSERT(dstTexture->texturePriv().textureType() != GrTextureType::kExternal);
     }
@@ -261,13 +267,11 @@
     fFS.codeAppend("}");
 }
 
-GrGLSLProgramBuilder::SamplerHandle GrGLSLProgramBuilder::emitSampler(GrTextureType textureType,
-                                                                      GrPixelConfig config,
+GrGLSLProgramBuilder::SamplerHandle GrGLSLProgramBuilder::emitSampler(const GrTexture* texture,
+                                                                      const GrSamplerState& state,
                                                                       const char* name) {
     ++fNumFragmentSamplers;
-    GrSLPrecision precision = GrSLSamplerPrecision(config);
-    GrSwizzle swizzle = this->shaderCaps()->configTextureSwizzle(config);
-    return this->uniformHandler()->addSampler(swizzle, textureType, precision, name);
+    return this->uniformHandler()->addSampler(texture, state, name, this->shaderCaps());
 }
 
 void GrGLSLProgramBuilder::emitFSOutputSwizzle(bool hasSecondaryOutput) {
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index 4e7c1ec..37db3d6 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -35,6 +35,7 @@
     const GrShaderCaps* shaderCaps() const { return this->caps()->shaderCaps(); }
 
     const GrPrimitiveProcessor& primitiveProcessor() const { return fPrimProc; }
+    const GrTextureProxy* const* primProcProxies() const { return fPrimProcProxies; }
     const GrPipeline& pipeline() const { return fPipeline; }
     GrProgramDesc* desc() { return fDesc; }
     const GrProgramDesc::KeyHeader& header() const { return fDesc->header(); }
@@ -74,17 +75,19 @@
     // number of each input/output type in a single allocation block, used by many builders
     static const int kVarsPerBlock;
 
-    GrGLSLVertexBuilder         fVS;
-    GrGLSLGeometryBuilder       fGS;
-    GrGLSLFragmentShaderBuilder fFS;
+    GrGLSLVertexBuilder          fVS;
+    GrGLSLGeometryBuilder        fGS;
+    GrGLSLFragmentShaderBuilder  fFS;
 
     int fStageIndex;
 
-    const GrPipeline&           fPipeline;
-    const GrPrimitiveProcessor& fPrimProc;
-    GrProgramDesc*              fDesc;
+    const GrPipeline&            fPipeline;
+    const GrPrimitiveProcessor&  fPrimProc;
+    const GrTextureProxy* const* fPrimProcProxies;
 
-    GrGLSLBuiltinUniformHandles fUniformHandles;
+    GrProgramDesc*               fDesc;
+
+    GrGLSLBuiltinUniformHandles  fUniformHandles;
 
     std::unique_ptr<GrGLSLPrimitiveProcessor> fGeometryProcessor;
     std::unique_ptr<GrGLSLXferProcessor> fXferProcessor;
@@ -92,7 +95,10 @@
     int fFragmentProcessorCnt;
 
 protected:
-    explicit GrGLSLProgramBuilder(const GrPrimitiveProcessor&, const GrPipeline&, GrProgramDesc*);
+    explicit GrGLSLProgramBuilder(const GrPrimitiveProcessor&,
+                                  const GrTextureProxy* const primProcProxies[],
+                                  const GrPipeline&,
+                                  GrProgramDesc*);
 
     void addFeature(GrShaderFlags shaders, uint32_t featureBit, const char* extensionName);
 
@@ -128,9 +134,7 @@
     // Generates a possibly mangled name for a stage variable and writes it to the fragment shader.
     void nameExpression(SkString*, const char* baseName);
 
-    void emitAndInstallPrimProc(const GrPrimitiveProcessor&,
-                                SkString* outputColor,
-                                SkString* outputCoverage);
+    void emitAndInstallPrimProc(SkString* outputColor, SkString* outputCoverage);
     void emitAndInstallFragProcs(SkString* colorInOut, SkString* coverageInOut);
     SkString emitAndInstallFragProc(const GrFragmentProcessor&,
                                     int index,
@@ -139,7 +143,7 @@
                                     SkString output,
                                     SkTArray<std::unique_ptr<GrGLSLFragmentProcessor>>*);
     void emitAndInstallXferProc(const SkString& colorIn, const SkString& coverageIn);
-    SamplerHandle emitSampler(GrTextureType, GrPixelConfig, const char* name);
+    SamplerHandle emitSampler(const GrTexture*, const GrSamplerState&, const char* name);
     void emitFSOutputSwizzle(bool hasSecondaryOutput);
     bool checkSamplerCounts();
 
diff --git a/src/gpu/glsl/GrGLSLUniformHandler.h b/src/gpu/glsl/GrGLSLUniformHandler.h
index 180bf6f..5ee524c 100644
--- a/src/gpu/glsl/GrGLSLUniformHandler.h
+++ b/src/gpu/glsl/GrGLSLUniformHandler.h
@@ -16,6 +16,8 @@
 #define GR_NO_MANGLE_PREFIX "sk_"
 
 class GrGLSLProgramBuilder;
+class GrSamplerState;
+class GrTexture;
 
 // Handles for program uniforms (other than per-effect uniforms)
 struct GrGLSLBuiltinUniformHandles {
@@ -97,7 +99,8 @@
     virtual const GrShaderVar& samplerVariable(SamplerHandle) const = 0;
     virtual GrSwizzle samplerSwizzle(SamplerHandle) const = 0;
 
-    virtual SamplerHandle addSampler(GrSwizzle, GrTextureType, GrSLPrecision, const char* name) = 0;
+    virtual SamplerHandle addSampler(const GrTexture*, const GrSamplerState&, const char* name,
+                                     const GrShaderCaps*) = 0;
 
     virtual UniformHandle internalAddUniformArray(uint32_t visibility,
                                                   GrSLType type,
diff --git a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
index 48258cc..34f02bb 100644
--- a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
+++ b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
@@ -124,17 +124,19 @@
     }
     desc.finalize();
 
-    // TODO: use resource provider for pipeline
-    GrMtlPipelineState* pipelineState =
-            GrMtlPipelineStateBuilder::CreatePipelineState(primProc, pipeline, &desc, fGpu);
-    if (!pipelineState) {
-        return nullptr;
-    }
     const GrTextureProxy* const* primProcProxies = nullptr;
     if (fixedDynamicState) {
         primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
     }
+    SkASSERT(SkToBool(primProcProxies) == SkToBool(primProc.numTextureSamplers()));
 
+    // TODO: use resource provider for pipeline
+    GrMtlPipelineState* pipelineState =
+            GrMtlPipelineStateBuilder::CreatePipelineState(primProc, primProcProxies, pipeline,
+                                                           &desc, fGpu);
+    if (!pipelineState) {
+        return nullptr;
+    }
     // We cannot have an active encoder when we set the pipeline data since it requires its own
     // command encoder.
     SkASSERT(fActiveRenderCmdEncoder == nil);
diff --git a/src/gpu/mtl/GrMtlPipelineStateBuilder.h b/src/gpu/mtl/GrMtlPipelineStateBuilder.h
index 0596b3e..bfa0486 100644
--- a/src/gpu/mtl/GrMtlPipelineStateBuilder.h
+++ b/src/gpu/mtl/GrMtlPipelineStateBuilder.h
@@ -23,12 +23,15 @@
 class GrMtlPipelineStateBuilder : public GrGLSLProgramBuilder {
 public:
     static GrMtlPipelineState* CreatePipelineState(const GrPrimitiveProcessor&,
+                                                   const GrTextureProxy* const primProcProxies[],
                                                    const GrPipeline&,
                                                    GrProgramDesc*,
                                                    GrMtlGpu*);
 
 private:
-    GrMtlPipelineStateBuilder(const GrPrimitiveProcessor&, const GrPipeline&,
+    GrMtlPipelineStateBuilder(const GrPrimitiveProcessor&,
+                              const GrTextureProxy* const primProcProxies[],
+                              const GrPipeline&,
                               GrProgramDesc*, GrMtlGpu*);
 
     const GrCaps* caps() const override;
diff --git a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
index 3512bec..fb9cdf8 100644
--- a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
+++ b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
@@ -18,10 +18,11 @@
 
 GrMtlPipelineState* GrMtlPipelineStateBuilder::CreatePipelineState(
         const GrPrimitiveProcessor& primProc,
+        const GrTextureProxy* const primProcProxies[],
         const GrPipeline& pipeline,
         GrProgramDesc* desc,
         GrMtlGpu* gpu) {
-    GrMtlPipelineStateBuilder builder(primProc, pipeline, desc, gpu);
+    GrMtlPipelineStateBuilder builder(primProc, primProcProxies, pipeline, desc, gpu);
 
     if (!builder.emitAndInstallProcs()) {
         return nullptr;
@@ -30,10 +31,11 @@
 }
 
 GrMtlPipelineStateBuilder::GrMtlPipelineStateBuilder(const GrPrimitiveProcessor& primProc,
+                                                     const GrTextureProxy* const primProcProxies[],
                                                      const GrPipeline& pipeline,
                                                      GrProgramDesc* desc,
                                                      GrMtlGpu* gpu)
-        : INHERITED(primProc, pipeline, desc)
+        : INHERITED(primProc, primProcProxies, pipeline, desc)
         , fGpu(gpu)
         , fUniformHandler(this)
         , fVaryingHandler(this) {
diff --git a/src/gpu/mtl/GrMtlUniformHandler.h b/src/gpu/mtl/GrMtlUniformHandler.h
index 4a66f22..226b4a4 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.h
+++ b/src/gpu/mtl/GrMtlUniformHandler.h
@@ -59,10 +59,10 @@
                                           int arrayCount,
                                           const char** outName) override;
 
-    SamplerHandle addSampler(GrSwizzle swizzle,
-                             GrTextureType type,
-                             GrSLPrecision precision,
-                             const char* name) override;
+    SamplerHandle addSampler(const GrTexture*,
+                             const GrSamplerState&,
+                             const char* name,
+                             const GrShaderCaps*) override;
 
     int numSamplers() const { return fSamplers.count(); }
     const GrShaderVar& samplerVariable(SamplerHandle handle) const override {
diff --git a/src/gpu/mtl/GrMtlUniformHandler.mm b/src/gpu/mtl/GrMtlUniformHandler.mm
index 961457e..0d27eab 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.mm
+++ b/src/gpu/mtl/GrMtlUniformHandler.mm
@@ -6,6 +6,8 @@
 */
 
 #include "GrMtlUniformHandler.h"
+#include "GrTexture.h"
+#include "GrTexturePriv.h"
 #include "glsl/GrGLSLProgramBuilder.h"
 
 // TODO: this class is basically copy and pasted from GrVklUniformHandler so that we can have
@@ -249,15 +251,19 @@
     return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1);
 }
 
-GrGLSLUniformHandler::SamplerHandle GrMtlUniformHandler::addSampler(GrSwizzle swizzle,
-                                                                    GrTextureType type,
-                                                                    GrSLPrecision precision,
-                                                                    const char* name) {
+GrGLSLUniformHandler::SamplerHandle GrMtlUniformHandler::addSampler(const GrTexture* texture,
+                                                                    const GrSamplerState&,
+                                                                    const char* name,
+                                                                    const GrShaderCaps* caps) {
     SkASSERT(name && strlen(name));
     SkString mangleName;
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
+    GrSLPrecision precision = GrSLSamplerPrecision(texture->config());
+    GrSwizzle swizzle = caps->configTextureSwizzle(texture->config());
+    GrTextureType type = texture->texturePriv().textureType();
+
     UniformInfo& info = fSamplers.push_back();
     info.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type));
     info.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 9ffd8f8..1d6d585 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -580,7 +580,6 @@
         currCmdBuf->bindInputBuffer(fGpu, binding++,
                                     static_cast<const GrVkVertexBuffer*>(instanceBuffer));
     }
-
     if (indexBuffer) {
         SkASSERT(indexBuffer);
         SkASSERT(!indexBuffer->isMapped());
@@ -601,9 +600,19 @@
 
     VkRenderPass compatibleRenderPass = cbInfo.fRenderPass->vkRenderPass();
 
+    const GrTextureProxy* const* primProcProxies = nullptr;
+    if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
+        primProcProxies = dynamicStateArrays->fPrimitiveProcessorTextures;
+    } else if (fixedDynamicState) {
+        primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
+    }
+
+    SkASSERT(SkToBool(primProcProxies) == SkToBool(primProc.numTextureSamplers()));
+
     GrVkPipelineState* pipelineState =
         fGpu->resourceProvider().findOrCreateCompatiblePipelineState(pipeline,
                                                                      primProc,
+                                                                     primProcProxies,
                                                                      primitiveType,
                                                                      compatibleRenderPass);
     if (!pipelineState) {
@@ -624,10 +633,6 @@
     // 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());
     }
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index 33e1dfd..44c8ce2 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -48,7 +48,9 @@
         return GrBackendFormat::MakeVk(this->imageFormat());
     }
     uint32_t mipLevels() const { return fInfo.fLevelCount; }
-    GrVkYcbcrConversionInfo ycbcrConversionInfo() const { return fInfo.fYcbcrConversionInfo; }
+    const GrVkYcbcrConversionInfo& ycbcrConversionInfo() const {
+        return fInfo.fYcbcrConversionInfo;
+    }
     const Resource* resource() const { return fResource; }
     bool isLinearTiled() const {
         return SkToBool(VK_IMAGE_TILING_LINEAR == fInfo.fImageTiling);
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
index adefbe9..486bd73 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.cpp
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
@@ -18,6 +18,7 @@
 GrVkPipelineState* GrVkPipelineStateBuilder::CreatePipelineState(
         GrVkGpu* gpu,
         const GrPrimitiveProcessor& primProc,
+        const GrTextureProxy* const primProcProxies[],
         const GrPipeline& pipeline,
         const GrStencilSettings& stencil,
         GrPrimitiveType primitiveType,
@@ -25,7 +26,7 @@
         VkRenderPass compatibleRenderPass) {
     // create a builder.  This will be handed off to effects so they can use it to add
     // uniforms, varyings, textures, etc
-    GrVkPipelineStateBuilder builder(gpu, pipeline, primProc, desc);
+    GrVkPipelineStateBuilder builder(gpu, pipeline, primProc, primProcProxies, desc);
 
     if (!builder.emitAndInstallProcs()) {
         return nullptr;
@@ -37,8 +38,9 @@
 GrVkPipelineStateBuilder::GrVkPipelineStateBuilder(GrVkGpu* gpu,
                                                    const GrPipeline& pipeline,
                                                    const GrPrimitiveProcessor& primProc,
+                                                   const GrTextureProxy* const primProcProxies[],
                                                    GrProgramDesc* desc)
-        : INHERITED(primProc, pipeline, desc)
+        : INHERITED(primProc, primProcProxies, pipeline, desc)
         , fGpu(gpu)
         , fVaryingHandler(this)
         , fUniformHandler(this) {}
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.h b/src/gpu/vk/GrVkPipelineStateBuilder.h
index 10ac262..8f7b00b 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.h
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.h
@@ -59,6 +59,7 @@
     */
     static GrVkPipelineState* CreatePipelineState(GrVkGpu*,
                                                   const GrPrimitiveProcessor&,
+                                                  const GrTextureProxy* const primProcProxies[],
                                                   const GrPipeline&,
                                                   const GrStencilSettings&,
                                                   GrPrimitiveType,
@@ -76,6 +77,7 @@
     GrVkPipelineStateBuilder(GrVkGpu*,
                              const GrPipeline&,
                              const GrPrimitiveProcessor&,
+                             const GrTextureProxy* const primProcProxies[],
                              GrProgramDesc*);
 
     GrVkPipelineState* finalize(const GrStencilSettings&,
diff --git a/src/gpu/vk/GrVkPipelineStateCache.cpp b/src/gpu/vk/GrVkPipelineStateCache.cpp
index 2067385..bb07827 100644
--- a/src/gpu/vk/GrVkPipelineStateCache.cpp
+++ b/src/gpu/vk/GrVkPipelineStateCache.cpp
@@ -77,6 +77,7 @@
 
 GrVkPipelineState* GrVkResourceProvider::PipelineStateCache::refPipelineState(
         const GrPrimitiveProcessor& primProc,
+        const GrTextureProxy* const primProcProxies[],
         const GrPipeline& pipeline,
         GrPrimitiveType primitiveType,
         VkRenderPass compatibleRenderPass) {
@@ -114,7 +115,8 @@
         ++fCacheMisses;
 #endif
         GrVkPipelineState* pipelineState(GrVkPipelineStateBuilder::CreatePipelineState(
-                fGpu, primProc, pipeline, stencil, primitiveType, &desc, compatibleRenderPass));
+                fGpu, primProc, primProcProxies, pipeline, stencil, primitiveType, &desc,
+                compatibleRenderPass));
         if (nullptr == pipelineState) {
             return nullptr;
         }
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 823c64a..d09dafe 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -197,9 +197,10 @@
 }
 
 GrVkPipelineState* GrVkResourceProvider::findOrCreateCompatiblePipelineState(
-        const GrPipeline& pipeline, const GrPrimitiveProcessor& proc, GrPrimitiveType primitiveType,
+        const GrPipeline& pipeline, const GrPrimitiveProcessor& proc,
+        const GrTextureProxy* const primProcProxies[], GrPrimitiveType primitiveType,
         VkRenderPass compatibleRenderPass) {
-    return fPipelineStateCache->refPipelineState(proc, pipeline, primitiveType,
+    return fPipelineStateCache->refPipelineState(proc, primProcProxies, pipeline, primitiveType,
                                                  compatibleRenderPass);
 }
 
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 8bfafc2..7771de1 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -108,10 +108,12 @@
     GrVkSamplerYcbcrConversion* findOrCreateCompatibleSamplerYcbcrConversion(
             const GrVkYcbcrConversionInfo& ycbcrInfo);
 
-    GrVkPipelineState* findOrCreateCompatiblePipelineState(const GrPipeline&,
-                                                           const GrPrimitiveProcessor&,
-                                                           GrPrimitiveType,
-                                                           VkRenderPass compatibleRenderPass);
+    GrVkPipelineState* findOrCreateCompatiblePipelineState(
+            const GrPipeline&,
+            const GrPrimitiveProcessor&,
+            const GrTextureProxy* const primProcProxies[],
+            GrPrimitiveType,
+            VkRenderPass compatibleRenderPass);
 
     void getSamplerDescriptorSetHandle(VkDescriptorType type,
                                        const GrVkUniformHandler&,
@@ -179,6 +181,7 @@
         void abandon();
         void release();
         GrVkPipelineState* refPipelineState(const GrPrimitiveProcessor&,
+                                            const GrTextureProxy* const primProcProxies[],
                                             const GrPipeline&,
                                             GrPrimitiveType,
                                             VkRenderPass compatibleRenderPass);
diff --git a/src/gpu/vk/GrVkUniformHandler.cpp b/src/gpu/vk/GrVkUniformHandler.cpp
index 370b147..3a35cfc 100644
--- a/src/gpu/vk/GrVkUniformHandler.cpp
+++ b/src/gpu/vk/GrVkUniformHandler.cpp
@@ -6,6 +6,11 @@
 */
 
 #include "GrVkUniformHandler.h"
+
+#include "GrTexturePriv.h"
+#include "GrVkGpu.h"
+#include "GrVkPipelineStateBuilder.h"
+#include "GrVkTexture.h"
 #include "glsl/GrGLSLProgramBuilder.h"
 
 // To determine whether a current offset is aligned, we can just 'and' the lowest bits with the
@@ -254,15 +259,19 @@
     return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1);
 }
 
-GrGLSLUniformHandler::SamplerHandle GrVkUniformHandler::addSampler(GrSwizzle swizzle,
-                                                                   GrTextureType type,
-                                                                   GrSLPrecision precision,
-                                                                   const char* name) {
+GrGLSLUniformHandler::SamplerHandle GrVkUniformHandler::addSampler(const GrTexture* texture,
+                                                                   const GrSamplerState& state,
+                                                                   const char* name,
+                                                                   const GrShaderCaps* shaderCaps) {
     SkASSERT(name && strlen(name));
     SkString mangleName;
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
+    GrSLPrecision precision = GrSLSamplerPrecision(texture->config());
+    GrSwizzle swizzle = shaderCaps->configTextureSwizzle(texture->config());
+    GrTextureType type = texture->texturePriv().textureType();
+
     UniformInfo& info = fSamplers.push_back();
     info.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type));
     info.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
@@ -273,6 +282,17 @@
     info.fVariable.addLayoutQualifier(layoutQualifier.c_str());
     info.fVisibility = kFragment_GrShaderFlag;
     info.fUBOffset = 0;
+
+    // Check if we are dealing with an external texture and store the needed information if so
+    const GrVkTexture* vkTexture = static_cast<const GrVkTexture*>(texture);
+    if (vkTexture->ycbcrConversionInfo().isValid()) {
+        SkASSERT(type == GrTextureType::kExternal);
+        GrVkGpu* gpu = static_cast<GrVkPipelineStateBuilder*>(fProgramBuilder)->gpu();
+        info.fImmutableSampler = gpu->resourceProvider().findOrCreateCompatibleSampler(
+                state, vkTexture->ycbcrConversionInfo());
+        SkASSERT(info.fImmutableSampler);
+    }
+
     fSamplerSwizzles.push_back(swizzle);
     SkASSERT(fSamplerSwizzles.count() == fSamplers.count());
     return GrGLSLUniformHandler::SamplerHandle(fSamplers.count() - 1);
diff --git a/src/gpu/vk/GrVkUniformHandler.h b/src/gpu/vk/GrVkUniformHandler.h
index 4333c86..73b17d2 100644
--- a/src/gpu/vk/GrVkUniformHandler.h
+++ b/src/gpu/vk/GrVkUniformHandler.h
@@ -9,8 +9,11 @@
 #define GrVkUniformHandler_DEFINED
 
 #include "GrAllocator.h"
+#include "GrSamplerState.h"
 #include "GrShaderVar.h"
+#include "GrVkSampler.h"
 #include "glsl/GrGLSLUniformHandler.h"
+#include "vk/GrVkTypes.h"
 
 class GrVkUniformHandler : public GrGLSLUniformHandler {
 public:
@@ -31,11 +34,14 @@
         kFragBinding = 1,
     };
 
-    // fUBOffset is only valid if the GrSLType of the fVariable is not a sampler
     struct UniformInfo {
-        GrShaderVar fVariable;
-        uint32_t        fVisibility;
-        uint32_t        fUBOffset;
+        GrShaderVar             fVariable;
+        uint32_t                fVisibility;
+        // fUBOffset is only valid if the GrSLType of the fVariable is not a sampler
+        uint32_t                fUBOffset;
+        // The SamplerState, maxMipLevel, and ycbcrInfo are only valid if the GrSLType is a sampler
+        // and that sampler is used for sampling an external image with a ycbcr conversion.
+        const GrVkSampler*      fImmutableSampler = nullptr;
     };
     typedef GrTAllocator<UniformInfo> UniformInfoArray;
 
@@ -64,10 +70,10 @@
                                           int arrayCount,
                                           const char** outName) override;
 
-    SamplerHandle addSampler(GrSwizzle swizzle,
-                             GrTextureType type,
-                             GrSLPrecision precision,
-                             const char* name) override;
+    SamplerHandle addSampler(const GrTexture* texture,
+                             const GrSamplerState&,
+                             const char* name,
+                             const GrShaderCaps*) override;
 
     int numSamplers() const { return fSamplers.count(); }
     const GrShaderVar& samplerVariable(SamplerHandle handle) const override {
@@ -80,6 +86,10 @@
         return fSamplers[handle.toIndex()].fVisibility;
     }
 
+    const GrVkSampler* immutableSampler(UniformHandle u) const {
+        return fSamplers[u.toIndex()].fImmutableSampler;
+    }
+
     void appendUniformDecls(GrShaderFlags, SkString*) const override;
 
     bool hasGeometryUniforms() const { return fCurrentGeometryUBOOffset > 0; }