ユニバーサルレンダーパイプライン(URP)を有効にすると、自動的にSRP Batcherと呼ばれる強力な描画最適化機能が利用可能になります。これはドローコール(Draw Call)に起因するCPUオーバーヘッドを極限まで下げるコア技術ですが、設定ミスやシェーダーの記述方法一つで機能しなくなり(Incompatible判定)、パフォーマンスが急激に悪化します。本記事では、このSRP Batcherの不適合バグが発生する内部メカニズムを『料理人の調理プロセス』の例え話を交えて分かりやすく解説し、適合率を100%にするための正しい解決アプローチを提示します。
1. 例え話で理解する:『料理人の調理台整理』の無駄
SRP Batcherの動作原理と、それが不適合になったときの無駄を、『厨房でたくさんのハンバーグを連続で作る調理プロセス』を例えに考えてみましょう。
料理人(CPU)が、カウンター(GPU)に向けてハンバーグを大量に出す必要があります。しかし、注文が入る(オブジェクトを描画する)たびに、料理人は『調理台の上にあるフライパンや包丁(シェーダーや描画ステート)』を一度すべて片付け、シンクをピカピカに掃除し直してから、次のハンバーグを作るためにまた同じ道具を引っ張り出してセットする、という作業を繰り返しています。この「道具の出し入れと掃除」にかかる無駄な時間が、SRP Batcher不適合(Incompatible)における『CPUのステートバインドオーバーヘッド』です。料理人は料理そのものを作る時間より、片付けと準備で疲れ果ててしまいます。
これに対し、SRP Batcherが適合している状態は、フライパンや包丁などの重い道具(シェーダーコード)は調理台にしっかりと固定したままにします。そして、流れてくる注文に応じて、トッピングのソースや具材(マテリアルのパラメータ)だけをサッと素早く入れ替えて焼き続けます。調理器具を一切片付けないため、料理人(CPU)は息をつく間もなく超高速で連続して料理(ドローコール)をカウンター(GPU)に送り出すことができるのです。
SRP Batcher不適合バグとは、シェーダープロパティの定義の仕方が悪いために、システムが『このマテリアルは道具を片付けずにパラメータだけを入れ替える高速調理法が使えない(Incompatible)』と判断し、強制的に毎回調理台をピカピカに片付けさせる非効率な方法(ドローコールのたびに重いステートリセットを行う)を実行してしまうバグです。
2. 解決策A:自作HLSLシェーダーへのCBUFFERの正しい宣言
Shader Graphを使わず、手書きのHLSLコードでシェーダーを自作している場合、プロパティ変数の宣言場所が間違っていることが最も一般的な不適合原因です。すべてのマテリアルプロパティは、必ず `UnityPerMaterial` という名前の定数バッファブロック内に一括して並べなければなりません。
修正前のコード(不適合):
// CBUFFERブロックの外側に変数を単体でバラバラに宣言しているため不適合になる
float4 _BaseColor;
float _Glossiness;
float _Metalness;
修正後のコード(適合):
// ★超重要:UnityPerMaterial という名前のCBUFFERブロックで包む
CBUFFER_START(UnityPerMaterial)
float4 _BaseColor;
float _Glossiness;
float _Metalness;
CBUFFER_END
※テクスチャオブジェクト(`Texture2D`)やサンプラーステート(`SamplerState`)は、もともと定数バッファ内に格納できないリソースレジスタであるため、このCBUFFERブロックの外側(グローバルスコープ)に記述しても問題ありません。
3. 解決策B:C#スクリプトにおけるMaterialPropertyBlock使用の完全廃止
プログラマーがよく陥る罠として、C#から動的にゲームオブジェクトの色やマテリアルパラメータを個別に変更するために `Renderer.SetPropertyBlock(MaterialPropertyBlock)` を呼び出す手法があります。従来のBuilt-inレンダーパイプラインではこれがドローコール削減の推奨テクニックでしたが、URPのSRP Batcher環境においては、MaterialPropertyBlockを適用したオブジェクトは強制的にSRP Batcher不適合になります。
現在推奨される解決アプローチは、以下の通りです。
- 個別のマテリアルインスタンスの使用: スクリプトから直接 `renderer.material.SetColor` などを使用してマテリアルを複製・更新します。SRP Batcherはマテリアルインスタンスが別々であっても、シェーダーが同一で `UnityPerMaterial` CBUFFERが維持されていれば、ドローコールを自動で一括バッチングしてくれます。
- 頂点カラー(Vertex Color)の活用: シェーダー側で頂点カラーを入力として受けるように設計し、C#側からはメッシュ自体の頂点データを更新することで、マテリアルパラメータを一切書き換えることなく個別の色表現を行います。
これにより、C#スクリプトの動的変更を適用した状態であっても、SRP Batcherの超高速なバッチングの恩恵を100%受けることができます。