Child fragment processors are now written as separate functions

Bug: skia:
Change-Id: Icbf8f542637a874b3e2d3513d932b39728fa5e77
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/229385
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/core/SkNormalMapSource.cpp b/src/core/SkNormalMapSource.cpp
index b359733..b84e726 100644
--- a/src/core/SkNormalMapSource.cpp
+++ b/src/core/SkNormalMapSource.cpp
@@ -50,7 +50,7 @@
                                                    "Xform", &xformUniName);
 
             SkString dstNormalColorName("dstNormalColor");
-            this->emitChild(0, &dstNormalColorName, args);
+            this->invokeChild(0, &dstNormalColorName, args);
             fragBuilder->codeAppendf("float3 normal = normalize(%s.rgb - float3(0.5));",
                                      dstNormalColorName.c_str());
 
diff --git a/src/gpu/GrColorSpaceXform.cpp b/src/gpu/GrColorSpaceXform.cpp
index 39118a6..e8d739c 100644
--- a/src/gpu/GrColorSpaceXform.cpp
+++ b/src/gpu/GrColorSpaceXform.cpp
@@ -66,7 +66,7 @@
 
         if (this->numChildProcessors()) {
             SkString childColor("src_color");
-            this->emitChild(0, &childColor, args);
+            this->invokeChild(0, &childColor, args);
 
             SkString xformedColor;
             fragBuilder->appendColorGamutXform(&xformedColor, childColor.c_str(), &fColorSpaceHelper);
diff --git a/src/gpu/GrFragmentProcessor.cpp b/src/gpu/GrFragmentProcessor.cpp
index d2d9902..41975b3 100644
--- a/src/gpu/GrFragmentProcessor.cpp
+++ b/src/gpu/GrFragmentProcessor.cpp
@@ -233,7 +233,7 @@
             public:
                 void emitCode(EmitArgs& args) override {
                     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-                    this->emitChild(0, args);
+                    this->invokeChild(0, args);
                     fragBuilder->codeAppendf("%s.rgb *= %s.rgb;", args.fOutputColor,
                                                                 args.fInputColor);
                     fragBuilder->codeAppendf("%s *= %s.a;", args.fOutputColor, args.fInputColor);
@@ -310,15 +310,15 @@
                 void emitCode(EmitArgs& args) override {
                     // First guy's input might be nil.
                     SkString temp("out0");
-                    this->emitChild(0, args.fInputColor, &temp, args);
+                    this->invokeChild(0, args.fInputColor, &temp, args);
                     SkString input = temp;
                     for (int i = 1; i < this->numChildProcessors() - 1; ++i) {
                         temp.printf("out%d", i);
-                        this->emitChild(i, input.c_str(), &temp, args);
+                        this->invokeChild(i, input.c_str(), &temp, args);
                         input = temp;
                     }
                     // Last guy writes to our output variable.
-                    this->emitChild(this->numChildProcessors() - 1, input.c_str(), args);
+                    this->invokeChild(this->numChildProcessors() - 1, input.c_str(), args);
                 }
             };
             return new GLFP;
diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp
index cd8803e..5a9367a 100644
--- a/src/gpu/effects/GrSkSLFP.cpp
+++ b/src/gpu/effects/GrSkSLFP.cpp
@@ -162,7 +162,7 @@
         std::vector<SkString> childNames;
         for (int i = 0; i < this->numChildProcessors(); ++i) {
             childNames.push_back(SkStringPrintf("_child%d", i));
-            this->emitChild(i, &childNames[i], args);
+            this->invokeChild(i, &childNames[i], args);
         }
         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
         int substringStartIndex = 0;
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
index 1424939..d21b498 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
@@ -221,10 +221,10 @@
 
     // declare outputColor and emit the code for each of the two children
     SkString srcColor("xfer_src");
-    this->emitChild(0, inputColor, &srcColor, args);
+    this->invokeChild(0, inputColor, &srcColor, args);
 
     SkString dstColor("xfer_dst");
-    this->emitChild(1, inputColor, &dstColor, args);
+    this->invokeChild(1, inputColor, &dstColor, args);
 
     // emit blend code
     SkBlendMode mode = cs.getMode();
@@ -439,7 +439,7 @@
         ComposeOneFragmentProcessor::Child child =
             args.fFp.cast<ComposeOneFragmentProcessor>().child();
         SkString childColor("child");
-        this->emitChild(0, &childColor, args);
+        this->invokeChild(0, &childColor, args);
 
         // emit blend code
         fragBuilder->codeAppendf("// Compose Xfer Mode: %s\n", SkBlendMode_Name(mode));
diff --git a/src/gpu/effects/generated/GrComposeLerpEffect.cpp b/src/gpu/effects/generated/GrComposeLerpEffect.cpp
index c9b4d3b..1f0d9f4 100644
--- a/src/gpu/effects/generated/GrComposeLerpEffect.cpp
+++ b/src/gpu/effects/generated/GrComposeLerpEffect.cpp
@@ -29,13 +29,13 @@
                 args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, "weight");
         SkString _child0("_child0");
         if (_outer.child1_index >= 0) {
-            this->emitChild(_outer.child1_index, &_child0, args);
+            this->invokeChild(_outer.child1_index, &_child0, args);
         } else {
             fragBuilder->codeAppendf("half4 %s;", _child0.c_str());
         }
         SkString _child1("_child1");
         if (_outer.child2_index >= 0) {
-            this->emitChild(_outer.child2_index, &_child1, args);
+            this->invokeChild(_outer.child2_index, &_child1, args);
         } else {
             fragBuilder->codeAppendf("half4 %s;", _child1.c_str());
         }
