/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLStringStream.h"

#include "tests/Test.h"

static void test(skiatest::Reporter* r, const GrShaderCaps& caps, const char* src,
                 std::vector<const char*> expectedH, std::vector<const char*> expectedCPP) {
    SkSL::Program::Settings settings;
    settings.fCaps = &caps;
    settings.fRemoveDeadFunctions = false;
    SkSL::Compiler compiler;
    SkSL::StringStream output;
    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
                                                             SkSL::Program::kFragmentProcessor_Kind,
                                                             SkSL::String(src),
                                                             settings);
    if (!program) {
        SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
        return;
    }
    REPORTER_ASSERT(r, program);
    bool success = compiler.toH(*program, "Test", output);
    if (!success) {
        SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
    }
    REPORTER_ASSERT(r, success);
    if (success) {
        for (const char* expected : expectedH) {
            bool found = strstr(output.str().c_str(), expected);
            if (!found) {
                SkDebugf("HEADER MISMATCH:\nsource:\n%s\n\n"
                         "header expected:\n'%s'\n\n"
                         "header received:\n'%s'",
                         src, expected, output.str().c_str());
            }
            REPORTER_ASSERT(r, found);
        }
    }
    output.reset();
    success = compiler.toCPP(*program, "Test", output);
    if (!success) {
        SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
    }
    REPORTER_ASSERT(r, success);
    if (success) {
        for (const char* expected : expectedCPP) {
            bool found = strstr(output.str().c_str(), expected);
            if (!found) {
                SkDebugf("CPP MISMATCH:\nsource:\n%s\n\n"
                         "cpp expected:\n'%s'\n\n"
                         "cpp received:\n'%s'",
                         src, expected, output.str().c_str());
            }
            REPORTER_ASSERT(r, found);
        }
    }
}

static void test_failure(skiatest::Reporter* r, const char* src, const char* error) {
    SkSL::Compiler compiler;
    SkSL::Program::Settings settings;
    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
    settings.fCaps = caps.get();
    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
                                                             SkSL::Program::kFragmentProcessor_Kind,
                                                             SkSL::String(src),
                                                             settings);
    if (!compiler.errorCount()) {
        compiler.optimize(*program);
    }
    SkSL::String skError(error);
    if (compiler.errorText() != skError) {
        SkDebugf("SKSL ERROR:\n    source: %s\n    expected: %s    received: %s",
                 src, error, compiler.errorText().c_str());
    }
    REPORTER_ASSERT(r, compiler.errorText() == skError);
}

DEF_TEST(SkSLFPHelloWorld, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             /* HELLO WORLD */
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{
R"__Header__(/* HELLO WORLD */

/**************************************************************************************************
 *** This file was autogenerated from GrTest.fp; do not modify.
 **************************************************************************************************/
#ifndef GrTest_DEFINED
#define GrTest_DEFINED

#include "include/core/SkM44.h"
#include "include/core/SkTypes.h"


#include "src/gpu/GrFragmentProcessor.h"

class GrTest : public GrFragmentProcessor {
public:
    static std::unique_ptr<GrFragmentProcessor> Make() {
        return std::unique_ptr<GrFragmentProcessor>(new GrTest());
    }
    GrTest(const GrTest& src);
    std::unique_ptr<GrFragmentProcessor> clone() const override;
    const char* name() const override { return "Test"; }
private:
    GrTest()
    : INHERITED(kGrTest_ClassID, kNone_OptimizationFlags) {
    }
    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
    bool onIsEqual(const GrFragmentProcessor&) const override;
#if GR_TEST_UTILS
    SkString onDumpInfo() const override;
#endif
    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
    typedef GrFragmentProcessor INHERITED;
};
#endif
)__Header__"
         },
         /*expectedCPP=*/{
R"__Cpp__(/* HELLO WORLD */

/**************************************************************************************************
 *** This file was autogenerated from GrTest.fp; do not modify.
 **************************************************************************************************/
#include "GrTest.h"

#include "src/core/SkUtils.h"
#include "src/gpu/GrTexture.h"
#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
#include "src/sksl/SkSLCPP.h"
#include "src/sksl/SkSLUtil.h"
class GrGLSLTest : public GrGLSLFragmentProcessor {
public:
    GrGLSLTest() {}
    void emitCode(EmitArgs& args) override {
        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
        const GrTest& _outer = args.fFp.cast<GrTest>();
        (void) _outer;
        fragBuilder->codeAppendf(
R"SkSL(%s = half4(1.0);
)SkSL"
, args.fOutputColor);
    }
private:
    void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& _proc) override {
    }
};
GrGLSLFragmentProcessor* GrTest::onCreateGLSLInstance() const {
    return new GrGLSLTest();
}
void GrTest::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
}
bool GrTest::onIsEqual(const GrFragmentProcessor& other) const {
    const GrTest& that = other.cast<GrTest>();
    (void) that;
    return true;
}
GrTest::GrTest(const GrTest& src)
: INHERITED(kGrTest_ClassID, src.optimizationFlags()) {
        this->cloneAndRegisterAllChildProcessors(src);
}
std::unique_ptr<GrFragmentProcessor> GrTest::clone() const {
    return std::make_unique<GrTest>(*this);
}
#if GR_TEST_UTILS
SkString GrTest::onDumpInfo() const {
    return SkString();
}
#endif
)__Cpp__"
         });
}

