Add effect caching to distance field text.

This also is a step towards unifying GrDistanceFieldTextureEffect and GrDistanceFieldLCDTextureEffect.

R=robertphillips@google.com

Author: jvanverth@google.com

Review URL: https://codereview.chromium.org/424103002
diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp
index 0ef596a..32d8f0c 100755
--- a/src/gpu/GrDistanceFieldTextContext.cpp
+++ b/src/gpu/GrDistanceFieldTextContext.cpp
@@ -66,7 +66,10 @@
     fGammaTexture = NULL;
 
     fCurrVertex = 0;
-
+    fEffectTextureUniqueID = SK_InvalidUniqueID;
+    fEffectColor = GrColor_ILLEGAL;
+    fEffectFlags = 0;
+                                                        
     fVertices = NULL;
 }
 
@@ -111,6 +114,58 @@
     return GrColorPackRGBA(r, g, b, 0xff);
 }
 
+void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
+    GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
+    GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
+    
+    GrTexture* currTexture = fStrike->getTexture();
+    SkASSERT(currTexture);
+    uint32_t textureUniqueID = currTexture->getUniqueID();
+    
+    // set up any flags
+    uint32_t flags = 0;
+    flags |= fContext->getMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+    flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
+    flags |= fUseLCDText && fContext->getMatrix().rectStaysRect() ?
+    kRectToRect_DistanceFieldEffectFlag : 0;
+    bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout ==
+    fDeviceProperties.fGeometry.getLayout();
+    flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
+    
+    // see if we need to create a new effect
+    if (textureUniqueID != fEffectTextureUniqueID ||
+        filteredColor != fEffectColor ||
+        flags != fEffectFlags) {
+        if (fUseLCDText) {
+            GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
+            fCachedEffect.reset(GrDistanceFieldLCDTextureEffect::Create(currTexture,
+                                                                        params,
+                                                                        fGammaTexture,
+                                                                        gammaParams,
+                                                                        colorNoPreMul,
+                                                                        flags));
+        } else {
+#ifdef SK_GAMMA_APPLY_TO_A8
+            U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma,
+                                                                filteredColor);
+            fCachedEffect.reset(GrDistanceFieldTextureEffect::Create(currTexture,
+                                                                     params,
+                                                                     fGammaTexture,
+                                                                     gammaParams,
+                                                                     lum/255.f,
+                                                                     flags));
+#else
+            fCachedEffect.reset(GrDistanceFieldTextureEffect::Create(currTexture,
+                                                                     params, flags));
+#endif
+        }
+        fEffectTextureUniqueID = textureUniqueID;
+        fEffectColor = filteredColor;
+        fEffectFlags = flags;
+    }
+    
+}
+
 void GrDistanceFieldTextContext::flushGlyphs() {
     if (NULL == fDrawTarget) {
         return;
@@ -123,14 +178,8 @@
     if (fCurrVertex > 0) {
         // setup our sampler state for our text texture/atlas
         SkASSERT(SkIsAlign4(fCurrVertex));
-        GrTexture* currTexture = fStrike->getTexture();
-        SkASSERT(currTexture);
-        GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
-        GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
 
-        // Effects could be stored with one of the cache objects (atlas?)
-        int coordsIdx = drawState->hasColorVertexAttribute() ? kGlyphCoordsWithColorAttributeIndex :
-                                                               kGlyphCoordsNoColorAttributeIndex;
+        // get our current color
         SkColor filteredColor;
         SkColorFilter* colorFilter = fSkPaint.getColorFilter();
         if (NULL != colorFilter) {
@@ -138,21 +187,16 @@
         } else {
             filteredColor = fSkPaint.getColor();
         }
+        this->setupCoverageEffect(filteredColor);
+       
+        // Effects could be stored with one of the cache objects (atlas?)
+        int coordsIdx = drawState->hasColorVertexAttribute() ? kGlyphCoordsWithColorAttributeIndex :
+                                                               kGlyphCoordsNoColorAttributeIndex;
+        drawState->addCoverageEffect(fCachedEffect.get(), coordsIdx);
+        
+        // Set draw state
         if (fUseLCDText) {
             GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
-            bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout ==
-                                                            fDeviceProperties.fGeometry.getLayout();
-            drawState->addCoverageEffect(GrDistanceFieldLCDTextureEffect::Create(
-                                                            currTexture,
-                                                            params,
-                                                            fGammaTexture,
-                                                            gammaParams,
-                                                            colorNoPreMul,
-                                                            fContext->getMatrix().rectStaysRect() &&
-                                                            fContext->getMatrix().isSimilarity(),
-                                                            useBGR),
-                                         coordsIdx)->unref();
-
             if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
                 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
                 fPaint.numColorStages()) {
@@ -170,21 +214,6 @@
             drawState->setBlendConstant(colorNoPreMul);
             drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
         } else {
-#ifdef SK_GAMMA_APPLY_TO_A8
-            U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma,
-                                                                filteredColor);
-            drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
-                                                              currTexture, params,
-                                                              fGammaTexture, gammaParams,
-                                                              lum/255.f,
-                                                              fContext->getMatrix().isSimilarity()),
-                                         coordsIdx)->unref();
-#else
-            drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
-                                                              currTexture, params,
-                                                              fContext->getMatrix().isSimilarity()),
-                                         coordsIdx)->unref();
-#endif
             // set back to normal in case we took LCD path previously.
             drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
             //drawState->setColor(fPaint.getColor());
