Open5

C-WebGL: GLES2エミュレーションの実装(シェーダ関連)

okuokuokuoku

シェーダー関連は割とややこしいトピックが多い。

  1. ActiveUniform/Attribのクエリ -- WebGL ではDOMStringを返却してくる関係で変換が必要。
  2. UniformLocationの変換 -- WebGLではUniformLocationはオブジェクトなので何らかの方法でキャッシュしてインデックスに変換する必要がある。WebGL2ではGLSL内でlocationを宣言できるので更にややこしい。
  3. AttribArrayの指定のトラッキング -- 描画時にclient side arrayになってないかチェックして、そうなっていた場合は手動でGPUにアップロードし、bindしてから描画する。

Emscriptenは、Uniform関連のクエリは完全に自前実装している。C-WebGLもそうすべきなんだけど面倒なので一旦適当に実装して様子見。。早すぎる最適化はどうのこうの。

okuokuokuoku

glGetActiveAttribglGetActiveUniform

https://github.com/okuoku/cwgl-glue/commit/b055daa43a1c60a11f4b09cfa61f7e1166ba4692

length パラメタは NULL でも良い。

https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGetActiveAttrib.xhtml

If the length of the returned string is not required, a value of NULL can be passed in the length argument.

というか length が返却するのは実際に書いた長さなのか。。

https://github.com/okuoku/cwgl-glue/commit/3e561b43a58d1f91980ff17f25fffe64a153fb91

GLsizei が符号付なのは世間的にどうなんだ。。?

okuokuokuoku

Uniform

https://github.com/okuoku/cwgl-glue/commit/0557a43ffc412164fb1fba623a4031c8593cf04a

一度コンテキスト側に覚えさせたUniformLocationの解放はコンテキスト側で勝手にやる方針にした。

https://github.com/okuoku/cwgl-glue/blob/0557a43ffc412164fb1fba623a4031c8593cf04a/src/glue-ctx.c#L80-L91

吉と出るか凶と出るか。。いやダメだなコレ。。LinkProgramが成功する度にキャッシュ消さないとダメじゃん。。あと、関数は失敗を許容されていて、失敗した場合は -1 を返却する。

https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGetUniformLocation.xhtml

LinkProgram 時にステートをクリアする

めんどいので glLinkProgram が発行されたら常にキャッシュ消すことにした。

https://github.com/okuoku/cwgl-glue/commit/08c076d0666e003957e5df39a20ef532c01541dd

ちなみにこの挙動はWebGL固有で、本来のOpenGL ESでは実際にprogramが有効化されるまでは直前の挙動をキープするように要求されている。まぁ実際のプログラムでこれに依存しているケースはバグの可能性が高いから良いんじゃないかということで。。

https://registry.khronos.org/webgl/specs/latest/1.0/#6.43

エラー返却

https://github.com/okuoku/cwgl-glue/commit/46e0ab36c7a1154ac9750603c42419d2ef471c96

こっちは簡単。

okuokuokuoku

Arrayは面倒。。

Emscriptenでも話題に挙がってるけど:

https://github.com/emscripten-core/emscripten/issues/4214

glDrawElements によってclient側のどのメモリにアクセスされるかを検出するのが割と難しい。まぁこのケースは殆ど無いから実装をサボっても良いんじゃないだろうか。。

サボりたいのは:

  1. glDrawElements にclient side arrayがあるケース。Bufferのコピーを持つか、描画の度に ELEMENT_ARRAY_BUFFER になっているバッファをダウンロードしてきて(※ 本来のWebGLではできない)最大のindexを導出し、それを元にGPUに転送すべきデータを求める必要がある。
  2. 異なる stride の複数のattributeを単一のbufferに纏められるケース
  3. ELEMENT_ARRAY_BUFFERARRAY_BUFFER に同じbuffer objectを使うケース( https://github.com/emscripten-core/emscripten/issues/4033 )

仮にこれで動かないアプリがあれば、32頂点以下ならハンドルするみたいな例外を設けるのが簡単だと思う。