Revert "Move GrDomainEffect functionality into GrTextureEffect and delete"

This reverts commit 988af46dd203ec03d6bc3ac91bb6520dbb032ebf.

Reason for revert: <INSERT REASONING HERE>

Original change's description:
> Move GrDomainEffect functionality into GrTextureEffect and delete
> the former.
> 
> New factories for GrTextureEffect have two main variants:
> MakeTexelSubset(): adjusts the input integer rectangle to account for
> filtering
> 
> MakeSubset(): assumes caller has calculated the exact rectangle needed
> as floats.
> 
> Currently this disables filtering for shader-based mirroring or repeat.
> Will fix this later. The old effect also did not support this.
> 
> Change-Id: If47d8ecfbb349b0d7b39ab5ba864fe3cc1b139e4
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/265518
> Commit-Queue: Brian Salomon <bsalomon@google.com>
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>

TBR=bsalomon@google.com,michaelludwig@google.com

Change-Id: Ib81a79798668cd7df7d07f72a5113be9fc74180c
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266536
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index ee06e6b..5400492 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -25,6 +25,7 @@
 #include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/GrSamplerState.h"
 #include "src/gpu/GrTextureProxy.h"
+#include "src/gpu/effects/GrTextureDomain.h"
 #include "tools/gpu/TestOps.h"
 
 #include <memory>
@@ -32,7 +33,7 @@
 
 namespace skiagm {
 /**
- * This GM directly exercises GrTextureEffect::MakeTexelSubset.
+ * This GM directly exercises GrDomainEffect.
  */
 class TextureDomainEffect : public GpuGM {
 public:
@@ -53,8 +54,8 @@
 
     SkISize onISize() override {
         const SkScalar canvasWidth =
-                kDrawPad + 2 * ((kTargetWidth + 2 * kDrawPad) * GrSamplerState::kWrapModeCount +
-                                kTestPad * GrSamplerState::kWrapModeCount);
+                kDrawPad + 2 * ((kTargetWidth + 2 * kDrawPad) * GrTextureDomain::kModeCount +
+                                kTestPad * GrTextureDomain::kModeCount);
         return SkISize::Make(SkScalarCeilToInt(canvasWidth), 800);
     }
 
@@ -116,23 +117,20 @@
         for (int tm = 0; tm < textureMatrices.count(); ++tm) {
             for (size_t d = 0; d < SK_ARRAY_COUNT(texelDomains); ++d) {
                 SkScalar x = kDrawPad + kTestPad;
-                for (int m = 0; m < GrSamplerState::kWrapModeCount; ++m) {
-                    auto wm = static_cast<GrSamplerState::WrapMode>(m);
+                for (int m = 0; m < GrTextureDomain::kModeCount; ++m) {
+                    GrTextureDomain::Mode mode = (GrTextureDomain::Mode) m;
                     if (fFilter != GrSamplerState::Filter::kNearest &&
-                        (wm == GrSamplerState::WrapMode::kRepeat ||
-                         wm == GrSamplerState::WrapMode::kMirrorRepeat)) {
+                        (mode == GrTextureDomain::kRepeat_Mode ||
+                         mode == GrTextureDomain::kMirrorRepeat_Mode)) {
                         // [Mirror] Repeat mode doesn't produce correct results with bilerp
                         // filtering
                         continue;
                     }
-                    GrSamplerState sampler(wm, fFilter);
-                    const auto& caps = *context->priv().caps();
-                    auto fp1 = GrTextureEffect::MakeTexelSubset(proxy,
-                                                                fBitmap.alphaType(),
-                                                                textureMatrices[tm],
-                                                                sampler,
-                                                                texelDomains[d],
-                                                                caps);
+                    auto fp1 = GrTextureEffect::Make(proxy, fBitmap.alphaType(),
+                                                     textureMatrices[tm], fFilter);
+                    fp1 = GrDomainEffect::Make(
+                            std::move(fp1), GrTextureDomain::MakeTexelDomain(texelDomains[d], mode),
+                            mode, fFilter);
                     if (!fp1) {
                         continue;
                     }
@@ -147,7 +145,6 @@
                     // Draw again with a translated local rect and compensating translate matrix.
                     drawRect = localRect.makeOffset(x, y);
                     static constexpr SkVector kT = {-100, 300};
-
                     if (auto op = sk_gpu_test::test_ops::MakeRect(context,
                                                                   std::move(fp2),
                                                                   drawRect,
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index deb5cb7..37313bc 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -126,10 +126,11 @@
                     { indices[i][2], SkColorChannel::kR },
                     { -1, SkColorChannel::kA }
                 };
-                const auto& caps = *context->priv().caps();
-                std::unique_ptr<GrFragmentProcessor> fp(GrYUVtoRGBEffect::Make(
-                        proxies, yuvaIndices, static_cast<SkYUVColorSpace>(space),
-                        GrSamplerState::Filter::kNearest, caps));
+
+                std::unique_ptr<GrFragmentProcessor> fp(
+                        GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
+                                               static_cast<SkYUVColorSpace>(space),
+                                               GrSamplerState::Filter::kNearest));
                 if (fp) {
                     GrPaint grPaint;
                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
@@ -243,10 +244,9 @@
 
             GrPaint grPaint;
             grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
-            const auto& caps = *context->priv().caps();
             auto fp = GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
                                              static_cast<SkYUVColorSpace>(space),
-                                             GrSamplerState::Filter::kNearest, caps);
+                                             GrSamplerState::Filter::kNearest);
             if (fp) {
                 SkMatrix viewMatrix;
                 viewMatrix.setTranslate(x, y);
@@ -354,10 +354,8 @@
                 }
 
                 const SkRect* domainPtr = j > 0 ? &domain : nullptr;
-                const auto& caps = *context->priv().caps();
-                std::unique_ptr<GrFragmentProcessor> fp(
-                        GrYUVtoRGBEffect::Make(proxies, yuvaIndices, kJPEG_SkYUVColorSpace,
-                                               kFilters[i], caps, SkMatrix::I(), domainPtr));
+                std::unique_ptr<GrFragmentProcessor> fp(GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
+                        kJPEG_SkYUVColorSpace, kFilters[i], SkMatrix::I(), domainPtr));
                 if (fp) {
                     GrPaint grPaint;
                     grPaint.addColorFragmentProcessor(std::move(fp));
diff --git a/src/core/SkBlurMF.cpp b/src/core/SkBlurMF.cpp
index 5102f9e..d6df230 100644
--- a/src/core/SkBlurMF.cpp
+++ b/src/core/SkBlurMF.cpp
@@ -28,6 +28,7 @@
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/GrStyle.h"
 #include "src/gpu/GrTextureProxy.h"
+#include "src/gpu/effects/GrTextureDomain.h"
 #include "src/gpu/effects/GrTextureEffect.h"
 #include "src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h"
 #include "src/gpu/effects/generated/GrRRectBlurEffect.h"
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index f63f31e..514e85a 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -311,15 +311,17 @@
         }
 
         GrPaint paint;
-        std::unique_ptr<GrFragmentProcessor> fp;
+        auto fp = GrTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
+                                        GrSamplerState::Filter::kBilerp);
         if (i == 1) {
-            GrSamplerState::WrapMode wrapMode;
+            // GrDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
+            GrTextureDomain::Mode domainMode;
             if (mode == SkTileMode::kClamp) {
-                wrapMode = GrSamplerState::WrapMode::kClamp;
+                domainMode = GrTextureDomain::kClamp_Mode;
             } else {
-                // GrTextureEffect does not support WrapMode::k[Mirror]Repeat with
-                // GrSamplerState::Filter::kBilerp. So we use kClampToBorder.
-                wrapMode = GrSamplerState::WrapMode::kClampToBorder;
+                // GrDomainEffect does not support k[Mirror]Repeat with GrSamplerState::Filter.
+                // So we use decal.
+                domainMode = GrTextureDomain::kDecal_Mode;
             }
             SkRect domain = SkRect::Make(*contentRect);
             domain.inset((i < scaleFactorX) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f,
@@ -331,14 +333,8 @@
             if (domain.fBottom < domain.fTop) {
                 domain.fTop = domain.fBottom = SkScalarAve(domain.fTop, domain.fBottom);
             }
-            const auto& caps = *context->priv().caps();
-            GrSamplerState sampler(wrapMode, GrSamplerState::Filter::kBilerp);
-            fp = GrTextureEffect::MakeSubset(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
-                                             sampler, domain, caps);
+            fp = GrDomainEffect::Make(std::move(fp), domain, domainMode, true);
             srcRect.offset(-srcOffset);
-        } else {
-            fp = GrTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
-                                       GrSamplerState::Filter::kBilerp);
         }
         paint.addColorFragmentProcessor(std::move(fp));
         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
@@ -392,9 +388,11 @@
     }
 
     GrPaint paint;
-    const auto& caps = *context->priv().caps();
-    auto fp = GrTextureEffect::MakeTexelSubset(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
-                                               GrSamplerState::Filter::kBilerp, srcBounds, caps);
+    SkRect domain = GrTextureDomain::MakeTexelDomain(srcBounds, GrTextureDomain::kClamp_Mode,
+                                                     GrTextureDomain::kClamp_Mode);
+    auto fp = GrTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
+                                    GrSamplerState::Filter::kBilerp);
+    fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode, true);
     paint.addColorFragmentProcessor(std::move(fp));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     GrFixedClip clip(SkIRect::MakeSize(dstSize));
diff --git a/src/core/SkGpuBlurUtils.h b/src/core/SkGpuBlurUtils.h
index 47bb9f0..53463e8 100644
--- a/src/core/SkGpuBlurUtils.h
+++ b/src/core/SkGpuBlurUtils.h
@@ -10,6 +10,7 @@
 
 #if SK_SUPPORT_GPU
 #include "src/gpu/GrRenderTargetContext.h"
+#include "src/gpu/effects/GrTextureDomain.h"
 
 class GrContext;
 class GrTexture;
diff --git a/src/effects/imagefilters/SkArithmeticImageFilter.cpp b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
index ecca33e..8a08602 100644
--- a/src/effects/imagefilters/SkArithmeticImageFilter.cpp
+++ b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
@@ -25,6 +25,7 @@
 #include "src/gpu/GrTextureProxy.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/effects/GrSkSLFP.h"
+#include "src/gpu/effects/GrTextureDomain.h"
 #include "src/gpu/effects/generated/GrConstColorProcessor.h"
 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
@@ -347,17 +348,18 @@
 
     GrPaint paint;
     std::unique_ptr<GrFragmentProcessor> bgFP;
-    const auto& caps = *ctx.getContext()->priv().caps();
-    GrSamplerState sampler(GrSamplerState::WrapMode::kClampToBorder,
-                           GrSamplerState::Filter::kNearest);
 
     if (backgroundProxy) {
         SkIRect bgSubset = background->subset();
         SkMatrix backgroundMatrix = SkMatrix::MakeTrans(
                 SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
                 SkIntToScalar(bgSubset.top()  - backgroundOffset.fY));
-        bgFP = GrTextureEffect::MakeTexelSubset(std::move(backgroundProxy), background->alphaType(),
-                                                backgroundMatrix, sampler, bgSubset, caps);
+        bgFP = GrTextureEffect::Make(std::move(backgroundProxy), background->alphaType(),
+                                     backgroundMatrix, GrSamplerState::Filter::kNearest);
+        bgFP = GrDomainEffect::Make(
+                std::move(bgFP),
+                GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
+                GrTextureDomain::kDecal_Mode, false);
         bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
                                              background->alphaType(),
                                              ctx.colorSpace());
@@ -371,14 +373,18 @@
         SkMatrix foregroundMatrix = SkMatrix::MakeTrans(
                 SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
                 SkIntToScalar(fgSubset.top()  - foregroundOffset.fY));
-        auto fgFP = GrTextureEffect::MakeTexelSubset(std::move(foregroundProxy),
-                                                     foreground->alphaType(), foregroundMatrix,
-                                                     sampler, fgSubset, caps);
-        fgFP = GrColorSpaceXformEffect::Make(std::move(fgFP),
-                                             foreground->getColorSpace(),
-                                             foreground->alphaType(),
-                                             ctx.colorSpace());
-        paint.addColorFragmentProcessor(std::move(fgFP));
+        auto foregroundFP =
+                GrTextureEffect::Make(std::move(foregroundProxy), foreground->alphaType(),
+                                      foregroundMatrix, GrSamplerState::Filter::kNearest);
+        foregroundFP = GrDomainEffect::Make(
+                std::move(foregroundFP),
+                GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
+                GrTextureDomain::kDecal_Mode, false);
+        foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
+                                                     foreground->getColorSpace(),
+                                                     foreground->alphaType(),
+                                                     ctx.colorSpace());
+        paint.addColorFragmentProcessor(std::move(foregroundFP));
 
         static auto effect = std::get<0>(SkRuntimeEffect::Make(SkString(SKSL_ARITHMETIC_SRC)));
         ArithmeticFPInputs inputs;
diff --git a/src/effects/imagefilters/SkXfermodeImageFilter.cpp b/src/effects/imagefilters/SkXfermodeImageFilter.cpp
index 0a80ea2..8924456 100644
--- a/src/effects/imagefilters/SkXfermodeImageFilter.cpp
+++ b/src/effects/imagefilters/SkXfermodeImageFilter.cpp
@@ -22,7 +22,9 @@
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrRenderTargetContext.h"
 #include "src/gpu/GrTextureProxy.h"
+
 #include "src/gpu/SkGr.h"
+#include "src/gpu/effects/GrTextureDomain.h"
 #include "src/gpu/effects/GrTextureEffect.h"
 #include "src/gpu/effects/generated/GrConstColorProcessor.h"
 #endif
@@ -258,17 +260,18 @@
 
     GrPaint paint;
     std::unique_ptr<GrFragmentProcessor> bgFP;
-    const auto& caps = *ctx.getContext()->priv().caps();
-    GrSamplerState sampler(GrSamplerState::WrapMode::kClampToBorder,
-                           GrSamplerState::Filter::kNearest);
 
     if (backgroundProxy) {
         SkIRect bgSubset = background->subset();
         SkMatrix bgMatrix = SkMatrix::MakeTrans(
                 SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
                 SkIntToScalar(bgSubset.top()  - backgroundOffset.fY));
-        bgFP = GrTextureEffect::MakeTexelSubset(std::move(backgroundProxy), background->alphaType(),
-                                                bgMatrix, sampler, bgSubset, caps);
+        bgFP = GrTextureEffect::Make(std::move(backgroundProxy), background->alphaType(), bgMatrix,
+                                     GrSamplerState::Filter::kNearest);
+        bgFP = GrDomainEffect::Make(
+                std::move(bgFP),
+                GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
+                GrTextureDomain::kDecal_Mode, false);
         bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
                                              background->alphaType(),
                                              ctx.colorSpace());
@@ -282,14 +285,18 @@
         SkMatrix fgMatrix = SkMatrix::MakeTrans(
                 SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
                 SkIntToScalar(fgSubset.top()  - foregroundOffset.fY));
-        auto fgFP = GrTextureEffect::MakeTexelSubset(std::move(foregroundProxy),
-                                                     foreground->alphaType(), fgMatrix, sampler,
-                                                     fgSubset, caps);
-        fgFP = GrColorSpaceXformEffect::Make(std::move(fgFP),
-                                             foreground->getColorSpace(),
-                                             foreground->alphaType(),
-                                             ctx.colorSpace());
-        paint.addColorFragmentProcessor(std::move(fgFP));
+        auto foregroundFP =
+                GrTextureEffect::Make(std::move(foregroundProxy), foreground->alphaType(), fgMatrix,
+                                      GrSamplerState::Filter::kNearest);
+        foregroundFP = GrDomainEffect::Make(
+                std::move(foregroundFP),
+                GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
+                GrTextureDomain::kDecal_Mode, false);
+        foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
+                                                     foreground->getColorSpace(),
+                                                     foreground->alphaType(),
+                                                     ctx.colorSpace());
+        paint.addColorFragmentProcessor(std::move(foregroundFP));
 
         std::unique_ptr<GrFragmentProcessor> xferFP = this->makeFGFrag(std::move(bgFP));
 
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index cc6f145..ead5dfe 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -8,7 +8,6 @@
 #include "src/gpu/GrImageTextureMaker.h"
 
 #include "src/gpu/GrColorSpaceXform.h"
-#include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/effects/GrYUVtoRGBEffect.h"
@@ -114,9 +113,8 @@
         domain = &constraintRect;
     }
 
