Rewrite switch statements in GLSL strict-ES2 mode.

Once this lands, switch statements will work everywhere--Metal, SPIR-V,
GLSL, and SkVM.

Change-Id: I2797d0a872de8be77bb9f7aa6acb93421d571d70
Bug: skia:12450
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/452356
Commit-Queue: John Stiles <johnstiles@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-23 16:26:53 -04:00 committed by SkCQ
parent 4144211e06
commit 45e3838006
7 changed files with 204 additions and 6 deletions

View File

@ -176,6 +176,10 @@ sksl_glsl_tests = [
"/sksl/glsl/UsesPrecisionModifiers.sksl", "/sksl/glsl/UsesPrecisionModifiers.sksl",
"/sksl/glsl/Version110.sksl", "/sksl/glsl/Version110.sksl",
"/sksl/glsl/Version450Core.sksl", "/sksl/glsl/Version450Core.sksl",
"/sksl/runtime/Switch.rts",
"/sksl/runtime/SwitchDefaultOnly.rts",
"/sksl/runtime/SwitchWithFallthrough.rts",
"/sksl/runtime/SwitchWithLoops.rts",
] ]
sksl_metal_tests = [ sksl_metal_tests = [

View File

@ -1355,8 +1355,66 @@ void GLSLCodeGenerator::writeDoStatement(const DoStatement& d) {
void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) { void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
if (fProgram.fConfig->strictES2Mode()) { if (fProgram.fConfig->strictES2Mode()) {
// TODO(skia:12450): write switch compatibility code String fallthroughVar = "_tmpSwitchFallthrough" + to_string(fVarCount++);
fContext.fErrors->error(s.fOffset, "switch statements are not supported"); String valueVar = "_tmpSwitchValue" + to_string(fVarCount++);
String loopVar = "_tmpSwitchLoop" + to_string(fVarCount++);
this->write("int ");
this->write(valueVar);
this->write(" = ");
this->writeExpression(*s.value(), Precedence::kAssignment);
this->write(", ");
this->write(fallthroughVar);
this->writeLine(" = 0; ");
this->write("for (int ");
this->write(loopVar);
this->write(" = 0; ");
this->write(loopVar);
this->write(" < 1; ");
this->write(loopVar);
this->writeLine("++) {");
fIndentation++;
bool firstCase = true;
for (const std::unique_ptr<Statement>& stmt : s.cases()) {
const SwitchCase& c = stmt->as<SwitchCase>();
if (c.value()) {
this->write("if ((");
if (firstCase) {
firstCase = false;
} else {
this->write(fallthroughVar);
this->write(" > 0) || (");
}
this->write(valueVar);
this->write(" == ");
this->writeExpression(*c.value(), Precedence::kEquality);
this->writeLine(")) {");
fIndentation++;
// We write the entire case-block statement here, and then set `switchFallthrough`
// to 1. If the case-block had a break statement in it, we break out of the outer
// for-loop entirely, meaning the `switchFallthrough` assignment never occurs, nor
// does any code after it inside the switch. We've forbidden `continue` statements
// inside switch case-blocks entirely, so we don't need to consider their effect on
// control flow; see the Finalizer in FunctionDefinition::Convert.
this->writeStatement(*c.statement());
this->finishLine();
this->write(fallthroughVar);
this->write(" = 1;");
this->writeLine();
fIndentation--;
this->writeLine("}");
} else {
// This is the default case. Since it's always last, we can just dump in the code.
this->writeStatement(*c.statement());
this->finishLine();
}
}
fIndentation--;
this->writeLine("}");
return;
} }
this->write("switch ("); this->write("switch (");

View File

@ -323,10 +323,10 @@ SKSL_TEST(SkSLStaticIf, "shared/StaticIf.sksl")
SKSL_TEST_ES3(SkSLStaticSwitch, "shared/StaticSwitch.sksl") SKSL_TEST_ES3(SkSLStaticSwitch, "shared/StaticSwitch.sksl")
SKSL_TEST(SkSLStructArrayFollowedByScalar, "shared/StructArrayFollowedByScalar.sksl") SKSL_TEST(SkSLStructArrayFollowedByScalar, "shared/StructArrayFollowedByScalar.sksl")
SKSL_TEST(SkSLStructsInFunctions, "shared/StructsInFunctions.sksl") SKSL_TEST(SkSLStructsInFunctions, "shared/StructsInFunctions.sksl")
SKSL_TEST_ES3(SkSLSwitch, "shared/Switch.sksl") SKSL_TEST(SkSLSwitch, "shared/Switch.sksl")
SKSL_TEST_ES3(SkSLSwitchDefaultOnly, "shared/SwitchDefaultOnly.sksl") SKSL_TEST(SkSLSwitchDefaultOnly, "shared/SwitchDefaultOnly.sksl")
SKSL_TEST_ES3(SkSLSwitchWithFallthrough, "shared/SwitchWithFallthrough.sksl") SKSL_TEST(SkSLSwitchWithFallthrough, "shared/SwitchWithFallthrough.sksl")
SKSL_TEST_ES3(SkSLSwitchWithLoops, "shared/SwitchWithLoops.sksl") SKSL_TEST(SkSLSwitchWithLoops, "shared/SwitchWithLoops.sksl")
SKSL_TEST(SkSLSwizzleBoolConstants, "shared/SwizzleBoolConstants.sksl") SKSL_TEST(SkSLSwizzleBoolConstants, "shared/SwizzleBoolConstants.sksl")
SKSL_TEST(SkSLSwizzleByConstantIndex, "shared/SwizzleByConstantIndex.sksl") SKSL_TEST(SkSLSwizzleByConstantIndex, "shared/SwizzleByConstantIndex.sksl")
SKSL_TEST(SkSLSwizzleConstants, "shared/SwizzleConstants.sksl") SKSL_TEST(SkSLSwizzleConstants, "shared/SwizzleConstants.sksl")

View File

@ -0,0 +1,22 @@
uniform vec4 colorGreen;
uniform vec4 colorRed;
vec4 main() {
vec4 color;
int _tmpSwitchValue1 = int(colorGreen.y), _tmpSwitchFallthrough0 = 0;
for (int _tmpSwitchLoop2 = 0; _tmpSwitchLoop2 < 1; _tmpSwitchLoop2++) {
if ((_tmpSwitchValue1 == 0)) {
color = colorRed;
break;
_tmpSwitchFallthrough0 = 1;
}
if ((_tmpSwitchFallthrough0 > 0) || (_tmpSwitchValue1 == 1)) {
color = colorGreen;
break;
_tmpSwitchFallthrough0 = 1;
}
color = colorRed;
break;
}
return color;
}

View File

@ -0,0 +1,9 @@
uniform vec4 colorGreen;
uniform vec4 colorRed;
vec4 main() {
int _tmpSwitchValue1 = int(colorGreen.y), _tmpSwitchFallthrough0 = 0;
for (int _tmpSwitchLoop2 = 0; _tmpSwitchLoop2 < 1; _tmpSwitchLoop2++) {
return colorGreen;
}
}

View File

@ -0,0 +1,53 @@
uniform vec4 colorGreen;
uniform vec4 colorRed;
bool switch_fallthrough_twice_bi(int value) {
bool ok = false;
int _tmpSwitchValue1 = value, _tmpSwitchFallthrough0 = 0;
for (int _tmpSwitchLoop2 = 0; _tmpSwitchLoop2 < 1; _tmpSwitchLoop2++) {
if ((_tmpSwitchValue1 == 0)) {
break;
_tmpSwitchFallthrough0 = 1;
}
if ((_tmpSwitchFallthrough0 > 0) || (_tmpSwitchValue1 == 1)) {
{
}
_tmpSwitchFallthrough0 = 1;
}
if ((_tmpSwitchFallthrough0 > 0) || (_tmpSwitchValue1 == 2)) {
{
}
_tmpSwitchFallthrough0 = 1;
}
if ((_tmpSwitchFallthrough0 > 0) || (_tmpSwitchValue1 == 3)) {
ok = true;
break;
_tmpSwitchFallthrough0 = 1;
}
break;
}
return ok;
}
vec4 main() {
int x = int(colorGreen.y);
bool _0_ok = false;
int _tmpSwitchValue4 = x, _tmpSwitchFallthrough3 = 0;
for (int _tmpSwitchLoop5 = 0; _tmpSwitchLoop5 < 1; _tmpSwitchLoop5++) {
if ((_tmpSwitchValue4 == 2)) {
break;
_tmpSwitchFallthrough3 = 1;
}
if ((_tmpSwitchFallthrough3 > 0) || (_tmpSwitchValue4 == 1)) {
{
}
_tmpSwitchFallthrough3 = 1;
}
if ((_tmpSwitchFallthrough3 > 0) || (_tmpSwitchValue4 == 0)) {
_0_ok = true;
break;
_tmpSwitchFallthrough3 = 1;
}
break;
}
return _0_ok && switch_fallthrough_twice_bi(x) ? colorGreen : colorRed;
}

View File

@ -0,0 +1,52 @@
uniform vec4 colorGreen;
uniform vec4 colorRed;
bool switch_with_continue_in_loop_bi(int x) {
int val = 0;
int _tmpSwitchValue1 = x, _tmpSwitchFallthrough0 = 0;
for (int _tmpSwitchLoop2 = 0; _tmpSwitchLoop2 < 1; _tmpSwitchLoop2++) {
if ((_tmpSwitchValue1 == 1)) {
for (int i = 0;i < 10; ++i) {
++val;
continue;
++val;
}
_tmpSwitchFallthrough0 = 1;
}
++val;
}
return val == 11;
}
bool loop_with_break_in_switch_bi(int x) {
int val = 0;
for (int i = 0;i < 10; ++i) {
int _tmpSwitchValue4 = x, _tmpSwitchFallthrough3 = 0;
for (int _tmpSwitchLoop5 = 0; _tmpSwitchLoop5 < 1; _tmpSwitchLoop5++) {
if ((_tmpSwitchValue4 == 1)) {
++val;
break;
_tmpSwitchFallthrough3 = 1;
}
return false;
}
++val;
}
return val == 20;
}
vec4 main() {
int x = int(colorGreen.y);
int _0_val = 0;
int _tmpSwitchValue7 = x, _tmpSwitchFallthrough6 = 0;
for (int _tmpSwitchLoop8 = 0; _tmpSwitchLoop8 < 1; _tmpSwitchLoop8++) {
if ((_tmpSwitchValue7 == 1)) {
for (int _1_i = 0;_1_i < 10; ++_1_i) {
++_0_val;
break;
++_0_val;
}
_tmpSwitchFallthrough6 = 1;
}
++_0_val;
}
return (_0_val == 2 && switch_with_continue_in_loop_bi(x)) && loop_with_break_in_switch_bi(x) ? colorGreen : colorRed;
}