glslangValidatorを使ってシェーダの文法チェックをする(4-Ex1.)

# はじめに

シェーダに記述する内容が増えるにつれて、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の場合)
```bash
brew install cmake
```

## 1) プロジェクト本体のチェックアウト
```bash
cd PATH/TO/WORKDIR
git clone --depth 1 https://github.com/KhronosGroup/glslang.git
cd glslang
```

## 2) 外部プロジェクトのチェックアウト
```bash
cd External
git clone https://github.com/google/googletest.git External/googletest
cd ../
```

## 3) Configure
```bash
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$(pwd)/install" $SOURCE_DIR
```

## 4) ビルド
```bash
make -j4 install
```

## 5) 確認
```bash
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をコピーするなどしておくと便利です。

# 実行サンプル

```bash
# バグがない場合
$ 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のコンパイル結果を返すようになっています。

※[macOSでOpenGLを勝手に勉強する(目次)](https://qiita.com/uechoco@github/items/655806d9252c035f9efc)