C-WebGL: シェーダーのバリデーションを通す
いきなりGPUドライバのSPIR-V処理系に突っ込んでもデバッグできる気がしないので、SPIRV-Toolsに含まれるバリデータとオプティマイザに掛けて、パッチした中間コードが正しいかどうかを事前に検証しておくことにした。
OpName の位置制約
ERROR: 16: Name is in an invalid layout section
OpName %cwgl_ubo_s "cwgl_ubo_s"
Vulkan的には OpName はデバッグ命令扱いなのか。。(WebGL1の場合は名前でマッチングするしかないため、 OpName な名前が残っていないとシェーダが使えない)
- These debug instructions, which must be grouped in the following order:
というわけで専用のバッファを設けて、そちらに書いてから繋ぐように変更した。
OpNop は block内にのみ配置できる?
... マジで。。?そんなの仕様書に書いてなくない。。?
ERROR: 32: Nop must appear in a block
OpNop
SPIRV-Tools内では、特にNopを狙いうちしたバリデーションではないが。。
とりあえず最終段で OpNop を削除することにした。
OpAccessChain の型間違い
ERROR: 49: The result pointer storage class and base pointer storage class in OpAccessChain do not match.
%25 = OpAccessChain %_ptr_Private_mat4v4float %cwgl_ubo %uint_0
%cwgl_ubo はこちらで生成した uniform なのでSPIR-V上のstorage classも当然 Uniform 。つまり、 Uniform strorage classを持つTypePointerをわざわざ生成しないといけないのか。。
MatrixStride の生成わすれ
ERROR: 42: Structure id 32 decorated as Block must be explicitly laid out with MatrixStride decorations.
%cwgl_ubo_s = OpTypeStruct %mat4v4float
元々 ColMajor と Block の付けわすれ:
ArrayStride の付けわすれ:
は直してたけど、 MatrixStride も必要だった。
Vulkanは OriginLowerLeft ではない
ERROR: 5: [VUID-StandaloneSpirv-OriginLowerLeft-04653] In the Vulkan environment, the OriginLowerLeft execution mode must not be used.
OpExecutionMode %main OriginLowerLeft
これはまぁglslangの生成したASTにフラグを立てればOK。
どこで座標系の違いを吸収するか考えてなかったな。。Texture命令の前でflipするとか、Textureを常に上下反転するとか色々考えられはするけど。。
gl_FragColor には location = 0 が必要
ERROR: 32: Variable must be decorated with a location
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
... これってglslangが生成してくれるもんなんじゃないの。。? GLES3以降の世界では、gl_FragColor や gl_FragDataはbuilt inとして扱う必要がない。WebGL1の世界では、 gl_FragColor が唯一のOutputになるので、自動的にlocation 0がアサインされる。
Type定義は順番に行う必要がある
ERROR: 205: Operand 383[%uint_4] requires a previous definition
%cwgl_ubo_s = OpTypeStruct %mat3v3float %mat3v3float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %uint_4
bool 型を int32 に変換する 際に、int32の型を先に宣言しておく必要があった。
Type定義は重複できない
ERROR: 205: Duplicate non-aggregate type declarations are not allowed. Opcode: TypeInt id: 383
%uint_4 = OpTypeInt 32 0
マジかよ。。まぁ重複を避けるだけなら簡単。。
Function内の OpVariable は先頭に配置する
ERROR: 259: All OpVariable instructions in a function must be the first instructions in the first block.
%objectNormal = OpVariable %_ptr_Function_v3float Function
そんな制約有るのかよ!C言語でもあるまいし。。
All
OpVariableinstructions in a function must be in the first block in the function. These instructions, together with any intermixedOpLineandOpNoLineinstructions, must be the first instructions in that block.
あったわ。。ブロックは OpLabel で開始するというルールになっているため、 OpLabel OpLine OpNoLine OpVariable を読み飛ばす必要がある。あとついでに OpFunction も同じ関数の中で読みとばすことにした。
配列への OpAccessChain のresult typeは要素のtype
ERROR: 1002: OpAccessChain result type (OpTypeArray) does not match the type that results from indexing into the base <id> (OpTypeVector).
%2738 = OpAccessChain %_ptr_Uniform__arr_v3float_uint_9 %cwgl_ubo %uint_15 %uint_0_0
冷静に考えると当たり前だけど。。非常にダサいけど配列要素の型を事前に解決して使う方式にした。さすがにリファクタしたくなってきたな。。
bool 型への変換
ERROR: 1167: OpLoad Result Type <id> '148[%bool]' does not match Pointer <id> '2549[%isOrthographic]'s type.
%2550 = OpLoad %bool %isOrthographic
UBOではboolを宣言できないためint32に変換しているが、これをGLSL内でのboolに代入するには型変換が必要になる。これは単に OpLoad の代わりに OpINotEqual で定数ゼロと比較してやれば良い。
vec3 を配列にしたときは要素のアラインは16bytesになる
ERROR: 930: Structure id 2845 decorated as Block for variable in Uniform storage class must follow relaxed uniform buffer layout rules: member 15 contains an array with stride 12 not satisfyi
ng alignment to 16
%cwgl_ubo_s = OpTypeStruct %mat4v4float %v3float %uint %float %float %v3float %float %v3float %float %float %float %float %float %float %v3float %_arr_v3float_uint_9 %int %float %uint
要は vec4 と同じにする。