Distance field fixes for Android
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 26 Mar 2014 19:49:03 +0000 (19:49 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 26 Mar 2014 19:49:03 +0000 (19:49 +0000)
- Expand glyph size by 2 on each side to compensate for bilerp lookup
- Correct for Adreno tendency to drop entire tile if any pixel has divide-by-0
- Fix blurriness on Adreno by using uv coords to compute gradient instead
  of st coords
- Add faster version for uniform scale

BUG=skia:2173

Committed: http://code.google.com/p/skia/source/detail?r=13955

R=bsalomon@google.com

Author: jvanverth@google.com

Review URL: https://codereview.chromium.org/205343008

git-svn-id: http://skia.googlecode.com/svn/trunk@13958 2bbb7eff-a529-9590-31e7-b0007b416f81

src/core/SkDistanceFieldGen.cpp
src/gpu/GrAtlas.cpp
src/gpu/GrAtlas.h
src/gpu/GrDistanceFieldTextContext.cpp
src/gpu/GrOvalRenderer.cpp
src/gpu/GrTextStrike.cpp
src/gpu/GrTextStrike.h
src/gpu/effects/GrDistanceFieldTextureEffect.cpp
src/gpu/effects/GrDistanceFieldTextureEffect.h
src/gpu/gl/GrGLCaps.cpp
src/gpu/gl/GrGLCaps.h

index 2093a2ba0e6a247d0c8babac5199e079f827553d..8ec7f323ece17fc89759f7256e5b941a88010b15 100755 (executable)
@@ -336,10 +336,10 @@ bool SkGenerateDistanceFieldFromImage(unsigned char* distanceField,
     SkASSERT(NULL != image);
 
     // the final distance field will have additional texels on each side to handle
-    // the maximum distance
+    // the maximum distance + 1 for bilerp
     // we expand our temp data by one more on each side to simplify
     // the scanning code -- will always be treated as infinitely far away
-    int pad = distanceMagnitude+1;
+    int pad = distanceMagnitude+2;
 
     // set params for distance field data
     int dataWidth = width + 2*pad;
index 349b47f620626a22fac6c1038915cb8e008d4200..e17171f8018f9cc277493ea317917f6551d97dbf 100644 (file)
@@ -209,7 +209,3 @@ GrPlot* GrAtlasMgr::getUnusedPlot() {
 
     return NULL;
 }
-
-SkISize GrAtlas::getSize() const {
-    return SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, GR_ATLAS_TEXTURE_HEIGHT);
-}
index 7219ab24c25de30fcb36bf1b3a5f4494867fe253..c7536e380085f1cca584e710bb616155976ded70 100644 (file)
@@ -103,8 +103,6 @@ public:
 
     bool isEmpty() { return 0 == fPlots.count(); }
 
-    SkISize getSize() const;
-
 private:
     SkTDArray<GrPlot*> fPlots;
 
index 1a422c682a7d56f924aae5e39b7210bc07aec6d0..4ce336beb01db0a309b242b17e75289d4850ab03 100755 (executable)
@@ -81,10 +81,10 @@ void GrDistanceFieldTextContext::flushGlyphs() {
         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
 
         // This effect could be stored with one of the cache objects (atlas?)
-        SkISize size = fStrike->getAtlasSize();
         drawState->addCoverageEffect(
-                                GrDistanceFieldTextureEffect::Create(fCurrTexture, params, size),
-                                kGlyphCoordsAttributeIndex)->unref();
+                         GrDistanceFieldTextureEffect::Create(fCurrTexture, params,
+                                                              fContext->getMatrix().isSimilarity()),
+                         kGlyphCoordsAttributeIndex)->unref();
 
         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
             if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
index ac33a5cc5d9e7665283f5c5bd27f51ee54088d5e..23ce230bfa8ffa6008b29774d283b323cc2c0632 100644 (file)
@@ -232,8 +232,9 @@ public:
             builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
             // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
             // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
-            // TODO: restrict this to Adreno-only
-            builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
+            if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
+                builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
+            }
             builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
             builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
 
@@ -380,8 +381,9 @@ public:
             builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
             // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
             // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
