概述
做Unity性能优化,减少Draw Calls是最头疼的事。
既然用到了URP管线,那么在做渲染优化的时候,就需要针对URP管线做相应的优化。
今天发现了除了动态batching和静态batching以外的一个非常酷炫的batching,那就是SRP Batching。
因为动态batching限制较多,包括模型顶点不能超过300个,shader的顶点属性不能超过900个(假如你的vertex shader用到了vertex position,normal,一个uv,那300个顶点*3 = 900,这样计算的),并且材质和贴图得必须一致,还不能用材质的copy等等。
其中模型顶点数量限制这个就很无解了,我们项目的模型顶点动辄就超过500到一千,那么做动态batching就是天方夜谭。
但是draw calls数量太多了,还是得想办法呀。皇天不负有心人,终于让我发现了SRP Batching。
这是我没有做SRP Batching之前,在Frame Debugger的状况:
我的天,单单是渲染阴影就用了100个draw calls。
甚至还提示无法batching的原因是:
"Dynamic Batching is turned off in Player Settings or is disabled temporarily in the current context to avoid z-fighting".
一头雾水的报错,因为pipelineAsset里确实勾选了Dynamic Batching。不过放心,下面的SRP Batching会解决这个问题。
渲染Opaque(不透明物体)13个。
渲染Transparents(半透明物体)93个!总共100+13+93 = 206个draw calls!
SetPass calls到达196。
具体实现
首先在Pipeline Asset里勾选SRP Batcher。
接着,得让我们的shader是SRP compatible(符合SRP的)的。
前提是我们修改的shader是支持URP管线的,具体怎么让shader支持URP管线可以参阅我之前的博客。
非常重要的步骤就是加入CBUFFER,例如:
CBUFFER_START(UnityPerMaterial)
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
CBUFFER_END
UnityPerMaterial的意思是在 Properties大括号里声明的所有变量,如下方的_Color和_MainTex变量:
Properties
{
_Color("与主贴图正片叠底的颜色", Color) = (1,1,1,1)
_MainTex("主贴图", 2D) = "white" {}
}
所以注意,不要加入Properties以外的属性变量。否则会报
"unitypermaterial var is not declared in shader property section"的错。
一般加入CBUFFER,并生效的话,SRP就compatible了。
然后我们来打开Frame Debugger来看看Batching的情况:
我的天,Transparents(半透明物体)的draw calls从93个跌到了11个!简直是魔法!
渲染Opaque(不透明物体)的从13跌倒了2个!
ShaderCaster阴影的SRP Batching
如果你的shader有用到ShaderCaster来实现阴影的话。以下的代码可以直接用来使阴影的SRP Batching生效。
pass {
Name "ShadowCast"
Tags{ "LightMode" = "ShadowCaster" }
HLSLPROGRAM
#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
CBUFFER_START(UnityPerMaterial)
CBUFFER_END
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
};
Varyings ShadowPassVertex(Attributes input)
{
Varyings output;
float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _MainLightPosition.xyz));
output.positionCS = positionCS;
return output;
}
half4 ShadowPassFragment(Varyings input) : SV_TARGET
{
return 0;
}
ENDHLSL
}
同样打开打开Frame Debugger来看看Batching的情况:
我的天,draw calls从100个跌到了10个!酷毙了!
最终,SetPass calls从196优化到109个。
参考文章:
https://zhuanlan.zhihu.com/p/137455866
https://zhuanlan.zhihu.com/p/156858564
https://blogs.unity3d.com/2019/02/28/srp-batcher-speed-up-your-rendering/