/* * 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); } 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"; } 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; } 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(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=*/{ "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 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__" }); } 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);", }); }