QShaderBaker类

编译GLSL/Vulkan着色器到SPIR-V,翻译成其他着色语言,并收集反射元数据。更多...

头文件 #include <QShaderBaker>
自从Qt 6.6

公共类型

公共函数

QShaderBaker()
~QShaderBaker()
QShaderbake()
QStringerrorMessage() const
voidsetBatchableVertexShaderExtraInputLocation(int location)
voidsetBreakOnShaderTranslationError(bool enable)
voidsetGeneratedShaderVariants(const QList<QShader::Variant> &v)
voidsetGeneratedShaders(const QList<QShaderBaker::GeneratedShader> &v)
(since 6.7) voidsetMultiViewCount(int count)
voidsetPerTargetCompilation(bool enable)
voidsetPreamble(const QByteArray &preamble)
voidsetSourceDevice(QIODevice *device, QShader::Stage stage, const QString &fileName = QString())
voidsetSourceFileName(const QString &fileName)
voidsetSourceFileName(const QString &fileName, QShader::Stage stage)
voidsetSourceString(const QByteArray &sourceString, QShader::Stage stage, const QString &fileName = QString())
voidsetTessellationMode(QShaderDescription::TessellationMode mode)
voidsetTessellationOutputVertexCount(int count)

详细说明

警告: QShaderBaker,就像Qt Gui模块中的QRhi类家族,包括QShader和QShaderDescription,提供有限的兼容性保证。这些类没有源代码或二进制兼容性保证,这意味着API仅保证与应用程序开发所针对的Qt版本一起工作。但是,源代码不兼容的更改旨在尽量保持最小,并且仅会在次要版本(6.7、6.8等)中进行。要在应用程序中使用此类,请链接到Qt::ShaderToolsPrivate(如果使用CMake),并使用带有rhi前缀的包含头文件,例如#include <rhi/qshaderbaker.h>

QShaderBaker可以从图形(顶点、片段等)或计算着色器中提取信息,并生成多个与之相关联的源代码或字节码变体,同时包含反射信息。这些结果通过QShader实例表示,该实例还提供了简单快速的序列化和反序列化功能。

注意:建议应用程序和库避免直接使用此类。相反,鼓励所有Qt用户在构建时通过CMake调用qsb命令行工具进行离线编译。qsb工具使用QShaderBaker并将生成的QShader序列化版本写入文件。此类的使用应限于无法避免运行时编译的情况,例如处理用户提供的或动态生成的着色器源代码字符串。

目前,输入格式始终被假定为Vulkan风格的GLSL。有关概述,请参阅GL_KHR_vulkan_glsl规范,请注意,Qt Shader Tools模块旨在与QRhi模块的类一起使用,因此目前有一些概念和构造(推送常数、存储缓冲区、子流程等)不适用。将来可能会引入其他选项,例如,通过启用HLSL作为源格式,一旦将HLSL编译为SPIR-V认为合适时。

可以通过调用QShader::description()从生成的QShader获取反射元数据。这在需要发现着色器所期望的顶点输入和着色器资源集合及其布局时至关重要,因为许多现代图形API没有内置的着色器反射功能。

典型工作流程

假设有一个应用程序拥有如下顶点和片段着色器:

顶点着色器

#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;
}

片段着色器

#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);
}

要获取可以直接传递给QRhiGraphicsPipelineQShader实例,有两种选择:在离线时进行着色器包生成,或在运行时进行。

第一种涉及运行qsb工具

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

示例使用适用于QRhi的适当翻译目标。这意味着GLSL/ES 100、GLSL 120、HLSL Shader Model 5.0和Metal着色语言1.2。

请注意命令行选项与通过setGeneratedShaders()指定的选项相对应。一旦结果文件可用,它们可以与应用程序一起分发(通常嵌入到可执行文件中,由Qt资源系统管理),并在运行时通过QShader::fromSerialized()加载和传递。

虽然此处未展示,但在Windows上qsb还可以调用fxc或在macOS上调用相应的XCode工具,将生成的HLSL或Metal着色器代码编译成字节码,并将编译版本包含在QShader中。将生成的着色器包写入文件后,可以通过运行qsb -d在文件上执行来检查其内容。使用--help运行qsb以获取更多信息。

另一种方法是同样在运行时执行。这涉及到创建一个QShaderBaker实例,调用setSourceFileName(),然后通过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();

另请参阅QShader

成员类型文档

模板类型QShaderBaker::GeneratedShader

QPair<QShader::Source, QShaderVersion>的同义词。

成员函数文档

QShaderBaker::QShaderBaker()

构建一个新的 QShaderBaker。

[noexcept] QShaderBaker::~QShaderBaker()

析构函数。

QShader QShaderBaker::bake()

运行编译和翻译过程。

返回一个 QShader 实例。要检查过程是否成功,调用 QShader::isValid()。如果提示 false,则调用 errorMessage() 以获取日志。