DEF_TEST(SkSLFPInputHalf2, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in uniform half2 point;
             void main() {
                 sk_OutColor = half4(point, point);
             }
         )__SkSL__",
         /*expectedH=*/{
             "static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {",
             "return std::unique_ptr<GrFragmentProcessor>(new GrTest(point));",
             "GrTest(SkPoint point)",
             ", point(point)"
         },
         /*expectedCPP=*/{
R"__Cpp__(fragBuilder->codeAppendf(
R"SkSL(%s = half4(%s, %s);
)SkSL"
, args.fOutputColor, args.fUniformHandler->getUniformCStr(pointVar), args.fUniformHandler->getUniformCStr(pointVar));
)__Cpp__",
             "if (point != that.point) return false;"
         });
}

DEF_TEST(SkSLFPInputHalf1, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             layout(key) in half value;
             void main() {
                 sk_OutColor = half4(value);
             }
         )__SkSL__",
         /*expectedH=*/{
R"__Cpp__(static std::unique_ptr<GrFragmentProcessor> Make(float value) {
        return std::unique_ptr<GrFragmentProcessor>(new GrTest(value));
    }
)__Cpp__",
R"__Cpp__(GrTest(float value)
    : INHERITED(kGrTest_ClassID, kNone_OptimizationFlags)
    , value(value) {
    }
)__Cpp__",
         },
         /*expectedCPP=*/{
R"__Cpp__(void GrTest::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
    b->add32(sk_bit_cast<uint32_t>(value));
}
)__Cpp__",
R"__Cpp__(bool GrTest::onIsEqual(const GrFragmentProcessor& other) const {
    const GrTest& that = other.cast<GrTest>();
    (void) that;
    if (value != that.value) return false;
    return true;
}
)__Cpp__",
R"__Cpp__(GrTest::GrTest(const GrTest& src)
: INHERITED(kGrTest_ClassID, src.optimizationFlags())
, value(src.value) {
        this->cloneAndRegisterAllChildProcessors(src);
}
)__Cpp__",
R"__Cpp__(std::unique_ptr<GrFragmentProcessor> GrTest::clone() const {
    return std::make_unique<GrTest>(*this);
}
)__Cpp__",
R"__Cpp__(#if GR_TEST_UTILS
SkString GrTest::onDumpInfo() const {
    return SkStringPrintf("(value=%f)", value);
}
)__Cpp__",
        });
}

DEF_TEST(SkSLFPUniform, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             void main() {
                 sk_OutColor = color;
             }
         )__SkSL__",
         /*expectedH=*/{
             "static std::unique_ptr<GrFragmentProcessor> Make()"
         },
         /*expectedCPP=*/{
             "colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
                                                        "kHalf4_GrSLType, \"color\");",
         });
}

// SkSLFPInUniform tests the simplest plumbing case, default type, no tracking
// with a setUniform template that supports inlining the value call with no
// local variable.
DEF_TEST(SkSLFPInUniform, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in uniform half4 color;
             void main() {
                 sk_OutColor = color;
             }
         )__SkSL__",
         /*expectedH=*/{
             "static std::unique_ptr<GrFragmentProcessor> Make(SkRect color) {",
         },
         /*expectedCPP=*/{
            "colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
                                                        "kHalf4_GrSLType, \"color\");",
            "pdman.set4fv(colorVar, 1, reinterpret_cast<const float*>(&(_outer.color)));"
         });
}

// As above, but tests in uniform's ability to override the default ctype.
DEF_TEST(SkSLFPInUniformCType, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             layout(ctype=SkPMColor4f) in uniform half4 color;
             void main() {
                 sk_OutColor = color;
             }
         )__SkSL__",
         /*expectedH=*/{
             "static std::unique_ptr<GrFragmentProcessor> Make(SkPMColor4f color) {",
         },
         /*expectedCPP=*/{
            "colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
                                                        "kHalf4_GrSLType, \"color\");",
            "pdman.set4fv(colorVar, 1, (_outer.color).vec());"
         });
}

// Add state tracking to the default typed SkRect <-> half4 uniform. But since
// it now has to track state, the value inlining previously done for the
// setUniform call is removed in favor of a local variable.
DEF_TEST(SkSLFPTrackedInUniform, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             layout(tracked) in uniform half4 color;
             void main() {
                 sk_OutColor = color;
             }
         )__SkSL__",
         /*expectedH=*/{
             "static std::unique_ptr<GrFragmentProcessor> Make(SkRect color) {",
         },
         /*expectedCPP=*/{
            "SkRect colorPrev = SkRect::MakeEmpty();",
            "colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
                                                        "kHalf4_GrSLType, \"color\");",
            "const SkRect& colorValue = _outer.color;",
            "if (colorPrev.isEmpty() || colorPrev != colorValue) {",
            "colorPrev = colorValue;",
            "pdman.set4fv(colorVar, 1, reinterpret_cast<const float*>(&colorValue));"
         });
}

