From 4362a38563a958083aca2b456aaaa9f756f6f4e1 Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Wed, 26 Mar 2014 19:49:03 +0000 Subject: [PATCH] Distance field fixes for Android - 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 | 4 +- src/gpu/GrAtlas.cpp | 4 -- src/gpu/GrAtlas.h | 2 - src/gpu/GrDistanceFieldTextContext.cpp | 6 +- src/gpu/GrOvalRenderer.cpp | 10 ++-- src/gpu/GrTextStrike.cpp | 19 ++++-- src/gpu/GrTextStrike.h | 2 - src/gpu/effects/GrDistanceFieldTextureEffect.cpp | 75 ++++++++++++++++-------- src/gpu/effects/GrDistanceFieldTextureEffect.h | 10 ++-- src/gpu/gl/GrGLCaps.cpp | 6 ++ src/gpu/gl/GrGLCaps.h | 3 + 11 files changed, 90 insertions(+), 51 deletions(-) diff --git a/src/core/SkDistanceFieldGen.cpp b/src/core/SkDistanceFieldGen.cpp index 2093a2b..8ec7f32 100755 --- a/src/core/SkDistanceFieldGen.cpp +++ b/src/core/SkDistanceFieldGen.cpp @@ -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; diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp index 349b47f..e17171f 100644 --- a/src/gpu/GrAtlas.cpp +++ b/src/gpu/GrAtlas.cpp @@ -209,7 +209,3 @@ GrPlot* GrAtlasMgr::getUnusedPlot() { return NULL; } - -SkISize GrAtlas::getSize() const { - return SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, GR_ATLAS_TEXTURE_HEIGHT); -} diff --git a/src/gpu/GrAtlas.h b/src/gpu/GrAtlas.h index 7219ab2..c7536e3 100644 --- a/src/gpu/GrAtlas.h +++ b/src/gpu/GrAtlas.h @@ -103,8 +103,6 @@ public: bool isEmpty() { return 0 == fPlots.count(); } - SkISize getSize() const; - private: SkTDArray fPlots; diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp index 1a422c6..4ce336b 100755 --- a/src/gpu/GrDistanceFieldTextContext.cpp +++ b/src/gpu/GrDistanceFieldTextContext.cpp @@ -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() || diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp index ac33a5c..23ce230 100644 --- a/src/gpu/GrOvalRenderer.cpp +++ b/src/gpu/GrOvalRenderer.cpp @@ -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 diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp index 0a1fd1e..7084b93 100644 --- a/src/gpu/GrTextStrike.cpp +++ b/src/gpu/GrTextStrike.cpp @@ -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; diff --git a/src/gpu/GrTextStrike.h b/src/gpu/GrTextStrike.h index 8036ec3..2f2b720 100644 --- a/src/gpu/GrTextStrike.h +++ b/src/gpu/GrTextStrike.h @@ -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 { diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp index 9159a70..dd370b6 100755 --- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp +++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp @@ -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().numVertexAttribs()); SkAssertResult(builder->enableFeature(GrGLShaderBuilder::kStandardDerivatives_GLSLFeature)); + const GrDistanceFieldTextureEffect& dfTexEffect = + drawEffect.castEffect(); 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(); - 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(); + + 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()); } diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h index 1292c03..212532e 100644 --- a/src/gpu/effects/GrDistanceFieldTextureEffect.h +++ b/src/gpu/effects/GrDistanceFieldTextureEffect.h @@ -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; diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index a60230a..47f3f0f 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -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; } diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h index 7f0c887..0939f80 100644 --- a/src/gpu/gl/GrGLCaps.h +++ b/src/gpu/gl/GrGLCaps.h @@ -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; }; -- 2.7.4