SRP Batcherについてまとめる
SRP Batcherとは
・DrawCall毎にCBuffer(Constant Buffer)を更新せずに更新が必要な値のみアップロードを行うことでアップロード済みのバッファを使用することができるという仕組み
・同じシェーダを使用している場合に別マテリアルに分けても同一SetPassで描画することができ、SetPassCallを減らすことが可能になる
※GPUへのアップロード頻度を減らせるだけであり、DrawCall自体を減らせるわけでない
似たような機能でGPUIntancingという同一マテリアルのメッシュをまとめて1回のDrawCallで描画できDrawCallを減らせる仕組みがあるが、SRP BatcherはGPUInstancingと違いメッシュの結合を行わないためDrawCallは減らすことはできないがSetPassCallの頻度を減らすことをができる機能であるという違いがある
SRP Batcher使用時の注意点
・シェーダーをSRPBatcherに対応させる必要がある
・MaterialPropertyBlocksを一緒に使用することはできない
・シェーダーバリアントをできるだけ少なくする必要がある
・GPUInstancingと併用することはできない
・UnityCG.cgincをインクルードするとUnityPerDrawのCBUFFERに含まれるエンジン定数が5つのみになってしまい、SRP Batcherに対応させるために必要なエンジン定数が別のCBUFFERに含まれてしまうため扱いに注意する
SRP Batcherを使ってみる
環境
・Unity2022.3.13f
・URP14.0.9
1.シェーダーの用意
シンプルにテクスチャを貼り、色を加算合成するシェーダーを作成
Unlit_AddColor
Shader "Custom/UnlitAddColor"
{
Properties
{
[MainTexture] _MainTex ("Texture", 2D) = "white" {}
_AddColor ("Color", Color) = (0, 0, 0, 0)
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="Opaque"
}
Pass
{
Name "Universal Forward"
Tags
{
"LightMode" = "UniversalForward"
}
Cull Back
ZTest LEqual
ZWrite On
HLSLPROGRAM
#pragma target 4.5
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float4 pos: POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _AddColor;
v2f vert(appdata i)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(i);
UNITY_TRANSFER_INSTANCE_ID(i, o);
VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
o.pos = vInput.positionCS;
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
return o;
}
half4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
half4 baseColor = tex2D(_MainTex, i.uv);
baseColor.rgb += _AddColor.rgb;
return baseColor;
}
ENDHLSL
}
}
}
**
作成したシェーダーを見てみると
SRP Batcherの項目にnot compatibleと表示されておりSRP Batcherが有効になっていないことが分かります

2.オブジェクトを配置・Materialを設定
オブジェクトを配置し、それぞれに1.で作成したシェーダーのマテリアルを設定する
・Cubeは青色
・Sphereは赤色
に加算合成させるようにしました

FrameDebuggerを見てみる
FrameDebuggerで見てみるとSRP Batcherによってバッチされていないことが分かります

3. シェーダーをSRP Batcherに対応させる
シェーダーのinspectorを見ると
Material property is found in another cbuffer than 'UnityPerMaterial' (_AddColor)
と表示されているので_AddColorのプロパティーをUnityPerMaterialというCBUFFERに入れればよいという事が分かります
Unlit_AddColor
Shader "Custom/UnlitAddColor"
{
Properties
{
[MainTexture] _MainTex ("Texture", 2D) = "white" {}
_AddColor ("Color", Color) = (0, 0, 0, 0)
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="Opaque"
}
Pass
{
Name "Universal Forward"
Tags
{
"LightMode" = "UniversalForward"
}
Cull Back
ZTest LEqual
ZWrite On
HLSLPROGRAM
#pragma target 4.5
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float4 pos: POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
CBUFFER_START(UnityPerMaterial)
half4 _AddColor;
CBUFFER_END
v2f vert(appdata i)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(i);
UNITY_TRANSFER_INSTANCE_ID(i, o);
VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
o.pos = vInput.positionCS;
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
return o;
}
half4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
half4 baseColor = tex2D(_MainTex, i.uv);
baseColor.rgb += _AddColor.rgb;
return baseColor;
}
ENDHLSL
}
}
}
sampler2D _MainTex;
float4 _MainTex_ST;
CBUFFER_START(UnityPerMaterial)
half4 _AddColor;
CBUFFER_END
UnityPerMaterialのCBUFFERに入れるために_AddColorをCBUFFER_START(UnityPerMaterial)とCBUFFER_ENDで囲むだけです
_AddColorをUnityPerMaterialに入れると次は
Material property is found in another cbuffer than 'UnityPerMaterial'(_MainTex_ST)
と表示されたのでプロパティ全てをUnityPerMaterailのCBUFFERに含める必要があるそうです

Unlit_AddColor
Shader "Custom/UnlitAddColor"
{
Properties
{
[MainTexture] _MainTex ("Texture", 2D) = "white" {}
_AddColor ("Color", Color) = (0, 0, 0, 0)
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="Opaque"
}
Pass
{
Name "Universal Forward"
Tags
{
"LightMode" = "UniversalForward"
}
Cull Back
ZTest LEqual
ZWrite On
HLSLPROGRAM
#pragma target 4.5
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float4 pos: POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
half4 _AddColor;
CBUFFER_END
v2f vert(appdata i)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(i);
UNITY_TRANSFER_INSTANCE_ID(i, o);
VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
o.pos = vInput.positionCS;
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
return o;
}
half4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
half4 baseColor = tex2D(_MainTex, i.uv);
baseColor.rgb += _AddColor.rgb;
return baseColor;
}
ENDHLSL
}
}
}
sampler2D _MainTex;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
half4 _AddColor;
CBUFFER_END
_MainTex_STもUnityPerMaterialに含めるとcompatibleとなってSRP Batcherに対応できたことが分かります

4. FrameDebuggerを見てみる
SRP BatchがSRP Batcherによってバッチされた描画になります

Detailsの項目を見ると、
Meshsの項目に「Sphere」と「Cube」の項目がありSRP Batcherによってバッチされたことが分かります
DrawCallsは"2"となっているためドローコールは減ってはいません

5. Planeを追加してみる
Planeを追加してみてどうなるか見てみます。
(デフォルトで適用されるシェーダーはUniversal Render Pipeline/Litのようですね)

ここで、FrameDebuggerを見てみるとPlane分のSRP Batchが追加されていますね

Planeには Cube,Sphereとは別のシェーダーが割り当てられているためバッチされていないことが分かります
Discussion