diff --git a/src/gpu/effects/generated/GrComposeLerpRedEffect.cpp b/src/gpu/effects/generated/GrComposeLerpRedEffect.cpp
index d75b411..d82bf0a 100644
--- a/src/gpu/effects/generated/GrComposeLerpRedEffect.cpp
+++ b/src/gpu/effects/generated/GrComposeLerpRedEffect.cpp
@@ -25,18 +25,18 @@
         (void)_outer;
         SkString _child0("_child0");
         if (_outer.child1_index >= 0) {
-            this->emitChild(_outer.child1_index, &_child0, args);
+            this->invokeChild(_outer.child1_index, &_child0, args);
         } else {
             fragBuilder->codeAppendf("half4 %s;", _child0.c_str());
         }
         SkString _child1("_child1");
         if (_outer.child2_index >= 0) {
-            this->emitChild(_outer.child2_index, &_child1, args);
+            this->invokeChild(_outer.child2_index, &_child1, args);
         } else {
             fragBuilder->codeAppendf("half4 %s;", _child1.c_str());
         }
         SkString _child2("_child2");
-        this->emitChild(_outer.lerp_index, &_child2, args);
+        this->invokeChild(_outer.lerp_index, &_child2, args);
         fragBuilder->codeAppendf("%s = mix(%s ? %s : %s, %s ? %s : %s, %s.x);\n", args.fOutputColor,
                                  _outer.child1_index >= 0 ? "true" : "false", _child0.c_str(),
                                  args.fInputColor, _outer.child2_index >= 0 ? "true" : "false",
diff --git a/src/gpu/effects/generated/GrMixerEffect.cpp b/src/gpu/effects/generated/GrMixerEffect.cpp
index af5a7af..f0da5b3 100644
--- a/src/gpu/effects/generated/GrMixerEffect.cpp
+++ b/src/gpu/effects/generated/GrMixerEffect.cpp
@@ -29,12 +29,12 @@
                 args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, "weight");
         SkString _input0 = SkStringPrintf("%s", args.fInputColor);
         SkString _child0("_child0");
-        this->emitChild(_outer.fp0_index, _input0.c_str(), &_child0, args);
+        this->invokeChild(_outer.fp0_index, _input0.c_str(), &_child0, args);
         fragBuilder->codeAppendf("half4 in0 = %s;", _child0.c_str());
         SkString _input1 = SkStringPrintf("%s", args.fInputColor);
         SkString _child1("_child1");
         if (_outer.fp1_index >= 0) {
-            this->emitChild(_outer.fp1_index, _input1.c_str(), &_child1, args);
+            this->invokeChild(_outer.fp1_index, _input1.c_str(), &_child1, args);
         } else {
             fragBuilder->codeAppendf("half4 %s;", _child1.c_str());
         }