-    const auto& caps = *fImage->context()->priv().caps();
-    auto fp = GrYUVtoRGBEffect::Make(fImage->fProxies, fImage->fYUVAIndices, fImage->fYUVColorSpace,
-                                     filter, caps, textureMatrix, domain);
+    auto fp = GrYUVtoRGBEffect::Make(fImage->fProxies, fImage->fYUVAIndices,
+                                     fImage->fYUVColorSpace, filter, textureMatrix, domain);
     if (fImage->fFromColorSpace) {
         fp = GrColorSpaceXformEffect::Make(std::move(fp), fImage->fFromColorSpace.get(),
                                            fImage->alphaType(), fImage->colorSpace());
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index f3ae030..fe6b12b 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -107,6 +107,7 @@
         kGrDistanceFieldA8TextGeoProc_ClassID,
         kGrDistanceFieldLCDTextGeoProc_ClassID,
         kGrDistanceFieldPathGeoProc_ClassID,
+        kGrDomainEffect_ClassID,
         kGrDualIntervalGradientColorizer_ClassID,
         kGrEllipseEffect_ClassID,
         kGrFillRRectOp_Processor_ClassID,
diff --git a/src/gpu/GrProcessorUnitTest.cpp b/src/gpu/GrProcessorUnitTest.cpp
index 21f282b..afd79ed 100644
--- a/src/gpu/GrProcessorUnitTest.cpp
+++ b/src/gpu/GrProcessorUnitTest.cpp
@@ -81,7 +81,7 @@
  * we verify the count is as expected.  If a new factory is added, then these numbers must be
  * manually adjusted.
  */
-static const int kFPFactoryCount = 36;
+static const int kFPFactoryCount = 37;
 static const int kGPFactoryCount = 14;
 static const int kXPFactoryCount = 4;
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index b98386b..bab4d1db 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -45,6 +45,7 @@
 #include "src/gpu/SkGr.h"
 #include "src/gpu/effects/GrBicubicEffect.h"
 #include "src/gpu/effects/GrRRectEffect.h"
+#include "src/gpu/effects/GrTextureDomain.h"
 #include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
 #include "src/gpu/geometry/GrQuad.h"
 #include "src/gpu/geometry/GrQuadUtils.h"
diff --git a/src/gpu/GrSamplerState.h b/src/gpu/GrSamplerState.h
index fc6b728..848c84c 100644
--- a/src/gpu/GrSamplerState.h
+++ b/src/gpu/GrSamplerState.h
@@ -27,9 +27,6 @@
     constexpr GrSamplerState(WrapMode wrapXAndY, Filter filter)
             : fWrapModes{wrapXAndY, wrapXAndY}, fFilter(filter) {}
 
-    constexpr GrSamplerState(WrapMode wrapX, WrapMode wrapY, Filter filter)
-            : fWrapModes{wrapX, wrapY}, fFilter(filter) {}
-
     constexpr GrSamplerState(const WrapMode wrapModes[2], Filter filter)
             : fWrapModes{wrapModes[0], wrapModes[1]}, fFilter(filter) {}
 
diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp
index 243582d..46c6134 100644
--- a/src/gpu/GrTextureProducer.cpp
+++ b/src/gpu/GrTextureProducer.cpp
@@ -33,12 +33,15 @@
 
     SkRect localRect = inputProxy->getBoundsRect();
 
+    bool needsDomain = false;
     bool resizing = false;
     if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
-        resizing = localRect.width() != dstRect.width() || localRect.height() != dstRect.height();
+        bool resizing = localRect.width()  != dstRect.width() ||
+                        localRect.height() != dstRect.height();
+        needsDomain = resizing && inputProxy->isFunctionallyExact();
     }
 
-    if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !resizing &&
+    if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
         dstWillRequireMipMaps) {
         sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get(),
                                                                      colorType);
@@ -54,13 +57,18 @@
         return nullptr;
     }
 
-    const auto& caps = *context->priv().caps();
     GrPaint paint;
 
-    GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
-    auto boundsRect = SkIRect::MakeSize(inputProxy->dimensions());
-    auto fp = GrTextureEffect::MakeTexelSubset(std::move(inputProxy), kUnknown_SkAlphaType,
-                                               SkMatrix::I(), sampler, boundsRect, localRect, caps);
+    auto fp = GrTextureEffect::Make(std::move(inputProxy), kUnknown_SkAlphaType, SkMatrix::I(),
+                                    copyParams.fFilter);
+    if (needsDomain) {
+        const SkRect domain = localRect.makeInset(0.5f, 0.5f);
+        // This would cause us to read values from outside the subset. Surely, the caller knows
+        // better!
+        SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
+        fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode,
+                                  copyParams.fFilter);
+    }
     paint.addColorFragmentProcessor(std::move(fp));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
@@ -191,19 +199,21 @@
         const SkRect& domain,
         const GrSamplerState::Filter* filterOrNullForBicubic) {
     SkASSERT(kTightCopy_DomainMode != domainMode);
-    const auto& caps = *fContext->priv().caps();
+    bool clampToBorderSupport = fContext->priv().caps()->clampToBorderSupport();
     SkAlphaType srcAlphaType = this->alphaType();
     if (filterOrNullForBicubic) {
-        GrSamplerState::WrapMode wrapMode = fDomainNeedsDecal
+        GrSamplerState::WrapMode wrapMode = fDomainNeedsDecal && clampToBorderSupport
                                                     ? GrSamplerState::WrapMode::kClampToBorder
                                                     : GrSamplerState::WrapMode::kClamp;
         GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
-        if (kNoDomain_DomainMode == domainMode) {
-            return GrTextureEffect::Make(std::move(proxy), srcAlphaType, textureMatrix,
-                                         samplerState, caps);
+        auto fp =
+                GrTextureEffect::Make(std::move(proxy), srcAlphaType, textureMatrix, samplerState);
+        if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
+            GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
+                                                               : GrTextureDomain::kClamp_Mode;
+            return GrDomainEffect::Make(std::move(fp), domain, wrapMode, *filterOrNullForBicubic);
         }
-        return GrTextureEffect::MakeSubset(std::move(proxy), srcAlphaType, textureMatrix,
-                                           samplerState, domain, caps);
+        return fp;
     } else {
         static const GrSamplerState::WrapMode kClampClamp[] = {
                 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
@@ -211,7 +221,6 @@
                 GrSamplerState::WrapMode::kClampToBorder, GrSamplerState::WrapMode::kClampToBorder};
 
         static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
-        bool clampToBorderSupport = caps.clampToBorderSupport();
         if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
             GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
                                          : GrTextureDomain::kClamp_Mode;
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index 178554a..d974f2e 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -168,9 +168,8 @@
     }
 
     GrPaint paint;
-    const auto& caps = *ctx->priv().caps();
     auto yuvToRgbProcessor = GrYUVtoRGBEffect::Make(yuvTextureProxies, yuvaIndices, yuvColorSpace,
-                                                    GrSamplerState::Filter::kNearest, caps);
+                                                    GrSamplerState::Filter::kNearest);
     paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
 
     // If the caller expects the pixels in a different color space than the one from the image,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index ba29ad7..6fa5173 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -43,6 +43,7 @@
 #include "src/gpu/GrTracing.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/effects/GrBicubicEffect.h"
+#include "src/gpu/effects/GrTextureDomain.h"
 #include "src/gpu/geometry/GrShape.h"
 #include "src/gpu/text/GrTextTarget.h"
 #include "src/image/SkImage_Base.h"
@@ -935,7 +936,6 @@
     // the rest from the SkPaint.
     std::unique_ptr<GrFragmentProcessor> fp;
 
