aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Van Verth <jvanverth@google.com>2019-03-29 08:50:41 -0400
committerSkia Commit-Bot <skia-commit-bot@chromium.org>2019-04-01 20:30:44 +0000
commitcd749e013a4a4aa41f527a0bb37912398ccafdbb (patch)
tree3d41a3bfaa44c3abd5da35ccc6e0a2224f27f9be
parent12f2883b9b50d3f4999eb2615e408656fb877bca (diff)
downloadskia-cd749e013a4a4aa41f527a0bb37912398ccafdbb.tar.gz
Reland "Reland "Fix blurry edges on large ovals.""
This is a reland of 4dd7c1dae12c8e3b0d7777910b9ab8b29efe5ebf Original change's description: > Reland "Fix blurry edges on large ovals." > > This is a reland of c7aed036c341eff661de2ccc3a728a9b68635159 > > Changes the precision for the GrOvalOpFactory shaders to be float rather than half. > In the medium-precision case, adds a scale factor to the shaders to ensure that the > denominator of the distance calculation stays within range. If enough precision isn't > available, falls back to a path renderer. > > Original change's description: > > Fix blurry edges on large ovals. > > > > Changes the precision for the GrOvalOpFactory shaders to be float rather than half. In the > > low-precision case, it falls back to a path renderer for large ovals. > > > > Bug: b/110380864, skia:8873 > > Change-Id: I89e8dd067a2e0cab35b1bb515adaab4a2fb4f222 > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/201615 > > Commit-Queue: Jim Van Verth <jvanverth@google.com> > > Reviewed-by: Brian Salomon <bsalomon@google.com> > > Bug: b/110380864, skia:8873 > Change-Id: Ic72d819f589ffebf9c5250bb4df4de22ab23f282 > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/202718 > Reviewed-by: Brian Osman <brianosman@google.com> > Reviewed-by: Brian Salomon <bsalomon@google.com> > Commit-Queue: Jim Van Verth <jvanverth@google.com> Bug: b/110380864, skia:8873 Change-Id: I026d943f4ac005e04ef7350c5af67fda6865ae92 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/203729 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Jim Van Verth <jvanverth@google.com> (cherry picked from commit 20ae25ce11f74753ed042295b3b7016e3460a404) Reviewed-on: https://skia-review.googlesource.com/c/skia/+/204768 Auto-Submit: Jim Van Verth <jvanverth@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
-rw-r--r--gm/conicpaths.cpp30
-rw-r--r--src/gpu/GrShaderCaps.cpp2
-rw-r--r--src/gpu/GrShaderCaps.h3
-rw-r--r--src/gpu/effects/GrEllipseEffect.cpp35
-rw-r--r--src/gpu/effects/GrEllipseEffect.fp39
-rw-r--r--src/gpu/effects/GrEllipseEffect.h8
-rw-r--r--src/gpu/gl/GrGLCaps.cpp1
-rw-r--r--src/gpu/ops/GrOvalOpFactory.cpp215
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;