Unity合批规则
Unity合批规则
参考资料
Unity基础知识回顾 DrawCall、Batch、SetPassCall
基础概念
DrawCall
CPU每次调用图像编程接口 glDrawElements(OpenGl中的图元渲染函数)或者 DrawIndexedPrimitive(DirectX中的顶点绘制方法)命令GPU渲染的操作称为一次Draw Call。Draw Call就是一次渲染命令的调用,它指向一个需要被渲染的图元(primitive)列表,不包含任何材质信息,glDrawElements 或者 DrawIndexedPrimitive 函数的作用是将CPU准备好的顶点数据渲染出来。
Batch
把数据加载到显存,设置渲染状态,CPU调用GPU渲染的过程称之为一个Batch。这其实就是渲染流程的运用阶段,最终输出一个渲染图元(点、线、面等),再传递给GPU进行几何阶段和光栅化阶段的渲染显示。一个Batch必然会触发一次或多次DrawCall,且包含了该对象的所有的网格和顶点数据以及材质信息。把数据加载到显存是指把渲染所需的数据从硬盘加载到内存(RAM),再将网格和纹理等加载到显卡(VRAM),这一步比较耗时。设置渲染状态就是设置场景中的网格的顶点(Vertex)/片元(Fragment)着色器,光源属性,材质等。Unity提供的动态合批(Dynamic Batching )合并的就是这一过程,将渲染状态相同的对象合并成一个Batch,减少DrawCall。
SetPassCall
Shader脚本中一个Pass语义块就是一个完整的渲染流程,一个着色器可以包含多个Pass语义块,每当GPU运行一个Pass之前,就会产生一个SetPassCall,所以可以理解为一次完整的渲染流程次数。
静态合批
勾选static,Unity在build的时候,会自动生成合并的网格,并将它以文件形式存储合并后的数据,当场景被加载时,一次性提交整个合并模型的顶点数据,根据引擎的场景管理系统判断各个子模型的可见性,然后设置一次渲染状态,调用多次drawCall分别绘制每个子模型
Tips
- 渲染状态:包括所使用的着色器,光源,材质等
前提
- 共享相同的材质
- 运行时不能移动,旋转或缩放
优点
- 本质上是空间换时间的策略
- 静态合批并不减少DrawCall的数量,但是由于我们预先把所有的子模型的顶点变换到了世界空间下,并且这些子模型共享材质,所以在多次Draw call调用之间并没有渲染状态的切换,渲染API会缓存绘制命令,起到了渲染优化的目的。另外,在运行时所有的顶点位置处理不再需要进行计算,节约了计算资源。
缺点
- 注意如果多个GameObject在静态批处理之前共享相同的几何体,则会在编辑器或运行时为每个GameObject创建几何体的副本,这会增大内存的开销。例如,在密集的森林级别将树标记为静态可能会产生严重的内存影响
动态合批
在进行场景绘制之前将所有的共享同一材质的模型的顶带你信息变换到世界空间中,然后通过一次Draw call绘制多个模型,达到合批的目的,模型顶点变换的操作是由CPU完成的,所以回带来一些CPU的性能消耗
合批规则
- 获得一个按Hierarchy的顺序的列表
- 计算每个物体的深度。
- 深度从0开始递增。如果世界包围盒Z轴不为0(或isCanvasInjectionIndex),则需独占一个批次,同时独占一个深度。即等于之前所有物体最大深度+1,后一个物体深度需要+1。
- 对一般物体的深度。会判断是否可跟之前的物件共享深度,走接下来的流程。
- 先按格子(默认大小是120,根据包围盒再计算)划分出多个格子。(只是为了加速求交)
- 计算物体包围相交哪些格子,再跟格子中已有的物体进行包围盒相交判断。如果不相交则使用当前深度;如果相交且可合批,则使用相交物体中最大的深度;如果相交且不可合批,则使用相交物体中最大的深度+1。合批条件:无独占批次+材质相同+贴图相同+裁剪开关和裁剪矩形相同+贴图A8格式一致(kTexFormatAlpha8)
- 将该物体加入所有相交的格子中。若遇到独占深度的物体,则格子数据清空。即后续物件不跟之前的物件共享深度。
- 排序:按照深度->材质->贴图->层级顺序优先级升序排序。
- 合批:对排序后的列表,从头开始一个一个检测是否能与前面的物体合批。合批条件:无独占批次(只判断isCanvasInjectionIndex)+材质相同+贴图相同+裁剪开关和裁剪矩形相同(都有mask或都没有)+贴图A8格式一致(kTexFormatAlpha8)+ posz为0或处于同hierarchy,非SubBatch只判断前两个条件,一般情况下UI的材质都一样。
深度值计算规则
1 | 深度值的计算规则 |
例子
通过一个例子来实际去计算和分析这种计算过程,我们抛出两种情况,注意这里修改了材质或贴图保证了white_image和red_image不能合批
图1
图2
图1和图2在Hierarchy中的顺序如下
图1和图2的区别在于红点image是否与另外一个image进行交叉,我们先来计算图1的情况
计算UI的层级队列,结果如下
计算UI深度
TIPS:这里用(1)和(2)区分两个image层级由0开始计算,遍历该层的所有UI,收集层级比其小的UI,即底层物体。
图1 and 图2:
white_image(1): 无
red_image(1): white_image(1)
white_image(2):white_image(1)、red_image(1)
red_image(2):white_image(1)、red_image(1)、white_image(2)
分三种情况
- 无相交底层物体,深度为0
- 有1个下层物体且相交时,可batch时(即材质相同+贴图相同),深度为下层物体深度;不可batch时,深度为下层物体深度+1
- 有n个下层物体且相交时,根据(2.)规则计算所有depth_i,取max(depth_1, depth_2, … depth_x)作为层级
图1:
white_image(1)无底层物体,深度为0(情况1)
red_image(1)与white_image(1)相交,且不可batch,则red_image(1)的深度为1(情况3)
white_image(2)无底层物体,深度为0(情况1)
red_image(2)与white_image(2)相交,且不可batch,则red_image(2)的深度为1(情况2)
图2:
white_image(1)无底层物体,深度为0(情况1)
red_image(1)与white_image(1)相交,且不可batch,则red_image(1)的深度为2(情况2)
white_image(2)与white_image(1)、red_image(1)相交,则red_image(1)的深度为3(情况3)
red_image(2)与white_image(1)、red_image(1)、white_image(2)相交,则red_image(2)的深度为4(情况4)
这时我们按照深度->材质->贴图->层级顺序优先级排序
图1排序结果为:white_image(1),white_image(2),red_image(1),red_image(2)
图2排序结果为:white_image(1),red_image(1),white_image(2),red_image(2)
进行合批操作
图1:white_image(1)与white_image(2)合批,red_image(1)与red_image(2)合批,batch为2
图2:前后全都无法合批,batch为4