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 <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2021-09-20 14:04:41 -04:00 committed by SkCQ
parent f62934b85a
commit be056f4f62
10 changed files with 129 additions and 64 deletions

View File

@ -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",
]

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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<Statement>& stmt : s.cases()) {
const SwitchCase& c = stmt->as<SwitchCase>();
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<ReturnStatement>());
break;
case Statement::Kind::kSwitch:
this->writeSwitchStatement(s.as<SwitchStatement>());
break;
case Statement::Kind::kVarDeclaration:
this->writeVarDeclaration(s.as<VarDeclaration>());
break;
case Statement::Kind::kDiscard:
case Statement::Kind::kSwitch:
SkDEBUGFAIL("Unsupported control flow");
break;
case Statement::Kind::kInlineMarker:

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -2,7 +2,8 @@
#include <simd/simd.h>
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;
}