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
も必要だった。
OriginLowerLeft
ではない
Vulkanは 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
マジかよ。。まぁ重複を避けるだけなら簡単。。
OpVariable
は先頭に配置する
Function内の 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
OpVariable
instructions in a function must be in the first block in the function. These instructions, together with any intermixedOpLine
andOpNoLine
instructions, 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
と同じにする。