diff options
author | bsalomon <bsalomon@google.com> | 2015-12-09 17:14:40 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-12-09 17:14:40 -0800 |
commit | e87256c46291f7ce10f1d9a1d691d01ecf4eb3cc (patch) | |
tree | 4c4a068523f44054fa89ef7bd347fd03d4c98b94 /src/gpu | |
parent | f1ecd21bf6fd69953f78dd79502c7c9df92cdc9a (diff) |
Do elliptical clips in normalized space on devices with a "real" mediump
BUG=chromium:477684
Review URL: https://codereview.chromium.org/1517573002
Diffstat (limited to 'src/gpu')
-rw-r--r-- | src/gpu/effects/GrOvalEffect.cpp | 73 | ||||
-rw-r--r-- | src/gpu/effects/GrRRectEffect.cpp | 89 |
2 files changed, 121 insertions, 41 deletions
diff --git a/src/gpu/effects/GrOvalEffect.cpp b/src/gpu/effects/GrOvalEffect.cpp index 4532ddfd3c..c7e685f902 100644 --- a/src/gpu/effects/GrOvalEffect.cpp +++ b/src/gpu/effects/GrOvalEffect.cpp @@ -133,19 +133,19 @@ void GLCircleEffect::emitCode(EmitArgs& args) { // mediump. It'd be nice to only to this on mediump devices but we currently don't have the // caps here. if (GrProcessorEdgeTypeIsInverseFill(ce.getEdgeType())) { - fragBuilder->codeAppendf("\t\tfloat d = (length((%s.xy - %s.xy) * %s.w) - 1.0) * %s.z;\n", + fragBuilder->codeAppendf("float d = (length((%s.xy - %s.xy) * %s.w) - 1.0) * %s.z;", circleName, fragmentPos, circleName, circleName); } else { - fragBuilder->codeAppendf("\t\tfloat d = (1.0 - length((%s.xy - %s.xy) * %s.w)) * %s.z;\n", + fragBuilder->codeAppendf("float d = (1.0 - length((%s.xy - %s.xy) * %s.w)) * %s.z;", circleName, fragmentPos, circleName, circleName); } if (GrProcessorEdgeTypeIsAA(ce.getEdgeType())) { - fragBuilder->codeAppend("\t\td = clamp(d, 0.0, 1.0);\n"); + fragBuilder->codeAppend("d = clamp(d, 0.0, 1.0);"); } else { - fragBuilder->codeAppend("\t\td = d > 0.5 ? 1.0 : 0.0;\n"); + fragBuilder->codeAppend("d = d > 0.5 ? 1.0 : 0.0;"); } - fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("d")).c_str()); } @@ -276,6 +276,7 @@ protected: private: GrGLSLProgramDataManager::UniformHandle fEllipseUniform; + GrGLSLProgramDataManager::UniformHandle fScaleUniform; SkPoint fPrevCenter; SkVector fPrevRadii; @@ -295,39 +296,55 @@ void GLEllipseEffect::emitCode(EmitArgs& args) { kVec4f_GrSLType, kHigh_GrSLPrecision, "ellipse", &ellipseName); + // 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. + const char* scaleName = nullptr; + if (args.fGLSLCaps->floatPrecisionVaries()) { + fScaleUniform = args.fUniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, + "scale", &scaleName); + } GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* fragmentPos = fragBuilder->fragmentPosition(); // d is the offset to the ellipse center - fragBuilder->codeAppendf("\t\tvec2 d = %s.xy - %s.xy;\n", fragmentPos, ellipseName); - fragBuilder->codeAppendf("\t\tvec2 Z = d * %s.zw;\n", ellipseName); + fragBuilder->codeAppendf("vec2 d = %s.xy - %s.xy;", fragmentPos, ellipseName); + if (scaleName) { + fragBuilder->codeAppendf("d *= %s.y;", scaleName); + } + fragBuilder->codeAppendf("vec2 Z = d * %s.zw;", ellipseName); // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1. - fragBuilder->codeAppend("\t\tfloat implicit = dot(Z, d) - 1.0;\n"); + fragBuilder->codeAppend("float implicit = dot(Z, d) - 1.0;"); // grad_dot is the squared length of the gradient of the implicit. - fragBuilder->codeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n"); - // avoid calling inversesqrt on zero. - fragBuilder->codeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); - fragBuilder->codeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n"); + fragBuilder->codeAppendf("float grad_dot = 4.0 * dot(Z, Z);"); + // Avoid calling inversesqrt on zero. + fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); + fragBuilder->codeAppendf("float approx_dist = implicit * inversesqrt(grad_dot);"); + if (scaleName) { + fragBuilder->codeAppendf("approx_dist *= %s.x;", scaleName); + } switch (ee.getEdgeType()) { case kFillAA_GrProcessorEdgeType: - fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); + fragBuilder->codeAppend("float alpha = clamp(0.5 - approx_dist, 0.0, 1.0);"); break; case kInverseFillAA_GrProcessorEdgeType: - fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n"); + fragBuilder->codeAppend("float alpha = clamp(0.5 + approx_dist, 0.0, 1.0);"); break; case kFillBW_GrProcessorEdgeType: - fragBuilder->codeAppend("\t\tfloat alpha = approx_dist > 0.0 ? 0.0 : 1.0;\n"); + fragBuilder->codeAppend("float alpha = approx_dist > 0.0 ? 0.0 : 1.0;"); break; case kInverseFillBW_GrProcessorEdgeType: - fragBuilder->codeAppend("\t\tfloat alpha = approx_dist > 0.0 ? 1.0 : 0.0;\n"); + fragBuilder->codeAppend("float alpha = approx_dist > 0.0 ? 1.0 : 0.0;"); break; case kHairlineAA_GrProcessorEdgeType: SkFAIL("Hairline not expected here."); } - fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); } @@ -341,8 +358,26 @@ void GLEllipseEffect::onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& effect) { const EllipseEffect& ee = effect.cast<EllipseEffect>(); if (ee.getRadii() != fPrevRadii || ee.getCenter() != fPrevCenter) { - SkScalar invRXSqd = 1.f / (ee.getRadii().fX * ee.getRadii().fX); - SkScalar invRYSqd = 1.f / (ee.getRadii().fY * ee.getRadii().fY); + 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 (fScaleUniform.isValid()) { + if (ee.getRadii().fX > ee.getRadii().fY) { + invRXSqd = 1.f; + invRYSqd = (ee.getRadii().fX * ee.getRadii().fX) / + (ee.getRadii().fY * ee.getRadii().fY); + pdman.set2f(fScaleUniform, ee.getRadii().fX, 1.f / ee.getRadii().fX); + } else { + invRXSqd = (ee.getRadii().fY * ee.getRadii().fY) / + (ee.getRadii().fX * ee.getRadii().fX); + invRYSqd = 1.f; + pdman.set2f(fScaleUniform, ee.getRadii().fY, 1.f / ee.getRadii().fY); + } + } else { + invRXSqd = 1.f / (ee.getRadii().fX * ee.getRadii().fX); + invRYSqd = 1.f / (ee.getRadii().fY * ee.getRadii().fY); + } pdman.set4f(fEllipseUniform, ee.getCenter().fX, ee.getCenter().fY, invRXSqd, invRYSqd); fPrevCenter = ee.getCenter(); fPrevRadii = ee.getRadii(); diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp index 0b758759e8..0b492762c2 100644 --- a/src/gpu/effects/GrRRectEffect.cpp +++ b/src/gpu/effects/GrRRectEffect.cpp @@ -498,7 +498,8 @@ protected: private: GrGLSLProgramDataManager::UniformHandle fInnerRectUniform; GrGLSLProgramDataManager::UniformHandle fInvRadiiSqdUniform; - SkRRect fPrevRRect; + GrGLSLProgramDataManager::UniformHandle fScaleUniform; + SkRRect fPrevRRect; typedef GrGLSLFragmentProcessor INHERITED; }; @@ -530,55 +531,77 @@ void GLEllipticalRRectEffect::emitCode(EmitArgs& args) { // The code below is a simplified version of the above that performs maxs on the vector // components before computing distances and alpha values so that only one distance computation // need be computed to determine the min alpha. - fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); + fragBuilder->codeAppendf("vec2 dxy0 = %s.xy - %s.xy;", rectName, fragmentPos); + fragBuilder->codeAppendf("vec2 dxy1 = %s.xy - %s.zw;", fragmentPos, rectName); + + // 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 largest radius. The scale uniform will be scale, 1/scale. The + // radii uniform values are already in this normalized space. + const char* scaleName = nullptr; + if (args.fGLSLCaps->floatPrecisionVaries()) { + fScaleUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "scale", &scaleName); + } + // The uniforms with the inv squared radii are highp to prevent underflow. switch (erre.getRRect().getType()) { case SkRRect::kSimple_Type: { const char *invRadiiXYSqdName; fInvRadiiSqdUniform = uniformHandler->addUniform( GrGLSLUniformHandler::kFragment_Visibility, - kVec2f_GrSLType, kHigh_GrSLPrecision, + kVec2f_GrSLType, kDefault_GrSLPrecision, "invRadiiXY", &invRadiiXYSqdName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); + fragBuilder->codeAppend("vec2 dxy = max(max(dxy0, dxy1), 0.0);"); + if (scaleName) { + fragBuilder->codeAppendf("dxy *= %s.y;", scaleName); + } // Z is the x/y offsets divided by squared radii. - fragBuilder->codeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName); + fragBuilder->codeAppendf("vec2 Z = dxy * %s.xy;", invRadiiXYSqdName); break; } case SkRRect::kNinePatch_Type: { const char *invRadiiLTRBSqdName; fInvRadiiSqdUniform = uniformHandler->addUniform( GrGLSLUniformHandler::kFragment_Visibility, - kVec4f_GrSLType, kHigh_GrSLPrecision, + kVec4f_GrSLType, kDefault_GrSLPrecision, "invRadiiLTRB", &invRadiiLTRBSqdName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); + if (scaleName) { + fragBuilder->codeAppendf("dxy0 *= %s.y;", scaleName); + fragBuilder->codeAppendf("dxy1 *= %s.y;", scaleName); + } + fragBuilder->codeAppend("vec2 dxy = max(max(dxy0, dxy1), 0.0);"); // Z is the x/y offsets divided by squared radii. We only care about the (at most) one // corner where both the x and y offsets are positive, hence the maxes. (The inverse // squared radii will always be positive.) - fragBuilder->codeAppendf("\t\tvec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);\n", + fragBuilder->codeAppendf("vec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);", invRadiiLTRBSqdName, invRadiiLTRBSqdName); + break; } default: SkFAIL("RRect should always be simple or nine-patch."); } // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1. - fragBuilder->codeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n"); + fragBuilder->codeAppend("float implicit = dot(Z, dxy) - 1.0;"); // grad_dot is the squared length of the gradient of the implicit. - fragBuilder->codeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n"); + fragBuilder->codeAppend("float grad_dot = 4.0 * dot(Z, Z);"); // avoid calling inversesqrt on zero. - fragBuilder->codeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); - fragBuilder->codeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n"); + fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); + fragBuilder->codeAppend("float approx_dist = implicit * inversesqrt(grad_dot);"); + if (scaleName) { + fragBuilder->codeAppendf("approx_dist *= %s.x;", scaleName); + } if (kFillAA_GrProcessorEdgeType == erre.getEdgeType()) { - fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); + fragBuilder->codeAppend("float alpha = clamp(0.5 - approx_dist, 0.0, 1.0);"); } else { - fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n"); + fragBuilder->codeAppend("float alpha = clamp(0.5 + approx_dist, 0.0, 1.0);"); } - fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); } @@ -593,6 +616,8 @@ void GLEllipticalRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& effect) { const EllipticalRRectEffect& erre = effect.cast<EllipticalRRectEffect>(); const SkRRect& rrect = erre.getRRect(); + // If we're using a scale factor to work around precision issues, choose the largest radius + // as the scale factor. The inv radii need to be pre-adjusted by the scale factor. if (rrect != fPrevRRect) { SkRect rect = rrect.getBounds(); const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); @@ -601,8 +626,18 @@ void GLEllipticalRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman, switch (erre.getRRect().getType()) { case SkRRect::kSimple_Type: rect.inset(r0.fX, r0.fY); - pdman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), - 1.f / (r0.fY * r0.fY)); + if (fScaleUniform.isValid()) { + if (r0.fX > r0.fY) { + pdman.set2f(fInvRadiiSqdUniform, 1.f, (r0.fX * r0.fX) / (r0.fY * r0.fY)); + pdman.set2f(fScaleUniform, r0.fX, 1.f / r0.fX); + } else { + pdman.set2f(fInvRadiiSqdUniform, (r0.fY * r0.fY) / (r0.fX * r0.fX), 1.f); + pdman.set2f(fScaleUniform, r0.fY, 1.f / r0.fY); + } + } else { + pdman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), + 1.f / (r0.fY * r0.fY)); + } break; case SkRRect::kNinePatch_Type: { const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); @@ -612,10 +647,20 @@ void GLEllipticalRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman, rect.fTop += r0.fY; rect.fRight -= r1.fX; rect.fBottom -= r1.fY; - pdman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), - 1.f / (r0.fY * r0.fY), - 1.f / (r1.fX * r1.fX), - 1.f / (r1.fY * r1.fY)); + if (fScaleUniform.isValid()) { + float scale = SkTMax(SkTMax(r0.fX, r0.fY), SkTMax(r1.fX, r1.fY)); + float scaleSqd = scale * scale; + pdman.set4f(fInvRadiiSqdUniform, scaleSqd / (r0.fX * r0.fX), + scaleSqd / (r0.fY * r0.fY), + scaleSqd / (r1.fX * r1.fX), + scaleSqd / (r1.fY * r1.fY)); + pdman.set2f(fScaleUniform, scale, 1.f / scale); + } else { + pdman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), + 1.f / (r0.fY * r0.fY), + 1.f / (r1.fX * r1.fX), + 1.f / (r1.fY * r1.fY)); + } break; } default: |