Relocate gradient negative fract() workaround to GLSLCodeGenerator

Change-Id: If5aba5b266f86f677b6e63b0f79792f1d3213336
Reviewed-on: https://skia-review.googlesource.com/32202
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index 186f683..767955a 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -1557,14 +1557,7 @@
         fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
         break;
     case SkShader::kRepeat_TileMode:
-        if (shaderCaps->canUseFractForNegativeValues()) {
-            fragBuilder->codeAppendf("float clamp_t = fract(%s);", t);
-        } else {
-            // Tegra3 fract() sometimes yields undefined results when the arg is negative.
-            // TODO: relocate this workaround to GLSLCodeGenerator.
-            fragBuilder->codeAppendf("float clamp_t = 0.5 - sign(%s) * (0.5 - fract(abs(%s)));",
-                                     t, t);
-        }
+        fragBuilder->codeAppendf("float clamp_t = fract(%s);", t);
         break;
     case SkShader::kMirror_TileMode:
         fragBuilder->codeAppendf("float t_1 = %s - 1.0;", t);
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index 69d3028..64527a0 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -234,6 +234,18 @@
             return;
         }
     }
+    if (!fProgram.fSettings.fCaps->canUseFractForNegativeValues() && c.fFunction.fName == "fract" &&
+        c.fFunction.fBuiltin) {
+        ASSERT(c.fArguments.size() == 1);
+
+        this->write("(0.5 - sign(");
+        this->writeExpression(*c.fArguments[0], kSequence_Precedence);
+        this->write(") * (0.5 - fract(abs(");
+        this->writeExpression(*c.fArguments[0], kSequence_Precedence);
+        this->write("))))");
+
+        return;
+    }
     if (fProgram.fSettings.fCaps->mustForceNegatedAtanParamToFloat() &&
         c.fFunction.fName == "atan" &&
         c.fFunction.fBuiltin && c.fArguments.size() == 2 &&
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
index f0951a3..dba5196 100644
--- a/src/sksl/SkSLUtil.h
+++ b/src/sksl/SkSLUtil.h
@@ -206,6 +206,13 @@
         return result;
     }
 
+    static sk_sp<GrShaderCaps> CannotUseFractForNegativeValues() {
+        sk_sp<GrShaderCaps> result = sk_make_sp<GrShaderCaps>(GrContextOptions());
+        result->fVersionDeclString = "#version 400";
+        result->fCanUseFractForNegativeValues = false;
+        return result;
+    }
+
     static sk_sp<GrShaderCaps> MustForceNegatedAtanParamToFloat() {
         sk_sp<GrShaderCaps> result = sk_make_sp<GrShaderCaps>(GrContextOptions());
         result->fVersionDeclString = "#version 400";
diff --git a/tests/SkSLGLSLTest.cpp b/tests/SkSLGLSLTest.cpp
index 80727d9..c1bf2bb 100644
--- a/tests/SkSLGLSLTest.cpp
+++ b/tests/SkSLGLSLTest.cpp
@@ -360,6 +360,29 @@
          "}\n");
 }
 
+DEF_TEST(SkSLFractNegative, r) {
+    static constexpr char input[] =
+        "void main() {"
+        "float x = -42.0;"
+        "sk_FragColor.r = fract(x);"
+        "}";
+    static constexpr char output_default[] =
+        "#version 400\n"
+        "out vec4 sk_FragColor;\n"
+        "void main() {\n"
+        "    sk_FragColor.x = fract(-42.0);\n"
+        "}\n";
+    static constexpr char output_workaround[] =
+        "#version 400\n"
+        "out vec4 sk_FragColor;\n"
+        "void main() {\n"
+        "    sk_FragColor.x = (0.5 - sign(-42.0) * (0.5 - fract(abs(-42.0))));\n"
+        "}\n";
+
+    test(r, input, *SkSL::ShaderCapsFactory::Default(), output_default);
+    test(r, input, *SkSL::ShaderCapsFactory::CannotUseFractForNegativeValues(), output_workaround);
+}
+
 DEF_TEST(SkSLNegatedAtan, r) {
     test(r,
          "void main() { float2 x = float2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }",