Switch from querying swizzle on caps to using swizzles stored on proxies.

Change-Id: I03f4a3affd6dda7a83bee8eec768dcaa93a6b801
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/220534
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/gm/clockwise.cpp b/gm/clockwise.cpp
index 02ef01e..24886ff 100644
--- a/gm/clockwise.cpp
+++ b/gm/clockwise.cpp
@@ -160,7 +160,8 @@
         if (!vertexBuffer) {
             return;
         }
-        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kPlus);
+        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kPlus,
+                            flushState->drawOpArgs().fOutputSwizzle);
         GrMesh mesh(GrPrimitiveType::kTriangleStrip);
         mesh.setNonIndexedNonInstanced(4);
         mesh.setVertexData(std::move(vertexBuffer));
diff --git a/gm/fwidth_squircle.cpp b/gm/fwidth_squircle.cpp
index 267e065..92b92f0 100644
--- a/gm/fwidth_squircle.cpp
+++ b/gm/fwidth_squircle.cpp
@@ -169,7 +169,8 @@
         if (!vertexBuffer) {
             return;
         }
-        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver);
+        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
+                            flushState->drawOpArgs().fOutputSwizzle);
         GrMesh mesh(GrPrimitiveType::kTriangleStrip);
         mesh.setNonIndexedNonInstanced(4);
         mesh.setVertexData(std::move(vertexBuffer));
diff --git a/gm/samplelocations.cpp b/gm/samplelocations.cpp
index 273d627..4bab565 100644
--- a/gm/samplelocations.cpp
+++ b/gm/samplelocations.cpp
@@ -227,6 +227,7 @@
         );
 
         GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
+                            flushState->drawOpArgs().fOutputSwizzle,
                             GrPipeline::InputFlags::kHWAntialias, &kStencilWrite);
 
         GrMesh mesh(GrPrimitiveType::kTriangleStrip);
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index 6ce53fb..5eb5955 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -338,7 +338,8 @@
         GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH));
     }
 