-            // TODO: restrict this to Adreno-only
-            builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
+            if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
+                builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
+            }
             builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
             if (kHairline == ellipseEffect.getMode()) {
                 // can probably do this with one step
index 0a1fd1e71f765f8476d73cae4503a2745ffb9f81..7084b9383da6829f10ad3e712128f3fbedaadf89 100644 (file)
@@ -181,7 +181,11 @@ void GrFontCache::dump() const {
             GrTexture* texture = fAtlasMgr[i]->getTexture();
             if (NULL != texture) {
                 SkString filename;
+#ifdef SK_BUILD_FOR_ANDROID
+                filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
+#else
                 filename.printf("fontcache_%d%d.png", gDumpCount, i);
+#endif
                 texture->savePixels(filename.c_str());
             }
         }
@@ -248,11 +252,13 @@ GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
 
     GrGlyph* glyph = fPool.alloc();
     // expand bounds to hold full distance field data
+    // + room for bilerp
+    int pad = DISTANCE_FIELD_RANGE+1;
     if (fUseDistanceField) {
-        bounds.fLeft   -= DISTANCE_FIELD_RANGE;
-        bounds.fRight  += DISTANCE_FIELD_RANGE;
-        bounds.fTop    -= DISTANCE_FIELD_RANGE;
-        bounds.fBottom += DISTANCE_FIELD_RANGE;
+        bounds.fLeft   -= pad;
+        bounds.fRight  += pad;
+        bounds.fTop    -= pad;
+        bounds.fBottom += pad;
     }
     glyph->init(packed, bounds);
     fCache.insert(packed, glyph);
@@ -292,8 +298,9 @@ bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
         // but must shrink back down to get the packed glyph data
         int dfWidth = glyph->width();
         int dfHeight = glyph->height();
-        int width = dfWidth - 2*DISTANCE_FIELD_RANGE;
-        int height = dfHeight - 2*DISTANCE_FIELD_RANGE;
+        int pad = DISTANCE_FIELD_RANGE+1;
+        int width = dfWidth - 2*pad;
+        int height = dfHeight - 2*pad;
         int stride = width*bytesPerPixel;
 
         size_t size = width * height * bytesPerPixel;
index 8036ec323a98bcd38c4bd2145d522604a6487410..2f2b7202a2f13a10533e79818e1ef9e60a4e6b0e 100644 (file)
@@ -39,8 +39,6 @@ public:
     inline GrGlyph* getGlyph(GrGlyph::PackedID, GrFontScaler*);
     bool addGlyphToAtlas(GrGlyph*, GrFontScaler*);
 
-    SkISize getAtlasSize() const { return fAtlas.getSize(); }
-
     // testing
     int countGlyphs() const { return fCache.getArray().count(); }
     const GrGlyph* glyphAt(int index) const {
index 9159a701e32a966bbb0ba1a3d465b35cd3abc6d2..dd370b6d4e66e32e37ff4f638c50d66015be1087 100755 (executable)
@@ -23,7 +23,7 @@ public:
     GrGLDistanceFieldTextureEffect(const GrBackendEffectFactory& factory,
                                    const GrDrawEffect& drawEffect)
         : INHERITED (factory)
-        , fTextureSize(SkSize::Make(-1.f,-1.f)) {}
+        , fTextureSize(SkISize::Make(-1,-1)) {}
 
     virtual void emitCode(GrGLFullShaderBuilder* builder,
                           const GrDrawEffect& drawEffect,
@@ -35,6 +35,8 @@ public:
         SkASSERT(1 == drawEffect.castEffect<GrDistanceFieldTextureEffect>().numVertexAttribs());
 
         SkAssertResult(builder->enableFeature(GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
+        const GrDistanceFieldTextureEffect& dfTexEffect =
+                                              drawEffect.castEffect<GrDistanceFieldTextureEffect>();
 
         SkString fsCoordName;
         const char* vsCoordName;
@@ -61,16 +63,37 @@ public:
         // we adjust for the effect of the transformation on the distance by using
         // the length of the gradient of the texture coordinates. We use st coordinates
         // to ensure we're mapping 1:1 from texel space to pixel space.
-        builder->fsCodeAppendf("\tvec2 st = %s*%s;\n", fsCoordName.c_str(), textureSizeUniName);
-        builder->fsCodeAppend("\tvec2 Jdx = dFdx(st);\n");
-        builder->fsCodeAppend("\tvec2 Jdy = dFdy(st);\n");
-        builder->fsCodeAppend("\tvec2 st_grad = normalize(st);\n");
-        builder->fsCodeAppend("\tvec2 grad = vec2(st_grad.x*Jdx.x + st_grad.y*Jdy.x,\n");
-        builder->fsCodeAppend("\t                 st_grad.x*Jdx.y + st_grad.y*Jdy.y);\n");
-
-        // this gives us a smooth step across approximately one fragment
-        // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
-        builder->fsCodeAppend("\tfloat afwidth = 0.7071*length(grad);\n");
+        builder->fsCodeAppendf("\tvec2 uv = %s;\n", fsCoordName.c_str());
+        builder->fsCodeAppendf("\tvec2 st = uv*%s;\n", textureSizeUniName);
+        builder->fsCodeAppend("\tfloat afwidth;\n");
+        if (dfTexEffect.isUniformScale()) {
+            // this gives us a smooth step across approximately one fragment
+            // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
+            builder->fsCodeAppend("\tafwidth = 0.7071*dFdx(st.x);\n");
+        } else {
+            builder->fsCodeAppend("\tvec2 Jdx = dFdx(st);\n");
+            builder->fsCodeAppend("\tvec2 Jdy = dFdy(st);\n");
+
+            builder->fsCodeAppend("\tvec2 uv_grad;\n");
+            if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
+                // this is to compensate for the Adreno, which likes to drop tiles on division by 0
+                builder->fsCodeAppend("\tfloat uv_len2 = dot(uv, uv);\n");
+                builder->fsCodeAppend("\tif (uv_len2 < 0.0001) {\n");
+                builder->fsCodeAppend("\t\tuv_grad = vec2(0.7071, 0.7071);\n");
+                builder->fsCodeAppend("\t} else {\n");
+                builder->fsCodeAppend("\t\tuv_grad = uv*inversesqrt(uv_len2);\n");
+                builder->fsCodeAppend("\t}\n");
+            } else {
+                builder->fsCodeAppend("\tuv_grad = normalize(uv);\n");
+            }
+            builder->fsCodeAppend("\tvec2 grad = vec2(uv_grad.x*Jdx.x + uv_grad.y*Jdy.x,\n");
+            builder->fsCodeAppend("\t                 uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n");
+
+            // this gives us a smooth step across approximately one fragment
+            // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
+            builder->fsCodeAppend("\tafwidth = 0.7071*length(grad);\n");
+        }
+
         builder->fsCodeAppend("\tfloat val = smoothstep(-afwidth, afwidth, distance);\n");
 
         builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
@@ -80,20 +103,27 @@ public:
     virtual void setData(const GrGLUniformManager& uman,
                          const GrDrawEffect& drawEffect) SK_OVERRIDE {
         SkASSERT(fTextureSizeUni.isValid());
-        const GrDistanceFieldTextureEffect& distanceFieldEffect =
-                                              drawEffect.castEffect<GrDistanceFieldTextureEffect>();
-        if (distanceFieldEffect.getSize().width() != fTextureSize.width() ||
-            distanceFieldEffect.getSize().height() != fTextureSize.height()) {
-            fTextureSize = distanceFieldEffect.getSize();
+
+        GrTexture* texture = drawEffect.effect()->get()->texture(0);
+        if (texture->width() != fTextureSize.width() ||
+            texture->height() != fTextureSize.height()) {
+            fTextureSize = SkISize::Make(texture->width(), texture->height());
             uman.set2f(fTextureSizeUni,
-                       distanceFieldEffect.getSize().width(),
-                       distanceFieldEffect.getSize().height());
+                       SkIntToScalar(fTextureSize.width()),
+                       SkIntToScalar(fTextureSize.height()));
         }
     }
 
+    static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
+        const GrDistanceFieldTextureEffect& dfTexEffect =
+                                              drawEffect.castEffect<GrDistanceFieldTextureEffect>();
+
+        return dfTexEffect.isUniformScale() ? 0x1 : 0x0;
+    }
+
 private:
     GrGLUniformManager::UniformHandle fTextureSizeUni;
-    SkSize                            fTextureSize;
+    SkISize                           fTextureSize;
 
     typedef GrGLVertexEffect INHERITED;
 };
@@ -102,9 +132,9 @@ private:
 
 GrDistanceFieldTextureEffect::GrDistanceFieldTextureEffect(GrTexture* texture,
                                                            const GrTextureParams& params,
-                                                           const SkISize& size)
+                                                           bool uniformScale)
     : fTextureAccess(texture, params)
-    , fSize(SkSize::Make(SkIntToScalar(size.width()), SkIntToScalar(size.height()))) {
+    , fUniformScale(uniformScale) {
     this->addTextureAccess(&fTextureAccess);
     this->addVertexAttrib(kVec2f_GrSLType);
 }
@@ -149,7 +179,6 @@ GrEffectRef* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random,
     };
     GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
                                                            GrTextureParams::kNone_FilterMode);
