动态合批与静态合批

参考自:

Unity渲染优化的4种批处理:静态批处理,动态批处理,SRP Batcher 与 GPU Instancing - 知乎 (zhihu.com)

合批/批量渲染 (Batch)、实例化Instancing - 知乎 (zhihu.com)

Unity动态合批(Dynamic Batching)与静态合批(Static Batching) - 简书 (jianshu.com)

什么是DrawCall

渲染流水线的第一步是【CPU和GPU之间的通信】,有如下3个步骤:
1. 把数据加载到显存中:把网格和纹理等数据从硬盘加载到显存中(因为显卡对显存的访问速度更快)
2. 设置渲染状态:CPU根据材质球设置渲染状态,比如,使用哪个顶点着色器/片元着色器、光源属性、纹理等
3. 调用Draw Call:准备好上述工作后,CPU就调用一个渲染命令(Draw Call)来告诉GPU可以开始渲染啦。

GPU的渲染能力是很强的,渲染200个还是2000个三角网格通常没有什么区别,渲染速度往往快于CPU提交命令的速度。如果一帧中Draw Call数量太多,CPU就会在“设置渲染状态-提交Draw Call”上花费大量时间,造成性能问题。

①如果多个DrawCall可以合并为1个DrawCall,就可以通过减少DrawCall来优化渲染性能——比如GPU Instancing,Dynamic Batching
②如果有一组DrawCall使用相同的渲染状态,那么对它们进行批处理,CPU就能在“设置渲染状态”上节省时间——比如SRP Batcher,Static batching——Batch(批)即可理解为“使用相同渲染状态的一组DrawCall”

Unity引擎内建了两种合批渲染技术:Static batching(静态合批)和Dynamic batching(动态合批)。

静态合批

静态合批是物体勾选Static后,Unity在Build的时候,会自动生成合并的网格,并将它以文件形式存储合并后的数据,这样在当场景被加载时,一次性提交整个合并模型的顶点数据,根据引擎的场景管理系统判断各个子模型的可见性。然后设置一次渲染状态,调用多次Draw call分别绘制每一个子模型。需要相同材质而且不能发生变换(旋转平移等等)。

标明为 Static 的静态物件,如果在使用相同材质球的条件下,在Build(项目打包)的时候Unity会自动地提取这些共享材质的静态模型的Vertex buffer和Index buffer。根据其摆放在场景中的位置等最终状态信息,将这些模型的顶点数据变换到世界空间下,存储在新构建的大Vertex buffer和Index buffer中。并且记录每一个子模型的Index buffer数据在构建的大Index buffer中的起始及结束位置。

使用方法:场景中选中需要静态合批的物体,在该物体的Inspector窗口中将右上角的Static勾选。

条件:①使用相同的材质球  ②标明为Static的静态物体

适用范围:像生活中的静态物体一样,建筑,树木等,因为计算合并后的数据在场景内就是固定的不会再变动

优势:

网格通常在预处理阶段(打包)时合并,运行时顶点、索引信息也不会发生变化,所以无需CPU消耗算力维护;

若采用相同的材质,则以一次渲染命令,便可以同时渲染出多个本来相对独立的物体,减少了DrawCall的次数。在渲染前,可以先进行视锥体剔除,减少了顶点着色器对不可见顶点的处理次数,提高了GPU的效率。(官方文档中写着,不会减少DC,但是为了开发人员理解所以编辑器上显示减少了)

弊端:

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

②包体增大

结论:

静态合批采用了以空间换时间的策略来提升渲染效率,静态批处理不一定减少DrawCall,但是会让CPU在“设置渲染状态-提交Draw Call”上更高效,由于我们预先把所有的子模型的顶点变换到了世界空间下,并且这些子模型共享材质,所以在多次Draw call调用之间并没有渲染状态的切换,渲染API会缓存绘制命令,起到了渲染优化的目的。此外,在运行时所有的顶点位置处理不再需要进行计算,节约了计算资源

静态合批在大多数平台上的限制是64k顶点和64k索引。

动态合批 

在运行时Unity自动把每一帧画面里符合条件*的多个模型网格合并为一个,再传递给GPU,

网格的动态批处理旨在优化旧低端设备的性能。在现代消费类硬件上,动态批处理在 CPU 上执行的工作可能大于绘制调用的开销。这会对性能产生负面影响。
*需符合的条件:(2021)
1. 使用相同材质引用的网格实例——只有使用相同材质球的Mesh才可以被批处理。(详见拓展1)
2. 物体之间Transform不能具有镜像关系——比如物体A的scale=+1,物体B的scale=-1,那就不能被批处理。
3. 着色器使用的顶点属性数量不能大于900——比如,漫反射计算需要使用顶点的“位置、法线、UV”这3种属性,所以模型的顶点数不能超过300;如果着色器需要使用顶点的“位置、法线、UV0、UV1和切向量”这5种属性,那就只能批处理180顶点以下的物体。
4. 材质的着色器不能依赖多个过程——多Pass的shader会中断批处理。(详见拓展3)
5. 网格实例应引用相同的光照纹理文件——如果物体使用光照纹理,需要保证它们指向光照纹理中的同一位置,才可以被动态批处理。

使用方法:

  1. 转到 Edit > Project Settings > Player
  2. “Other Settings”中,启用 Dynamic Batching”。

在自定义管线的工程下,会有渲染管线资源,在上面勾选静态合批,引擎就会在可以动态合批的时候自动进行,需要注意的是,在Unity的自定义管线项目里当物体可以同时进行多种合批的时候,合批的优先使用顺序SRPBatcher>静态合批>GpuInstance>动态合批

使用条件:①使用相同的材质球②在视野范围内的物体

适用范围: 移动的,没有进行前几种合批的物体,

动态合批的过程:

既然不能预先计算,那么可以在运行时,在cpu里直接把可以合批物体的顶点等数据变换到世界空间下

②渲染时,提交合并后的顶点数据,设置一次渲染状态,调用一次DrawCall绘制多个模型(不同于静态合批的多次调用DrawCall,因为可以进行动态合批的物体已经判断过可见性)

动态合批是利用CPU的计算性能消耗来换取渲染状态设置,几何处理和绘制调用的消耗

缺点:消耗CPU的计算性能,当计算消耗大于设置渲染状态等消耗时得不偿失。

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

  1. 动态合批不会创建常驻内存的“合并后网格”,也就是说它不会在运行时造成内存的显著增长,也不会影响打包时的包体大小;
  2. 动态合批在绘制前会先将顶点转换到世界坐标系下,然后再填充进顶点、索引缓冲区;静态合批后子网格不接受任何变换操作,仅手动合批后的Root节点可被操作,因此静态合批的顶点、索引缓冲区中的信息不会被修改(Root的变换信息则会通过Constant Buffer传入);
  3. 因为2的原因,动态合批的主要开销在于遍历顶点进行空间变换时的对CPU性能的开销;静态合批没有这个操作,所以也没有这个开销;
  4. 动态合批使用根据渲染器类型分配的公共缓冲区,而静态合批使用自己专用的缓冲区。
  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值