- Qt 6.8
- Shader Tools
- Qt Shader Tools Build System Integration
- Qt 6.8.3 Reference Documentation

# Qt Shader Tools Build System Integration

Compiles shaders and adds them to a Qt resource

## Introduction

The Qt Shader Tools module provides a CMake macro file that provides useful functions applications can take into use in their `CMakeLists.txt`.

When using the `qt6_add_shaders` function, the qsb tool will get invoked automatically by the build system, and the resulting `.qsb` files get added to the resource system implicitly.

## First Example

Let's look at a simple example. Assume that we have a Qt Quick application that wants to provides its own wobble effect via ShaderEffect. The fragment shader is implemented in `wobble.frag`. The ShaderEffect item's fragmentShader property refers to `wobble.frag.qsb`. How do we ensure this .qsb file gets generated at build time?

     ...
     project(exampleapp LANGUAGES CXX)
     ...
     find_package(Qt6 COMPONENTS ShaderTools)
     ...
     qt6_add_executable(exampleapp
         main.cpp
     )
     ...
     qt6_add_resources(exampleapp "exampleapp"
         PREFIX
             "/"
         FILES
             "main.qml"
     )

     qt6_add_shaders(exampleapp "exampleapp_shaders"
         PREFIX
             "/"
         FILES
             "wobble.frag"
     )

The above is sufficient to enable the application to access `:/wobble.frag.qsb` at run time. The original Vulkan-style GLSL source code (wobble.frag) is not included in the application's executable and does not need to be shipped. If there are errors in the shader code, the `glslang` compiler messages are printed at build time and the build fails. When changing the shader source file, the changes are picked up automatically in the next build, like they would for C++ and other source files.

The key is the `qt6_add_shaders` function, which shares similarity with `qt6_add_resources`. Without specifying further parameters, the function will lead to running qsb with a reasonable set of default arguments that are suitable for fragment shaders when targeting Vulkan, Metal, Direct 3D, and OpenGL or OpenGL ES.

Note: Watch out for the `find_package` line. It is important to include the `find_package` for `ShaderTools`, otherwise `qt6_add_shaders` will not be available.

Note: The target that is passed as the first argument of the `qt6_add_shaders` function must exist before the function is called.

Note: Multiple `qt6_add_shaders` calls are supported. In complex applications it is not unlikely that different sets of shaders need different settings. The name after the project (`"exampleapp_shaders"` in the above example) has to be unique for each call.

## Configuration

By default `qt6_add_shaders` invokes qsb as follows:

     qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -o <output>.qsb <input>

This means that the resulting package will contain SPIR-V (for Vulkan 1.0), GLSL ES 100 (for OpenGL ES 2.0 and newer), GLSL 120 (for non-core profile OpenGL contexts), GLSL 150 (for core profile OpenGL contexts), HLSL source for Shader Model 5.0 (for Direct3D 11.1), and Metal Shading Language 1.2 source (for Metal).

This is a good set of defaults for Qt Quick, and creates applications that are highly portable to a wide variety of systems. These defaults are not always suitable however. If the shader uses functions or constructs that do not have an equivalent in these targets, the process, and so the build, will fail. If that is the case, the targets will need to be adjusted, and this also means that the application's minimum system requirements get adjusted implicitly. As an example, take the `textureLod` GLSL function that is only available with OpenGL ES 3.0 and up (meaning GLSL ES 300 or higher). When requesting GLSL `300 es` instead of `100 es`, the build will succeed, but the resulting application will now require OpenGL ES 3.0 or higher and will not be compatible with OpenGL ES 2.0 based systems.

### Shader type

The type of shader is deduced from the file extension. Thus, the extension must be one of the following:

- `.vert` - for vertex shaders
- `.tesc` - for tessellation control shaders
- `.tese` - for tessellation evaluation shaders
- `.frag` - for fragment (pixel) shaders
- `.comp` - for compute shaders

Note: Tessellation control and evaluation shaders are not currently supported with Direct 3D (HLSL). A possible workaround to this is to manually create hull and domain shaders and inject them via the file subsitution syntax in the `FILES` section.

### Targets

The following keywords are available:

