OpenGLの拡張名では GL_ARB_draw_indirect となる DrawIndirect の機能を試してみました。これはVRAM上のバッファに描画用データを格納して、その情報を元に描画を行う機能となります。通常 glDrawArrays や glDrawElements で描画の際に関数の引数として設定していたデータについて、VRAM上から該当するデータを参照して描画するコマンドとなります。
これで何がおいしいのかというと、GPU上で描画の詳細情報を決めることができるようになります。ジオメトリシェーダーで描画プリミティブ数を増やしたことについても、CPU側が把握せずに描画することができたり、OpenCLやComputeShader といったもので GPU上で計算した結果を即描画を行うということが可能になります。従来ですと、一度CPUに情報を引き渡して描画コマンドを実行する必要がありました。このCPUへの通知が不要になった分、GPU側が全力で動いたままにすることができます。
準備
頂点バッファと同じように VBO を作成します。この作成したバッファを描画コマンド格納用のバッファとします。インデックス描画のON/OFF で以下のように格納するべきデータレイアウトが異なります。
// インデックスなし struct CommandDrawArrays { GLuint vertexCount; GLuint instanceCount; GLuint firstVertex; GLuint baseInstance; }; // インデックスあり struct CommandDrawElements { GLuint indexCount; GLuint instanceCount; GLuint firstIndex; GLint baseVertex; GLuint baseInstance; };
このデータで中身をセットして、まずはVRAMにデータを送っておきます。バインド先には GL_INDIRECT_BUFFER を指定します。baseVertex, baseInstance には基本ゼロを設定しておきます。baseInstance については OpenGL Version によって、ゼロ以外を許さなかったりします。
CommandDarwElements src; // srcメンバに値をセットしておく. GLuint idbuf; // indirect buffer glGenBuffers( 1, &idbuf ); glBindBuffer( GL_INDIRECT_BUFFER, idbuf ); glBufferData( GL_INDIRECT_BUFFER, sizeof(CommandDrawElements), &src, GL_STATIC_DRAW );
描画
コマンドをバッファに格納したあと描画するには次のようにします。描画用のデータはバッファの先頭に格納したので、indirect用のポインタにはヌル(ゼロ)を指定しています。このあたりはVertex Buffer Object のときと同じような考え方でよさそうです。
glBindVertexArray( vao ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo ); glBindBuffer( GL_INDIRECT_BUFFER, idbuf ); glDrawElementsIndirect( GL_TRIANGLES, GL_UNSIGNED_SHORT, NULL );
ちなみに、インデックスなしの時には、glDrawArraysIndirect を使用するように変化します。
まとめ
今回は DrawIndirect の基本的なサンプルを紹介しました。OpenGLのこのDrawIndirect の機能は DirectX と比較して、充実している部分だと聞いています。近いうちにどこが優れているのか調べてみたいと思います。