Open12

C-WebGL: シェーダーのバリデーションを通す

okuokuokuoku

いきなりGPUドライバのSPIR-V処理系に突っ込んでもデバッグできる気がしないので、SPIRV-Toolsに含まれるバリデータとオプティマイザに掛けて、パッチした中間コードが正しいかどうかを事前に検証しておくことにした。

okuokuokuoku

OpName の位置制約

ERROR: 16: Name is in an invalid layout section
  OpName %cwgl_ubo_s "cwgl_ubo_s"

Vulkan的には OpName はデバッグ命令扱いなのか。。(WebGL1の場合は名前でマッチングするしかないため、 OpName な名前が残っていないとシェーダが使えない)

  1. These debug instructions, which must be grouped in the following order:

というわけで専用のバッファを設けて、そちらに書いてから繋ぐように変更した。

https://github.com/okuoku/yuniframe/commit/686bc2de347b44230e8562efe9a5a9608adbbffb

okuokuokuoku

OpNop は block内にのみ配置できる?

... マジで。。?そんなの仕様書に書いてなくない。。?

ERROR: 32: Nop must appear in a block
  OpNop

SPIRV-Tools内では、特にNopを狙いうちしたバリデーションではないが。。

とりあえず最終段で OpNop を削除することにした。

https://github.com/okuoku/yuniframe/commit/0ed1bb2e95ddba188bb7a4c26abe8c24e938ca1a

okuokuokuoku

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をわざわざ生成しないといけないのか。。

https://github.com/okuoku/yuniframe/commit/85b7432ce3000dc75dc3acaaaea65baef4dd1d43

okuokuokuoku

MatrixStride の生成わすれ

ERROR: 42: Structure id 32 decorated as Block must be explicitly laid out with MatrixStride decorations.
  %cwgl_ubo_s = OpTypeStruct %mat4v4float

元々 ColMajorBlock の付けわすれ:

https://github.com/okuoku/yuniframe/commit/5427247d8e5c8560dc8ad241c01ad9b77b8ac205

ArrayStride の付けわすれ:

https://github.com/okuoku/yuniframe/commit/eb7ad7f403b8995b5d7d57b81cfdc8b64c47f0ac

は直してたけど、 MatrixStride も必要だった。

https://github.com/okuoku/yuniframe/commit/e00fe8c8e7ccf4f4acf51cf61a8a4a186088ce71

okuokuokuoku

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。

https://github.com/okuoku/yuniframe/commit/4fef57613d76e22470a984c150121f3cf189d5d4

どこで座標系の違いを吸収するか考えてなかったな。。Texture命令の前でflipするとか、Textureを常に上下反転するとか色々考えられはするけど。。

okuokuokuoku

gl_FragColor には location = 0 が必要

ERROR: 32: Variable must be decorated with a location
  %gl_FragColor = OpVariable %_ptr_Output_v4float Output

... これってglslangが生成してくれるもんなんじゃないの。。? GLES3以降の世界では、gl_FragColorgl_FragDataはbuilt inとして扱う必要がない。WebGL1の世界では、 gl_FragColor が唯一のOutputになるので、自動的にlocation 0がアサインされる。

https://github.com/okuoku/yuniframe/commit/3809f3043c48258b1e4bc8675cf1be5511da76e2

okuokuokuoku

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の型を先に宣言しておく必要があった。

https://github.com/okuoku/yuniframe/commit/79f611cfb20b126f71d5ad7aeab1cee7efbfec00

Type定義は重複できない

ERROR: 205: Duplicate non-aggregate type declarations are not allowed. Opcode: TypeInt id: 383
  %uint_4 = OpTypeInt 32 0

マジかよ。。まぁ重複を避けるだけなら簡単。。

https://github.com/okuoku/yuniframe/commit/76dfcb41f5f0302446bcabda8de91c725085bb86

okuokuokuoku

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 OpVariable instructions in a function must be in the first block in the function. These instructions, together with any intermixed OpLine and OpNoLine instructions, must be the first instructions in that block.

あったわ。。ブロックは OpLabel で開始するというルールになっているため、 OpLabel OpLine OpNoLine OpVariable を読み飛ばす必要がある。あとついでに OpFunction も同じ関数の中で読みとばすことにした。

https://github.com/okuoku/yuniframe/commit/29b98c644bac4cf50ae2528f73cbf3c9393d0996

okuokuokuoku

配列への 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

冷静に考えると当たり前だけど。。非常にダサいけど配列要素の型を事前に解決して使う方式にした。さすがにリファクタしたくなってきたな。。

https://github.com/okuoku/yuniframe/commit/7f74453c00e80ed31f49b33777f2b3ccf6985a81

okuokuokuoku

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 で定数ゼロと比較してやれば良い。

https://github.com/okuoku/yuniframe/commit/9e28fb8de6aa1c9d9f535bcb5529f68c51fe24e0

okuokuokuoku

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 と同じにする。

https://github.com/okuoku/yuniframe/commit/37e039f86c41d7c6e29dbf53c81c44fef36c2e1e