-    SkISize size = SkISize::Make(1024, 2048);
 
-    return GrDistanceFieldTextureEffect::Create(textures[texIdx], params, size);
+    return GrDistanceFieldTextureEffect::Create(textures[texIdx], params, random->nextBool());
 }
index 1292c0322126608943068f6c02955922028167ec..212532e0a8664d765e46ac3393cb64b51d3ccaee 100644 (file)
@@ -21,8 +21,8 @@ class GrGLDistanceFieldTextureEffect;
  */
 class GrDistanceFieldTextureEffect : public GrVertexEffect {
 public:
-    static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& p, const SkISize& s) {
-        AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, p, s)));
+    static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& para, bool uniformScale) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, para, uniformScale)));
         return CreateEffectRef(effect);
     }
 
@@ -31,7 +31,7 @@ public:
     static const char* Name() { return "DistanceFieldTexture"; }
 
     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-    const SkSize& getSize() const { return fSize; }
+    bool isUniformScale() const { return fUniformScale; }
 
     typedef GrGLDistanceFieldTextureEffect GLEffect;
 
@@ -39,12 +39,12 @@ public:
 
 private:
     GrDistanceFieldTextureEffect(GrTexture* texture, const GrTextureParams& params,
-                                 const SkISize& textureSize);
+                                 bool uniformScale);
 
     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
 
     GrTextureAccess fTextureAccess;