-    const auto& caps = *this->caps();
     if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) {
         // Use a constrained texture domain to avoid color bleeding
         SkRect domain;
@@ -955,8 +955,9 @@
             static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
             fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, domain, kDir, srcAlphaType);
         } else {
-            fp = GrTextureEffect::MakeSubset(std::move(proxy), srcAlphaType, texMatrix,
-                                             samplerState, domain, caps);
+            fp = GrTextureEffect::Make(std::move(proxy), srcAlphaType, texMatrix, samplerState);
+            fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode,
+                                      samplerState.filter());
         }
     } else if (bicubic) {
         SkASSERT(GrSamplerState::Filter::kNearest == samplerState.filter());
@@ -964,7 +965,7 @@
         static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
         fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, wrapMode, kDir, srcAlphaType);
     } else {
-        fp = GrTextureEffect::Make(std::move(proxy), srcAlphaType, texMatrix, samplerState, caps);
+        fp = GrTextureEffect::Make(std::move(proxy), srcAlphaType, texMatrix, samplerState);
     }
 
     fp = GrColorSpaceXformEffect::Make(std::move(fp), bitmap.colorSpace(), bitmap.alphaType(),
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index cd15573..ad629c5 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -319,6 +319,191 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                          const SkRect& domain,
+                                                          GrTextureDomain::Mode mode,
+                                                          bool decalIsFiltered) {
+    return Make(std::move(fp), domain, mode, mode, decalIsFiltered);
+}
+
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                          const SkRect& domain,
+                                                          GrTextureDomain::Mode modeX,
+                                                          GrTextureDomain::Mode modeY,
+                                                          bool decalIsFiltered) {
+    if (modeX == GrTextureDomain::kIgnore_Mode && modeY == GrTextureDomain::kIgnore_Mode) {
+        return fp;
+    }
+    int count = 0;
+    GrCoordTransform* coordTransform = nullptr;
+    for (auto [transform, ignored] : GrFragmentProcessor::FPCoordTransformRange(*fp)) {
+        ++count;
+        coordTransform = &transform;
+    }
+    // If there are no coord transforms on the passed FP or it's children then there's no need to
+    // enforce a domain.
+    // We have a limitation that only one coord transform is support when overriding local coords.
+    // If that limit were relaxed we would need to add a coord transform for each descendent FP
+    // transform and possibly have multiple domain rects to account for different proxy
+    // normalization and y-reversals.
+    if (count != 1) {
+        return fp;
+    }
+    GrCoordTransform transformCopy = *coordTransform;
+    // Reset the child FP's coord transform.
+    *coordTransform = {};
+    // If both domain modes happen to be ignore, it would be faster to just drop the domain logic
+    // entirely and return the original FP. We'd need a GrMatrixProcessor if the matrix is not
+    // identity, though.
+    return std::unique_ptr<GrFragmentProcessor>(new GrDomainEffect(
+            std::move(fp), transformCopy, domain, modeX, modeY, decalIsFiltered));
+}
+
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                          const SkRect& domain,
+                                                          GrTextureDomain::Mode mode,
+                                                          GrSamplerState::Filter filter) {
+    bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
+    return Make(std::move(fp), domain, mode, filterIfDecal);
+}
+
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                          const SkRect& domain,
+                                                          GrTextureDomain::Mode modeX,
+                                                          GrTextureDomain::Mode modeY,
+                                                          GrSamplerState::Filter filter) {
+    bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
+    return Make(std::move(fp), domain, modeX, modeY, filterIfDecal);
+}
+GrFragmentProcessor::OptimizationFlags GrDomainEffect::Flags(GrFragmentProcessor* fp,
+                                                             GrTextureDomain::Mode modeX,
+                                                             GrTextureDomain::Mode modeY) {
+    auto fpFlags = GrFragmentProcessor::ProcessorOptimizationFlags(fp);
+    if (modeX == GrTextureDomain::kDecal_Mode || modeY == GrTextureDomain::kDecal_Mode) {
+        return fpFlags & ~kPreservesOpaqueInput_OptimizationFlag;
+    }
+    return fpFlags;
+}
+
+GrDomainEffect::GrDomainEffect(std::unique_ptr<GrFragmentProcessor> fp,
+                               const GrCoordTransform& coordTransform,
+                               const SkRect& domain,
+                               GrTextureDomain::Mode modeX,
+                               GrTextureDomain::Mode modeY,
+                               bool decalIsFiltered)
+        : INHERITED(kGrDomainEffect_ClassID, Flags(fp.get(), modeX, modeY))
+        , fCoordTransform(coordTransform)
+        , fDomain(domain, modeX, modeY)
+        , fDecalIsFiltered(decalIsFiltered) {
+    SkASSERT(fp);
+    fp->setSampledWithExplicitCoords(true);
+    this->registerChildProcessor(std::move(fp));
+    this->addCoordTransform(&fCoordTransform);
+    if (fDomain.modeX() != GrTextureDomain::kDecal_Mode &&
+        fDomain.modeY() != GrTextureDomain::kDecal_Mode) {
+        // Canonicalize this don't care value so we don't have to worry about it elsewhere.
+        fDecalIsFiltered = false;
+    }
+}
+
+GrDomainEffect::GrDomainEffect(const GrDomainEffect& that)
+        : INHERITED(kGrDomainEffect_ClassID, that.optimizationFlags())
+        , fCoordTransform(that.fCoordTransform)
+        , fDomain(that.fDomain)
+        , fDecalIsFiltered(that.fDecalIsFiltered) {
+    auto child = that.childProcessor(0).clone();
+    child->setSampledWithExplicitCoords(true);
+    this->registerChildProcessor(std::move(child));
+    this->addCoordTransform(&fCoordTransform);
+}
+
+void GrDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                           GrProcessorKeyBuilder* b) const {
+    b->add32(GrTextureDomain::GLDomain::DomainKey(fDomain));
+}
+
+GrGLSLFragmentProcessor* GrDomainEffect::onCreateGLSLInstance() const {
+    class GLSLProcessor : public GrGLSLFragmentProcessor {
+    public:
+        void emitCode(EmitArgs& args) override {
+            const GrDomainEffect& de = args.fFp.cast<GrDomainEffect>();
+            const GrTextureDomain& domain = de.fDomain;
+
+            SkString coords2D =
+                    args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
+
+            fGLDomain.sampleProcessor(domain, args.fInputColor, args.fOutputColor, coords2D, this,
+                                      args, 0);
+        }
+
+    protected:
+        void onSetData(const GrGLSLProgramDataManager& pdman,
+                       const GrFragmentProcessor& fp) override {
+            const GrDomainEffect& de = fp.cast<GrDomainEffect>();
+            const GrTextureDomain& domain = de.fDomain;
+            // TODO: Update GrCoordTransform to return a view instead of proxy
+            const GrSurfaceProxy* proxy = de.fCoordTransform.proxy();
+            // If we don't have a proxy the value of the origin doesn't matter
+            GrSurfaceOrigin origin = proxy ? proxy->origin() : kTopLeft_GrSurfaceOrigin;
+            fGLDomain.setData(pdman, domain, proxy, origin, de.fDecalIsFiltered);
+        }
+
+    private:
+        GrTextureDomain::GLDomain         fGLDomain;
+    };
+
+    return new GLSLProcessor;
+}
+
+bool GrDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
+    auto& td = sBase.cast<GrDomainEffect>();
+    return fDomain == td.fDomain && fDecalIsFiltered == td.fDecalIsFiltered;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDomainEffect);
+
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::TestCreate(GrProcessorTestData* d) {
+    do {
+        GrTextureDomain::Mode modeX =
+                (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
+        GrTextureDomain::Mode modeY =
+                (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
+        auto child = GrProcessorUnitTest::MakeChildFP(d);
+        const auto* childPtr = child.get();
+        SkRect domain;
+        // We assert if the child's coord transform has a proxy and the domain rect is outside its
+        // bounds.
+        GrFragmentProcessor::CoordTransformIter ctIter(*child);
+        if (!ctIter) {
+            continue;
+        }
+        auto [transform, fp] = *ctIter;
+        if (auto proxy = transform.proxy()) {
+            auto [w, h] = proxy->backingStoreDimensions();
+            domain.fLeft   = d->fRandom->nextRangeScalar(0, w);
+            domain.fRight  = d->fRandom->nextRangeScalar(0, w);
+            domain.fTop    = d->fRandom->nextRangeScalar(0, h);
+            domain.fBottom = d->fRandom->nextRangeScalar(0, h);
+        } else {
+            domain.fLeft   = d->fRandom->nextRangeScalar(-100.f, 100.f);
+            domain.fRight  = d->fRandom->nextRangeScalar(-100.f, 100.f);
+            domain.fTop    = d->fRandom->nextRangeScalar(-100.f, 100.f);
+            domain.fBottom = d->fRandom->nextRangeScalar(-100.f, 100.f);
+        }
+        domain.sort();
+        bool filterIfDecal = d->fRandom->nextBool();
+        auto result = GrDomainEffect::Make(std::move(child), domain, modeX, modeY, filterIfDecal);
+        if (result && result.get() != childPtr) {
+            return result;
+        }
+    } while (true);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
         sk_sp<GrSurfaceProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
     return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
index 70e6766..8e2a6e4 100644
--- a/src/gpu/effects/GrTextureDomain.h
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -231,6 +231,85 @@
     int     fIndex;
 };
 
+/**
+ * This effect applies a domain rectangle with an edge "mode" to the result of the child FP's coord
+ * transform. Currently the passed FP (including its descendants) must have exactly 1 coord
+ * transform (due to internal program builder restrictions). Also, it's important to note that the
+ * domain rectangle is applied  AFTER the corod transform. This allows us to continue to lift the
+ * coord transform to the vertex shader. It might make this nicer for some use cases to add a
+ * pre-coord transform option and try to adjust the domain rect internally to convert to
+ * post-coord transform and keep everything in the vertex shader for simple use cases.
+ */
+class GrDomainEffect : public GrFragmentProcessor {
+public:
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
+                                                     const SkRect& domain,
+                                                     GrTextureDomain::Mode,
+                                                     bool decalIsFiltered);
+
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
+                                                     const SkRect& domain,
+                                                     GrTextureDomain::Mode modeX,
+                                                     GrTextureDomain::Mode modeY,
+                                                     bool decalIsFiltered);
+
+    // These variants infer decalIsFiltered from the Filter mode (true if not kNearest).
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
+                                                     const SkRect& domain,
+                                                     GrTextureDomain::Mode,
+                                                     GrSamplerState::Filter);
+
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
+                                                     const SkRect& domain,
+                                                     GrTextureDomain::Mode modeX,
+                                                     GrTextureDomain::Mode modeY,
+                                                     GrSamplerState::Filter);
+
+    const char* name() const override { return "Domain"; }
+
+    std::unique_ptr<GrFragmentProcessor> clone() const override {
+        return std::unique_ptr<GrFragmentProcessor>(new GrDomainEffect(*this));
+    }
+
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override {
+        SkString str;
+        str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f], filterDecal: %d",
+                    fDomain.domain().fLeft, fDomain.domain().fTop, fDomain.domain().fRight,
+                    fDomain.domain().fBottom, fDecalIsFiltered);
+        str.append(INHERITED::dumpInfo());
+        return str;
+    }
+#endif
+
+private:
+    GrFragmentProcessor::OptimizationFlags Flags(GrFragmentProcessor*, GrTextureDomain::Mode,
+                                                 GrTextureDomain::Mode);
+
+    GrCoordTransform fCoordTransform;
+    GrTextureDomain fDomain;
+    bool fDecalIsFiltered;
+
+    GrDomainEffect(std::unique_ptr<GrFragmentProcessor>,
+                   const GrCoordTransform& transform,
+                   const SkRect& domain,
+                   GrTextureDomain::Mode modeX,
+                   GrTextureDomain::Mode modeY,
+                   bool decalIsFiltered);
+
+    explicit GrDomainEffect(const GrDomainEffect&);
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
 class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor {
 public:
     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy>,
diff --git a/src/gpu/effects/GrTextureEffect.cpp b/src/gpu/effects/GrTextureEffect.cpp
index c8a52fb..e964a77 100644
--- a/src/gpu/effects/GrTextureEffect.cpp
+++ b/src/gpu/effects/GrTextureEffect.cpp
@@ -8,233 +8,24 @@
 #include "src/gpu/effects/GrTextureEffect.h"
 
 #include "include/gpu/GrTexture.h"
-#include "src/gpu/GrTexturePriv.h"
 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
 #include "src/sksl/SkSLCPP.h"
 #include "src/sksl/SkSLUtil.h"
 
-namespace {
-struct Span {
-    float fA = 0.f, fB = 0.f;
-
-    Span makeInset(float o) const {
-        Span r = {fA + o, fB - o};
-        if (r.fA > r.fB) {
-            r.fA = r.fB = (r.fA + r.fB) / 2;
-        }
-        return r;
-    }
-
-    bool contains(Span r) const { return fA <= r.fA && fB >= r.fB; }
-};
-}  // anonymous namespace
-
-GrTextureEffect::Sampling::Sampling(GrSamplerState sampler, SkISize size, const GrCaps& caps)
-        : fHWSampler(sampler) {
-    if (!caps.clampToBorderSupport()) {
-        if (fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder) {
-            fHWSampler.setWrapModeX(GrSamplerState::WrapMode::kClamp);
-            fShaderModes[0] = ShaderMode::kDecal;
-            Span span{0, (float)size.width()};
-            if (sampler.filter() != GrSamplerState::Filter::kNearest) {
-                span = span.makeInset(0.5f);
-            }
-            fShaderSubset.fLeft  = span.fA;
-            fShaderSubset.fRight = span.fB;
-        }
-        if (fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder) {
-            fHWSampler.setWrapModeY(GrSamplerState::WrapMode::kClamp);
-            fShaderModes[1] = ShaderMode::kDecal;
-            Span span{0, (float)size.height()};
-            if (sampler.filter() != GrSamplerState::Filter::kNearest) {
-                span = span.makeInset(0.5f);
-            }
-            fShaderSubset.fTop    = span.fA;
-            fShaderSubset.fBottom = span.fB;
-        }
-    }
-}
-
-GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
-                                    GrSamplerState sampler,
-                                    const SkRect& subset,
-                                    bool adjustForFilter,
-                                    const SkRect* domain,
-                                    const GrCaps& caps) {
-    using Mode = GrSamplerState::WrapMode;
-    using Filter = GrSamplerState::Filter;
-
-    struct Result1D {
-        ShaderMode fShaderMode;
-        Span fShaderSubset;
-        Mode fHWMode;
-        Filter fFilter;
-    };
-
-    auto resolve = [adjustForFilter, filter = sampler.filter(), &caps](int size, Mode mode,
-                                                                       Span subset, Span domain) {
-        float inset;
-        Result1D r;
-        r.fFilter = filter;
-        bool canDoHW = mode != Mode::kClampToBorder || caps.clampToBorderSupport();
-        if (canDoHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
-            r.fShaderMode = ShaderMode::kNone;
-            r.fHWMode = mode;
-            return r;
-        }
-
-        inset = (adjustForFilter && filter != Filter::kNearest) ? 0.5 : 0;
-        auto insetSubset = subset.makeInset(inset);
-
-        if (canDoHW && insetSubset.contains(domain)) {
-            r.fShaderMode = ShaderMode::kNone;
-            r.fHWMode = mode;
-            return r;
-        }
-
-        if (mode == Mode::kRepeat || mode == Mode::kMirrorRepeat) {
-            r.fFilter = Filter::kNearest;
-            r.fShaderSubset = subset;
-        } else {
-            r.fShaderSubset = insetSubset;
-        }
-        r.fShaderMode = static_cast<ShaderMode>(mode);
-        r.fHWMode = Mode::kClamp;
-        return r;
-    };
-
-    SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
-
-    Span subsetX{subset.fLeft, subset.fRight};
-    auto domainX = domain ? Span{domain->fLeft, domain->fRight}
-                          : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
-    auto x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX);
-
-    Span subsetY{subset.fTop, subset.fBottom};
-    auto domainY = domain ? Span{domain->fTop, domain->fBottom}
-                          : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
-    auto y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY);
-
-    fHWSampler = {x.fHWMode, y.fHWMode, std::min(x.fFilter, y.fFilter)};
-    fShaderModes[0] = x.fShaderMode;
-    fShaderModes[1] = y.fShaderMode;
-    fShaderSubset = {x.fShaderSubset.fA, y.fShaderSubset.fA,
-                     x.fShaderSubset.fB, y.fShaderSubset.fB};
-}
-
-bool GrTextureEffect::Sampling::usesDecal() const {
-    return fShaderModes[0] == ShaderMode::kDecal || fShaderModes[1] == ShaderMode::kDecal ||
-           fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
-           fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder;
-}
-
 std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(sk_sp<GrSurfaceProxy> proxy,
                                                            SkAlphaType alphaType,
                                                            const SkMatrix& matrix,
-                                                           GrSamplerState::Filter filter) {
+                                                           GrSamplerState sampler) {
     return std::unique_ptr<GrFragmentProcessor>(
-            new GrTextureEffect(std::move(proxy), alphaType, matrix, Sampling(filter)));
-}
-
-std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(sk_sp<GrSurfaceProxy> proxy,
-                                                           SkAlphaType alphaType,
-                                                           const SkMatrix& matrix,
-                                                           GrSamplerState sampler,
-                                                           const GrCaps& caps) {
-    Sampling sampling(sampler, proxy->dimensions(), caps);
-    return std::unique_ptr<GrFragmentProcessor>(
-            new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
-}
-
-std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeTexelSubset(sk_sp<GrSurfaceProxy> proxy,
-                                                                      SkAlphaType alphaType,
-                                                                      const SkMatrix& matrix,
-                                                                      GrSamplerState sampler,
-                                                                      const SkIRect& subset,
-                                                                      const GrCaps& caps) {
-    Sampling sampling(*proxy, sampler, SkRect::Make(subset), true, nullptr, caps);
-    return std::unique_ptr<GrFragmentProcessor>(
-            new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
-}
-
-std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeTexelSubset(sk_sp<GrSurfaceProxy> proxy,
-                                                                      SkAlphaType alphaType,
-                                                                      const SkMatrix& matrix,
-                                                                      GrSamplerState sampler,
-                                                                      const SkIRect& subset,
-                                                                      const SkRect& domain,
-                                                                      const GrCaps& caps) {
-    Sampling sampling(*proxy, sampler, SkRect::Make(subset), true, &domain, caps);
-    return std::unique_ptr<GrFragmentProcessor>(
-            new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
-}
-
-std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(sk_sp<GrSurfaceProxy> proxy,
-                                                                 SkAlphaType alphaType,
-                                                                 const SkMatrix& matrix,
-                                                                 GrSamplerState sampler,
-                                                                 const SkRect& subset,
-                                                                 const GrCaps& caps) {
-    Sampling sampling(*proxy, sampler, subset, false, nullptr, caps);
-    return std::unique_ptr<GrFragmentProcessor>(
-            new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
-}
-
-std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(sk_sp<GrSurfaceProxy> proxy,
-                                                                 SkAlphaType alphaType,
-                                                                 const SkMatrix& matrix,
-                                                                 GrSamplerState sampler,
-                                                                 const SkRect& subset,
-                                                                 const SkRect& domain,
-                                                                 const GrCaps& caps) {
-    Sampling sampling(*proxy, sampler, subset, false, &domain, caps);
-    return std::unique_ptr<GrFragmentProcessor>(
-            new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
+            new GrTextureEffect(std::move(proxy), alphaType, matrix, sampler));
 }
 
 GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
     class Impl : public GrGLSLFragmentProcessor {
-        UniformHandle fSubsetUni;
-        UniformHandle fDecalUni;
-
     public:
         void emitCode(EmitArgs& args) override {
-            auto appendWrap = [](GrGLSLShaderBuilder* builder, ShaderMode mode, const char* inCoord,
-                                 const char* domainStart, const char* domainEnd, bool is2D,
-                                 const char* out) {
-                switch (mode) {
-                    case ShaderMode::kNone:
-                        builder->codeAppendf("%s = %s;\n", out, inCoord);
-                        break;
-                    case ShaderMode::kDecal:
-                        // The lookup coordinate to use for decal will be clamped just like
-                        // kClamp_Mode, it's just that the post-processing will be different, so
-                        // fall through
-                    case ShaderMode::kClamp:
-                        builder->codeAppendf("%s = clamp(%s, %s, %s);", out, inCoord, domainStart,
-                                             domainEnd);
-                        break;
-                    case ShaderMode::kRepeat:
-                        builder->codeAppendf("%s = mod(%s - %s, %s - %s) + %s;", out, inCoord,
-                                             domainStart, domainEnd, domainStart, domainStart);
-                        break;
-                    case ShaderMode::kMirrorRepeat: {
-                        const char* type = is2D ? "float2" : "float";
-                        builder->codeAppend("{");
-                        builder->codeAppendf("%s w = %s - %s;", type, domainEnd, domainStart);
-                        builder->codeAppendf("%s w2 = 2 * w;", type);
-                        builder->codeAppendf("%s m = mod(%s - %s, w2);", type, inCoord,
-                                             domainStart);
-                        builder->codeAppendf("%s = mix(m, w2 - m, step(w, m)) + %s;", out,
-                                             domainStart);
-                        builder->codeAppend("}");
-                        break;
-                    }
-                }
-            };
-            auto te = args.fFp.cast<GrTextureEffect>();
             const char* coords;
             if (args.fFp.coordTransformsApplyToLocalCoords()) {
                 coords = args.fTransformedCoords[0].fVaryingPoint.c_str();
@@ -242,165 +33,30 @@
                 coords = "_coords";
             }
             auto* fb = args.fFragBuilder;
-            if (te.fShaderModes[0] == ShaderMode::kNone &&
-                te.fShaderModes[1] == ShaderMode::kNone) {
-                fb->codeAppendf("%s = ", args.fOutputColor);
-                fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
-                                                args.fTexSamplers[0], coords);
-                fb->codeAppendf(";");
-            } else {
-                const char* subsetName;
-                SkString uniName("TexDom");
-                fSubsetUni = args.fUniformHandler->addUniform(
-                        kFragment_GrShaderFlag, kHalf4_GrSLType, "subset", &subsetName);
-
-                // Always use a local variable for the input coordinates; often callers pass in an
-                // expression and we want to cache it across all of its references in the code below
-                auto inCoords = fb->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
-                fb->codeAppend("float2 clampedCoord;");
-                SkString start;
-                SkString end;
-                if (te.fShaderModes[0] == te.fShaderModes[1]) {
-                    // Doing the domain setup using vectors seems to avoid shader compilation issues
-                    // on Chromecast, possibly due to reducing shader length.
-                    start.printf("%s.xy", subsetName);
-                    end.printf("%s.zw", subsetName);
-                    appendWrap(fb, te.fShaderModes[0], inCoords.c_str(), start.c_str(), end.c_str(),
-                               true, "clampedCoord");
-                } else {
-                    SkString origX, origY;
-                    // Apply x mode to the x coordinate using the left and right edges of the domain
-                    // rect (stored as the x and z components of the domain uniform).
-                    start.printf("%s.x", subsetName);
-                    end.printf("%s.z", subsetName);
-                    origX.printf("%s.x", inCoords.c_str());
-                    appendWrap(fb, te.fShaderModes[0], origX.c_str(), start.c_str(), end.c_str(),
-                               false, "clampedCoord.x");
-                    // Repeat the same logic for y.
-                    start.printf("%s.y", subsetName);
-                    end.printf("%s.w", subsetName);
-                    origY.printf("%s.y", inCoords.c_str());
-                    appendWrap(fb, te.fShaderModes[1], origY.c_str(), start.c_str(), end.c_str(),
-                               false, "clampedCoord.y");
-                }
-                SkString textureLookup;
-                fb->appendTextureLookup(&textureLookup, args.fTexSamplers[0], "clampedCoord");
-                fb->codeAppendf("half4 textureColor = %s;", textureLookup.c_str());
-
-                // Apply decal mode's transparency interpolation if needed
-                bool decalX = te.fShaderModes[0] == ShaderMode::kDecal;
-                bool decalY = te.fShaderModes[1] == ShaderMode::kDecal;
-                if (decalX || decalY) {
-                    const char* decalName;
-                    // Half3 since this will hold texture width, height, and then a step function
-                    // control param
-                    fDecalUni = args.fUniformHandler->addUniform(
-                            kFragment_GrShaderFlag, kHalf3_GrSLType, uniName.c_str(), &decalName);
-                    // The decal err is the max absolute value between the clamped coordinate and
-                    // the original pixel coordinate. This will then be clamped to 1.f if it's
-                    // greater than the control parameter, which simulates kNearest and kBilerp
-                    // behavior depending on if it's 0 or 1.
-                    if (decalX && decalY) {
-                        fb->codeAppendf(
-                                "half err = max(half(abs(clampedCoord.x - %s.x) * %s.x), "
-                                "               half(abs(clampedCoord.y - %s.y) * %s.y));",
-                                inCoords.c_str(), decalName, inCoords.c_str(), decalName);
-                    } else if (decalX) {
-                        fb->codeAppendf("half err = half(abs(clampedCoord.x - %s.x) * %s.x);",
-                                        inCoords.c_str(), decalName);
-                    } else {
-                        SkASSERT(decalY);
-                        fb->codeAppendf("half err = half(abs(clampedCoord.y - %s.y) * %s.y);",
-                                        inCoords.c_str(), decalName);
-                    }
-
-                    // Apply a transform to the error rate, which let's us simulate nearest or
-                    // bilerp filtering in the same shader. When the texture is nearest filtered,
-                    // fSizeName.z is set to 0 so this becomes a step function centered at the
-                    // clamped coordinate. When bilerp, fSizeName.z is set to 1 and it becomes
-                    // a simple linear blend between texture and transparent.
-                    fb->codeAppendf(
-                            "if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }",
-                            decalName, decalName);
-                    fb->codeAppend("textureColor = mix(textureColor, half4(0), err);");
-                }
-                fb->codeAppendf("%s = textureColor * %s;", args.fOutputColor, args.fInputColor);
-            }
-        }
-
-    protected:
-        void onSetData(const GrGLSLProgramDataManager& pdm,
-                       const GrFragmentProcessor& fp) override {
-            const auto& te = fp.cast<GrTextureEffect>();
-            if (fSubsetUni.isValid()) {
-                const float w = te.fSampler.peekTexture()->width();
-                const float h = te.fSampler.peekTexture()->height();
-
-                const auto& s = te.fSubset;
-                float rect[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
-                float decalW[3];
-
-                if (te.fSampler.view().origin() == kBottomLeft_GrSurfaceOrigin) {
-                    rect[1] = h - rect[1];
-                    rect[3] = h - rect[3];
-                    std::swap(rect[1], rect[3]);
-                }
-
-                if (te.fSampler.peekTexture()->texturePriv().textureType() !=
-                    GrTextureType::kRectangle) {
-                    float iw = 1.f / w;
-                    float ih = 1.f / h;
-                    rect[0] *= iw;
-                    rect[2] *= iw;
-                    rect[1] *= ih;
-                    rect[3] *= ih;
-                    decalW[0] = w;
-                    decalW[1] = h;
-                } else {
-                    decalW[0] = 1;
-                    decalW[1] = 1;
-                }
-                pdm.set4fv(fSubsetUni, 1, rect);
-
-                if (fDecalUni.isValid()) {
-                    bool filter = te.textureSampler(0).samplerState().filter() !=
-                                  GrSamplerState::Filter::kNearest;
-                    decalW[2] = filter ? 1.f : 0.f;
-                    pdm.set3fv(fDecalUni, 1, decalW);
-                }
-            }
+            fb->codeAppendf("%s = ", args.fOutputColor);
+            fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
+                                            args.fTexSamplers[0], coords);
+            fb->codeAppendf(";");
         }
     };
     return new Impl;
 }
 
-void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
-    bool shaderFilter = (fShaderModes[0] == ShaderMode::kDecal ||
-                         fShaderModes[1] == ShaderMode::kDecal) &&
-                        fSampler.samplerState().filter() != GrSamplerState::Filter::kNearest;
-    auto m0 = static_cast<uint32_t>(fShaderModes[0]);
-    auto m1 = static_cast<uint32_t>(fShaderModes[1]);
-    b->add32(shaderFilter << 31 | (m0 << 16) | m1);
-}
+void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const {}
 
-bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
-    auto that = other.cast<GrTextureEffect>();
-    return fShaderModes[0] == that.fShaderModes[1] && fShaderModes[1] == that.fShaderModes[1] &&
-           fSubset == that.fSubset;
+bool GrTextureEffect::onIsEqual(const GrFragmentProcessor&) const { return true; }
+
+static inline bool uses_border(const GrSamplerState s) {
+    return s.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
+           s.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder;
 }
 
 GrTextureEffect::GrTextureEffect(sk_sp<GrSurfaceProxy> texture, SkAlphaType alphaType,
-                                 const SkMatrix& matrix, const Sampling& sampling)
+                                 const SkMatrix& matrix, GrSamplerState sampler)
         : GrFragmentProcessor(kGrTextureEffect_ClassID,
-                              ModulateForSamplerOptFlags(alphaType, sampling.usesDecal()))
+                              ModulateForSamplerOptFlags(alphaType, uses_border(sampler)))
         , fCoordTransform(matrix, texture.get())
