Support sample coords in .fp main()

This removes the code generation support for @coordTransform and sk_TransformedCoords2D
when processing .fp files. Instead, the main() function can optionally add a float2
parameter. This is marked as the SK_MAIN_COORDS builtin, just like the main function
for a runtime pipeline stage.

Bug: skia:10416
Change-Id: I0c192d890bb798a1167bc445003f6ddffe6118f0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/299687
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/effects/GrMagnifierEffect.fp b/src/gpu/effects/GrMagnifierEffect.fp
index 5dc7cec..22f53b0 100644
--- a/src/gpu/effects/GrMagnifierEffect.fp
+++ b/src/gpu/effects/GrMagnifierEffect.fp
@@ -16,8 +16,7 @@
 
 uniform half2 offset;
 
-void main() {
-    float2 coord = sk_TransformedCoords2D[0];
+void main(float2 coord) {
     float2 zoom_coord = offset + coord * float2(xInvZoom, yInvZoom);
     float2 delta = (coord - boundsUniform.xy) * boundsUniform.zw;
     delta = min(delta, half2(1.0, 1.0) - delta);
diff --git a/src/gpu/effects/generated/GrMagnifierEffect.cpp b/src/gpu/effects/generated/GrMagnifierEffect.cpp
index b447b1a..118e1b6 100644
--- a/src/gpu/effects/generated/GrMagnifierEffect.cpp
+++ b/src/gpu/effects/generated/GrMagnifierEffect.cpp
@@ -48,9 +48,8 @@
         offsetVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag,
                                                      kHalf2_GrSLType, "offset");
         fragBuilder->codeAppendf(
-                R"SkSL(float2 coord = %s;
-float2 zoom_coord = float2(%s) + coord * float2(%s, %s);
-float2 delta = (coord - %s.xy) * %s.zw;
+                R"SkSL(float2 zoom_coord = float2(%s) + %s * float2(%s, %s);
+float2 delta = (%s - %s.xy) * %s.zw;
 delta = min(delta, float2(half2(1.0, 1.0)) - delta);
 delta *= float2(%s, %s);
 float weight = 0.0;
@@ -63,21 +62,21 @@
     float2 delta_squared = delta * delta;
     weight = min(min(delta_squared.x, delta_squared.y), 1.0);
 })SkSL",
-                args.fSampleCoord, args.fUniformHandler->getUniformCStr(offsetVar),
+                args.fUniformHandler->getUniformCStr(offsetVar), args.fSampleCoord,
                 args.fUniformHandler->getUniformCStr(xInvZoomVar),
-                args.fUniformHandler->getUniformCStr(yInvZoomVar),
+                args.fUniformHandler->getUniformCStr(yInvZoomVar), args.fSampleCoord,
                 args.fUniformHandler->getUniformCStr(boundsUniformVar),
                 args.fUniformHandler->getUniformCStr(boundsUniformVar),
                 args.fUniformHandler->getUniformCStr(xInvInsetVar),
                 args.fUniformHandler->getUniformCStr(yInvInsetVar));
-        SkString _coords1077("mix(coord, zoom_coord, weight)");
-        SkString _sample1077;
-        _sample1077 = this->invokeChild(_outer.src_index, args, _coords1077.c_str());
+        SkString _coords1043 = SkStringPrintf("mix(%s, zoom_coord, weight)", args.fSampleCoord);
+        SkString _sample1043;
+        _sample1043 = this->invokeChild(_outer.src_index, args, _coords1043.c_str());
         fragBuilder->codeAppendf(
                 R"SkSL(
 %s = %s;
 )SkSL",
-                args.fOutputColor, _sample1077.c_str());
+                args.fOutputColor, _sample1043.c_str());
     }
 
 private:
diff --git a/src/gpu/gradients/GrLinearGradientLayout.fp b/src/gpu/gradients/GrLinearGradientLayout.fp
index e35bffc..758344e 100644
--- a/src/gpu/gradients/GrLinearGradientLayout.fp
+++ b/src/gpu/gradients/GrLinearGradientLayout.fp
@@ -5,7 +5,7 @@
  * found in the LICENSE file.
  */
 