-    GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kPlus);
+    GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kPlus,
+                        state->drawOpArgs().fOutputSwizzle);
 
     std::unique_ptr<GrCCCoverageProcessor> proc;
     if (state->caps().shaderCaps()->geometryShaderSupport()) {
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 7fbf793..0a37d62 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -316,7 +316,19 @@
 
 bool GrCaps::canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                             const SkIRect& srcRect, const SkIPoint& dstPoint) const {
-    return dst->readOnly() ? false : this->onCanCopySurface(dst, src, srcRect, dstPoint);
+    if (dst->readOnly()) {
+        return false;
+    }
+    // Currently we only ever do copies where the configs are the same. This check really should be
+    // checking if the backend formats, color types, and swizzle are compatible. Our copy always
+    // copies exact byte to byte from src to dst so when need to check the if we do this, the dst
+    // has the expected values stored in the right places taking the swizzle into account. For now
+    // we can be more restrictive and just make sure the configs are the same and if we generalize
+    // copies and swizzles more in the future this can be updated.
+    if (dst->config() != src->config()) {
+        return false;
+    }
+    return this->onCanCopySurface(dst, src, srcRect, dstPoint);
 }
 
 bool GrCaps::validateSurfaceDesc(const GrSurfaceDesc& desc, GrMipMapped mipped) const {
diff --git a/src/gpu/GrFragmentProcessor.h b/src/gpu/GrFragmentProcessor.h
index 44004c0..5789a1e 100644
--- a/src/gpu/GrFragmentProcessor.h
+++ b/src/gpu/GrFragmentProcessor.h
@@ -448,6 +448,7 @@
 
     GrTextureProxy* proxy() const { return fProxy.get(); }
     const GrSamplerState& samplerState() const { return fSamplerState; }
+    const GrSwizzle& swizzle() const { return this->proxy()->textureSwizzle(); }
 
     bool isInitialized() const { return SkToBool(fProxy.get()); }
 
diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp
index 996ea15..5c13afe 100644
--- a/src/gpu/GrOpFlushState.cpp
+++ b/src/gpu/GrOpFlushState.cpp
@@ -44,6 +44,7 @@
     pipelineArgs.fCaps = &this->caps();
     pipelineArgs.fResourceProvider = this->resourceProvider();
     pipelineArgs.fUserStencil = stencilSettings;
+    pipelineArgs.fOutputSwizzle = this->drawOpArgs().fOutputSwizzle;
     GrPipeline pipeline(pipelineArgs, std::move(processorSet), this->detachAppliedClip());
 
     while (fCurrDraw != fDraws.end() && fCurrDraw->fOp == op) {
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 5e81e4a..a990553 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -64,6 +64,7 @@
         // TODO: do we still need the dst proxy here?
         GrRenderTargetProxy* fProxy;
         GrAppliedClip* fAppliedClip;
+        GrSwizzle fOutputSwizzle;
         GrXferProcessor::DstProxy fDstProxy;
     };
 
diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp
index 895c223..9a338f3 100644
--- a/src/gpu/GrPipeline.cpp
+++ b/src/gpu/GrPipeline.cpp
@@ -18,7 +18,8 @@
 
 GrPipeline::GrPipeline(const InitArgs& args,
                        GrProcessorSet&& processors,
-                       GrAppliedClip&& appliedClip) {
+                       GrAppliedClip&& appliedClip)
+        : fOutputSwizzle(args.fOutputSwizzle) {
     SkASSERT(processors.isFinalized());
 
     fFlags = (Flags)args.fInputFlags;
@@ -93,14 +94,16 @@
     return this->getXferProcessor().xferBarrierType(caps);
 }
 
-GrPipeline::GrPipeline(GrScissorTest scissorTest, SkBlendMode blendmode, InputFlags inputFlags,
+GrPipeline::GrPipeline(GrScissorTest scissorTest, SkBlendMode blendmode,
+                       const GrSwizzle& outputSwizzle, InputFlags inputFlags,
                        const GrUserStencilSettings* userStencil)
         : fWindowRectsState()
         , fUserStencilSettings(userStencil)
         , fFlags((Flags)inputFlags)
         , fXferProcessor(GrPorterDuffXPFactory::MakeNoCoverageXP(blendmode))
         , fFragmentProcessors()
-        , fNumColorProcessors(0) {
+        , fNumColorProcessors(0)
+        , fOutputSwizzle(outputSwizzle) {
     if (GrScissorTest::kEnabled == scissorTest) {
         fFlags |= Flags::kScissorEnabled;
     }
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index a582e3b..de68e7f 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -61,6 +61,7 @@
         const GrCaps* fCaps = nullptr;
         GrResourceProvider* fResourceProvider = nullptr;
         GrXferProcessor::DstProxy fDstProxy;
+        GrSwizzle fOutputSwizzle;
     };
 
     /**
@@ -96,7 +97,8 @@
      * must be "Porter Duff" (<= kLastCoeffMode). If using GrScissorTest::kEnabled, the caller must
      * specify a scissor rectangle through the DynamicState struct.
      **/
-    GrPipeline(GrScissorTest, SkBlendMode, InputFlags = InputFlags::kNone,
+    GrPipeline(GrScissorTest, SkBlendMode, const GrSwizzle& outputSwizzle,
+               InputFlags = InputFlags::kNone,
                const GrUserStencilSettings* = &GrUserStencilSettings::kUnused);
 
     GrPipeline(const InitArgs&, GrProcessorSet&&, GrAppliedClip&&);
@@ -189,6 +191,8 @@
     // Used by Vulkan and Metal to cache their respective pipeline objects
     uint32_t getBlendInfoKey() const;
 
+    const GrSwizzle& outputSwizzle() const { return fOutputSwizzle; }
+
 private:
     void markAsBad() { fFlags |= Flags::kIsBad; }
 
@@ -218,6 +222,8 @@
 
     // This value is also the index in fFragmentProcessors where coverage processors begin.
     int fNumColorProcessors;
+
+    GrSwizzle fOutputSwizzle;
 };
 
 GR_MAKE_BITFIELD_CLASS_OPS(GrPipeline::InputFlags);
diff --git a/src/gpu/GrPrimitiveProcessor.cpp b/src/gpu/GrPrimitiveProcessor.cpp
index 2060f3a..32790b9 100644
--- a/src/gpu/GrPrimitiveProcessor.cpp
+++ b/src/gpu/GrPrimitiveProcessor.cpp
@@ -56,24 +56,28 @@
 GrPrimitiveProcessor::TextureSampler::TextureSampler(GrTextureType textureType,
                                                      GrPixelConfig config,
                                                      const GrSamplerState& samplerState,
+                                                     const GrSwizzle& swizzle,
                                                      uint32_t extraSamplerKey) {
-    this->reset(textureType, config, samplerState, extraSamplerKey);
+    this->reset(textureType, config, samplerState, swizzle, extraSamplerKey);
 }
 
 GrPrimitiveProcessor::TextureSampler::TextureSampler(GrTextureType textureType,
                                                      GrPixelConfig config,
                                                      GrSamplerState::Filter filterMode,
-                                                     GrSamplerState::WrapMode wrapXAndY) {
-    this->reset(textureType, config, filterMode, wrapXAndY);
+                                                     GrSamplerState::WrapMode wrapXAndY,
+                                                     const GrSwizzle& swizzle) {
+    this->reset(textureType, config, filterMode, wrapXAndY, swizzle);
 }
 
 void GrPrimitiveProcessor::TextureSampler::reset(GrTextureType textureType,
                                                  GrPixelConfig config,
                                                  const GrSamplerState& samplerState,
+                                                 const GrSwizzle& swizzle,
                                                  uint32_t extraSamplerKey) {
     SkASSERT(kUnknown_GrPixelConfig != config);
     fSamplerState = samplerState;
     fSamplerState.setFilterMode(clamp_filter(textureType, samplerState.filter()));
+    fSwizzle = swizzle;
     fTextureType = textureType;
     fConfig = config;
     fExtraSamplerKey = extraSamplerKey;
@@ -83,10 +87,12 @@
 void GrPrimitiveProcessor::TextureSampler::reset(GrTextureType textureType,
                                                  GrPixelConfig config,
                                                  GrSamplerState::Filter filterMode,
-                                                 GrSamplerState::WrapMode wrapXAndY) {
+                                                 GrSamplerState::WrapMode wrapXAndY,
+                                                 const GrSwizzle& swizzle) {
     SkASSERT(kUnknown_GrPixelConfig != config);
     filterMode = clamp_filter(textureType, filterMode);
     fSamplerState = GrSamplerState(wrapXAndY, filterMode);
+    fSwizzle = swizzle;
     fTextureType = textureType;
     fConfig = config;
 }
diff --git a/src/gpu/GrPrimitiveProcessor.h b/src/gpu/GrPrimitiveProcessor.h
index 4fa67f6..83b161f 100644
--- a/src/gpu/GrPrimitiveProcessor.h
+++ b/src/gpu/GrPrimitiveProcessor.h
@@ -254,24 +254,27 @@
 public:
     TextureSampler() = default;
 
-    TextureSampler(GrTextureType, GrPixelConfig, const GrSamplerState&, uint32_t extraSamplerKey);
+    TextureSampler(GrTextureType, GrPixelConfig, const GrSamplerState&, const GrSwizzle&,
+                   uint32_t extraSamplerKey);
 
-    explicit TextureSampler(GrTextureType, GrPixelConfig,
-                            GrSamplerState::Filter = GrSamplerState::Filter::kNearest,
-                            GrSamplerState::WrapMode wrapXAndY = GrSamplerState::WrapMode::kClamp);
+    explicit TextureSampler(GrTextureType, GrPixelConfig, GrSamplerState::Filter,
+                            GrSamplerState::WrapMode wrapXAndY, const GrSwizzle&);
 
     TextureSampler(const TextureSampler&) = delete;
     TextureSampler& operator=(const TextureSampler&) = delete;
 
-    void reset(GrTextureType, GrPixelConfig, const GrSamplerState&, uint32_t extraSamplerKey = 0);
+    void reset(GrTextureType, GrPixelConfig, const GrSamplerState&, const GrSwizzle&,
+               uint32_t extraSamplerKey = 0);
     void reset(GrTextureType, GrPixelConfig,
                GrSamplerState::Filter,
-               GrSamplerState::WrapMode wrapXAndY);
+               GrSamplerState::WrapMode wrapXAndY,
+               const GrSwizzle& swizzle);
 
     GrTextureType textureType() const { return fTextureType; }
     GrPixelConfig config() const { return fConfig; }
 
     const GrSamplerState& samplerState() const { return fSamplerState; }
+    const GrSwizzle& swizzle() const { return fSwizzle; }
 
     uint32_t extraSamplerKey() const { return fExtraSamplerKey; }
 
@@ -279,6 +282,7 @@
 
 private:
     GrSamplerState fSamplerState;
+    GrSwizzle fSwizzle;
     GrTextureType fTextureType = GrTextureType::k2D;
     GrPixelConfig fConfig = kUnknown_GrPixelConfig;
     uint32_t fExtraSamplerKey = 0;
diff --git a/src/gpu/GrProgramDesc.cpp b/src/gpu/GrProgramDesc.cpp
index 05f8505..0ea5417 100644
--- a/src/gpu/GrProgramDesc.cpp
+++ b/src/gpu/GrProgramDesc.cpp
@@ -39,14 +39,14 @@
     return SkToU16(value);
 }
 
-static uint32_t sampler_key(GrTextureType textureType, GrPixelConfig config,
-                            const GrShaderCaps& caps) {
+static uint32_t sampler_key(GrTextureType textureType, const GrSwizzle& swizzle,
+                            GrPixelConfig config, const GrShaderCaps& caps) {
     int samplerTypeKey = texture_type_key(textureType);
 
-    GR_STATIC_ASSERT(2 == sizeof(caps.configTextureSwizzle(config).asKey()));
+    GR_STATIC_ASSERT(2 == sizeof(swizzle.asKey()));
     uint16_t swizzleKey = 0;
     if (caps.textureSwizzleAppliedInShader()) {
-        swizzleKey = caps.configTextureSwizzle(config).asKey();
+        swizzleKey = swizzle.asKey();
     }
     return SkToU32(samplerTypeKey |
                    swizzleKey << kSamplerOrImageTypeKeyBits |
@@ -63,7 +63,8 @@
     for (int i = 0; i < numTextureSamplers; ++i) {
         const GrFragmentProcessor::TextureSampler& sampler = fp.textureSampler(i);
         const GrTexture* tex = sampler.peekTexture();
-        k32[i] = sampler_key(tex->texturePriv().textureType(), tex->config(), caps);
+        k32[i] = sampler_key(tex->texturePriv().textureType(), sampler.swizzle(), tex->config(),
+                             caps);
         uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
                 sampler.samplerState(), sampler.proxy()->backendFormat());
         if (extraSamplerKey) {
@@ -86,7 +87,7 @@
     uint32_t* k32 = b->add32n(numTextureSamplers);
     for (int i = 0; i < numTextureSamplers; ++i) {
         const GrPrimitiveProcessor::TextureSampler& sampler = pp.textureSampler(i);
-        k32[i] = sampler_key(sampler.textureType(), sampler.config(), caps);
+        k32[i] = sampler_key(sampler.textureType(), sampler.swizzle(), sampler.config(), caps);
         uint32_t extraSamplerKey = sampler.extraSamplerKey();
         if (extraSamplerKey) {
             SkASSERT(sampler.textureType() == GrTextureType::kExternal);
@@ -245,7 +246,7 @@
 
     // make sure any padding in the header is zeroed.
     memset(header, 0, kHeaderSize);
-    header->fOutputSwizzle = shaderCaps.configOutputSwizzle(renderTarget->config()).asKey();
+    header->fOutputSwizzle = pipeline.outputSwizzle().asKey();
     header->fColorFragmentProcessorCnt = pipeline.numColorFragmentProcessors();
     header->fCoverageFragmentProcessorCnt = pipeline.numCoverageFragmentProcessors();
     // Fail if the client requested more processors than the key can fit.
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index f07d77d..080893e 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -418,6 +418,7 @@
                 chain.head(),
                 fTarget->asRenderTargetProxy(),
                 chain.appliedClip(),
+                fTarget.get()->asRenderTargetProxy()->outputSwizzle(),
                 chain.dstProxy()
             };
             flushState->setOpArgs(&opArgs);
@@ -503,6 +504,7 @@
             chain.head(),
             fTarget->asRenderTargetProxy(),
             chain.appliedClip(),
+            fTarget.get()->asRenderTargetProxy()->outputSwizzle(),
             chain.dstProxy()
         };
 
diff --git a/src/gpu/GrRenderTargetProxy.h b/src/gpu/GrRenderTargetProxy.h
index 84d003a..cbc4f68 100644
--- a/src/gpu/GrRenderTargetProxy.h
+++ b/src/gpu/GrRenderTargetProxy.h
@@ -125,7 +125,6 @@
     // in the constructors, and always looks for the full 16 byte alignment, even if the fields in
     // that particular class don't require it. Changing the size of this object can move the start
     // address of other types, leading to this problem.
-
     int                fSampleCnt;
     GrSwizzle          fOutputSwizzle;
     bool               fNeedsStencil;
diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h
index 53af67d..1a03b52 100644
--- a/src/gpu/GrShaderCaps.h
+++ b/src/gpu/GrShaderCaps.h
@@ -222,22 +222,8 @@
 
     int maxFragmentSamplers() const { return fMaxFragmentSamplers; }
 
-    /**
-     * Given a texture's config, this determines what swizzle must be appended to accesses to the
-     * texture in generated shader code. Swizzling may be implemented in texture parameters or a
-     * sampler rather than in the shader. In this case the returned swizzle will always be "rgba".
-     */
-    const GrSwizzle& configTextureSwizzle(GrPixelConfig config) const {
-        return fConfigTextureSwizzle[config];
-    }
-
     bool textureSwizzleAppliedInShader() const { return fTextureSwizzleAppliedInShader; }
 
-    /** Swizzle that should occur on the fragment shader outputs for a given config. */
-    const GrSwizzle& configOutputSwizzle(GrPixelConfig config) const {
-        return fConfigOutputSwizzle[config];
-    }
-
     GrGLSLGeneration generation() const { return fGLSLGeneration; }
 
 private:
@@ -309,9 +295,6 @@
 
     AdvBlendEqInteraction fAdvBlendEqInteraction;
 
-    GrSwizzle fConfigTextureSwizzle[kGrPixelConfigCnt];
-    GrSwizzle fConfigOutputSwizzle[kGrPixelConfigCnt];
-
     friend class GrCaps;  // For initialization.
     friend class GrGLCaps;
     friend class GrMockCaps;
diff --git a/src/gpu/GrSurfaceContext.cpp b/src/gpu/GrSurfaceContext.cpp
index c12cc2e..b2ca779 100644
--- a/src/gpu/GrSurfaceContext.cpp
+++ b/src/gpu/GrSurfaceContext.cpp
@@ -548,6 +548,7 @@
 
     SkASSERT(src->backendFormat().textureType() != GrTextureType::kExternal);
     SkASSERT(src->origin() == this->asSurfaceProxy()->origin());
+    SkASSERT(src->config() == this->asSurfaceProxy()->config());
 
     GrSurfaceProxy* dst = this->asSurfaceProxy();
 
diff --git a/src/gpu/GrTextureOpList.cpp b/src/gpu/GrTextureOpList.cpp
index 86f8f60..fb63e76 100644
--- a/src/gpu/GrTextureOpList.cpp
+++ b/src/gpu/GrTextureOpList.cpp
@@ -85,6 +85,7 @@
                 fRecordedOps[i].get(),
                 nullptr,
                 nullptr,
+                GrSwizzle(),
                 GrXferProcessor::DstProxy()
             };
             flushState->setOpArgs(&opArgs);
@@ -115,6 +116,7 @@
             fRecordedOps[i].get(),
             nullptr,
             nullptr,
+            GrSwizzle(),
             GrXferProcessor::DstProxy()
         };
         flushState->setOpArgs(&opArgs);
diff --git a/src/gpu/ccpr/GrCCDrawPathsOp.cpp b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
index 612251b..189081f 100644
--- a/src/gpu/ccpr/GrCCDrawPathsOp.cpp
+++ b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
@@ -412,6 +412,7 @@
     initArgs.fCaps = &flushState->caps();
     initArgs.fResourceProvider = flushState->resourceProvider();
     initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
+    initArgs.fOutputSwizzle = flushState->drawOpArgs().fOutputSwizzle;
     auto clip = flushState->detachAppliedClip();
     GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
     GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
@@ -426,7 +427,8 @@
         SkASSERT(atlas->isInstantiated());
 
         GrCCPathProcessor pathProc(
-                atlas->peekTexture(), atlas->origin(), fViewMatrixIfUsingLocalCoords);
+                atlas->peekTexture(), atlas->textureSwizzle(), atlas->origin(),
+                fViewMatrixIfUsingLocalCoords);
         GrTextureProxy* atlasProxy = range.fAtlasProxy;
         fixedDynamicState.fPrimitiveProcessorTextures = &atlasProxy;
         pathProc.drawPaths(flushState, pipeline, &fixedDynamicState, *resources, baseInstance,
diff --git a/src/gpu/ccpr/GrCCFiller.cpp b/src/gpu/ccpr/GrCCFiller.cpp
index a73dfa7..ba79a04 100644
--- a/src/gpu/ccpr/GrCCFiller.cpp
+++ b/src/gpu/ccpr/GrCCFiller.cpp
@@ -468,7 +468,8 @@
     GrResourceProvider* rp = flushState->resourceProvider();
     const PrimitiveTallies& batchTotalCounts = fBatches[batchID].fTotalPrimitiveCounts;
 
-    GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus);
+    GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus,
+                        flushState->drawOpArgs().fOutputSwizzle);
 
     if (batchTotalCounts.fTriangles) {
         proc->reset(PrimitiveType::kTriangles, rp);
diff --git a/src/gpu/ccpr/GrCCPathProcessor.cpp b/src/gpu/ccpr/GrCCPathProcessor.cpp
index 3cd07ed..72a0932 100644
--- a/src/gpu/ccpr/GrCCPathProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPathProcessor.cpp
@@ -79,11 +79,12 @@
     }
 }
 
-GrCCPathProcessor::GrCCPathProcessor(const GrTexture* atlasTexture, GrSurfaceOrigin atlasOrigin,
+GrCCPathProcessor::GrCCPathProcessor(const GrTexture* atlasTexture, const GrSwizzle& swizzle,
+                                     GrSurfaceOrigin atlasOrigin,
                                      const SkMatrix& viewMatrixIfUsingLocalCoords)
         : INHERITED(kGrCCPathProcessor_ClassID)
         , fAtlasAccess(atlasTexture->texturePriv().textureType(), atlasTexture->config(),
-                       GrSamplerState::Filter::kNearest, GrSamplerState::WrapMode::kClamp)
+                       GrSamplerState::Filter::kNearest, GrSamplerState::WrapMode::kClamp, swizzle)
         , fAtlasSize(SkISize::Make(atlasTexture->width(), atlasTexture->height()))
         , fAtlasOrigin(atlasOrigin) {
     // TODO: Can we just assert that atlas has GrCCAtlas::kTextureOrigin and remove fAtlasOrigin?
diff --git a/src/gpu/ccpr/GrCCPathProcessor.h b/src/gpu/ccpr/GrCCPathProcessor.h
index 1243340..7a5d8f6 100644
--- a/src/gpu/ccpr/GrCCPathProcessor.h
+++ b/src/gpu/ccpr/GrCCPathProcessor.h
@@ -53,7 +53,7 @@
     static sk_sp<const GrGpuBuffer> FindVertexBuffer(GrOnFlushResourceProvider*);
     static sk_sp<const GrGpuBuffer> FindIndexBuffer(GrOnFlushResourceProvider*);
 
-    GrCCPathProcessor(const GrTexture* atlasTexture, GrSurfaceOrigin atlasOrigin,
+    GrCCPathProcessor(const GrTexture* atlasTexture, const GrSwizzle&, GrSurfaceOrigin atlasOrigin,
                       const SkMatrix& viewMatrixIfUsingLocalCoords = SkMatrix::I());
 
     const char* name() const override { return "GrCCPathProcessor"; }
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.cpp b/src/gpu/ccpr/GrCCPerFlushResources.cpp
index 95cbaf0..a4ed663 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.cpp
+++ b/src/gpu/ccpr/GrCCPerFlushResources.cpp
@@ -82,9 +82,11 @@
         auto srcProxy = fSrcProxy.get();
         SkASSERT(srcProxy->isInstantiated());
 
-        GrCCPathProcessor pathProc(srcProxy->peekTexture(), srcProxy->origin());
+        GrCCPathProcessor pathProc(srcProxy->peekTexture(), srcProxy->textureSwizzle(),
+                                   srcProxy->origin());
 
-        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc);
+        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc,
+                            flushState->drawOpArgs().fOutputSwizzle);
         GrPipeline::FixedDynamicState dynamicState;
         dynamicState.fPrimitiveProcessorTextures = &srcProxy;
 
diff --git a/src/gpu/ccpr/GrCCStroker.cpp b/src/gpu/ccpr/GrCCStroker.cpp
index 1f21719..a34a4c6 100644
--- a/src/gpu/ccpr/GrCCStroker.cpp
+++ b/src/gpu/ccpr/GrCCStroker.cpp
@@ -689,7 +689,8 @@
     startIndices[(int)GrScissorTest::kEnabled] = (!startScissorSubBatch)
             ? &fZeroTallies : fScissorSubBatches[startScissorSubBatch - 1].fEndInstances;
 
-    GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus);
+    GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus,
+                        flushState->drawOpArgs().fOutputSwizzle);
 
     // Draw linear strokes.
     this->appendStrokeMeshesToBuffers(0, batch, startIndices, startScissorSubBatch, drawBounds);
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp
index edd5809..5ae4dfd 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.cpp
+++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp
@@ -154,7 +154,8 @@
     for (int i = 0; i < numActiveProxies; ++i) {
         SkASSERT(proxies[i]);
         SkASSERT(proxies[i]->isize() == fAtlasSize);
-        fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
+        fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params,
+                                  proxies[i]->textureSwizzle());
     }
     this->setTextureSamplerCnt(numActiveProxies);
 }
@@ -173,7 +174,8 @@
         SkASSERT(proxies[i]->isize() == fAtlasSize);
 
         if (!fTextureSamplers[i].isInitialized()) {
-            fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
+            fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params,
+                                      proxies[i]->textureSwizzle());
         }
     }
     this->setTextureSamplerCnt(numActiveProxies);
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
index 209de7a..92d404a 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.cpp
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
@@ -241,7 +241,8 @@
     for (int i = 0; i < numProxies; ++i) {
         SkASSERT(proxies[i]);
         SkASSERT(proxies[i]->isize() == fAtlasSize);
-        fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
+        fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params,
+                                  proxies[i]->textureSwizzle());
     }
     this->setTextureSamplerCnt(numProxies);
 }
@@ -259,7 +260,8 @@
         SkASSERT(proxies[i]);
         SkASSERT(proxies[i]->isize() == fAtlasSize);
         if (!fTextureSamplers[i].isInitialized()) {
-            fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
+            fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params,
+                                      proxies[i]->textureSwizzle());
         }
     }
     this->setTextureSamplerCnt(numProxies);
@@ -535,7 +537,8 @@
     for (int i = 0; i < numProxies; ++i) {
         SkASSERT(proxies[i]);
         SkASSERT(proxies[i]->isize() == fAtlasSize);
-        fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
+        fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params,
+                                  proxies[i]->textureSwizzle());
     }
     this->setTextureSamplerCnt(numProxies);
 }
