Vulkan 上の Slang シェーディング言語:モジュール、ジェネリクス、自動微分

Slang は「HLSL は唯一まともな GPU 言語だが、Microsoft のコンパイラに 縛られている」に対する Khronos の答えだ。モダンなシェーディング言語で、 モジュール、ジェネリクス、インターフェース、自動微分を備え、1つの ソースから SPIR-V(および DXIL、Metal、WGSL)へコンパイルできる。 Vulkan では .slang ファイル → slangc -target spirv → VkShaderModule で、GLSL/HLSL と同じパイプラインだ。

なぜ GLSL ではだめか

compare.txt
GLSL:      モジュールなし、ジェネリクスなし、1970年代のプリプロセッサ
           インクルード、弱い型システム、クロージャなし。小さなシェーダー
           には十分だが、エンジン規模のシェーディングでは苦痛。
Slang:     モジュールの import、ジェネリック関数/構造体、インターフェース、
           微分可能プログラミング(順/逆自動微分)、本物のリフレクション
           API、1つのソース → 全バックエンド。

味見

light.slang
// ジェネリックで再利用可能な関数 — 素の GLSL では不可能:
T shade<T>(T a, T b, float t) where T : IFloat
    { return a * (1 - t) + b * t; }

[shader("compute")]
[numthreads(64)]
void main(uint3 id : SV_DispatchThreadID) {
    result[id.x] = shade(a[id.x], b[id.x], 0.5f);
}

Vulkan 向けコンパイル

build.sh
slangc shader.slang -target spirv -o shader.spv -entry main
# マルチエントリー → エントリーごとに1モジュール:
slangc shader.slang -target spirv \
    -entry vertMain -stage vertex   -o vert.spv \
    -entry fragMain -stage fragment -o frag.spv
# デバッグ情報を埋め込む:
slangc shader.slang -target spirv -g -o shader.spv

リフレクション API(slang-reflection/libslang)は、SPIR-V を再パース せずにディスクリプタバインディング、プッシュ定数レイアウト、 スレッドグループサイズを与えてくれる。spirv-reflect の記事で使った のと同じツールだが、ソースレベルだ。

決定的な機能:自動微分

diff.slang
// [Differentiable] — slang が逆伝播パスを自動的に計算する:
[Differentiable]
float loss(float3 pred, float3 target) {
    float3 d = pred - target;
    return dot(d, d);
}

// シェーダー内:bwd_diff(loss)(…) が勾配を返す —
// 微分コードを書くのはあなたではなく自動微分機構だ

ニューラルテクスチャ圧縮、学習 BRDF、微分可能レンダリング。勾配コードが 手で導出されるのではなく生成されるシェーディング言語は、今のところ Slang だけだ。

Vulkan 統合の落とし穴

  • set/binding は [[vk::binding(N,S)]] または Slang 独自の [vk::binding] 属性にマップされる。DXC の HLSL 経路と同じモデルだ。
  • プッシュ定数は [[vk::push_constant]] ConstantBuffer<T> だ。HLSL の 記事と同じ仕組み。
  • slangc は SPIR-V を直接出力する。その SPIR-V は spirv-opt と spirv-val(ツール記事参照)に通せるほどクリーンだ。
  • SPIRV-Reflect は Slang の出力を問題なく読める。バインディングと エントリーポイントは通常の SPIR-V デコレーションとして現れる。

実践的なルール

  • エントリーポイントごとに slangc -target spirv を使う。マルチ エントリーの .slang も VkShaderModule ごとに1つの .spv になる。
  • モジュールは #include を置き換える。import foo; は名前空間付きで 依存関係が追跡される。古い glslang インクルードチェーンのハックは 削除する。
  • slangd が LSP だ。VSCode/Vim で .slang の補完とエラーが得られる。
  • ジェネリクスはインスタンス化ごとに単相化される。異なる T での2つの shade<T> 呼び出しは2つの SPIR-V 関数を生成する。巨大なジェネリック グラフでは特殊化コストを確認する。
  • Vulkan 専用プロジェクトでは、Slang は自動微分 + モジュール + 本物の ツールチェーンをもたらす。すでに GLSL と小さなシェーダーに全力投入 しているなら、それは必需品ではなくツールチェーンの切り替えだ。

同じカテゴリの類似記事をチェック: Vulkan C++