はじめに
DirectX 12とVulkanのために, glslangとDirectXShaderCompiler (dxc) のライブラリを作成し, プログラム内でシェーダのコンパイルができるようにします.
ツールで事前コンパイルの方法にしないのは, 単純に開発時やチュートリアルのためのプログラムを作るときに楽だからです.
シェーダ言語と中間言語とバイナリの関係は, 現在は次のようになっています. 歴史的に, GLSLがHLSLに比較して機能が少なく当初は中間言語がなかったり, HLSLは当初は中間言語ではなくアセンブラだったりします.
HLSL -> DXIL -> Vendor Specific Binary
GLSL -> SPIR-V -> Vendor Specific Binary
dxcには, HLSLからSPIR-Vへのコンパイル機能があります.
glslangについては, HLSLをSPIR-Vへ変換する機能は開発中です.
Status: Partially complete. Semantics are not reference quality and input is not validated. This is in contrast to the DXC project, which receives a much larger investment and attempts to have definitive/reference-level semantics.
最適解はまだわかりませんが, HLSLからDXILとSPIR-Vが安定しているのではないかと思います. HLSLとGLSLを両方メンテナンスすることは避けたいです.
基本的なソフトウェアエンジニアリングについて知っていることが前提です. Visual Studio, Make, CMake, Python, Ninjaの説明もしません.
ビルド
Windows
glslang
リポジトリGitHub.
Visual StudioからInstallを行いたい場合, ファイル属性もローカルに反映してしまうため, 次のようにして抑制します.
git config --global core.fileMode false
Win32 Runtimeを変更するには, CMakeLists.txt
の中でChooseMSVCCRT.cmake
をincludeする前に指定します.
set(LLVM_USE_CRT_RELEASE "MT")
set(LLVM_USE_CRT_DEBUG "MTd")
include(ChooseMSVCCRT.cmake)
glslangディレクトリ内で, ビルド環境を作成します.
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="path" -G "generator" ..
DirectXShaderCompiler
ビルド
結果を先に書くと, ビルド済みのものをダウンロードする方が安定すると思います.
Installが上手く動かず必要なバイナリやヘッダーがわかりにくいです.
リポジトリGitHub.
ATLと.Net Framework 4.5が必要です, Visual Studio Installerでインストールします.
$ git clone https://github.com/Microsoft/DirectXShaderCompiler.git
$ cd DirectXShaderCompiler
$ git submodule update --init
$ .\utils\hct\hctstart.cmd -x64 [path-to-sources] [path-to-build]
$ .\utils\hct\hctbuild -vs2019 -spirv -x64
使いそうなものだけ, utils\hct\hctbuild --help
で説明が見れますが, utils\hct\hctbuild
を直接見た方が早いです. -spirv
のようにヘルプに説明がないものがある.
-
-s
: プロジェクトファイルを生成するだけ- Windowsでは, Visual Studioで開いてビルド, インストールするのが正解. 後述の
-Debug
,-Release
オプションではデバッグ・リリースビルドの区別ができない.
- Windowsでは, Visual Studioで開いてビルド, インストールするのが正解. 後述の
-
-no-parallel
: Ninjaでは効果ないように見える. Visual Studioなら自分で修正すればいいので, 存在価値が不明, make用か? -no-dxilconv
-
-vs2017
,-vs2019
: コンパイラ -
-ninja
: ビルド環境 -
-x86
,-x64
,-arm
,-arm64
: アーキテクチャ -
-Debug
,-Release
使い方
まず, HLSLからDXILへコンパイル.
Wiki: Using-dxc.exe-and-dxcompiler.dll
HLSL to DXIL
{//HLSL -> DXIL
CComPtr<IDxcBlobEncoding> sourceBlob = nullptr;
utils->CreateBlob(hlsl, sizeof(hlsl), 65001, &sourceBlob);
DxcBuffer source;
source.Ptr = sourceBlob->GetBufferPointer();
source.Size = sourceBlob->GetBufferSize();
source.Encoding = DXC_CP_ACP;
LPCWSTR args[] = {
L"shader.hlsl", // Optional shader source file name for error reporting and for PIX shader source view.
L"-E", L"main", // Entry point.
L"-T", L"vs_6_0", // Target.
L"-Zi", // Enable debug information.
//L"-Qstrip_reflect", // Strip reflection into a separate blob.
};
CComPtr<IDxcResult> results;
compiler->Compile(
&source,
args,
_countof(args),
includeHandler,
IID_PPV_ARGS(&results));
CComPtr<IDxcBlobUtf8> errors = nullptr;
results->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&errors), nullptr);
if(nullptr != errors && 0 < errors->GetStringLength()) {
printf("[Error]\n%s\n", errors->GetStringPointer());
}
HRESULT status = E_FAIL;
results->GetStatus(&status);
if(FAILED(status)) {
return EXIT_FAILURE;
}
// shader binary
CComPtr<IDxcBlob> shaderBlob = nullptr;
CComPtr<IDxcBlobUtf16> shaderName = nullptr;
results->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(&shaderBlob), &shaderName);
if(nullptr != shaderBlob) {
printf("A shader binary exists.\n");
}
// pdb
CComPtr<IDxcBlob> pdbBlob = nullptr;
CComPtr<IDxcBlobUtf16> pdbName = nullptr;
results->GetOutput(DXC_OUT_PDB, IID_PPV_ARGS(&pdbBlob), &pdbName);
if(nullptr != pdbBlob) {
printf("A shader pdb %ls exists.\n", pdbName->GetStringPointer());
}
// shader hash
CComPtr<IDxcBlob> hashBlob = nullptr;
results->GetOutput(DXC_OUT_SHADER_HASH, IID_PPV_ARGS(&hashBlob), nullptr);
if(nullptr != hashBlob) {
DxcShaderHash* hash = (DxcShaderHash*)hashBlob->GetBufferPointer();
printf("A shader hash: ");
for(size_t i = 0; i < _countof(hash->HashDigest); ++i) {
printf("%X", hash->HashDigest[i]);
}
printf("\n");
}
// separated reflection
CComPtr<IDxcBlob> reflectionBlob;
results->GetOutput(DXC_OUT_REFLECTION, IID_PPV_ARGS(&reflectionBlob), nullptr);
if(nullptr != reflectionBlob) {
DxcBuffer reflectionBuffer;
reflectionBuffer.Ptr = reflectionBlob->GetBufferPointer();
reflectionBuffer.Size = reflectionBlob->GetBufferSize();
reflectionBuffer.Encoding = DXC_CP_ACP;
CComPtr<ID3D12ShaderReflection> reflection;
utils->CreateReflection(&reflectionBuffer, IID_PPV_ARGS(&reflection));
D3D12_SHADER_DESC shaderDesc;
if(SUCCEEDED(reflection->GetDesc(&shaderDesc))){
printf("shader %d\n", shaderDesc.Version);
}
}
}
HLSLからSPIR-Vへコンパイル, オプションは使いそうなものを選んだが, 実際に試験してみないとどれが必要かわからない.
Wiki: SPIR‐V CodeGen
Wiki: HLSL to SPIR-V Feature Mapping Manual
オプションが異なるだけです. ただし, デバッグ情報もリフレクション情報も生成されないので調査中です.
HLSL to SPIR-V
LPCWSTR args[] = {
L"-spirv", // Generates SPIR-V code.
L"-E", L"main", // Entry point.
L"-T", L"vs_6_0", // Shader profile.
L"-Zi", // Enable debug information.
L"-fvk-use-gl-layout", // Uses strict OpenGL std140/std430 layout rules for resources.
//L"-fvk-use-dx-layout", //Uses DirectX layout rules for resources.
L"-fspv-reflect", // Emits additional SPIR-V instructions to aid reflection.
L"-fspv-flatten-resource-arrays", // Flattens arrays of textures and samplers into individual resources.
}
Linux
まだライブラリをビルドしたところまで, コードでは試していません.
- C/C++コンパイラとツールチェイン
- Git
- Python
- CMake
- Ninja
glslang
$ git clone https://github.com/KhronosGroup/glslang.git
$ mkdir build && cd build
$ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="XXX"
$ make install
DirectXShaderCompiler
libexecinfo-dev
が必要かもしれません.
$ mkdir build && cd build
$ cmake .. -GNinja $(cat ../utils/cmake-predefined-config-params) -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="XXX"
$ ninja build
$ ninja install
まとめ
とりあえず, dxcでHLSLをコンパイルするところまで. 後に追記するかも.