GLSL Switch Evaluation Without Break Statements

In the OpenGL Shading Language (GLSL), the switch statement controls program flow by matching an integer selector against constant integer case labels. When a developer omits break statements between these labels, GLSL exhibits standard fall-through behavior identical to C and C++. This article explains the exact execution mechanics of uninhibited fall-through, clarifying how the GPU evaluates subsequent case labels, transfers execution control, and ultimately resolves statement blocks in shader code.

The Initial Evaluation and Jump

The switch statement accepts a scalar expression that evaluates to an integer (int or uint). During execution, GLSL evaluates this expression exactly once. The runtime then compares the result against the compile-time constant expressions provided in each case label.

Once a matching integer label is found, the GPU directly jumps program control to the first statement following that label. If no case matches and a default label exists, execution jumps directly to the default block.

Fall-Through Mechanics and Label Ignorance

When a break statement is omitted, execution does not stop at the end of the matched case block. Instead, it enters a state known as fall-through:

  1. Subsequent Labels Are Not Evaluated: Once the initial match is made, the GPU does not evaluate or compare any remaining case labels against the selector variable. In GLSL, case labels are purely compile-time jump targets (entry points) rather than runtime conditions.
  2. Linear Execution Continues: The shader program continues executing subsequent instructions linearly down the switch body, passing right through any subsequent case or default markers as if they were nonexistent.
  3. Termination Conditions: Fall-through execution continues uninterrupted until the control flow encounters an explicit exit statement—such as a break, return, or discard—or until it reaches the closing brace (}) of the switch block.

Code Demonstration

Consider the following GLSL code snippet:

int mode = 1;
int outputValue = 0;

switch (mode) {
    case 0:
        outputValue += 1;
        break;
    case 1:
        outputValue += 10;
        // break omitted intentionally
    case 2:
        outputValue += 100;
        break;
    default:
        outputValue += 1000;
}

In this example:

  • The expression mode evaluates to 1.
  • Control jumps directly to case 1:, adding 10 to outputValue.
  • Because there is no break, execution continues directly into case 2:. The label case 2: is not re-checked against mode.
  • The statement outputValue += 100; executes, making outputValue equal to 110.
  • Control then encounters the break; statement inside the case 2 section, successfully terminating the switch construct and bypassing the default block.

Compiler and Hardware Considerations

While fall-through is valid in GLSL (from version 1.30 and ESSL 3.00 onward), hardware implementations flatten or unroll these control flows into branch-and-jump instructions at the machine code level. Omitting break statements intentionally can be useful for grouping multiple values that share common logic, but doing so accidentally can result in unexpected state modifications and divergent thread execution across a warp or wavefront.