From be056f4f62de76c3f3d6ffb2321bf6592841dc46 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Mon, 20 Sep 2021 14:04:41 -0400 Subject: [PATCH] Add switch statement support to PipelineStage. This allows us to write SKSL_TEST_ES3 tests in SkSLTest and have them run properly. Previously, such a test would assert inside the pipeline- stage generator. In ES2 mode, we will rewrite switches as chained ifs, but in ES3 mode we will want to continue emitting them as-is (they will be faster than chained ifs on a modern GPU). `writeSwitchStatement` is adapted from GLSLCodeGenerator. Change-Id: I532ea5ed49869e7cdffced0cdcd0e353af8d4d79 Bug: skia:12450 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/450478 Commit-Queue: John Stiles Commit-Queue: Brian Osman Auto-Submit: John Stiles Reviewed-by: Brian Osman --- gn/sksl_tests.gni | 1 + resources/sksl/runtime/Switch.rts | 15 ++++ resources/sksl/shared/Switch.sksl | 18 ++-- .../SkSLPipelineStageCodeGenerator.cpp | 28 +++++- tests/SkSLTest.cpp | 1 + tests/sksl/runtime/Switch.skvm | 5 ++ tests/sksl/runtime/Switch.stage | 5 ++ tests/sksl/shared/Switch.asm.frag | 85 +++++++++++-------- tests/sksl/shared/Switch.glsl | 17 ++-- tests/sksl/shared/Switch.metal | 18 ++-- 10 files changed, 129 insertions(+), 64 deletions(-) create mode 100644 resources/sksl/runtime/Switch.rts create mode 100644 tests/sksl/runtime/Switch.skvm create mode 100644 tests/sksl/runtime/Switch.stage diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni index 098a7c61e0..5a18df93c2 100644 --- a/gn/sksl_tests.gni +++ b/gn/sksl_tests.gni @@ -556,6 +556,7 @@ sksl_rte_tests = [ "/sksl/runtime/LoopFloat.rts", "/sksl/runtime/PrecisionQualifiers.rts", "/sksl/runtime/SampleWithExplicitCoord.rts", + "/sksl/runtime/Switch.rts", "/sksl/runtime/VectorIndexing.rts", ] diff --git a/resources/sksl/runtime/Switch.rts b/resources/sksl/runtime/Switch.rts new file mode 100644 index 0000000000..72061b44be --- /dev/null +++ b/resources/sksl/runtime/Switch.rts @@ -0,0 +1,15 @@ +uniform half4 colorGreen, colorRed; + +half4 main(float2 coords) { + // Basic switch test. + switch (int(colorGreen.g)) { + case 0: + return colorRed; + + case 1: + return colorGreen; + + default: + return colorRed; + } +} diff --git a/resources/sksl/shared/Switch.sksl b/resources/sksl/shared/Switch.sksl index deabebc64e..72061b44be 100644 --- a/resources/sksl/shared/Switch.sksl +++ b/resources/sksl/shared/Switch.sksl @@ -1,17 +1,15 @@ -uniform float unknownInput; +uniform half4 colorGreen, colorRed; -void main() { +half4 main(float2 coords) { // Basic switch test. - half value; - switch (int(unknownInput)) { + switch (int(colorGreen.g)) { case 0: - value = 0.0; - break; + return colorRed; + case 1: - value = 1.0; - break; + return colorGreen; + default: - value = 2.0; + return colorRed; } - sk_FragColor = value.xxxx; } diff --git a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp index fed3718bc8..9e7bcdd1a8 100644 --- a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp +++ b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp @@ -30,6 +30,7 @@ #include "src/sksl/ir/SkSLPrefixExpression.h" #include "src/sksl/ir/SkSLReturnStatement.h" #include "src/sksl/ir/SkSLStructDefinition.h" +#include "src/sksl/ir/SkSLSwitchStatement.h" #include "src/sksl/ir/SkSLSwizzle.h" #include "src/sksl/ir/SkSLTernaryExpression.h" #include "src/sksl/ir/SkSLVarDeclarations.h" @@ -98,6 +99,7 @@ private: void writeDoStatement(const DoStatement& d); void writeForStatement(const ForStatement& f); void writeReturnStatement(const ReturnStatement& r); + void writeSwitchStatement(const SwitchStatement& s); void writeProgramElement(const ProgramElement& e); @@ -277,6 +279,28 @@ void PipelineStageCodeGenerator::writeReturnStatement(const ReturnStatement& r) this->write(";"); } +void PipelineStageCodeGenerator::writeSwitchStatement(const SwitchStatement& s) { + this->write("switch ("); + this->writeExpression(*s.value(), Precedence::kTopLevel); + this->writeLine(") {"); + for (const std::unique_ptr& stmt : s.cases()) { + const SwitchCase& c = stmt->as(); + if (c.value()) { + this->write("case "); + this->writeExpression(*c.value(), Precedence::kTopLevel); + this->writeLine(":"); + } else { + this->writeLine("default:"); + } + if (!c.statement()->isEmpty()) { + this->writeStatement(*c.statement()); + this->writeLine(); + } + } + this->writeLine(); + this->write("}"); +} + String PipelineStageCodeGenerator::functionName(const FunctionDeclaration& decl) { if (decl.isMain()) { return String(decl.name()); @@ -637,11 +661,13 @@ void PipelineStageCodeGenerator::writeStatement(const Statement& s) { case Statement::Kind::kReturn: this->writeReturnStatement(s.as()); break; + case Statement::Kind::kSwitch: + this->writeSwitchStatement(s.as()); + break; case Statement::Kind::kVarDeclaration: this->writeVarDeclaration(s.as()); break; case Statement::Kind::kDiscard: - case Statement::Kind::kSwitch: SkDEBUGFAIL("Unsupported control flow"); break; case Statement::Kind::kInlineMarker: diff --git a/tests/SkSLTest.cpp b/tests/SkSLTest.cpp index 53843c52f3..c167ebe299 100644 --- a/tests/SkSLTest.cpp +++ b/tests/SkSLTest.cpp @@ -323,6 +323,7 @@ SKSL_TEST(SkSLStaticIf, "shared/StaticIf.sksl") SKSL_TEST_ES3(SkSLStaticSwitch, "shared/StaticSwitch.sksl") SKSL_TEST(SkSLStructArrayFollowedByScalar, "shared/StructArrayFollowedByScalar.sksl") SKSL_TEST(SkSLStructsInFunctions, "shared/StructsInFunctions.sksl") +SKSL_TEST_ES3(SkSLSwitch, "shared/Switch.sksl") SKSL_TEST(SkSLSwizzleBoolConstants, "shared/SwizzleBoolConstants.sksl") SKSL_TEST(SkSLSwizzleByConstantIndex, "shared/SwizzleByConstantIndex.sksl") SKSL_TEST(SkSLSwizzleConstants, "shared/SwizzleConstants.sksl") diff --git a/tests/sksl/runtime/Switch.skvm b/tests/sksl/runtime/Switch.skvm new file mode 100644 index 0000000000..6b0bcda33b --- /dev/null +++ b/tests/sksl/runtime/Switch.skvm @@ -0,0 +1,5 @@ +### Compilation failed: + +error: 5: switch statements are not supported +error: 3: function 'main' can exit without returning a value +2 errors diff --git a/tests/sksl/runtime/Switch.stage b/tests/sksl/runtime/Switch.stage new file mode 100644 index 0000000000..6b0bcda33b --- /dev/null +++ b/tests/sksl/runtime/Switch.stage @@ -0,0 +1,5 @@ +### Compilation failed: + +error: 5: switch statements are not supported +error: 3: function 'main' can exit without returning a value +2 errors diff --git a/tests/sksl/shared/Switch.asm.frag b/tests/sksl/shared/Switch.asm.frag index ed5beb9537..4036eff35d 100644 --- a/tests/sksl/shared/Switch.asm.frag +++ b/tests/sksl/shared/Switch.asm.frag @@ -1,25 +1,31 @@ OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %sk_FragColor %sk_Clockwise -OpExecutionMode %main OriginUpperLeft +OpEntryPoint Fragment %_entrypoint_v "_entrypoint" %sk_FragColor %sk_Clockwise +OpExecutionMode %_entrypoint_v OriginUpperLeft OpName %sk_FragColor "sk_FragColor" OpName %sk_Clockwise "sk_Clockwise" OpName %_UniformBuffer "_UniformBuffer" -OpMemberName %_UniformBuffer 0 "unknownInput" +OpMemberName %_UniformBuffer 0 "colorGreen" +OpMemberName %_UniformBuffer 1 "colorRed" +OpName %_entrypoint_v "_entrypoint_v" OpName %main "main" -OpName %value "value" OpDecorate %sk_FragColor RelaxedPrecision OpDecorate %sk_FragColor Location 0 OpDecorate %sk_FragColor Index 0 OpDecorate %sk_Clockwise BuiltIn FrontFacing OpMemberDecorate %_UniformBuffer 0 Offset 0 +OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision +OpMemberDecorate %_UniformBuffer 1 Offset 16 +OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision OpDecorate %_UniformBuffer Block OpDecorate %10 Binding 0 OpDecorate %10 DescriptorSet 0 -OpDecorate %value RelaxedPrecision +OpDecorate %30 RelaxedPrecision OpDecorate %31 RelaxedPrecision -OpDecorate %32 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %43 RelaxedPrecision %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Output_v4float = OpTypePointer Output %v4float @@ -27,38 +33,49 @@ OpDecorate %32 RelaxedPrecision %bool = OpTypeBool %_ptr_Input_bool = OpTypePointer Input %bool %sk_Clockwise = OpVariable %_ptr_Input_bool Input -%_UniformBuffer = OpTypeStruct %float +%_UniformBuffer = OpTypeStruct %v4float %v4float %_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer %10 = OpVariable %_ptr_Uniform__UniformBuffer Uniform %void = OpTypeVoid -%14 = OpTypeFunction %void -%_ptr_Function_float = OpTypePointer Function %float -%_ptr_Uniform_float = OpTypePointer Uniform %float +%15 = OpTypeFunction %void +%v2float = OpTypeVector %float 2 +%float_0 = OpConstant %float 0 +%19 = OpConstantComposite %v2float %float_0 %float_0 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%23 = OpTypeFunction %v4float %_ptr_Function_v2float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float %int = OpTypeInt 32 1 %int_0 = OpConstant %int 0 -%float_0 = OpConstant %float 0 -%float_1 = OpConstant %float 1 -%float_2 = OpConstant %float 2 -%main = OpFunction %void None %14 -%15 = OpLabel -%value = OpVariable %_ptr_Function_float Function -%18 = OpAccessChain %_ptr_Uniform_float %10 %int_0 -%22 = OpLoad %float %18 -%23 = OpConvertFToS %int %22 -OpSelectionMerge %24 None -OpSwitch %23 %27 0 %25 1 %26 -%25 = OpLabel -OpStore %value %float_0 -OpBranch %24 -%26 = OpLabel -OpStore %value %float_1 -OpBranch %24 -%27 = OpLabel -OpStore %value %float_2 -OpBranch %24 -%24 = OpLabel -%31 = OpLoad %float %value -%32 = OpCompositeConstruct %v4float %31 %31 %31 %31 -OpStore %sk_FragColor %32 +%int_1 = OpConstant %int 1 +%_entrypoint_v = OpFunction %void None %15 +%16 = OpLabel +%20 = OpVariable %_ptr_Function_v2float Function +OpStore %20 %19 +%22 = OpFunctionCall %v4float %main %20 +OpStore %sk_FragColor %22 OpReturn OpFunctionEnd +%main = OpFunction %v4float None %23 +%24 = OpFunctionParameter %_ptr_Function_v2float +%25 = OpLabel +%26 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0 +%30 = OpLoad %v4float %26 +%31 = OpCompositeExtract %float %30 1 +%32 = OpConvertFToS %int %31 +OpSelectionMerge %33 None +OpSwitch %32 %36 0 %34 1 %35 +%34 = OpLabel +%37 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1 +%39 = OpLoad %v4float %37 +OpReturnValue %39 +%35 = OpLabel +%40 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0 +%41 = OpLoad %v4float %40 +OpReturnValue %41 +%36 = OpLabel +%42 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1 +%43 = OpLoad %v4float %42 +OpReturnValue %43 +%33 = OpLabel +OpUnreachable +OpFunctionEnd diff --git a/tests/sksl/shared/Switch.glsl b/tests/sksl/shared/Switch.glsl index 39b48e2292..f2ea94ca87 100644 --- a/tests/sksl/shared/Switch.glsl +++ b/tests/sksl/shared/Switch.glsl @@ -1,17 +1,14 @@ out vec4 sk_FragColor; -uniform float unknownInput; -void main() { - float value; - switch (int(unknownInput)) { +uniform vec4 colorGreen; +uniform vec4 colorRed; +vec4 main() { + switch (int(colorGreen.y)) { case 0: - value = 0.0; - break; + return colorRed; case 1: - value = 1.0; - break; + return colorGreen; default: - value = 2.0; + return colorRed; } - sk_FragColor = vec4(value); } diff --git a/tests/sksl/shared/Switch.metal b/tests/sksl/shared/Switch.metal index fcc6674854..2f800f2ca2 100644 --- a/tests/sksl/shared/Switch.metal +++ b/tests/sksl/shared/Switch.metal @@ -2,7 +2,8 @@ #include using namespace metal; struct Uniforms { - float unknownInput; + float4 colorGreen; + float4 colorRed; }; struct Inputs { }; @@ -12,17 +13,16 @@ struct Outputs { fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) { Outputs _out; (void)_out; - float value; - switch (int(_uniforms.unknownInput)) { + switch (int(_uniforms.colorGreen.y)) { case 0: - value = 0.0; - break; + _out.sk_FragColor = _uniforms.colorRed; + return _out; case 1: - value = 1.0; - break; + _out.sk_FragColor = _uniforms.colorGreen; + return _out; default: - value = 2.0; + _out.sk_FragColor = _uniforms.colorRed; + return _out; } - _out.sk_FragColor = float4(value); return _out; }