Remove default implementation of GrEffect::isEqual. Make GrSingleTextureEffect abstract.
Review URL: https://codereview.appspot.com/7142049

git-svn-id: http://skia.googlecode.com/svn/trunk@7254 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
index 1f88826..da52aad 100644
--- a/gm/texdata.cpp
+++ b/gm/texdata.cpp
@@ -12,7 +12,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
-#include "effects/GrSingleTextureEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
 #include "SkColorPriv.h"
 #include "SkDevice.h"
 
@@ -113,7 +113,7 @@
                 SkMatrix tm;
                 tm = vm;
                 tm.postIDiv(2*S, 2*S);
-                paint.colorStage(0)->setEffect(GrSingleTextureEffect::Create(texture, tm))->unref();
+                paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(texture, tm))->unref();
 
                 ctx->drawRect(paint, GrRect::MakeWH(2*S, 2*S));
 
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 3248f03..bf301bb 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -128,6 +128,8 @@
       '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h',
       '<(skia_src_path)/gpu/effects/GrConvolutionEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrConvolutionEffect.h',
+      '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.h',
       '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.h',
       '<(skia_src_path)/gpu/effects/GrTextureDomainEffect.cpp',
diff --git a/include/gpu/GrEffect.h b/include/gpu/GrEffect.h
index 1e0ce79..507bd57 100644
--- a/include/gpu/GrEffect.h
+++ b/include/gpu/GrEffect.h
@@ -104,21 +104,30 @@
      */
     virtual const GrBackendEffectFactory& getFactory() const = 0;
 
-    /** Returns true if the other effect will generate identical output.
-        Must only be called if the two are already known to be of the
-        same type (i.e.  they return the same value from getFactory()).
+    /** Returns true if this and other effect conservatively draw identically. It can only return
+        true when the two effects are of the same subclass (i.e. they return the same object from
+        from getFactory()).
 
-        Equality is not the same thing as equivalence.
-        To test for equivalence (that they will generate the same
-        shader code, but may have different uniforms), check equality
-        of the EffectKey produced by the GrBackendEffectFactory:
-        a.getFactory().glEffectKey(a) == b.getFactory().glEffectKey(b).
-
-        The default implementation of this function returns true iff
-        the two stages have the same return value for numTextures() and
-        for texture() over all valid indices.
+        A return value of true from isEqual() should not be used to test whether the effects would
+        generate the same shader code. To test for identical code generation use the EffectKey
+        computed by the GrBackendEffectFactory:
+            effectA.getFactory().glEffectKey(effectA) == effectB.getFactory().glEffectKey(effectB).
      */
-    virtual bool isEqual(const GrEffect&) const;
+    bool isEqual(const GrEffect& other) const {
+        if (&this->getFactory() != &other.getFactory()) {
+            return false;
+        }
+        bool result = this->onIsEqual(other);
+#if GR_DEBUG
+        if (result) {
+            GrAssert(this->numTextures() == other.numTextures());
+            for (int i = 0; i < this->numTextures(); ++i) {
+                GrAssert(*fTextureAccesses[i] == *other.fTextureAccesses[i]);
+            }
+        }
+#endif
+        return result;
+    }
 
     /** Human-meaningful string to identify this effect; may be embedded
         in generated shader code. */
@@ -167,6 +176,12 @@
     }
 
 private:
+
+    /** Subclass implements this to support isEqual(). It will only be called if it is known that
+        the two effects are of the same subclass (i.e. they return the same object
+        from getFactory()).*/
+    virtual bool onIsEqual(const GrEffect& other) const = 0;
+
     void EffectRefDestroyed() {
         fEffectRef = NULL;
     }
diff --git a/include/gpu/GrEffectStage.h b/include/gpu/GrEffectStage.h
index 597ea49..0060152 100644
--- a/include/gpu/GrEffectStage.h
+++ b/include/gpu/GrEffectStage.h
@@ -39,10 +39,6 @@
             return false;
         }
 
-        if (this->getEffect()->getFactory() != other.getEffect()->getFactory()) {
-            return false;
-        }
-
         if (!this->getEffect()->isEqual(*other.getEffect())) {
             return false;
         }
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index 2f7173e..d91b978 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -332,7 +332,7 @@
 #if SK_SUPPORT_GPU
 
 #include "GrTextureAccess.h"
-#include "effects/GrSingleTextureEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
 #include "SkGr.h"
 
 GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
@@ -360,7 +360,7 @@
         return NULL;
     }
 
-    GrEffectRef* effect = GrSingleTextureEffect::Create(texture, matrix, params);
+    GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
     GrUnlockCachedBitmapTexture(texture);
     return effect;
 }
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
index 5a93b47..43915b3 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -152,7 +152,6 @@
 
     virtual ~GrBlendEffect();
 
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
     const GrBackendEffectFactory& getFactory() const;
     SkBlendImageFilter::Mode mode() const { return fMode; }
 
@@ -162,6 +161,8 @@
     void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, GrTexture* background);
     GrTextureAccess             fForegroundAccess;
     GrTextureAccess             fBackgroundAccess;
@@ -245,9 +246,11 @@
 GrBlendEffect::~GrBlendEffect() {
 }
 
