12.3 - GLSL Control Structures

GLSL is based on of the C programming language and its control structures are very similar to C.

Overall Execution

A shader program is composed of one or more functions. Execution always begins with the main function which receives no parameters and returns no value:

void main(void) {
  // statement(s)
}

There is no limit to the number of functions you can create. Functions must be defined before they can be called. A function header defines a function’s name, its parameter list, and the data type of it’s return value. For example:

vec3 example(float x, bool beta) {
  // statement(s)
}

defines a function called “example” that receives two values when it is called, a floating point value and a Boolean value. It must return a vector of 3 floating point values.

All parameters are “pass by value” by default. You can change this behavior using these “parameter qualifiers”:

  • in: “pass by value”; if the parameter’s value is changed in the function, the actual parameter from the calling statement is unchanged.
  • out: “pass by reference”; the parameter is not initialized when the function is called; any changes in the parameter’s value changes the actual parameter from the calling statement.
  • inout: the parameter’s value is initialized by the calling statement and any changes made by the function change the actual parameter from the calling statement.

The following example demonstrates these parameter qualifiers:

vec3 example(in float x, in bool beta, inout int gamma, out int theta) {
  // statement(s)
}

// Call example: 3.5 is copied into x,
//               true is copied into beta,
//               delta is copied into gamma, and
//               chi is NOT copied into theta.
vec3 phi = example(3.5, true, delta, chi);
// After the call, the value of delta might be changed,
//                 the value of chi has changed,
//                 phi contains the returned value

Selection

The if statement allows statements to be executed or skipped based on a Boolean test. They can be nested, as shown in the following examples:

if (x <= 5) {
  // statement(s)
}

if (x <= 5) {
  // statement(s)
} else {
  // statement(s)
}

if (j == 1) {
    // statement(s)
} else {
    if (j == 2) {
        // statement(s)
    } else {
        if (j == 3) {
            // statement(s)
        } else {
            // statement(s)
        }
    }
}

Selection statements are discouraged because “they can reduce the ability to execute operations in parallel on 3D graphics processors” (1) Also from (1),

If your shaders must use branches, follow these recommendations:

* Best performance: Branch on a constant known when the shader is compiled.
* Acceptable: Branch on a uniform variable.
* Potentially slow: Branch on a value computed inside the shader.

Iteration

Repeating a group of statements can be done in one of three ways. These are demonstrated in the following examples. If the loop control variable is declared in the loop, its scope is limited to the loop. Loops can be nested.

for (int j = 0; j < 5; j += 1)
  // statement(s)
}

int j = 0;
while (j < 5) {
  // statement(s)
  j += 1;
}

int j = 0;
do {
  // statement(s)
  j += 1;
} while (j < 5);

The while and do-while loops are optional. The only loop construct you are guaranteed to have is the for loop. In addition, there are many restrictions on the looping constructs. In general “control flow is limited to loops where the maximum number of iterations can easily be determined at compile time.”

Restrictions on loops:

  • There can only be one loop control variable of type int or float.

  • The initialization of the for statement must be of the form:

    type-specifier identifier = constant-expression
    

    Consequently the loop control variable cannot be a global variable.

  • The test for loop termination of the for statement must have the form:

    loop_control_variable   relational_operator   constant_expression
    

    where relational_operator is one of: >, >=, <, <=, ==, or !=

  • The “update” of the loop control variable in the for statement must have the form:

    loop_control_variable++
    loop_control_variable--
    loop_control_variable += constant_expression
    loop_control_variable -= constant_expression
    
  • The loop control variable can not be changed in the body of the loop.

Modifying Control Inside a Loop

Inside a loop you can modify the flow of control with the following statements:

  • break: immediately terminates a loop and jumps to the first statement after the loop.
  • continue: skips any remaining statements in the loop and jumps to the next iteration of the loop.
  • return: immediately exits the current function, thus terminating the active loop.

Glossary

control structures
The statements in a language that determine the order of statement execution.
GLSL function
A set of related statements that perform a task and then return a value.
in function parameter
A value sent to a function at the start of a function’s execution.
out function parameter
A variable that is changed by a function and sent back to the calling function.
inout function parameter
A variable sent to a function and changed after the function’s execution is finished.
selection
The determination of which statements to execute and which statements to skip.
iteration
The repeated execution of a set of statements.
Next Section - 12.4 - GLSL Operators (Mathematical and Logical)