UnityのSRP Batcherについてまとめました。
Unity2021.1.11f1
SRP Batcherとは?
SRP Batcherはシェーダ(正確にはシェーダキーワード)が同じもの多数描画する際に効率的に描画を行うことができる機能です。
メッシュやマテリアルが違っていても、シェーダキーワードが同一で描画順が連続していれば動作します。
GPUにマテリアルの情報をキャッシュすることで実現される仕組みになっていて、
Dynamic Bachingとは異なりドローコールは描画する数だけ発行されますが、セットパスコールがまとめられるという挙動になります。
SRP Batcherの動作要件は以下の通りです。
- Scriptable Render Pipelineでのみ使える(ビルトインパイプラインでは使えない)
- メッシュかスキンメッシュのみで、パーティクルには非対応
- シェーダが対応している必要がある
- ほとんどすべてのプラットフォームで動作する
Dynamic Baching・GPUインスタンシングとの違い
SRP Batcherと目的が似ている機能として、Dynamic BatchingとGPUインスタンシングが存在します。
Dynamic Batching
まずDynamic Batchingは同じマテリアルを適用する複数のメッシュをリアルタイムで結合する処理です。
これによりドローコールやセットパスコールが1回で済み、この部分については処理負荷の軽減につながります。
しかしながらメッシュの結合処理自体に安くはないCPU負荷がかかるというデメリットがあります。
また適用条件が厳しい(頂点属性900以下かつ頂点数300以下のメッシュ)ため、使えるケースが限られます。
現状のUnityでは、組み込みパイプラインとSRPのどちらもデフォルトでDynamic Batchingはオフになっています。
GPUインスタンシング
GPUインスタンシングもDynamic Batchingと同じく、マテリアルが一緒のものを一気に描画できる機能です。
ただしDynamic Batchingのようなメッシュの結合は不要で、GPUで効率的な処理を行います。
また色違いなど、プロパティが異なるケースにも対応可能です。
ただしシェーダはGPUインスタンシングに対応している必要があります。
またメッシュは同一のものである必要があります。
SRP Batcherの使い方
SRP Batcherを使うにはまずレンダリングパイプラインをSRPを使ったものにする必要があります。
これに関しては以下のUniversal RPについての記事を参考にしてください。
次にScriptable Render Pipeline Asset(Universal Render Pipeline Asset)のSRP BatcherのチェックボックスがONになっていることを確認します。
デフォルトではこの項目はONになっています。
次にUniversal Render Pipeline/Lit
シェーダを使ったマテリアルを複数用意します。
そして異なるメッシュを持つレンダラにそれぞれをアサインします。
この状態で再生し、FrameDebuggerでレンダリング順を確認します。
3つのドローコールが一つのセットパスコールにまとめられていることが確認できました。
シェーダの書き方
さて次にSBP Batcherに対応したシェーダを書いてみます。
まずシンプルなSRPのUnlitシェーダから始めます。
Shader "Example" { SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalRenderPipeline" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; }; struct Varyings { float4 positionHCS : SV_POSITION; }; Varyings vert(Attributes IN) { Varyings OUT; OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz); return OUT; } half4 frag() : SV_Target { return half4(1, 0, 0, 1); } ENDHLSL } } }
上記のようにプロパティの存在しないシェーダはこの状態で普通にSRP Batchingされます。
このシェーダにプロパティを追加する際には、バッチングのために以下のコメント部分のようにCBUFFERの記述が必要になります。
Shader "Example" { Properties { _BaseColor("Base Color", Color) = (1, 1, 1, 1) } SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalRenderPipeline" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; }; struct Varyings { float4 positionHCS : SV_POSITION; }; // SBP Batcherに対応するため、マテリアルに紐づくプロパティは全てこのブロック内に記述する必要がある CBUFFER_START(UnityPerMaterial) half4 _BaseColor; CBUFFER_END Varyings vert(Attributes IN) { Varyings OUT; OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz); return OUT; } half4 frag() : SV_Target { // BaseColorを返す return _BaseColor; } ENDHLSL } } }
このようにSRP Batcherに対応するには、マテリアルに紐づくプロパティを全てUnityPerMaterialという名前のCBUFFERで宣言する必要があります。
また、組み込みのプロパティをUnityPerDrawという名前のCBUFFERで宣言する必要もありますが、
これは基本的にIncludeされているファイル内で行われているので今回は対応する必要はありません。
CGINCLUDEを使っている場合には以下のように注意が必要そうですが、HLSLPROGRAMを使う分には問題ないかと思われます。