-bool GrBlendEffect::isEqual(const GrEffect& sBase) const {
+bool GrBlendEffect::onIsEqual(const GrEffect& sBase) const {
     const GrBlendEffect& s = static_cast<const GrBlendEffect&>(sBase);
-    return INHERITED::isEqual(sBase) && fMode == s.fMode;
+    return fForegroundAccess.getTexture() == s.fForegroundAccess.getTexture() &&
+           fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture() &&
+           fMode == s.fMode;
 }
 
 const GrBackendEffectFactory& GrBlendEffect::getFactory() const {
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index 0fb438d..06fb4ef 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -336,11 +336,6 @@
         return GrTBackendEffectFactory<ColorMatrixEffect>::getInstance();
     }
 
-    virtual bool isEqual(const GrEffect& s) const {
-        const ColorMatrixEffect& cme = static_cast<const ColorMatrixEffect&>(s);
-        return cme.fMatrix == fMatrix;
-    }
-
     virtual void getConstantColorComponents(GrColor* color,
                                             uint32_t* validFlags) const SK_OVERRIDE {
         // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
@@ -456,6 +451,11 @@
 private:
     ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) {}
 
+    virtual bool onIsEqual(const GrEffect& s) const {
+        const ColorMatrixEffect& cme = static_cast<const ColorMatrixEffect&>(s);
+        return cme.fMatrix == fMatrix;
+    }
+
     SkColorMatrix fMatrix;
 
     typedef GrGLEffect INHERITED;
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 9a67ac3..17abe28 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -245,7 +245,6 @@
 
     virtual ~GrDisplacementMapEffect();
 
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
     const GrBackendEffectFactory& getFactory() const;
     SkDisplacementMapEffect::ChannelSelectorType xChannelSelector() const
         { return fXChannelSelector; }
@@ -259,6 +258,8 @@
     void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
                             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
                             SkScalar scale, GrTexture* displacement, GrTexture* color);
@@ -360,10 +361,13 @@
 GrDisplacementMapEffect::~GrDisplacementMapEffect() {
 }
 
-bool GrDisplacementMapEffect::isEqual(const GrEffect& sBase) const {
+bool GrDisplacementMapEffect::onIsEqual(const GrEffect& sBase) const {
     const GrDisplacementMapEffect& s = static_cast<const GrDisplacementMapEffect&>(sBase);
-    return INHERITED::isEqual(sBase) && fXChannelSelector == s.fXChannelSelector &&
-           fYChannelSelector == s.fYChannelSelector && fScale == s.fScale;
+    return fDisplacementAccess.getTexture() == s.fDisplacementAccess.getTexture() &&
+           fColorAccess.getTexture() == s.fColorAccess.getTexture() &&
+           fXChannelSelector == s.fXChannelSelector &&
+           fYChannelSelector == s.fYChannelSelector &&
+           fScale == s.fScale;
 }
 
 const GrBackendEffectFactory& GrDisplacementMapEffect::getFactory() const {
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index ddb033d..780a3a4 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -307,8 +307,6 @@
     GrLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale);
     virtual ~GrLightingEffect();
 
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
-
     const SkLight* light() const { return fLight; }
     SkScalar surfaceScale() const { return fSurfaceScale; }
 
@@ -318,6 +316,9 @@
         *validFlags = 0;
     }
 
+protected:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
 private:
     typedef GrSingleTextureEffect INHERITED;
     const SkLight* fLight;
@@ -342,9 +343,11 @@
     typedef GrGLDiffuseLightingEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
     SkScalar kd() const { return fKD; }
+
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GrDiffuseLightingEffect(GrTexture* texture,
                             const SkLight* light,
                             SkScalar surfaceScale,
@@ -374,11 +377,12 @@
     typedef GrGLSpecularLightingEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
     SkScalar ks() const { return fKS; }
     SkScalar shininess() const { return fShininess; }
 
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GrSpecularLightingEffect(GrTexture* texture,
                              const SkLight* light,
                              SkScalar surfaceScale,
@@ -1050,10 +1054,9 @@
     fLight->unref();
 }
 
-bool GrLightingEffect::isEqual(const GrEffect& sBase) const {
-    const GrLightingEffect& s =
-        static_cast<const GrLightingEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
+bool GrLightingEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrLightingEffect& s = static_cast<const GrLightingEffect&>(sBase);
+    return this->texture(0) == s.texture(0) &&
            fLight->isEqual(*s.fLight) &&
            fSurfaceScale == s.fSurfaceScale;
 }
@@ -1068,10 +1071,10 @@
     return GrTBackendEffectFactory<GrDiffuseLightingEffect>::getInstance();
 }
 
-bool GrDiffuseLightingEffect::isEqual(const GrEffect& sBase) const {
+bool GrDiffuseLightingEffect::onIsEqual(const GrEffect& sBase) const {
     const GrDiffuseLightingEffect& s =
         static_cast<const GrDiffuseLightingEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
+    return INHERITED::onIsEqual(sBase) &&
             this->kd() == s.kd();
 }
 
@@ -1280,10 +1283,10 @@
     return GrTBackendEffectFactory<GrSpecularLightingEffect>::getInstance();
 }
 