// Test the case where the template does not support variable inlining in
// setUniform (i.e. it references the value multiple times).
DEF_TEST(SkSLFPNonInlinedInUniform, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in uniform half2 point;
             void main() {
                 sk_OutColor = half4(point, point);
             }
         )__SkSL__",
         /*expectedH=*/{
             "static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {",
         },
         /*expectedCPP=*/{
            "pointVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
                                                        "kHalf2_GrSLType, \"point\");",
            "const SkPoint& pointValue = _outer.point;",
            "pdman.set2f(pointVar, pointValue.fX, pointValue.fY);"
         });
}

// Test handling conditional uniforms (that use when= in layout), combined with
// state tracking and custom ctypes to really put the code generation through its paces.
DEF_TEST(SkSLFPConditionalInUniform, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             layout(key) in bool test;
             layout(ctype=SkPMColor4f, tracked, when=test) in uniform half4 color;
             void main() {
               if (test) {
                 sk_OutColor = color;
               } else {
                 sk_OutColor = half4(1);
               }
             }
         )__SkSL__",
         /*expectedH=*/{
             "static std::unique_ptr<GrFragmentProcessor> Make(bool test, SkPMColor4f color) {",
         },
         /*expectedCPP=*/{
            "SkPMColor4f colorPrev = {SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}",
            "auto test = _outer.test;",
            "if (test) {",
            "colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
                                                        "kHalf4_GrSLType, \"color\");",
            "if (colorVar.isValid()) {",
            "const SkPMColor4f& colorValue = _outer.color;",
            "if (colorPrev != colorValue) {",
            "colorPrev = colorValue;",
            "pdman.set4fv(colorVar, 1, colorValue.vec());"
         });
}

DEF_TEST(SkSLFPSections, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @header { header section }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{
             "header section"
         },
         /*expectedCPP=*/{});
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @class { class section }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{
             "class GrTest : public GrFragmentProcessor {\n"
             "public:\n"
             " class section"
         },
         /*expectedCPP=*/{});
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @cpp { cpp section }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            "cpp section"
         });
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @constructorParams { int x, float y, std::vector<float> z }
             in float w;
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{
             "Make(float w,  int x, float y, std::vector<float> z )",
             "return std::unique_ptr<GrFragmentProcessor>(new GrTest(w, x, y, z));",
             "GrTest(float w,  int x, float y, std::vector<float> z )",
             ", w(w) {"
         },
         /*expectedCPP=*/{});
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @constructor { constructor section }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{
             "private:\n constructor section"
         },
         /*expectedCPP=*/{});
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @initializers { initializers section }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{
             ": INHERITED(kGrTest_ClassID, kNone_OptimizationFlags)\n    ,  initializers section"
         },
         /*expectedCPP=*/{});
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             half x = 10;
             @emitCode { fragBuilder->codeAppendf("half y = %d\n", x * 2); }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            "x = 10.0;\n"
            " fragBuilder->codeAppendf(\"half y = %d\\n\", x * 2);"
         });
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @fields { fields section }
             @clone { }
             @dumpInfo { }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{
            "const char* name() const override { return \"Test\"; }\n"
            " fields section private:"
         },
         /*expectedCPP=*/{});
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @make { make section }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{
            "public:\n"
            " make section"
         },
         /*expectedCPP=*/{});
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half calculated;
             layout(key) in half provided;
             @setData(varName) { varName.set1f(calculated, provided * 2); }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
             "void onSetData(const GrGLSLProgramDataManager& varName, "
                            "const GrFragmentProcessor& _proc) override {\n",
             "UniformHandle& calculated = calculatedVar;",
             "auto provided = _outer.provided;",
             "varName.set1f(calculated, provided * 2);"
         });
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @test(testDataName) { testDataName section }
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
             "#if GR_TEST_UTILS\n"
             "std::unique_ptr<GrFragmentProcessor> GrTest::TestCreate(GrProcessorTestData* testDataName) {\n"
             " testDataName section }\n"
             "#endif"
         });
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             @dumpInfo {dump all the fields}
             void main() {
                 sk_OutColor = half4(1);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
R"__Cpp__(#if GR_TEST_UTILS
SkString GrTest::onDumpInfo() const {
dump all the fields
}
#endif)__Cpp__"
         });
}

DEF_TEST(SkSLFPMainCoords, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             void main(float2 coord) {
                 sk_OutColor = half4(coord, coord);
             }
         )__SkSL__",
         /*expectedH=*/{
             "this->setUsesSampleCoordsDirectly();"
         },
         /*expectedCPP=*/{
            "fragBuilder->codeAppendf(\n"
            "R\"SkSL(%s = half4(%s, %s);\n"
            ")SkSL\"\n"
            ", args.fOutputColor, args.fSampleCoord, args.fSampleCoord);"
         });
}

DEF_TEST(SkSLFPLayoutWhen, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
            layout(when=someExpression(someOtherExpression())) uniform half sometimes;
            void main() {
            }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            "if (someExpression(someOtherExpression())) {\n"
            "            sometimesVar = args.fUniformHandler->addUniform"
         });
}

