| /* |
| * Copyright 2022 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkColor.h" |
| #include "include/core/SkTypes.h" |
| #include "include/private/SkSLProgramKind.h" |
| #include "src/base/SkArenaAlloc.h" |
| #include "src/core/SkRasterPipeline.h" |
| #include "src/sksl/SkSLCompiler.h" |
| #include "src/sksl/SkSLProgramSettings.h" |
| #include "src/sksl/SkSLUtil.h" |
| #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h" |
| #include "src/sksl/codegen/SkSLRasterPipelineCodeGenerator.h" |
| #include "src/sksl/ir/SkSLFunctionDeclaration.h" |
| #include "src/sksl/ir/SkSLProgram.h" |
| #include "src/sksl/tracing/SkRPDebugTrace.h" |
| #include "tests/Test.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| |
| //#define DUMP_PROGRAMS 1 |
| #if defined(DUMP_PROGRAMS) |
| #include "src/core/SkStreamPriv.h" |
| #endif |
| |
| static void test(skiatest::Reporter* r, |
| const char* src, |
| SkSpan<const float> uniforms, |
| SkColor4f startingColor, |
| std::optional<SkColor4f> expectedResult) { |
| SkSL::Compiler compiler(SkSL::ShaderCapsFactory::Default()); |
| SkSL::ProgramSettings settings; |
| settings.fMaxVersionAllowed = SkSL::Version::k300; |
| std::unique_ptr<SkSL::Program> program = compiler.convertProgram( |
| SkSL::ProgramKind::kRuntimeColorFilter, std::string(src), settings); |
| if (!program) { |
| ERRORF(r, "Unexpected error compiling %s\n%s", src, compiler.errorText().c_str()); |
| return; |
| } |
| const SkSL::FunctionDeclaration* main = program->getFunction("main"); |
| if (!main) { |
| ERRORF(r, "Program must have a 'main' function"); |
| return; |
| } |
| SkArenaAlloc alloc(/*firstHeapAllocation=*/1000); |
| SkRasterPipeline pipeline(&alloc); |
| pipeline.append_constant_color(&alloc, startingColor); |
| SkSL::SkRPDebugTrace debugTrace; |
| std::unique_ptr<SkSL::RP::Program> rasterProg = |
| SkSL::MakeRasterPipelineProgram(*program, *main->definition(), &debugTrace); |
| if (!rasterProg && !expectedResult.has_value()) { |
| // We didn't get a program, as expected. Test passes. |
| return; |
| } |
| if (!rasterProg && expectedResult.has_value()) { |
| ERRORF(r, "MakeRasterPipelineProgram failed"); |
| return; |
| } |
| if (rasterProg && !expectedResult.has_value()) { |
| ERRORF(r, "MakeRasterPipelineProgram should have failed, but didn't"); |
| return; |
| } |
| |
| #if defined(DUMP_PROGRAMS) |
| // Dump the program instructions via SkDebugf. |
| SkDebugf("-----\n\n"); |
| SkDebugfStream stream; |
| rasterProg->dump(&stream); |
| SkDebugf("\n-----\n\n"); |
| #endif |
| |
| // Append the SkSL program to the raster pipeline. |
| rasterProg->appendStages(&pipeline, &alloc, /*callbacks=*/nullptr, uniforms); |
| |
| // Move the float values from RGBA into an 8888 memory buffer. |
| uint32_t out[SkRasterPipeline_kMaxStride_highp] = {}; |
| SkRasterPipeline_MemoryCtx outCtx{/*pixels=*/out, /*stride=*/SkRasterPipeline_kMaxStride_highp}; |
| pipeline.append(SkRasterPipelineOp::store_8888, &outCtx); |
| pipeline.run(0, 0, 1, 1); |
| |
| // Make sure the first pixel (exclusively) of `out` matches RGBA. |
| uint32_t expected = expectedResult->toBytes_RGBA(); |
| REPORTER_ASSERT(r, out[0] == expected, |
| "Got:%02X%02X%02X%02X Expected:%02X%02X%02X%02X", |
| (out[0] >> 24) & 0xFF, |
| (out[0] >> 16) & 0xFF, |
| (out[0] >> 8) & 0xFF, |
| out[0] & 0xFF, |
| (expected >> 24) & 0xFF, |
| (expected >> 16) & 0xFF, |
| (expected >> 8) & 0xFF, |
| expected & 0xFF); |
| |
| // Make sure the rest of the pixels are untouched. |
| for (size_t i = 1; i < std::size(out); ++i) { |
| REPORTER_ASSERT(r, out[i] == 0); |
| } |
| } |
| |
| DEF_TEST(SkSLRasterPipelineCodeGeneratorIfElseTest, r) { |
| // Add in your SkSL here. |
| test(r, |
| R"__SkSL__( |
| const half4 colorWhite = half4(1); |
| |
| half4 ifElseTest(half4 colorBlue, half4 colorGreen, half4 colorRed) { |
| half4 result = half4(0); |
| if (colorWhite != colorBlue) { // TRUE |
| if (colorGreen == colorRed) { // FALSE |
| result = colorRed; |
| } else { |
| result = colorGreen; |
| } |
| } else { |
| if (colorRed != colorGreen) { // TRUE, but in a false branch |
| result = colorBlue; |
| } else { // FALSE, and in a false branch |
| result = colorWhite; |
| } |
| } |
| if (colorRed == colorBlue) { // FALSE |
| return colorWhite; |
| } |
| if (colorRed != colorGreen) { // TRUE |
| return result; |
| } |
| if (colorRed == colorWhite) { // FALSE |
| return colorBlue; |
| } |
| return colorRed; |
| } |
| |
| half4 main(half4) { |
| return ifElseTest(colorWhite.00b1, colorWhite.0g01, colorWhite.r001); |
| } |
| )__SkSL__", |
| /*uniforms=*/{}, |
| /*startingColor=*/SkColor4f{0.0, 0.0, 0.0, 0.0}, |
| /*expectedResult=*/SkColor4f{0.0f, 1.0f, 0.0f, 1.0f}); |
| } |
| |
| DEF_TEST(SkSLRasterPipelineCodeGeneratorTernaryTest, r) { |
| // Add in your SkSL here. |
| test(r, |
| R"__SkSL__( |
| half4 main(half4 colorWhite) { |
| half4 colorBlue = colorWhite.00ba, |
| colorGreen = colorWhite.0g0a, |
| colorRed = colorWhite.r00a; |
| // This ternary matches the initial if-else block inside IfElseTest. |
| half4 result; |
| result = (colorWhite != colorBlue) // TRUE |
| ? (colorGreen == colorRed ? colorRed : colorGreen) // FALSE |
| : (colorRed != colorGreen ? colorBlue : colorWhite); // in false branch |
| |
| // This ternary matches the second portion of IfElseTest. |
| return colorRed == colorBlue ? colorWhite : |
| colorRed != colorGreen ? result : // TRUE |
| colorRed == colorWhite ? colorBlue : |
| colorRed; |
| } |
| )__SkSL__", |
| /*uniforms=*/{}, |
| /*startingColor=*/SkColor4f{1.0, 1.0, 1.0, 1.0}, |
| /*expectedResult=*/SkColor4f{0.0f, 1.0f, 0.0f, 1.0f}); |
| } |
| |
| DEF_TEST(SkSLRasterPipelineCodeGeneratorNestedTernaryTest, r) { |
| // Add in your SkSL here. |
| test(r, |
| R"__SkSL__( |
| half4 main(half4) { |
| half three = 3, one = 1, two = 2; |
| half result = (three > (one > two ? 2.0 : 5.0)) ? 1.0 : 0.499; |
| return half4(result); |
| } |
| )__SkSL__", |
| /*uniforms=*/{}, |
| /*startingColor=*/SkColor4f{0.0, 0.0, 0.0, 0.0}, |
| /*expectedResult=*/SkColor4f{0.499f, 0.499f, 0.499f, 0.499f}); |
| } |
| |
| DEF_TEST(SkSLRasterPipelineCodeGeneratorArithmeticTest, r) { |
| test(r, |
| R"__SkSL__( |
| half4 main(half4) { |
| const half4 colorGreen = half4(0,1,0,1), colorRed = half4(1,0,0,1); |
| |
| half a = 3.0, b = 4.0, c = a + b - 2.0; |
| if (a*a + b*b == c*c*c/5.0) { |
| int A = 3, B = 4, C = A + B - 2; |
| if (A*A + B*B == C*C*C/5) { |
| return colorGreen; |
| } |
| } |
| |
| return colorRed; |
| } |
| )__SkSL__", |
| /*uniforms=*/{}, |
| /*startingColor=*/SkColor4f{0.0, 0.0, 0.0, 0.0}, |
| /*expectedResult=*/SkColor4f{0.0f, 1.0f, 0.0f, 1.0f}); |
| } |
| |
| DEF_TEST(SkSLRasterPipelineCodeGeneratorCoercedTypeTest, r) { |
| static constexpr float kUniforms[] = {0.0, 1.0, 0.0, 1.0, |
| 1.0, 0.0, 0.0, 1.0}; |
| test(r, |
| R"__SkSL__( |
| uniform half4 colorGreen; |
| uniform float4 colorRed; |
| half4 main(half4 color) { |
| return ((colorGreen + colorRed) == float4(1.0, 1.0, 0.0, 2.0)) ? colorGreen |
| : colorGreen.gr01; |
| } |
| )__SkSL__", |
| kUniforms, |
| /*startingColor=*/SkColor4f{0.0, 0.0, 0.0, 0.0}, |
| /*expectedResult=*/SkColor4f{0.0, 1.0, 0.0, 1.0}); |
| } |
| |
| DEF_TEST(SkSLRasterPipelineCodeGeneratorIdentitySwizzle, r) { |
| static constexpr float kUniforms[] = {0.0, 1.0, 0.0, 1.0, |
| 1.0, 0.0, 0.0, 1.0}; |
| test(r, |
| R"__SkSL__( |
| uniform half4 colorGreen, colorRed; |
| half4 main(vec4 color) { |
| return (color.r == 0.5 && |
| color.rg == half2(0.5, 1.0) && |
| color.rgb == half3(0.5, 1.0, 0.0)) ? colorGreen : colorRed; |
| } |
| )__SkSL__", |
| kUniforms, |
| /*startingColor=*/SkColor4f{0.5, 1.0, 0.0, 0.25}, |
| /*expectedResult=*/SkColor4f{0.0, 1.0, 0.0, 1.0}); |
| |
| } |
| |
| DEF_TEST(SkSLRasterPipelineCodeGeneratorBitwiseNotTest, r) { |
| static constexpr int32_t kUniforms[] = { 0, 12, 3456, 4567890, |
| ~0, ~12, ~3456, ~4567890}; |
| test(r, |
| R"__SkSL__( |
| uniform int4 value, expected; |
| const half4 colorGreen = half4(0,1,0,1), colorRed = half4(1,0,0,1); |
| |
| half4 main(vec4) { |
| return (~value.x == expected.x && |
| ~value.xy == expected.xy && |
| ~value.xyz == expected.xyz && |
| ~value.xyzw == expected.xyzw) ? colorGreen : colorRed; |
| } |
| )__SkSL__", |
| SkSpan((const float*)kUniforms, std::size(kUniforms)), |
| /*startingColor=*/SkColor4f{0.0, 0.0, 0.0, 0.0}, |
| /*expectedResult=*/SkColor4f{0.0, 1.0, 0.0, 1.0}); |
| } |
| |
| DEF_TEST(SkSLRasterPipelineCodeGeneratorComparisonIntrinsicTest, r) { |
| test(r, |
| R"__SkSL__( |
| half4 main(vec4) { |
| const half4 colorGreen = half4(0,1,0,1), colorRed = half4(1,0,0,1); |
| half4 a = half4(1, 2, 0, 1), |
| b = half4(2, 2, 1, 0); |
| int3 c = int3(1111, 3333, 5555), |
| d = int3(1111, 5555, 3333); |
| uint2 e = uint2(1111111111u, 222), |
| f = uint2(3333333333u, 222); |
| return (lessThan(a, b) == bool4(true, false, true, false) && |
| lessThan(c, d) == bool3(false, true, false) && |
| lessThan(e, f) == bool2(true, false) && |
| greaterThan(a, b) == bool4(false, false, false, true) && |
| greaterThan(c, d) == bool3(false, false, true) && |
| greaterThan(e, f) == bool2(false, false) && |
| lessThanEqual(a, b) == bool4(true, true, true, false) && |
| lessThanEqual(c, d) == bool3(true, true, false) && |
| lessThanEqual(e, f) == bool2(true, true) && |
| greaterThanEqual(a, b) == bool4(false, true, false, true) && |
| greaterThanEqual(c, d) == bool3(true, false, true) && |
| greaterThanEqual(e, f) == bool2(false, true) && |
| equal(a, b) == bool4(false, true, false, false) && |
| equal(c, d) == bool3(true, false, false) && |
| equal(e, f) == bool2(false, true) && |
| notEqual(a, b) == bool4(true, false, true, true) && |
| notEqual(c, d) == bool3(false, true, true) && |
| notEqual(e, f) == bool2(true, false) && |
| max(a, b) == half4(2, 2, 1, 1) && |
| max(c, d) == int3(1111, 5555, 5555) && |
| max(e, f) == uint2(3333333333u, 222) && |
| max(a, 1) == half4(1, 2, 1, 1) && |
| max(c, 3333) == int3(3333, 3333, 5555) && |
| max(e, 7777) == uint2(1111111111u, 7777) && |
| min(a, b) == half4(1, 2, 0, 0) && |
| min(c, d) == int3(1111, 3333, 3333) && |
| min(e, f) == uint2(1111111111u, 222) && |
| min(a, 1) == half4(1, 1, 0, 1) && |
| min(c, 3333) == int3(1111, 3333, 3333) && |
| min(e, 7777) == uint2(7777, 222)) ? colorGreen : colorRed; |
| } |
| )__SkSL__", |
| /*uniforms=*/{}, |
| /*startingColor=*/SkColor4f{0.0, 0.0, 0.0, 0.0}, |
| /*expectedResult=*/SkColor4f{0.0, 1.0, 0.0, 1.0}); |
| } |