-bool GrSpecularLightingEffect::isEqual(const GrEffect& sBase) const {
+bool GrSpecularLightingEffect::onIsEqual(const GrEffect& sBase) const {
     const GrSpecularLightingEffect& s =
         static_cast<const GrSpecularLightingEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
+    return INHERITED::onIsEqual(sBase) &&
            this->ks() == s.ks() &&
            this->shininess() == s.shininess();
 }
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index e7f266e..bcf0baf 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -48,7 +48,7 @@
     static const char* Name() { return "Magnifier"; }
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     float x_offset() const { return fXOffset; }
     float y_offset() const { return fYOffset; }
@@ -75,6 +75,8 @@
         , fXInset(xInset)
         , fYInset(yInset) {}
 
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GR_DECLARE_EFFECT_TEST;
 
     float fXOffset;
@@ -230,10 +232,10 @@
     return GrTBackendEffectFactory<GrMagnifierEffect>::getInstance();
 }
 
-bool GrMagnifierEffect::isEqual(const GrEffect& sBase) const {
-     const GrMagnifierEffect& s =
-        static_cast<const GrMagnifierEffect&>(sBase);
-    return (this->fXOffset == s.fXOffset &&
+bool GrMagnifierEffect::onIsEqual(const GrEffect& sBase) const {
+     const GrMagnifierEffect& s = static_cast<const GrMagnifierEffect&>(sBase);
+    return (this->texture(0) == s.texture(0) &&
+            this->fXOffset == s.fXOffset &&
             this->fYOffset == s.fYOffset &&
             this->fXZoom == s.fXZoom &&
             this->fYZoom == s.fYZoom &&
@@ -241,6 +243,10 @@
             this->fYInset == s.fYInset);
 }
 
+void GrMagnifierEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp
index 03b10be..50933ee 100644
--- a/src/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -287,7 +287,6 @@
 
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
 
 private:
     GrMatrixConvolutionEffect(GrTexture*,
@@ -299,6 +298,8 @@
                               TileMode tileMode,
                               bool convolveAlpha);
 
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     SkISize  fKernelSize;
     float   *fKernel;
     float    fGain;
@@ -518,10 +519,10 @@
     return GrTBackendEffectFactory<GrMatrixConvolutionEffect>::getInstance();
 }
 
-bool GrMatrixConvolutionEffect::isEqual(const GrEffect& sBase) const {
+bool GrMatrixConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
     const GrMatrixConvolutionEffect& s =
         static_cast<const GrMatrixConvolutionEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
+    return this->texture(0) == s.texture(0) &&
            fKernelSize == s.kernelSize() &&
            !memcmp(fKernel, s.kernel(), fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
            fGain == s.gain() &&
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index ed30aca..c409435 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -257,13 +257,15 @@
     typedef GrGLMorphologyEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
 protected:
 
     MorphologyType fType;
 
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
 
     GR_DECLARE_EFFECT_TEST;
@@ -399,15 +401,21 @@
     return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance();
 }
 
-bool GrMorphologyEffect::isEqual(const GrEffect& sBase) const {
+bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const {
     const GrMorphologyEffect& s =
         static_cast<const GrMorphologyEffect&>(sBase);
-    return (INHERITED::isEqual(sBase) &&
+    return (this->texture(0) == s.texture(0) &&
             this->radius() == s.radius() &&
             this->direction() == s.direction() &&
             this->type() == s.type());
 }
 
+void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // This is valid because the color components of the result of the kernel all come
+    // exactly from existing values in the source texture.
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_EFFECT_TEST(GrMorphologyEffect);
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 0a922e7..afb4a99 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -235,13 +235,14 @@
 
     static const char* Name() { return "ColorTable"; }
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
 
     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     typedef GLColorTableEffect GLEffect;
 
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     explicit ColorTableEffect(GrTexture* texture, unsigned flags);
 
     GR_DECLARE_EFFECT_TEST;
@@ -344,8 +345,8 @@
     return GrTBackendEffectFactory<ColorTableEffect>::getInstance();
 }
 
-bool ColorTableEffect::isEqual(const GrEffect& sBase) const {
-    return INHERITED::isEqual(sBase);
+bool ColorTableEffect::onIsEqual(const GrEffect& sBase) const {
+    return this->texture(0) == sBase.texture(0);
 }
 
 void ColorTableEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index ba72eea..7645024 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -829,6 +829,24 @@
     }
 }
 
+bool GrGradientEffect::onIsEqual(const GrEffect& effect) const {
+    const GrGradientEffect& s = static_cast<const GrGradientEffect&>(effect);
+    return fTextureAccess.getTexture() == s.fTextureAccess.getTexture()  &&
+           fTextureAccess.getParams().getTileModeX() ==
+                s.fTextureAccess.getParams().getTileModeX() &&
+           this->useAtlas() == s.useAtlas() &&
+           fYCoord == s.getYCoord() &&
+           fMatrix.cheapEqualTo(s.getMatrix());
+}
+
+void GrGradientEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    if (fIsOpaque && (kA_ValidComponentFlag & *validFlags) && 0xff == GrColorUnpackA(*color)) {
+        *validFlags = kA_ValidComponentFlag;
+    } else {
+        *validFlags = 0;
+    }
+}
+
 int GrGradientEffect::RandomGradientParams(SkRandom* random,
                                            SkColor colors[],
                                            SkScalar** stops,
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index 27c4438..1828905 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -238,20 +238,7 @@
     SkScalar getYCoord() const { return fYCoord; };
     const SkMatrix& getMatrix() const { return fMatrix;}
 
-    virtual bool isEqual(const GrEffect& effect) const SK_OVERRIDE {
-        const GrGradientEffect& s = static_cast<const GrGradientEffect&>(effect);
-        return INHERITED::isEqual(effect) && this->useAtlas() == s.useAtlas() &&
-               fYCoord == s.getYCoord() && fMatrix.cheapEqualTo(s.getMatrix());
-    }
-
-    virtual void getConstantColorComponents(GrColor* color,
-                                            uint32_t* validFlags) const SK_OVERRIDE {
-        if (fIsOpaque && (kA_ValidComponentFlag & *validFlags) && 0xff == GrColorUnpackA(*color)) {
-            *validFlags = kA_ValidComponentFlag;
-        } else {
-            *validFlags = 0;
-        }
-    }
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
 protected:
 
@@ -268,7 +255,10 @@
                                     SkScalar** stops,
                                     SkShader::TileMode* tm);
 
+    virtual bool onIsEqual(const GrEffect& effect) const SK_OVERRIDE;
+
 private:
+
     GrTextureAccess fTextureAccess;
     SkScalar fYCoord;
     GrTextureStripAtlas* fAtlas;
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
index 0a15424..23b976a 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -383,13 +383,6 @@
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendEffectFactory<GrConical2Gradient>::getInstance();
     }
