Skip to content
Henrik Rydgård edited this page Mar 16, 2018 · 15 revisions

HLSL FAQ

Overview

This document will describe the usage of HLSL in glslang, including basic invocation, creating SPIR-V modules, binding and linkage, and reflection queries. It is not meant to describe the HLSL language.

Currently the HLSL FE is complete enough to run many types of complex real world workloads. A few incomplete areas will be described in "Limitations" below. It can accept shaders for any shader stage, and will handle common language constructs for functions, control flow, variable and type declarations, registers and pack offsets, most DX10 and later texture methods, most intrinsic functions, most preprocessor functionality, most built in semantics, attributes where they effect stage functionality, and more.

Please note that the HLSL FE is not currently a validator. It accepts well formed shaders, but may not reject invalid shaders. Behavior in the latter case is undefined except where explicitly noted.

Basic Usage

Obtaining and Building

Obtaining and building glslang is outside the scope of this document. The github source is here. Please see the README.md file included with the project for details.

Compiling HLSL shaders

Simple Example

This document will assume that glslangValidator is somewhere in your search path, or that you have given a path to it explicitly. "glslangValidator -?" will print a usage message including the options below.

Here is an example of a simple compilation of an HLSL shader:

glslangValidator -e main -o test.spv -V -D myshader.frag

This will compile the HLSL shader given in "myshader.frag", with an entry point named "main", and produce a SPIR-V binary module as output in "test.spv".

The options above are as follows:

The "-D" option indicates that the input is an HLSL shader. The shader stage can be automatically determined from the file suffix if the suffix is in the following list:

vert   for a vertex shader
tesc   for a hull (tessellation control) shader
tese   for a domain (tessellation evaluation) shader
geom   for a geometry shader
frag   for a pixel (fragment) shader
comp   for a compute shader

The "-e name" option specifies the shader entry point function name.

The -V option creates a SPIR-V binary.

The "-o name" option provides the filename for the SPIR-V binary - in this case "test.spv"

Common Options

If the file to be compiled does not have a suffix as above, the stage can be explicitly provided using the -S option. For example:

glslangValidator -S frag -e main -o test.spv -V -D myshader.frag

The "-q" option can be used to print the reflection database:

glslangValidator -q -S frag -e main -o test.spv -V -D myshader.frag

There are other options related to binding mappings that will be described later.

Advanced Options

--keep-uncalled  This will preserve uncalled functions.  Normally they are eliminated from the output.

--shift-sampler-binding  These are described in the binding section of this document below.
--shift-texture-binding
--shift-cbuffer-binding
--shift-uav-binding
--auto-map-bindings
--hlsl-iomap

--hlsl-offsets  *TODO...*

Limitations

The following features are unimplemented or mostly unimplemented. This may be an incomplete list.

  • DX9 texturing
  • Some preprocessor features
  • Some mixtures of object types, e.g, structured buffers as members in classes or structures.
  • mapping of "half" type to float
  • inout parameter bidirectional conversions
  • #include support
  • immediate sampler modes
  • most Shader model 6 features
  • Command line specification of preprocessor symbols
  • Texture2D etc templatized on user structures (as opposed to float4, int4, etc)
  • Implicit matrix truncations
  • GatherCmp* methods except for *Red.

The following have partial support:

  • classes (no inheritance from interfaces yet)
  • namespaces
  • some unusual intrinsic functions are unimplemented. E.g, printf.
  • GetSamplePosition returns fixed positions, and only for 1, 2, 4, 8 and 16 cases. Others produce (0,0).

Resource Bindings

IO variables can adopt bindings from ": register()" specifications in the shader, or be automatically assigned bindings. Offsets can be provided per HLSL register space, and these bindings can be queried with the reflection interface described below.

register()

Binding numbers are can be assigned in the shader text using the register() syntax. For example, this will create a tbuffer named "tbufName" with a binding number of 8:

