見出し画像

UnityのSRP Batcherとは何なのか

UnityにはScriptable Render Pipeline環境で使用できるSRP Batcherという仕組みがあります。SRP Batcherに関してはUnity Blogでも解説されているのですが、周囲の人と話してみるとうまく伝わっていない印象です。ここではUnityで初めてシェーダーを書いたという方や、逆に自分でレンダラーを作ったことがあるがUnityのことはよく知らないという方向けに解説させていただきたいと思います。
一言でいえば、Draw Call毎にConstant Bufferを更新するという非効率な仕組みをやめたということです。これによりレンダリングに関連するCPUコストが削減されます。

Constant Buffer(CBUFFER)とは何なのか

最近の一般的なGraphics API(DirectX11, 12, Metal, Vulkanなど)ではConstant Buffer(定数バッファー)という仕組みがあります。ここにはUnityで言うところのMaterial Propertyや各種変換行列などのCPUからGPUに渡す変数が記録されています。Constant Bufferが無い時代はまとめて管理する仕組みが無く変数を一つ一つ更新していました(OpenGLで言うところのUniform変数)。これは非効率だということでバッファを使用してまとめて更新できるようにしたのがConstant Bufferです。CPUでも変数を一つ一つ代入するよりmemcpyで一気にコピーした方が速いですよね?

Draw Call毎に更新することの何が悪いのか

最初にConstant BufferがDraw Call毎に更新されていた、ということを説明しましたが何が悪いのでしょうか?それはConstant Bufferを更新して反映させるためにはCPUからGPUへバッファをアップロードする必要があります。なので毎フレーム、すべてのバッファを更新するのは非効率なわけです。基本的には毎フレーム更新されるようなもの(各種変換行列など)とたまにしか更新されないもの(Material Propertyなど)に分けておき、更新されたときにアップロードします。(実はこの辺りの効率的な更新方法については色々あるのですが、解説するのは大変なので省きます)
その上でDraw Call毎にアップロード済みのどのバッファを使用するかを切り替えるという方法が取られます。

なんでそんなことになっていたの?

それはUnity Blogの記事で触れられています。

さらに、Unity は、歴史的に見て非定数のバッファーを考慮して設計されており、DirectX 9 などのグラフィックス API に対応しています。

つまりConstant BufferのないGraphics APIを前提に設計していたため、その上に効率的なConstant Bufferの使用方法を実装することは困難だったということです。Unityがここ数年取り組んでいるScriptable Render Pipelineという仕組みは新しい実装のため、やっとこのことについて修正することができたという話ですね。

本当にそういうことなの?

では実際にUnityがレンダリングする様子を見てみましょう。
Windows環境でDirectX11 APIで描画されているところをRenderDocでキャプチャしたものです。

画像1

見比べてみると完全に異なることが分かります。まずSRP Batcherありの場合ですが、VS, PSSetConstantBufersというのが、どのConstant Bufferを使用するのかというのを設定する関数です。ちなみにSetConstantBuffers1という関数も呼び出されていますが、これは大きなConstant Bufferに対してオフセット指定ができるものです。
次にSRP Batcherなしの方を見るとMap/Unmapという関数が呼び出されています。これはConstant Bufferを書き換えるための関数です。すでにどのConstant Bufferを使用するのかは設定されているのでDraw Call毎に内容を書き換えています。複数のConstant Bufferを使用しているのは各種変換行列などとMaterial Propertyを分けることは行われているからでしょう。そうすれば続けて同じマテリアルが使用される場合は各種変換行列などのConstant Bufferだけの書き換えで済みます。なので同じマテリアルを続けて使用する場合は負荷が少ないため、できるだけマテリアルを共通化してSetPassを減らした方がよいというUnity側の説明があったのもうなずけます。

実際、非SRP Batcher環境はどうだったの?

ここにOculus QuestでUnityを使用した際のパフォーマンス分析が記載されています。"影響の少ない変更"の"ドローコール間でのマテリアルの色の変更"に着目してください。Material Propertyを更新しようが更新しまいが、Draw Call毎にConstant Bufferを更新するため、何も変わらないという結果になっています。じゃあ今後はMaterial Propertyの更新について気を付けた方がいいのかというと、よほど変なことをしなければすべてのマテリアルを毎フレームいじっていたというとこは無いとは思うので今まで通りでよい気がします。ちなみにこのドキュメントはOculus Questに関係なく、特にモバイル向けのゲームを作成する際でも参考になると思います。

最後に

結局のところUnityは歴史のあるエンジン(15年!)なので現在使用されている技術と乖離してしまって非効率な部分があり、それを修正したということです。今時のレンダラーでは当たり前のようにやることですが、歴史がありすぎて大変だったんでしょうね…。

この記事が気に入ったらサポートをしてみませんか?