Intervals

Intervals are a way to generate multiple variants of a single shader, based on whether the value of a special variable falls into specific range. They can be created from an int or float shader variable using the interval keyword. Consider this example:

int var;
interval var:below_zero<0, zero<1, positive_less_than_four<4, four<5, five_or_higher;
// same goes for floats

This declaration will create 5 permutations of the shader (below_zero, zero, positive_less_than_four, four and five_or_higher), and the var interval itself can be accessed in hlsl blocks with special preprocessor tag ##.

int var;
interval var:below_zero<0, zero<1, positive_less_than_four<4, four<5, five_or_higher;

shader example_shader {
  hlsl {
    ##if var == five_or_higher
      ...
    ##endif
  }
}

It is essential to understand that this construction doesn’t mean that var equals to 5, but it means var is within the declared interval five_or_higher, so this syntax applies to float numbers as well.

Warning

Notice that intervals can be created not only from global variables (as in example above), but also from static and dynamic variables (described in Local variables).

Intervals on static variables are resolved (appropriate shader variant is selected) only once during material instantiation, so shader variants dependant on static variables are not switched during runtime.

Intervals on dynamic and global variables, however, are resolved each time the shader is used for rendering (on setStates() call), because such variables can change during runtime. This has worse CPU performance impact, compared to static intervals.

Optional intervals

If the interval is used in HLSL code blocks, you can make this interval optional. All conditions in HLSL code which use optional intervals will be replaced with HLSL branches, thus reducing the number of shader variants.

Warning

Shaders with optional intervals can be used only for conditionals in HLSL blocks and need to be compiled with -optionalAsBranches flag (otherwise optional intervals will act the same as regular intervals). This feature should only be used for dev build.

int test_optional = 0;
optional interval test_optional : zero < 1, one < 2, two < 3, three;

shader testMaterial
{
  // ...
  hlsl
  {
    float3 color = float3(0, 0, 0);
##if test_optional == one
    color = float3(1, 0, 0);
##elif test_optional == two || test_optional == three
    color = float3(0, 0, 1);
##if test_optional == two
    color *= 0.5;
##endif

##else
    color = float3(0, 0, 1);
##endif
  }
}

Assumes

Shader variables can be assigned a fixed value when the shader is compiled by assuming. Such shader vars may not be changed at runtime, their values will be constant in the binary. This allows to reduce number of shader variants or disable specific features for specific platforms.

You can assume intervals inside a config .blk file for the shader compiler. To do this, create an assume_vars block inside a Compile block and then specify the assumes you want, following regular .blk syntax:

Compile
{
  // ...
  assume_vars
  {
    include common_assumes.blk
    supports_sh_6_1:i = 0
    static_shadows_custom_fxaa:i=0
    grass_use_quads:i=0
    bloom_tex:i = 1
  }
}

Note

By assuming a texture, e.g. bloom_tex:i = 1, you declare that this texture is never NULL.

Assume statement

You can use assume interval_name = interval_value; statement in DSHL shaders to force an interval to be fixed. This can be useful to disable unnecessary shader variants when the same interval is shared among different shaders.

include "deferred_shadow_common.dshl"

shader deferred_shadow_classify_tiles
{
  assume use_ssss = off;
  // ...
}