Unity开发——CPU优化篇
前言
本篇是关于CPU的优化,参考地址为UWA的CPU优化,但是UWA有些总结的比较精简,我会对一些展开解释和补充,下面就是按照示意图进行说明,有些篇幅大的会再开新的博客来说明。
好了,不多说,上干货。
示意图
一、渲染模块
1.DrawCall
1.1什么是DrawCall
Draw Call就是CPU调用图形编程接口,比如DirectX或OpenGL,来命令GPU进行渲染的操作。
1.2为什么DC会有损耗
在讲损耗之前,需要明白什么是Batch和合批处理。
Batch:图形渲染及优化—Batch
合批技术:图形渲染及优化—Unity合批技术实践
从上面两个博客总结来说:
1.每一次Batch的提交就是一次Draw call调用。
2.损耗主要在:
命令从Runtime到Driver的过程中,CPU要发生从用户模式到内核模式的切换。模式切换对于CPU来说是一件非常耗时的工作,所以如果所有的API调用Runtime都直接发送渲染命令给Driver,那就会导致每次API调用都发生CPU模式切换,这个性能消耗是非常大的。
3.那解决方法就是:
Runtime中的Command Buffer可以将一些没有必要马上发送给Driver的命令缓冲起来,在适当的时机一起发送给Driver,进而在Video Card执行。以这样的方式来寻求最少的CPU模式切换,提升效率。
1.3如何优化呢
针对上述方案:将命令缓存,在适当时间,统一发送给Driver。
大家肯定会想到的是Dynamic Batching动态批处理和Static batching静态批处理,上面的合批技术也介绍了,我也不展开叙述。只说些注意点:
Static batching:
Static batching并不减少Draw call的数量,是预先把所有的子模型的顶点变换到了世界空间下,并且这些子模型共享材质,所以在多次Draw call调用之间并没有渲染状态的切换,渲染API会缓存绘制命令,起到了渲染优化的目的。
需要静态批处理的模型会合并到一个新的网格结构中,这意味着模型不能移动,但由于它只需要一次合并操作,所以比动态批处理更高效。
Unity入门精要也有写关于静态批处理的缺陷:
- 不能移动,时刻保持静止。
- Static batching会导致应用打包之后体积增大,应用运行时所占用的内存体积也会增大。
- 需要更多的内存,因为需要储存合并后的几何结构。如果每个物体都共享了相同的网格,那么每一个物体都会对应一个该网格的复制品,即一个网格就会变成多个网格再发送给GPU。举个例子:1000棵树木使用了相同树的模型,再使用了静态批处理,就会多1000倍的内存,这就会成为了一个性能瓶颈。解决办法就是要么忍受牺牲内存换性能,要么不使用静态批处理,转而使用动态批处理。
Dynamic batching:
Dynamic batching在进行场景绘制之前将所有的共享同一材质的模型的顶点信息变换到世界空间中,然后通过一次Draw call绘制多个模型,达到合批的目的。模型顶点变换的操作是由CPU完成的,所以这会带来一些CPU的性能消耗。
经过动态批处理的物体仍然是可以移动的,这是因为在处理每帧时,Unity都会重新合并一次网格。
动态批处理的好处多,但是限制也多:
- 能进行Dynamic batching的模型最高能有900个顶点属性。这里注意不是900个顶点,而是900个定点属性。如果我们在Shader中使用了Vertex Position,Normal and single UV,那么能够进行Dynamic batching的模型最多只能够有300个顶点。如果我们在Shader中使用了Vertex Position、Normal、UV0、UV1 and Tangent那么顶点的数量就减少到180个。(未来可能因为硬件升级,取消这些限制,那就是非常完美了!!!)
- 很多地方都会写模型缩放会影响动态批处理,但是在unity5之后,这个限制已经没有了。
- 多Pass的shader会中断批处理。因为Multi-pass Shader通常会导致一个物体要连续绘制多次,并切换渲染状态。这会打破其跟其他物体进行Dynamic batching的机会。如果一个GameObject接受一个以上的per-pixel light那么就会为每一个per-pixel light产生多余的模型提交和绘制。我们可以在Quality Settings中将的per-pixel light数量限制为1.这样设置之后Unity通过各种条件判断只会选择出一个Light执行per-pixel lighting。GameObject的绘制在一个Pass内就可以完成,避免了附加的Pass,提高了可合批的概率。
- 在Unity的渲染管线中无论采用Forward Rendering或者是Deferred Rendering,半透明物体的渲染都要在最后采用Forward Rendering的方式渲染(Deferred Rendering无法支持半透明渲染)。所有的半透明物体在绘制之前要进行严格的排序,绘制操作按序执行。因为物体绘制的顺序被严格限定,所以物体间能够进行动态合批的概率很小。
- 进行合批的前提是多个GameObject共享同一材质,但是对于Shadow casters的渲染是个例外。仅管Shadow casters使用不同的材质,但是只要它们的材质中给Shadow Caster Pass使用的参数是相同的,他们也能够进行Dynamic batching。
总结:Dynamic batching在降低Draw call的同时会导致额外的CPU性能消耗,所以仅仅在合批操作的性能消耗小于不合批,Dynamic batching才会有意义。而新一代图形API( Metal、Vulkan)在批次间的消耗降低了很多,所以在这种情况下使用Dynamic batching很可能不能获得性能提升。Dynamic batching相对于Static batching不需要预先复制模型顶点,所以在内存占用和发布的程序体积方面要优于Static batching。但是Dynamic batching会带来一些运行时CPU性能消耗,Static batching在这一点要比Dynamic batching更加高效。所以我们在实践中可以根据具体的场景灵活地平衡两种合批技术的使用。