-        , fSampler(std::move(texture), sampling.fHWSampler)
-        , fSubset(sampling.fShaderSubset)
-        , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]} {
-    // We always compare the range even when it isn't used so assert we have canonical don't care
-    // values.
-    SkASSERT(fShaderModes[0] != ShaderMode::kNone || (fSubset.fLeft == 0 && fSubset.fRight == 0));
-    SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
+        , fSampler(std::move(texture), sampler) {
     this->setTextureSamplerCnt(1);
     this->addCoordTransform(&fCoordTransform);
 }
@@ -408,9 +64,7 @@
 GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
         : INHERITED(kGrTextureEffect_ClassID, src.optimizationFlags())
         , fCoordTransform(src.fCoordTransform)
-        , fSampler(src.fSampler)
-        , fSubset(src.fSubset)
-        , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]} {
+        , fSampler(src.fSampler) {
     this->setTextureSamplerCnt(1);
     this->addCoordTransform(&fCoordTransform);
 }
@@ -441,6 +95,6 @@
                                              : GrSamplerState::Filter::kNearest);
 
     const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
-    return GrTextureEffect::Make(std::move(proxy), at, matrix, params, *testData->caps());
+    return GrTextureEffect::Make(std::move(proxy), at, matrix, params);
 }
 #endif
