HLSL to GLSL Source Converter

DirectX and OpenGL use different shading languages, which share a lot in common, but sometimes differ substantially. For cross-platform applications, maintaining two versions of each shader is time-consuming and error-prone. Diligent Engine uses HLSL2GLSL Converter that allows shader authored in HLSL to be converted into GLSL source. Since no intermediate representation is used, all tools available for HLSL shader development, analysis and optimization can be used directly, while GLSL source is automatically generated from HLSL shaders.

Conversion details

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

Requirements

The converter supports HLSL5.0, all shader types (vertex, geometry, pixel, domain, hull, and compute) as well as most of the language constructs. There are however few special requirements that must be met in order for the HLSL source to be successfully converted to GLSL:

  • Inputs to a vertex shader may have ATTRIBn semantic, where  n defines the location of the corresponding GLSL input variable ( layout(location = n)). For any other input semantic, the converter automatically assigns input location
  • Inputs of a subsequent shader stage must be declared in exact same order as outputs of the previous shader stage. Return value of a function counts as its first output.
    • The converter parses all input and output arguments (including structure members) in the order of declaration and automatically assigns locations to every argument. To make sure that input and output locations match, the arguments must be declared in exact same order. For the same reason, if an argument is not used by the shader, it still needs to be declared to preserve original ordering.
      The code snippet below gives examples of supported shader declarations:

  • When tessellation is enabled in OpenGL, partitioning as well as output patch topology are defined by the tessellation evaluation shader (domain shader) rather than by the tessellation control shader (hull shader). As a result, the converter cannot generate GLSL code without using special hints. To indicate missing attributes, the following specially formatted comment should be added right on top of the domain shader entry function:

    For example, the following is a valid domain shader declaration:
  • Geometry, Domain and Hull shaders must be defined in separate files
  • 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; -> b = Less(x, y);
      • all(p>=q) -> all( GreaterEqual(p,q) )
  • When accessing elements of an HLSL matrix, the first index is always a row:
    mat[row][column]
    In GLSL, the first index is always a 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()

Limitations

Converter does not perform macros expansion, so usage of preprocessor directives is limited to text block that do not need to be converted. The following are some examples that are not supported:

  • Using macros in declarations of shader entry points:

    The following is not allowed as well:

    In cases like that it is necessary to create two separate shader entry points and give them distinctive names. Likewise, macros cannot be used in definitions of structures that are used to pass data between shader stages:

    Similarly to shader entry points, in the scenario above, the two structures need to be defined with distinctive names. Shader macros are allowed in structures that are not used to pass data between shader stages.
  • Defining language keywords with macros is not allowed:

Macros can be used within function bodies: