Shader Converter

DirectX and OpenGL use different shading languages. While mostly being very similar, the language syntax differ substantially in some places. Having two versions of each shader is clearly not an option for real projects. Maintaining intermediate representation that translates to both languages is one solution, but it might complicate shader development and debugging.

HLSL converter allows HLSL shader files to be converted into GLSL source. The entire shader development can thus be performed using HLSL tools. Since no intermediate representation is used, shader files can be directly compiled by HLSL compiler. All tools available for HLSL shader development, analysis and optimization can be used. The source can then be transparently converted to GLSL.

Conversion details

Please visit this page for the full list of supported language features.

Requirements

The following requirements must be met in order for the HLSL shader to be successfully converted to GLSL shader:

  • Shader entry point must be declared as void function.
  • Structure members cannot be assigned system-value semantics (such as SV_Position). Such variables must be declared as direct shader input/output.
  • User-defined semantics must be assigned to structure members. Nested structures are not supported.
  • All vertex shader inputs must have ATTRIB#  semantic or a system-value semantic.
  • Below are examples of supported vertex/pixel shader declarations:

  •  
  • GLSL allows samplers to be declared as global variables or function arguments only. It does not allow local variables of sampler type.

Textures and samplers

The following rules are used to convert HLSL texture declaration into GLSL sampler:

  • HLSL texture dimension defines GLSL sampler dimension:
    • Texture2D        sampler2D
    • TextureCube      samplerCube
  • HLSL texture component type defines GLSL sampler type. If no type is specified, float4  is assumed:
    • Texture2D<float>            sampler2D
    • Texture3D<uint4>            usampler3D
    • Texture2DArray<int2>        isampler2DArray
    • Texture2D               →    sampler2D
  • To distinguish if sampler should be shadow or not, the converter tries to find <Texture Name>_sampler among samplers (global variables and function arguments). If the sampler type is comparison, the texture is converted to shadow sampler. If sampler state is either not comparison or not found, regular sampler is used. Examples:

    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓


     

GLSL requires format to be specified for all images (rw textures) allowing writes. HLSL converter allows GLSL image format specification inside the special comment block:
RWTexture2D<float /* format=r32f */ > Tex2D;

Important notes/known issues:

  • GLSL compiler does not handle float3 structure members correctly. It is strongly suggested avoid using this type in structure definitions
  • At least NVidia GLSL compiler does not apply layout(row_major) to structure members. By default, all matrices in both HLSL and GLSL are column major
  • GLSL compiler does not properly handle structs passed as function arguments!!!!

    DO NOT pass structs to functions, use only built-in types!!!
  • GLSL does not support most of the implicit type conversions. The following are some examples of the required modifications to HLSL code:
    • float4 vec = 0; ->  float4 vec = float4(0.0, 0.0, 0.0, 0.0);
    • float x = 0;    ->  float x = 0.0;
    • uint x = 0;     ->  uint x = 0u;
    • GLES is immensely strict about type conversions. For instance, this code will produce compiler error: float4(0, 0, 0, 0). It must be written as float4(0.0, 0.0, 0.0, 0.0)
  • GLSL does not support relational and boolean operations on vector types:

    • To facilitate relational and Boolean operations on vector types, the following functions are predefined:
      • Less
      • LessEqual
      • Greater
      • GreaterEqual
      • Equal
      • NotEqual
      • Not
      • And
      • Or
      • BoolToFloat
    • Examples:
      • bool2 b = x < y;   ->   bool2 b = Less(x, y);
      • all(p>=q)          ->   all( GreaterEqual(p,q) )
  • When accessing elements of an HLSL matrix, the first index is always row:
    mat[row][column]
    In GLSL, the first index is always column:
    mat[column][row]
    MATRIX_ELEMENT(mat, row, col)  macros is provided to facilitate matrix element retrieval
  • The following functions do not have counterparts in GLSL and should be avoided:
    • Texture2DArray.SampleCmpLevelZero()
    • TextureCube.SampleCmpLevelZero()
    • TextureCubeArray.SampleCmpLevelZero()
  • Shader converter creates shader interface blocks to process non-system generated input/output parameters. For instance, to process Out parameter of the vertex shader below

    the following interface block will be created:

    OpenGL requires that interface block definitions in different shader stages must match exaclty: they must define the exact same variables (type/array count and NAME), in the exact same order. Since variable names must match, this effectively means that shader input/output parameter names must also match exactly. This limitation seems to be relaxed in desktop GL and some GLES. For instance, the following code works fine on Desktop GL and on Intel GLES, but fails on NVidia GLES:

    To make it run on NVidia GLES, shader intput/output parameter names must be exactly the same:

    Moreover, even when fragment shader does not actually use the parameter, it still must be declared.

    • If the requirements above are not met, the shaders are still compiled successfuly, but glDraw*() command fails with useless error 1282 ( IVALID_OPERATION)

Limitations

  • Using Texture* keywords in macros is not supported. The following lines will not work:
  • Only vertex, pixel and compute shaders are currently supported