skia2/tests/SkSLFPTest.cpp
Michael Ludwig 9094f2c8b5 Add fields to fragmentProcessors in sksl code.
Getters for GrFragmentProcessor's optimization flags and state are
exposed as constant fields on the fragmentProcessor variable in sksl.
The CPP code generation then emits extra instructions to invoke the
particular getter when building the final sksl string at runtime.

Bug: skia:
Change-Id: If6bf4f4df6c446fb6331484d36effb1386172918
Reviewed-on: https://skia-review.googlesource.com/152381
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
2018-09-07 17:52:05 +00:00

629 lines
24 KiB
C++

/*
* 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 "SkSLCompiler.h"
#include "Test.h"
static void test(skiatest::Reporter* r, const char* src, const GrShaderCaps& caps,
std::vector<const char*> expectedH, std::vector<const char*> expectedCPP) {
SkSL::Program::Settings settings;
settings.fCaps = &caps;
SkSL::Compiler compiler;
SkSL::StringStream output;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
SkSL::Program::kFragmentProcessor_Kind,
SkSL::String(src),
settings);
if (!program) {
SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
return;
}
REPORTER_ASSERT(r, program);
bool success = compiler.toH(*program, "Test", output);
if (!success) {
SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
}
REPORTER_ASSERT(r, success);
if (success) {
for (const char* expected : expectedH) {
bool found = strstr(output.str().c_str(), expected);
if (!found) {
SkDebugf("HEADER MISMATCH:\nsource:\n%s\n\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);
}
}
}
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 \"SkTypes.h\"\n"
"#include \"GrFragmentProcessor.h\"\n"
"#include \"GrCoordTransform.h\"\n"
"class GrTest : public GrFragmentProcessor {\n"
"public:\n"
" static std::unique_ptr<GrFragmentProcessor> Make() {\n"
" return std::unique_ptr<GrFragmentProcessor>(new GrTest());\n"
" }\n"
" GrTest(const GrTest& src);\n"
" std::unique_ptr<GrFragmentProcessor> 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"
},
{
"/* HEADER */\n"
"\n"
"/**************************************************************************************************\n"
" *** This file was autogenerated from GrTest.fp; do not modify.\n"
" **************************************************************************************************/\n"
"#include \"GrTest.h\"\n"
"#include \"glsl/GrGLSLFragmentProcessor.h\"\n"
"#include \"glsl/GrGLSLFragmentShaderBuilder.h\"\n"
"#include \"glsl/GrGLSLProgramBuilder.h\"\n"
"#include \"GrTexture.h\"\n"
"#include \"SkSLCPP.h\"\n"
"#include \"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<GrTest>();\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<GrTest>();\n"
" (void) that;\n"
" return true;\n"
"}\n"
"GrTest::GrTest(const GrTest& src)\n"
": INHERITED(kGrTest_ClassID, src.optimizationFlags()) {\n"
"}\n"
"std::unique_ptr<GrFragmentProcessor> GrTest::clone() const {\n"
" return std::unique_ptr<GrFragmentProcessor>(new GrTest(*this));\n"
"}\n"
});
}
DEF_TEST(SkSLFPInput, r) {
test(r,
"in half2 point;"
"void main() {"
"sk_OutColor = half4(point, point);"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"const SkPoint& point() const { return fPoint; }",
"static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {",
"return std::unique_ptr<GrFragmentProcessor>(new GrTest(point));",
"GrTest(SkPoint point)",
", fPoint(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 (fPoint != that.fPoint) return false;"
});
}
DEF_TEST(SkSLFPUniform, r) {
test(r,
"uniform half4 color;"
"void main() {"
"sk_OutColor = color;"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"static std::unique_ptr<GrFragmentProcessor> Make()"
},
{
"fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
"kDefault_GrSLPrecision, \"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<GrFragmentProcessor> Make(SkRect color) {",
},
{
"fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
"kDefault_GrSLPrecision, \"color\");",
"pdman.set4fv(fColorVar, 1, reinterpret_cast<const float*>(&(_outer.color())));"
});
}
// As above, but tests in uniform's ability to override the default ctype.
DEF_TEST(SkSLFPInUniformCType, r) {
test(r,
"layout(ctype=GrColor4f) in uniform half4 color;"
"void main() {"
"sk_OutColor = color;"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"static std::unique_ptr<GrFragmentProcessor> Make(GrColor4f color) {",
},
{
"fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
"kDefault_GrSLPrecision, \"color\");",
"pdman.set4fv(fColorVar, 1, (_outer.color()).fRGBA);"
});
}
// 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<GrFragmentProcessor> Make(SkRect color) {",
},
{
"SkRect fColorPrev = SkRect::MakeEmpty();",
"fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
"kDefault_GrSLPrecision, \"color\");",
"const SkRect& colorValue = _outer.color();",
"if (fColorPrev.isEmpty() || fColorPrev != colorValue) {",
"fColorPrev = colorValue;",
"pdman.set4fv(fColorVar, 1, reinterpret_cast<const float*>(&colorValue));"
});
}
// Test the case where the template does not support variable inlining in
// setUniform (i.e. it references the value multiple times).
DEF_TEST(SkSLFPNonInlinedInUniform, r) {
test(r,
"in uniform half2 point;"
"void main() {"
"sk_OutColor = half4(point, point);"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {",
},
{
"fPointVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "
"kDefault_GrSLPrecision, \"point\");",
"const SkPoint& pointValue = _outer.point();",
"pdman.set2f(fPointVar, 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,
"in bool test;"
"layout(ctype=GrColor4f, 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<GrFragmentProcessor> Make(bool test, GrColor4f color) {",
},
{
"GrColor4f fColorPrev = GrColor4f::kIllegalConstructor",
"auto test = _outer.test();",
"if (test) {",
"fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "
"kDefault_GrSLPrecision, \"color\");",
"if (fColorVar.isValid()) {",
"const GrColor4f& colorValue = _outer.color();",
"if (fColorPrev != colorValue) {",
"fColorPrev = colorValue;",
"pdman.set4fv(fColorVar, 1, colorValue.fRGBA);"
});
}
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<float> z }"
"in float w;"
"void main() {"
"sk_OutColor = float4(1);"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"Make(float w, int x, float y, std::vector<float> z )",
"return std::unique_ptr<GrFragmentProcessor>(new GrTest(w, x, y, z));",
"GrTest(float w, int x, float y, std::vector<float> z )",
", fW(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(),
{
"GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n"
" fields section typedef GrFragmentProcessor INHERITED;"
},
{});
test(r,
"@make { make section }"
"void main() {"
"sk_OutColor = half4(1);"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"public:\n"
" make section"
},
{});
test(r,
"uniform half calculated;"
"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 = fCalculatedVar;",
"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<GrFragmentProcessor> 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]);",
"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"
" fSometimesVar = args.fUniformHandler->addUniform"
});
}
DEF_TEST(SkSLFPChildProcessors, r) {
test(r,
"in fragmentProcessor child1;"
"in fragmentProcessor child2;"
"void main() {"
" sk_OutColor = process(child1) * process(child2);"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"this->registerChildProcessor(std::move(child1));",
"this->registerChildProcessor(std::move(child2));"
},
{
"SkString _child0(\"_child0\");",
"this->emitChild(0, &_child0, args);",
"SkString _child1(\"_child1\");",
"this->emitChild(1, &_child1, args);",
"this->registerChildProcessor(src.childProcessor(0).clone());",
"this->registerChildProcessor(src.childProcessor(1).clone());"
});
}
DEF_TEST(SkSLFPChildProcessorsWithInput, r) {
test(r,
"in fragmentProcessor child1;"
"in fragmentProcessor child2;"
"void main() {"
" half4 childIn = sk_InColor;"
" half4 childOut1 = process(child1, childIn);"
" half4 childOut2 = process(child2, childOut1);"
" sk_OutColor = childOut2;"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"this->registerChildProcessor(std::move(child1));",
"this->registerChildProcessor(std::move(child2));"
},
{
"SkString _input0(\"childIn\");",
"SkString _child0(\"_child0\");",
"this->emitChild(0, _input0.c_str(), &_child0, args);",
"SkString _input1(\"childOut1\");",
"SkString _child1(\"_child1\");",
"this->emitChild(1, _input1.c_str(), &_child1, args);",
"this->registerChildProcessor(src.childProcessor(0).clone());",
"this->registerChildProcessor(src.childProcessor(1).clone());"
});
}
DEF_TEST(SkSLFPChildProcessorWithInputExpression, r) {
test(r,
"in fragmentProcessor child;"
"void main() {"
" sk_OutColor = process(child, sk_InColor * half4(0.5));"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"this->registerChildProcessor(std::move(child));",
},
{
"SkString _input0 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);",
"SkString _child0(\"_child0\");",
"this->emitChild(0, _input0.c_str(), &_child0, args);",
"this->registerChildProcessor(src.childProcessor(0).clone());",
});
}
DEF_TEST(SkSLFPNestedChildProcessors, r) {
test(r,
"in fragmentProcessor child1;"
"in fragmentProcessor child2;"
"void main() {"
" sk_OutColor = process(child2, sk_InColor * process(child1, sk_InColor * half4(0.5)));"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"this->registerChildProcessor(std::move(child1));",
"this->registerChildProcessor(std::move(child2));"
},
{
"SkString _input0 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);",
"SkString _child0(\"_child0\");",
"this->emitChild(0, _input0.c_str(), &_child0, args);",
"SkString _input1 = SkStringPrintf(\"%s * %s\", args.fInputColor, _child0.c_str());",
"SkString _child1(\"_child1\");",
"this->emitChild(1, _input1.c_str(), &_child1, args);",
"this->registerChildProcessor(src.childProcessor(0).clone());",
"this->registerChildProcessor(src.childProcessor(1).clone());"
});
}
DEF_TEST(SkSLFPChildFPAndGlobal, r) {
test(r,
"in fragmentProcessor child;"
"bool hasCap = sk_Caps.externalTextureSupport;"
"void main() {"
" if (hasCap) {"
" sk_OutColor = process(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 _input0 = SkStringPrintf(\"%s\", args.fInputColor);",
"SkString _child0(\"_child0\");",
"this->emitChild(0, _input0.c_str(), &_child0, args);",
"fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(1.0);\\n}"
"\\n\", args.fOutputColor, _child0.c_str(), args.fOutputColor);",
"this->registerChildProcessor(src.childProcessor(0).clone());"
});
}
DEF_TEST(SkSLFPChildProcessorInlineFieldAccess, r) {
test(r,
"in fragmentProcessor child;"
"void main() {"
" if (child.preservesOpaqueInput) {"
" sk_OutColor = process(child, sk_InColor);"
" } else {"
" sk_OutColor = half4(1);"
" }"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"this->registerChildProcessor(std::move(child));"
},
{
"fragBuilder->codeAppendf(\"if (%s) {\", "
"(_outer.childProcessor(0).preservesOpaqueInput() ? \"true\" : \"false\"));",
"SkString _input0 = SkStringPrintf(\"%s\", args.fInputColor);",
"SkString _child0(\"_child0\");",
"this->emitChild(0, _input0.c_str(), &_child0, args);",
"fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(1.0);\\n}\\n\""
", args.fOutputColor, _child0.c_str(), args.fOutputColor);",
"this->registerChildProcessor(src.childProcessor(0).clone());"
});
}
DEF_TEST(SkSLFPChildProcessorFieldAccess, r) {
test(r,
"in fragmentProcessor child;"
"bool opaque = child.preservesOpaqueInput;"
"void main() {"
" if (opaque) {"
" sk_OutColor = process(child);"
" } else {"
" sk_OutColor = half4(0.5);"
" }"
"}",
*SkSL::ShaderCapsFactory::Default(),
{
"this->registerChildProcessor(std::move(child));"
},
{
"opaque = _outer.childProcessor(0).preservesOpaqueInput();",
"fragBuilder->codeAppendf(\"bool opaque = %s;\\nif (opaque) {\", "
"(opaque ? \"true\" : \"false\"));",
"SkString _child0(\"_child0\");",
"this->emitChild(0, &_child0, args);",
"fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(0.5);\\n}\\n\""
", args.fOutputColor, _child0.c_str(), args.fOutputColor);",
"this->registerChildProcessor(src.childProcessor(0).clone());"
});
}