-    SkSize          fSize;
+    bool            fUniformScale;
 
     GR_DECLARE_EFFECT_TEST;
 
index a60230a2fe91de7e744a27a002cac988e6b3f912..47f3f0f0c819d6fd97312bd4cb424ebe42369d92 100644 (file)
@@ -47,6 +47,7 @@ void GrGLCaps::reset() {
     fFixedFunctionSupport = false;
     fDiscardFBSupport = false;
     fFullClearIsFree = false;
+    fDropsTileOnZeroDivide = false;
 }
 
 GrGLCaps::GrGLCaps(const GrGLCaps& caps) : GrDrawTargetCaps() {
@@ -84,6 +85,7 @@ GrGLCaps& GrGLCaps::operator = (const GrGLCaps& caps) {
     fFixedFunctionSupport = caps.fFixedFunctionSupport;
     fDiscardFBSupport = caps.fDiscardFBSupport;
     fFullClearIsFree = caps.fFullClearIsFree;
+    fDropsTileOnZeroDivide = caps.fDropsTileOnZeroDivide;
 
     return *this;
 }
@@ -244,6 +246,9 @@ void GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
         }
     }
 
+    // Adreno GPUs have a tendency to drop tiles when there is a divide-by-zero in a shader
+    fDropsTileOnZeroDivide = kQualcomm_GrGLVendor == ctxInfo.vendor();
+
     this->initFSAASupport(ctxInfo, gli);
     this->initStencilFormats(ctxInfo);
 
@@ -661,5 +666,6 @@ SkString GrGLCaps::dump() const {
              (fUseNonVBOVertexAndIndexDynamicData ? "YES" : "NO"));
     r.appendf("Discard FrameBuffer support: %s\n", (fDiscardFBSupport ? "YES" : "NO"));
     r.appendf("Full screen clear is free: %s\n", (fFullClearIsFree ? "YES" : "NO"));
+    r.appendf("Drops tile on zero divide: %s\n", (fDropsTileOnZeroDivide ? "YES" : "NO"));
     return r;
 }
index 7f0c8874ca2e441245c98aabb21d44ab931e6d10..0939f80c26562909f8b250e695d7684d19f5e3d5 100644 (file)
@@ -250,6 +250,8 @@ public:
 
     bool fullClearIsFree() const { return fFullClearIsFree; }
 
+    bool dropsTileOnZeroDivide() const { return fDropsTileOnZeroDivide; }
+
 private:
     /**
      * Maintains a bit per GrPixelConfig. It is used to avoid redundantly
@@ -332,6 +334,7 @@ private:
     bool fFixedFunctionSupport : 1;
     bool fDiscardFBSupport : 1;
     bool fFullClearIsFree : 1;
+    bool fDropsTileOnZeroDivide : 1;
 
     typedef GrDrawTargetCaps INHERITED;
 };