DEF_TEST(SkSLFPChildProcessors, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor child1;
             in fragmentProcessor child2;
             void main() {
                 sk_OutColor = sample(child1) * sample(child2);
             }
         )__SkSL__",
         /*expectedH=*/{
            "this->registerChild(std::move(child1), SkSL::SampleUsage::PassThrough());",
            "this->registerChild(std::move(child2), SkSL::SampleUsage::PassThrough());"
         },
         /*expectedCPP=*/{
            "SkString _sample149 = this->invokeChild(0, args);\n",
            "SkString _sample166 = this->invokeChild(1, args);\n",
            "fragBuilder->codeAppendf(\n"
            "R\"SkSL(%s = %s * %s;\n"
            ")SkSL\"\n"
            ", args.fOutputColor, _sample149.c_str(), _sample166.c_str());",
            "this->cloneAndRegisterAllChildProcessors(src);",
         });
}

DEF_TEST(SkSLFPChildProcessorsWithInput, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             in fragmentProcessor child1;
             in fragmentProcessor child2;
             void main() {
                 half4 childIn = color;
                 half4 childOut1 = sample(child1, childIn);
                 half4 childOut2 = sample(child2, childOut1);
                 sk_OutColor = childOut2;
             }
         )__SkSL__",
         /*expectedH=*/{
            "this->registerChild(std::move(child1), SkSL::SampleUsage::PassThrough());",
            "this->registerChild(std::move(child2), SkSL::SampleUsage::PassThrough());"
         },
         /*expectedCPP=*/{
            "this->cloneAndRegisterAllChildProcessors(src);",
            R"__Cpp__(
        SkString _input227("childIn");
        SkString _sample227 = this->invokeChild(0, _input227.c_str(), args);
        fragBuilder->codeAppendf(
R"SkSL(
half4 childOut1 = %s;)SkSL"
, _sample227.c_str());
        SkString _input287("childOut1");
        SkString _sample287 = this->invokeChild(1, _input287.c_str(), args);
        fragBuilder->codeAppendf(
R"SkSL(
half4 childOut2 = %s;
%s = childOut2;
)SkSL"
, _sample287.c_str(), args.fOutputColor);
)__Cpp__"});
}

DEF_TEST(SkSLFPChildProcessorWithInputExpression, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             in fragmentProcessor child;
             void main() {
                 sk_OutColor = sample(child, color * half4(0.5));
             }
         )__SkSL__",
         /*expectedH=*/{
            "this->registerChild(std::move(child), SkSL::SampleUsage::PassThrough());",
         },
         /*expectedCPP=*/{
            "this->cloneAndRegisterAllChildProcessors(src);",
            R"__Cpp__(
        SkString _input140 = SkStringPrintf("%s * half4(0.5)", args.fUniformHandler->getUniformCStr(colorVar));
        SkString _sample140 = this->invokeChild(0, _input140.c_str(), args);
        fragBuilder->codeAppendf(
R"SkSL(%s = %s;
)SkSL"
, args.fOutputColor, _sample140.c_str());
)__Cpp__"});
}

DEF_TEST(SkSLFPNestedChildProcessors, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             in fragmentProcessor child1;
             in fragmentProcessor child2;
             void main() {
                 sk_OutColor = sample(child2, color * sample(child1, color * half4(0.5)));
             }
         )__SkSL__",
         /*expectedH=*/{
            "this->registerChild(std::move(child1), SkSL::SampleUsage::PassThrough());",
            "this->registerChild(std::move(child2), SkSL::SampleUsage::PassThrough());"
         },
         /*expectedCPP=*/{
            "this->cloneAndRegisterAllChildProcessors(src);",
            R"__Cpp__(
        SkString _input206 = SkStringPrintf("%s * half4(0.5)", args.fUniformHandler->getUniformCStr(colorVar));
        SkString _sample206 = this->invokeChild(0, _input206.c_str(), args);
        SkString _input183 = SkStringPrintf("%s * %s", args.fUniformHandler->getUniformCStr(colorVar), _sample206.c_str());
        SkString _sample183 = this->invokeChild(1, _input183.c_str(), args);
        fragBuilder->codeAppendf(
R"SkSL(%s = %s;
)SkSL"
, args.fOutputColor, _sample183.c_str());
)__Cpp__",
            });
}

DEF_TEST(SkSLFPChildFPAndGlobal, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor child;
             bool hasCap = sk_Caps.externalTextureSupport;
             void main() {
                 if (hasCap) {
                     sk_OutColor = sample(child);
                 } else {
                     sk_OutColor = half4(1);
                 }
             }
         )__SkSL__",
         /*expectedH=*/{
            "this->registerChild(std::move(child), SkSL::SampleUsage::PassThrough());"
         },
         /*expectedCPP=*/{
            "this->cloneAndRegisterAllChildProcessors(src);",
            R"__Cpp__(
hasCap = sk_Caps.externalTextureSupport;
        fragBuilder->codeAppendf(
R"SkSL(bool hasCap = %s;
if (hasCap) {)SkSL"
, (hasCap ? "true" : "false"));
        SkString _sample200 = this->invokeChild(0, args);
        fragBuilder->codeAppendf(
R"SkSL(
    %s = %s;
} else {
    %s = half4(1.0);
}
)SkSL"
, args.fOutputColor, _sample200.c_str(), args.fOutputColor);
)__Cpp__",
         });
}