diff --git a/src/gpu/effects/GrTextureEffect.h b/src/gpu/effects/GrTextureEffect.h
index 9e62a48..c6fc305 100644
--- a/src/gpu/effects/GrTextureEffect.h
+++ b/src/gpu/effects/GrTextureEffect.h
@@ -15,117 +15,23 @@
 
 class GrTextureEffect : public GrFragmentProcessor {
 public:
-    /** Make from a filter. The sampler will be configured with clamp mode. */
-    static std::unique_ptr<GrFragmentProcessor> Make(
-            sk_sp<GrSurfaceProxy>,
-            SkAlphaType,
-            const SkMatrix& = SkMatrix::I(),
-            GrSamplerState::Filter = GrSamplerState::Filter::kNearest);
-
-    /**
-     * Make from a full GrSamplerState. Caps are required to determine support for kClampToBorder.
-     * This will be emulated in the shader if there is no hardware support.
-     */
     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy>,
                                                      SkAlphaType,
-                                                     const SkMatrix&,
-                                                     GrSamplerState,
-                                                     const GrCaps& caps);
-
-    /**
-     * Makes a texture effect that samples a subset of a texture. The wrap modes of the
-     * GrSampleState are applied to the subset in the shader rather than using HW samplers.
-     * The 'subset' parameter specifies the texels in the base level. The shader code will
-     * avoid allowing bilerp filtering to read outside the texel window. However, if MIP
-     * filtering is used and a shader invocation reads from a level other than the base
-     * then it may read texel values that were computed from in part from base level texels
-     * outside the window. More specifically, we treat the MIP map case exactly like the
-     * bilerp case in terms of how the final texture coords are computed.
-     */
-    static std::unique_ptr<GrFragmentProcessor> MakeTexelSubset(sk_sp<GrSurfaceProxy>,
-                                                                SkAlphaType,
-                                                                const SkMatrix&,
-                                                                GrSamplerState,
-                                                                const SkIRect& subset,
-                                                                const GrCaps& caps);
-    /**
-     * The same as above but also takes a 'domain' that specifies any known limit on the post-
-     * matrix texture coords that will be used to sample the texture. Specifying this requires
-     * knowledge of how this effect will be nested into a paint, the local coords used with the
-     * draw, etc. It is only used to attempt to optimize away the shader subset calculations.
-     */
-    static std::unique_ptr<GrFragmentProcessor> MakeTexelSubset(sk_sp<GrSurfaceProxy>,
-                                                                SkAlphaType,
-                                                                const SkMatrix&,
-                                                                GrSamplerState,
-                                                                const SkIRect& subset,
-                                                                const SkRect& domain,
-                                                                const GrCaps& caps);
-
-    /**
-     * This is similar to MakeTexelSubset but takes a float rather than integer subset rect.
-     * No adjustment is done for filtering, the texture coordinates are limited to the
-     * unmodified subset. The subset should be unnormalized. The effect will apply texture
-     * coordinate normalization after subset restriction (logically).
-     */
-    static std::unique_ptr<GrFragmentProcessor> MakeSubset(sk_sp<GrSurfaceProxy>,
-                                                           SkAlphaType,
-                                                           const SkMatrix&,
-                                                           GrSamplerState,
-                                                           const SkRect& subset,
-                                                           const GrCaps& caps);
-
-    /**
-     * The same as above but also takes a 'domain' that specifies any known limit on the post-
-     * matrix texture coords that will be used to sample the texture. Specifying this requires
-     * knowledge of how this effect will be nested into a paint, the local coords used with the
-     * draw, etc. It is only used to attempt to optimize away the shader subset calculations.
-     */
-    static std::unique_ptr<GrFragmentProcessor> MakeSubset(sk_sp<GrSurfaceProxy>,
-                                                           SkAlphaType,
-                                                           const SkMatrix&,
-                                                           GrSamplerState,
-                                                           const SkRect& subset,
-                                                           const SkRect& domain,
-                                                           const GrCaps& caps);
+                                                     const SkMatrix& = SkMatrix::I(),
+                                                     GrSamplerState = {});
 
     std::unique_ptr<GrFragmentProcessor> clone() const override;
 
-    const char* name() const override { return "TextureEffect"; }
+    const char* name() const override { return "SimpleTextureEffect"; }
 
 private:
-    enum class ShaderMode : uint16_t {
-        kClamp         = static_cast<int>(GrSamplerState::WrapMode::kClamp),
-        kRepeat        = static_cast<int>(GrSamplerState::WrapMode::kRepeat),
-        kMirrorRepeat  = static_cast<int>(GrSamplerState::WrapMode::kMirrorRepeat),
-        kDecal         = static_cast<int>(GrSamplerState::WrapMode::kClampToBorder),
-        kNone,
-    };
-
-    struct Sampling {
-        GrSamplerState fHWSampler;
-        ShaderMode fShaderModes[2] = {ShaderMode::kNone, ShaderMode::kNone};
-        SkRect fShaderSubset = {0, 0, 0, 0};
-        Sampling(GrSamplerState::Filter filter) : fHWSampler(filter) {}
-        Sampling(GrSamplerState, SkISize, const GrCaps&);
-        Sampling(const GrSurfaceProxy& proxy,
-                 GrSamplerState sampler,
-                 const SkRect&,
-                 bool,
-                 const SkRect*,
-                 const GrCaps&);
-        inline bool usesDecal() const;
-    };
-
     GrCoordTransform fCoordTransform;
     TextureSampler fSampler;
-    SkRect fSubset;
-    ShaderMode fShaderModes[2];
-
-    inline GrTextureEffect(sk_sp<GrSurfaceProxy>, SkAlphaType, const SkMatrix&, const Sampling&);
 
     GrTextureEffect(const GrTextureEffect& src);
 
