Open3
C-WebGL: SPIR-V上で直接ESSL1をVulkan用に変換できるか問題
... というわけで、ESSL → GLSLのトランスパイラを書くよりSPIR-V上で直接変換してしまう方が100倍簡単なので挑戦してみる。
簡単な例
ESSL1シェーダ(変換元):
uniform bool paint;
void main() {
if(paint){
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}else{
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}
SPIR-V Crossでのラウンドトリップ(ESSL1を出力する):
$ /cygdrive/c/VulkanSDK/1.2.162.0/Bin/spirv-cross.exe out.frag.spv
#version 100
precision mediump float;
precision highp int;
uniform bool paint;
void main()
{
if (paint)
{
gl_FragData[0] = vec4(1.0);
}
else
{
gl_FragData[0] = vec4(0.0, 0.0, 0.0, 1.0);
}
}
SPIR-V CrossでのGLSL450変換:
#version 450
uniform bool paint;
out vec4 _RESERVED_IDENTIFIER_FIXUP_gl_FragColor;
void main()
{
if (paint)
{
_RESERVED_IDENTIFIER_FIXUP_gl_FragColor = vec4(1.0);
}
else
{
_RESERVED_IDENTIFIER_FIXUP_gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}
out
への貼り替えやリネームは勝手にやってくれることがわかる。この状況では bool
型が外に見えているのでバリデーションに通らない。
$ /cygdrive/c/VulkanSDK/1.2.162.0/Bin/spirv-val.exe out.frag.spv
error: line 15: If OpTypeBool is stored in conjunction with OpVariable, it can only be used with non-externally visible shader Storage Classes: Workgroup, CrossWorkgroup, Private, and Function
%paint = OpVariable %_ptr_UniformConstant_bool UniformConstant
手動で変換する
#version 450
layout(binding = 0) uniform indataStruct {
bool paint; // ★ Uniform に入れる
}indata ;
bool paint; // ★ 元のUniformはグローバル変数にする
layout(location = 0) out vec4 xgl_FragColor;
void main() {
paint = indata.paint; // ★ 先頭でグローバル変数に代入する
if(paint){
xgl_FragColor = vec4(1.0,1.0,1.0,1.0);
}else{
xgl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}
これを spirv-opt -Os
で最適化したあとに、再度GLSLに変換すると、
#version 450
layout(binding = 0, std140) uniform indataStruct
{
uint paint; // ★ 何か勝手にuintになっている
} indata;
layout(location = 0) out vec4 xgl_FragColor;
void main()
{
if (indata.paint != 0u) // ★ 代入文が消えている
{
xgl_FragColor = vec4(1.0);
}
else
{
xgl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}
のように、求めているコードを得られた。後は手変換前後で逆アセンブリを比較してやれば良さそう。
逆アセンブリの比較
... 変数の宣言だけでも割と冗長だな。。
やらないといけないのは、
- 対象の変数を
OpVariable
命令を走査して探す。UniformConstant
で、かつ samplerやテクスチャでないものが対象 - グローバル変数に変換するための
OpTypePointer
命令を生成(UniformConstant
→Private
) - 対象の変数の
OpVariable
を 2. で生成した型にパッチ - UBOになる構造体を
OpTypeStruct
で生成 - 代入文を
main
の先頭に生成
SPIR-Vは32bitのワード単位で自由に命令列の挿入や削除が可能なため、GLSLフロントエンドのASTを直接編集するよりは簡単に変換できる...と、思う。