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
によってバッチされていないことが分かります
SRP Batcher
に対応させる
3. シェーダーをシェーダーの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