-    virtual bool isEqual(const GrEffect& sBase) const SK_OVERRIDE {
-        const GrConical2Gradient& s = static_cast<const GrConical2Gradient&>(sBase);
-        return (INHERITED::isEqual(sBase) &&
-                this->fCenterX1 == s.fCenterX1 &&
-                this->fRadius0 == s.fRadius0 &&
-                this->fDiffRadius == s.fDiffRadius);
-    }
 
     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
     bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
@@ -400,6 +393,14 @@
     typedef GrGLConical2Gradient GLEffect;
 
 private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const GrConical2Gradient& s = static_cast<const GrConical2Gradient&>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fDiffRadius == s.fDiffRadius);
+    }
+
     GrConical2Gradient(GrContext* ctx,
                        const SkTwoPointConicalGradient& shader,
                        const SkMatrix& matrix,
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp
index 0f79a2b..4f73510 100644
--- a/src/effects/gradients/SkTwoPointRadialGradient.cpp
+++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp
@@ -442,13 +442,6 @@
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
     }
-    virtual bool isEqual(const GrEffect& sBase) const SK_OVERRIDE {
-        const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase);
-        return (INHERITED::isEqual(sBase) &&
-                this->fCenterX1 == s.fCenterX1 &&
-                this->fRadius0 == s.fRadius0 &&
-                this->fPosRoot == s.fPosRoot);
-    }
 
     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
     bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
@@ -459,6 +452,14 @@
     typedef GrGLRadial2Gradient GLEffect;
 
 private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase);
+        return (INHERITED::isEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fPosRoot == s.fPosRoot);
+    }
+
     GrRadial2Gradient(GrContext* ctx,
                       const SkTwoPointRadialGradient& shader,
                       const SkMatrix& matrix,
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index a8ce055..43dda9c 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -1860,7 +1860,7 @@
         scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
                              i < scaleFactorY ? 0.5f : 1.0f);
 
-        paint.colorStage(0)->setEffect(GrSingleTextureEffect::Create(srcTexture,
+        paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(srcTexture,
                                                                      matrix,
                                                                      true))->unref();
         this->drawRectToRect(paint, dstRect, srcRect);
@@ -1919,7 +1919,7 @@
         // FIXME:  This should be mitchell, not bilinear.
         matrix.setIDiv(srcTexture->width(), srcTexture->height());
         this->setRenderTarget(dstTexture->asRenderTarget());
-        paint.colorStage(0)->setEffect(GrSingleTextureEffect::Create(srcTexture,
+        paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(srcTexture,
                                                                      matrix,
                                                                      true))->unref();
         SkRect dstRect(srcRect);
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 993076b..13ed287 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -16,7 +16,7 @@
 #include "GrStencil.h"
 #include "GrTexture.h"
 #include "GrRenderTarget.h"
-#include "effects/GrSingleTextureEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
 
 #include "SkXfermode.h"
 
@@ -193,11 +193,11 @@
     ////
 
     /**
-     * Creates a GrSingleTextureEffect.
+     * Creates a GrSimpleTextureEffect.
      */
     void createTextureEffect(int stageIdx, GrTexture* texture, const SkMatrix& matrix) {
         GrAssert(!this->getStage(stageIdx).getEffect());
-        GrEffectRef* effect = GrSingleTextureEffect::Create(texture, matrix);
+        GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix);
         this->stage(stageIdx)->setEffect(effect)->unref();
     }
     void createTextureEffect(int stageIdx,
@@ -205,7 +205,7 @@
                              const SkMatrix& matrix,
                              const GrTextureParams& params) {
         GrAssert(!this->getStage(stageIdx).getEffect());
-        GrEffectRef* effect = GrSingleTextureEffect::Create(texture, matrix, params);
+        GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
         this->stage(stageIdx)->setEffect(effect)->unref();
     }
 
diff --git a/src/gpu/GrEffect.cpp b/src/gpu/GrEffect.cpp
index d1ef4b7..40a519a 100644
--- a/src/gpu/GrEffect.cpp
+++ b/src/gpu/GrEffect.cpp
@@ -86,18 +86,6 @@
     return this->getFactory().name();
 }
 
