Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkSLCompiler.h"
      9 
     10 #include "Test.h"
     11 
     12 static void test(skiatest::Reporter* r, const char* src, const GrShaderCaps& caps,
     13                  std::vector<const char*> expectedH, std::vector<const char*> expectedCPP) {
     14     SkSL::Program::Settings settings;
     15     settings.fCaps = &caps;
     16     SkSL::Compiler compiler;
     17     SkSL::StringStream output;
     18     std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
     19                                                              SkSL::Program::kFragmentProcessor_Kind,
     20                                                              SkSL::String(src),
     21                                                              settings);
     22     if (!program) {
     23         SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
     24         return;
     25     }
     26     REPORTER_ASSERT(r, program);
     27     bool success = compiler.toH(*program, "Test", output);
     28     if (!success) {
     29         SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
     30     }
     31     REPORTER_ASSERT(r, success);
     32     if (success) {
     33         for (const char* expected : expectedH) {
     34             bool found = strstr(output.str().c_str(), expected);
     35             if (!found) {
     36                 SkDebugf("HEADER MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src,
     37                          expected, output.str().c_str());
     38             }
     39             REPORTER_ASSERT(r, found);
     40         }
     41     }
     42     output.reset();
     43     success = compiler.toCPP(*program, "Test", output);
     44     if (!success) {
     45         SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
     46     }
     47     REPORTER_ASSERT(r, success);
     48     if (success) {
     49         for (const char* expected : expectedCPP) {
     50             bool found = strstr(output.str().c_str(), expected);
     51             if (!found) {
     52                 SkDebugf("CPP MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src,
     53                          expected, output.str().c_str());
     54             }
     55             REPORTER_ASSERT(r, found);
     56         }
     57     }
     58 }
     59 
     60 DEF_TEST(SkSLFPHelloWorld, r) {
     61     test(r,
     62          "/* HEADER */"
     63          "void main() {"
     64          "sk_OutColor = half4(1);"
     65          "}",
     66          *SkSL::ShaderCapsFactory::Default(),
     67          {
     68              "/* HEADER */\n"
     69              "\n"
     70              "/**************************************************************************************************\n"
     71              " *** This file was autogenerated from GrTest.fp; do not modify.\n"
     72              " **************************************************************************************************/\n"
     73              "#ifndef GrTest_DEFINED\n"
     74              "#define GrTest_DEFINED\n"
     75              "#include \"SkTypes.h\"\n"
     76              "#include \"GrFragmentProcessor.h\"\n"
     77              "#include \"GrCoordTransform.h\"\n"
     78              "class GrTest : public GrFragmentProcessor {\n"
     79              "public:\n"
     80              "    static std::unique_ptr<GrFragmentProcessor> Make() {\n"
     81              "        return std::unique_ptr<GrFragmentProcessor>(new GrTest());\n"
     82              "    }\n"
     83              "    GrTest(const GrTest& src);\n"
     84              "    std::unique_ptr<GrFragmentProcessor> clone() const override;\n"
     85              "    const char* name() const override { return \"Test\"; }\n"
     86              "private:\n"
     87              "    GrTest()\n"
     88              "    : INHERITED(kGrTest_ClassID, kNone_OptimizationFlags) {\n"
     89              "    }\n"
     90              "    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;\n"
     91              "    void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) "
     92                     "const override;\n"
     93              "    bool onIsEqual(const GrFragmentProcessor&) const override;\n"
     94              "    GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n"
     95              "    typedef GrFragmentProcessor INHERITED;\n"
     96              "};\n"
     97              "#endif\n"
     98          },
     99          {
    100              "/* HEADER */\n"
    101              "\n"
    102              "/**************************************************************************************************\n"
    103              " *** This file was autogenerated from GrTest.fp; do not modify.\n"
    104              " **************************************************************************************************/\n"
    105              "#include \"GrTest.h\"\n"
    106              "#include \"glsl/GrGLSLFragmentProcessor.h\"\n"
    107              "#include \"glsl/GrGLSLFragmentShaderBuilder.h\"\n"
    108              "#include \"glsl/GrGLSLProgramBuilder.h\"\n"
    109              "#include \"GrTexture.h\"\n"
    110              "#include \"SkSLCPP.h\"\n"
    111              "#include \"SkSLUtil.h\"\n"
    112              "class GrGLSLTest : public GrGLSLFragmentProcessor {\n"
    113              "public:\n"
    114              "    GrGLSLTest() {}\n"
    115              "    void emitCode(EmitArgs& args) override {\n"
    116              "        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;\n"
    117              "        const GrTest& _outer = args.fFp.cast<GrTest>();\n"
    118              "        (void) _outer;\n"
    119              "        fragBuilder->codeAppendf(\"%s = half4(1.0);\\n\", args.fOutputColor);\n"
    120              "    }\n"
    121              "private:\n"
    122              "    void onSetData(const GrGLSLProgramDataManager& pdman, "
    123                                 "const GrFragmentProcessor& _proc) override {\n"
    124              "    }\n"
    125              "};\n"
    126              "GrGLSLFragmentProcessor* GrTest::onCreateGLSLInstance() const {\n"
    127              "    return new GrGLSLTest();\n"
    128              "}\n"
    129              "void GrTest::onGetGLSLProcessorKey(const GrShaderCaps& caps, "
    130                                                 "GrProcessorKeyBuilder* b) const {\n"
    131              "}\n"
    132              "bool GrTest::onIsEqual(const GrFragmentProcessor& other) const {\n"
    133              "    const GrTest& that = other.cast<GrTest>();\n"
    134              "    (void) that;\n"
    135              "    return true;\n"
    136              "}\n"
    137              "GrTest::GrTest(const GrTest& src)\n"
    138              ": INHERITED(kGrTest_ClassID, src.optimizationFlags()) {\n"
    139              "}\n"
    140              "std::unique_ptr<GrFragmentProcessor> GrTest::clone() const {\n"
    141              "    return std::unique_ptr<GrFragmentProcessor>(new GrTest(*this));\n"
    142              "}\n"
    143          });
    144 }
    145 
    146 DEF_TEST(SkSLFPInput, r) {
    147     test(r,
    148          "in half2 point;"
    149          "void main() {"
    150          "sk_OutColor = half4(point, point);"
    151          "}",
    152          *SkSL::ShaderCapsFactory::Default(),
    153          {
    154              "const SkPoint& point() const { return fPoint; }",
    155              "static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {",
    156              "return std::unique_ptr<GrFragmentProcessor>(new GrTest(point));",
    157              "GrTest(SkPoint point)",
    158              ", fPoint(point)"
    159          },
    160          {
    161              "fragBuilder->codeAppendf(\"%s = half4(half2(%f, %f), half2(%f, %f));\\n\", "
    162                                       "args.fOutputColor, _outer.point().fX, _outer.point().fY, "
    163                                       "_outer.point().fX, _outer.point().fY);",
    164              "if (fPoint != that.fPoint) return false;"
    165          });
    166 }
    167 
    168 DEF_TEST(SkSLFPUniform, r) {
    169     test(r,
    170          "uniform half4 color;"
    171          "void main() {"
    172          "sk_OutColor = color;"
    173          "}",
    174          *SkSL::ShaderCapsFactory::Default(),
    175          {
    176              "static std::unique_ptr<GrFragmentProcessor> Make()"
    177          },
    178          {
    179             "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
    180                                                          "\"color\");",
    181          });
    182 }
    183 
    184 // SkSLFPInUniform tests the simplest plumbing case, default type, no tracking
    185 // with a setUniform template that supports inlining the value call with no
    186 // local variable.
    187 DEF_TEST(SkSLFPInUniform, r) {
    188     test(r,
    189          "in uniform half4 color;"
    190          "void main() {"
    191          "sk_OutColor = color;"
    192          "}",
    193          *SkSL::ShaderCapsFactory::Default(),
    194          {
    195              "static std::unique_ptr<GrFragmentProcessor> Make(SkRect color) {",
    196          },
    197          {
    198             "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
    199                                                          "\"color\");",
    200             "pdman.set4fv(fColorVar, 1, reinterpret_cast<const float*>(&(_outer.color())));"
    201          });
    202 }
    203 
    204 // As above, but tests in uniform's ability to override the default ctype.
    205 DEF_TEST(SkSLFPInUniformCType, r) {
    206     test(r,
    207          "layout(ctype=SkPMColor4f) in uniform half4 color;"
    208          "void main() {"
    209          "sk_OutColor = color;"
    210          "}",
    211          *SkSL::ShaderCapsFactory::Default(),
    212          {
    213              "static std::unique_ptr<GrFragmentProcessor> Make(SkPMColor4f color) {",
    214          },
    215          {
    216             "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
    217                                                          "\"color\");",
    218             "pdman.set4fv(fColorVar, 1, (_outer.color()).vec());"
    219          });
    220 }
    221 
    222 // Add state tracking to the default typed SkRect <-> half4 uniform. But since
    223 // it now has to track state, the value inlining previously done for the
    224 // setUniform call is removed in favor of a local variable.
    225 DEF_TEST(SkSLFPTrackedInUniform, r) {
    226     test(r,
    227          "layout(tracked) in uniform half4 color;"
    228          "void main() {"
    229          "sk_OutColor = color;"
    230          "}",
    231          *SkSL::ShaderCapsFactory::Default(),
    232          {
    233              "static std::unique_ptr<GrFragmentProcessor> Make(SkRect color) {",
    234          },
    235          {
    236             "SkRect fColorPrev = SkRect::MakeEmpty();",
    237             "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
    238                                                          "\"color\");",
    239             "const SkRect& colorValue = _outer.color();",
    240             "if (fColorPrev.isEmpty() || fColorPrev != colorValue) {",
    241             "fColorPrev = colorValue;",
    242             "pdman.set4fv(fColorVar, 1, reinterpret_cast<const float*>(&colorValue));"
    243          });
    244 }
    245 
    246 // Test the case where the template does not support variable inlining in
    247 // setUniform (i.e. it references the value multiple times).
    248 DEF_TEST(SkSLFPNonInlinedInUniform, r) {
    249     test(r,
    250          "in uniform half2 point;"
    251          "void main() {"
    252          "sk_OutColor = half4(point, point);"
    253          "}",
    254          *SkSL::ShaderCapsFactory::Default(),
    255          {
    256              "static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {",
    257          },
    258          {
    259             "fPointVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "
    260                                                          "\"point\");",
    261             "const SkPoint& pointValue = _outer.point();",
    262             "pdman.set2f(fPointVar, pointValue.fX, pointValue.fY);"
    263          });
    264 }
    265 
    266 // Test handling conditional uniforms (that use when= in layout), combined with
    267 // state tracking and custom ctypes to really put the code generation through its paces.
    268 DEF_TEST(SkSLFPConditionalInUniform, r) {
    269     test(r,
    270          "in bool test;"
    271          "layout(ctype=SkPMColor4f, tracked, when=test) in uniform half4 color;"
    272          "void main() {"
    273          "  if (test) {"
    274          "    sk_OutColor = color;"
    275          "  } else {"
    276          "    sk_OutColor = half4(1);"
    277          "  }"
    278          "}",
    279          *SkSL::ShaderCapsFactory::Default(),
    280          {
    281              "static std::unique_ptr<GrFragmentProcessor> Make(bool test, SkPMColor4f color) {",
    282          },
    283          {
    284             "SkPMColor4f fColorPrev = {SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}",
    285             "auto test = _outer.test();",
    286             "if (test) {",
    287             "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
    288                                                          "\"color\");",
    289             "if (fColorVar.isValid()) {",
    290             "const SkPMColor4f& colorValue = _outer.color();",
    291             "if (fColorPrev != colorValue) {",
    292             "fColorPrev = colorValue;",
    293             "pdman.set4fv(fColorVar, 1, colorValue.vec());"
    294          });
    295 }
    296 
    297 DEF_TEST(SkSLFPSections, r) {
    298     test(r,
    299          "@header { header section }"
    300          "void main() {"
    301          "sk_OutColor = half4(1);"
    302          "}",
    303          *SkSL::ShaderCapsFactory::Default(),
    304          {
    305              "header section"
    306          },
    307          {});
    308     test(r,
    309          "@class { class section }"
    310          "void main() {"
    311          "sk_OutColor = half4(1);"
    312          "}",
    313          *SkSL::ShaderCapsFactory::Default(),
    314          {
    315              "class GrTest : public GrFragmentProcessor {\n"
    316              "public:\n"
    317              " class section"
    318          },
    319          {});
    320     test(r,
    321          "@cpp { cpp section }"
    322          "void main() {"
    323          "sk_OutColor = half4(1);"
    324          "}",
    325          *SkSL::ShaderCapsFactory::Default(),
    326          {},
    327          {"cpp section"});
    328     test(r,
    329          "@constructorParams { int x, float y, std::vector<float> z }"
    330          "in float w;"
    331          "void main() {"
    332          "sk_OutColor = half4(1);"
    333          "}",
    334          *SkSL::ShaderCapsFactory::Default(),
    335          {
    336              "Make(float w,  int x, float y, std::vector<float> z )",
    337              "return std::unique_ptr<GrFragmentProcessor>(new GrTest(w, x, y, z));",
    338              "GrTest(float w,  int x, float y, std::vector<float> z )",
    339              ", fW(w) {"
    340          },
    341          {});
    342     test(r,
    343          "@constructor { constructor section }"
    344          "void main() {"
    345          "sk_OutColor = half4(1);"
    346          "}",
    347          *SkSL::ShaderCapsFactory::Default(),
    348          {
    349              "private:\n constructor section"
    350          },
    351          {});
    352     test(r,
    353          "@initializers { initializers section }"
    354          "void main() {"
    355          "sk_OutColor = half4(1);"
    356          "}",
    357          *SkSL::ShaderCapsFactory::Default(),
    358          {
    359              ": INHERITED(kGrTest_ClassID, kNone_OptimizationFlags)\n    ,  initializers section"
    360          },
    361          {});
    362     test(r,
    363          "half x = 10;"
    364          "@emitCode { fragBuilder->codeAppendf(\"half y = %d\\n\", x * 2); }"
    365          "void main() {"
    366          "sk_OutColor = half4(1);"
    367          "}",
    368          *SkSL::ShaderCapsFactory::Default(),
    369          {},
    370          {
    371             "x = 10.0;\n"
    372             " fragBuilder->codeAppendf(\"half y = %d\\n\", x * 2);"
    373          });
    374     test(r,
    375          "@fields { fields section }"
    376          "@clone { }"
    377          "void main() {"
    378          "sk_OutColor = half4(1);"
    379          "}",
    380          *SkSL::ShaderCapsFactory::Default(),
    381          {
    382             "GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n"
    383             " fields section     typedef GrFragmentProcessor INHERITED;"
    384          },
    385          {});
    386     test(r,
    387          "@make { make section }"
    388          "void main() {"
    389          "sk_OutColor = half4(1);"
    390          "}",
    391          *SkSL::ShaderCapsFactory::Default(),
    392          {
    393             "public:\n"
    394             " make section"
    395          },
    396          {});
    397     test(r,
    398          "uniform half calculated;"
    399          "in half provided;"
    400          "@setData(varName) { varName.set1f(calculated, provided * 2); }"
    401          "void main() {"
    402          "sk_OutColor = half4(1);"
    403          "}",
    404          *SkSL::ShaderCapsFactory::Default(),
    405          {},
    406          {
    407              "void onSetData(const GrGLSLProgramDataManager& varName, "
    408                             "const GrFragmentProcessor& _proc) override {\n",
    409              "UniformHandle& calculated = fCalculatedVar;",
    410              "auto provided = _outer.provided();",
    411              "varName.set1f(calculated, provided * 2);"
    412          });
    413     test(r,
    414          "@test(testDataName) { testDataName section }"
    415          "void main() {"
    416          "sk_OutColor = half4(1);"
    417          "}",
    418          *SkSL::ShaderCapsFactory::Default(),
    419          {},
    420          {
    421              "#if GR_TEST_UTILS\n"
    422              "std::unique_ptr<GrFragmentProcessor> GrTest::TestCreate(GrProcessorTestData* testDataName) {\n"
    423              " testDataName section }\n"
    424              "#endif"
    425          });
    426 }
    427 
    428 DEF_TEST(SkSLFPTransformedCoords, r) {
    429     test(r,
    430          "void main() {"
    431          "sk_OutColor = half4(sk_TransformedCoords2D[0], sk_TransformedCoords2D[0]);"
    432          "}",
    433          *SkSL::ShaderCapsFactory::Default(),
    434          {},
    435          {
    436             "SkString sk_TransformedCoords2D_0 = "
    437                                          "fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);",
    438             "fragBuilder->codeAppendf(\"%s = half4(%s, %s);\\n\", args.fOutputColor, "
    439                               "sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str());"
    440          });
    441 
    442 }
    443 
    444 DEF_TEST(SkSLFPLayoutWhen, r) {
    445     test(r,
    446          "layout(when=someExpression(someOtherExpression())) uniform half sometimes;"
    447          "void main() {"
    448          "}",
    449          *SkSL::ShaderCapsFactory::Default(),
    450          {},
    451          {
    452             "if (someExpression(someOtherExpression())) {\n"
    453             "            fSometimesVar = args.fUniformHandler->addUniform"
    454          });
    455 
    456 }
    457 
    458 DEF_TEST(SkSLFPChildProcessors, r) {
    459     test(r,
    460          "in fragmentProcessor child1;"
    461          "in fragmentProcessor child2;"
    462          "void main() {"
    463          "    sk_OutColor = process(child1) * process(child2);"
    464          "}",
    465          *SkSL::ShaderCapsFactory::Default(),
    466          {
    467             "this->registerChildProcessor(std::move(child1));",
    468             "this->registerChildProcessor(std::move(child2));"
    469          },
    470          {
    471             "SkString _child0(\"_child0\");",
    472             "this->emitChild(_outer.child1_index(), &_child0, args);",
    473             "SkString _child1(\"_child1\");",
    474             "this->emitChild(_outer.child2_index(), &_child1, args);",
    475             "this->registerChildProcessor(src.childProcessor(fChild1_index).clone());",
    476             "this->registerChildProcessor(src.childProcessor(fChild2_index).clone());"
    477          });
    478 }
    479 
    480 DEF_TEST(SkSLFPChildProcessorsWithInput, r) {
    481     test(r,
    482          "in fragmentProcessor child1;"
    483          "in fragmentProcessor child2;"
    484          "void main() {"
    485          "    half4 childIn = sk_InColor;"
    486          "    half4 childOut1 = process(child1, childIn);"
    487          "    half4 childOut2 = process(child2, childOut1);"
    488          "    sk_OutColor = childOut2;"
    489          "}",
    490          *SkSL::ShaderCapsFactory::Default(),
    491          {
    492             "this->registerChildProcessor(std::move(child1));",
    493             "this->registerChildProcessor(std::move(child2));"
    494          },
    495          {
    496             "SkString _input0(\"childIn\");",
    497             "SkString _child0(\"_child0\");",
    498             "this->emitChild(_outer.child1_index(), _input0.c_str(), &_child0, args);",
    499             "SkString _input1(\"childOut1\");",
    500             "SkString _child1(\"_child1\");",
    501             "this->emitChild(_outer.child2_index(), _input1.c_str(), &_child1, args);",
    502             "this->registerChildProcessor(src.childProcessor(fChild1_index).clone());",
    503             "this->registerChildProcessor(src.childProcessor(fChild2_index).clone());"
    504          });
    505 }
    506 
    507 DEF_TEST(SkSLFPChildProcessorWithInputExpression, r) {
    508     test(r,
    509          "in fragmentProcessor child;"
    510          "void main() {"
    511          "    sk_OutColor = process(child, sk_InColor * half4(0.5));"
    512          "}",
    513          *SkSL::ShaderCapsFactory::Default(),
    514          {
    515             "this->registerChildProcessor(std::move(child));",
    516          },
    517          {
    518             "SkString _input0 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);",
    519             "SkString _child0(\"_child0\");",
    520             "this->emitChild(_outer.child_index(), _input0.c_str(), &_child0, args);",
    521             "this->registerChildProcessor(src.childProcessor(fChild_index).clone());",
    522          });
    523 }
    524 
    525 DEF_TEST(SkSLFPNestedChildProcessors, r) {
    526     test(r,
    527          "in fragmentProcessor child1;"
    528          "in fragmentProcessor child2;"
    529          "void main() {"
    530          "    sk_OutColor = process(child2, sk_InColor * process(child1, sk_InColor * half4(0.5)));"
    531          "}",
    532          *SkSL::ShaderCapsFactory::Default(),
    533          {
    534             "this->registerChildProcessor(std::move(child1));",
    535             "this->registerChildProcessor(std::move(child2));"
    536          },
    537          {
    538             "SkString _input0 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);",
    539             "SkString _child0(\"_child0\");",
    540             "this->emitChild(_outer.child1_index(), _input0.c_str(), &_child0, args);",
    541             "SkString _input1 = SkStringPrintf(\"%s * %s\", args.fInputColor, _child0.c_str());",
    542             "SkString _child1(\"_child1\");",
    543             "this->emitChild(_outer.child2_index(), _input1.c_str(), &_child1, args);",
    544             "this->registerChildProcessor(src.childProcessor(fChild1_index).clone());",
    545             "this->registerChildProcessor(src.childProcessor(fChild2_index).clone());"
    546          });
    547 }
    548 
    549 DEF_TEST(SkSLFPChildFPAndGlobal, r) {
    550     test(r,
    551          "in fragmentProcessor child;"
    552          "bool hasCap = sk_Caps.externalTextureSupport;"
    553          "void main() {"
    554          "    if (hasCap) {"
    555          "        sk_OutColor = process(child, sk_InColor);"
    556          "    } else {"
    557          "        sk_OutColor = half4(1);"
    558          "    }"
    559          "}",
    560          *SkSL::ShaderCapsFactory::Default(),
    561          {
    562             "this->registerChildProcessor(std::move(child));"
    563          },
    564          {
    565             "hasCap = sk_Caps.externalTextureSupport;",
    566             "fragBuilder->codeAppendf(\"bool hasCap = %s;\\nif (hasCap) {\", "
    567                     "(hasCap ? \"true\" : \"false\"));",
    568             "SkString _input0 = SkStringPrintf(\"%s\", args.fInputColor);",
    569             "SkString _child0(\"_child0\");",
    570             "this->emitChild(_outer.child_index(), _input0.c_str(), &_child0, args);",
    571             "fragBuilder->codeAppendf(\"\\n    %s = %s;\\n} else {\\n    %s = half4(1.0);\\n}"
    572                     "\\n\", args.fOutputColor, _child0.c_str(), args.fOutputColor);",
    573             "this->registerChildProcessor(src.childProcessor(fChild_index).clone());"
    574          });
    575 }
    576 
    577 DEF_TEST(SkSLFPChildProcessorInlineFieldAccess, r) {
    578     test(r,
    579          "in fragmentProcessor child;"
    580          "void main() {"
    581          "    if (child.preservesOpaqueInput) {"
    582          "        sk_OutColor = process(child, sk_InColor);"
    583          "    } else {"
    584          "        sk_OutColor = half4(1);"
    585          "    }"
    586          "}",
    587          *SkSL::ShaderCapsFactory::Default(),
    588          {
    589             "this->registerChildProcessor(std::move(child));"
    590          },
    591          {
    592             "fragBuilder->codeAppendf(\"if (%s) {\", "
    593                     "(_outer.childProcessor(_outer.child_index()).preservesOpaqueInput() ? "
    594                     "\"true\" : \"false\"));",
    595             "SkString _input0 = SkStringPrintf(\"%s\", args.fInputColor);",
    596             "SkString _child0(\"_child0\");",
    597             "this->emitChild(_outer.child_index(), _input0.c_str(), &_child0, args);",
    598             "fragBuilder->codeAppendf(\"\\n    %s = %s;\\n} else {\\n    %s = half4(1.0);\\n}\\n\""
    599                     ", args.fOutputColor, _child0.c_str(), args.fOutputColor);",
    600             "this->registerChildProcessor(src.childProcessor(fChild_index).clone());"
    601          });
    602 }
    603 
    604 DEF_TEST(SkSLFPChildProcessorFieldAccess, r) {
    605     test(r,
    606          "in fragmentProcessor child;"
    607          "bool opaque = child.preservesOpaqueInput;"
    608          "void main() {"
    609          "    if (opaque) {"
    610          "        sk_OutColor = process(child);"
    611          "    } else {"
    612          "        sk_OutColor = half4(0.5);"
    613          "    }"
    614          "}",
    615          *SkSL::ShaderCapsFactory::Default(),
    616          {
    617             "this->registerChildProcessor(std::move(child));"
    618          },
    619          {
    620             "opaque = _outer.childProcessor(_outer.child_index()).preservesOpaqueInput();",
    621             "fragBuilder->codeAppendf(\"bool opaque = %s;\\nif (opaque) {\", "
    622                     "(opaque ? \"true\" : \"false\"));",
    623             "SkString _child0(\"_child0\");",
    624             "this->emitChild(_outer.child_index(), &_child0, args);",
    625             "fragBuilder->codeAppendf(\"\\n    %s = %s;\\n} else {\\n    %s = half4(0.5);\\n}\\n\""
    626                     ", args.fOutputColor, _child0.c_str(), args.fOutputColor);",
    627             "this->registerChildProcessor(src.childProcessor(fChild_index).clone());"
    628          });
    629 }
    630 
    631 DEF_TEST(SkSLFPNullableChildProcessor, r) {
    632     test(r,
    633          "in fragmentProcessor? child;"
    634          "void main() {"
    635          "    if (child != null) {"
    636          "        sk_OutColor = process(child);"
    637          "    } else {"
    638          "        sk_OutColor = half4(0.5);"
    639          "    }"
    640          "}",
    641          *SkSL::ShaderCapsFactory::Default(),
    642          {},
    643          {
    644             "SkString _child0(\"_child0\");",
    645             "if (_outer.child_index() >= 0) {",
    646                 "this->emitChild(_outer.child_index(), &_child0, args);",
    647             "} else {",
    648                 "fragBuilder->codeAppendf(\"half4 %s;\", _child0.c_str());",
    649             "}",
    650             "fragBuilder->codeAppendf(\"\\n    %s = %s;\\n} else {\\n    %s = half4(0.5);\\n}\\n\""
    651                     ", args.fOutputColor, _child0.c_str(), args.fOutputColor);",
    652          });
    653 }
    654