/* * 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 char* src, const GrShaderCaps& caps, std::vector expectedH, std::vector expectedCPP) { SkSL::Program::Settings settings; settings.fCaps = ∩︀ 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\nexpected:\n'%s'\n\nreceived:\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\nexpected:\n'%s'\n\nreceived:\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, "/* HEADER */" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), { "/* HEADER */\n" "\n" "/**************************************************************************************************\n" " *** This file was autogenerated from GrTest.fp; do not modify.\n" " **************************************************************************************************/\n" "#ifndef GrTest_DEFINED\n" "#define GrTest_DEFINED\n" "#include \"include/core/SkTypes.h\"\n" "#include \"include/private/SkM44.h\"\n\n" "#include \"src/gpu/GrCoordTransform.h\"\n" "#include \"src/gpu/GrFragmentProcessor.h\"\n" "class GrTest : public GrFragmentProcessor {\n" "public:\n" " static std::unique_ptr Make() {\n" " return std::unique_ptr(new GrTest());\n" " }\n" " GrTest(const GrTest& src);\n" " std::unique_ptr clone() const override;\n" " const char* name() const override { return \"Test\"; }\n" "private:\n" " GrTest()\n" " : INHERITED(kGrTest_ClassID, kNone_OptimizationFlags) {\n" " }\n" " GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;\n" " void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) const " "override;\n" " bool onIsEqual(const GrFragmentProcessor&) const override;\n" " GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n" " typedef GrFragmentProcessor INHERITED;\n" "};\n" "#endif\n" }, { "/**************************************************************************************************\n" " *** This file was autogenerated from GrTest.fp; do not modify.\n" " **************************************************************************************************/\n" "#include \"GrTest.h\"\n\n" "#include \"include/gpu/GrTexture.h\"\n" "#include \"src/gpu/glsl/GrGLSLFragmentProcessor.h\"\n" "#include \"src/gpu/glsl/GrGLSLFragmentShaderBuilder.h\"\n" "#include \"src/gpu/glsl/GrGLSLProgramBuilder.h\"\n" "#include \"src/sksl/SkSLCPP.h\"\n" "#include \"src/sksl/SkSLUtil.h\"\n" "class GrGLSLTest : public GrGLSLFragmentProcessor {\n" "public:\n" " GrGLSLTest() {}\n" " void emitCode(EmitArgs& args) override {\n" " GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;\n" " const GrTest& _outer = args.fFp.cast();\n" " (void) _outer;\n" " fragBuilder->codeAppendf(\"%s = half4(1.0);\\n\", args.fOutputColor);\n" " }\n" "private:\n" " void onSetData(const GrGLSLProgramDataManager& pdman, " "const GrFragmentProcessor& _proc) override {\n" " }\n" "};\n" "GrGLSLFragmentProcessor* GrTest::onCreateGLSLInstance() const {\n" " return new GrGLSLTest();\n" "}\n" "void GrTest::onGetGLSLProcessorKey(const GrShaderCaps& caps, " "GrProcessorKeyBuilder* b) const {\n" "}\n" "bool GrTest::onIsEqual(const GrFragmentProcessor& other) const {\n" " const GrTest& that = other.cast();\n" " (void) that;\n" " return true;\n" "}\n" "GrTest::GrTest(const GrTest& src)\n" ": INHERITED(kGrTest_ClassID, src.optimizationFlags()) {\n" "}\n" "std::unique_ptr GrTest::clone() const {\n" " return std::unique_ptr(new GrTest(*this));\n" "}\n" }); } DEF_TEST(SkSLFPInput, r) { test(r, "layout(key) in half2 point;" "void main() {" "sk_OutColor = half4(point, point);" "}", *SkSL::ShaderCapsFactory::Default(), { "static std::unique_ptr Make(SkPoint point) {", "return std::unique_ptr(new GrTest(point));", "GrTest(SkPoint point)", ", point(point)" }, { "fragBuilder->codeAppendf(\"%s = half4(half2(%f, %f), half2(%f, %f));\\n\", " "args.fOutputColor, _outer.point.fX, _outer.point.fY, " "_outer.point.fX, _outer.point.fY);", "if (point != that.point) return false;" }); } DEF_TEST(SkSLFPUniform, r) { test(r, "uniform half4 color;" "void main() {" "sk_OutColor = color;" "}", *SkSL::ShaderCapsFactory::Default(), { "static std::unique_ptr Make()" }, { "colorVar = args.fUniformHandler->addUniform(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, "in uniform half4 color;" "void main() {" "sk_OutColor = color;" "}", *SkSL::ShaderCapsFactory::Default(), { "static std::unique_ptr Make(SkRect color) {", }, { "colorVar = args.fUniformHandler->addUniform(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, "layout(ctype=SkPMColor4f) in uniform half4 color;" "void main() {" "sk_OutColor = color;" "}", *SkSL::ShaderCapsFactory::Default(), { "static std::unique_ptr Make(SkPMColor4f color) {", }, { "colorVar = args.fUniformHandler->addUniform(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, "layout(tracked) in uniform half4 color;" "void main() {" "sk_OutColor = color;" "}", *SkSL::ShaderCapsFactory::Default(), { "static std::unique_ptr Make(SkRect color) {", }, { "SkRect colorPrev = SkRect::MakeEmpty();", "colorVar = args.fUniformHandler->addUniform(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, "in uniform half2 point;" "void main() {" "sk_OutColor = half4(point, point);" "}", *SkSL::ShaderCapsFactory::Default(), { "static std::unique_ptr Make(SkPoint point) {", }, { "pointVar = args.fUniformHandler->addUniform(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, "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::ShaderCapsFactory::Default(), { "static std::unique_ptr Make(bool test, SkPMColor4f color) {", }, { "SkPMColor4f colorPrev = {SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}", "auto test = _outer.test;", "if (test) {", "colorVar = args.fUniformHandler->addUniform(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, "@header { header section }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), { "header section" }, {}); test(r, "@class { class section }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), { "class GrTest : public GrFragmentProcessor {\n" "public:\n" " class section" }, {}); test(r, "@cpp { cpp section }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), {}, {"cpp section"}); test(r, "@constructorParams { int x, float y, std::vector z }" "in float w;" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), { "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) {" }, {}); test(r, "@constructor { constructor section }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), { "private:\n constructor section" }, {}); test(r, "@initializers { initializers section }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), { ": INHERITED(kGrTest_ClassID, kNone_OptimizationFlags)\n , initializers section" }, {}); test(r, "half x = 10;" "@emitCode { fragBuilder->codeAppendf(\"half y = %d\\n\", x * 2); }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), {}, { "x = 10.0;\n" " fragBuilder->codeAppendf(\"half y = %d\\n\", x * 2);" }); test(r, "@fields { fields section }" "@clone { }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), { "const char* name() const override { return \"Test\"; }\n" " fields section private:" }, {}); test(r, "@make { make section }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), { "public:\n" " make section" }, {}); test(r, "uniform half calculated;" "layout(key) in half provided;" "@setData(varName) { varName.set1f(calculated, provided * 2); }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), {}, { "void onSetData(const GrGLSLProgramDataManager& varName, " "const GrFragmentProcessor& _proc) override {\n", "UniformHandle& calculated = calculatedVar;", "auto provided = _outer.provided;", "varName.set1f(calculated, provided * 2);" }); test(r, "@test(testDataName) { testDataName section }" "void main() {" "sk_OutColor = half4(1);" "}", *SkSL::ShaderCapsFactory::Default(), {}, { "#if GR_TEST_UTILS\n" "std::unique_ptr GrTest::TestCreate(GrProcessorTestData* testDataName) {\n" " testDataName section }\n" "#endif" }); } DEF_TEST(SkSLFPTransformedCoords, r) { test(r, "void main() {" "sk_OutColor = half4(sk_TransformedCoords2D[0], sk_TransformedCoords2D[0]);" "}", *SkSL::ShaderCapsFactory::Default(), {}, { "SkString sk_TransformedCoords2D_0 = " "fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);", "fragBuilder->codeAppendf(\"%s = half4(%s, %s);\\n\", args.fOutputColor, " "sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str());" }); } DEF_TEST(SkSLFPLayoutWhen, r) { test(r, "layout(when=someExpression(someOtherExpression())) uniform half sometimes;" "void main() {" "}", *SkSL::ShaderCapsFactory::Default(), {}, { "if (someExpression(someOtherExpression())) {\n" " sometimesVar = args.fUniformHandler->addUniform" }); } DEF_TEST(SkSLFPChildProcessors, r) { test(r, "in fragmentProcessor child1;" "in fragmentProcessor child2;" "void main() {" " sk_OutColor = sample(child1) * sample(child2);" "}", *SkSL::ShaderCapsFactory::Default(), { "this->registerChildProcessor(std::move(child1));", "this->registerChildProcessor(std::move(child2));" }, { "SkString _sample93;\n", "_sample93 = this->invokeChild(_outer.child1_index, args);\n", "SkString _sample110;\n", "_sample110 = this->invokeChild(_outer.child2_index, args);\n", "fragBuilder->codeAppendf(\"%s = %s * %s;\\n\", args.fOutputColor, _sample93.c_str(), " "_sample110.c_str());\n", "this->registerChildProcessor(src.childProcessor(child1_index).clone());", "this->registerChildProcessor(src.childProcessor(child2_index).clone());" }); } DEF_TEST(SkSLFPChildProcessorsWithInput, r) { test(r, "in fragmentProcessor child1;" "in fragmentProcessor child2;" "void main() {" " half4 childIn = sk_InColor;" " half4 childOut1 = sample(child1, childIn);" " half4 childOut2 = sample(child2, childOut1);" " sk_OutColor = childOut2;" "}", *SkSL::ShaderCapsFactory::Default(), { "this->registerChildProcessor(std::move(child1));", "this->registerChildProcessor(std::move(child2));" }, { "SkString _input128(\"childIn\");", "SkString _sample128;", "_sample128 = this->invokeChild(_outer.child1_index, _input128.c_str(), args);", "fragBuilder->codeAppendf(\"\\nhalf4 childOut1 = %s;\", _sample128.c_str());", "SkString _input174(\"childOut1\");", "SkString _sample174;", "_sample174 = this->invokeChild(_outer.child2_index, _input174.c_str(), args);", "this->registerChildProcessor(src.childProcessor(child1_index).clone());", "this->registerChildProcessor(src.childProcessor(child2_index).clone());" }); } DEF_TEST(SkSLFPChildProcessorWithInputExpression, r) { test(r, "in fragmentProcessor child;" "void main() {" " sk_OutColor = sample(child, sk_InColor * half4(0.5));" "}", *SkSL::ShaderCapsFactory::Default(), { "this->registerChildProcessor(std::move(child));", }, { "SkString _input64 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);", "SkString _sample64;", "_sample64 = this->invokeChild(_outer.child_index, _input64.c_str(), args);", "fragBuilder->codeAppendf(\"%s = %s;\\n\", args.fOutputColor, _sample64.c_str());", "this->registerChildProcessor(src.childProcessor(child_index).clone());", }); } DEF_TEST(SkSLFPNestedChildProcessors, r) { test(r, "in fragmentProcessor child1;" "in fragmentProcessor child2;" "void main() {" " sk_OutColor = sample(child2, sk_InColor * sample(child1, sk_InColor * half4(0.5)));" "}", *SkSL::ShaderCapsFactory::Default(), { "this->registerChildProcessor(std::move(child1));", "this->registerChildProcessor(std::move(child2));" }, { "SkString _input121 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);", "SkString _sample121;", "_sample121 = this->invokeChild(_outer.child1_index, _input121.c_str(), args);", "SkString _input93 = SkStringPrintf(\"%s * %s\", args.fInputColor, _sample121.c_str());", "SkString _sample93;", "_sample93 = this->invokeChild(_outer.child2_index, _input93.c_str(), args);", "fragBuilder->codeAppendf(\"%s = %s;\\n\", args.fOutputColor, _sample93.c_str());", "this->registerChildProcessor(src.childProcessor(child1_index).clone());", "this->registerChildProcessor(src.childProcessor(child2_index).clone());" }); } DEF_TEST(SkSLFPChildFPAndGlobal, r) { test(r, "in fragmentProcessor child;" "bool hasCap = sk_Caps.externalTextureSupport;" "void main() {" " if (hasCap) {" " sk_OutColor = sample(child, sk_InColor);" " } else {" " sk_OutColor = half4(1);" " }" "}", *SkSL::ShaderCapsFactory::Default(), { "this->registerChildProcessor(std::move(child));" }, { "hasCap = sk_Caps.externalTextureSupport;", "fragBuilder->codeAppendf(\"bool hasCap = %s;\\nif (hasCap) {\", (hasCap ? \"true\" : " "\"false\"));", "SkString _input130 = SkStringPrintf(\"%s\", args.fInputColor);", "SkString _sample130;", "_sample130 = this->invokeChild(_outer.child_index, _input130.c_str(), args);", "fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(1.0);\\n}\\n\"," " args.fOutputColor, _sample130.c_str(), args.fOutputColor);", "this->registerChildProcessor(src.childProcessor(child_index).clone());" }); } DEF_TEST(SkSLFPChildProcessorInlineFieldAccess, r) { test(r, "in fragmentProcessor child;" "void main() {" " if (child.preservesOpaqueInput) {" " sk_OutColor = sample(child, sk_InColor);" " } else {" " sk_OutColor = half4(1);" " }" "}", *SkSL::ShaderCapsFactory::Default(), { "this->registerChildProcessor(std::move(child));" }, { "fragBuilder->codeAppendf(\"if (%s) {\", " "(_outer.childProcessor(_outer.child_index).preservesOpaqueInput() ? ", "SkString _input105 = SkStringPrintf(\"%s\", args.fInputColor);", "SkString _sample105;", "_sample105 = this->invokeChild(_outer.child_index, _input105.c_str(), args);", "fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(1.0);\\n}\\n\"," " args.fOutputColor, _sample105.c_str(), args.fOutputColor);", "this->registerChildProcessor(src.childProcessor(child_index).clone());" }); } DEF_TEST(SkSLFPChildProcessorFieldAccess, r) { test(r, "in fragmentProcessor child;" "bool opaque = child.preservesOpaqueInput;" "void main() {" " if (opaque) {" " sk_OutColor = sample(child);" " } else {" " sk_OutColor = half4(0.5);" " }" "}", *SkSL::ShaderCapsFactory::Default(), { "this->registerChildProcessor(std::move(child));" }, { "opaque = _outer.childProcessor(_outer.child_index).preservesOpaqueInput();", "fragBuilder->codeAppendf(\"bool opaque = %s;\\nif (opaque) {\", (opaque ? \"true\" : " "\"false\"));", "SkString _sample126;", "_sample126 = this->invokeChild(_outer.child_index, args);", "fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(0.5);\\n}\\n\"," " args.fOutputColor, _sample126.c_str(), args.fOutputColor);", "this->registerChildProcessor(src.childProcessor(child_index).clone());" }); } DEF_TEST(SkSLFPNullableChildProcessor, r) { test(r, "in fragmentProcessor? child;" "void main() {" " if (child != null) {" " sk_OutColor = sample(child);" " } else {" " sk_OutColor = half4(0.5);" " }" "}", *SkSL::ShaderCapsFactory::Default(), {}, { "fragBuilder->codeAppendf(\"if (%s) {\", _outer.child_index >= 0 ? \"true\" : " "\"false\");", "SkString _sample93;", "if (_outer.child_index >= 0) {", "_sample93 = this->invokeChild(_outer.child_index, args);", "}", "fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(0.5);\\n}\\n\"," " args.fOutputColor, _sample93.c_str(), args.fOutputColor);" }); } DEF_TEST(SkSLFPBadIn, r) { test_failure(r, "in half4 c;" "void main() {" " sk_OutColor = c;" "}", "error: 1: 'in' variable must be either 'uniform' or 'layout(key)', or there must be a " "custom @setData function\n1 error\n"); } DEF_TEST(SkSLFPSampleCoords, r) { test(r, "in fragmentProcessor child;" "@coordTransform { SkMatrix() }" "void main() {" " sk_OutColor = sample(child) + sample(child, sk_TransformedCoords2D[0] / 2);" "}", *SkSL::ShaderCapsFactory::Default(), {}, { "SkString _sample94;\n", "_sample94 = this->invokeChild(_outer.child_index, args);\n", "SkString _sample110;\n", "SkString sk_TransformedCoords2D_0 = fragBuilder->ensureCoords2D(" "args.fTransformedCoords[0].fVaryingPoint);\n", "SkString _coords110 = SkStringPrintf(\"%s / 2.0\", " "sk_TransformedCoords2D_0.c_str());\n", "_sample110 = this->invokeChild(_outer.child_index, args, _coords110.c_str());\n", "fragBuilder->codeAppendf(\"%s = %s + %s;\\n\", args.fOutputColor, _sample94.c_str(), " "_sample110.c_str());\n" }); } DEF_TEST(SkSLFPFunction, r) { test(r, "in fragmentProcessor? child;" "half4 flip(half4 c) { return c.abgr; }" "void main() {" " sk_OutColor = flip(sk_InColor);" "}", *SkSL::ShaderCapsFactory::Default(), {}, { "SkString flip_name;", "const GrShaderVar flip_args[] = { GrShaderVar(\"c\", kHalf4_GrSLType)};", "fragBuilder->emitFunction(kHalf4_GrSLType, \"flip\", 1, flip_args, " "\"return c.wzyx;\\n\", &flip_name);", "fragBuilder->codeAppendf(\"%s = %s(%s);\\n\", args.fOutputColor, flip_name.c_str(), " "args.fInputColor);" }); }