猫茶の研究日誌

ゲーム開発などの技術や、そのほか趣味などの雑記。

HLSL→WGSLへの変換をやってみる(SPIR-V経由)

目次

はじめに

DirectX向けにHLSL言語で書かれたシェーダプログラムを、WebGPUで使えるWGSLへと変換してみる記事です。
いわゆるトランスパイルというやつです。

実験してみたものの、すぐに忘れそうなので備忘録を兼ねて記事にしておきます。

SPIR-VとTintを使います。(詳細は後述)

余談ですが、WebGPUについて、C++でブラウザ向けに使う入門記事を先日書いたので、試してみたい方はぜひご参照ください。

zenn.dev

また、今回はWindows環境で解説します。
そのほかの環境は以下の通りです。

DirectX Shader Compiler(dxcompiler.dll: 1.7 - 1.7.2212.40 (e043f4a12); dxil.dll: 1.7(101.7.2212.36))
Tint (mainブランチ、 コミット 4ed79e689a0632837d3d956038ef7d78206dc22d(2023年4月上旬ごろ))
ninja 1.11.1
CMake 3.24.0

変換の流れ

HLSL→WGSLの直接変換する手段は、私が知る限り存在しません。
しかし、世の中には「 SPIR-V 」という便利な中間言語があります。

SPIR-Vは、シェーディング言語に特化した中間言語で、多くの環境に向けて使うことができます。
HLSLやWGSLも、SPIR-V形式での入出力に対応しています。
こちらのSPIR-Vを中継して、変換していきます。

つまり、 HLSL→SPIR-V→WGSL という流れでの変換になります。

HLSL→SPIR-V

DirectX Shader Compiler(DXC)を使います。
Microsoftが提供するオープンソースのHLSLコンパイラです。
ShaderModel 6.0以降にも対応しています。
SPIR-Vへ変換する機能もあるため、こちらを使います。

ちなみに、HLSL→SPIR-Vの変換ができるソフトウェアは、
UnityTechnologiesの HLSLccHLSLCrossCompiler のFork)や、
GoogleShaderc などもあります。

SPIR-V→WGSL

Tint を使います。
Tintは、Googleによって、Chromium向けのWebGPUの実装「Dawn」とともに開発されている、シェーダーコンパイラです。

スタンドアロンで、exe形式として使ったり、ライブラリとして組み込むなどの使いかたもできます。

環境構築

DirectX Shader Compiler(DXC)

GitHubにビルドされた状態でReleaseされてるので dxc_xxxxxxxx.zip をDLしてきます。

https://github.com/microsoft/DirectXShaderCompiler/releases

DLできたら、適当なところに解凍します。

あとは、環境に合ったdxc.exeのある場所にPATHを通してください。
(比較的基礎的な部分なので、PATHの通し方は本記事では触れません)

私はx64環境なので、[DXCを解凍したディレクトリ]\bin\x64 にPATHを通しました。

任意のディレクトリで、

dxc --version

を実行して正常に動作すればOKです。

Tint

Tintはバイナリ形式では配布されていないので、自力でビルドする必要があります。

depot-toolsのインストール

Chromiumの開発で必要なツール群です。
公式のドキュメント(https://dawn.googlesource.com/tint/+/refs/heads/main#building)いわく、Chromiumの依存関係管理まわりが必要だそうなので、入れておきます。

公式のセットアップページ(http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up
にアクセスして、

WINDOWS
Download the depot_tools bundle and extract it somewhere.

と書いているあたりのリンクから、Windows用のzipファイル(depot_tools.zip)をDLしてきてください。

DLできたら、解凍、適当な場所に配置します。
こちらも同様にPATHを通しておきます。

Tintのビルド

copy standalone.gclient .gclient

gclient sync

# 出力先ディレクトリを作る
md out\Debug\
cd out\Debug\

cmakeとninjaを使ってビルドをします。
SPIR-Vを読み込みできるように、 TINT_BUILD_SPV_READER オプションを指定しておきます。

(ninjaをインストールしていない場合は、ninjaをダウンロードしてきて、PATHを通してください。
https://github.com/ninja-build/ninja/releases

cmake -GNinja ../.. TINT_BUILD_SPV_READER
# ビルド
ninja

ついでなので、ninjaを使わずに、CMakeとmakeでビルドする方法も書いておきます。

cmake ../.. TINT_BUILD_SPV_READER
# ビルド
make

Tintのインストール

ビルドに成功すると、
out/Debugにtint.exeファイルなどが沢山できているので、
これらを適当なディレクトリに配置し、これにもPATHを通しておきます。

任意のディレクトリで、

tint --help

を実行して正常に動作すればOKです。

変換をする

変換前のHLSL形式のShaderはこちらです。
ごくシンプルな、頂点カラーそのままを出力するFragmentShader(PixelShader)です。

struct VSOut {
        float4 Pos : SV_Position;
        float3 Color : TEXCOORD0;
};

float4 main(VSOut In) : SV_Target0
{
    return float4(In.Color, 1.0f);;
}

HLSL→SPIR-V(DXCを使用)

dxc.exe を使います。

dxc -spirv -T ps_6_0 -E main FragmentTest.hlsl -Fo FragmentTest.spv

指定しているオプションについては、下表のとおりです。
dxcのオプションの詳細は、公式ドキュメントdxc --help コマンドから確認してください。

オプション 説明
-spirv SPIR-V形式に出力
-T ps_6_0 ターゲット。
シェーダーステージ(vs/ps/csなど)や、
シェーダーモデル(6_0など)を指定。
ここでは、PixelShaderとShaderModel 6.0を指定しています。
-E main エントリポイントの関数名を指定します。
FragmentTest.hlsl コンパイル元のhlslファイル。
-Fo FragmentTest.spv 出力先のSPIR-V形式のファイル。

成功すれば、FragmentTest.spvが出力されます。

SPIR-V→WGSL(Tintを使用)

tint.exeを使います。

tint FragmentTest.spv -o FragmentTest.wgsl

無事、WGSL形式のシェーダーが出力されました。

var<private> in_var_TEXCOORD0 : vec3<f32>;

var<private> out_var_SV_Target0 : vec4<f32>;

fn main_1() {
  let x_13 : vec3<f32> = in_var_TEXCOORD0;
  out_var_SV_Target0 = vec4<f32>(x_13.x, x_13.y, x_13.z, 1.0f);
  return;
}

struct main_out {
  @location(0)
  out_var_SV_Target0_1 : vec4<f32>,
}

@fragment
fn main(@location(0) in_var_TEXCOORD0_param : vec3<f32>) -> main_out {
  in_var_TEXCOORD0 = in_var_TEXCOORD0_param;
  main_1();
  return main_out(out_var_SV_Target0);
}

おわりに

HLSL→SPIR-V→WGSLの変換をやりました。

エンジン開発などでのクロスプラットフォーム対応や、HLSLのincludeなどのプリプロセッサ機能を使いたい場合などに活きそうな技術です。

あと、ゲームコンソールでもSDKとかにトランスパイル出来る仕組みってあるのかなぁってふと疑問を抱いたりしてます。NDAに触れちゃうであろう事項なので、いまのただの学生という身分では知りようがないのですが。

これからも、WebGPUなど色々と調査・研究していきます。

参考にしたもの

HLSL を SPIR-V 経由で GLSL に変換してみた - Qiita

SPIR‐V CodeGen · microsoft/DirectXShaderCompiler Wiki · GitHub

refs/heads/main - tint - Git at Google

http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up