-void main() {
+void main(float2 coord) {
     // We add a tiny delta to t. When gradient stops are set up so that a hard stop in a vertically
     // or horizontally oriented gradient falls exactly at a column or row of pixel centers we can
     // we can get slightly different interpolated t values along the column/row. By adding the delta
@@ -13,7 +13,7 @@
     // falls at X.5 - delta then we still could get inconsistent results, but that is much less
     // likely. crbug.com/938592
     // If/when we add filtering of the gradient this can be removed.
-    half t = half(sk_TransformedCoords2D[0].x) + 0.00001;
+    half t = half(coord.x) + 0.00001;
     sk_OutColor = half4(t, 1, 0, 0); // y = 1 for always valid
 }
 
diff --git a/src/gpu/gradients/GrRadialGradientLayout.fp b/src/gpu/gradients/GrRadialGradientLayout.fp
index 6e9894f..ab521c5 100644
--- a/src/gpu/gradients/GrRadialGradientLayout.fp
+++ b/src/gpu/gradients/GrRadialGradientLayout.fp
@@ -5,8 +5,8 @@
  * found in the LICENSE file.
  */
 
-void main() {
-    half t = half(length(sk_TransformedCoords2D[0]));
+void main(float2 coord) {
+    half t = half(length(coord));
     sk_OutColor = half4(t, 1, 0, 0); // y = 1 for always valid
 }
 
diff --git a/src/gpu/gradients/GrSweepGradientLayout.fp b/src/gpu/gradients/GrSweepGradientLayout.fp
index 4c03691..24b95dd 100644
--- a/src/gpu/gradients/GrSweepGradientLayout.fp
+++ b/src/gpu/gradients/GrSweepGradientLayout.fp
@@ -8,7 +8,7 @@
 layout(tracked) in uniform half bias;
 layout(tracked) in uniform half scale;
 
-void main() {
+void main(float2 coord) {
     // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
     // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in (sqrt(x^2
     // + y^2) + x) as the second parameter to atan2 in these cases. We let the device handle the
@@ -16,10 +16,9 @@
     // using atan instead.
     half angle;
     if (sk_Caps.atan2ImplementedAsAtanYOverX) {
-        angle = half(2 * atan(-sk_TransformedCoords2D[0].y,
-                              length(sk_TransformedCoords2D[0]) - sk_TransformedCoords2D[0].x));
+        angle = half(2 * atan(-coord.y, length(coord) - coord.x));
     } else {
-        angle = half(atan(-sk_TransformedCoords2D[0].y, -sk_TransformedCoords2D[0].x));
+        angle = half(atan(-coord.y, -coord.x));
     }
 
     // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
diff --git a/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp b/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp
index 6d92364..9517e4f 100644
--- a/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp
+++ b/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp
@@ -25,14 +25,7 @@
 // each FP
 layout(tracked) in uniform half2 focalParams;
 
-void main() {
-    // p typed as a float2 is intentional; while a half2 is adequate for most normal cases in the
-    // two point conic gradient's coordinate system, when the gradient is composed with a local
-    // perspective matrix, certain out-of-bounds regions become ill behaved on mobile devices.
-    // On desktops, they are properly clamped after the fact, but on many Adreno GPUs the
-    // calculations of t and x_t below overflow and produce an incorrect interpolant (which then
-    // renders the wrong border color sporadically). Increasing precition alleviates that issue.
-    float2 p = sk_TransformedCoords2D[0];
+void main(float2 p) {
     float t = -1;
     half v = 1; // validation flag, set to negative to discard fragment later
 
diff --git a/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp b/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
index 96e29e1..2dda47f 100644
--- a/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
@@ -41,16 +41,15 @@
         focalParamsVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag,
                                                           kHalf2_GrSLType, "focalParams");
         fragBuilder->codeAppendf(
-                R"SkSL(float2 p = %s;
-float t = -1.0;
+                R"SkSL(float t = -1.0;
 half v = 1.0;
 @switch (%d) {
     case 1:
         {
             half r0_2 = %s.y;
-            t = float(r0_2) - p.y * p.y;
+            t = float(r0_2) - %s.y * %s.y;
             if (t >= 0.0) {
-                t = p.x + sqrt(t);
+                t = %s.x + sqrt(t);
             } else {
                 v = -1.0;
             }
@@ -60,9 +59,9 @@
         {
             half r0 = %s.x;
             @if (%s) {
-                t = length(p) - float(r0);
+                t = length(%s) - float(r0);
             } else {
-                t = -length(p) - float(r0);
+                t = -length(%s) - float(r0);
             }
         }
         break;
@@ -72,16 +71,16 @@
             half fx = %s.y;
             float x_t = -1.0;
             @if (%s) {
-                x_t = dot(p, p) / p.x;
+                x_t = dot(%s, %s) / %s.x;
             } else if (%s) {
-                x_t = length(p) - p.x * float(invR1);
+                x_t = length(%s) - %s.x * float(invR1);
             } else {
-                float temp = p.x * p.x - p.y * p.y;
+                float temp = %s.x * %s.x - %s.y * %s.y;
                 if (temp >= 0.0) {
                     @if (%s || !%s) {
-                        x_t = -sqrt(temp) - p.x * float(invR1);
+                        x_t = -sqrt(temp) - %s.x * float(invR1);
                     } else {
-                        x_t = sqrt(temp) - p.x * float(invR1);
+                        x_t = sqrt(temp) - %s.x * float(invR1);
                     }
                 }
             }
@@ -111,16 +110,18 @@
 }
 %s = half4(half(t), v, 0.0, 0.0);
 )SkSL",
-                args.fSampleCoord, (int)_outer.type,
+                (int)_outer.type, args.fUniformHandler->getUniformCStr(focalParamsVar),
+                args.fSampleCoord, args.fSampleCoord, args.fSampleCoord,
                 args.fUniformHandler->getUniformCStr(focalParamsVar),
+                (_outer.isRadiusIncreasing ? "true" : "false"), args.fSampleCoord,
+                args.fSampleCoord, args.fUniformHandler->getUniformCStr(focalParamsVar),
                 args.fUniformHandler->getUniformCStr(focalParamsVar),
-                (_outer.isRadiusIncreasing ? "true" : "false"),
-                args.fUniformHandler->getUniformCStr(focalParamsVar),
-                args.fUniformHandler->getUniformCStr(focalParamsVar),
-                (_outer.isFocalOnCircle ? "true" : "false"),
-                (_outer.isWellBehaved ? "true" : "false"), (_outer.isSwapped ? "true" : "false"),
-                (_outer.isRadiusIncreasing ? "true" : "false"),
-                (_outer.isWellBehaved ? "true" : "false"),
+                (_outer.isFocalOnCircle ? "true" : "false"), args.fSampleCoord, args.fSampleCoord,
+                args.fSampleCoord, (_outer.isWellBehaved ? "true" : "false"), args.fSampleCoord,
+                args.fSampleCoord, args.fSampleCoord, args.fSampleCoord, args.fSampleCoord,
+                args.fSampleCoord, (_outer.isSwapped ? "true" : "false"),
+                (_outer.isRadiusIncreasing ? "true" : "false"), args.fSampleCoord,
+                args.fSampleCoord, (_outer.isWellBehaved ? "true" : "false"),
                 (_outer.isRadiusIncreasing ? "true" : "false"),
                 (_outer.isNativelyFocal ? "true" : "false"),
                 (_outer.isNativelyFocal ? "true" : "false"), (_outer.isSwapped ? "true" : "false"),
diff --git a/src/sksl/README b/src/sksl/README
index 525726b..466fdb5 100644
--- a/src/sksl/README
+++ b/src/sksl/README
@@ -82,7 +82,13 @@
 An extension of SkSL allows for the creation of fragment processors in pure
 SkSL. The program defines its inputs similarly to a normal SkSL program (with
 'in' and 'uniform' variables), but the 'main()' function represents only this
-fragment processor's portion of the overall fragment shader.
+fragment processor's portion of the overall fragment shader. The 'main' function
+can optionally have a 'float2' parameter that holds the sample coordinates the
+processor is evaluated at.
+
+Processors do not need to declare this parameter if they do not rely on local
+coordinates or only invoke child processors using the no-arg and matrix-based
+sample() functions.
 
 Within an '.fp' fragment processor file:
 
@@ -133,10 +139,6 @@
 * 'in uniform' variables support a 'tracked' flag in the layout that will
   have the generated code automatically implement state tracking on the uniform
   value to minimize GPU calls.
-* the 'sk_TransformedCoords2D' array provides access to 2D transformed
-  coordinates. sk_TransformedCoords2D[0] is equivalent to calling
-  fragBuilder->ensureCoords2D(args.fTransformedCoords[0]) (and the result is
-  cached, so you need not worry about using the value repeatedly).
 * Uniform variables support an additional 'when' layout key.
   'layout(when=foo) uniform int x;' means that this uniform will only be
   emitted when the 'foo' expression is true.
diff --git a/src/sksl/SkSLAnalysis.cpp b/src/sksl/SkSLAnalysis.cpp
index 558917c..e77aa31 100644
--- a/src/sksl/SkSLAnalysis.cpp
+++ b/src/sksl/SkSLAnalysis.cpp
@@ -151,9 +151,15 @@
 protected:
     // Only bother recursing through the main function for the sample coord builtin
     bool visitProgramElement(const ProgramElement& pe) override {
-        if (pe.fKind == ProgramElement::kFunction_Kind &&
-            ((const FunctionDefinition&) pe).fDeclaration.fName == "main") {
-            return this->INHERITED::visitProgramElement(pe);
+        if (pe.fKind == ProgramElement::kFunction_Kind) {
+            // Both kFragmentProcessor and kPipelineStage types use the first argument for
+            // the main coords builtin. If that isn't in the signature, there's no need to
+            // recurse deeper.
+            const FunctionDeclaration& func = ((const FunctionDefinition&) pe).fDeclaration;
+            if (func.fName == "main" && func.fParameters.size() >= 1 &&
+                func.fParameters.front()->fType == *this->program().fContext->fFloat2_Type) {
+                return this->INHERITED::visitProgramElement(pe);
+            }
         }
         // No recursion, but returning false will allow visitor to continue to siblings
         return false;
@@ -161,20 +167,8 @@
 
     bool visitExpression(const Expression& e) override {
         if (e.fKind == Expression::kVariableReference_Kind) {
-            // For SkRuntimeEffects
             const VariableReference& var = (const VariableReference&) e;
             return var.fVariable.fModifiers.fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN;
-        } else if (e.fKind == Expression::kIndex_Kind) {
-            // For .fp files that use sk_TransformedCoords2D[0] for the time being
-            const IndexExpression& index = (const IndexExpression&) e;
-            if (index.fBase->fKind == Expression::kVariableReference_Kind) {
-                const VariableReference& base = (const VariableReference&) *index.fBase;
-                if (base.fVariable.fModifiers.fLayout.fBuiltin == SK_TRANSFORMEDCOORDS2D_BUILTIN) {
-                    SkASSERT(index.fIndex->fKind == Expression::kIntLiteral_Kind &&
-                                ((IntLiteral&) *index.fIndex).fValue == 0);
-                    return true;
-                }
-            }
         }
         return this->INHERITED::visitExpression(e);
     }
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index e1ea72c..0a88298 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -115,22 +115,7 @@
     const Expression& base = *i.fBase;
     if (base.fKind == Expression::kVariableReference_Kind) {
         int builtin = ((VariableReference&) base).fVariable.fModifiers.fLayout.fBuiltin;
-        if (SK_TRANSFORMEDCOORDS2D_BUILTIN == builtin) {
-            this->write("%s");
-            if (i.fIndex->fKind != Expression::kIntLiteral_Kind) {
-                fErrors.error(i.fIndex->fOffset,
-                              "index into sk_TransformedCoords2D must be an integer literal");
-                return;
-            }
-            int64_t index = ((IntLiteral&) *i.fIndex).fValue;
-            if (index != 0) {
-                fErrors.error(i.fIndex->fOffset, "Only sk_TransformedCoords2D[0] is allowed");
-                return;
-            }
-            fAccessSampleCoordsDirectly = true;
-            fFormatArgs.push_back("args.fSampleCoord");
-            return;
-        } else if (SK_TEXTURESAMPLERS_BUILTIN == builtin) {
+        if (SK_TEXTURESAMPLERS_BUILTIN == builtin) {
             this->write("%s");
             if (i.fIndex->fKind != Expression::kIntLiteral_Kind) {
                 fErrors.error(i.fIndex->fOffset,
@@ -297,6 +282,11 @@
             this->write("%s");
             fFormatArgs.push_back(String("args.fOutputColor"));
             break;
+        case SK_MAIN_COORDS_BUILTIN:
+            this->write("%s");
+            fFormatArgs.push_back(String("args.fSampleCoord"));
+            fAccessSampleCoordsDirectly = true;
+            break;
         case SK_WIDTH_BUILTIN:
             this->write("sk_Width");
             break;
@@ -1110,12 +1100,6 @@
         this->writef("%s::%s(const %s& src)\n"
                      ": INHERITED(k%s_ClassID, src.optimizationFlags())", fFullName.c_str(),
                      fFullName.c_str(), fFullName.c_str(), fFullName.c_str());
-        const auto transforms = fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION);
-        for (size_t i = 0; i < transforms.size(); ++i) {
-            const Section& s = *transforms[i];
-            String fieldName = HCodeGenerator::CoordTransformName(s.fArgument, i);
-            this->writef("\n, %s(src.%s)", fieldName.c_str(), fieldName.c_str());
-        }
         for (const Variable* param : fSectionAndParameterHelper.getParameters()) {
             String fieldName = HCodeGenerator::FieldName(String(param->fName).c_str());
             if (param->fType.nonnullable() != *fContext.fFragmentProcessor_Type) {
@@ -1145,11 +1129,6 @@
         if (samplerCount) {
             this->writef("     this->setTextureSamplerCnt(%d);", samplerCount);
         }
-        for (size_t i = 0; i < transforms.size(); ++i) {
-            const Section& s = *transforms[i];
-            String fieldName = HCodeGenerator::CoordTransformName(s.fArgument, i);
-            this->writef("    this->addCoordTransform(&%s);\n", fieldName.c_str());
-        }
         if (fAccessSampleCoordsDirectly) {
             this->writef("    this->setUsesSampleCoordsDirectly();\n");
         }
diff --git a/src/sksl/SkSLCPPCodeGenerator.h b/src/sksl/SkSLCPPCodeGenerator.h
index ecf7f63..8543ab1 100644
--- a/src/sksl/SkSLCPPCodeGenerator.h
+++ b/src/sksl/SkSLCPPCodeGenerator.h
@@ -123,7 +123,8 @@
     std::vector<String> fExtraEmitCodeBlocks;
 
     std::vector<String> fFormatArgs;
-    // true if the sksl referenced sk_TransformedCoords[0]
+    // true if the sksl declared its main() function with a float2 parameter AND referenced that
+    // parameter in its body.
     bool fAccessSampleCoordsDirectly = false;
 
     // if true, we are writing a C++ expression instead of a GLSL expression
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index 474dbb8..2350751 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -28,7 +28,6 @@
 #define SK_IN_BUILTIN                  10002
 #define SK_INCOLOR_BUILTIN             10003
 #define SK_OUTCOLOR_BUILTIN            10004
-#define SK_TRANSFORMEDCOORDS2D_BUILTIN 10005
 #define SK_TEXTURESAMPLERS_BUILTIN     10006
 #define SK_OUT_BUILTIN                 10007
 #define SK_LASTFRAGCOLOR_BUILTIN       10008
diff --git a/src/sksl/SkSLHCodeGenerator.cpp b/src/sksl/SkSLHCodeGenerator.cpp
index 40bae45..f7c324b 100644
--- a/src/sksl/SkSLHCodeGenerator.cpp
+++ b/src/sksl/SkSLHCodeGenerator.cpp
@@ -239,19 +239,6 @@
     }
     this->writef(")");
     this->writeSection(INITIALIZERS_SECTION, "\n    , ");
-    const auto transforms = fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION);
-    for (size_t i = 0; i < transforms.size(); ++i) {
-        const Section& s = *transforms[i];
-        String field = CoordTransformName(s.fArgument.c_str(), i);
-        if (s.fArgument.size()) {
-            this->writef("\n    , %s(%s, %s.proxy(), %s.origin())", field.c_str(), s.fText.c_str(),
-                         FieldName(s.fArgument.c_str()).c_str(),
-                         FieldName(s.fArgument.c_str()).c_str());
-        }
-        else {
-            this->writef("\n    , %s(%s)", field.c_str(), s.fText.c_str());
-        }
-    }
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         String nameString(param->fName);
         const char* name = nameString.c_str();
@@ -345,22 +332,11 @@
     if (samplerCount) {
         this->writef("        this->setTextureSamplerCnt(%d);", samplerCount);
     }
-    for (size_t i = 0; i < transforms.size(); ++i) {
-        const Section& s = *transforms[i];
-        String field = CoordTransformName(s.fArgument.c_str(), i);
-        this->writef("        this->addCoordTransform(&%s);\n", field.c_str());
-    }
     this->writef("    }\n");
 }
 
 void HCodeGenerator::writeFields() {
     this->writeSection(FIELDS_SECTION);
-    const auto transforms = fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION);
-    for (size_t i = 0; i < transforms.size(); ++i) {
-        const Section& s = *transforms[i];
-        this->writef("    GrCoordTransform %s;\n",
-                     CoordTransformName(s.fArgument.c_str(), i).c_str());
-    }
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         String name = FieldName(String(param->fName).c_str());
         if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 40b91284..c73e5ca 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -862,6 +862,19 @@
                 }
                 break;
             }
+            case Program::kFragmentProcessor_Kind: {
+                bool valid = parameters.size() <= 1;
+                if (parameters.size() == 1) {
+                    valid = parameters[0]->fType == *fContext.fFloat2_Type &&
+                            parameters[0]->fModifiers.fFlags == 0;
+                }
+
+                if (!valid) {
+                    fErrors.error(f.fOffset, ".fp 'main' must be declared main() or main(float2)");
+                    return;
+                }
+                break;
+            }
             case Program::kGeneric_Kind:
                 break;
             default:
@@ -949,6 +962,10 @@
                 SkASSERT(parameters.size() == 1);
                 parameters[0]->fModifiers.fLayout.fBuiltin = SK_OUTCOLOR_BUILTIN;
             }
+        } else if (fd.fName == "main" && fKind == Program::kFragmentProcessor_Kind) {
+            if (parameters.size() == 1) {
+                parameters[0]->fModifiers.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
+            }
         }
         for (size_t i = 0; i < parameters.size(); i++) {
             fSymbolTable->addWithoutOwnership(parameters[i]->fName, decl->fParameters[i]);
diff --git a/src/sksl/SkSLSectionAndParameterHelper.h b/src/sksl/SkSLSectionAndParameterHelper.h
index b30f4bd..4baaa2c 100644
--- a/src/sksl/SkSLSectionAndParameterHelper.h
+++ b/src/sksl/SkSLSectionAndParameterHelper.h
@@ -22,7 +22,6 @@
 #define CONSTRUCTOR_SECTION        "constructor"
 #define CONSTRUCTOR_CODE_SECTION   "constructorCode"
 #define CONSTRUCTOR_PARAMS_SECTION "constructorParams"
-#define COORD_TRANSFORM_SECTION    "coordTransform"
 #define CPP_SECTION                "cpp"
 #define CPP_END_SECTION            "cppEnd"
 #define HEADER_SECTION             "header"
@@ -73,7 +72,6 @@
                !strcmp(name, CONSTRUCTOR_SECTION) ||
                !strcmp(name, CONSTRUCTOR_CODE_SECTION) ||
                !strcmp(name, CONSTRUCTOR_PARAMS_SECTION) ||
-               !strcmp(name, COORD_TRANSFORM_SECTION) ||
                !strcmp(name, CPP_SECTION) ||
                !strcmp(name, CPP_END_SECTION) ||
                !strcmp(name, EMIT_CODE_SECTION) ||
@@ -89,8 +87,7 @@
     }
 
     static bool SectionAcceptsArgument(const char* name) {
-        return !strcmp(name, COORD_TRANSFORM_SECTION) ||
-               !strcmp(name, SAMPLER_PARAMS_SECTION) ||
+        return !strcmp(name, SAMPLER_PARAMS_SECTION) ||
                !strcmp(name, SET_DATA_SECTION) ||
                !strcmp(name, TEST_CODE_SECTION);
     }
@@ -102,8 +99,7 @@
     }
 
     static bool SectionPermitsDuplicates(const char* name) {
-        return !strcmp(name, COORD_TRANSFORM_SECTION) ||
-               !strcmp(name, SAMPLER_PARAMS_SECTION);
+        return !strcmp(name, SAMPLER_PARAMS_SECTION);
     }
 
 private:
diff --git a/src/sksl/sksl_fp.inc b/src/sksl/sksl_fp.inc
index 81362e6..1e6449e 100644
--- a/src/sksl/sksl_fp.inc
+++ b/src/sksl/sksl_fp.inc
@@ -17,7 +17,6 @@
 
 layout(builtin=10003) half4 sk_InColor;
 layout(builtin=10004) out half4 sk_OutColor;
-layout(builtin=10005) float2[] sk_TransformedCoords2D;
 layout(builtin=10006) sampler2D[] sk_TextureSamplers;
 layout(builtin=10011) half sk_Width;
 layout(builtin=10012) half sk_Height;
diff --git a/tests/SkSLFPTest.cpp b/tests/SkSLFPTest.cpp
index 692298d..a14ac87 100644
--- a/tests/SkSLFPTest.cpp
+++ b/tests/SkSLFPTest.cpp
@@ -498,12 +498,12 @@
          });
 }
 
-DEF_TEST(SkSLFPTransformedCoords, r) {
+DEF_TEST(SkSLFPMainCoords, r) {
     test(r,
          *SkSL::ShaderCapsFactory::Default(),
          R"__SkSL__(
-             void main() {
-                 sk_OutColor = half4(sk_TransformedCoords2D[0], sk_TransformedCoords2D[0]);
+             void main(float2 coord) {
+                 sk_OutColor = half4(coord, coord);
              }
          )__SkSL__",
          /*expectedH=*/{
@@ -807,9 +807,8 @@
          *SkSL::ShaderCapsFactory::Default(),
          R"__SkSL__(
              in fragmentProcessor child;
-             @coordTransform { SkMatrix() }
-             void main() {
-                 sk_OutColor = sample(child) + sample(child, sk_TransformedCoords2D[0] / 2);
+             void main(float2 coord) {
+                 sk_OutColor = sample(child) + sample(child, coord / 2);
              }
          )__SkSL__",
          /*expectedH=*/{
@@ -817,15 +816,15 @@
              "this->setUsesSampleCoordsDirectly();"
          },
          /*expectedCPP=*/{
-            "SkString _sample150;\n",
-            "_sample150 = this->invokeChild(_outer.child_index, args);\n",
-            "SkString _sample166;\n",
-            "SkString _coords166 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);\n",
-            "_sample166 = this->invokeChild(_outer.child_index, args, _coords166.c_str());\n",
+            "SkString _sample118;\n",
+            "_sample118 = this->invokeChild(_outer.child_index, args);\n",
+            "SkString _sample118;\n",
+            "SkString _coords134 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);\n",
+            "_sample134 = this->invokeChild(_outer.child_index, args, _coords134.c_str());\n",
             "fragBuilder->codeAppendf(\n"
             "R\"SkSL(%s = %s + %s;\n"
             ")SkSL\"\n"
-            ", args.fOutputColor, _sample150.c_str(), _sample166.c_str());"
+            ", args.fOutputColor, _sample118.c_str(), _sample134.c_str());"
         });
 }
 
@@ -972,9 +971,9 @@
          *SkSL::ShaderCapsFactory::Default(),
          R"__SkSL__(
              in fragmentProcessor? child;
-             void main() {
+             void main(float2 coord) {
                  sk_OutColor = sample(child, float3x3(0.5));
-                 sk_OutColor = sample(child, sk_TransformedCoords2D[0].xy / 2);
+                 sk_OutColor = sample(child, coord / 2);
              }
          )__SkSL__",
          /*expectedH=*/{
@@ -983,8 +982,8 @@
          },
          /*expectedCPP=*/{
              "this->invokeChildWithMatrix(_outer.child_index, args)",
-             "SkString _coords168 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);",
-             "this->invokeChild(_outer.child_index, args, _coords168.c_str())",
+             "SkString _coords180 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);",
+             "this->invokeChild(_outer.child_index, args, _coords180.c_str())",
          });
 }
 
@@ -993,10 +992,10 @@
          *SkSL::ShaderCapsFactory::Default(),
          R"__SkSL__(
              in fragmentProcessor? child;
-             void main() {
+             void main(float2 coord) {
                  float3x3 matrix = float3x3(sk_InColor.a);
                  sk_OutColor = sample(child, matrix);
-                 sk_OutColor = sample(child, sk_TransformedCoords2D[0].xy / 2);
+                 sk_OutColor = sample(child, coord / 2);
              }
          )__SkSL__",
          /*expectedH=*/{
@@ -1004,9 +1003,9 @@
                     "SkSL::SampleMatrix::MakeVariable(true), true);"
          },
          /*expectedCPP=*/{
-             "SkString _matrix166(\"matrix\");",
-             "this->invokeChildWithMatrix(_outer.child_index, args, _matrix166.c_str())",
-             "SkString _coords220 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);",
-             "this->invokeChild(_outer.child_index, args, _coords220.c_str()",
+             "SkString _matrix178(\"matrix\");",
+             "this->invokeChildWithMatrix(_outer.child_index, args, _matrix178.c_str())",
+             "SkString _coords232 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);",
+             "this->invokeChild(_outer.child_index, args, _coords232.c_str()",
          });
 }