diff --git a/src/gpu/GrDistanceFieldTextContext.h b/src/gpu/GrDistanceFieldTextContext.h
index 7676fc1..672922d 100644
--- a/src/gpu/GrDistanceFieldTextContext.h
+++ b/src/gpu/GrDistanceFieldTextContext.h
@@ -34,11 +34,17 @@
     SkScalar                fTextRatio;
     bool                    fUseLCDText;
     bool                    fEnableDFRendering;
+    SkAutoTUnref<GrEffect>  fCachedEffect;
+    // Used to check whether fCachedEffect is still valid.
+    uint32_t                fEffectTextureUniqueID;
+    SkColor                 fEffectColor;
+    uint32_t                fEffectFlags;
     GrTexture*              fGammaTexture;
 
     void init(const GrPaint&, const SkPaint&);
     void drawPackedGlyph(GrGlyph::PackedID, SkFixed left, SkFixed top, GrFontScaler*);
     void flushGlyphs();                 // automatically called by destructor
+    void setupCoverageEffect(const SkColor& filteredColor);
     void finish();
 
     enum {
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
index 0c0c1af..6757157 100755
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
+++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
@@ -79,7 +79,7 @@
         builder->fsCodeAppendf("\tvec2 uv = %s;\n", fsCoordName.c_str());
         builder->fsCodeAppendf("\tvec2 st = uv*%s;\n", textureSizeUniName);
         builder->fsCodeAppend("\tfloat afwidth;\n");
-        if (dfTexEffect.isSimilarity()) {
+        if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) {
             // this gives us a smooth step across approximately one fragment
             builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*dFdx(st.x);\n");
         } else {
@@ -153,7 +153,7 @@
         const GrDistanceFieldTextureEffect& dfTexEffect =
                                               drawEffect.castEffect<GrDistanceFieldTextureEffect>();
 
-        b->add32(dfTexEffect.isSimilarity());
+        b->add32(dfTexEffect.getFlags());
     }
 
 private:
@@ -174,13 +174,14 @@
                                                            const GrTextureParams& gammaParams,
                                                            float luminance,
 #endif
-                                                           bool similarity)
+                                                           uint32_t flags)
     : fTextureAccess(texture, params)
 #ifdef SK_GAMMA_APPLY_TO_A8
     , fGammaTextureAccess(gamma, gammaParams)
     , fLuminance(luminance)
 #endif