-bool GrEffect::isEqual(const GrEffect& s) const {
-    if (this->numTextures() != s.numTextures()) {
-        return false;
-    }
-    for (int i = 0; i < this->numTextures(); ++i) {
-        if (this->textureAccess(i) != s.textureAccess(i)) {
-            return false;
-        }
-    }
-    return true;
-}
-
 void GrEffect::addTextureAccess(const GrTextureAccess* access) {
     fTextureAccesses.push_back(access);
 }
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index f205569..0cf3b35 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -8,6 +8,7 @@
 #include "SkGpuDevice.h"
 
 #include "effects/GrTextureDomainEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
 
 #include "GrContext.h"
 #include "GrTextContext.h"
@@ -443,7 +444,7 @@
     GrTexture* texture = fRenderTarget->asTexture();
     if (NULL != texture) {
         paint->colorStage(kBitmapTextureIdx)->setEffect(
-            GrSingleTextureEffect::Create(texture, SkMatrix::I()))->unref();
+            GrSimpleTextureEffect::Create(texture, SkMatrix::I()))->unref();
         return true;
     }
     return false;
@@ -797,7 +798,7 @@
             // Blend pathTexture over blurTexture.
             context->setRenderTarget(blurTexture->asRenderTarget());
             paint.colorStage(0)->setEffect(
-                GrSingleTextureEffect::Create(pathTexture, matrix))->unref();
+                GrSimpleTextureEffect::Create(pathTexture, matrix))->unref();
             if (SkMaskFilter::kInner_BlurType == blurType) {
                 // inner:  dst = dst * src
                 paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
@@ -829,7 +830,7 @@
 
     grp->coverageStage(MASK_IDX)->reset();
     grp->coverageStage(MASK_IDX)->setEffect(
-        GrSingleTextureEffect::Create(blurTexture, matrix))->unref();
+        GrSimpleTextureEffect::Create(blurTexture, matrix))->unref();
     context->drawRect(*grp, finalRect);
     return true;
 }
@@ -885,7 +886,7 @@
     m.setTranslate(-dstM.fBounds.fLeft*SK_Scalar1, -dstM.fBounds.fTop*SK_Scalar1);
     m.postIDiv(texture->width(), texture->height());
 
-    grp->coverageStage(MASK_IDX)->setEffect(GrSingleTextureEffect::Create(texture, m))->unref();
+    grp->coverageStage(MASK_IDX)->setEffect(GrSimpleTextureEffect::Create(texture, m))->unref();
     GrRect d;
     d.setLTRB(SkIntToScalar(dstM.fBounds.fLeft),
               SkIntToScalar(dstM.fBounds.fTop),
@@ -1340,7 +1341,7 @@
                                                    GrTextureDomainEffect::kClamp_WrapMode,
                                                    params.isBilerp()));
     } else {
-        effect.reset(GrSingleTextureEffect::Create(texture, SkMatrix::I(), params));
+        effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));
     }
     grPaint->colorStage(kBitmapTextureIdx)->setEffect(effect);
     fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
@@ -1418,7 +1419,7 @@
     // draw sprite uses the default texture params
     SkAutoCachedTexture act(this, bitmap, NULL, &texture);
     grPaint.colorStage(kBitmapTextureIdx)->setEffect(
-        GrSingleTextureEffect::Create(texture, SkMatrix::I()))->unref();
+        GrSimpleTextureEffect::Create(texture, SkMatrix::I()))->unref();
 
     SkImageFilter* filter = paint.getImageFilter();
     if (NULL != filter) {
@@ -1426,7 +1427,7 @@
                  GrRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)));
         if (filteredTexture) {
             grPaint.colorStage(kBitmapTextureIdx)->setEffect(
-                GrSingleTextureEffect::Create(filteredTexture, SkMatrix::I()))->unref();
+                GrSimpleTextureEffect::Create(filteredTexture, SkMatrix::I()))->unref();
             texture = filteredTexture;
             filteredTexture->unref();
         }
@@ -1500,7 +1501,7 @@
         GrTexture* filteredTexture = filter_texture(this, fContext, devTex, filter, rect);
         if (filteredTexture) {
             grPaint.colorStage(kBitmapTextureIdx)->setEffect(
-                GrSingleTextureEffect::Create(filteredTexture, SkMatrix::I()))->unref();
+                GrSimpleTextureEffect::Create(filteredTexture, SkMatrix::I()))->unref();
             devTex = filteredTexture;
             filteredTexture->unref();
         }
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 5e99dad..309dcd8 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -8,6 +8,7 @@
 #include "GrConfigConversionEffect.h"
 #include "GrContext.h"
 #include "GrTBackendEffectFactory.h"
+#include "GrSimpleTextureEffect.h"
 #include "gl/GrGLEffect.h"
 #include "gl/GrGLEffectMatrix.h"
 #include "SkMatrix.h"
@@ -115,9 +116,16 @@
     return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance();
 }
 
