はじめに
シェーダに記述する内容が増えるにつれて、typoしたりしてシェーダがうまく動かないことが増えてくることもあると思います。
XcodeはGLSLシェーダをシンタックスハイライト付きで表示したり編集したりすることはできますが、文法チェックをしてコンパイルが通るかどうかを検証してくれることはありません。アプリケーションを実行して、シェーダをOpenGLにアタッチして、コンパイルされてみないと、シェーダにバグがあるかどうかがわからないということです。ビルドや実行時間がかったり該当のシェーダの動作確認のために操作が増えてくるとイテレーションのスピードが落ちますので、単純な文法ミスはアプリケーションの実行前に気づける環境が作れると良いかもしれません。
今回は、 glslangValidator を用いて、文法チェックする環境を構築します。
OpenGL/OpenGL ES Reference Compiler
OpenGLの仕様策定を行っているKhronos Groupが公式に配布しているツールとして、Reference Compilerがあります。
https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/
https://github.com/KhronosGroup/glslang
このツールは、ソースコードが公開されており、各自の環境でビルドすると glslangValidator という実行ファイルが生成されます。この glslangValidator を用いることで、アプリケーションを実行しなくてもシェーダプログラムをコンパイルにかけて文法チェックをすることができます。
ビルド方法
基本的にはリポジトリ https://github.com/KhronosGroup/glslang のREADMEに書いてある手順通りにやるだけです。LinuxとWindowsの手順しか書いてありませんがmacOSはLinuxの手順と同じになります。
0) cmakeのインストール(brewの場合)
brew install cmake
1) プロジェクト本体のチェックアウト
cd PATH/TO/WORKDIR
git clone --depth 1 https://github.com/KhronosGroup/glslang.git
cd glslang
2) 外部プロジェクトのチェックアウト
cd External
git clone https://github.com/google/googletest.git External/googletest
cd ../
3) Configure
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$(pwd)/install" $SOURCE_DIR
4) ビルド
make -j4 install
5) 確認
ls install/bin/glslangValidator
./install/bin/glslangValidator -h
Usage: glslangValidator [option]... [file]...
'file' can end in .<stage> for auto-stage classification, where <stage> is:
.conf to provide a config file that replaces the default configuration
(see -c option below for generating a template)
.vert for a vertex shader
.tesc for a tessellation control shader
.tese for a tessellation evaluation shader
.geom for a geometry shader
.frag for a fragment shader
.comp for a compute shader
.mesh for a mesh shader
.task for a task shader
.rgen for a ray generation shader
.rint for a ray intersection shader
.rahit for a ray any hit shader
.rchit for a ray closest hit shader
.rmiss for a ray miss shader
.rcall for a ray callable shader
.glsl for .vert.glsl, .tesc.glsl, ..., .comp.glsl compound suffixes
.hlsl for .vert.hlsl, .tesc.hlsl, ..., .comp.hlsl compound suffixes
Options:
-C cascading errors; risk crash from accumulation of error recoveries
-D input is HLSL (this is the default when any suffix is .hlsl)
-D<macro=def>
-D<macro> define a pre-processor macro
-E print pre-processed GLSL; cannot be used with -l;
errors will appear on stderr
-G[ver] create SPIR-V binary, under OpenGL semantics; turns on -l;
default file name is <stage>.spv (-o overrides this);
'ver', when present, is the version of the input semantics,
which will appear in #define GL_SPIRV ver;
'--client opengl100' is the same as -G100;
a '--target-env' for OpenGL will also imply '-G'
-H print human readable form of SPIR-V; turns on -V
-I<dir> add dir to the include search path; includer's directory
is searched first, followed by left-to-right order of -I
-Od disables optimization; may cause illegal SPIR-V for HLSL
-Os optimizes SPIR-V to minimize size
-S <stage> uses specified stage rather than parsing the file extension
choices for <stage> are vert, tesc, tese, geom, frag, or comp
-U<macro> undefine a pre-processor macro
-V[ver] create SPIR-V binary, under Vulkan semantics; turns on -l;
default file name is <stage>.spv (-o overrides this)
'ver', when present, is the version of the input semantics,
which will appear in #define VULKAN ver
'--client vulkan100' is the same as -V100
a '--target-env' for Vulkan will also imply '-V'
-c configuration dump;
creates the default configuration file (redirect to a .conf file)
-d default to desktop (#version 110) when there is no shader #version
(default is ES version 100)
-e <name> | --entry-point <name>
specify <name> as the entry-point function name
-f{hlsl_functionality1}
'hlsl_functionality1' enables use of the
SPV_GOOGLE_hlsl_functionality1 extension
-g generate debug information
-h print this usage message
-i intermediate tree (glslang AST) is printed out
-l link all input files together to form a single module
-m memory leak mode
-o <file> save binary to <file>, requires a binary option (e.g., -V)
-q dump reflection query database
-r | --relaxed-errors relaxed GLSL semantic error-checking mode
-s silence syntax and semantic error reporting
-t multi-threaded mode
-v | --version
print version strings
-w | --suppress-warnings
suppress GLSL warnings, except as required by "#extension : warn"
-x save binary output as text-based 32-bit hexadecimal numbers
-u<name>:<loc> specify a uniform location override for --aml
--uniform-base <base> set a base to use for generated uniform locations
--auto-map-bindings | --amb automatically bind uniform variables
without explicit bindings
--auto-map-locations | --aml automatically locate input/output lacking
'location' (fragile, not cross stage)
--client {vulkan<ver>|opengl<ver>} see -V and -G
-dumpfullversion | -dumpversion print bare major.minor.patchlevel
--flatten-uniform-arrays | --fua flatten uniform texture/sampler arrays to
scalars
--hlsl-offsets allow block offsets to follow HLSL rules
works independently of source language
--hlsl-iomap perform IO mapping in HLSL register space
--hlsl-enable-16bit-types allow 16-bit types in SPIR-V for HLSL
--invert-y | --iy invert position.Y output in vertex shader
--keep-uncalled | --ku don't eliminate uncalled functions
--no-storage-format | --nsf use Unknown image format
--resource-set-binding [stage] name set binding
set descriptor set and binding for
individual resources
--resource-set-binding [stage] set
set descriptor set for all resources
--rsb synonym for --resource-set-binding
--shift-image-binding [stage] num
base binding number for images (uav)
--shift-image-binding [stage] [num set]...
per-descriptor-set shift values
--sib synonym for --shift-image-binding
--shift-sampler-binding [stage] num
base binding number for samplers
--shift-sampler-binding [stage] [num set]...
per-descriptor-set shift values
--ssb synonym for --shift-sampler-binding
--shift-ssbo-binding [stage] num base binding number for SSBOs
--shift-ssbo-binding [stage] [num set]...
per-descriptor-set shift values
--sbb synonym for --shift-ssbo-binding
--shift-texture-binding [stage] num
base binding number for textures
--shift-texture-binding [stage] [num set]...
per-descriptor-set shift values
--stb synonym for --shift-texture-binding
--shift-uav-binding [stage] num base binding number for UAVs
--shift-uav-binding [stage] [num set]...
per-descriptor-set shift values
--suavb synonym for --shift-uav-binding
--shift-UBO-binding [stage] num base binding number for UBOs
--shift-UBO-binding [stage] [num set]...
per-descriptor-set shift values
--sub synonym for --shift-UBO-binding
--shift-cbuffer-binding | --scb synonyms for --shift-UBO-binding
--spirv-dis output standard-form disassembly; works only
when a SPIR-V generation option is also used
--spirv-val execute the SPIRV-Tools validator
--source-entrypoint <name> the given shader source function is
renamed to be the <name> given in -e
--sep synonym for --source-entrypoint
--stdin read from stdin instead of from a file;
requires providing the shader stage using -S
--target-env {vulkan1.0 | vulkan1.1 | opengl |
spirv1.0 | spirv1.1 | spirv1.2 | spirv1.3}
set execution environment that emitted code
will execute in (versus source language
semantics selected by --client) defaults:
* 'vulkan1.0' under '--client vulkan<ver>'
* 'opengl' under '--client opengl<ver>'
* 'spirv1.0' under --target-env vulkan1.0
* 'spirv1.3' under --target-env vulkan1.1
multiple --targen-env can be specified.
--variable-name <name>
--vn <name> creates a C header file that contains a
uint32_t array named <name>
initialized with the shader binary code
6) 必要があれば、PATHを通す
./bash_profileなどを編集して、glslangValidatorのフォルダのPATHを通したり、
PATHの通った場所にglslangValidatorをコピーするなどしておくと便利です。
実行サンプル
# バグがない場合
$ glslangValidator -S frag MyGLGame/myshader.fsh
$ glslangValidator -S vert MyGLGame/myshader.vsh
# バグが有る場合
$ glslangValidator -S frag MyGLGame/myshader.fsh
MyGLGame/myshader.fsh
ERROR: 0:19: '' : syntax error, unexpected UNIFORM, expecting COMMA or SEMICOLON
ERROR: 1 compilation errors. No code generated.
シェーダファイルの冒頭に #version 410
などとシェーダバージョンの指定が書かれていると思いますが、このバージョンに合わせてコンパイル結果が変わります。例えば #version 300 es
などとなっていた場合は、OpenGL ESのコンパイル結果を返すようになっています。