Unity - DrawCall优化相关 Batch 动态合批 静态合批

什么是DrawCall

CPU呼叫GPU进行绘制是一次DrawCall

以 OpenGL 为例,就是调用带有绘制功能的 API 的次数

如:DrawCall : 10 次,那就意味着调用了 glDrawXXXX 的 API 10 次


 

啥叫:Batch

Batch 直译:批量,的意思
在 实时渲染 中,已动态合批为例(Dynamic Batch)一般理解为:为了减少 DrawCall,或是减少 SetPassCall 而将绘制时材质一样(或是说 shader + shader 参数 + 绘制前状态,都一样)的 VBO,IBO,等数据打包到一个大的 VBO,或是 IBO 中,然后在调用一次 DrawCall,从而提升性能:SetPass 的 State 时,或是多次 Draw API 调用产生过多的 CPU 消耗的性能的问题
(另外还有:静态合批(Static Batch)、Instanced 批量(GPU Instancing Batch)绘制,都算是 Batch 的方式)
所以 Batch 的目的是:将原本需要 多次 SetDrawState + 多次 DrawCall,优化为:1次 SetDrawState + 1次 DrawCall
可以简单的理解为:批量渲染是通过减少CPU向GPU发送渲染命令(DrawCall)的次数,以及减少GPU切换渲染状态的次数,尽量让GPU一次多做一些事情,来提升逻辑线和渲染线的整体效率。但这是建立在GPU相对空闲,而CPU把更多的时间都耗费在渲染命令的提交上时,才有意义。

 

合批最重要的前提:材质必须相同!!!

Batch ≠ DrawCall

 


 

优化方式

相同材质的物体一同绘制

那么如何相同材质的物体一同绘制

就是把相同材质的物体的网格合并一同绘制

 

Why?

  • 底层的图形api有一个绘制三角形的接口,这个就是CPU调用gpu绘制的接口
  • 在调用这个接口前会准备顶点数据,即网格数据
  • 把网格合并后只要进行一次绘制三角形的接口调用,减少了DrawCa

 


 

为啥要优化DrawCall

上面已经说了,Cpu叫GPU执行一次绘制渲染就是DrawCall。

传输数据消耗比较大,GPU的计算能力也相对较强,就可能存在CPU传递的指令只让渲染一小部分,GPU执行完毕以后,但是下一条的指令还没有过来的时候,

这就好比这条路车多了,堵车了,那么如何梳理交通,让一次通过的红绿灯让更多的车子过去,提供更高效的通行效率和时间呢?关键就是在于,这次红绿灯,我让多少车过去了。

所以,提高DrawCall的效率非常的重要,就是保证尽量的让每次的DrawCall能够渲染更多的内容(让更多的车子过去)。这样,即便是红绿灯通行次数少了(DrawCall的提交次数),但是

通行效率提高了,一次性过去的车子多了,那么也能有效的减少拥堵的情况。

然后如何去提升DrawCall的效率呢,主要的方法就是合批(Batch),,也就是上面解释的名词。


Unity中的合批

静态合批 Static Batching

  • Unity中把物体标记为Static,然后开启静态合批
  • 限制
    • 需要保持static,不能改变transform
    • 使用相同材质的物体才能合批
    • 一个批次上限为~15k个顶点
       

动态合批 Dynamic Batching

Unity自带动态合批,需要在Unity中开启动态合批选项
会导致cpu消耗,如果不是gpu有瓶颈,最好关闭动态合批
前提
   使用顶点位置、法线、UV0、UV1和切线为一个着色器提供180个顶点
   使用顶点位置、法线和单一UV的着色器的300个顶点

 

动态合批的限制条件呢?

●材质球相同;

●Mesh顶点数量不能超过300以及顶点属性不能超过900;

●缩放不能为负值(x、y、z向量的乘积不能为负)等。

 

 


 

静态合批的利弊

静态合批采用了以空间换时间的策略来提升渲染效率。

 

其优势在于:网格通常在预处理阶段(打包)时合并,运行时顶点、索引信息也不会发生变化,所以无需CPU消耗算力维护;若采用相同的材质,则以一次渲染命令,便可以同时渲染出多个本来相对独立的物体,减少了DrawCall的次数。
在渲染前,可以先进行视锥体剔除,减少了顶点着色器对不可见顶点的处理次数,提高了GPU的效率。

其弊端在于:合批后的网格会常驻内存,在有些场景下可能并不适用。比如森林中的每一棵树的网格都相同,如果对它采用静态合批策略,合批后的网格基本等同于:单颗树网格 x 树的数量,这对内存的消耗可能就十分巨大了。

总而言之,静态合批在解决场景中材质基本相同、网格不同、且自始至终都保持静止的物体上时,很适用。

 


动态合批与静态合批的区别

1、动态合批不会创建常驻内存的“合并后网格”,也就是说它不会在运行时造成内存的显著增长,也不会影响打包时的包体大小;

2、动态合批在绘制前会先将顶点转换到世界坐标系下,然后再填充进顶点、索引缓冲区;静态合批后子网格不接受任何变换操作,仅手动合批后的Root节点可被操作,因此静态合批的顶点、索引缓冲区中的信息不会被修改(Root的变换信息则会通过Constant Buffer传入);

3、因为2的原因,动态合批的主要开销在于遍历顶点进行空间变换时的对CPU性能的开销;静态合批没有这个操作,所以也没有这个开销;

4、动态合批使用根据渲染器类型分配的公共缓冲区,而静态合批使用自己专用的缓冲区。

 


 

开启方法

Player Setting > Other Settings

 

posted @ 2021-05-20 10:16  黑羽青衣  阅读(1836)  评论(0编辑  收藏  举报