-bool GrConfigConversionEffect::isEqual(const GrEffect& s) const {
+bool GrConfigConversionEffect::onIsEqual(const GrEffect& s) const {
     const GrConfigConversionEffect& other = static_cast<const GrConfigConversionEffect&>(s);
-    return other.fSwapRedAndBlue == fSwapRedAndBlue && other.fPMConversion == fPMConversion;
+    return this->texture(0) == s.texture(0) &&
+           other.fSwapRedAndBlue == fSwapRedAndBlue &&
+           other.fPMConversion == fPMConversion;
+}
+
+void GrConfigConversionEffect::getConstantColorComponents(GrColor* color,
+                                                          uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -263,7 +271,7 @@
         // If we returned a GrConfigConversionEffect that was equivalent to a GrSingleTextureEffect
         // then we may pollute our texture cache with redundant shaders. So in the case that no
         // conversions were requested we instead return a GrSingleTextureEffect.
-        stage->setEffect(GrSingleTextureEffect::Create(texture, matrix))->unref();
+        stage->setEffect(GrSimpleTextureEffect::Create(texture, matrix))->unref();
         return true;
     } else {
         if (kRGBA_8888_GrPixelConfig != texture->config() &&
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index b8dd7d0..3845d32 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -45,7 +45,8 @@
     typedef GrGLConfigConversionEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     bool swapsRedAndBlue() const { return fSwapRedAndBlue; }
     PMConversion  pmConversion() const { return fPMConversion; }
@@ -65,6 +66,8 @@
                             PMConversion pmConversion,
                             const SkMatrix& matrix);
 
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     bool            fSwapRedAndBlue;
     PMConversion    fPMConversion;
 
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
index 1f4c094..82c908a 100644
--- a/src/gpu/effects/GrConvolutionEffect.cpp
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -167,10 +167,9 @@
     return GrTBackendEffectFactory<GrConvolutionEffect>::getInstance();
 }
 
-bool GrConvolutionEffect::isEqual(const GrEffect& sBase) const {
-     const GrConvolutionEffect& s =
-        static_cast<const GrConvolutionEffect&>(sBase);
-    return (INHERITED::isEqual(sBase) &&
+bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
+     const GrConvolutionEffect& s = static_cast<const GrConvolutionEffect&>(sBase);
+    return (this->texture(0) == s.texture(0) &&
             this->radius() == s.radius() &&
             this->direction() == s.direction() &&
             0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h
index 4f0c9ec..944d34f 100644
--- a/src/gpu/effects/GrConvolutionEffect.h
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -51,7 +51,12 @@
     typedef GrGLConvolutionEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+        // If the texture was opaque we could know that the output color if we knew the sum of the
+        // kernel values.
+        *validFlags = 0;
+    }
 
     enum {
         // This was decided based on the min allowed value for the max texture
@@ -77,6 +82,8 @@
                         int halfWidth,
                         float gaussianSigma);
 
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GR_DECLARE_EFFECT_TEST;
 
     typedef Gr1DKernelEffect INHERITED;
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
new file mode 100644
index 0000000..69ab13e
--- /dev/null
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrSimpleTextureEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrTexture.h"
+
+class GrGLSimpleTextureEffect : public GrGLEffect {
+public:
+    GrGLSimpleTextureEffect(const GrBackendEffectFactory& factory, const GrEffect&)
+    : INHERITED (factory) {}
+
+    virtual void emitCode(GrGLShaderBuilder* builder,
+                          const GrEffectStage&,
+                          EffectKey key,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray& samplers) SK_OVERRIDE {
+        const char* coordName;
+        GrSLType coordType = fEffectMatrix.emitCode(builder, key, vertexCoords, &coordName);
+        builder->fFSCode.appendf("\t%s = ", outputColor);
+        builder->appendTextureLookupAndModulate(&builder->fFSCode,
+                                                inputColor,
+                                                samplers[0],
+                                                coordName,
+                                                coordType);
+        builder->fFSCode.append(";\n");
+    }
+
+    static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        const GrSimpleTextureEffect& ste =
+            static_cast<const GrSimpleTextureEffect&>(*stage.getEffect());
+        return GrGLEffectMatrix::GenKey(ste.getMatrix(),
+                                        stage.getCoordChangeMatrix(),
+                                        ste.texture(0));
+    }
+
+    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
+        const GrSimpleTextureEffect& ste =
+            static_cast<const GrSimpleTextureEffect&>(*stage.getEffect());
+        fEffectMatrix.setData(uman, ste.getMatrix(), stage.getCoordChangeMatrix(), ste.texture(0));
+    }
+
+private:
+    GrGLEffectMatrix fEffectMatrix;
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrSimpleTextureEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
+const GrBackendEffectFactory& GrSimpleTextureEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrSimpleTextureEffect>::getInstance();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrSimpleTextureEffect);
+
+GrEffectRef* GrSimpleTextureEffect::TestCreate(SkRandom* random,
+                                               GrContext* context,
+                                               GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
+    return GrSimpleTextureEffect::Create(textures[texIdx], matrix);
+}
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
new file mode 100644
index 0000000..5ccbe33
--- /dev/null
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrSimpleTextureEffect_DEFINED
+#define GrSimpleTextureEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+
+class GrGLSimpleTextureEffect;
+
+/**
+ * The output color of this effect is a modulation of the input color and a sample from a texture.
+ * The coord to sample the texture is determine by a matrix. It allows explicit specification of
+ * the filtering and wrap modes (GrTextureParams).
+ */
+class GrSimpleTextureEffect : public GrSingleTextureEffect {
+public:
+    /* unfiltered, clamp mode */
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix) {
+        SkAutoTUnref<GrEffect> effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix)));
+        return CreateEffectRef(effect);
+    }
+
+    /* clamp mode */
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix, bool bilerp) {
+        SkAutoTUnref<GrEffect> effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, bilerp)));
+        return CreateEffectRef(effect);
+    }
+
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix, const GrTextureParams& p) {
+        SkAutoTUnref<GrEffect> effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, p)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrSimpleTextureEffect() {}
+
+    static const char* Name() { return "Texture"; }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    typedef GrGLSimpleTextureEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix)
+        : GrSingleTextureEffect(texture, matrix) {}
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix, bool bilerp)
+        : GrSingleTextureEffect(texture, matrix, bilerp) {}
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix, const GrTextureParams& params)
+        : GrSingleTextureEffect(texture, matrix, params) {}
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
+        const GrSimpleTextureEffect& ste = static_cast<const GrSimpleTextureEffect&>(other);
+        return this->hasSameTextureParamsAndMatrix(ste);
+    }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrSingleTextureEffect.cpp b/src/gpu/effects/GrSingleTextureEffect.cpp
index 18e35e4..7183ba3 100644
--- a/src/gpu/effects/GrSingleTextureEffect.cpp
+++ b/src/gpu/effects/GrSingleTextureEffect.cpp
@@ -6,56 +6,6 @@
  */
 
 #include "effects/GrSingleTextureEffect.h"