- `GLSL` - Requests generating source code for the given list of GLSL versions. Watch out that the list follows the comma-separated `qsb` syntax. For example, a compute shader will want to specify `"310 es,430"` here as the defaults are not suitable for it.
- `NOGLSL` - This argument-less keyword disables generating GLSL source. Suitable for applications that do not wish to function with OpenGL at all.
- `HLSL` - Requests generating source code for the given list of HLSL (shader model) versions. The `qsb` tool follows GLSL-style version numbers and therefore `50` corresponds to Shader Model 5.0, `51` is 5.1.
- `NOHLSL` - This argument-less keyword disables generating HLSL source. Suitable for applications that do not wish to function with Direct 3D at all.
- `MSL` - Requests generating source code for the given version of the Metal Shading Language. `12` corresponds to 1.2, `20` to 2.0.
- `NOMSL` - This argument-less keyword disables generating MSL source. Suitable for applications that do not wish to function with Metal at all.

The most commonly overridden setting is `GLSL`. For example, if the application's shaders use OpenGL 3.x features, it will likely want to specify something higher than `100 es` or `120`:

     qt_add_shaders(exampleapp "res_gl3shaders"
         GLSL "300es,330"
         PREFIX
             "/shaders"
         FILES
            shaders/ssao.vert
            shaders/ssao.frag
            shaders/skybox.vert
            shaders/skybox.frag
     )

Note: The space before the `es` suffix is optional.

### Tessellation

- `TESSELLATION` - This argument-less keyword indicates that the shaders are used in a pipeline that uses tessellation. This is relevant only when there are vertex shaders listed and Metal shader generation is not disabled. See this snippet for an example.

  This option was introduced in Qt 6.5.

- `TESSELLATION_VERTEX_COUNT` - This option takes a number which indicates the output vertex count from the tessellation control stage. Specifying this is mandatory for tessellation evaluation shaders used with Metal. The default value is 3. If it does not match the tessellation control stage, the generated MSL code will not function as expected.

  This option was introduced in Qt 6.5.

- `TESSELLATION_MODE` - This option specifies the tessellation mode. It can take one of two values: `"triangles"` or `"quads"`. The default value is `triangles`. The option must be specified when a tessellation control shader is in the `FILES` list. It must match the tessellation evaluation stage.

  This option was introduced in Qt 6.5.

### Multiview

- `VIEW_COUNT` - This option specifies the number of views a vertex shader is used with. When working with multiview (GL_OVR_multiview2, VK_KHR_multiview, D3D12 view instancing, etc.), always specify the correct VIEW_COUNT with a value >= 2 for the relevant shaders in order to enable generating correct GLSL shader code. Note however that setting VIEW_COUNT should be avoided for vertex shaders that do not rely on multiview, since setting the value effectively makes the generated GLSL code multiview-dependent. To overcome this, group the vertex shaders accordingly into multiple qt_add_shaders() calls. Setting `VIEW_COUNT` automatically injects a preprocessor define, `QSHADER_VIEW_COUNT`, with the same value into the shader source code. In addition, the `#extension GL_EXT_multiview : require` line is injected automatically in vertex shaders when a view count of 2 or greater is set. Note that for multiview the minimum GLSL version is `330` and `300 es`. For HLSL, the minimum version is `61`. Applications are advised to set up the language targets accordingly (or to newer versions).

  This option was introduced in Qt 6.7.

- `MULTIVIEW` - Requests generating both a non-multiview and a view count 2 set of shaders. This is effectively a convenience for manually creating two qt_add_shaders() calls with the appropriate GLSL/HLSL/MSL/VIEW_COUNT arguments. This option is meant mainly for Qt's own use, but applications can use it too as long as the implicit settings for the multiview variants are sufficient for the application's shaders; these are: GLSL 330,300es HLSL 61 MSL 12. VIEW_COUNT is set to 2 for the multiview variants. The multiview variants are stored in files with a `.mv2qsb` suffix added (in addition to `.qsb`).

  This option was introduced in Qt 6.8.

### Qt Quick specifics

- `BATCHABLE` - Specifying this single, argument-less keyword is essential for vertex shaders that are used with Qt Quick, either in a ShaderEffect or in a QSGMaterialShader. It has no effect for fragment or compute shaders, and different types can safely be included in the same list since the keyword is taken into account only for the `.vert` files. Equivalent to the `-b` argument of qsb.
- `ZORDER_LOC` - When `BATCHABLE` is specified, an additional vertex input is injected with location `7` by default. This keyword is used to change this location to another value. This becomes relevant if the vertex shader has many inputs and 7 is in use and would clash.

### Invoking external tools

- `PRECOMPILE` - Equivalent to the `-c` or `-t` options of qsb, depending on the platform. When building on Windows, this leads to invoking `fxc` from the Windows SDK to do the first phase of compilation (HLSL source to DXBC bytecode) at build time instead of at run time. The resulting `.qsb` file will only include the compilation results (the intermediate shader format), not the original shader source code.
- `OPTIMIZED` - Invokes `spirv-opt` (which must be available from the Vulkan SDK or elsewhere) to perform optimizations on the SPIR-V bytecode. Equivalent to the `-O` argument of qsb.

### Other settings

- `DEFINES` - Defines macros that are active during shader compilation. Equivalent to the `-D` argument of qsb. The list has the form of `"name1=value1;name2=value2"`. Alternatively, just like `FILES`, the list can be separated by newlines.
- `OUTPUTS` - When the name of the generated .qsb file needs to be different from the source, for example because one shader file serves as the source for multiple .qsb files due to differentiating via `DEFINES`, this list can contain an entry for each item in `FILES`, specifying a file name typically ending in `.qsb`. The specified name is then passed in the `-o` argument to qsb instead of just appending `.qsb` to the source file name.
- `DEBUGINFO` - Enables generating full debug information for SPIR-V, thus enabling tools like RenderDoc to display the full source when inspecting a pipeline or when performing vertex or fragment debugging. Equivalent to the `-g` argument of qsb. Also has an effect for Direct 3D in case the `PRECOMPILE` keyword has been specified, as `fxc` is then instructed to include debug information in the generated intermediate bytecode.
- `QUIET` - Suppresses debug and warning output from qsb. Only fatal errors are printed.
- `OUTPUT_TARGETS` - When using qt_add_shaders with static libraries, one or more special targets will be generated. Should you wish to perform additional processing on these targets pass a value to the OUTPUT_TARGETS parameter.

## Substituting Hand-Crafted Shaders

The CMake integration also supports specifying replacements for given versions of the shader in the resulting .qsb file. This in effect is equivalent to running qsb with the `-r` command-line option.

This is enabled by the following special syntax in the FILES list:

     FILES
         "shaders/externalsampler.frag@glsl,100es,shaders/externalsampler_gles.frag"

The filename can be followed by any number of @-separated replacement specifications. Each of these specifies the shading language, the version, and the file from which the data is to be read separated by commas. See the QSB Manual for details.

### Tessellation Example

Take a graphics pipeline consisting of four stages, the vertex stage with the shader `vertex.vert`, the tessellation control stage with the shader `tess.tesc`, the tessellation evaluation stage with the shader `tess.tese`, and the fragment stage with the shader `fragment.frag`.

To build a fully portable application that is able to function with any of Vulkan, OpenGL, Metal, and Direct 3D, there are two main things to take care of: The HLSL versions of the tessellation shaders must be created manually and then injected. Whereas with Metal, the appropriate keyword must be specified.

First, the vertex and fragment shaders are listed. In order to support Metal, the `TESSELLATION` keyword is added. This enables special processing and translation for `vertex.vert` when generating the Metal shader code. For OpenGL, we restrict the GLSL language version since tessellation support is only available in newer OpenGL versions.

     qt6_add_shaders(project "shaders_tessellation_part1"
         PREFIX
             "/shaders"
         GLSL
             "410,320es"
         TESSELLATION
         FILES
             "vertex.vert"
             "fragment.frag"
     )

Second, the tessellation shaders are listed in a separate qt6_add_shaders() call. This is due to the `NOHLSL` keyword. The vertex and fragment shaders should still get translated to HLSL as usual, so keeping all four shaders in one single qt6_add_shaders() call is not feasible. For Metal, some tessellation settings (output vertex count, mode) are specified since these will need to be known upfront, unlike with Vulkan and OpenGL.

     qt6_add_shaders(project "shaders_tessellation_part2"
         PREFIX
             "/shaders"
         NOHLSL
         GLSL
             "410,320es"
         TESSELLATION_VERTEX_COUNT
             3
         TESSELLATION_MODE
             "triangles"
         FILES
             "tess.tesc@hlsl,50,tess_hull.hlsl"
             "tess.tese@hlsl,50,tess_domain.hlsl"
     )

Note: Manually writing hull and domain HLSL shaders is recommended for advanced users only. Certain constructs, such as constant buffers, will need special care so that all resource interfaces and layouts stay compatible with the SPIR-V/GLSL/MSL shaders.