+    inline GrTextureEffect(sk_sp<GrSurfaceProxy>, SkAlphaType, const SkMatrix&, GrSamplerState);
+
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
 
     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
index a85b810..987db7e 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.cpp
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -19,7 +19,6 @@
                                                             const SkYUVAIndex yuvaIndices[4],
                                                             SkYUVColorSpace yuvColorSpace,
                                                             GrSamplerState::Filter filterMode,
-                                                            const GrCaps& caps,
                                                             const SkMatrix& localMatrix,
                                                             const SkRect* domain) {
     int numPlanes;
@@ -64,18 +63,16 @@
         } else if (domain) {
             planeDomain = *domain;
         }
-
+        planeFPs[i] =
+                GrTextureEffect::Make(proxies[i], kUnknown_SkAlphaType, *planeMatrix, planeFilter);
         if (domain) {
             SkASSERT(planeFilter != GrSamplerState::Filter::kMipMap);
             if (planeFilter != GrSamplerState::Filter::kNearest) {
                 // Inset by half a pixel for bilerp, after scaling to the size of the plane
                 planeDomain.inset(0.5f, 0.5f);
             }
-            planeFPs[i] = GrTextureEffect::MakeSubset(proxies[i], kUnknown_SkAlphaType,
-                                                      *planeMatrix, planeFilter, planeDomain, caps);
-        } else {
-            planeFPs[i] = GrTextureEffect::Make(proxies[i], kUnknown_SkAlphaType, *planeMatrix,
-                                                planeFilter);
+            planeFPs[i] = GrDomainEffect::Make(std::move(planeFPs[i]), planeDomain,
+                                               GrTextureDomain::kClamp_Mode, false);
         }
     }
 
@@ -190,6 +187,7 @@
 
         UniformHandle fColorSpaceMatrixVar;
         UniformHandle fColorSpaceTranslateVar;
+        GrTextureDomain::GLDomain fGLDomains[4];
     };
 
     return new GrGLSLYUVtoRGBEffect;
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.h b/src/gpu/effects/GrYUVtoRGBEffect.h
index 6ed3b63..b99de32 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.h
+++ b/src/gpu/effects/GrYUVtoRGBEffect.h
@@ -10,9 +10,11 @@
 
 #include "include/core/SkTypes.h"
 
-#include "include/core/SkYUVAIndex.h"
 #include "src/gpu/GrCoordTransform.h"
 #include "src/gpu/GrFragmentProcessor.h"
+#include "src/gpu/effects/GrTextureDomain.h"
+
+#include "include/core/SkYUVAIndex.h"
 
 class GrYUVtoRGBEffect : public GrFragmentProcessor {
 public:
@@ -24,7 +26,6 @@
                                                      const SkYUVAIndex indices[4],
                                                      SkYUVColorSpace yuvColorSpace,
                                                      GrSamplerState::Filter filterMode,
-                                                     const GrCaps&,
                                                      const SkMatrix& localMatrix = SkMatrix::I(),
                                                      const SkRect* domain = nullptr);
 #ifdef SK_DEBUG
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 2eb257c..3b7fafc 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -30,6 +30,7 @@
 #include "src/gpu/GrTexturePriv.h"
 #include "src/gpu/GrTextureProxy.h"
 #include "src/gpu/SkGr.h"
+#include "src/gpu/effects/GrTextureDomain.h"
 #include "src/gpu/effects/generated/GrClampFragmentProcessor.h"
 #include "src/gpu/geometry/GrQuad.h"
 #include "src/gpu/geometry/GrQuadBuffer.h"
@@ -1071,21 +1072,13 @@
 
         GrSurfaceProxy* proxy = proxyView.proxy();
         std::unique_ptr<GrFragmentProcessor> fp;
+        fp = GrTextureEffect::Make(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter);
         if (domain) {
             // Update domain to match what GrTextureOp would do for bilerp, but don't do any
-            // normalization since GrTextureEffect handles that and the origin.
+            // normalization since GrTextureDomainEffect handles that and the origin.
             SkRect correctedDomain = normalize_domain(filter, {1.f, 1.f, 0.f}, domain);
-            const auto& caps = *context->priv().caps();
-            SkRect localRect;
-            if (localQuad.asRect(&localRect)) {
-                fp = GrTextureEffect::MakeSubset(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter,
-                                                 correctedDomain, localRect, caps);
-            } else {
-                fp = GrTextureEffect::MakeSubset(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter,
-                                                 correctedDomain, caps);
-            }
-        } else {
-            fp = GrTextureEffect::Make(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter);
+            fp = GrDomainEffect::Make(std::move(fp), correctedDomain, GrTextureDomain::kClamp_Mode,
+                                      filter);
         }
         fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform));
         paint.addColorFragmentProcessor(std::move(fp));
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 6a21b93..44a0275 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -348,9 +348,8 @@
     GrPaint paint;
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-    const auto& caps = *ctx->priv().caps();
     auto fp = GrYUVtoRGBEffect::Make(proxies, yuvaIndices, yuvColorSpace,
-                                     GrSamplerState::Filter::kNearest, caps);
+                                     GrSamplerState::Filter::kNearest);
     if (colorSpaceXform) {
         fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(colorSpaceXform));
     }
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index 2e3bba5..a88c349 100755
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -204,6 +204,22 @@
     GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
                                             tile_mode_to_wrap_mode(fTileModeY)};
 
+    // If either domainX or domainY are un-ignored, a texture domain effect has to be used to
+    // implement the decal mode (while leaving non-decal axes alone). The wrap mode originally
+    // clamp-to-border is reset to clamp since the hw cannot implement it directly.
+    GrTextureDomain::Mode domainX = GrTextureDomain::kIgnore_Mode;
+    GrTextureDomain::Mode domainY = GrTextureDomain::kIgnore_Mode;
+    if (!args.fContext->priv().caps()->clampToBorderSupport()) {
+        if (wrapModes[0] == GrSamplerState::WrapMode::kClampToBorder) {
+            domainX = GrTextureDomain::kDecal_Mode;
+            wrapModes[0] = GrSamplerState::WrapMode::kClamp;
+        }
+        if (wrapModes[1] == GrSamplerState::WrapMode::kClampToBorder) {
+            domainY = GrTextureDomain::kDecal_Mode;
+            wrapModes[1] = GrSamplerState::WrapMode::kClamp;
+        }
+    }
+
     // Must set wrap and filter on the sampler before requesting a texture. In two places below
     // we check the matrix scale factors to determine how to interpret the filter quality setting.
     // This completely ignores the complexity of the drawVertices case where explicit local coords
@@ -224,31 +240,22 @@
 
     lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
 
-    const auto& caps = *args.fContext->priv().caps();
-
     std::unique_ptr<GrFragmentProcessor> inner;
     if (doBicubic) {
-        // If either domainX or domainY are un-ignored, a texture domain effect has to be used to
-        // implement the decal mode (while leaving non-decal axes alone). The wrap mode originally
-        // clamp-to-border is reset to clamp since the hw cannot implement it directly.
-        GrTextureDomain::Mode domainX = GrTextureDomain::kIgnore_Mode;
-        GrTextureDomain::Mode domainY = GrTextureDomain::kIgnore_Mode;
-        if (!caps.clampToBorderSupport()) {
-            if (wrapModes[0] == GrSamplerState::WrapMode::kClampToBorder) {
-                domainX = GrTextureDomain::kDecal_Mode;
-                wrapModes[0] = GrSamplerState::WrapMode::kClamp;
-            }
-            if (wrapModes[1] == GrSamplerState::WrapMode::kClampToBorder) {
-                domainY = GrTextureDomain::kDecal_Mode;
-                wrapModes[1] = GrSamplerState::WrapMode::kClamp;
-            }
-        }
+        // domainX and domainY will properly apply the decal effect with the texture domain used in
+        // the bicubic filter if clamp to border was unsupported in hardware
         static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
         inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes, domainX, domainY,
                                       kDir, srcAlphaType);
     } else {
-        inner = GrTextureEffect::Make(std::move(proxy), srcAlphaType, lmInverse, samplerState,
-                                      caps);
+        auto dimensions = proxy->dimensions();
+        inner = GrTextureEffect::Make(std::move(proxy), srcAlphaType, lmInverse, samplerState);
+        if (domainX != GrTextureDomain::kIgnore_Mode || domainY != GrTextureDomain::kIgnore_Mode) {
+            SkRect domain = GrTextureDomain::MakeTexelDomain(SkIRect::MakeSize(dimensions),
+                                                             domainX, domainY);
+            inner = GrDomainEffect::Make(std::move(inner), domain, domainX, domainY,
+                                         samplerState.filter());
+        }
     }
     inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(), srcAlphaType,
                                           args.fDstColorInfo->colorSpace());