-#include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
-#include "gl/GrGLSL.h"
-#include "gl/GrGLTexture.h"
-#include "GrTBackendEffectFactory.h"
-#include "GrTexture.h"
-
-class GrGLSingleTextureEffect : public GrGLEffect {
-public:
-    GrGLSingleTextureEffect(const GrBackendEffectFactory& factory, const GrEffect&)
-    : INHERITED (factory) {}
-
-    virtual void emitCode(GrGLShaderBuilder* builder,
-                          const GrEffectStage&,
-                          EffectKey key,
-                          const char* vertexCoords,
-                          const char* outputColor,
-                          const char* inputColor,
-                          const TextureSamplerArray& samplers) SK_OVERRIDE {
-        const char* coordName;
-        GrSLType coordType = fEffectMatrix.emitCode(builder, key, vertexCoords, &coordName);
-        builder->fFSCode.appendf("\t%s = ", outputColor);
-        builder->appendTextureLookupAndModulate(&builder->fFSCode,
-                                                inputColor,
-                                                samplers[0],
-                                                coordName,
-                                                coordType);
-        builder->fFSCode.append(";\n");
-    }
-
-    static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
-        const GrSingleTextureEffect& ste =
-            static_cast<const GrSingleTextureEffect&>(*stage.getEffect());
-        return GrGLEffectMatrix::GenKey(ste.getMatrix(),
-                                        stage.getCoordChangeMatrix(),
-                                        ste.texture(0));
-    }
-
-    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
-        const GrSingleTextureEffect& ste =
-            static_cast<const GrSingleTextureEffect&>(*stage.getEffect());
-        fEffectMatrix.setData(uman, ste.getMatrix(), stage.getCoordChangeMatrix(), ste.texture(0));
-    }
-
-private:
-    GrGLEffectMatrix fEffectMatrix;
-    typedef GrGLEffect INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const SkMatrix& m)
     : fTextureAccess(texture)
@@ -79,31 +29,3 @@
 
 GrSingleTextureEffect::~GrSingleTextureEffect() {
 }
-
-void GrSingleTextureEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    // If the input alpha is 0xff and the texture has no alpha channel, then the output alpha is
-    // 0xff
-    if ((*validFlags & kA_ValidComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
-        GrPixelConfigIsOpaque(fTextureAccess.getTexture()->config())) {
-        *validFlags = kA_ValidComponentFlag;
-    } else {
-        *validFlags = 0;
-    }
-}
-
-const GrBackendEffectFactory& GrSingleTextureEffect::getFactory() const {
-    return GrTBackendEffectFactory<GrSingleTextureEffect>::getInstance();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_EFFECT_TEST(GrSingleTextureEffect);
-
-GrEffectRef* GrSingleTextureEffect::TestCreate(SkRandom* random,
-                                               GrContext* context,
-                                               GrTexture* textures[]) {
-    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
-                                      GrEffectUnitTest::kAlphaTextureIdx;
-    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
-    return GrSingleTextureEffect::Create(textures[texIdx], matrix);
-}
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
index e475cb5..4f25ddb 100644
--- a/src/gpu/effects/GrSingleTextureEffect.h
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -11,60 +11,48 @@
 #include "GrEffect.h"
 #include "SkMatrix.h"
 
-class GrGLSingleTextureEffect;
 class GrTexture;
 
 /**
- * An effect that draws a single texture with a texture matrix; commonly used as a base class. The
- * output color is the texture color is modulated against the input color.
+ * A base class for effects that draw a single texture with a texture matrix.
  */
 class GrSingleTextureEffect : public GrEffect {
 public:
-    /* unfiltered, clamp mode */
-    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix) {
-        SkAutoTUnref<GrEffect> effect(SkNEW_ARGS(GrSingleTextureEffect, (tex, matrix)));
-        return CreateEffectRef(effect);
-    }
-
-    /* clamp mode */
-    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix, bool bilerp) {
-        SkAutoTUnref<GrEffect> effect(SkNEW_ARGS(GrSingleTextureEffect, (tex, matrix, bilerp)));
-        return CreateEffectRef(effect);
-    }
-
-    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix, const GrTextureParams& p) {
-        SkAutoTUnref<GrEffect> effect(SkNEW_ARGS(GrSingleTextureEffect, (tex, matrix, p)));
-        return CreateEffectRef(effect);
-    }
-
     virtual ~GrSingleTextureEffect();
 