这是一个昂贵的操作。当从应用程序中调用此操作时,最好在单独的线程中进行。

注意:QShaderBaker 实例是可以重用的:在调用 bake() 之后,相同的实例可以用不同的输入再次使用。但是,应当在实例的生命周期内只在一个单独的线程中使用 QShaderBaker 实例。

QString QShaderBaker::errorMessage() const

返回上次 bake() 运行的错误消息,如果没有错误则返回空字符串。

注意:错误包括文件读取错误、编译和翻译失败。未请求任何目标和变体不算是错误,即使结果 QShader 是无效的。

void QShaderBaker::setBatchableVertexShaderExtraInputLocation(int location)

当生成 QShader::BatchableVertexShader 变体时,location 指定插入顶点输入的位置。默认值是 7,只有在顶点着色器已经使用了输入位置 7 时需要覆盖此值。

void QShaderBaker::setBreakOnShaderTranslationError(bool enable)

控制着色器翻译(从 SPIR-V 到 GLSL/HLSL/MSL)失败时的行为。默认情况下此设置是 true,这意味着如果请求的着色器无法生成,则 bake() 将以错误返回。如果不希望这样,且目的是尽可能生成可以生成的,而静默跳过其余内容,则将 enable 设置为 false。

针对多个 GLSL 版本可能导致错误,因为某些特性无法转换为指定版本。例如,尝试使用 textureSize() 将着色器翻译到 GLSL ES 100 会失败整个 bake() 调用,并显示错误消息 "textureSize 在 ESSL 100 中不受支持"。如果可以接受结果中不包含 GLSL ES 100 着色器,即使已请求,则将此标志设置为 false 可以使 bake() 成功。

void QShaderBaker::setGeneratedShaderVariants(const QList<QShader::Variant> &v)

指定要生成哪些着色器变体。结果 QShader 中可以为每个着色器版本有多个变体。

在大多数情况下 v 包含单独一个条目,QShader::StandardShader

注意: 当未设置任何变体时, resulting QShader 将为空,因此是无效的。

void QShaderBaker::setGeneratedShaders(const QList<QShaderBaker::GeneratedShader> &v)

指定要编译或翻译的着色器类型。默认情况下不生成任何内容,因此调用此函数在 bake() 之前是强制性的

注意: 如果未调用此函数,或者 v 为空或仅包含无效条目,则 resulting QShader 将为空,因此是无效的。

例如,可能的最小烘焙目标为 SPIR-V,不进行其他语言翻译。要请求此操作,请执行

baker.setGeneratedShaders({ QShader::SpirvShader, QShaderVersion(100) });

注意: QShaderBaker 只处理 SPIR-V 和可读源目标。将 API 特定的中间格式编译为更进一步的步骤,例如 QShader::DxbcShaderQShader::MetalLibShader,由 qsb 命令行工具实现,不包含在 QShaderBaker 运行时 API 中。

[since 6.7] void QShaderBaker::setMultiViewCount(int count)

当使用多视图(例如,使用 gl_ViewIndex 的顶点着色器,对一个依赖于 GL_OVR_multiview2、VK_KHR_multiview 等的渲染器)进行着色器编译时,对于某些目标,有必要在着色器中声明视图数量。这不是在 Vulkan 风格的 GLSL 代码中执行的操作,对于 SPIR-V 或 HLSL 等目标也不相关,但对于 OpenGL 和 GLSL 是必需的,因此需要作为额外元数据提供值。

默认情况下,该值为 0,表示禁用注入 num_views 语句。设置为 1 没有用,因为这始终是默认的 num_views。因此,count 应当 >= 2 才能生效。例如,当设置为 2 时,生成的 GLSL 着色器将包含 layout(num_views = 2) in; 语句。

设置 count 为 2 或更大的值也会注入一些预处理语句:QSHADER_VIEW_COUNT 被设置为 count,而自动启用 GL_EXT_multiview 扩展。因此,设置适当的 count 可与其他类型的着色器相关,例如,当在顶点和片段着色器之间共享统一缓冲区时,并且两个着色器都必须能够编写类似于 #if QSHADER_VIEW_COUNT >= 2 的内容。

此函数是在 Qt 6.7 中引入的。

void QShaderBaker::setPerTargetCompilation(bool enable)

设置按目标编译为 enable。默认情况下此设置为禁用,表示对于每种变体,Vulkan/GLSL 源代码仅编译一次到 SPIR-V(默认情况下,顶点着色器和可批处理变体请求时要编译两次)。然后,生成的 SPIR-V 被翻译成各种目标语言(GLSL、HLSL、MSL)。

