fbe28593e5
At this point, every created instance of GrCoordTransform is an identity so can be removed. Adding it manually using addCoordTransforms() is no different than relying on the (current) implicitly returned coord transform if the FP calls setUsesSampleCoordsDirectly(). Removing the addCoordTransform() lets us enforce that this remains the case and this CL also deletes all of those members that were previously used to provide access to the sample coordinates. As part of this, GrFragmentProcessor.h and many other files no longer need to include GrCoordTransform.h. This exposed a surprising popularity on SkMatrixPriv.h so I updated those files to include that header directly. Technically, a .fp file can still have an @coordTransform section and the sksl generator will try and call addCoordTransform, which will then fail to build. A follow up CL removes that support in .fp generation. Bug: skia:10416 Change-Id: I5e4d2bb49ee6d7e56ac75ca00be5631106fec20b Reviewed-on: https://skia-review.googlesource.com/c/skia/+/299291 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
1013 lines
36 KiB
C++
1013 lines
36 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 "src/sksl/SkSLCompiler.h"
|
|
#include "src/sksl/SkSLStringStream.h"
|
|
|
|
#include "tests/Test.h"
|
|
|
|
static void test(skiatest::Reporter* r, const GrShaderCaps& caps, const char* src,
|
|
std::vector<const char*> expectedH, std::vector<const char*> expectedCPP) {
|
|
SkSL::Program::Settings settings;
|
|
settings.fCaps = ∩︀
|
|
settings.fRemoveDeadFunctions = false;
|
|
SkSL::Compiler compiler;
|
|
SkSL::StringStream output;
|
|
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
|
|
SkSL::Program::kFragmentProcessor_Kind,
|
|
SkSL::String(src),
|
|
settings);
|
|
if (!program) {
|
|
SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
|
|
return;
|
|
}
|
|
REPORTER_ASSERT(r, program);
|
|
bool success = compiler.toH(*program, "Test", output);
|
|
if (!success) {
|
|
SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
|
|
}
|
|
REPORTER_ASSERT(r, success);
|
|
if (success) {
|
|
for (const char* expected : expectedH) {
|
|
bool found = strstr(output.str().c_str(), expected);
|
|
if (!found) {
|
|
SkDebugf("HEADER MISMATCH:\nsource:\n%s\n\n"
|
|
"header expected:\n'%s'\n\n"
|
|
"header received:\n'%s'",
|
|
src, expected, output.str().c_str());
|
|
}
|
|
REPORTER_ASSERT(r, found);
|
|
}
|
|
}
|
|
output.reset();
|
|
success = compiler.toCPP(*program, "Test", output);
|
|
if (!success) {
|
|
SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
|
|
}
|
|
REPORTER_ASSERT(r, success);
|
|
if (success) {
|
|
for (const char* expected : expectedCPP) {
|
|
bool found = strstr(output.str().c_str(), expected);
|
|
if (!found) {
|
|
SkDebugf("CPP MISMATCH:\nsource:\n%s\n\n"
|
|
"cpp expected:\n'%s'\n\n"
|
|
"cpp received:\n'%s'",
|
|
src, expected, output.str().c_str());
|
|
}
|
|
REPORTER_ASSERT(r, found);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void test_failure(skiatest::Reporter* r, const char* src, const char* error) {
|
|
SkSL::Compiler compiler;
|
|
SkSL::Program::Settings settings;
|
|
sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
|
|
settings.fCaps = caps.get();
|
|
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
|
|
SkSL::Program::kFragmentProcessor_Kind,
|
|
SkSL::String(src),
|
|
settings);
|
|
if (!compiler.errorCount()) {
|
|
compiler.optimize(*program);
|
|
}
|
|
SkSL::String skError(error);
|
|
if (compiler.errorText() != skError) {
|
|
SkDebugf("SKSL ERROR:\n source: %s\n expected: %s received: %s",
|
|
src, error, compiler.errorText().c_str());
|
|
}
|
|
REPORTER_ASSERT(r, compiler.errorText() == skError);
|
|
}
|
|
|
|
DEF_TEST(SkSLFPHelloWorld, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
/* HELLO WORLD */
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
R"__Header__(/* HELLO WORLD */
|
|
|
|
/**************************************************************************************************
|
|
*** This file was autogenerated from GrTest.fp; do not modify.
|
|
**************************************************************************************************/
|
|
#ifndef GrTest_DEFINED
|
|
#define GrTest_DEFINED
|
|
|
|
#include "include/core/SkM44.h"
|
|
#include "include/core/SkTypes.h"
|
|
|
|
|
|
#include "src/gpu/GrFragmentProcessor.h"
|
|
|
|
class GrTest : public GrFragmentProcessor {
|
|
public:
|
|
static std::unique_ptr<GrFragmentProcessor> Make() {
|
|
return std::unique_ptr<GrFragmentProcessor>(new GrTest());
|
|
}
|
|
GrTest(const GrTest& src);
|
|
std::unique_ptr<GrFragmentProcessor> clone() const override;
|
|
const char* name() const override { return "Test"; }
|
|
private:
|
|
GrTest()
|
|
: INHERITED(kGrTest_ClassID, kNone_OptimizationFlags) {
|
|
}
|
|
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
|
|
void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) const override;
|
|
bool onIsEqual(const GrFragmentProcessor&) const override;
|
|
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/gpu/GrTexture.h"
|
|
#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
|
|
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
|
|
#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
|
|
#include "src/sksl/SkSLCPP.h"
|
|
#include "src/sksl/SkSLUtil.h"
|
|
class GrGLSLTest : public GrGLSLFragmentProcessor {
|
|
public:
|
|
GrGLSLTest() {}
|
|
void emitCode(EmitArgs& args) override {
|
|
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
|
const GrTest& _outer = args.fFp.cast<GrTest>();
|
|
(void) _outer;
|
|
fragBuilder->codeAppendf(
|
|
R"SkSL(%s = half4(1.0);
|
|
)SkSL"
|
|
, args.fOutputColor);
|
|
}
|
|
private:
|
|
void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& _proc) override {
|
|
}
|
|
};
|
|
GrGLSLFragmentProcessor* GrTest::onCreateGLSLInstance() const {
|
|
return new GrGLSLTest();
|
|
}
|
|
void GrTest::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
|
|
}
|
|
bool GrTest::onIsEqual(const GrFragmentProcessor& other) const {
|
|
const GrTest& that = other.cast<GrTest>();
|
|
(void) that;
|
|
return true;
|
|
}
|
|
GrTest::GrTest(const GrTest& src)
|
|
: INHERITED(kGrTest_ClassID, src.optimizationFlags()) {
|
|
}
|
|
std::unique_ptr<GrFragmentProcessor> GrTest::clone() const {
|
|
return std::unique_ptr<GrFragmentProcessor>(new GrTest(*this));
|
|
}
|
|
)__Cpp__"
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPInput, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
layout(key) in half2 point;
|
|
void main() {
|
|
sk_OutColor = half4(point, point);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {",
|
|
"return std::unique_ptr<GrFragmentProcessor>(new GrTest(point));",
|
|
"GrTest(SkPoint point)",
|
|
", point(point)"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(%s = half4(half2(%f, %f), half2(%f, %f));\n"
|
|
")SkSL\"\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,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
uniform half4 color;
|
|
void main() {
|
|
sk_OutColor = color;
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"static std::unique_ptr<GrFragmentProcessor> Make()"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
|
|
"kHalf4_GrSLType, \"color\");",
|
|
});
|
|
}
|
|
|
|
// SkSLFPInUniform tests the simplest plumbing case, default type, no tracking
|
|
// with a setUniform template that supports inlining the value call with no
|
|
// local variable.
|
|
DEF_TEST(SkSLFPInUniform, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in uniform half4 color;
|
|
void main() {
|
|
sk_OutColor = color;
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"static std::unique_ptr<GrFragmentProcessor> Make(SkRect color) {",
|
|
},
|
|
/*expectedCPP=*/{
|
|
"colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
|
|
"kHalf4_GrSLType, \"color\");",
|
|
"pdman.set4fv(colorVar, 1, reinterpret_cast<const float*>(&(_outer.color)));"
|
|
});
|
|
}
|
|
|
|
// As above, but tests in uniform's ability to override the default ctype.
|
|
DEF_TEST(SkSLFPInUniformCType, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
layout(ctype=SkPMColor4f) in uniform half4 color;
|
|
void main() {
|
|
sk_OutColor = color;
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"static std::unique_ptr<GrFragmentProcessor> Make(SkPMColor4f color) {",
|
|
},
|
|
/*expectedCPP=*/{
|
|
"colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
|
|
"kHalf4_GrSLType, \"color\");",
|
|
"pdman.set4fv(colorVar, 1, (_outer.color).vec());"
|
|
});
|
|
}
|
|
|
|
// Add state tracking to the default typed SkRect <-> half4 uniform. But since
|
|
// it now has to track state, the value inlining previously done for the
|
|
// setUniform call is removed in favor of a local variable.
|
|
DEF_TEST(SkSLFPTrackedInUniform, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
layout(tracked) in uniform half4 color;
|
|
void main() {
|
|
sk_OutColor = color;
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"static std::unique_ptr<GrFragmentProcessor> Make(SkRect color) {",
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkRect colorPrev = SkRect::MakeEmpty();",
|
|
"colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
|
|
"kHalf4_GrSLType, \"color\");",
|
|
"const SkRect& colorValue = _outer.color;",
|
|
"if (colorPrev.isEmpty() || colorPrev != colorValue) {",
|
|
"colorPrev = colorValue;",
|
|
"pdman.set4fv(colorVar, 1, reinterpret_cast<const float*>(&colorValue));"
|
|
});
|
|
}
|
|
|
|
// Test the case where the template does not support variable inlining in
|
|
// setUniform (i.e. it references the value multiple times).
|
|
DEF_TEST(SkSLFPNonInlinedInUniform, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in uniform half2 point;
|
|
void main() {
|
|
sk_OutColor = half4(point, point);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {",
|
|
},
|
|
/*expectedCPP=*/{
|
|
"pointVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
|
|
"kHalf2_GrSLType, \"point\");",
|
|
"const SkPoint& pointValue = _outer.point;",
|
|
"pdman.set2f(pointVar, pointValue.fX, pointValue.fY);"
|
|
});
|
|
}
|
|
|
|
// Test handling conditional uniforms (that use when= in layout), combined with
|
|
// state tracking and custom ctypes to really put the code generation through its paces.
|
|
DEF_TEST(SkSLFPConditionalInUniform, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
layout(key) in bool test;
|
|
layout(ctype=SkPMColor4f, tracked, when=test) in uniform half4 color;
|
|
void main() {
|
|
if (test) {
|
|
sk_OutColor = color;
|
|
} else {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"static std::unique_ptr<GrFragmentProcessor> Make(bool test, SkPMColor4f color) {",
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkPMColor4f colorPrev = {SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}",
|
|
"auto test = _outer.test;",
|
|
"if (test) {",
|
|
"colorVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, "
|
|
"kHalf4_GrSLType, \"color\");",
|
|
"if (colorVar.isValid()) {",
|
|
"const SkPMColor4f& colorValue = _outer.color;",
|
|
"if (colorPrev != colorValue) {",
|
|
"colorPrev = colorValue;",
|
|
"pdman.set4fv(colorVar, 1, colorValue.vec());"
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPSections, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@header { header section }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"header section"
|
|
},
|
|
/*expectedCPP=*/{});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@class { class section }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"class GrTest : public GrFragmentProcessor {\n"
|
|
"public:\n"
|
|
" class section"
|
|
},
|
|
/*expectedCPP=*/{});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@cpp { cpp section }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{},
|
|
/*expectedCPP=*/{
|
|
"cpp section"
|
|
});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@constructorParams { int x, float y, std::vector<float> z }
|
|
in float w;
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"Make(float w, int x, float y, std::vector<float> z )",
|
|
"return std::unique_ptr<GrFragmentProcessor>(new GrTest(w, x, y, z));",
|
|
"GrTest(float w, int x, float y, std::vector<float> z )",
|
|
", w(w) {"
|
|
},
|
|
/*expectedCPP=*/{});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@constructor { constructor section }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"private:\n constructor section"
|
|
},
|
|
/*expectedCPP=*/{});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@initializers { initializers section }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
": INHERITED(kGrTest_ClassID, kNone_OptimizationFlags)\n , initializers section"
|
|
},
|
|
/*expectedCPP=*/{});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
half x = 10;
|
|
@emitCode { fragBuilder->codeAppendf("half y = %d\n", x * 2); }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{},
|
|
/*expectedCPP=*/{
|
|
"x = 10.0;\n"
|
|
" fragBuilder->codeAppendf(\"half y = %d\\n\", x * 2);"
|
|
});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@fields { fields section }
|
|
@clone { }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"const char* name() const override { return \"Test\"; }\n"
|
|
" fields section private:"
|
|
},
|
|
/*expectedCPP=*/{});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@make { make section }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"public:\n"
|
|
" make section"
|
|
},
|
|
/*expectedCPP=*/{});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
uniform half calculated;
|
|
layout(key) in half provided;
|
|
@setData(varName) { varName.set1f(calculated, provided * 2); }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{},
|
|
/*expectedCPP=*/{
|
|
"void onSetData(const GrGLSLProgramDataManager& varName, "
|
|
"const GrFragmentProcessor& _proc) override {\n",
|
|
"UniformHandle& calculated = calculatedVar;",
|
|
"auto provided = _outer.provided;",
|
|
"varName.set1f(calculated, provided * 2);"
|
|
});
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
@test(testDataName) { testDataName section }
|
|
void main() {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{},
|
|
/*expectedCPP=*/{
|
|
"#if GR_TEST_UTILS\n"
|
|
"std::unique_ptr<GrFragmentProcessor> GrTest::TestCreate(GrProcessorTestData* testDataName) {\n"
|
|
" testDataName section }\n"
|
|
"#endif"
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPTransformedCoords, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
void main() {
|
|
sk_OutColor = half4(sk_TransformedCoords2D[0], sk_TransformedCoords2D[0]);
|
|
}
|
|
)__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=*/{
|
|
"child1_index = this->registerChild(std::move(child1));",
|
|
"child2_index = this->registerChild(std::move(child2));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkString _sample149;\n",
|
|
"_sample149 = this->invokeChild(_outer.child1_index, args);\n",
|
|
"SkString _sample166;\n",
|
|
"_sample166 = this->invokeChild(_outer.child2_index, args);\n",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(%s = %s * %s;\n"
|
|
")SkSL\"\n"
|
|
", args.fOutputColor, _sample149.c_str(), _sample166.c_str());",
|
|
"child1_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child1_index));",
|
|
"child2_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child2_index));",
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPChildProcessorsWithInput, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
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__",
|
|
/*expectedH=*/{
|
|
"child1_index = this->registerChild(std::move(child1));",
|
|
"child2_index = this->registerChild(std::move(child2));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkString _input198(\"childIn\");",
|
|
"SkString _sample198;",
|
|
"_sample198 = this->invokeChild(_outer.child1_index, _input198.c_str(), args);",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(\n"
|
|
"half4 childOut1 = %s;)SkSL\"\n"
|
|
", _sample198.c_str());",
|
|
"SkString _input258(\"childOut1\");",
|
|
"SkString _sample258;",
|
|
"_sample258 = this->invokeChild(_outer.child2_index, _input258.c_str(), args);",
|
|
"child1_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child1_index));",
|
|
"child2_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child2_index));",
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPChildProcessorWithInputExpression, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in fragmentProcessor child;
|
|
void main() {
|
|
sk_OutColor = sample(child, sk_InColor * half4(0.5));
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"child_index = this->registerChild(std::move(child));",
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkString _input106 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);",
|
|
"SkString _sample106;",
|
|
"_sample106 = this->invokeChild(_outer.child_index, _input106.c_str(), args);",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(%s = %s;\n"
|
|
")SkSL\"\n"
|
|
", args.fOutputColor, _sample106.c_str());",
|
|
"child_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child_index));",
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPNestedChildProcessors, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in fragmentProcessor child1;
|
|
in fragmentProcessor child2;
|
|
void main() {
|
|
sk_OutColor = sample(child2, sk_InColor * sample(child1, sk_InColor * half4(0.5)));
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"child1_index = this->registerChild(std::move(child1));",
|
|
"child2_index = this->registerChild(std::move(child2));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkString _input177 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);",
|
|
"SkString _sample177;",
|
|
"_sample177 = this->invokeChild(_outer.child1_index, _input177.c_str(), args);",
|
|
"SkString _input149 = SkStringPrintf(\"%s * %s\", args.fInputColor, _sample177.c_str());",
|
|
"SkString _sample149;",
|
|
"_sample149 = this->invokeChild(_outer.child2_index, _input149.c_str(), args);",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(%s = %s;\n"
|
|
")SkSL\"\n"
|
|
", args.fOutputColor, _sample149.c_str());",
|
|
"child1_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child1_index));",
|
|
"child2_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child2_index));",
|
|
});
|
|
}
|
|
|
|
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, sk_InColor);
|
|
} else {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"child_index = this->registerChild(std::move(child));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"hasCap = sk_Caps.externalTextureSupport;",
|
|
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(bool hasCap = %s;\n"
|
|
"if (hasCap) {)SkSL\"\n"
|
|
", (hasCap ? \"true\" : \"false\"));",
|
|
"SkString _input200(args.fInputColor);",
|
|
"SkString _sample200;",
|
|
"_sample200 = this->invokeChild(_outer.child_index, _input200.c_str(), args);",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(\n"
|
|
" %s = %s;\n"
|
|
"} else {\n"
|
|
" %s = half4(1.0);\n"
|
|
"}\n"
|
|
")SkSL\"\n"
|
|
", args.fOutputColor, _sample200.c_str(), args.fOutputColor);",
|
|
"child_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child_index));",
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPChildProcessorInlineFieldAccess, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in fragmentProcessor child;
|
|
void main() {
|
|
if (child.preservesOpaqueInput) {
|
|
sk_OutColor = sample(child, sk_InColor);
|
|
} else {
|
|
sk_OutColor = half4(1);
|
|
}
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"child_index = this->registerChild(std::move(child));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(if (%s) {)SkSL\"\n"
|
|
", (_outer.childProcessor(_outer.child_index).preservesOpaqueInput() ? \"true\" : \"false\"));",
|
|
"SkString _input161(args.fInputColor);",
|
|
"SkString _sample161;",
|
|
"_sample161 = this->invokeChild(_outer.child_index, _input161.c_str(), args);",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(\n"
|
|
" %s = %s;\n"
|
|
"} else {\n"
|
|
" %s = half4(1.0);\n"
|
|
"}\n"
|
|
")SkSL\"\n"
|
|
", args.fOutputColor, _sample161.c_str(), args.fOutputColor);",
|
|
"child_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child_index));",
|
|
});
|
|
}
|
|
|
|
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=*/{
|
|
"child_index = this->registerChild(std::move(child));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"opaque = _outer.childProcessor(_outer.child_index).preservesOpaqueInput();",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(bool opaque = %s;\n"
|
|
"if (opaque) {)SkSL\"\n"
|
|
", (opaque ? \"true\" : \"false\"));",
|
|
"SkString _sample196;",
|
|
"_sample196 = this->invokeChild(_outer.child_index, 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);",
|
|
"child_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.child_index));",
|
|
});
|
|
}
|
|
|
|
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.child_index >= 0 ? \"true\" : \"false\");",
|
|
"SkString _sample149;",
|
|
"if (_outer.child_index >= 0) {",
|
|
"_sample149 = this->invokeChild(_outer.child_index, 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(SkSLFPSampleCoords, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in fragmentProcessor child;
|
|
@coordTransform { SkMatrix() }
|
|
void main() {
|
|
sk_OutColor = sample(child) + sample(child, sk_TransformedCoords2D[0] / 2);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"child_index = this->registerExplicitlySampledChild(std::move(child));",
|
|
"this->setUsesSampleCoordsDirectly();"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkString _sample150;\n",
|
|
"_sample150 = this->invokeChild(_outer.child_index, args);\n",
|
|
"SkString _sample166;\n",
|
|
"SkString _coords166 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);\n",
|
|
"_sample166 = this->invokeChild(_outer.child_index, args, _coords166.c_str());\n",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(%s = %s + %s;\n"
|
|
")SkSL\"\n"
|
|
", args.fOutputColor, _sample150.c_str(), _sample166.c_str());"
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPFunction, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in fragmentProcessor? child;
|
|
half4 flip(half4 c) { return c.abgr; }
|
|
void main() {
|
|
sk_OutColor = flip(sk_InColor);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{},
|
|
/*expectedCPP=*/{
|
|
"SkString flip_name;",
|
|
"const GrShaderVar flip_args[] = { GrShaderVar(\"c\", kHalf4_GrSLType)};",
|
|
"fragBuilder->emitFunction(kHalf4_GrSLType, \"flip\", 1, flip_args,\n"
|
|
"R\"SkSL(return c.wzyx;\n"
|
|
")SkSL\", &flip_name);",
|
|
"fragBuilder->codeAppendf(\n"
|
|
"R\"SkSL(half4 inlineResult0;\n"
|
|
"half4 inlineArg1_0 = %s;\n"
|
|
"{\n"
|
|
" inlineResult0 = inlineArg1_0.wzyx;\n"
|
|
"}\n"
|
|
"%s = inlineResult0;\n"
|
|
"\n"
|
|
")SkSL\"\n"
|
|
", args.fInputColor, args.fOutputColor);"
|
|
});
|
|
}
|
|
|
|
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::SampleMatrix::MakeConstUniform(\"float3x3(2.0)\", true));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"this->invokeChildWithMatrix(_outer.child_index, 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::SampleMatrix::MakeConstUniform(\"matrix\", true));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"this->invokeChildWithMatrix(_outer.child_index, 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::SampleMatrix::MakeConstUniform(\"matrix\", matrix.hasPerspective()));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"this->invokeChildWithMatrix(_outer.child_index, 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::SampleMatrix::MakeVariable(true));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkString _matrix191(args.fUniformHandler->getUniformCStr(matrixAVar));",
|
|
"this->invokeChildWithMatrix(_outer.child_index, args, _matrix191.c_str());",
|
|
"SkString _matrix247(args.fUniformHandler->getUniformCStr(matrixBVar));",
|
|
"this->invokeChildWithMatrix(_outer.child_index, 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 constant/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::SampleMatrix::MakeVariable(true));"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkString _matrix145 = SkStringPrintf(\"0.5 * %s\", "
|
|
"args.fUniformHandler->getUniformCStr(matrixVar));",
|
|
"this->invokeChildWithMatrix(_outer.child_index, args, _matrix145.c_str());"
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPMatrixSampleConstantAndExplicitly, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in fragmentProcessor? child;
|
|
void main() {
|
|
sk_OutColor = sample(child, float3x3(0.5));
|
|
sk_OutColor = sample(child, sk_TransformedCoords2D[0].xy / 2);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"this->registerChild(std::move(child), "
|
|
"SkSL::SampleMatrix::MakeConstUniform(\"float3x3(0.5)\", true), true);"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"this->invokeChildWithMatrix(_outer.child_index, args)",
|
|
"SkString _coords168 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);",
|
|
"this->invokeChild(_outer.child_index, args, _coords168.c_str())",
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLFPMatrixSampleVariableAndExplicitly, r) {
|
|
test(r,
|
|
*SkSL::ShaderCapsFactory::Default(),
|
|
R"__SkSL__(
|
|
in fragmentProcessor? child;
|
|
void main() {
|
|
float3x3 matrix = float3x3(sk_InColor.a);
|
|
sk_OutColor = sample(child, matrix);
|
|
sk_OutColor = sample(child, sk_TransformedCoords2D[0].xy / 2);
|
|
}
|
|
)__SkSL__",
|
|
/*expectedH=*/{
|
|
"this->registerChild(std::move(child), "
|
|
"SkSL::SampleMatrix::MakeVariable(true), true);"
|
|
},
|
|
/*expectedCPP=*/{
|
|
"SkString _matrix166(\"matrix\");",
|
|
"this->invokeChildWithMatrix(_outer.child_index, args, _matrix166.c_str())",
|
|
"SkString _coords220 = SkStringPrintf(\"%s / 2.0\", args.fSampleCoord);",
|
|
"this->invokeChild(_outer.child_index, args, _coords220.c_str()",
|
|
});
|
|
}
|