/* * 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 expectedH, std::vector expectedCPP) { SkSL::Program::Settings settings; settings.fCaps = ∩︀ settings.fRemoveDeadFunctions = false; SkSL::Compiler compiler; SkSL::StringStream output; std::unique_ptr 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 caps = SkSL::ShaderCapsFactory::Default(); settings.fCaps = caps.get(); std::unique_ptr program = compiler.convertProgram( SkSL::Program::kFragmentProcessor_Kind, SkSL::String(src), settings); if (!compiler.errorCount()) { compiler.optimize(*program); } if (!compiler.errorCount()) { SkSL::StringStream output; compiler.toH(*program, "Test", output); } if (!compiler.errorCount()) { SkSL::StringStream output; compiler.toCPP(*program, "Test", output); } 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 Make() { return std::unique_ptr(new GrTest()); } GrTest(const GrTest& src); std::unique_ptr clone() const override; const char* name() const override { return "Test"; } bool usesExplicitReturn() const override; 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(); (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(); (void) that; return true; } bool GrTest::usesExplicitReturn() const { return false; } GrTest::GrTest(const GrTest& src) : INHERITED(kGrTest_ClassID, src.optimizationFlags()) { this->cloneAndRegisterAllChildProcessors(src); } std::unique_ptr GrTest::clone() const { return std::make_unique(*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 Make(SkPoint point) {", "return std::unique_ptr(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 Make(float value) { return std::unique_ptr(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(value)); } )__Cpp__", R"__Cpp__(bool GrTest::onIsEqual(const GrFragmentProcessor& other) const { const GrTest& that = other.cast(); (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 GrTest::clone() const { return std::make_unique(*this); } )__Cpp__", R"__Cpp__(#if GR_TEST_UTILS SkString GrTest::onDumpInfo() const { return SkStringPrintf("(value=%f)", value); } )__Cpp__", }); } DEF_TEST(SkSLFPUseExplicitReturn, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( half4 main() { return half4(0, 1, 0, 1); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{R"__Cpp__( fragBuilder->codeAppendf( R"SkSL(return half4(0.0, 1.0, 0.0, 1.0); )SkSL" ); )__Cpp__", R"__Cpp__( bool GrTest::usesExplicitReturn() const { return true; } )__Cpp__"}); } DEF_TEST(SkSLFPBothExplicitReturnAndSkOutColor, r) { test_failure(r, R"__SkSL__( half4 main() { sk_OutColor = half4(1, 0, 1, 0); return half4(0, 1, 0, 1); } )__SkSL__", "error: 4: Fragment processors must not mix sk_OutColor and return statements\n\n" "1 error\n"); } DEF_TEST(SkSLFPCannotReturnWithSkOutColor, r) { // We don't support returns with sk_OutColor at all, even when it conceptually makes sense. // sk_OutColor is going away, so there's no need to add engineering effort to that path. test_failure(r, R"__SkSL__( void main() { sk_OutColor = half4(1, 0, 1, 0); return; } )__SkSL__", "error: 4: Fragment processors must not mix sk_OutColor and return statements\n\n" "1 error\n"); } 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 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 Make(SkRect color) {", }, /*expectedCPP=*/{ "colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, " "kHalf4_GrSLType, \"color\");", "pdman.set4fv(colorVar, 1, reinterpret_cast(&(_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 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 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(&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 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 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 z } in float w; void main() { sk_OutColor = half4(1); } )__SkSL__", /*expectedH=*/{ "Make(float w, int x, float y, std::vector z )", "return std::unique_ptr(new GrTest(w, x, y, z));", "GrTest(float w, int x, float y, std::vector 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=*/{ "bool usesExplicitReturn() const override;\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 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 _0_flip; { _0_flip = %s.wzyx; } %s = _0_flip; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPSwitchWithMultipleReturnsInside, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; half4 switchy(half4 c) { switch (int(c.x)) { case 0: return c.yyyy; default: 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; default: 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(SkSLFPTernaryExpressionsShouldNotInlineResults, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; half count = 0; bool test(half4 v) { return v.x <= 0.5; } half4 trueSide(half4 v) { count += 1; return half4(sin(v.x), sin(v.y), sin(v.z), sin(v.w)); } half4 falseSide(half4 v) { count += 1; return half4(cos(v.y), cos(v.z), cos(v.w), cos(v.z)); } void main() { sk_OutColor = test(color) ? trueSide(color) : falseSide(color); sk_OutColor *= count; } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(SkString trueSide_name; const GrShaderVar trueSide_args[] = { GrShaderVar("v", kHalf4_GrSLType)}; fragBuilder->emitFunction(kHalf4_GrSLType, "trueSide", 1, trueSide_args, R"SkSL(count += 1.0; return half4(sin(v.x), sin(v.y), sin(v.z), sin(v.w)); )SkSL", &trueSide_name); SkString falseSide_name; const GrShaderVar falseSide_args[] = { GrShaderVar("v", kHalf4_GrSLType)}; fragBuilder->emitFunction(kHalf4_GrSLType, "falseSide", 1, falseSide_args, R"SkSL(count += 1.0; return half4(cos(v.y), cos(v.z), cos(v.w), cos(v.z)); )SkSL", &falseSide_name); fragBuilder->codeAppendf( R"SkSL(half count = %f; bool _0_test; { _0_test = %s.x <= 0.5; } %s = _0_test ? %s(%s) : %s(%s); %s *= count; )SkSL" , count, args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor, trueSide_name.c_str(), args.fUniformHandler->getUniformCStr(colorVar), falseSide_name.c_str(), args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPShortCircuitEvaluationsCannotInlineRightHandSide, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; bool testA(half4 v) { return v.x <= 0.5; } bool testB(half4 v) { return v.x > 0.5; } void main() { sk_OutColor = half4(0); if (testA(color) && testB(color)) { sk_OutColor = half4(0.5); } if (testB(color) || testA(color)) { sk_OutColor = half4(1.0); } } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__( SkString testA_name; const GrShaderVar testA_args[] = { GrShaderVar("v", kHalf4_GrSLType)}; fragBuilder->emitFunction(kBool_GrSLType, "testA", 1, testA_args, R"SkSL(return v.x <= 0.5; )SkSL", &testA_name); SkString testB_name; const GrShaderVar testB_args[] = { GrShaderVar("v", kHalf4_GrSLType)}; fragBuilder->emitFunction(kBool_GrSLType, "testB", 1, testB_args, R"SkSL(return v.x > 0.5; )SkSL", &testB_name); fragBuilder->codeAppendf( R"SkSL(%s = half4(0.0); bool _0_testA; { _0_testA = %s.x <= 0.5; } if (_0_testA && %s(%s)) { %s = half4(0.5); } bool _1_testB; { _1_testB = %s.x > 0.5; } if (_1_testB || %s(%s)) { %s = half4(1.0); } )SkSL" , args.fOutputColor, args.fUniformHandler->getUniformCStr(colorVar), testB_name.c_str(), args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor, args.fUniformHandler->getUniformCStr(colorVar), testA_name.c_str(), args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPWhileTestCannotBeInlined, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( bool shouldLoop(half4 v) { return v.x < 0.5; } void main() { sk_OutColor = half4(0); while (shouldLoop(sk_OutColor)) { sk_OutColor += half4(0.125); } } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(SkString shouldLoop_name; const GrShaderVar shouldLoop_args[] = { GrShaderVar("v", kHalf4_GrSLType)}; fragBuilder->emitFunction(kBool_GrSLType, "shouldLoop", 1, shouldLoop_args, R"SkSL(return v.x < 0.5; )SkSL", &shouldLoop_name); fragBuilder->codeAppendf( R"SkSL(%s = half4(0.0); while (%s(%s)) { %s += half4(0.125); } )SkSL" , args.fOutputColor, shouldLoop_name.c_str(), args.fOutputColor, args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPInlinedWhileBodyMustBeInAScope, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( half4 adjust(half4 v) { return v + half4(0.125); } void main() { sk_OutColor = half4(0); while (sk_OutColor.x < 0.5) sk_OutColor = adjust(sk_OutColor); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->emitFunction(kHalf4_GrSLType, "adjust", 1, adjust_args, R"SkSL(return v + half4(0.125); )SkSL", &adjust_name); fragBuilder->codeAppendf( R"SkSL(%s = half4(0.0); while (%s.x < 0.5) { half4 _0_adjust; { _0_adjust = %s + half4(0.125); } %s = _0_adjust; } )SkSL" , args.fOutputColor, args.fOutputColor, args.fOutputColor, args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPDoWhileTestCannotBeInlined, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( bool shouldLoop(half4 v) { return v.x < 0.5; } void main() { sk_OutColor = half4(0); do { sk_OutColor += half4(0.125); } while (shouldLoop(sk_OutColor)); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(SkString shouldLoop_name; const GrShaderVar shouldLoop_args[] = { GrShaderVar("v", kHalf4_GrSLType)}; fragBuilder->emitFunction(kBool_GrSLType, "shouldLoop", 1, shouldLoop_args, R"SkSL(return v.x < 0.5; )SkSL", &shouldLoop_name); fragBuilder->codeAppendf( R"SkSL(%s = half4(0.0); do { %s += half4(0.125); } while (%s(%s)); )SkSL" , args.fOutputColor, args.fOutputColor, shouldLoop_name.c_str(), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPInlinedDoWhileBodyMustBeInAScope, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( half4 adjust(half4 v) { return v + half4(0.125); } void main() { sk_OutColor = half4(0); do sk_OutColor = adjust(sk_OutColor); while (sk_OutColor.x < 0.5); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->emitFunction(kHalf4_GrSLType, "adjust", 1, adjust_args, R"SkSL(return v + half4(0.125); )SkSL", &adjust_name); fragBuilder->codeAppendf( R"SkSL(%s = half4(0.0); do { half4 _0_adjust; { _0_adjust = %s + half4(0.125); } %s = _0_adjust; } while (%s.x < 0.5); )SkSL" , args.fOutputColor, args.fOutputColor, args.fOutputColor, args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPOnlyForInitializerExpressionsCanBeInlined, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( half4 initLoopVar() { return half4(0.0625); } bool shouldLoop(half4 v) { return v.x < 0.5; } half4 grow(half4 v) { return v + half4(0.125); } void main() { for (sk_OutColor = initLoopVar(); shouldLoop(sk_OutColor); sk_OutColor = grow(sk_OutColor)) { } } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(SkString initLoopVar_name; const GrShaderVar initLoopVar_args[] = { }; fragBuilder->emitFunction(kHalf4_GrSLType, "initLoopVar", 0, initLoopVar_args, R"SkSL(return half4(0.0625); )SkSL", &initLoopVar_name); SkString shouldLoop_name; const GrShaderVar shouldLoop_args[] = { GrShaderVar("v", kHalf4_GrSLType)}; fragBuilder->emitFunction(kBool_GrSLType, "shouldLoop", 1, shouldLoop_args, R"SkSL(return v.x < 0.5; )SkSL", &shouldLoop_name); SkString grow_name; const GrShaderVar grow_args[] = { GrShaderVar("v", kHalf4_GrSLType)}; fragBuilder->emitFunction(kHalf4_GrSLType, "grow", 1, grow_args, R"SkSL(return v + half4(0.125); )SkSL", &grow_name); fragBuilder->codeAppendf( R"SkSL(for (%s = half4(0.0625); %s(%s); %s = %s(%s)) { } )SkSL" , args.fOutputColor, shouldLoop_name.c_str(), args.fOutputColor, args.fOutputColor, grow_name.c_str(), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPInlinedForBodyMustBeInAScope, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( half4 adjust(half4 v) { return v + half4(0.125); } void main() { sk_OutColor = half4(0); for (int x=0; x<4; ++x) sk_OutColor = adjust(sk_OutColor); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->codeAppendf( R"SkSL(%s = half4(0.0); for (int x = 0;x < 4; ++x) { half4 _0_adjust; { _0_adjust = %s + half4(0.125); } %s = _0_adjust; } )SkSL" , args.fOutputColor, args.fOutputColor, args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPIfTestsCanBeInlined, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; bool ifTest(half4 v) { return color.x >= 0.5; } void main() { if (ifTest(color)) sk_OutColor = half4(1.0); else sk_OutColor = half4(0.5); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->codeAppendf( R"SkSL(bool _0_ifTest; { _0_ifTest = %s.x >= 0.5; } if (_0_ifTest) %s = half4(1.0); else %s = half4(0.5); )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPInlinedIfBodyMustBeInAScope, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; half4 ifBody() { return color + half4(0.125); } void main() { half4 c = color; if (c.x >= 0.5) c = ifBody(); sk_OutColor = c; } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->codeAppendf( R"SkSL(half4 c = %s; if (c.x >= 0.5) { half4 _0_ifBody; { _0_ifBody = %s + half4(0.125); } c = _0_ifBody; } %s = c; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar)); )__Cpp__"}); } DEF_TEST(SkSLFPInlinedElseBodyMustBeInAScope, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; half4 elseBody() { return color + half4(0.125); } void main() { half4 c = color; if (c.x >= 0.5) ; else c = elseBody(); sk_OutColor = c; } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->codeAppendf( R"SkSL(half4 c = %s; if (c.x >= 0.5) { } else { half4 _0_elseBody; { _0_elseBody = %s + half4(0.125); } c = _0_elseBody; } %s = c; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar)); )__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 _0_switchy; { half4 result; switch (int(%s.x)) { case 0: result = %s.yyyy; } result = %s.zzzz; _0_switchy = result; } %s = _0_switchy; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), 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 _0_switchy; { half4 result; switch (int(%s.x)) { case 1: result = %s.yyyy; break; default: result = %s.zzzz; break; } _0_switchy = result; } %s = _0_switchy; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), 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 _0_loopy; { half4 pix; for (int x = 0;x < 5; ++x) { if (x == int(%s.w)) pix = %s.yyyy; } pix = %s.zzzz; _0_loopy = pix; } %s = _0_loopy; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPInlinerManglesOverlappingNames, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; half add(half a, half b) { half c = a + b; return c; } half mul(half a, half b) { return a * b; } half fma(half a, half b, half c) { return add(mul(a, b), c); } half4 main() { half a = fma(color.x, color.y, color.z); half b = fma(color.y, color.z, color.w); half c = fma(color.z, color.w, color.x); return half4(a, b, mul(c, c), mul(a, mul(b, c))); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{R"__Cpp__(fragBuilder->codeAppendf( R"SkSL(half _2_fma; half _3_a = %s.x; half _4_b = %s.y; half _5_c = %s.z; { half _0_mul; { _0_mul = _3_a * _4_b; } half _1_add; { half c = _0_mul + _5_c; _1_add = c; } _2_fma = _1_add; } half a = _2_fma; half _6_fma; half _7_a = %s.y; half _8_b = %s.z; half _9_c = %s.w; { half _0_mul; { _0_mul = _7_a * _8_b; } half _1_add; { half c = _0_mul + _9_c; _1_add = c; } _6_fma = _1_add; } half b = _6_fma; half _10_fma; half _11_a = %s.z; half _12_b = %s.w; half _13_c = %s.x; { half _0_mul; { _0_mul = _11_a * _12_b; } half _1_add; { half c = _0_mul + _13_c; _1_add = c; } _10_fma = _1_add; } half c = _10_fma; half _14_mul; { _14_mul = c * c; } half _15_mul; { _15_mul = b * c; } half _16_mul; { _16_mul = a * _15_mul; } return half4(a, b, _14_mul, _16_mul); )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar)); )__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 _0_branchy; { if (%s.z == %s.w) _0_branchy = %s.yyyy; else _0_branchy = %s.zzzz; } %s = _0_branchy; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPUnnecessaryBlocksDoNotAffectEarlyReturnDetection, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; half4 blocky(half4 c) { { return c; } } void main() { sk_OutColor = blocky(color); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->codeAppendf( R"SkSL(half4 _0_blocky; { { _0_blocky = %s; } } %s = _0_blocky; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPInlinedEarlyReturnsAreWrappedInDoWhileBlock, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; inline half4 returny(half4 c) { if (c.x > c.y) return c.xxxx; if (c.y > c.z) return c.yyyy; return c.zzzz; } void main() { sk_OutColor = returny(color); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->codeAppendf( R"SkSL(half4 _0_returny; do { if (%s.x > %s.y) { _0_returny = %s.xxxx; break; } if (%s.y > %s.z) { _0_returny = %s.yyyy; break; } { _0_returny = %s.zzzz; break; } } while (false); %s = _0_returny; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fOutputColor); )__Cpp__"}); } DEF_TEST(SkSLFPEarlyReturnDetectionSupportsIfElse, r) { // An if-else statement at the end of a function, with a return as the last statement on all // paths, are not actually "early" returns. The inliner is able to recognize this pattern. test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half4 color; inline half4 branchy(half4 c) { c *= 0.5; if (c.x > 0) return c.xxxx; else if (c.y > 0) return c.yyyy; else if (c.z > 0) return c.zzzz; else return c.wwww; } inline half4 branchyAndBlocky(half4 c) {{{ if (c.x > 0) { half4 d = c * 0.5; return d.xxxx; } else {{{ if (c.x < 0) { return c.wwww; } else { return c.yyyy; } }}} }}} void main() { sk_OutColor = branchy(color) * branchyAndBlocky(color); } )__SkSL__", /*expectedH=*/{}, /*expectedCPP=*/{ R"__Cpp__(fragBuilder->codeAppendf( R"SkSL(half4 _0_branchy; half4 _1_c = %s; { _1_c *= 0.5; if (_1_c.x > 0.0) _0_branchy = _1_c.xxxx; else if (_1_c.y > 0.0) _0_branchy = _1_c.yyyy; else if (_1_c.z > 0.0) _0_branchy = _1_c.zzzz; else _0_branchy = _1_c.wwww; } half4 _2_branchyAndBlocky; { { { if (%s.x > 0.0) { half4 d = %s * 0.5; _2_branchyAndBlocky = d.xxxx; } else { { { if (%s.x < 0.0) { _2_branchyAndBlocky = %s.wwww; } else { _2_branchyAndBlocky = %s.yyyy; } } } } } } } %s = _0_branchy * _2_branchyAndBlocky; )SkSL" , args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), args.fUniformHandler->getUniformCStr(colorVar), 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__" }); } DEF_TEST(SkSLUniformArrays, r) { test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( uniform half scalarArray[4]; uniform half2 pointArray[2]; void main() { sk_OutColor = half4(scalarArray[0] * pointArray[0].x + scalarArray[1] * pointArray[0].y + scalarArray[2] * pointArray[1].x + scalarArray[3] * pointArray[1].y); } )__SkSL__", /*expectedH=*/{ "Make()", }, /*expectedCPP=*/{ "void onSetData(const GrGLSLProgramDataManager& pdman, " "const GrFragmentProcessor& _proc) override {\n }" }); test(r, *SkSL::ShaderCapsFactory::Default(), R"__SkSL__( in uniform half scalarArray[4]; in uniform half2 pointArray[2]; void main() { sk_OutColor = half4(scalarArray[0] * pointArray[0].x + scalarArray[1] * pointArray[0].y + scalarArray[2] * pointArray[1].x + scalarArray[3] * pointArray[1].y); } )__SkSL__", /*expectedH=*/{ "Make(std::array scalarArray, std::array pointArray)", "std::array scalarArray;", "std::array pointArray;", }, /*expectedCPP=*/{ "pdman.set1fv(scalarArrayVar, 4, &(_outer.scalarArray)[0]);", "pdman.set2fv(pointArrayVar, 2, &pointArrayValue[0].fX);", }); }