tbuffer tbufName : register(b8) { ...

The same applies for other resource classes, for example, samplers.

Binding numbers can also be assigned through attributes in front of declarations:

[[vk::binding(binding, set)]]

Some examples can be found [here].(https://github.com/KhronosGroup/glslang/blob/046bae0babd17ecc19fc7cbe40c35aa13ac2ee65/Test/hlsl.attributeC11.frag)

auto-mapped binding numbers

IO variables without explicit bindings can be auto-assigned bindings using the --auto-map-bindings command line option. HLSL shaders should also include --hlsl-iomap to perform the mapping in HLSL register class space. For example:

glslangValidator -D -e main -q --hlsl-iomap --auto-map-bindings test.frag

The mapping of HLSL types to register spaces is as follows:

t – for shader resource views (SRV)
    TEXTURE1D
    TEXTURE1DARRAY
    TEXTURE2D
    TEXTURE2DARRAY
    TEXTURE3D
    TEXTURECUBE
    TEXTURECUBEARRAY
    TEXTURE2DMS
    TEXTURE2DMSARRAY
    STRUCTUREDBUFFER
    BYTEADDRESSBUFFER
    BUFFER
    TBUFFER
    
s – for samplers
    SAMPLER
    SAMPLER1D
    SAMPLER2D
    SAMPLER3D
    SAMPLERCUBE
    SAMPLERSTATE
    SAMPLERCOMPARISONSTATE

u – for unordered access views (UAV)
    RWBYTEADDRESSBUFFER
    RWSTRUCTUREDBUFFER
    APPENDSTRUCTUREDBUFFER
    CONSUMESTRUCTUREDBUFFER
    RWBUFFER
    RWTEXTURE1D
    RWTEXTURE1DARRAY
    RWTEXTURE2D
    RWTEXTURE2DARRAY
    RWTEXTURE3D

b – for constant buffer views (CBV)
    CBUFFER
    CONSTANTBUFFER

These flags can also be set through the TShader API:

void TShader::setAutoMapBindings(bool map);
void TShader::setHlslIoMapping(bool hlslIoMap);

Binding Offsets

IO objects can have offsets applied in the HLSL register class namespace class as described above. These command line options provide an offset which will be added to the binding number provided by either register() or --auto-map-bindings:

--shift-sampler-binding  s#
--shift-texture-binding  t#
--shift-cbuffer-binding  b#
--shift-uav-binding      u#

These have shortcuts: for example, --ssb is a synonym for --shift-sampler-binding. See the help message for the entire set.

Here is an example which uses the above options (in shortcut form) along with binding automapping, in HLSL register space:

glslangValidator --amb --hlsl-offsets --sbb 20 --suavb 30 -q -e main -i -V -D test.frag

These values can also be set through the TShader API:

void TShader::setShiftSamplerBinding(unsigned int base);
void TShader::setShiftTextureBinding(unsigned int base);
void TShader::setShiftUavBinding(unsigned int base);
void TShader::setShiftCbufferBinding(unsigned int base);

Custom mappings can be created by overriding the TIoMapResolver class.

Reflection Queries

There are two groups of uniform reflection data, both with type information attached which can be queried through a TProgram class API. The first group is uniform variables, and the second is for uniform blocks. The number of each can be queried via:

int TProgram::getNumLiveUniformVariables() const;
int TProgram::getNumLiveUniformBlocks() const;

A variable name can be mapped to a uniform index via:

int TProgram::getUniformIndex(const char* name) const;

The uniform block index associated with a uniform variable index can be obtained via:

int TProgram::getUniformBlockIndex(int index) const;

Type Data

The reflection API is also defined in the TProgram class. Type information for uniforms, or uniform blocks, can be obtained via:

const TType* TProgram::getUniformTType(int index) const;
const TType* TProgram::getUniformBlockTType(int index) const;

StructuredBuffer counter buffers

Some StructuredBuffer types have associated counter buffers. These types are:

AppendStructuredBuffer
ConsumeStructuredBuffer
RWStructuredBuffer

The counter buffer is a separate buffer object. Its index can be queried with the following API:

int TProgram::getUniformBlockCounterIndex(int index) const;

The index argument is an index in the Uniform Block Reflection list for the structured buffer. The return value is another index in the Uniform Block Reflection list for the associated counter. If there is no associated counter, the query returns -1.

Performance Considerations

  • Currently, hull shader patch constant functions with control point frequency inputs are emulated by invoking the hull shader entry point once for each control point.

Other Tools

The SPIRV-tools project contains a number of tools that are useful in conjunction with SPIR-V modules created through glslang:

  • spirv-opt is a module optimizer.
  • spirv-val is a module validator.
  • spirv-dis is a module disassembler.
  • spirv-stats prints statistics for a module.

See the project's README for more details.