DEF_TEST(SkSLFPChildProcessorInlineFieldAccess, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor child;
             void main() {
                 if (child.preservesOpaqueInput) {
                     sk_OutColor = sample(child);
                 } else {
                     sk_OutColor = half4(1);
                 }
             }
         )__SkSL__",
         /*expectedH=*/{
            "this->registerChild(std::move(child), SkSL::SampleUsage::PassThrough());"
         },
         /*expectedCPP=*/{
            "this->cloneAndRegisterAllChildProcessors(src);",
            R"__Cpp__(
        fragBuilder->codeAppendf(
R"SkSL(if (%s) {)SkSL"
, (_outer.childProcessor(0)->preservesOpaqueInput() ? "true" : "false"));
        SkString _sample161 = this->invokeChild(0, args);
        fragBuilder->codeAppendf(
R"SkSL(
    %s = %s;
} else {
    %s = half4(1.0);
}
)SkSL"
, args.fOutputColor, _sample161.c_str(), args.fOutputColor);
)__Cpp__",
         });
}

DEF_TEST(SkSLFPChildProcessorFieldAccess, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor child;
             bool opaque = child.preservesOpaqueInput;
             void main() {
                 if (opaque) {
                     sk_OutColor = sample(child);
                 } else {
                     sk_OutColor = half4(0.5);
                 }
         }
         )__SkSL__",
         /*expectedH=*/{
            "this->registerChild(std::move(child), SkSL::SampleUsage::PassThrough());"
         },
         /*expectedCPP=*/{
            "opaque = _outer.childProcessor(0)->preservesOpaqueInput();",
            "fragBuilder->codeAppendf(\n"
            "R\"SkSL(bool opaque = %s;\n"
            "if (opaque) {)SkSL\"\n"
            ", (opaque ? \"true\" : \"false\"));",
            "SkString _sample196 = this->invokeChild(0, args);",
            "fragBuilder->codeAppendf(\n"
            "R\"SkSL(\n"
            "    %s = %s;\n"
            "} else {\n"
            "    %s = half4(0.5);\n"
            "}\n"
            ")SkSL\"\n"
            ", args.fOutputColor, _sample196.c_str(), args.fOutputColor);",
            "this->cloneAndRegisterAllChildProcessors(src);",
         });
}

DEF_TEST(SkSLFPNullableChildProcessor, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor? child;
             void main() {
                 if (child != null) {
                     sk_OutColor = sample(child);
                 } else {
                     sk_OutColor = half4(0.5);
                 }
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            "fragBuilder->codeAppendf(\n"
            "R\"SkSL(if (%s) {)SkSL\"\n"
            ", _outer.childProcessor(0) ? \"true\" : \"false\");",
            "SkString _sample149 = this->invokeChild(0, args);",
            "fragBuilder->codeAppendf(\n"
            "R\"SkSL(\n"
            "    %s = %s;\n"
            "} else {\n"
            "    %s = half4(0.5);\n"
            "}\n"
            ")SkSL\"\n"
            ", args.fOutputColor, _sample149.c_str(), args.fOutputColor);",
         });
}

DEF_TEST(SkSLFPBadIn, r) {
    test_failure(r,
         R"__SkSL__(
             in half4 c;
             void main() {
                 sk_OutColor = c;
             }
         )__SkSL__",
         "error: 4: 'in' variable must be either 'uniform' or 'layout(key)', or there must be a "
         "custom @setData function\n1 error\n");
}

DEF_TEST(SkSLFPNoFPLocals, r) {
    test_failure(r,
    R"__SkSL__(
        void main() {
            fragmentProcessor child;
        }
    )__SkSL__",
    "error: 1: variables of type 'fragmentProcessor' must be global\n"
    "1 error\n");
}

DEF_TEST(SkSLFPNoFPParams, r) {
    test_failure(r,
    R"__SkSL__(
        in fragmentProcessor child;
        half4 helper(fragmentProcessor fp) { return sample(fp); }
        void main() {
            sk_OutColor = helper(child);
        }
    )__SkSL__",
    "error: 3: parameters of type 'fragmentProcessor' not allowed\n"
    "error: 5: unknown identifier 'helper'\n"
    "2 errors\n");
}

DEF_TEST(SkSLFPNoFPReturns, r) {
    test_failure(r,
    R"__SkSL__(
        in fragmentProcessor child;
        fragmentProcessor get_child() { return child; }
        void main() {
            sk_OutColor = sample(get_child());
        }
    )__SkSL__",
    "error: 3: functions may not return type 'fragmentProcessor'\n"
    "error: 5: unknown identifier 'get_child'\n"
    "2 errors\n");
}

DEF_TEST(SkSLFPNoFPConstructors, r) {
    test_failure(r,
    R"__SkSL__(
        in fragmentProcessor child;
        void main() {
            sk_OutColor = sample(fragmentProcessor(child));
        }
    )__SkSL__",
    "error: 4: cannot construct 'fragmentProcessor'\n"
    "1 error\n");
}

DEF_TEST(SkSLFPNoFPExpressions, r) {
    test_failure(r,
    R"__SkSL__(
        in fragmentProcessor child1;
        in fragmentProcessor child2;
        void main(float2 coord) {
            sk_OutColor = sample(coord.x > 10 ? child1 : child2);
        }
    )__SkSL__",
    "error: 5: ternary expression of type 'fragmentProcessor' not allowed\n"
    "1 error\n");
}

DEF_TEST(SkSLFPSampleCoords, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor child;
             void main(float2 coord) {
                 sk_OutColor = sample(child) + sample(child, coord / 2);
             }
         )__SkSL__",
         /*expectedH=*/{
             "this->registerChild(std::move(child), SkSL::SampleUsage(SkSL::SampleUsage::Kind::kNone, \"\", false, true, true));",
             "this->setUsesSampleCoordsDirectly();"
         },
         /*expectedCPP=*/{
            "SkString _sample118 = this->invokeChild(0, args);\n",
            "SkString _coords134 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);\n",
            "SkString _sample134 = this->invokeChild(0, args, _coords134.c_str());\n",
            "fragBuilder->codeAppendf(\n"
            "R\"SkSL(%s = %s + %s;\n"
            ")SkSL\"\n"
            ", args.fOutputColor, _sample118.c_str(), _sample134.c_str());"
        });
}

DEF_TEST(SkSLFPFunction, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             half4 flip(half4 c) { return c.abgr; }
             void main() {
                 sk_OutColor = flip(color);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            R"__Cpp__(
        SkString flip_name;
        const GrShaderVar flip_args[] = { GrShaderVar("c", kHalf4_GrSLType)};
        fragBuilder->emitFunction(kHalf4_GrSLType, "flip", 1, flip_args,
R"SkSL(return c.wzyx;
)SkSL", &flip_name);
        fragBuilder->codeAppendf(
R"SkSL(half4 _inlineResulthalf4fliphalf40;
half4 _inlineArghalf4fliphalf41_0 = %s;
{
    _inlineResulthalf4fliphalf40 = _inlineArghalf4fliphalf41_0.wzyx;
}
%s = _inlineResulthalf4fliphalf40;

)SkSL"
, args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor);
)__Cpp__"});
}

DEF_TEST(SkSLFPSwitchWithReturnInsideCannotBeInlined, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             half4 switchy(half4 c) {
                 switch (int(c.x)) {
                     case 0: return c.yyyy;
                 }
                 return c.zzzz;
             }
             void main() {
                 sk_OutColor = switchy(color);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
         R"__Cpp__(fragBuilder->emitFunction(kHalf4_GrSLType, "switchy", 1, switchy_args,
R"SkSL(switch (int(c.x)) {
    case 0:
        return c.yyyy;
}
return c.zzzz;
)SkSL", &switchy_name);
        fragBuilder->codeAppendf(
R"SkSL(%s = %s(%s);
)SkSL"
, args.fOutputColor, switchy_name.c_str(), args.fUniformHandler->getUniformCStr(colorVar));
)__Cpp__",
         });
}

DEF_TEST(SkSLFPSwitchWithoutReturnInsideCanBeInlined, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             half4 switchy(half4 c) {
                 half4 result;
                 switch (int(c.x)) {
                     case 0: result = c.yyyy;
                 }
                 result = c.zzzz;
                 return result;
             }
             void main() {
                 sk_OutColor = switchy(color);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
         R"__Cpp__(fragBuilder->codeAppendf(
R"SkSL(half4 _inlineResulthalf4switchyhalf40;
half4 _inlineArghalf4switchyhalf41_0 = %s;
{
    half4 result;
    switch (int(_inlineArghalf4switchyhalf41_0.x)) {
        case 0:
            result = _inlineArghalf4switchyhalf41_0.yyyy;
    }
    result = _inlineArghalf4switchyhalf41_0.zzzz;
    _inlineResulthalf4switchyhalf40 = result;
}
%s = _inlineResulthalf4switchyhalf40;

)SkSL"
, args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor);
)__Cpp__",
         });
}

DEF_TEST(SkSLFPForLoopWithReturnInsideCannotBeInlined, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             half4 loopy(half4 c) {
                 for (int x=0; x<5; ++x) {
                     if (x == int(c.w)) return c.yyyy;
                 }
                 return c.zzzz;
             }
             void main() {
                 sk_OutColor = loopy(color);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
         R"__Cpp__(fragBuilder->emitFunction(kHalf4_GrSLType, "loopy", 1, loopy_args,
R"SkSL(for (int x = 0;x < 5; ++x) {
    if (x == int(c.w)) return c.yyyy;
}
return c.zzzz;
)SkSL", &loopy_name);
        fragBuilder->codeAppendf(
R"SkSL(%s = %s(%s);
)SkSL"
, args.fOutputColor, loopy_name.c_str(), args.fUniformHandler->getUniformCStr(colorVar));
)__Cpp__",
         });
}

DEF_TEST(SkSLFPSwitchWithCastCanBeInlined, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             half4 switchy(half4 c) {
                 half4 result;
                 switch (int(c.x)) {
                     case 1: result = c.yyyy; break;
                     default: result = c.zzzz; break;
                 }
                 return result;
             }
             void main() {
                 sk_OutColor = switchy(color);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{R"__Cpp__(fragBuilder->codeAppendf(
R"SkSL(half4 _inlineResulthalf4switchyhalf40;
half4 _inlineArghalf4switchyhalf41_0 = %s;
{
    half4 result;
    switch (int(_inlineArghalf4switchyhalf41_0.x)) {
        case 1:
            result = _inlineArghalf4switchyhalf41_0.yyyy;
            break;
        default:
            result = _inlineArghalf4switchyhalf41_0.zzzz;
            break;
    }
    _inlineResulthalf4switchyhalf40 = result;
}
%s = _inlineResulthalf4switchyhalf40;

)SkSL"
, args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor);
)__Cpp__"});
}

