Do elliptical clips in normalized space on devices with a "real" mediump
authorbsalomon <bsalomon@google.com>
Thu, 10 Dec 2015 01:14:40 +0000 (17:14 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 10 Dec 2015 01:14:40 +0000 (17:14 -0800)
BUG=chromium:477684

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

src/gpu/effects/GrOvalEffect.cpp
src/gpu/effects/GrRRectEffect.cpp

index 4532ddf..c7e685f 100644 (file)
@@ -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();
index 0b75875..0b49276 100644 (file)
@@ -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: