QShaderBaker Class

Compiles a GLSL/Vulkan shader into SPIR-V, translates into other shading languages, and gathers reflection metadata. More...

Header: #include <QShaderBaker>
Since: Qt 6.6

Public Types

Public Functions

QShaderBaker()
~QShaderBaker()
QShader bake()
QString errorMessage() const

Detailed Description

Warning: QShaderBaker, just like the QRhi family of classes in the Qt Gui module, including QShader and QShaderDescription, offers limited compatibility guarantees. There are no source or binary compatibility guarantees for these classes, meaning the API is only guaranteed to work with the Qt version the application was developed against. Source incompatible changes are however aimed to be kept at a minimum and will only be made in minor releases (6.7, 6.8, and so on). To use this class in an application, link to Qt::ShaderToolsPrivate (if using CMake), and include the headers with the rhi prefix, for example #include <rhi/qshaderbaker.h>.

QShaderBaker takes a graphics (vertex, fragment, etc.) or compute shader, and produces multiple - either source or bytecode - variants of it, together with reflection information. The results are represented by a QShader instance, which also provides simple and fast serialization and deserialization.

Note: Applications and libraries are recommended to avoid using this class directly. Rather, all Qt users are encouraged to rely on offline compilation by invoking the qsb command-line tool at build time via CMake. The qsb tool uses QShaderBaker and writes the serialized version of the generated QShader into a file. The usage of this class should be restricted to cases where run time compilation cannot be avoided, such as when working with user-provided or dynamically generated shader source strings.

The input format is always assumed to be Vulkan-flavored GLSL at the moment. See the GL_KHR_vulkan_glsl specification for an overview, keeping in mind that the Qt Shader Tools module is meant to be used in combination with the QRhi classes from Qt Rendering Hardware Interface module, and therefore a number of concepts and constructs (push constants, storage buffers, subpasses, etc.) are not applicable at the moment. Additional options may be introduced in the future, for example, by enabling HLSL as a source format, once HLSL to SPIR-V compilation is deemed suitable.

The reflection metadata is retrievable from the resulting QShader by calling QShader::description(). This is essential when having to discover what set of vertex inputs and shader resources a shader expects, and what the layouts of those are, as many modern graphics APIs offer no built-in shader reflection capabilities.

Typical Workflow

Let's assume an application has a vertex and fragment shader like the following:

Vertex shader:

 #version 440

 layout(location = 0) in vec4 position;
 layout(location = 1) in vec3 color;
 layout(location = 0) out vec3 v_color;

 layout(std140, binding = 0) uniform buf {
     mat4 mvp;
     float opacity;
 };

 void main()
 {
     v_color = color;
     gl_Position = mvp * position;
 }

Fragment shader:

 #version 440

 layout(location = 0) in vec3 v_color;
 layout(location = 0) out vec4 fragColor;

 layout(std140, binding = 0) uniform buf {
     mat4 mvp;
     float opacity;
 };

 void main()
 {
     fragColor = vec4(v_color * opacity, opacity);
 }

To get QShader instances that can be passed as-is to a QRhiGraphicsPipeline, there are two options: doing the shader pack generation off line, or at run time.

The former involves running the qsb tool:

 qsb --glsl "100 es,120" --hlsl 50 --msl 12 color.vert -o color.vert.qsb
 qsb --glsl "100 es,120" --hlsl 50 --msl 12 color.frag -o color.frag.qsb

The example uses the translation targets as appropriate for QRhi. This means GLSL/ES 100, GLSL 120, HLSL Shader Model 5.0, and Metal Shading Language 1.2.

Note how the command line options correspond to what can be specified via setGeneratedShaders(). Once the resulting files are available, they can be shipped with the application (typically embedded into the executable the the Qt Resource System), and can be loaded and passed to QShader::fromSerialized() at run time.

While not shown here, qsb can do more: it is also able to invoke fxc on Windows or the appropriate XCode tools on macOS to compile the generated HLSL or Metal shader code into bytecode and include the compiled versions in the QShader. After a baked shader pack is written into a file, its contents can be examined by running qsb -d on it. Run qsb with --help for more information.

The alternative approach is to perform the same at run time. This involves creating a QShaderBaker instance, calling setSourceFileName(), and then setting up the translation targets via setGeneratedShaders():

 baker.setGeneratedShaderVariants({ QShader::StandardShader });
 QList<QShaderBaker::GeneratedShader> targets;
 targets.append({ QShader::SpirvShader, QShaderVersion(100) });
 targets.append({ QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs) });
 targets.append({ QShader::SpirvShader, QShaderVersion(120) });
 targets.append({ QShader::HlslShader, QShaderVersion(50) });
 targets.append({ QShader::MslShader, QShaderVersion(12) });
 baker.setGeneratedShaders(targets);
 QShader shaders = baker.bake();
 if (!shaders.isValid())
     qWarning() << baker.errorMessage();

See also QShader.

Member Type Documentation

QShaderBaker::GeneratedShader

Synonym for QPair<QShader::Source, QShaderVersion>.

Member Function Documentation

QShaderBaker::QShaderBaker()

Constructs a new QShaderBaker.

QShaderBaker::~QShaderBaker()

Destructor.

QShader QShaderBaker::bake()

Runs the compilation and translation process.

Returns a QShader instance. To check if the process was successful, call QShader::isValid(). When that indicates false, call errorMessage() to retrieve the log.

This is an expensive operation. When calling this from applications, it can be advisable to do it on a separate thread.

Note: QShaderBaker instances are reusable: after calling bake(), the same instance can be used with different inputs again. However, a QShaderBaker instance should only be used on one single thread during its lifetime.

QString QShaderBaker::errorMessage() const

Returns the error message from the last bake() run, or an empty string if there was no error.

Note: Errors include file read errors, compilation, and translation failures. Not requesting any targets or variants does not count as an error even though the resulting QShader is invalid.