DEF_TEST(SkSLFPForLoopWithoutReturnInsideCanBeInlined, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             half4 loopy(half4 c) {
                 half4 pix;
                 for (int x=0; x<5; ++x) {
                     if (x == int(c.w)) pix = c.yyyy;
                 }
                 pix = c.zzzz;
                 return pix;
             }
             void main() {
                 sk_OutColor = loopy(color);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
         R"__Cpp__(fragBuilder->codeAppendf(
R"SkSL(half4 _inlineResulthalf4loopyhalf40;
half4 _inlineArghalf4loopyhalf41_0 = %s;
{
    half4 pix;
    for (int x = 0;x < 5; ++x) {
        if (x == int(_inlineArghalf4loopyhalf41_0.w)) pix = _inlineArghalf4loopyhalf41_0.yyyy;
    }
    pix = _inlineArghalf4loopyhalf41_0.zzzz;
    _inlineResulthalf4loopyhalf40 = pix;
}
%s = _inlineResulthalf4loopyhalf40;

)SkSL"
, args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor);
)__Cpp__",
         });
}

DEF_TEST(SkSLFPIfStatementWithReturnInsideCanBeInlined, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             half4 branchy(half4 c) {
                 if (c.z == c.w) return c.yyyy; else return c.zzzz;
             }
             void main() {
                 sk_OutColor = branchy(color);
             }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
         R"__Cpp__(fragBuilder->codeAppendf(
R"SkSL(half4 _inlineResulthalf4branchyhalf40;
half4 _inlineArghalf4branchyhalf41_0 = %s;
do {
    if (_inlineArghalf4branchyhalf41_0.z == _inlineArghalf4branchyhalf41_0.w) {
        _inlineResulthalf4branchyhalf40 = _inlineArghalf4branchyhalf41_0.yyyy;
        break;
    } else {
        _inlineResulthalf4branchyhalf40 = _inlineArghalf4branchyhalf41_0.zzzz;
        break;
    }
} while (false);
%s = _inlineResulthalf4branchyhalf40;

)SkSL"
, args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor);
)__Cpp__",
         });
}

DEF_TEST(SkSLFPGrSLTypesAreSupported, r) {
    // We thwart the optimizer by wrapping our return statement in a loop, which prevents inlining.
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             int test(int a) { for (;;) { return a; } }
             void main() { sk_OutColor = test(1).xxxx; }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            R"__Cpp__(const GrShaderVar test_args[] = { GrShaderVar("a", kInt_GrSLType)};)__Cpp__",
            R"__Cpp__(fragBuilder->emitFunction(kInt_GrSLType, "test", 1, test_args,)__Cpp__",
         });
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             int2 test(int2 a) { for (;;) { return a; } }
             void main() { sk_OutColor = test(int2(1)).xyxy; }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            R"__Cpp__(const GrShaderVar test_args[] = { GrShaderVar("a", kInt2_GrSLType)};)__Cpp__",
            R"__Cpp__(fragBuilder->emitFunction(kInt2_GrSLType, "test", 1, test_args,)__Cpp__",
         });
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             int3 test(int3 a) { for (;;) { return a; } }
             void main() { sk_OutColor = test(int3(1)).xyzx; }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            R"__Cpp__(const GrShaderVar test_args[] = { GrShaderVar("a", kInt3_GrSLType)};)__Cpp__",
            R"__Cpp__(fragBuilder->emitFunction(kInt3_GrSLType, "test", 1, test_args,)__Cpp__",
         });
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             int4 test(int4 a) { for (;;) { return a; } }
             void main() { sk_OutColor = test(int4(1)); }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            R"__Cpp__(const GrShaderVar test_args[] = { GrShaderVar("a", kInt4_GrSLType)};)__Cpp__",
            R"__Cpp__(fragBuilder->emitFunction(kInt4_GrSLType, "test", 1, test_args,)__Cpp__",
         });
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             half3x4 test(float3x4 a) { for (;;) { return half3x4(a); } }
             void main() { sk_OutColor = test(float3x4(0))[0]; }
         )__SkSL__",
         /*expectedH=*/{},
         /*expectedCPP=*/{
            R"__Cpp__(const GrShaderVar test_args[] = { GrShaderVar("a", kFloat3x4_GrSLType)};)__Cpp__",
            R"__Cpp__(fragBuilder->emitFunction(kHalf3x4_GrSLType, "test", 1, test_args,)__Cpp__",
         });
}

DEF_TEST(SkSLFPMatrixSampleConstant, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor? child;
             void main() {
                 sk_OutColor = sample(child, float3x3(2));
             }
         )__SkSL__",
         /*expectedH=*/{
             "this->registerChild(std::move(child), "
                    "SkSL::SampleUsage::UniformMatrix(\"float3x3(2.0)\", true));"
         },
         /*expectedCPP=*/{
             "this->invokeChildWithMatrix(0, args)"
         });
}

