Справочник QSB
qsb — это утилита командной строки, предоставляемая модулем Qt Shader Tools. Она интегрирует сторонние библиотеки, такие как glslang и SPIRV-Cross, при необходимости вызывает внешние инструменты, такие как fxc или spirv-opt, и генерирует файлы .qsb. Кроме того, она может использоваться для проверки содержимого пакета .qsb.
Приложение выводит обзор всех доступных опций, когда вы запускаете его из командной строки без передачи каких-либо аргументов:
Usage: qsb [options] file
Options:
-?, -h, --help Displays help on commandline options.
--help-all Displays help including Qt specific options.
-v, --version Displays version information.
-b, --batchable Also generates rewritten vertex shader for Qt
Quick scene graph batching.
--zorder-loc <location> The extra vertex input location when rewriting
for batching. Defaults to 7.
--glsl <versions> Comma separated list of GLSL versions to
generate. (for example, "100 es,120,330")
--hlsl <versions> Comma separated list of HLSL (Shader Model)
versions to generate. F.ex. 50 is 5.0, 51 is 5.1.
--msl <versions> Comma separated list of Metal Shading Language
versions to generate. F.ex. 12 is 1.2, 20 is 2.0.
-g Generate full debug info for SPIR-V and DXBC
-O Invoke spirv-opt to optimize SPIR-V for
performance
-o, --output <filename> Output file for the shader pack.
-c, --fxc In combination with --hlsl invokes fxc to store
DXBC instead of HLSL.
-t, --metallib In combination with --msl builds a Metal library
with xcrun metal(lib) and stores that instead of
the source.
-D, --define <name[=value]> Define macro. This argument can be specified
multiple times.
-p, --per-target Enable per-target compilation. (instead of
source->SPIRV->targets, do source->SPIRV->target
separately for each target)
-d, --dump Switches to dump mode. Input file is expected to
be a shader pack.
-x, --extract <what> Switches to extract mode. Input file is expected
to be a shader pack. Result is written to the
output specified by -o. Pass -b to choose the
batchable variant.
<what>=reflect|spirv,<version>|glsl,<version>|...
-r, --replace <what> Switches to replace mode. Replaces the specified
shader in the shader pack with the contents of a
file. This argument can be specified multiple
times. Pass -b to choose the batchable variant.
<what>=<target>,<filename> where
<target>=spirv,<version>|glsl,<version>|...
-s, --silent Enables silent mode. Only fatal errors will be
printed.
Arguments:
file Vulkan GLSL source file to compile Режимы работы
Существует три основных режима работы:
-
.qsbгенерация файлов. -
.qsbпроверка файлов. Например,qsb -d myshader.frag.qsbвыведет метаданные отражения (в формате JSON) и включенные шейдеры. - Режим извлечения. Это позволяет записать заданный шейдер из существующего файла
.qsbв отдельный файл. Например,qsb -x spirv.100 -o myshader.spv myshader.frag.qsbзаписывает двоичный код SPIR-V вmyshader.spv.
Пример
Рассмотрим следующий фрагмент шейдера:
#version 440
layout(location = 0) in vec2 v_texcoord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D tex;
layout(std140, binding = 0) uniform buf {
float uAlpha;
};
void main()
{
vec4 c = texture(tex, v_texcoord);
fragColor = vec4(c.rgb, uAlpha);
} Выполнение qsb -o shader.frag.qsb shader.frag приводит к генерации shader.frag.qsb. Проверка этого файла с помощью qsb -d shader.frag.qsb даёт нам:
Stage: Fragment
QSB_VERSION: 5
Has 1 shaders: (unordered list)
Shader 0: SPIR-V 100 [Standard]
Reflection info: {
"combinedImageSamplers": [
{
"binding": 1,
"name": "tex",
"set": 0,
"type": "sampler2D"
}
],
"inputs": [
{
"location": 0,
"name": "v_texcoord",
"type": "vec2"
}
],
"localSize": [
0,
0,
0
],
"outputs": [
{
"location": 0,
"name": "fragColor",
"type": "vec4"
}
],
"uniformBlocks": [
{
"binding": 0,
"blockName": "buf",
"members": [
{
"name": "uAlpha",
"offset": 0,
"size": 4,
"type": "float"
}
],
"set": 0,
"size": 4,
"structName": "_27"
}
]
}
Shader 0: SPIR-V 100 [Standard]
Entry point: main
Contents:
Binary of 864 bytes По умолчанию генерируется только SPIR-V, поэтому приложение, использующее этот пакет шейдеров, будет функционально работать только с Vulkan. Давайте сделаем его более полезным:
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -o shader.frag.qsb shader.frag
Это приводит к генерации пакета шейдеров, который делает его подходящим для OpenGL, Direct 3D и Metal также. Функционал, использованный в этом шейдере, базовый, и даже GLSL ES 100 (язык шейдинга OpenGL ES 2.0) подходит.
Проверка результата показывает:
Stage: Fragment
QSB_VERSION: 5
Has 6 shaders: (unordered list)
Shader 0: GLSL 120 [Standard]
Shader 1: HLSL 50 [Standard]
Shader 2: GLSL 100 es [Standard]
Shader 3: MSL 12 [Standard]
Shader 4: SPIR-V 100 [Standard]
Shader 5: GLSL 150 [Standard]
Reflection info: {
... <same as above>
}
Shader 0: GLSL 120 [Standard]
Entry point: main
Contents:
#version 120
struct buf
{
float uAlpha;
};
uniform buf _27;
uniform sampler2D tex;
varying vec2 v_texcoord;
void main()
{
vec4 c = texture2D(tex, v_texcoord);
gl_FragData[0] = vec4(c.xyz, _27.uAlpha);
}
************************************
Shader 1: HLSL 50 [Standard]
Entry point: main
Native resource binding map:
0 -> [0, -1]
1 -> [0, 0]
Contents:
cbuffer buf : register(b0)
{
float _27_uAlpha : packoffset(c0);
};
Texture2D<float4> tex : register(t0);
SamplerState _tex_sampler : register(s0);
static float2 v_texcoord;
static float4 fragColor;
struct SPIRV_Cross_Input
{
float2 v_texcoord : TEXCOORD0;
};
struct SPIRV_Cross_Output
{
float4 fragColor : SV_Target0;
};
void frag_main()
{
float4 c = tex.Sample(_tex_sampler, v_texcoord);
fragColor = float4(c.xyz, _27_uAlpha);
}
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
v_texcoord = stage_input.v_texcoord;
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.fragColor = fragColor;
return stage_output;
}
************************************
...
Shader 3: MSL 12 [Standard]
Entry point: main0
Native resource binding map:
0 -> [0, -1]
1 -> [0, 0]
Contents:
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct buf
{
float uAlpha;
};
struct main0_out
{
float4 fragColor [[color(0)]];
};
struct main0_in
{
float2 v_texcoord [[user(locn0)]];
};
fragment main0_out main0(main0_in in [[stage_in]], constant buf& _27 [[buffer(0)]], texture2d<float> tex [[texture(0)]], sampler texSmplr [[sampler(0)]])
{
main0_out out = {};
float4 c = tex.sample(texSmplr, in.v_texcoord);
out.fragColor = float4(c.xyz, _27.uAlpha);
return out;
}
************************************
... Теперь этот пакет можно использовать в Qt Quick со всеми поддерживаемыми графическими API: Vulkan, Direct 3D, Metal, OpenGL и OpenGL ES. Во время выполнения соответствующий шейдер автоматически подбирается Qt Rendering Hardware Interface, который находится под Qt Quick и Qt Quick 3D.
Помимо трансляции байткода SPIR-V обратно в исходный код более высокого уровня, система заботится об дополнительных проблемах, таких как обеспечение правильного сопоставления номеров связей SPIR-V на нативные ресурсы. Например, с HLSL мы видели такой раздел выше:
Native resource binding map: 0 -> [0, -1] 1 -> [0, 0]
Внутренне это позволяет сопоставить точку привязки стиля SPIR-V 0 с регистром HLSL b0 и привязкой 1 к t0 и s0. Это помогает сделать различия в привязках ресурсов между различными языками шейдинга прозрачными для пользователей Rendering Hardware Interface и позволяет всему в Qt работать с точками привязки стиля Vulkan/SPIR-V, как они указаны в исходном коде GLSL стиля Vulkan.
Типы шейдеров
Тип шейдера определяется из расширения входного файла. Таким образом, расширение должно быть одним из следующих:
-
.vert— для вершинных шейдеров -
.frag— для фрагментных (пиксельных) шейдеров -
.comp— для шейдеров вычислений
Языки и версии шейдинга
Всегда генерируется SPIR-V 1.0. Что генерируется дополнительно, зависит от аргументов командной строки --glsl, --hlsl, и --msl.
Все эти параметры следуют за списком, разделенным запятыми. Список должен включать номера версий GLSL-стиля с необязательным суффиксом (es, указывающим GLSL ES). Пробел между суффиксом и версией необязателен (отсутствие пробела может помочь избежать необходимости в кавычках).
Например, встроенные материалы Qt Quick (шейдеры, поддерживающие элементы, такие как Image, Text, Rectangle) все готовят свои шейдеры с --glsl "100 es,120,150" --hlsl 50 --msl 12. Это делает их совместимыми с OpenGL ES 2.0 и более поздними версиями, OpenGL 2.1 и более поздними версиями и контекстами профиля ядра OpenGL версии 3.2 и более поздними версиями.
Если шейдер использует функции или конструкции, которые не имеют эквивалента в указанных целевых системах, qsb завершится ошибкой. Если это так, целевые системы потребуют корректировки, и это также означает, что минимальные системные требования приложения неявно корректируются. К примеру, возьмите функцию GLSL textureLod , которая доступна только с OpenGL ES 3.0 и выше (что означает GLSL ES 300 или выше). При запросе GLSL 300 es вместо 100 es, qsb успешно выполнится, но приложение, использующее этот файл .qsb , теперь будет требовать OpenGL ES 3.0 или выше и не будет совместимо с системами на базе OpenGL ES 2.0.
Другой очевидный пример — это шейдеры вычислений: шейдеры .comp потребуют указания --glsl 310es,430, так как шейдеры вычислений доступны только с OpenGL ES 3.1 или более поздними версиями и OpenGL 4.3 или более поздними версиями.
Настройка версии модели шейдера для HLSL или версии языка шейдинга Metal ожидается редко. Модель шейдера 5.0 (--hlsl 50) и MSL 1.2 (--msl 12) обычно будут достаточны.
Использование пакетной обработки графика Qt Quick Scene Graph
Рендеринг Qt Quick Scene Graph поддерживает пакетную обработку геометрии для уменьшения количества вызовов отрисовки. Подробности см. на страницах Scene Graph в разделе Графика сцены. Это основано на вставке кода в функцию main() вершинного шейдера. В Qt 5.x это происходило во время выполнения, путём изменения предоставленного кода вершинного шейдера GLSL. В Qt 6 это не вариант. Вместо этого, пакетные варианты вершинных шейдеров могут быть созданы инструментом qsb. Это запрашивается аргументом -b. Когда входной файл не является вершинным шейдером с расширением .vert, это не оказывает никакого влияния. Однако для вершинных шейдеров это приведёт к генерации двух версий для каждой целевой системы. Qt Quick затем автоматически выберет правильный вариант (стандартный или пакетный) во время выполнения.
Примечание: Приложениям не нужно беспокоиться о подробностях пакетной обработки. Им просто нужно убедиться, что -b (или эквивалентное ключевое слово BATCHABLE при использовании интеграции CMake) указано при обработке вершинных шейдеров. Это актуально только для шейдеров Qt Quick, используемых с ShaderEffect или QSGMaterialShader.
Рассмотрим следующий пример вершинного шейдера:
#version 440
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texcoord;
layout(location = 0) out vec2 v_texcoord;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_texcoord = texcoord;
gl_Position = ubuf.mvp * position;
} Запуск qsb -b --glsl 330 -o example.vert.qsb example.vert приводит к:
Stage: Vertex
QSB_VERSION: 5
Has 4 shaders: (unordered list)
Shader 0: GLSL 330 [Standard]
Shader 1: GLSL 330 [Batchable]
Shader 2: SPIR-V 100 [Standard]
Shader 3: SPIR-V 100 [Batchable]
Reflection info: {
... Обратите внимание, как все языки и версии целевых систем теперь существуют в двух вариантах: Стандартном и немного изменённом Пакетном.
Вызов внешних инструментов
qsb может вызывать определённые внешние инструменты. Они делятся на две категории: инструменты для оптимизации байткода шейдера (SPIR-V) и платформенно-специфичные инструменты для выполнения первой фазы компиляции шейдера из исходного кода в некоторый промежуточный формат байткода.
Они активируются следующими параметрами командной строки:
-
-O— вызываетspirv-optкак пост-обработку двоичного файла SPIR-V. Файл.qsbбудет содержать оптимизированную версию. Это предполагает, чтоspirv-optдоступен в системе (например, из Vulkan SDK) и готов к вызову. -
-cили--fxc— вызываетfxc.exe, компилятор шейдеров Direct 3D. Результирующие данныеDXBC(DirectX Byte Code) сохраняются в файле.qsbвместо HLSL. Qt автоматически подберёт его во время выполнения, поэтому создателю файла.qsbрешать, что включать, исходный код HLSL или промежуточный формат. Всякий раз, когда это возможно, отдавайте предпочтение последнему, так как это устраняет необходимость парсить и использовать исходный код HLSL во время выполнения, что потенциально приводит к существенному увеличению производительности при создании графической конвейерной обработки. Недостатком является то, что этот аргумент может быть использован только когдаqsbвыполняется в Windows. -
-tили--metallib— вызывает соответствующие инструменты XCode Metal для генерации файла .metallib и включает его в пакет.qsbвместо исходного кода MSL. Этот вариант доступен только при выполненииqsbна macOS.
Другие параметры
-
-D— определяет макрос. Это позволяет использовать #ifdef и аналогичные конструкции в исходном коде GLSL. -
-g— включает генерацию полной отладочной информации для SPIR-V, что позволяет таким инструментам, как RenderDoc, отображать весь исходный код при проверке конвейера или при отладке вершинного или фрагментного шейдера. Также оказывает влияние на Direct 3D, когда указан-c, так какfxcтогда получает инструкции включать отладочную информацию в сгенерированный промежуточный байткод.
Работа с функциями GLSL, специфичными для OpenGL
Иногда может потребоваться использовать конструкции языка шейдинга, специфичные для OpenGL и GLSL, и неприменимые к другим языкам шейдинга, промежуточным форматам и графическим API.
Яркими примерами этого являются внешние текстуры и сэмплеры OpenGL ES. Реализация воспроизведения видео или отображения видоискателя может включать, в зависимости от платформы, работу с объектами текстур OpenGL, которые не предназначены для использования в качестве обычных 2D текстур, но могут использоваться, с ограниченным функционалом, через точку привязки GL_TEXTURE_EXTERNAL_OES в API OpenGL и тип сэмплера samplerExternalOES в шейдерах. Последний представляет потенциальную проблему при использовании конвейера шейдеров SPIR-V Qt: выполнение такого шейдера через qsb приведёт к ошибке из-за того, что samplerExternalOES не принимается как допустимый тип из-за того, что он не может быть сопоставлен с SPIR-V и другими целевыми языками шейдинга.
Чтобы преодолеть это, qsb предлагает возможность заменить содержимое любого заданного варианта шейдера в файле .qsb пользовательскими данными, считанными из файла, полностью заменяя исходный код шейдера или байткод, сгенерированные qsb.
Рассмотрим следующий фрагмент шейдера. Обратите внимание на тип tex. А что, если тип должен быть samplerExternalOES при запуске с OpenGL ES?
#version 440
layout(location = 0) in vec2 texCoord;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
float opacity;
} ubuf;
layout(binding = 1) uniform sampler2D tex;
void main()
{
fragColor = texture(tex, texCoord).rgba * ubuf.opacity;
} Просто изменение типа сэмплера samplerExternalOES не выполнимо. Это сразу приведёт к ошибкам компиляции.
Однако есть простое решение: написать отдельный, полностью ориентированный на OpenGL ES вариант шейдера и вставить его в файл .qsb. Следующий шейдер совместим только с GLSL ES и не может быть запущен через qsb. Однако мы знаем, что его можно обработать в OpenGL ES во время выполнения.
precision highp float;
#extension GL_OES_EGL_image_external : require
varying vec2 texCoord;
struct buf
{
float opacity;
};
uniform buf ubuf;
uniform samplerExternalOES tex;
void main()
{
gl_FragColor = texture2D(tex, texCoord).rgba * ubuf.opacity;
} Назовём это shader_gles.frag. После завершения qsb --glsl 100es -o shader.frag.qsb shader.frag, что даёт нам (полуготовый) файл .qsb, мы можем выполнить qsb -r glsl,100es,shader_gles.frag shader.frag.qsb для обновления shader.frag.qsb, заменив шейдер GLSL 100 es содержимым указанного файла (shader_gles.frag). Теперь shader.frag.qsb готов к использованию во время выполнения с OpenGL ES.
Примечание: Обратите внимание на сохранение неизменного интерфейса между шейдером и приложением. Всегда предварительно проверьте сгенерированный qsb GLSL-код, распечатав содержимое файла .qsb с помощью опции -d или извлекая шейдер GLSL ES 100, выполнив qsb -x glsl,100es -o gles_shader.frag shader.frag.qsb. Структуры, члены структур и имена униформ не должны отличаться и в ручном варианте вставки.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qtshadertools-qsb.html