在按目标编译模式下,每个目标都有一个单独的 GLSL 到 SPIR-V 编译步骤,这意味着对于通过 setGeneratedShaders() 请求的每个 GLSL/HLSL/MSL 版本都有。输入源相同,但插入具有特定目标预处理定义的项。这要花费更多时间,但允许应用程序提供单个着色器并使用 #ifdef 块来区分。当此模式禁用时,唯一实现相同的方式是提供多个着色器文件版本,分别处理每个版本,为每个版本提供 {.qsb} 文件,并根据运行时逻辑选择正确的文件。

以下宏将在该模式下自动定义。请注意,宏始终与着色语言相关联,而不是与图形API相关。

  • QSHADER_SPIRV - 当针对SPIR-V(典型的使用者是Vulkan)时定义。
  • QSHADER_SPIRV_VERSION - 目标SPIR-V版本号,例如100
  • QSHADER_GLSL - 当针对GLSL或GLSL ES(典型的使用者是OpenGL或OpenGL ES)时定义
  • QSHADER_GLSL_VERSION - 目标GLSL或GLSL ES版本号,例如100300330
  • QSHADER_GLSL_ES - 仅当针对GLSL ES时定义
  • QSHADER_HLSL - 当针对HLSL(典型的使用者是Direct 3D)时定义
  • QSHADER_HLSL_VERSION - 目标HLSL着色模型版本,例如50
  • QSHADER_MSL - 当针对Metal着色语言(典型的使用者是Metal)时定义
  • QSHADER_MSL_VERSION - 目标MSL版本,例如1220

这允许编写如下所示的着色器代码。

#if QSHADER_HLSL || QSHADER_MSL
vec2 uv = vec2(uv_coord.x, 1.0 - uv_coord.y);
#else
vec2 uv = uv_coord;
#endif

注意:版本号遵循GLSL启发的QShaderVersion语法,因此总是单个整数。

注意:每个QShaderDescription对应一个QShader,不论有多个单个目标。因此,不应使用上述宏将uniform块、顶点输入等成员条件化。

警告:注意图形API和着色语言概念之间的差异。QShaderBaker和相关工具严格根据着色语言的概念进行操作,忽略随后如何消费结果。因此,如果有一天Qt图形堆栈中的更高层开始使用SPIR-V(不仅仅是Vulkan),那么QSHADER_SPIRV意味着Vulkan的假设将不再成立。

void QShaderBaker::setPreamble(const QByteArray &preamble)

指定在正常着色器代码之前处理的自定义preamble

这不仅仅是将源字符串加在前边:所要求的GLSL版本指令的有效性不会受到影响。报告的错误消息中的行号也保持不变,忽略在preamble中给出的内容。

预分配的一个用例是透明地插入动态生成的#define语句。

void QShaderBaker::setSourceDevice(QIODevice *device, QShader::Stage stage, const QString &fileName = QString())

设置源device。这允许使用任何QIODevice而不是只使用文件。stage指定着色器阶段,而可选的fileName包含用于错误消息的文件名。

void QShaderBaker::setSourceFileName(const QString &fileName)

将着色器源文件名设置为fileName。这是调用bake时要读取的文件。着色器阶段将自动从文件扩展名中推断出来。如果这不希望或不可能,请使用带有阶段参数的重载。

支持文件扩展名有

  • .vert - 顶点着色器
  • .frag - 片段(像素)着色器
  • .tesc - 纹理控制(外壳)着色器
  • .tese - 纹理评估(领域)着色器
  • .geom - 几何着色器
  • .comp - 计算着色器

void QShaderBaker::setSourceFileName(const QString &fileName, QShader::Stage stage)

设置着色器源文件名为fileName。这是在调用bake()时读取的文件。着色器阶段由stage指定。

void QShaderBaker::setSourceString(const QByteArray &sourceString, QShader::Stage stage, const QString &fileName = QString())

设置输入着色器sourceStringstage指定着色器阶段,而可选的fileName包含用于错误消息的文件名。

void QShaderBaker::setTessellationMode(QShaderDescription::TessellationMode mode)

在生成用于纹理控制着色器的MSL着色器代码时,必须预先知道纹理mode(三角形或四边形)。在GLSL中,这通常在纹理评估着色器中声明,但对于Metal来说,在从纹理控制着色器生成计算着色器时也必须知道。

如果没有设置,默认为三角形。

void QShaderBaker::setTessellationOutputVertexCount(int count)

在为纹理评估着色器生成MSL着色器代码时,必须预先知道纹理控制着色器的输出顶点count。在GLSL中,这通常在纹理控制着色器中声明,但针对Metal,在从纹理评估着色器生成顶点着色器时也必须知道。

如果没有设置,默认值为3。

© 2024 The Qt Company Ltd. 本文档中的内容贡献为各自所有者的版权。提供的文档根据GNU自由文档许可版本1.3许可,由自由软件基金会发布。Qt及其相关标志是The Qt Company Ltd.在芬兰及其他全球国家的商标。所有其他商标均为其各自所有者的财产。