DEF_TEST(SkSLFPMatrixSampleUniform, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor? child;
             uniform float3x3 matrix;
             void main() {
                 sk_OutColor = sample(child, matrix);
             }
         )__SkSL__",
         /*expectedH=*/{
             // Since 'matrix' is just a uniform, the generated code can't determine perspective.
             "this->registerChild(std::move(child), "
                    "SkSL::SampleUsage::UniformMatrix(\"matrix\", true));"
         },
         /*expectedCPP=*/{
             "this->invokeChildWithMatrix(0, args)"
         });
}

DEF_TEST(SkSLFPMatrixSampleInUniform, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor? child;
             in uniform float3x3 matrix;
             void main() {
                 sk_OutColor = sample(child, matrix);
             }
         )__SkSL__",
         /*expectedH=*/{
             // Since 'matrix' is marked 'in', we can detect perspective at runtime
             "this->registerChild(std::move(child), "
                    "SkSL::SampleUsage::UniformMatrix(\"matrix\", matrix.hasPerspective()));"
         },
         /*expectedCPP=*/{
             "this->invokeChildWithMatrix(0, args)"
         });
}

DEF_TEST(SkSLFPMatrixSampleMultipleInUniforms, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor? child;
             in uniform float3x3 matrixA;
             in uniform float3x3 matrixB;
             void main() {
                 sk_OutColor = sample(child, matrixA);
                 sk_OutColor += sample(child, matrixB);
             }
         )__SkSL__",
         /*expectedH=*/{
             // FIXME it would be nice if codegen can produce
             // (matrixA.hasPerspective() || matrixB.hasPerspective()) even though it's variable.
             "this->registerChild(std::move(child), "
                    "SkSL::SampleUsage::VariableMatrix(true));"
         },
         /*expectedCPP=*/{
             "SkString _matrix191(args.fUniformHandler->getUniformCStr(matrixAVar));",
             "this->invokeChildWithMatrix(0, args, _matrix191.c_str());",
             "SkString _matrix247(args.fUniformHandler->getUniformCStr(matrixBVar));",
             "this->invokeChildWithMatrix(0, args, _matrix247.c_str());"
         });
}

DEF_TEST(SkSLFPMatrixSampleConstUniformExpression, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor? child;
             uniform float3x3 matrix;
             void main() {
                 sk_OutColor = sample(child, 0.5 * matrix);
             }
         )__SkSL__",
         /*expectedH=*/{
             // FIXME: "0.5 * matrix" is a uniform expression and could be lifted to the vertex
             // shader, once downstream code is able to properly map 'matrix' within the expression.
             "this->registerChild(std::move(child), "
                    "SkSL::SampleUsage::VariableMatrix(true));"
         },
         /*expectedCPP=*/{
            "SkString _matrix145 = SkStringPrintf(\"0.5 * %s\", "
                    "args.fUniformHandler->getUniformCStr(matrixVar));",
             "this->invokeChildWithMatrix(0, args, _matrix145.c_str());"
         });
}

DEF_TEST(SkSLFPMatrixSampleConstantAndExplicitly, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             in fragmentProcessor? child;
             void main(float2 coord) {
                 sk_OutColor = sample(child, float3x3(0.5));
                 sk_OutColor = sample(child, coord / 2);
             }
         )__SkSL__",
         /*expectedH=*/{
             "this->registerChild(std::move(child), "
                    "SkSL::SampleUsage(SkSL::SampleUsage::Kind::kUniform, \"float3x3(0.5)\", true, true, false));"
         },
         /*expectedCPP=*/{
             "this->invokeChildWithMatrix(0, args)",
             "SkString _coords180 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);",
             "this->invokeChild(0, args, _coords180.c_str())",
         });
}

DEF_TEST(SkSLFPMatrixSampleVariableAndExplicitly, r) {
    test(r,
         *SkSL::ShaderCapsFactory::Default(),
         R"__SkSL__(
             uniform half4 color;
             in fragmentProcessor? child;
             void main(float2 coord) {
                 float3x3 matrix = float3x3(color.a);
                 sk_OutColor = sample(child, matrix);
                 sk_OutColor = sample(child, coord / 2);
             }
         )__SkSL__",
         /*expectedH=*/{
             "this->registerChild(std::move(child), "
                    "SkSL::SampleUsage(SkSL::SampleUsage::Kind::kVariable, \"\", true, true, false));"
         },
         /*expectedCPP=*/{
             R"__Cpp__(
        colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, kHalf4_GrSLType, "color");
        fragBuilder->codeAppendf(
R"SkSL(float3x3 matrix = float3x3(float(%s.w));)SkSL"
, args.fUniformHandler->getUniformCStr(colorVar));
        SkString _matrix207("matrix");
        SkString _sample207 = this->invokeChildWithMatrix(0, args, _matrix207.c_str());
        fragBuilder->codeAppendf(
R"SkSL(
%s = %s;)SkSL"
, args.fOutputColor, _sample207.c_str());
        SkString _coords261 = SkStringPrintf("%s / 2.0", args.fSampleCoord);
        SkString _sample261 = this->invokeChild(0, args, _coords261.c_str());
        fragBuilder->codeAppendf(
R"SkSL(
%s = %s;
)SkSL"
, args.fOutputColor, _sample261.c_str());
)__Cpp__"
         });
}
