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/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,