diff options
author | android-autoroll <android-autoroll@skia-corp.google.com.iam.gserviceaccount.com> | 2019-04-01 20:38:24 +0000 |
---|---|---|
committer | android-autoroll <android-autoroll@skia-corp.google.com.iam.gserviceaccount.com> | 2019-04-01 20:38:24 +0000 |
commit | b88e6036078c79ef2a1f0b4b6dd34fbf911a0e04 (patch) | |
tree | 594459cc93a5a70b7e44120306cda927ee12361f | |
parent | 06781efb1e0092a7c7ac9b64d1ef8ad21e05dead (diff) | |
parent | cd749e013a4a4aa41f527a0bb37912398ccafdbb (diff) | |
download | skia-b88e6036078c79ef2a1f0b4b6dd34fbf911a0e04.tar.gz |
Roll external/skia 12f2883b9..cd749e013 (1 commits)
https://skia.googlesource.com/skia.git/+log/12f2883b9..cd749e013
2019-04-01 jvanverth@google.com Reland "Reland "Fix blurry edges on large ovals.""
The AutoRoll server is located here: https://autoroll-internal.skia.org/r/android-next-autoroll
Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
If the roll is causing failures, please contact the current sheriff, who should
be CC'd on the roll, and stop the roller if necessary.
Test: Presubmit checks will test this change.
Change-Id: I6a807d308c9f2d0c203e376d9f23283321b667e4
Exempt-From-Owner-Approval: The autoroll bot does not require owner approval.
-rw-r--r-- | gm/conicpaths.cpp | 30 | ||||
-rw-r--r-- | src/gpu/GrShaderCaps.cpp | 2 | ||||
-rw-r--r-- | src/gpu/GrShaderCaps.h | 3 | ||||
-rw-r--r-- | src/gpu/effects/GrEllipseEffect.cpp | 35 | ||||
-rw-r--r-- | src/gpu/effects/GrEllipseEffect.fp | 39 | ||||
-rw-r--r-- | src/gpu/effects/GrEllipseEffect.h | 8 | ||||
-rw-r--r-- | src/gpu/gl/GrGLCaps.cpp | 1 | ||||
-rw-r--r-- | src/gpu/ops/GrOvalOpFactory.cpp | 215 |
8 files changed, 252 insertions, 81 deletions
diff --git a/gm/conicpaths.cpp b/gm/conicpaths.cpp index e0a775a9dd..3f03bf0355 100644 --- a/gm/conicpaths.cpp +++ b/gm/conicpaths.cpp @@ -159,6 +159,36 @@ DEF_SIMPLE_GM(largecircle, canvas, 250, 250) { canvas->drawCircle(c, radius, paint); } +/* ovals should not be blurry */ +DEF_SIMPLE_GM(largeovals, canvas, 250, 250) { + // Test EllipseOp + SkRect r = SkRect::MakeXYWH(-520, -520, 5000, 4000); + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(100); + canvas->drawOval(r, paint); + r.offset(-15, -15); + paint.setColor(SK_ColorDKGRAY); + // we use stroke and fill to avoid falling into the SimpleFill path + paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setStrokeWidth(1); + canvas->drawOval(r, paint); + + // Test DIEllipseOp + canvas->rotate(1.0f); + r.offset(55, 55); + paint.setColor(SK_ColorGRAY); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(100); + canvas->drawOval(r, paint); + r.offset(-15, -15); + paint.setColor(SK_ColorLTGRAY); + paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setStrokeWidth(1); + canvas->drawOval(r, paint); +} + DEF_SIMPLE_GM(crbug_640176, canvas, 250, 250) { SkPath path; path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp index f13e8e716b..bd1f66731e 100644 --- a/src/gpu/GrShaderCaps.cpp +++ b/src/gpu/GrShaderCaps.cpp @@ -52,6 +52,7 @@ GrShaderCaps::GrShaderCaps(const GrContextOptions& options) { fFPManipulationSupport = false; fFloatIs32Bits = true; fHalfIs32Bits = false; + fHasLowFragmentPrecision = false; fUnsignedSupport = false; fBuiltinFMASupport = false; @@ -125,6 +126,7 @@ void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const { writer->appendBool("Floating point manipulation support", fFPManipulationSupport); writer->appendBool("float == fp32", fFloatIs32Bits); writer->appendBool("half == fp32", fHalfIs32Bits); + writer->appendBool("Has poor fragment precision", fHasLowFragmentPrecision); writer->appendBool("Builtin fma() support", fBuiltinFMASupport); writer->appendS32("Max FS Samplers", fMaxFragmentSamplers); diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h index b842ac92eb..3ebb92a677 100644 --- a/src/gpu/GrShaderCaps.h +++ b/src/gpu/GrShaderCaps.h @@ -86,6 +86,8 @@ public: bool halfIs32Bits() const { return fHalfIs32Bits; } + bool hasLowFragmentPrecision() const { return fHasLowFragmentPrecision; } + bool unsignedSupport() const { return fUnsignedSupport; } // SkSL only. @@ -263,6 +265,7 @@ private: bool fFPManipulationSupport : 1; bool fFloatIs32Bits : 1; bool fHalfIs32Bits : 1; + bool fHasLowFragmentPrecision : 1; bool fUnsignedSupport : 1; // Used by SkSL to know when to generate polyfills. diff --git a/src/gpu/effects/GrEllipseEffect.cpp b/src/gpu/effects/GrEllipseEffect.cpp index b0644902e2..23e7fc2b27 100644 --- a/src/gpu/effects/GrEllipseEffect.cpp +++ b/src/gpu/effects/GrEllipseEffect.cpp @@ -29,33 +29,34 @@ public: auto radii = _outer.radii(); (void)radii; prevRadii = float2(-1.0); - useScale = !sk_Caps.floatIs32Bits; + medPrecision = !sk_Caps.floatIs32Bits; fEllipseVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kFloat4_GrSLType, "ellipse"); - if (useScale) { + if (medPrecision) { fScaleVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kFloat2_GrSLType, "scale"); } fragBuilder->codeAppendf( - "float2 prevCenter;\nfloat2 prevRadii = float2(%f, %f);\nbool useScale = " - "%s;\nfloat2 d = sk_FragCoord.xy - %s.xy;\n@if (useScale) {\n d *= " + "float2 prevCenter;\nfloat2 prevRadii = float2(%f, %f);\nbool medPrecision = " + "%s;\nfloat2 d = sk_FragCoord.xy - %s.xy;\n@if (medPrecision) {\n d *= " "%s.y;\n}\nfloat2 Z = d * %s.zw;\nfloat implicit = dot(Z, d) - 1.0;\nfloat " - "grad_dot = 4.0 * dot(Z, Z);\ngrad_dot = max(grad_dot, 0.0001);\nfloat approx_dist " - "= implicit * inversesqrt(grad_dot);\n@if (useScale) {\n approx_dist *= " - "%s.x;\n}\nhalf alpha;\n@switch (%d) {\n case 0:\n alpha = approx_dist > " - "0.0 ? 0.0 : 1.0;\n break;\n case 1:\n alph", - prevRadii.fX, prevRadii.fY, (useScale ? "true" : "false"), + "grad_dot = 4.0 * dot(Z, Z);\n@if (medPrecision) {\n grad_dot = max(grad_dot, " + "6.1036000000000003e-05);\n} else {\n grad_dot = max(grad_dot, " + "1.1755e-38);\n}\nfloat approx_dist = implicit * inversesqrt(grad_dot);\n@if " + "(medPrecision) {\n approx_dist *= %s.x;\n}\nhalf alpha;\n@switch ", + prevRadii.fX, prevRadii.fY, (medPrecision ? "true" : "false"), args.fUniformHandler->getUniformCStr(fEllipseVar), fScaleVar.isValid() ? args.fUniformHandler->getUniformCStr(fScaleVar) : "float2(0)", args.fUniformHandler->getUniformCStr(fEllipseVar), - fScaleVar.isValid() ? args.fUniformHandler->getUniformCStr(fScaleVar) : "float2(0)", - (int)_outer.edgeType()); + fScaleVar.isValid() ? args.fUniformHandler->getUniformCStr(fScaleVar) + : "float2(0)"); fragBuilder->codeAppendf( - "a = clamp(0.5 - half(approx_dist), 0.0, 1.0);\n break;\n case 2:\n " - " alpha = approx_dist > 0.0 ? 1.0 : 0.0;\n break;\n case 3:\n " - "alpha = clamp(0.5 + half(approx_dist), 0.0, 1.0);\n break;\n default:\n " - " discard;\n}\n%s = %s * alpha;\n", - args.fOutputColor, args.fInputColor); + "(%d) {\n case 0:\n alpha = approx_dist > 0.0 ? 0.0 : 1.0;\n " + "break;\n case 1:\n alpha = clamp(0.5 - half(approx_dist), 0.0, 1.0);\n " + " break;\n case 2:\n alpha = approx_dist > 0.0 ? 1.0 : 0.0;\n " + " break;\n case 3:\n alpha = clamp(0.5 + half(approx_dist), 0.0, 1.0);\n " + " break;\n default:\n discard;\n}\n%s = %s * alpha;\n", + (int)_outer.edgeType(), args.fOutputColor, args.fInputColor); } private: @@ -100,7 +101,7 @@ private: } SkPoint prevCenter = float2(0); SkPoint prevRadii = float2(0); - bool useScale = false; + bool medPrecision = false; UniformHandle fEllipseVar; UniformHandle fScaleVar; }; diff --git a/src/gpu/effects/GrEllipseEffect.fp b/src/gpu/effects/GrEllipseEffect.fp index e50a451252..256d0f416f 100644 --- a/src/gpu/effects/GrEllipseEffect.fp +++ b/src/gpu/effects/GrEllipseEffect.fp @@ -19,8 +19,8 @@ float2 prevRadii = float2(-1); // The last two terms can underflow when float != fp32, so we also provide a workaround. uniform float4 ellipse; -bool useScale = !sk_Caps.floatIs32Bits; -layout(when=useScale) uniform float2 scale; +bool medPrecision = !sk_Caps.floatIs32Bits; +layout(when=medPrecision) uniform float2 scale; @make { static std::unique_ptr<GrFragmentProcessor> Make(GrClipEdgeType edgeType, SkPoint center, @@ -29,6 +29,14 @@ layout(when=useScale) uniform float2 scale; if (!caps.floatIs32Bits() && (radii.fX < 0.5f || radii.fY < 0.5f)) { return nullptr; } + // Very narrow ellipses produce bad results on devices without full float + if (!caps.floatIs32Bits() && (radii.fX > 255*radii.fY || radii.fY > 255*radii.fX)) { + return nullptr; + } + // Very large ellipses produce bad results on devices without full float + if (!caps.floatIs32Bits() && (radii.fX > 16384 || radii.fY > 16384)) { + return nullptr; + } return std::unique_ptr<GrFragmentProcessor>(new GrEllipseEffect(edgeType, center, radii)); } } @@ -39,17 +47,16 @@ layout(when=useScale) uniform float2 scale; if (radii != prevRadii || center != prevCenter) { float invRXSqd; float invRYSqd; - // If we're using a scale factor to work around precision issues, choose the larger radius - // as the scale factor. The inv radii need to be pre-adjusted by the scale factor. + // If we're using a scale factor to work around precision issues, choose the larger + // radius as the scale factor. The inv radii need to be pre-adjusted by the scale + // factor. if (scale.isValid()) { if (radii.fX > radii.fY) { invRXSqd = 1.f; - invRYSqd = (radii.fX * radii.fX) / - (radii.fY * radii.fY); + invRYSqd = (radii.fX * radii.fX) / (radii.fY * radii.fY); pdman.set2f(scale, radii.fX, 1.f / radii.fX); } else { - invRXSqd = (radii.fY * radii.fY) / - (radii.fX * radii.fX); + invRXSqd = (radii.fY * radii.fY) / (radii.fX * radii.fX); invRYSqd = 1.f; pdman.set2f(scale, radii.fY, 1.f / radii.fY); } @@ -67,10 +74,10 @@ void main() { // d is the offset to the ellipse center float2 d = sk_FragCoord.xy - ellipse.xy; // If we're on a device with a "real" mediump then we'll do the distance computation in a space - // that is normalized by the larger radius. The scale uniform will be scale, 1/scale. The - // inverse squared radii uniform values are already in this normalized space. The center is - // not. - @if (useScale) { + // that is normalized by the larger radius or 128, whichever is smaller. The scale uniform will + // be scale, 1/scale. The inverse squared radii uniform values are already in this normalized space. + // The center is not. + @if (medPrecision) { d *= scale.y; } float2 Z = d * ellipse.zw; @@ -79,9 +86,13 @@ void main() { // grad_dot is the squared length of the gradient of the implicit. float grad_dot = 4 * dot(Z, Z); // Avoid calling inversesqrt on zero. - grad_dot = max(grad_dot, 1e-4); + @if (medPrecision) { + grad_dot = max(grad_dot, 6.1036e-5); + } else { + grad_dot = max(grad_dot, 1.1755e-38); + } float approx_dist = implicit * inversesqrt(grad_dot); - @if (useScale) { + @if (medPrecision) { approx_dist *= scale.x; } diff --git a/src/gpu/effects/GrEllipseEffect.h b/src/gpu/effects/GrEllipseEffect.h index ca1574d7c3..175d28a180 100644 --- a/src/gpu/effects/GrEllipseEffect.h +++ b/src/gpu/effects/GrEllipseEffect.h @@ -27,6 +27,14 @@ public: if (!caps.floatIs32Bits() && (radii.fX < 0.5f || radii.fY < 0.5f)) { return nullptr; } + // Very narrow ellipses produce bad results on devices without full float + if (!caps.floatIs32Bits() && (radii.fX > 255 * radii.fY || radii.fY > 255 * radii.fX)) { + return nullptr; + } + // Very large ellipses produce bad results on devices without full float + if (!caps.floatIs32Bits() && (radii.fX > 16384 || radii.fY > 16384)) { + return nullptr; + } return std::unique_ptr<GrFragmentProcessor>(new GrEllipseEffect(edgeType, center, radii)); } GrEllipseEffect(const GrEllipseEffect& src); diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index 891c9ec74b..248d8a1e57 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -815,6 +815,7 @@ void GrGLCaps::initGLSL(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli shaderCaps->fFloatIs32Bits = is_float_fp32(ctxInfo, gli, GR_GL_HIGH_FLOAT); shaderCaps->fHalfIs32Bits = is_float_fp32(ctxInfo, gli, GR_GL_MEDIUM_FLOAT); + shaderCaps->fHasLowFragmentPrecision = kMali4xx_GrGLRenderer == ctxInfo.renderer(); // Unsigned integers only supported in and after GLSL 1.30. shaderCaps->fUnsignedSupport = ctxInfo.glslGeneration() >= k130_GrGLSLGeneration; diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp index d49096e022..8bf691410b 100644 --- a/src/gpu/ops/GrOvalOpFactory.cpp +++ b/src/gpu/ops/GrOvalOpFactory.cpp @@ -6,6 +6,7 @@ */ #include "GrOvalOpFactory.h" +#include "GrCaps.h" #include "GrDrawOpTest.h" #include "GrGeometryProcessor.h" #include "GrOpFlushState.h" @@ -506,15 +507,21 @@ sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrPr class EllipseGeometryProcessor : public GrGeometryProcessor { public: - EllipseGeometryProcessor(bool stroke, bool wideColor, const SkMatrix& localMatrix) + EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale, + const SkMatrix& localMatrix) : INHERITED(kEllipseGeometryProcessor_ClassID) - , fLocalMatrix(localMatrix) { + , fLocalMatrix(localMatrix) + , fStroke(stroke) + , fUseScale(useScale) { fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; fInColor = MakeColorAttribute("inColor", wideColor); - fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kHalf2_GrSLType}; - fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kHalf4_GrSLType}; + if (useScale) { + fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; + } else { + fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; + } + fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType}; this->setVertexAttributes(&fInPosition, 4); - fStroke = stroke; } ~EllipseGeometryProcessor() override {} @@ -543,12 +550,13 @@ private: // emit attributes varyingHandler->emitAttributes(egp); - GrGLSLVarying ellipseOffsets(kHalf2_GrSLType); + GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType; + GrGLSLVarying ellipseOffsets(offsetType); varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets); vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(), egp.fInEllipseOffset.name()); - GrGLSLVarying ellipseRadii(kHalf4_GrSLType); + GrGLSLVarying ellipseRadii(kFloat4_GrSLType); varyingHandler->addVarying("EllipseRadii", &ellipseRadii); vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name()); @@ -573,31 +581,63 @@ private: // the distance by the gradient, non-uniformly scaled by the inverse of the // ellipse size. + // On medium precision devices, we scale the denominator of the distance equation + // before taking the inverse square root to minimize the chance that we're dividing + // by zero, then we scale the result back. + // for outer curve - fragBuilder->codeAppendf("half2 offset = %s;", ellipseOffsets.fsIn()); + fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn()); if (egp.fStroke) { fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn()); } - fragBuilder->codeAppend("half test = dot(offset, offset) - 1.0;"); - fragBuilder->codeAppendf("half2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn()); - fragBuilder->codeAppend("half grad_dot = dot(grad, grad);"); + fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;"); + if (egp.fUseScale) { + fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);", + ellipseOffsets.fsIn(), ellipseRadii.fsIn()); + } else { + fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn()); + } + fragBuilder->codeAppend("float grad_dot = dot(grad, grad);"); // avoid calling inversesqrt on zero. - fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); - fragBuilder->codeAppend("half invlen = half(inversesqrt(grad_dot));"); - fragBuilder->codeAppend("half edgeAlpha = saturate(0.5-test*invlen);"); + if (args.fShaderCaps->floatIs32Bits()) { + fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);"); + } else { + fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);"); + } + if (egp.fUseScale) { + fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);", + ellipseOffsets.fsIn()); + } else { + fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);"); + } + fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);"); // for inner curve if (egp.fStroke) { - fragBuilder->codeAppendf("offset = %s*%s.zw;", ellipseOffsets.fsIn(), + fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(), ellipseRadii.fsIn()); fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;"); - fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn()); - fragBuilder->codeAppend("invlen = half(inversesqrt(dot(grad, grad)));"); + if (egp.fUseScale) { + fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);", + ellipseOffsets.fsIn(), ellipseRadii.fsIn()); + } else { + fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn()); + } + fragBuilder->codeAppend("grad_dot = dot(grad, grad);"); + if (!args.fShaderCaps->floatIs32Bits()) { + fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);"); + } + if (egp.fUseScale) { + fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);", + ellipseOffsets.fsIn()); + } else { + fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);"); + } fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);"); } - fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); + fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage); } static void GenKey(const GrGeometryProcessor& gp, @@ -626,6 +666,7 @@ private: SkMatrix fLocalMatrix; bool fStroke; + bool fUseScale; GR_DECLARE_GEOMETRY_PROCESSOR_TEST @@ -638,7 +679,7 @@ GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor); sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) { return sk_sp<GrGeometryProcessor>( new EllipseGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(), - GrTest::TestMatrix(d->fRandom))); + d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom))); } #endif @@ -657,14 +698,22 @@ enum class DIEllipseStyle { kStroke = 0, kHairline, kFill }; class DIEllipseGeometryProcessor : public GrGeometryProcessor { public: - DIEllipseGeometryProcessor(bool wideColor, const SkMatrix& viewMatrix, DIEllipseStyle style) + DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix, + DIEllipseStyle style) : INHERITED(kDIEllipseGeometryProcessor_ClassID) - , fViewMatrix(viewMatrix) { - fStyle = style; + , fViewMatrix(viewMatrix) + , fUseScale(useScale) + , fStyle(style) { fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; fInColor = MakeColorAttribute("inColor", wideColor); - fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType, kHalf2_GrSLType}; - fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kHalf2_GrSLType}; + if (useScale) { + fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType, + kFloat3_GrSLType}; + } else { + fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType, + kFloat2_GrSLType}; + } + fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; this->setVertexAttributes(&fInPosition, 4); } @@ -694,11 +743,12 @@ private: // emit attributes varyingHandler->emitAttributes(diegp); - GrGLSLVarying offsets0(kHalf2_GrSLType); + GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType; + GrGLSLVarying offsets0(offsetType); varyingHandler->addVarying("EllipseOffsets0", &offsets0); vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name()); - GrGLSLVarying offsets1(kHalf2_GrSLType); + GrGLSLVarying offsets1(kFloat2_GrSLType); varyingHandler->addVarying("EllipseOffsets1", &offsets1); vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name()); @@ -721,42 +771,62 @@ private: args.fFPCoordTransformHandler); // for outer curve - fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn()); - fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;"); - fragBuilder->codeAppendf("half2 duvdx = half2(dFdx(%s));", offsets0.fsIn()); - fragBuilder->codeAppendf("half2 duvdy = half2(dFdy(%s));", offsets0.fsIn()); + fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn()); + fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;"); + fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn()); + fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn()); fragBuilder->codeAppendf( - "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," - " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", + "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y," + " %s.x*duvdy.x + %s.y*duvdy.y);", offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn()); + if (diegp.fUseScale) { + fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn()); + } - fragBuilder->codeAppend("half grad_dot = dot(grad, grad);"); + fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);"); // avoid calling inversesqrt on zero. - fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); - fragBuilder->codeAppend("half invlen = half(inversesqrt(grad_dot));"); + if (args.fShaderCaps->floatIs32Bits()) { + fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);"); + } else { + fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);"); + } + fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);"); + if (diegp.fUseScale) { + fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn()); + } if (DIEllipseStyle::kHairline == diegp.fStyle) { // can probably do this with one step - fragBuilder->codeAppend("half edgeAlpha = saturate(1.0-test*invlen);"); + fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);"); fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);"); } else { - fragBuilder->codeAppend("half edgeAlpha = saturate(0.5-test*invlen);"); + fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);"); } // for inner curve if (DIEllipseStyle::kStroke == diegp.fStyle) { fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn()); fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); - fragBuilder->codeAppendf("duvdx = half2(dFdx(%s));", offsets1.fsIn()); - fragBuilder->codeAppendf("duvdy = half2(dFdy(%s));", offsets1.fsIn()); + fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn()); + fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn()); fragBuilder->codeAppendf( - "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," - " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", + "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y," + " %s.x*duvdy.x + %s.y*duvdy.y);", offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn()); - fragBuilder->codeAppend("invlen = half(inversesqrt(dot(grad, grad)));"); + if (diegp.fUseScale) { + fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn()); + } + fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);"); + if (!args.fShaderCaps->floatIs32Bits()) { + fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);"); + } + fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);"); + if (diegp.fUseScale) { + fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn()); + } fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);"); } - fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); + fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage); } static void GenKey(const GrGeometryProcessor& gp, @@ -795,6 +865,7 @@ private: Attribute fInEllipseOffsets1; SkMatrix fViewMatrix; + bool fUseScale; DIEllipseStyle fStyle; GR_DECLARE_GEOMETRY_PROCESSOR_TEST @@ -807,7 +878,7 @@ GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor); #if GR_TEST_UTILS sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) { return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor( - d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom), + d->fRandom->nextBool(), d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)))); } #endif @@ -1709,6 +1780,17 @@ public: params.fXRadius += scaledStroke.fX; params.fYRadius += scaledStroke.fY; } + + // For large ovals with low precision floats, we fall back to the path renderer. + // To compute the AA at the edge we divide by the gradient, which is clamped to a + // minimum value to avoid divides by zero. With large ovals and low precision this + // leads to blurring at the edge of the oval. + const SkScalar kMaxOvalRadius = 16384; + if (!context->priv().caps()->shaderCaps()->floatIs32Bits() && + (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) { + return nullptr; + } + return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix, params, stroke); } @@ -1716,7 +1798,9 @@ public: EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, const SkMatrix& viewMatrix, const DeviceSpaceParams& params, const SkStrokeRec& stroke) - : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { + : INHERITED(ClassID()) + , fHelper(helperArgs, GrAAType::kCoverage) + , fUseScale(false) { SkStrokeRec::Style style = stroke.getStyle(); bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; @@ -1764,6 +1848,8 @@ public: GrProcessorSet::Analysis finalize( const GrCaps& caps, const GrAppliedClip* clip, GrFSAAType fsaaType) override { + fUseScale = !caps.shaderCaps()->floatIs32Bits() && + !caps.shaderCaps()->hasLowFragmentPrecision(); SkPMColor4f* color = &fEllipses.front().fColor; return fHelper.finalizeProcessors( caps, clip, fsaaType, GrProcessorAnalysisCoverage::kSingleChannel, color); @@ -1779,7 +1865,7 @@ private: } // Setup geometry processor - sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, + sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, fUseScale, localMatrix)); QuadHelper helper(target, gp->vertexStride(), fEllipses.count()); GrVertexWriter verts{helper.vertices()}; @@ -1813,6 +1899,7 @@ private: verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds), color, origin_centered_tri_strip(xMaxOffset, yMaxOffset), + GrVertexWriter::If(fUseScale, SkTMax(xRadius, yRadius)), invRadii); } helper.recordDraw(target, std::move(gp)); @@ -1856,6 +1943,7 @@ private: Helper fHelper; bool fStroked; bool fWideColor; + bool fUseScale; SkSTArray<1, Ellipse, true> fEllipses; typedef GrMeshDrawOp INHERITED; @@ -1933,6 +2021,17 @@ public: params.fXRadius += strokeWidth; params.fYRadius += strokeWidth; } + + // For large ovals with low precision floats, we fall back to the path renderer. + // To compute the AA at the edge we divide by the gradient, which is clamped to a + // minimum value to avoid divides by zero. With large ovals and low precision this + // leads to blurring at the edge of the oval. + const SkScalar kMaxOvalRadius = 16384; + if (!context->priv().caps()->shaderCaps()->floatIs32Bits() && + (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) { + return nullptr; + } + if (DIEllipseStyle::kStroke == params.fStyle && (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) { params.fStyle = DIEllipseStyle::kFill; @@ -1942,7 +2041,9 @@ public: DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color, const DeviceSpaceParams& params, const SkMatrix& viewMatrix) - : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { + : INHERITED(ClassID()) + , fHelper(helperArgs, GrAAType::kCoverage) + , fUseScale(false) { // This expands the outer rect so that after CTM we end up with a half-pixel border SkScalar a = viewMatrix[SkMatrix::kMScaleX]; SkScalar b = viewMatrix[SkMatrix::kMSkewX]; @@ -1989,6 +2090,8 @@ public: GrProcessorSet::Analysis finalize( const GrCaps& caps, const GrAppliedClip* clip, GrFSAAType fsaaType) override { + fUseScale = !caps.shaderCaps()->floatIs32Bits() && + !caps.shaderCaps()->hasLowFragmentPrecision(); SkPMColor4f* color = &fEllipses.front().fColor; return fHelper.finalizeProcessors( caps, clip, fsaaType, GrProcessorAnalysisCoverage::kSingleChannel, color); @@ -2000,7 +2103,8 @@ private: void onPrepareDraws(Target* target) override { // Setup geometry processor sk_sp<GrGeometryProcessor> gp( - new DIEllipseGeometryProcessor(fWideColor, this->viewMatrix(), this->style())); + new DIEllipseGeometryProcessor(fWideColor, fUseScale, this->viewMatrix(), + this->style())); QuadHelper helper(target, gp->vertexStride(), fEllipses.count()); GrVertexWriter verts{helper.vertices()}; @@ -2030,6 +2134,7 @@ private: verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds), color, origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy), + GrVertexWriter::If(fUseScale, SkTMax(xRadius, yRadius)), origin_centered_tri_strip(innerRatioX + offsetDx, innerRatioY + offsetDy)); } @@ -2078,6 +2183,7 @@ private: Helper fHelper; bool fWideColor; + bool fUseScale; SkSTArray<1, Ellipse, true> fEllipses; typedef GrMeshDrawOp INHERITED; @@ -2593,7 +2699,9 @@ public: EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color, const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius, float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly) - : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { + : INHERITED(ClassID()) + , fHelper(helperArgs, GrAAType::kCoverage) + , fUseScale(false) { SkScalar innerXRadius = 0.0f; SkScalar innerYRadius = 0.0f; SkRect bounds = devRect; @@ -2647,6 +2755,7 @@ public: GrProcessorSet::Analysis finalize( const GrCaps& caps, const GrAppliedClip* clip, GrFSAAType fsaaType) override { + fUseScale = !caps.shaderCaps()->floatIs32Bits(); SkPMColor4f* color = &fRRects.front().fColor; return fHelper.finalizeProcessors( caps, clip, fsaaType, GrProcessorAnalysisCoverage::kSingleChannel, color); @@ -2662,7 +2771,7 @@ private: } // Setup geometry processor - sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, + sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, fUseScale, localMatrix)); // drop out the middle quad if we're stroked @@ -2715,25 +2824,30 @@ private: // shader, so can't be exactly 0 SK_ScalarNearlyZero, yMaxOffset}; + auto maybeScale = GrVertexWriter::If(fUseScale, SkTMax(rrect.fXRadius, rrect.fYRadius)); for (int i = 0; i < 4; ++i) { verts.write(bounds.fLeft, yCoords[i], color, xMaxOffset, yOuterOffsets[i], + maybeScale, reciprocalRadii); verts.write(bounds.fLeft + xOuterRadius, yCoords[i], color, SK_ScalarNearlyZero, yOuterOffsets[i], + maybeScale, reciprocalRadii); verts.write(bounds.fRight - xOuterRadius, yCoords[i], color, SK_ScalarNearlyZero, yOuterOffsets[i], + maybeScale, reciprocalRadii); verts.write(bounds.fRight, yCoords[i], color, xMaxOffset, yOuterOffsets[i], + maybeScale, reciprocalRadii); } } @@ -2778,6 +2892,7 @@ private: Helper fHelper; bool fStroked; bool fWideColor; + bool fUseScale; SkSTArray<1, RRect, true> fRRects; typedef GrMeshDrawOp INHERITED; |