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>
diff --git a/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp b/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp
index d4479db..5c86c94 100644
--- a/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp
@@ -1355,8 +1355,66 @@
void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
if (fProgram.fConfig->strictES2Mode()) {
- // TODO(skia:12450): write switch compatibility code
- fContext.fErrors->error(s.fOffset, "switch statements are not supported");
+ String fallthroughVar = "_tmpSwitchFallthrough" + to_string(fVarCount++);
+ 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 (");