-    , fIsSimilarity(similarity) {
+    , fFlags(flags & kNonLCD_DistanceFieldEffectMask) {
+    SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
     this->addTextureAccess(&fTextureAccess);
 #ifdef SK_GAMMA_APPLY_TO_A8
     this->addTextureAccess(&fGammaTextureAccess);
@@ -190,7 +191,10 @@
 
 bool GrDistanceFieldTextureEffect::onIsEqual(const GrEffect& other) const {
     const GrDistanceFieldTextureEffect& cte = CastEffect<GrDistanceFieldTextureEffect>(other);
-    return fTextureAccess == cte.fTextureAccess;
+    return fTextureAccess == cte.fTextureAccess &&
+           fGammaTextureAccess == cte.fGammaTextureAccess &&
+           fLuminance == cte.fLuminance &&
+           fFlags == cte.fFlags;
 }
 
 void GrDistanceFieldTextureEffect::getConstantColorComponents(GrColor* color,
@@ -242,7 +246,8 @@
                                                 textures[texIdx2], params2,
                                                 random->nextF(),
 #endif
-                                                random->nextBool());
+                                                random->nextBool() ?
+                                                    kSimilarity_DistanceFieldEffectFlag : 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -286,7 +291,8 @@
         // create LCD offset adjusted by inverse of transform
         builder->fsCodeAppendf("\tvec2 uv = %s;\n", fsCoordName.c_str());
         builder->fsCodeAppendf("\tvec2 st = uv*%s.xy;\n", textureSizeUniName);
-        if (dfTexEffect.isUniformScale()) {
+        bool isUniformScale = !!(dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask);
+        if (isUniformScale) {
             builder->fsCodeAppend("\tfloat dx = dFdx(st.x);\n");
             builder->fsCodeAppendf("\tvec2 offset = vec2(dx*%s.z, 0.0);\n", textureSizeUniName);
         } else {
@@ -327,7 +333,7 @@
         // transformations, and even then using a single factor seems like a reasonable
         // trade-off between quality and speed.
         builder->fsCodeAppend("\tfloat afwidth;\n");
-        if (dfTexEffect.isUniformScale()) {
+        if (isUniformScale) {
             // this gives us a smooth step across approximately one fragment
             builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*dx;\n");
         } else {
@@ -393,7 +399,7 @@
             texture->height() != fTextureSize.height()) {
             fTextureSize = SkISize::Make(texture->width(), texture->height());
             float delta = 1.0f/(3.0f*texture->width());
-            if (dfTexEffect.useBGR()) {
+            if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) {
                 delta = -delta;
             }
             uman.set3f(fTextureSizeUni,
@@ -418,7 +424,7 @@
         const GrDistanceFieldLCDTextureEffect& dfTexEffect =
                                            drawEffect.castEffect<GrDistanceFieldLCDTextureEffect>();
 
-        b->add32(dfTexEffect.isUniformScale());
+        b->add32(dfTexEffect.getFlags());
     }
 
 private:
@@ -436,12 +442,13 @@
                                                   GrTexture* texture, const GrTextureParams& params,
                                                   GrTexture* gamma, const GrTextureParams& gParams,
                                                   SkColor textColor,
-                                                  bool uniformScale, bool useBGR)
+                                                  uint32_t flags)
     : fTextureAccess(texture, params)
     , fGammaTextureAccess(gamma, gParams)
     , fTextColor(textColor)
-    , fUniformScale(uniformScale)
-    , fUseBGR(useBGR) {
+    , fFlags(flags & kLCD_DistanceFieldEffectMask) {
+    SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
+        
     this->addTextureAccess(&fTextureAccess);
     this->addTextureAccess(&fGammaTextureAccess);
     this->addVertexAttrib(kVec2f_GrSLType);
@@ -450,7 +457,10 @@
 bool GrDistanceFieldLCDTextureEffect::onIsEqual(const GrEffect& other) const {
     const GrDistanceFieldLCDTextureEffect& cte = 
                                             CastEffect<GrDistanceFieldLCDTextureEffect>(other);
-    return (fTextureAccess == cte.fTextureAccess && fGammaTextureAccess == cte.fGammaTextureAccess);
+    return (fTextureAccess == cte.fTextureAccess &&
+            fGammaTextureAccess == cte.fGammaTextureAccess &&
+            fTextColor == cte.fTextColor &&
+            fFlags == cte.fFlags);
 }
 
 void GrDistanceFieldLCDTextureEffect::getConstantColorComponents(GrColor* color,
@@ -496,8 +506,11 @@
                                         random->nextULessThan(256),
                                         random->nextULessThan(256),
                                         random->nextULessThan(256));
+    uint32_t flags = kUseLCD_DistanceFieldEffectFlag;
+    flags |= random->nextBool() ? kUniformScale_DistanceFieldEffectMask : 0;
+    flags |= random->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
     return GrDistanceFieldLCDTextureEffect::Create(textures[texIdx], params,
                                                    textures[texIdx2], params2,
                                                    textColor,
-                                                   random->nextBool(), random->nextBool());
+                                                   flags);
 }
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h
index 38e41e5..ab84753 100644
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.h
+++ b/src/gpu/effects/GrDistanceFieldTextureEffect.h
@@ -14,6 +14,24 @@
 class GrGLDistanceFieldTextureEffect;
 class GrGLDistanceFieldLCDTextureEffect;
 
+enum GrDistanceFieldEffectFlags {
+    kSimilarity_DistanceFieldEffectFlag = 0x01,   // ctm is similarity matrix
+    kRectToRect_DistanceFieldEffectFlag = 0x02,   // ctm maps rects to rects
+    kUseLCD_DistanceFieldEffectFlag     = 0x04,   // use lcd text
+    kBGR_DistanceFieldEffectFlag        = 0x08,   // lcd display has bgr order
+    kPortrait_DistanceFieldEffectFlag   = 0x10,   // lcd display is in portrait mode (not used yet)
+    
+    kUniformScale_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag |
+                                            kRectToRect_DistanceFieldEffectFlag,
+    // The subset of the flags relevant to GrDistanceFieldTextureEffect
+    kNonLCD_DistanceFieldEffectMask       = kSimilarity_DistanceFieldEffectFlag,
+    // The subset of the flags relevant to GrDistanceFieldLCDTextureEffect
+    kLCD_DistanceFieldEffectMask          = kSimilarity_DistanceFieldEffectFlag |
+                                            kRectToRect_DistanceFieldEffectFlag |
+                                            kUseLCD_DistanceFieldEffectFlag |
+                                            kBGR_DistanceFieldEffectFlag,
+};
+
 /**
  * The output color of this effect is a modulation of the input color and a sample from a
  * distance field texture (using a smoothed step function near 0.5).
@@ -25,14 +43,14 @@
 #ifdef SK_GAMMA_APPLY_TO_A8
     static GrEffect* Create(GrTexture* tex, const GrTextureParams& params,
                             GrTexture* gamma, const GrTextureParams& gammaParams, float lum,
-                            bool similarity) {
+                            uint32_t flags) {
        return SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params, gamma, gammaParams, lum,
-                                                        similarity));
+                                                        flags));
     }
 #else
     static GrEffect* Create(GrTexture* tex, const GrTextureParams& params,
-                            bool similarity) {
-        return  SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params, similarity));
+                            uint32_t flags) {
+        return  SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params, flags));
     }
 #endif
 
@@ -44,7 +62,7 @@
 #ifdef SK_GAMMA_APPLY_TO_A8
     float getLuminance() const { return fLuminance; }
 #endif
-    bool isSimilarity() const { return fIsSimilarity; }
+    uint32_t getFlags() const { return fFlags; }
 
     typedef GrGLDistanceFieldTextureEffect GLEffect;
 
@@ -55,7 +73,7 @@
 #ifdef SK_GAMMA_APPLY_TO_A8
                                  GrTexture* gamma, const GrTextureParams& gammaParams, float lum,
 #endif
-                                 bool uniformScale);
+                                 uint32_t flags);
 
     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
 
@@ -64,7 +82,7 @@
     GrTextureAccess fGammaTextureAccess;
     float           fLuminance;
 #endif
-    bool            fIsSimilarity;
+    uint32_t        fFlags;
 
     GR_DECLARE_EFFECT_TEST;
 
@@ -81,9 +99,9 @@
 public:
     static GrEffect* Create(GrTexture* tex, const GrTextureParams& params,
                             GrTexture* gamma, const GrTextureParams& gammaParams, 
-                            SkColor textColor, bool uniformScale, bool useBGR) {
+                            SkColor textColor, uint32_t flags) {
         return SkNEW_ARGS(GrDistanceFieldLCDTextureEffect,
-                          (tex, params, gamma, gammaParams, textColor, uniformScale, useBGR));
+                          (tex, params, gamma, gammaParams, textColor, flags));
     }
 
     virtual ~GrDistanceFieldLCDTextureEffect() {}
@@ -92,8 +110,7 @@
 
     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
     GrColor getTextColor() const { return fTextColor; }
-    bool isUniformScale() const { return fUniformScale; }
-    bool useBGR() const { return fUseBGR; }
+    uint32_t getFlags() const { return fFlags; }
 
     typedef GrGLDistanceFieldLCDTextureEffect GLEffect;
 
@@ -103,15 +120,14 @@
     GrDistanceFieldLCDTextureEffect(GrTexture* texture, const GrTextureParams& params,
                                     GrTexture* gamma, const GrTextureParams& gammaParams,
                                     SkColor textColor,
-                                    bool uniformScale, bool useBGR);
+                                    uint32_t flags);
 
     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
 
     GrTextureAccess fTextureAccess;
     GrTextureAccess fGammaTextureAccess;
     GrColor         fTextColor;
-    bool            fUniformScale;
-    bool            fUseBGR;
+    uint32_t        fFlags;
 
     GR_DECLARE_EFFECT_TEST;