@@ -554,7 +557,8 @@
         SkASSERT(proxies[i]->isize() == fAtlasSize);
 
         if (!fTextureSamplers[i].isInitialized()) {
-            fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
+            fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params,
+                                      proxies[i]->textureSwizzle());
         }
     }
     this->setTextureSamplerCnt(numProxies);
@@ -857,7 +861,8 @@
     for (int i = 0; i < numProxies; ++i) {
         SkASSERT(proxies[i]);
         SkASSERT(proxies[i]->isize() == fAtlasSize);
-        fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
+        fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params,
+                                  proxies[i]->textureSwizzle());
     }
     this->setTextureSamplerCnt(numProxies);
 }
@@ -876,7 +881,8 @@
         SkASSERT(proxies[i]->isize() == fAtlasSize);
 
         if (!fTextureSamplers[i].isInitialized()) {
-            fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
+            fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params,
+                                      proxies[i]->textureSwizzle());
         }
     }
     this->setTextureSamplerCnt(numProxies);
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index a5991e4..7dbf8c3 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -1528,7 +1528,6 @@
     fConfigTable[kUnknown_GrPixelConfig].fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = 0;
     fConfigTable[kUnknown_GrPixelConfig].fFormats.fExternalType = 0;
     fConfigTable[kUnknown_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType;
-    shaderCaps->fConfigTextureSwizzle[kUnknown_GrPixelConfig] = GrSwizzle::RGBA();
 
     fConfigTable[kRGBA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA;
     fConfigTable[kRGBA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA8;
@@ -1553,7 +1552,6 @@
     if (texStorageSupported) {
         fConfigTable[kRGBA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kRGBA_8888_GrPixelConfig] = GrSwizzle::RGBA();
 
     fConfigTable[kRGB_888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGB;
     fConfigTable[kRGB_888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGB8;
@@ -1587,13 +1585,11 @@
     if (texStorageSupported) {
         fConfigTable[kRGB_888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kRGB_888_GrPixelConfig] = GrSwizzle::RGBA();
     if (disableRGB8ForMali400) {
         fConfigTable[kRGB_888_GrPixelConfig].fFlags = 0;
     }
 
     fConfigTable[kRGB_888X_GrPixelConfig] = fConfigTable[kRGBA_8888_GrPixelConfig];
-    shaderCaps->fConfigTextureSwizzle[kRGB_888X_GrPixelConfig] = GrSwizzle::RGB1();
     // Currently we don't allow RGB_888X to be renderable because we don't have a way to handle
     // blends that reference the dst alpha when the values in the dst alpha channel are
     // uninitialized.
@@ -1614,7 +1610,6 @@
     } else {
         fConfigTable[kRG_88_GrPixelConfig].fFlags = 0;
     }
-    shaderCaps->fConfigTextureSwizzle[kRG_88_GrPixelConfig] = GrSwizzle::RGBA();
 
     fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] =
         GR_GL_BGRA;
@@ -1678,7 +1673,6 @@
     if (texStorageSupported && supportsBGRATexStorage) {
         fConfigTable[kBGRA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kBGRA_8888_GrPixelConfig] = GrSwizzle::RGBA();
 
     // We only enable srgb support if both textures and FBOs support srgb.
     if (GR_IS_GR_GL(standard)) {
@@ -1741,7 +1735,6 @@
     if (texStorageSupported && !disablePerFormatTextureStorageForCommandBufferES2) {
         fConfigTable[kSRGBA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kSRGBA_8888_GrPixelConfig] = GrSwizzle::RGBA();
     // sBGRA is not a "real" thing in OpenGL, but GPUs support it, and on platforms where
     // kN32 == BGRA, we need some way to work with it. (The default framebuffer on Windows
     // is in this format, for example).
@@ -1761,7 +1754,6 @@
     if (texStorageSupported) {
         fConfigTable[kSBGRA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kSBGRA_8888_GrPixelConfig] = GrSwizzle::RGBA();
 
     fConfigTable[kRGB_565_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGB;
     if (this->ES2CompatibilitySupport()) {
@@ -1793,7 +1785,6 @@
     if (texStorageSupported && GR_IS_GR_GL_ES(standard)) {
         fConfigTable[kRGB_565_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kRGB_565_GrPixelConfig] = GrSwizzle::RGBA();
 
     fConfigTable[kRGBA_4444_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA;
     fConfigTable[kRGBA_4444_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA4;
@@ -1814,7 +1805,6 @@
     if (texStorageSupported) {
         fConfigTable[kRGBA_4444_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kRGBA_4444_GrPixelConfig] = GrSwizzle::RGBA();
 
     fConfigTable[kRGBA_1010102_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA;
     fConfigTable[kRGBA_1010102_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGB10_A2;
@@ -1835,7 +1825,6 @@
     if (texStorageSupported) {
         fConfigTable[kRGBA_1010102_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kRGBA_1010102_GrPixelConfig] = GrSwizzle::RGBA();
 
     bool alpha8IsValidForGL = GR_IS_GR_GL(standard) &&
             (!fIsCoreProfile || version <= GR_GL_VER(3, 0));
@@ -1851,7 +1840,6 @@
     alphaInfo.fFormats.fBaseInternalFormat = GR_GL_ALPHA;
     alphaInfo.fFormats.fSizedInternalFormat = GR_GL_ALPHA8;
     alphaInfo.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_ALPHA;
-    shaderCaps->fConfigTextureSwizzle[kAlpha_8_as_Alpha_GrPixelConfig] = GrSwizzle::AAAA();
     if (fAlpha8IsRenderable && alpha8IsValidForGL) {
         alphaInfo.fFlags |= allRenderFlags;
     }
@@ -1862,7 +1850,6 @@
     redInfo.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_RED;
     redInfo.fFormats.fExternalType = GR_GL_UNSIGNED_BYTE;
     redInfo.fFormatType = kNormalizedFixedPoint_FormatType;
-    shaderCaps->fConfigTextureSwizzle[kAlpha_8_as_Red_GrPixelConfig] = GrSwizzle::RRRR();
 
     // ES2 Command Buffer does not allow TexStorage with R8_EXT (so Alpha_8 and Gray_8)
     if (texStorageSupported && !disablePerFormatTextureStorageForCommandBufferES2) {
@@ -1875,14 +1862,10 @@
     if (textureRedSupport) {
         redInfo.fFlags |= ConfigInfo::kTextureable_Flag | allRenderFlags;
         fConfigTable[kAlpha_8_GrPixelConfig] = redInfo;
-        shaderCaps->fConfigTextureSwizzle[kAlpha_8_GrPixelConfig] =
-                shaderCaps->fConfigTextureSwizzle[kAlpha_8_as_Red_GrPixelConfig];
     } else {
         redInfo.fFlags = 0;
 
         fConfigTable[kAlpha_8_GrPixelConfig] = alphaInfo;
-        shaderCaps->fConfigTextureSwizzle[kAlpha_8_GrPixelConfig] =
-                shaderCaps->fConfigTextureSwizzle[kAlpha_8_as_Alpha_GrPixelConfig];
     }
 
     ConfigInfo& grayLumInfo = fConfigTable[kGray_8_as_Lum_GrPixelConfig];
@@ -1891,7 +1874,6 @@
     grayLumInfo.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_LUMINANCE;
     grayLumInfo.fFormats.fExternalType = GR_GL_UNSIGNED_BYTE;
     grayLumInfo.fFormatType = kNormalizedFixedPoint_FormatType;
-    shaderCaps->fConfigTextureSwizzle[kGray_8_as_Lum_GrPixelConfig] = GrSwizzle::RGBA();
 
     if ((GR_IS_GR_GL(standard) && version <= GR_GL_VER(3, 0)) ||
         (GR_IS_GR_GL_ES(standard) && version < GR_GL_VER(3, 0)) ||
@@ -1906,7 +1888,6 @@
     grayRedInfo.fFormats.fExternalType = GR_GL_UNSIGNED_BYTE;
     grayRedInfo.fFormatType = kNormalizedFixedPoint_FormatType;
     grayRedInfo.fFlags = ConfigInfo::kTextureable_Flag;
-    shaderCaps->fConfigTextureSwizzle[kGray_8_as_Red_GrPixelConfig] = GrSwizzle::RRRA();
 
     // Leaving Gray8 as non-renderable, to keep things simple and match raster. However, we do
     // enable the FBOColorAttachment_Flag so that we can bind it to an FBO for copies.
@@ -1927,13 +1908,9 @@
 
     if (textureRedSupport) {
         fConfigTable[kGray_8_GrPixelConfig] = grayRedInfo;
-        shaderCaps->fConfigTextureSwizzle[kGray_8_GrPixelConfig] =
-                shaderCaps->fConfigTextureSwizzle[kGray_8_as_Red_GrPixelConfig];
     } else {
         grayRedInfo.fFlags = 0;
         fConfigTable[kGray_8_GrPixelConfig] = grayLumInfo;
-        shaderCaps->fConfigTextureSwizzle[kGray_8_GrPixelConfig] =
-                shaderCaps->fConfigTextureSwizzle[kGray_8_as_Lum_GrPixelConfig];
     }
 
     // Check for [half] floating point texture support
@@ -2030,7 +2007,6 @@
         if (texStorageSupported) {
             fConfigTable[fpconfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
         }
-        shaderCaps->fConfigTextureSwizzle[fpconfig] = GrSwizzle::RGBA();
     }
 
     GrGLenum redHalfExternalType;
@@ -2046,7 +2022,6 @@
     redHalf.fFormats.fBaseInternalFormat = GR_GL_RED;
     redHalf.fFormats.fSizedInternalFormat = GR_GL_R16F;
     redHalf.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_RED;
-    shaderCaps->fConfigTextureSwizzle[kAlpha_half_as_Red_GrPixelConfig] = GrSwizzle::RRRR();
     if (textureRedSupport && hasFP16Textures) {
         redHalf.fFlags = ConfigInfo::kTextureable_Flag;
 
@@ -2059,8 +2034,6 @@
         }
     }
     fConfigTable[kAlpha_half_GrPixelConfig] = redHalf;
-    shaderCaps->fConfigTextureSwizzle[kAlpha_half_GrPixelConfig] =
-            shaderCaps->fConfigTextureSwizzle[kAlpha_half_as_Red_GrPixelConfig];
 
     fConfigTable[kRGBA_half_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA;
     fConfigTable[kRGBA_half_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA16F;
@@ -2083,7 +2056,6 @@
     if (texStorageSupported && !disablePerFormatTextureStorageForCommandBufferES2) {
         fConfigTable[kRGBA_half_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
-    shaderCaps->fConfigTextureSwizzle[kRGBA_half_GrPixelConfig] = GrSwizzle::RGBA();
 
     // kRGBA_half_Clamped is just distinguished by clamps added to the shader. At the API level,
     // it's identical to kRGBA_half.
@@ -2122,7 +2094,6 @@
             fConfigTable[kRGB_ETC1_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag;
         }
     } // No WebGL support
-    shaderCaps->fConfigTextureSwizzle[kRGB_ETC1_GrPixelConfig] = GrSwizzle::RGBA();
 
     // 16 bit formats
     {
@@ -2169,9 +2140,6 @@
             if (r16AndRG1616Supported) {
                 r16Info.fFlags = ConfigInfo::kTextureable_Flag | allRenderFlags;
             }
-            // We should only ever be sampling the R channel of this format so don't bother
-            // with a fancy swizzle.
-            shaderCaps->fConfigTextureSwizzle[kR_16_GrPixelConfig] = GrSwizzle::RGBA();
         }
 
         {
@@ -2185,9 +2153,6 @@
             if (r16AndRG1616Supported) {
                 rg1616Info.fFlags = ConfigInfo::kTextureable_Flag | allRenderFlags;
             }
-            // We should only ever be sampling the R and G channels of this format so don't bother
-            // with a fancy swizzle.
-            shaderCaps->fConfigTextureSwizzle[kRG_1616_GrPixelConfig] = GrSwizzle::RGBA();
         }
 
         // Experimental (for Y416)
@@ -2202,7 +2167,6 @@
             if (rgba16161616Supported) {
                 rgba16161616Info.fFlags = ConfigInfo::kTextureable_Flag | allRenderFlags;
             }
-            shaderCaps->fConfigTextureSwizzle[kRGBA_16161616_GrPixelConfig] = GrSwizzle::RGBA();
         }
     }
 
@@ -2226,13 +2190,8 @@
                 rgHalf.fFlags |= fpRenderFlags;
             }
         }
-        // We should only ever be sampling the R and G channels of this format so don't bother
-        // with a fancy swizzle.
-        shaderCaps->fConfigTextureSwizzle[kRG_half_GrPixelConfig] = GrSwizzle::RGBA();
     }
 
-    // Bulk populate the texture internal/external formats here and then deal with exceptions below.
-
     // ES 2.0 requires that the internal/external formats match.
     bool useSizedTexFormats = (GR_IS_GR_GL(standard) ||
                               (GR_IS_GR_GL_ES(standard) && version >= GR_GL_VER(3,0)));
@@ -2306,19 +2265,6 @@
         }
     }
 
-    // Shader output swizzles will default to RGBA. When we've use GL_RED instead of GL_ALPHA to
-    // implement kAlpha_8_GrPixelConfig we need to swizzle the shader outputs so the alpha channel
-    // gets written to the single component.
-    if (textureRedSupport) {
-        for (int i = 0; i < kGrPixelConfigCnt; ++i) {
-            GrPixelConfig config = static_cast<GrPixelConfig>(i);
-            if (GrPixelConfigIsAlphaOnly(config) &&
-                fConfigTable[i].fFormats.fBaseInternalFormat == GR_GL_RED) {
-                shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA();
-            }
-        }
-    }
-
     for (int i = 0; i < kGrPixelConfigCnt; ++i) {
         if (ConfigInfo::kRenderableWithMSAA_Flag & fConfigTable[i].fFlags) {
             // We assume that MSAA rendering is supported only if we support non-MSAA rendering.
@@ -2531,12 +2477,6 @@
     SkASSERT((dstSampleCnt > 0) == SkToBool(dst->asRenderTargetProxy()));
     SkASSERT((srcSampleCnt > 0) == SkToBool(src->asRenderTargetProxy()));
 
-    // None of our copy methods can handle a swizzle.
-    if (this->shaderCaps()->configOutputSwizzle(src->config()) !=
-        this->shaderCaps()->configOutputSwizzle(dst->config())) {
-        return false;
-    }
-
     const GrTextureProxy* dstTex = dst->asTextureProxy();
     const GrTextureProxy* srcTex = src->asTextureProxy();
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 160a0b9..7d5bcf4 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -2052,9 +2052,7 @@
     this->flushProgram(std::move(program));
 
     // Swizzle the blend to match what the shader will output.
-    const GrSwizzle& swizzle = this->caps()->shaderCaps()->configOutputSwizzle(
-        renderTarget->config());
-    this->flushBlend(blendInfo, swizzle);
+    this->flushBlend(blendInfo, pipeline.outputSwizzle());
 
     fHWProgram->updateUniformsAndTextureBindings(renderTarget, origin,
                                                  primProc, pipeline, primProcProxiesToBind);
@@ -2981,7 +2979,8 @@
     }
 }
 
-void GrGLGpu::bindTexture(int unitIdx, GrSamplerState samplerState, GrGLTexture* texture) {
+void GrGLGpu::bindTexture(int unitIdx, GrSamplerState samplerState, const GrSwizzle& swizzle,
+                          GrGLTexture* texture) {
     SkASSERT(texture);
 
 #ifdef SK_DEBUG
@@ -3088,7 +3087,6 @@
     const GrGLTextureParameters::NonsamplerState& oldNonsamplerState =
             texture->parameters()->nonsamplerState();
     if (!this->caps()->shaderCaps()->textureSwizzleAppliedInShader()) {
-        const auto& swizzle = this->caps()->shaderCaps()->configTextureSwizzle(texture->config());
         newNonsamplerState.fSwizzleKey = swizzle.asKey();
         if (setAll || swizzle.asKey() != oldNonsamplerState.fSwizzleKey) {
             GrGLenum glValues[4];
@@ -3351,12 +3349,6 @@
 
 bool GrGLGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                             const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) {
-    // None of our copy methods can handle a swizzle.
-    if (this->caps()->shaderCaps()->configOutputSwizzle(src->config()) !=
-        this->caps()->shaderCaps()->configOutputSwizzle(dst->config())) {
-        return false;
-    }
-
     // Don't prefer copying as a draw if the dst doesn't already have a FBO object.
     // This implicitly handles this->glCaps().useDrawInsteadOfAllRenderTargetWrites().
     bool preferCopy = SkToBool(dst->asRenderTarget());
@@ -3667,7 +3659,8 @@
     }
     int w = srcRect.width();
     int h = srcRect.height();
-    this->bindTexture(0, GrSamplerState::ClampNearest(), srcTex);
+    // We don't swizzle at all in our copies.
+    this->bindTexture(0, GrSamplerState::ClampNearest(), GrSwizzle::RGBA(), srcTex);
     this->bindSurfaceFBOForPixelOps(dst, GR_GL_FRAMEBUFFER, kDst_TempFBOTarget);
     this->flushViewport(dst->width(), dst->height());
     fHWBoundRenderTargetUniqueID.makeInvalid();
@@ -3813,7 +3806,8 @@
     // Bind the texture, to get things configured for filtering.
     // We'll be changing our base level further below:
     this->setTextureUnit(0);
-    this->bindTexture(0, GrSamplerState::ClampBilerp(), glTex);
+    // The mipmap program does not do any swizzling.
+    this->bindTexture(0, GrSamplerState::ClampBilerp(), GrSwizzle::RGBA(), glTex);
 
     // Vertex data:
     if (!fMipmapProgramArrayBuffer) {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 23b5fb6..9b25de0 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -57,7 +57,7 @@
     }
 
     // Used by GrGLProgram to configure OpenGL state.
-    void bindTexture(int unitIdx, GrSamplerState samplerState, GrGLTexture* texture);
+    void bindTexture(int unitIdx, GrSamplerState samplerState, const GrSwizzle&, GrGLTexture*);
 
     // These functions should be used to bind GL objects. They track the GL state and skip redundant
     // bindings. Making the equivalent glBind calls directly will confuse the state tracking.
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 7583ce0..e96ffa3 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -101,6 +101,7 @@
     fXferProcessor->setData(fProgramDataManager, xp, dstTexture, offset);
     if (dstTexture) {
         fGpu->bindTexture(nextTexSamplerIdx++, GrSamplerState::ClampNearest(),
+                          pipeline.dstTextureProxy()->textureSwizzle(),
                           static_cast<GrGLTexture*>(dstTexture));
     }
     SkASSERT(nextTexSamplerIdx == fNumTextureSamplers);
@@ -110,7 +111,8 @@
                                                           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);
+        fGpu->bindTexture(i, primProc.textureSampler(i).samplerState(),
+                          primProc.textureSampler(i).swizzle(), tex);
     }
 }
 
@@ -123,7 +125,7 @@
         glslFP->setData(fProgramDataManager, *fp);
         for (int i = 0; i < fp->numTextureSamplers(); ++i) {
             const GrFragmentProcessor::TextureSampler& sampler = fp->textureSampler(i);
-            fGpu->bindTexture((*nextTexSamplerIdx)++, sampler.samplerState(),
+            fGpu->bindTexture((*nextTexSamplerIdx)++, sampler.samplerState(), sampler.swizzle(),
                               static_cast<GrGLTexture*>(sampler.peekTexture()));
         }
         fp = iter.next();
diff --git a/src/gpu/gl/GrGLUniformHandler.cpp b/src/gpu/gl/GrGLUniformHandler.cpp
index dc75504..256eaef 100644
--- a/src/gpu/gl/GrGLUniformHandler.cpp
+++ b/src/gpu/gl/GrGLUniformHandler.cpp
@@ -61,6 +61,7 @@
 
 GrGLSLUniformHandler::SamplerHandle GrGLUniformHandler::addSampler(const GrTexture* texture,
                                                                    const GrSamplerState&,
+                                                                   const GrSwizzle& swizzle,
                                                                    const char* name,
                                                                    const GrShaderCaps* shaderCaps) {
     SkASSERT(name && strlen(name));
@@ -69,7 +70,6 @@
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
-    GrSwizzle swizzle = shaderCaps->configTextureSwizzle(texture->config());
     GrTextureType type = texture->texturePriv().textureType();
 
     UniformInfo& sampler = fSamplers.push_back();
diff --git a/src/gpu/gl/GrGLUniformHandler.h b/src/gpu/gl/GrGLUniformHandler.h
index e0ed776..32ae6ed 100644
--- a/src/gpu/gl/GrGLUniformHandler.h
+++ b/src/gpu/gl/GrGLUniformHandler.h
@@ -38,8 +38,8 @@
                                           int arrayCount,
                                           const char** outName) override;
 
-    SamplerHandle addSampler(const GrTexture*, const GrSamplerState&, const char* name,
-                             const GrShaderCaps*) override;
+    SamplerHandle addSampler(const GrTexture*, const GrSamplerState&, const GrSwizzle&,
+                             const char* name, const GrShaderCaps*) override;
 
     const char* samplerVariable(SamplerHandle handle) const override {
         return fSamplers[handle.toIndex()].fVariable.c_str();
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index d1b5bac..3376268 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -109,6 +109,7 @@
         SkASSERT(sampler.config() == texture->config());
         texSamplers[i] = this->emitSampler(texture,
                                            sampler.samplerState(),
+                                           sampler.swizzle(),
                                            name.c_str());
     }
 
@@ -191,6 +192,7 @@
             const auto& sampler = subFP->textureSampler(i);
             texSamplers.emplace_back(this->emitSampler(sampler.peekTexture(),
                                                        sampler.samplerState(),
+                                                       sampler.swizzle(),
                                                        name.c_str()));
         }
     }
@@ -245,9 +247,10 @@
 
     if (GrTexture* dstTexture = fPipeline.peekDstTexture()) {
         // GrProcessor::TextureSampler sampler(dstTexture);
-        SkString name("DstTextureSampler");
+        SkASSERT(fPipeline.dstTextureProxy());
+        const GrSwizzle& swizzle = fPipeline.dstTextureProxy()->textureSwizzle();
         dstTextureSamplerHandle =
-                this->emitSampler(dstTexture, GrSamplerState(), "DstTextureSampler");
+                this->emitSampler(dstTexture, GrSamplerState(), swizzle, "DstTextureSampler");
         dstTextureOrigin = fPipeline.dstTextureProxy()->origin();
         SkASSERT(dstTexture->texturePriv().textureType() != GrTextureType::kExternal);
     }
@@ -284,9 +287,10 @@
 
 GrGLSLProgramBuilder::SamplerHandle GrGLSLProgramBuilder::emitSampler(const GrTexture* texture,
                                                                       const GrSamplerState& state,
+                                                                      const GrSwizzle& swizzle,
                                                                       const char* name) {
     ++fNumFragmentSamplers;
-    return this->uniformHandler()->addSampler(texture, state, name, this->shaderCaps());
+    return this->uniformHandler()->addSampler(texture, state, swizzle, name, this->shaderCaps());
 }
 
 bool GrGLSLProgramBuilder::checkSamplerCounts() {
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index 7563a92..c44f90e 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -158,7 +158,8 @@
                                     SkString output,
                                     SkTArray<std::unique_ptr<GrGLSLFragmentProcessor>>*);
     void emitAndInstallXferProc(const SkString& colorIn, const SkString& coverageIn);
-    SamplerHandle emitSampler(const GrTexture*, const GrSamplerState&, const char* name);
+    SamplerHandle emitSampler(const GrTexture*, const GrSamplerState&, const GrSwizzle&,
+                              const char* name);
     bool checkSamplerCounts();
 
 #ifdef SK_DEBUG
diff --git a/src/gpu/glsl/GrGLSLUniformHandler.h b/src/gpu/glsl/GrGLSLUniformHandler.h
index 288e07a..3380e70 100644
--- a/src/gpu/glsl/GrGLSLUniformHandler.h
+++ b/src/gpu/glsl/GrGLSLUniformHandler.h
@@ -79,8 +79,8 @@
     // Only called if GrShaderCaps(:textureSwizzleAppliedInShader() == true.
     virtual GrSwizzle samplerSwizzle(SamplerHandle) const = 0;
 
-    virtual SamplerHandle addSampler(const GrTexture*, const GrSamplerState&, const char* name,
-                                     const GrShaderCaps*) = 0;
+    virtual SamplerHandle addSampler(const GrTexture*, const GrSamplerState&, const GrSwizzle&,
+                                     const char* name, const GrShaderCaps*) = 0;
 
     virtual UniformHandle internalAddUniformArray(uint32_t visibility,
                                                   GrSLType type,
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index 9c747d6..89460f8 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -259,23 +259,6 @@
 void GrMtlCaps::initShaderCaps() {
     GrShaderCaps* shaderCaps = fShaderCaps.get();
 
-    // fConfigOutputSwizzle will default to RGBA so we only need to set it for alpha only config.
-    for (int i = 0; i < kGrPixelConfigCnt; ++i) {
-        GrPixelConfig config = static_cast<GrPixelConfig>(i);
-        if (GrPixelConfigIsAlphaOnly(config)) {
-            shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRR();
-            shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA();
-        } else {
-            if (kGray_8_GrPixelConfig == config) {
-                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRA();
-            } else if (kRGB_888X_GrPixelConfig == config || kRGB_888_GrPixelConfig == config ) {
-                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGB1();
-            } else {
-                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA();
-            }
-        }
-    }
-
     // Setting this true with the assumption that this cap will eventually mean we support varying
     // precisions and not just via modifiers.
     shaderCaps->fUsesPrecisionModifiers = true;
diff --git a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
index 331f427..155bc16 100644
--- a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
+++ b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
@@ -157,7 +157,7 @@
     SkASSERT(fActiveRenderCmdEncoder);
 
     [fActiveRenderCmdEncoder setRenderPipelineState:pipelineState->mtlPipelineState()];
-    pipelineState->setDrawState(fActiveRenderCmdEncoder, fRenderTarget->config(),
+    pipelineState->setDrawState(fActiveRenderCmdEncoder, pipeline.outputSwizzle(),
                                 pipeline.getXferProcessor());
 
     bool dynamicScissor =
@@ -187,7 +187,7 @@
             }
 
             [fActiveRenderCmdEncoder setRenderPipelineState:pipelineState->mtlPipelineState()];
-            pipelineState->setDrawState(fActiveRenderCmdEncoder, fRenderTarget->config(),
+            pipelineState->setDrawState(fActiveRenderCmdEncoder, pipeline.outputSwizzle(),
                                         pipeline.getXferProcessor());
         }
 
diff --git a/src/gpu/mtl/GrMtlPipelineState.h b/src/gpu/mtl/GrMtlPipelineState.h
index 5bbd5db..6e39f15 100644
--- a/src/gpu/mtl/GrMtlPipelineState.h
+++ b/src/gpu/mtl/GrMtlPipelineState.h
@@ -51,7 +51,8 @@
                  const GrPrimitiveProcessor& primPRoc, const GrPipeline& pipeline,
                  const GrTextureProxy* const primProcTextures[]);
 
-    void setDrawState(id<MTLRenderCommandEncoder>, GrPixelConfig, const GrXferProcessor&);
+    void setDrawState(id<MTLRenderCommandEncoder>, const GrSwizzle& outputSwizzle,
+                      const GrXferProcessor&);
 
     static void SetDynamicScissorRectState(id<MTLRenderCommandEncoder> renderCmdEncoder,
                                            const GrRenderTarget* renderTarget,
@@ -101,7 +102,7 @@
 
     void bind(id<MTLRenderCommandEncoder>);
 
-    void setBlendConstants(id<MTLRenderCommandEncoder>, GrPixelConfig, const GrXferProcessor&);
+    void setBlendConstants(id<MTLRenderCommandEncoder>, const GrSwizzle&, const GrXferProcessor&);
 
     void setDepthStencilState(id<MTLRenderCommandEncoder> renderCmdEncoder);
 
diff --git a/src/gpu/mtl/GrMtlPipelineState.mm b/src/gpu/mtl/GrMtlPipelineState.mm
index 5eb8fa2..9e3ac53 100644
--- a/src/gpu/mtl/GrMtlPipelineState.mm
+++ b/src/gpu/mtl/GrMtlPipelineState.mm
@@ -114,9 +114,10 @@
 }
 
 void GrMtlPipelineState::setDrawState(id<MTLRenderCommandEncoder> renderCmdEncoder,
-                                      GrPixelConfig config, const GrXferProcessor& xferProcessor) {
+                                      const GrSwizzle& outputSwizzle,
+                                      const GrXferProcessor& xferProcessor) {
     this->bind(renderCmdEncoder);
-    this->setBlendConstants(renderCmdEncoder, config, xferProcessor);
+    this->setBlendConstants(renderCmdEncoder, outputSwizzle, xferProcessor);
     this->setDepthStencilState(renderCmdEncoder);
 }
 
@@ -167,7 +168,7 @@
 }
 
 void GrMtlPipelineState::setBlendConstants(id<MTLRenderCommandEncoder> renderCmdEncoder,
-                                           GrPixelConfig config,
+                                           const GrSwizzle& swizzle,
                                            const GrXferProcessor& xferProcessor) {
     if (!renderCmdEncoder) {
         return;
@@ -179,7 +180,6 @@
     GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
     if (blend_coeff_refs_constant(srcCoeff) || blend_coeff_refs_constant(dstCoeff)) {
         // Swizzle the blend to match what the shader will output.
-        const GrSwizzle& swizzle = fGpu->caps()->shaderCaps()->configOutputSwizzle(config);
         SkPMColor4f blendConst = swizzle.applyTo(blendInfo.fBlendConstant);
 
         [renderCmdEncoder setBlendColorRed: blendConst.fR
diff --git a/src/gpu/mtl/GrMtlUniformHandler.h b/src/gpu/mtl/GrMtlUniformHandler.h
index ee7469d..0c31db6 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.h
+++ b/src/gpu/mtl/GrMtlUniformHandler.h
@@ -62,6 +62,7 @@
 
     SamplerHandle addSampler(const GrTexture*,
                              const GrSamplerState&,
+                             const GrSwizzle&,
                              const char* name,
                              const GrShaderCaps*) override;
 
diff --git a/src/gpu/mtl/GrMtlUniformHandler.mm b/src/gpu/mtl/GrMtlUniformHandler.mm
index 7d90bb4..346f9a3 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.mm
+++ b/src/gpu/mtl/GrMtlUniformHandler.mm
@@ -258,6 +258,7 @@
 
 GrGLSLUniformHandler::SamplerHandle GrMtlUniformHandler::addSampler(const GrTexture* texture,
                                                                     const GrSamplerState&,
+                                                                    const GrSwizzle& swizzle,
                                                                     const char* name,
                                                                     const GrShaderCaps* caps) {
     SkASSERT(name && strlen(name));
@@ -265,7 +266,6 @@
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
-    GrSwizzle swizzle = caps->configTextureSwizzle(texture->config());
     GrTextureType type = texture->texturePriv().textureType();
 
     UniformInfo& info = fSamplers.push_back();
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
index 0367876..57f4546 100644
--- a/src/gpu/ops/GrDrawPathOp.cpp
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -51,6 +51,7 @@
     args.fCaps = &state.caps();
     args.fResourceProvider = state.resourceProvider();
     args.fDstProxy = state.drawOpArgs().fDstProxy;
+    args.fOutputSwizzle = state.drawOpArgs().fOutputSwizzle;
     return args;
 }
 
diff --git a/src/gpu/ops/GrFillRRectOp.cpp b/src/gpu/ops/GrFillRRectOp.cpp
index d8f8487..82e286b 100644
--- a/src/gpu/ops/GrFillRRectOp.cpp
+++ b/src/gpu/ops/GrFillRRectOp.cpp
@@ -746,6 +746,7 @@
     initArgs.fCaps = &flushState->caps();
     initArgs.fResourceProvider = flushState->resourceProvider();
     initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
+    initArgs.fOutputSwizzle = flushState->drawOpArgs().fOutputSwizzle;
     auto clip = flushState->detachAppliedClip();
     GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
     GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index c133de4..923fd98 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -101,7 +101,7 @@
         uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(samplerState,
                                                                      proxy->backendFormat());
 
-        fSampler.reset(proxy->textureType(), proxy->config(), samplerState,
+        fSampler.reset(proxy->textureType(), proxy->config(), samplerState, proxy->textureSwizzle(),
                        extraSamplerKey);
         this->setTextureSamplerCnt(1);
         fInPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp
index 9bb9a85..5b22fc2 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.cpp
+++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp
@@ -858,11 +858,11 @@
     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
                                            GrTextureType textureType, GrPixelConfig textureConfig,
                                            const GrSamplerState& samplerState,
-                                           uint32_t extraSamplerKey,
+                                           const GrSwizzle& swizzle, uint32_t extraSamplerKey,
                                            sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
-                vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
-                std::move(textureColorSpaceXform)));
+                vertexSpec, caps, textureType, textureConfig, samplerState, swizzle,
+                extraSamplerKey, std::move(textureColorSpaceXform)));
     }
 
     const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
@@ -1053,11 +1053,12 @@
     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
                                    GrTextureType textureType, GrPixelConfig textureConfig,
                                    const GrSamplerState& samplerState,
+                                   const GrSwizzle& swizzle,
                                    uint32_t extraSamplerKey,
                                    sk_sp<GrColorSpaceXform> textureColorSpaceXform)
             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
-            , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
+            , fSampler(textureType, textureConfig, samplerState, swizzle, extraSamplerKey) {
         SkASSERT(spec.hasLocalCoords());
         this->initializeAttrs(spec);
         this->setTextureSamplerCnt(1);
@@ -1136,10 +1137,10 @@
 
 sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
         GrTextureType textureType, GrPixelConfig textureConfig,
-        const GrSamplerState& samplerState, uint32_t extraSamplerKey,
+        const GrSamplerState& samplerState, const GrSwizzle& swizzle, uint32_t extraSamplerKey,
         sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
     return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
-                                                samplerState, extraSamplerKey,
+                                                samplerState, swizzle, extraSamplerKey,
                                                 std::move(textureColorSpaceXform));
 }
 
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h
index d643c2f..ba74aab 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.h
+++ b/src/gpu/ops/GrQuadPerEdgeAA.h
@@ -83,7 +83,7 @@
 
     sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec,
             const GrShaderCaps& caps, GrTextureType textureType, GrPixelConfig textureConfig,
-            const GrSamplerState& samplerState, uint32_t extraSamplerKey,
+            const GrSamplerState& samplerState, const GrSwizzle& swizzle, uint32_t extraSamplerKey,
             sk_sp<GrColorSpaceXform> textureColorSpaceXform);
 
     // Fill vertices with the vertex data needed to represent the given quad. The device position,
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 692500f..f583777 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -405,6 +405,7 @@
         int numTotalQuads = 0;
         auto textureType = fProxies[0].fProxy->textureType();
         auto config = fProxies[0].fProxy->config();
+        const GrSwizzle& swizzle = fProxies[0].fProxy->textureSwizzle();
         GrAAType aaType = this->aaType();
         for (const auto& op : ChainRange<TextureOp>(this)) {
             if (op.fQuads.quadType() > quadType) {
@@ -428,6 +429,7 @@
                 }
                 SkASSERT(proxy->config() == config);
                 SkASSERT(proxy->textureType() == textureType);
+                SkASSERT(proxy->textureSwizzle() == swizzle);
             }
             if (op.aaType() == GrAAType::kCoverage) {
                 SkASSERT(aaType == GrAAType::kCoverage || aaType == GrAAType::kNone);
@@ -446,7 +448,7 @@
 
         sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
                 vertexSpec, *target->caps().shaderCaps(),
-                textureType, config, samplerState, extraSamplerKey,
+                textureType, config, samplerState, swizzle, extraSamplerKey,
                 std::move(fTextureColorSpaceXform));
 
         // We'll use a dynamic state array for the GP textures when there are multiple ops.
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 3c5ec88..a88051c 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -137,11 +137,6 @@
         return false;
     }
 
-    if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
-        this->shaderCaps()->configOutputSwizzle(dstConfig)) {
-        return false;
-    }
-
     return true;
 }
 
@@ -160,11 +155,6 @@
         return false;
     }
 
-    if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
-        this->shaderCaps()->configOutputSwizzle(dstConfig)) {
-        return false;
-    }
-
     // We cannot blit images that are multisampled. Will need to figure out if we can blit the
     // resolved msaa though.
     if (dstSampleCnt > 1 || srcSampleCnt > 1) {
@@ -566,34 +556,6 @@
     GrShaderCaps* shaderCaps = fShaderCaps.get();
     shaderCaps->fVersionDeclString = "#version 330\n";
 
-
-    // fConfigOutputSwizzle will default to RGBA so we only need to set it for alpha only config.
-    for (int i = 0; i < kGrPixelConfigCnt; ++i) {
-        GrPixelConfig config = static_cast<GrPixelConfig>(i);
-        // Vulkan doesn't support a single channel format stored in alpha.
-        if (GrPixelConfigIsAlphaOnly(config) &&
-            kAlpha_8_as_Alpha_GrPixelConfig != config) {
-            shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRR();
-            shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA();
-        } else {
-            if (kGray_8_GrPixelConfig == config ||
-                kGray_8_as_Red_GrPixelConfig == config) {
-                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRA();
-            } else if (kRGBA_4444_GrPixelConfig == config) {
-                // The vulkan spec does not require R4G4B4A4 to be supported for texturing so we
-                // store the data in a B4G4R4A4 texture and then swizzle it when doing texture reads
-                // or writing to outputs. Since we're not actually changing the data at all, the
-                // only extra work is the swizzle in the shader for all operations.
-                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::BGRA();
-                shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::BGRA();
-            } else if (kRGB_888X_GrPixelConfig == config) {
-                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGB1();
-            } else {
-                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA();
-            }
-        }
-    }
-
     // Vulkan is based off ES 3.0 so the following should all be supported
     shaderCaps->fUsesPrecisionModifiers = true;
     shaderCaps->fFlatInterpolationSupport = true;
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 10682c6..422c95a 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -759,7 +759,7 @@
     }
     GrVkPipeline::SetDynamicViewportState(fGpu, cbInfo.currentCmdBuf(), fRenderTarget);
     GrVkPipeline::SetDynamicBlendConstantState(fGpu, cbInfo.currentCmdBuf(),
-                                               fRenderTarget->config(),
+                                               pipeline.outputSwizzle(),
                                                pipeline.getXferProcessor());
 
     return pipelineState;
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index 830ace4..3f70d37 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -626,7 +626,7 @@
 
 void GrVkPipeline::SetDynamicBlendConstantState(GrVkGpu* gpu,
                                                 GrVkCommandBuffer* cmdBuffer,
-                                                GrPixelConfig pixelConfig,
+                                                const GrSwizzle& swizzle,
                                                 const GrXferProcessor& xferProcessor) {
     GrXferProcessor::BlendInfo blendInfo;
     xferProcessor.getBlendInfo(&blendInfo);
@@ -635,7 +635,6 @@
     float floatColors[4];
     if (blend_coeff_refs_constant(srcCoeff) || blend_coeff_refs_constant(dstCoeff)) {
         // Swizzle the blend to match what the shader will output.
-        const GrSwizzle& swizzle = gpu->caps()->shaderCaps()->configOutputSwizzle(pixelConfig);
         SkPMColor4f blendConst = swizzle.applyTo(blendInfo.fBlendConstant);
         floatColors[0] = blendConst.fR;
         floatColors[1] = blendConst.fG;
diff --git a/src/gpu/vk/GrVkPipeline.h b/src/gpu/vk/GrVkPipeline.h
index 04c3472..b7b295e 100644
--- a/src/gpu/vk/GrVkPipeline.h
+++ b/src/gpu/vk/GrVkPipeline.h
@@ -42,7 +42,8 @@
     static void SetDynamicScissorRectState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*,
                                            GrSurfaceOrigin, SkIRect);
     static void SetDynamicViewportState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*);
-    static void SetDynamicBlendConstantState(GrVkGpu*, GrVkCommandBuffer*, GrPixelConfig,
+    static void SetDynamicBlendConstantState(GrVkGpu*, GrVkCommandBuffer*,
+                                             const GrSwizzle& outputSwizzle,
                                              const GrXferProcessor&);
 
 #ifdef SK_TRACE_VK_RESOURCES
diff --git a/src/gpu/vk/GrVkUniformHandler.cpp b/src/gpu/vk/GrVkUniformHandler.cpp
index 0182a80..2613550 100644
--- a/src/gpu/vk/GrVkUniformHandler.cpp
+++ b/src/gpu/vk/GrVkUniformHandler.cpp
@@ -258,6 +258,7 @@
 
 GrGLSLUniformHandler::SamplerHandle GrVkUniformHandler::addSampler(const GrTexture* texture,
                                                                    const GrSamplerState& state,
+                                                                   const GrSwizzle& swizzle,
                                                                    const char* name,
                                                                    const GrShaderCaps* shaderCaps) {
     SkASSERT(name && strlen(name));
@@ -265,7 +266,6 @@
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
-    GrSwizzle swizzle = shaderCaps->configTextureSwizzle(texture->config());
     GrTextureType type = texture->texturePriv().textureType();
 
     UniformInfo& info = fSamplers.push_back();
diff --git a/src/gpu/vk/GrVkUniformHandler.h b/src/gpu/vk/GrVkUniformHandler.h
index a435203..8d80f20 100644
--- a/src/gpu/vk/GrVkUniformHandler.h
+++ b/src/gpu/vk/GrVkUniformHandler.h
@@ -71,6 +71,7 @@
 
     SamplerHandle addSampler(const GrTexture* texture,
                              const GrSamplerState&,
+                             const GrSwizzle&,
                              const char* name,
                              const GrShaderCaps*) override;
 
diff --git a/tests/DrawOpAtlasTest.cpp b/tests/DrawOpAtlasTest.cpp
index 7f08741..f688dbd 100644
--- a/tests/DrawOpAtlasTest.cpp
+++ b/tests/DrawOpAtlasTest.cpp
@@ -219,6 +219,7 @@
         op.get(),
         rtc->asRenderTargetProxy(),
         nullptr,
+        rtc->asRenderTargetProxy()->outputSwizzle(),
         GrXferProcessor::DstProxy(nullptr, SkIPoint::Make(0, 0))
     };
 
diff --git a/tests/GrMeshTest.cpp b/tests/GrMeshTest.cpp
index 2930867..440dc34 100644
--- a/tests/GrMeshTest.cpp
+++ b/tests/GrMeshTest.cpp
@@ -384,7 +384,7 @@
 }
 
 void DrawMeshHelper::drawMesh(const GrMesh& mesh) {
-    GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc);
+    GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc, GrSwizzle::RGBA());
     GrMeshTestProcessor mtp(mesh.isInstanced(), mesh.hasVertexData());
     fState->rtCommandBuffer()->draw(mtp, pipeline, nullptr, nullptr, &mesh, 1,
                                     SkRect::MakeIWH(kImageWidth, kImageHeight));
diff --git a/tests/GrPipelineDynamicStateTest.cpp b/tests/GrPipelineDynamicStateTest.cpp
index fc3f8f9..fb892b7 100644
--- a/tests/GrPipelineDynamicStateTest.cpp
+++ b/tests/GrPipelineDynamicStateTest.cpp
@@ -140,7 +140,7 @@
     }
     void onPrepare(GrOpFlushState*) override {}
     void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
-        GrPipeline pipeline(fScissorTest, SkBlendMode::kSrc);
+        GrPipeline pipeline(fScissorTest, SkBlendMode::kSrc, state->drawOpArgs().fOutputSwizzle);
         SkSTArray<kNumMeshes, GrMesh> meshes;
         for (int i = 0; i < kNumMeshes; ++i) {
             GrMesh& mesh = meshes.emplace_back(GrPrimitiveType::kTriangleStrip);