-    static const char* Name() { return "Single Texture"; }
-
-    /** Note that if this class is sub-classed, the subclass may have to override this function.
-     */
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
     const SkMatrix& getMatrix() const { return fMatrix; }
 
-    typedef GrGLSingleTextureEffect GLEffect;
-
-    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-
-    virtual bool isEqual(const GrEffect& effect) const SK_OVERRIDE {
-        const GrSingleTextureEffect& ste = static_cast<const GrSingleTextureEffect&>(effect);
-        return INHERITED::isEqual(effect) && fMatrix.cheapEqualTo(ste.getMatrix());
-    }
-
 protected:
     GrSingleTextureEffect(GrTexture*, const SkMatrix&); /* unfiltered, clamp mode */
     GrSingleTextureEffect(GrTexture*, const SkMatrix&, bool bilerp); /* clamp mode */
     GrSingleTextureEffect(GrTexture*, const SkMatrix&, const GrTextureParams&);
 
+    /**
+     * Helper for subclass onIsEqual() functions.
+     */
+    bool hasSameTextureParamsAndMatrix(const GrSingleTextureEffect& other) const {
+        const GrTextureAccess& otherAccess = other.fTextureAccess;
+        // We don't have to check the accesses' swizzles because they are inferred from the texture.
+        return fTextureAccess.getTexture() == otherAccess.getTexture() &&
+               fTextureAccess.getParams() == otherAccess.getParams() &&
+               this->getMatrix().cheapEqualTo(other.getMatrix());
+    }
+
+    /**
+     * Can be used as a helper to implement subclass getConstantColorComponents(). It assumes that
+     * the subclass output color will be a modulation of the input color with a value read from the
+     * texture.
+     */
+    void updateConstantColorComponentsForModulation(GrColor* color, uint32_t* validFlags) const {
+        if ((*validFlags & kA_ValidComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
+            GrPixelConfigIsOpaque(this->texture(0)->config())) {
+            *validFlags = kA_ValidComponentFlag;
+        } else {
+            *validFlags = 0;
+        }
+    }
+
 private:
-
-    GR_DECLARE_EFFECT_TEST;
-
     GrTextureAccess fTextureAccess;
     SkMatrix        fMatrix;
 
diff --git a/src/gpu/effects/GrTextureDomainEffect.cpp b/src/gpu/effects/GrTextureDomainEffect.cpp
index f82c62f..c4a59d8 100644
--- a/src/gpu/effects/GrTextureDomainEffect.cpp
+++ b/src/gpu/effects/GrTextureDomainEffect.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GrTextureDomainEffect.h"
+#include "GrSimpleTextureEffect.h"
 #include "GrTBackendEffectFactory.h"
 #include "gl/GrGLEffect.h"
 #include "gl/GrGLEffectMatrix.h"
@@ -128,7 +129,7 @@
                                            bool bilerp) {
     static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
     if (kClamp_WrapMode == wrapMode && domain.contains(kFullRect)) {
-        return GrSingleTextureEffect::Create(texture, matrix, bilerp);
+        return GrSimpleTextureEffect::Create(texture, matrix, bilerp);
     } else {
         SkRect clippedDomain;
         // We don't currently handle domains that are empty or don't intersect the texture.
@@ -171,9 +172,17 @@
     return GrTBackendEffectFactory<GrTextureDomainEffect>::getInstance();
 }
 
-bool GrTextureDomainEffect::isEqual(const GrEffect& sBase) const {
+bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const {
     const GrTextureDomainEffect& s = static_cast<const GrTextureDomainEffect&>(sBase);
-    return (INHERITED::isEqual(sBase) && this->fTextureDomain == s.fTextureDomain);
+    return this->hasSameTextureParamsAndMatrix(s) && this->fTextureDomain == s.fTextureDomain;
+}
+
+void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    if (kDecal_WrapMode == fWrapMode) {
+        *validFlags = 0;
+    } else {
+        this->updateConstantColorComponentsForModulation(color, validFlags);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrTextureDomainEffect.h b/src/gpu/effects/GrTextureDomainEffect.h
index a5c2d70..b7f665c 100644
--- a/src/gpu/effects/GrTextureDomainEffect.h
+++ b/src/gpu/effects/GrTextureDomainEffect.h
@@ -47,7 +47,7 @@
     typedef GrGLTextureDomainEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     const SkRect& domain() const { return fTextureDomain; }
     WrapMode wrapMode() const { return fWrapMode; }
@@ -77,6 +77,8 @@
                           WrapMode,
                           bool bilerp);
 
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GR_DECLARE_EFFECT_TEST;
 
     typedef GrSingleTextureEffect INHERITED;