diff --git a/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp b/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp
index 9d7934e..689e079 100644
--- a/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp
@@ -44,7 +44,7 @@
                 _outer.literalColor.fA);
         SkString _input0("constColor");
         SkString _child0("_child0");
-        this->emitChild(_outer.fp_index, _input0.c_str(), &_child0, args);
+        this->invokeChild(_outer.fp_index, _input0.c_str(), &_child0, args);
         fragBuilder->codeAppendf("\n%s = %s;\n", args.fOutputColor, _child0.c_str());
     }
 
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
index 4101860..dec2c96 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
@@ -16,21 +16,33 @@
     this->onSetData(pdman, processor);
 }
 
-void GrGLSLFragmentProcessor::emitChild(int childIndex, const char* inputColor, EmitArgs& args) {
-    this->internalEmitChild(childIndex, inputColor, args.fOutputColor, args);
+void GrGLSLFragmentProcessor::invokeChild(int childIndex, const char* inputColor, EmitArgs& args) {
+    while (childIndex >= (int) fFunctionNames.size()) {
+        fFunctionNames.emplace_back();
+    }
+    this->internalInvokeChild(childIndex, inputColor, args.fOutputColor, args);
 }
 
-void GrGLSLFragmentProcessor::emitChild(int childIndex, const char* inputColor,
-                                        SkString* outputColor, EmitArgs& args) {
+void GrGLSLFragmentProcessor::invokeChild(int childIndex, const char* inputColor,
+                                          SkString* outputColor, EmitArgs& args) {
     SkASSERT(outputColor);
     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     outputColor->append(fragBuilder->getMangleString());
     fragBuilder->codeAppendf("half4 %s;", outputColor->c_str());
-    this->internalEmitChild(childIndex, inputColor, outputColor->c_str(), args);
+    while (childIndex >= (int) fFunctionNames.size()) {
+        fFunctionNames.emplace_back();
+    }
+    if (fFunctionNames[childIndex].size() == 0) {
+        this->internalInvokeChild(childIndex, inputColor, outputColor->c_str(), args);
+    } else {
+        fragBuilder->codeAppendf("%s = %s(%s);", outputColor->c_str(),
+                                 fFunctionNames[childIndex].c_str(),
+                                 inputColor ? inputColor : "half4(1)");
+    }
 }
 
-void GrGLSLFragmentProcessor::internalEmitChild(int childIndex, const char* inputColor,
-                                                const char* outputColor, EmitArgs& args) {
+void GrGLSLFragmentProcessor::internalInvokeChild(int childIndex, const char* inputColor,
+                                                  const char* outputColor, EmitArgs& args) {
     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
     fragBuilder->onBeforeChildProcEmitCode();  // call first so mangleString is updated
@@ -48,24 +60,24 @@
 
     const GrFragmentProcessor& childProc = args.fFp.childProcessor(childIndex);
 
-    // emit the code for the child in its own scope
-    fragBuilder->codeAppend("{\n");
-    fragBuilder->codeAppendf("// Child Index %d (mangle: %s): %s\n", childIndex,
-                             fragBuilder->getMangleString().c_str(), childProc.name());
     TransformedCoordVars coordVars = args.fTransformedCoords.childInputs(childIndex);
     TextureSamplers textureSamplers = args.fTexSamplers.childInputs(childIndex);
 
-    // EmitArgs properly updates inputColor to half4(1) if it was null
     EmitArgs childArgs(fragBuilder,
                        args.fUniformHandler,
                        args.fShaderCaps,
                        childProc,
                        outputColor,
-                       inputName.size() > 0 ? inputName.c_str() : nullptr,
+                       "_input",
                        coordVars,
                        textureSamplers);
-    this->childProcessor(childIndex)->emitCode(childArgs);
-    fragBuilder->codeAppend("}\n");
+    fFunctionNames[childIndex] = fragBuilder->writeProcessorFunction(
+                                                               this->childProcessor(childIndex),
+                                                               childArgs);
+    fragBuilder->codeAppendf("%s = %s(%s);\n", outputColor,
+                                               fFunctionNames[childIndex].c_str(),
+                                               inputName.size() > 0 ? inputName.c_str()
+                                                                    : "half4(1)");
 
     fragBuilder->onAfterChildProcEmitCode();
 }
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.h b/src/gpu/glsl/GrGLSLFragmentProcessor.h
index 4a8ce70..c9971a2 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.h
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.h
@@ -136,31 +136,31 @@
         return fChildProcessors[index];
     }
 
-    // Emit the child with the default input color (solid white)
-    inline void emitChild(int childIndex, SkString* outputColor, EmitArgs& parentArgs) {
-        this->emitChild(childIndex, nullptr, outputColor, parentArgs);
+    // Invoke the child with the default input color (solid white)
+    inline void invokeChild(int childIndex, SkString* outputColor, EmitArgs& parentArgs) {
+        this->invokeChild(childIndex, nullptr, outputColor, parentArgs);
     }
 
-    /** Will emit the code of a child proc in its own scope. Pass in the parent's EmitArgs and
-     *  emitChild will automatically extract the coords and samplers of that child and pass them
-     *  on to the child's emitCode(). Also, any uniforms or functions emitted by the child will
-     *  have their names mangled to prevent redefinitions. The output color name is also mangled
-     *  therefore in an in/out param. It will be declared in mangled form by emitChild(). It is
-     *  legal to pass nullptr as inputColor, since all fragment processors are required to work
-     *  without an input color.
+    /** Invokes a child proc in its own scope. Pass in the parent's EmitArgs and invokeChild will
+     *  automatically extract the coords and samplers of that child and pass them on to the child's
+     *  emitCode(). Also, any uniforms or functions emitted by the child will have their names
+     *  mangled to prevent redefinitions. The output color name is also mangled therefore in an
+     *  in/out param. It will be declared in mangled form by invokeChild(). It is legal to pass
+     *  nullptr as inputColor, since all fragment processors are required to work without an input
+     *  color.
      */
-    void emitChild(int childIndex, const char* inputColor, SkString* outputColor,
-                   EmitArgs& parentArgs);
+    void invokeChild(int childIndex, const char* inputColor, SkString* outputColor,
+                     EmitArgs& parentArgs);
 
     // Use the parent's output color to hold child's output, and use the
     // default input color of solid white
-    inline void emitChild(int childIndex, EmitArgs& args) {
+    inline void invokeChild(int childIndex, EmitArgs& args) {
         // null pointer cast required to disambiguate the function call
-        this->emitChild(childIndex, (const char*) nullptr, args);
+        this->invokeChild(childIndex, (const char*) nullptr, args);
     }
 
     /** Variation that uses the parent's output color variable to hold the child's output.*/
-    void emitChild(int childIndex, const char* inputColor, EmitArgs& parentArgs);
+    void invokeChild(int childIndex, const char* inputColor, EmitArgs& parentArgs);
 
     /**
      * Pre-order traversal of a GLSLFP hierarchy, or of multiple trees with roots in an array of
@@ -189,7 +189,10 @@
     virtual void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) {}
 
 private:
-    void internalEmitChild(int, const char*, const char*, EmitArgs&);
+    void internalInvokeChild(int, const char*, const char*, EmitArgs&);
+
+    // one per child; either not present or empty string if not yet emitted
+    SkTArray<SkString> fFunctionNames;
 
     SkTArray<GrGLSLFragmentProcessor*, true> fChildProcessors;
 
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
index 2b6894a..986ad37 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
@@ -156,6 +156,26 @@
     this->codeAppendf("}");
 }
 
+SkString GrGLSLFPFragmentBuilder::writeProcessorFunction(GrGLSLFragmentProcessor* fp,
+                                                         GrGLSLFragmentProcessor::EmitArgs& args) {
+    this->onBeforeChildProcEmitCode();
+    this->nextStage();
+    this->codeAppendf("half4 %s;\n", args.fOutputColor);
+    fp->emitCode(args);
+    this->codeAppendf("return %s;", args.fOutputColor);
+    GrShaderVar inColor(args.fInputColor, kHalf4_GrSLType);
+    SkString result;
+    this->emitFunction(kHalf4_GrSLType,
+                       "stage",
+                       1,
+                       &inColor,
+                       this->code().c_str(),
+                       &result);
+    this->deleteStage();
+    this->onAfterChildProcEmitCode();
+    return result;
+}
+
 const char* GrGLSLFragmentShaderBuilder::dstColor() {
     SkDEBUGCODE(fHasReadDstColorThisStage_DebugOnly = true;)
 
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
index 050029c..e454fc8 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
@@ -10,6 +10,7 @@
 
 #include "include/gpu/GrBlend.h"
 #include "src/gpu/GrProcessor.h"
+#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
 #include "src/gpu/glsl/GrGLSLShaderBuilder.h"
 
 class GrRenderTarget;
@@ -97,6 +98,9 @@
     virtual void onBeforeChildProcEmitCode() = 0;
     virtual void onAfterChildProcEmitCode() = 0;
 
+    virtual SkString writeProcessorFunction(GrGLSLFragmentProcessor* fp,
+                                            GrGLSLFragmentProcessor::EmitArgs& args);
+
     virtual const SkString& getMangleString() const = 0;
 
     virtual void forceHighPrecision() = 0;
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.h b/src/gpu/glsl/GrGLSLShaderBuilder.h
index 0086e85..959d24a 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.h
@@ -198,6 +198,11 @@
         fCodeIndex++;
     }
 
+    void deleteStage() {
+        fShaderStrings.pop_back();
+        fCodeIndex--;
+    }
+
     SkString& extensions() { return fShaderStrings[kExtensions]; }
     SkString& definitions() { return fShaderStrings[kDefinitions]; }
     SkString& precisionQualifier() { return fShaderStrings[kPrecisionQualifier]; }
diff --git a/src/gpu/gradients/generated/GrClampedGradientEffect.cpp b/src/gpu/gradients/generated/GrClampedGradientEffect.cpp
index 7bb2452..fb9369e 100644
--- a/src/gpu/gradients/generated/GrClampedGradientEffect.cpp
+++ b/src/gpu/gradients/generated/GrClampedGradientEffect.cpp
@@ -36,7 +36,7 @@
         rightBorderColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
                                                                kHalf4_GrSLType, "rightBorderColor");
         SkString _child1("_child1");
-        this->emitChild(_outer.gradLayout_index, &_child1, args);
+        this->invokeChild(_outer.gradLayout_index, &_child1, args);
         fragBuilder->codeAppendf(
                 "half4 t = %s;\nif (!%s && t.y < 0.0) {\n    %s = half4(0.0);\n} else if (t.x < "
                 "0.0) {\n    %s = %s;\n} else if (t.x > 1.0) {\n    %s = %s;\n} else {",
@@ -48,7 +48,7 @@
                 args.fUniformHandler->getUniformCStr(rightBorderColorVar));
         SkString _input0("t");
         SkString _child0("_child0");
-        this->emitChild(_outer.colorizer_index, _input0.c_str(), &_child0, args);
+        this->invokeChild(_outer.colorizer_index, _input0.c_str(), &_child0, args);
         fragBuilder->codeAppendf("\n    %s = %s;\n}\n@if (%s) {\n    %s.xyz *= %s.w;\n}\n",
                                  args.fOutputColor, _child0.c_str(),
                                  (_outer.makePremul ? "true" : "false"), args.fOutputColor,
diff --git a/src/gpu/gradients/generated/GrTiledGradientEffect.cpp b/src/gpu/gradients/generated/GrTiledGradientEffect.cpp
index cf5d583..76a0975 100644
--- a/src/gpu/gradients/generated/GrTiledGradientEffect.cpp
+++ b/src/gpu/gradients/generated/GrTiledGradientEffect.cpp
@@ -30,7 +30,7 @@
         auto colorsAreOpaque = _outer.colorsAreOpaque;
         (void)colorsAreOpaque;
         SkString _child1("_child1");
-        this->emitChild(_outer.gradLayout_index, &_child1, args);
+        this->invokeChild(_outer.gradLayout_index, &_child1, args);
         fragBuilder->codeAppendf(
                 "half4 t = %s;\nif (!%s && t.y < 0.0) {\n    %s = half4(0.0);\n} else {\n    @if "
                 "(%s) {\n        half t_1 = t.x - 1.0;\n        half tiled_t = (t_1 - 2.0 * "
@@ -43,7 +43,7 @@
                 args.fOutputColor, (_outer.mirror ? "true" : "false"));
         SkString _input0("t");
         SkString _child0("_child0");
-        this->emitChild(_outer.colorizer_index, _input0.c_str(), &_child0, args);
+        this->invokeChild(_outer.colorizer_index, _input0.c_str(), &_child0, args);
         fragBuilder->codeAppendf("\n    %s = %s;\n}\n@if (%s) {\n    %s.xyz *= %s.w;\n}\n",
                                  args.fOutputColor, _child0.c_str(),
                                  (_outer.makePremul ? "true" : "false"), args.fOutputColor,
diff --git a/src/shaders/SkLightingShader.cpp b/src/shaders/SkLightingShader.cpp
index fc8dff8..4614431 100644
--- a/src/shaders/SkLightingShader.cpp
+++ b/src/shaders/SkLightingShader.cpp
@@ -166,7 +166,7 @@
             fragBuilder->codeAppendf("half4 diffuseColor = %s;", args.fInputColor);
 
             SkString dstNormalName("dstNormal");
-            this->emitChild(0, &dstNormalName, args);
+            this->invokeChild(0, &dstNormalName, args);
 
             fragBuilder->codeAppendf("float3 normal = %s.xyz;", dstNormalName.c_str());
 
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index c7846ad..f564f3c 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -431,16 +431,16 @@
 
         // Set to the empty string when no input color parameter should be emitted, which means this
         // must be properly formatted with a prefixed comma when the parameter should be inserted
-        // into the emitChild() parameter list.
+        // into the invokeChild() parameter list.
         String inputArg;
         if (c.fArguments.size() > 1) {
             SkASSERT(c.fArguments.size() == 2);
-            // Use the emitChild() variant that accepts an input color, so convert the 2nd
+            // Use the invokeChild() variant that accepts an input color, so convert the 2nd
             // argument's expression into C++ code that produces sksl stored in an SkString.
             String inputName = "_input" + to_string(index);
             addExtraEmitCodeLine(convertSKSLExpressionToCPP(*c.fArguments[1], inputName));
 
-            // emitChild() needs a char*
+            // invokeChild() needs a char*
             inputArg = ", " + inputName + ".c_str()";
         }
 
@@ -450,7 +450,7 @@
         if (c.fArguments[0]->fType.kind() == Type::kNullable_Kind) {
             addExtraEmitCodeLine("if (_outer." + String(child.fName) + "_index >= 0) {\n    ");
         }
-        addExtraEmitCodeLine("this->emitChild(_outer." + String(child.fName) + "_index" +
+        addExtraEmitCodeLine("this->invokeChild(_outer." + String(child.fName) + "_index" +
                              inputArg + ", &" + childName + ", args);");
         if (c.fArguments[0]->fType.kind() == Type::kNullable_Kind) {
             // Null FPs are not emitted, but their output can still be referenced in dependent
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index 2f2989b..1079d96 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -1035,6 +1035,8 @@
 }
 
 void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
+    fSetupFragPositionLocal = false;
+    fSetupFragCoordWorkaround = false;
     if (fProgramKind != Program::kPipelineStage_Kind) {
         this->writeTypePrecision(f.fDeclaration.fReturnType);
         this->writeType(f.fDeclaration.fReturnType);
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index df5455c..4b625a3 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -244,6 +244,11 @@
         this->write("_globals");
         separator = ", ";
     }
+    if (this->requirements(c.fFunction) & kFragCoord_Requirement) {
+        this->write(separator);
+        this->write("_fragCoord");
+        separator = ", ";
+    }
     for (size_t i = 0; i < c.fArguments.size(); ++i) {
         const Expression& arg = *c.fArguments[i];
         this->write(separator);
@@ -499,8 +504,8 @@
 
 void MetalCodeGenerator::writeFragCoord() {
     if (fProgram.fInputs.fRTHeight) {
-        this->write("float4(_fragCoord.x, _anonInterface0.u_skRTHeight - _fragCoord.y, 0.0, "
-                    "_fragCoord.w)");
+        this->write("float4(_fragCoord.x, _globals->_anonInterface0->u_skRTHeight - "
+                    "_fragCoord.y, 0.0, _fragCoord.w)");
     } else {
         this->write("float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w)");
     }
@@ -853,25 +858,31 @@
         this->write(" ");
         this->writeName(f.fDeclaration.fName);
         this->write("(");
-        if (this->requirements(f.fDeclaration) & kInputs_Requirement) {
+        Requirements requirements = this->requirements(f.fDeclaration);
+        if (requirements & kInputs_Requirement) {
             this->write("Inputs _in");
             separator = ", ";
         }
-        if (this->requirements(f.fDeclaration) & kOutputs_Requirement) {
+        if (requirements & kOutputs_Requirement) {
             this->write(separator);
             this->write("thread Outputs* _out");
             separator = ", ";
         }
-        if (this->requirements(f.fDeclaration) & kUniforms_Requirement) {
+        if (requirements & kUniforms_Requirement) {
             this->write(separator);
             this->write("Uniforms _uniforms");
             separator = ", ";
         }
-        if (this->requirements(f.fDeclaration) & kGlobals_Requirement) {
+        if (requirements & kGlobals_Requirement) {
             this->write(separator);
             this->write("thread Globals* _globals");
             separator = ", ";
         }
+        if (requirements & kFragCoord_Requirement) {
+            this->write(separator);
+            this->write("float4 _fragCoord");
+            separator = ", ";
+        }
     }
     for (const auto& param : f.fDeclaration.fParameters) {
         this->write(separator);
@@ -1543,7 +1554,7 @@
             const VariableReference& v = (const VariableReference&) e;
             Requirements result = kNo_Requirements;
             if (v.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
-                result = kInputs_Requirement;
+                result = kGlobals_Requirement | kFragCoord_Requirement;
             } else if (Variable::kGlobal_Storage == v.fVariable.fStorage) {
                 if (v.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
                     result = kInputs_Requirement;
diff --git a/src/sksl/SkSLMetalCodeGenerator.h b/src/sksl/SkSLMetalCodeGenerator.h
index a777a51..1cf4198 100644
--- a/src/sksl/SkSLMetalCodeGenerator.h
+++ b/src/sksl/SkSLMetalCodeGenerator.h
@@ -90,11 +90,12 @@
 
 protected:
     typedef int Requirements;
-    static constexpr Requirements kNo_Requirements      = 0;
-    static constexpr Requirements kInputs_Requirement   = 1 << 0;
-    static constexpr Requirements kOutputs_Requirement  = 1 << 1;
-    static constexpr Requirements kUniforms_Requirement = 1 << 2;
-    static constexpr Requirements kGlobals_Requirement  = 1 << 3;
+    static constexpr Requirements kNo_Requirements       = 0;
+    static constexpr Requirements kInputs_Requirement    = 1 << 0;
+    static constexpr Requirements kOutputs_Requirement   = 1 << 1;
+    static constexpr Requirements kUniforms_Requirement  = 1 << 2;
+    static constexpr Requirements kGlobals_Requirement   = 1 << 3;
+    static constexpr Requirements kFragCoord_Requirement = 1 << 4;
 
     enum IntrinsicKind {
         kSpecial_IntrinsicKind,
diff --git a/src/sksl/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
index 113cee6..424dff6 100644
--- a/src/sksl/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
@@ -100,7 +100,7 @@
             }
         }
         SkASSERT(found);
-        fExtraEmitCodeCode += "        this->emitChild(" + to_string(index) + ", fChildren[" +
+        fExtraEmitCodeCode += "        this->invokeChild(" + to_string(index) + ", fChildren[" +
                               to_string(index) + "], args);\n";
         this->write("%s");
         fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kChildProcessor,