Flow Control Statement Syntax

Flow-control statements determine which statement block to execute next. There are several flow-control statements including:

Most current vertex and pixel shader hardware is designed to run a shader line by line, executing each instruction once. HLSL supports flow control, which includes static branching, predicated instructions, static looping, dynamic branching, and dynamic looping.

The most common branching support in current hardware shading models is static branching. Static branching allows a statement block to be switched on or off based on a Boolean shader constant. This is a convenient method for enabling or disabling code paths. Between draw calls you can decide which features you want to support with the current shader, and then set the Boolean flags required to get that behavior. Now that HLSL supports branching, any statements that are disabled by a Boolean constant are skipped during execution.

The most familiar branching support is dynamic branching. With dynamic branching, the comparison condition resides in a variable, which means that the comparison is done for each vertex or pixel at runtime (as opposed to the comparison occuring at compile time, or between 2 draw calls). The performance hit is the cost of the branch plus the cost of the instructions on the side of the branch taken. Dynamic branching is currently available for vertex shaders. Optimizing shaders that work with these models is similar to optimizing code that runs on a CPU.

if statement

The if statement chooses which statement block to execute next based on the result of a comparison:

if ( (Normal dot LightDirection) > 0 ) 

The comparison is followed by the statement block:

{
    // face is lit, so add a diffuse color component for example
    ...  
}

Remember that a statement block is one or more statements, enclosed in curly braces. The statement block can be expanded up to n statements, as long as you don't exceed the shader instruction slot count.

An if statement can also use an optional else block. If the if expression is true, the code in the statement block associated with the if statement is processed. Otherwise, the statement block associated with the optional else block is processed.

do statement

The do statement executes a statement block, and then evaluates a conditional expression to determine whether to execute the statement block again. This is repeated until the conditional expression fails.

do  
{
  // one or more statements
  color /= 2;
} 
    while ( color.a > 0.33f )

This code divides the color components by 2, and then checks the resulting alpha component to see if the first statement will be repeated. Each subsequent time that the alpha component is greater than 0.33f, the color components are divided in half. When the alpha component is less than or equal to 0.33f, the while comparison will fail and the program will continue at the next instruction.

This example uses a statement block that has a single statement in it. The statement block can be expanded to n statements.

do  {
    color.r /= 2;
    color.g /= 4;
    color.b /= 8;
    ....  // Other statements
    color.a /= 8; 
}
    while ( color.a > 0.33f )

Don't forget to modify the alpha value, either in the statement block or in the comparison statement. Otherwise, you will cause a infinite loop, which will definitely reduce the shader throughput.

for statement

A for statement implements a loop that provides static control over the number of times a statement block will be executed. It contains an initialization expression, a comparison expression, and an increment (or decrement) expression, followed by a statement block.

for ( int i = 0; i<2; i++ )
{
   // one or more statements
   ...
   statement n;
};

Here is an example that uses a loop to sample a texture over four different sets of texture coordinates.

sampler RenderTarget;
float4 textureCoordinates[4]; 
float4 outColor[4];

for ( int i = 0; i<3; i++ )
{
    outColor[i] = tex2D(RenderTarget, textureCoordinates[i]);
}

The statement block is executed each time that the comparison expression succeeds.

while statement

The while statement implements a loop, which evaluates an expression to determine whether to execute a statement block.

while ( color.a > 0.33f )
{
    color /= 2; 
}

This code checks to see if the alpha component is greater than 0.33f. For each time that it is, the color components are each divided in half. As soon as alpha is less than or equal to 0.33f, the comparison expression will fail and the program will continue at the next instruction after the statement block.

This example uses a statement block that has a single statement in it.

    color /= 2; 

When a single statement is used, the enclosing curly braces are optional. The statement block can be expanded to n statements.

while ( color.a > 0.33f )
{
    color.r /= 2;
    color.g /= 4;
    color.b /= 8;
    ....  // Other statements
    color.a /= 8; 
}

Don't forget to modify the alpha value, either in the comparison expression or in the statement block.

Remarks

Curly braces ({}) start and end a statement block. When a statement block uses a single statement, the curly braces are optional. Everyone has their own preference for handling an if statement that encompasses one statement.

if ( some expression )
   color.rgb = tex3D(Sampler, texturecoordinates);

This is equivalent to using the curly braces:

if ( some expression )
{
   color.rgb = tex3D(Sampler, texturecoordinates);
}

Some people find this easier to read.

Previously, using an if statement resulted in assembly-language shader code that implements both the if side and the else side of the code flow. The code is executed linearly and the output results from the side of the if statement that would have been taken. Here is the corresponding HLSL code:

if (Value > 0)
    oPos = Value1; 
else
    oPos = Value2; 

And here is the assembly code, compiled for vs_1_1:

// Calculate linear interpolate (lerp) value in r0.w
mov r1.w, c2.x               
slt r0.w, c3.x, r1.w         
// Lerp between value1 and value2
mov r7, -c1                      
add r2, r7, c0                   
mad oPos, r0.w, r2, c1  

Some hardware allows for either static or dynamic looping, but most require linear execution. On the models that do not support looping, all loops must be unrolled. An example is the DepthOfField Sample sample that uses unrolled loops even for ps_1_1 shaders.