meshからgpuへの命令の経路について

2022/10/13に公開

bevyのようなフレームワークからgpuへどうやっていくの

bevyはrustで作られたゲームエンジンを指します。これは、UnityやUnread Engine同様にゲームを作るためのゲームエンジンです。わたしは、このbevyを使ってsvgから読み取ったデータから図形を作成するソースを作っています。

ゲーム画面上に図形を表示するときに使う「mesh」というオブジェクトがあります。これは、かんたんに言うと、点、線、面を表示するために使うオブジェクトです。これを使って、点、線、四角形を書きます。

ここでコーディング中に問題になったのが、うまく線が引けないという事態に陥りました。どういうことかというと、四角形を作るために[[0,0], [0,1], [1,1], [1,0]]という配列があるとすると、四角形の頂点と頂点の間に斜線が入ってしまうという事態が起こってしまいました。

これは、bevyが提供しているライブラリではなく、wgpuというweb gpuをrustで扱えるライブラリを使っているようでした。meshで使われているmeshの属性を決定する定数はwgpuから持ってきていることを確認しました。

Web GPUはw3Cが定めるWebブラウザからgpuへのアクセスの規格です。ですので、wgpuはweb gpuの規格に依存している可能性が高いです。

このことから、bevyやunity, unreal engineはOpenGLやVulkan,Web GPUといったGPUへの標準規格に依存してその上に自身が作るライブラリを作ってと推測できます。

Bevyに限った話ではありますが、下記のような順番でgpuに命令を送っていると推測されます。

  1. Bevyのコードからwgpuを使ってgpuに命令を送る
  2. wgpuはweb gpuのapiをもとにして、命令をデコレートして送る
  3. web gpuに規格に基づいた命令によって、gpuが実行して画面に表示される

上記のような手順で私達が画面上で表示を見るとことができると推測されます。
このweb GPUはRailsのようなわかりやすいapiの集合ではなく、低レイヤーで使われるapiに近い集まりです。つまり、GPUに対しての命令をアセンブラ命令の集まりを一つにした命令集に近いです。

OpenGL, Web GPU, Vulkanとは

OpenGL, Web GPU, Vulkanはグラフィックライブラリの規格に当たります。OpenGLならAPIを通して画面に三角形、四角形、または複雑な図形を表現することができます。

上記のライブラリが無いと、メモリやGPUに対して直接命令する必要があります。アセンブラ言語を使ってプログラムを作って直接命令を送って表示します。この方法だと、アセンブラの知識が必要ですし、そのアセンブラのコーディングも人によって精度にムラが出てきます。そこがバグの原因になる可能性があります。

OpenGL、Web GPU, Vulkanは機関が作った規格なので、利用者は規格通りに動くことさえ確認できればその上のアプリケーションで低レイヤー部分を共通化することができます。

OpenGL, Web GPU, VulkanはRailsのライブラリのような便利なツールの集合ではないです。RailsはRailsを構成するライブラリを組み合わせて作られるWebフレームワークです。OpenGL, Web GPU, Vulkanといった規格は便利なツールは提供しません。これらが提供するのは「面を表示する」、「メッシュを表示する」、「シェーダーを表示する」といったことしか提供しません。特に、Web GPUとVulkanは低レイヤーapiを提供しています。

なぜ低レイヤーapiにするのか

低レイヤーでapiで作るのは、低レイヤーAPIにするとライブラリ固有のバグの回避と、レンダリング性能が上がるからだそうだ。

openGLはgpuに対する命令のラッパーが大量にあって扱いやすさがある反面、openGL自体のバグに遭遇したときにデバッグに難儀したという。低レイヤーのapiに準拠していれば、低レイヤーapiがバグったとしても回避方法は利用者側がリカバリーできたり、別のapiを使うことで回避することができます。

このため、低レイヤーのapiを使うことで実装時にあるライブラリ特有のバグを解消するために低レイヤーapiにします。

更に、レンダリング性能を上げるのにも一役買うと言われています。これは、ゲームのプログラミングするのに際して、GPUに対してパイプライン処理の実装、並列化が期待できます。従来のOpenGLのようなライブラリだと、これらの実装はOpenGLを解釈して実装しなければなりません。

GPUに対しての命令を実装したいのに、それをするためにはOpenGLを経由するためOpenGLにない実装はできなくなります。そこで必要となるのOpenGLの解釈を予想して並列にさせる方法が取ることができます。

これを解消するためには、命令一つ一つを単純にさせる必要があります。Shaderを使うにしても、単純なAPIの組み合わせによって命令を送ることで実装者のバグに対する負担を減らします。その代わりに、学習曲線は急になります。この学習曲線を犠牲にしても低レイヤーAPIが必要なのです。

どのレベルで抽象化するのか

ここまで来ると、実装者はどのレベルで実装する必要があるのか、という問に突き当たります。

抽象化とは、ある処理を人間にとって読みやすくる作業と解釈することができます。詰まるとこと、実装にするときに人間が同じ処理をするときに楽をするための作業です。

どのレベルに合わせるというのは重要な問です。このレベルというのはインターネットでよく使われるOSI参照モデルのレイヤーを想像してくれればよいでしょう。これを例にすると、レイヤー1で持つべき処理を2、3に置いたらどこでどの処理が行われているかがわからなくなります。物理層の01で処理する作業がレイヤー2,3で行われていたら処理が混在してしまって、修正の困難さが上がり実装の困難さも上がります。

これらをゲームに置き換えて見ると、単なる行列をGPUに送る処理をmeshという表示に関するものに置き換えた場合、処理が負えなくなってしまいます。そのため、行うべき手段はレベル別の抽象化となります。

この場合はまず行列をGPUに送る部分を抽象化します。GPUに送るにしても、受け取った配列の構造を決めて、使用者は配列の構造と関数、構造体の使い方だけを知っている状態で使えるようにします。その上の階層で、受け取ったファイルからの1階層目に対して送る処理を書きます。このように徐々にボトムから攻めて言って最終的に作りたいものを作ります。

抽象化をすることでより良い成果を労せずに手に入れることができるのです。そのために抽象化は必要なのです。

参考資料

https://ja.wikipedia.org/wiki/WebGL
https://ja.wikipedia.org/wiki/WebGPU
https://gpuweb.github.io/gpuweb
https://tokoik.github.io/gg/ggnote01.pdf
WebGPU - コアの全てを canvas 抜きで

Discussion