Unity优化之Drawcall
一、什么是Drawcalls
在Unity中,每次CPU准备数据并通知GPU的过程就称之为一个DrawCall。这个过程会指定一个Mesh被渲染,绘制材质。
二、Drawcalls有什么影响
为了CPU和GPU可以进行并行工作,需要一个命令缓冲区,由CPU向其中添加命令,然后由GPU从中读取命令,这样就实现了通过CPU准备数据,通知GPU进行渲染。在每次调用DrawCall之前,CPU需要向GPU发送很多内容,主要是包括数据,渲染状态,命令等。所以如果DrawCall数量过多就会导致CPU进行大量计算,进而导致CPU的过载,影响程序运行效率。
三、查看Drawcalls
在unity中查看drawcalls有2个方法,如图1所示查看Game窗口中的State下的Batces,这个数量和drawcalls数量相同。第二种方法是查看Window → Analysis → Profiler 中的Rendering下的Drawcalls,如图2所示。
图1 图2
四、Drawcalls优化
1. 3D场景优化
1.1 静态批处理
静态批处理首先需要到 Project Setting → Player → Other Setting 中将Static Bathing 勾选上,然后把需要静止的物体标记为Static,然后无论大小,相同材质的都会组成Batch。如下图所示,当没有勾选 static 的时候,场景中2个cube的drawcalls为4,勾选了之后drawcalls变成了3。
1.2 动态批处理
动态批处理需要在Project Setting → Player → Other Setting 中将Dynamic Bathing 勾选上,Unity会自动将使用相同材质的物体合并处理。如图所示,场景中虽然有3个cube,但是drawcalls还是只有3。
不过,在使用动态批处理的时候,具有一些局限性。
-
顶点属性最大限制900的可移动物体,
-
使用lightmap的物体不行进行批处理
-
使用多通道的shader也不会进行批处理
-
缩放比不同的物体不会批处理
1.3 勾选 Enable GPU Instancing
在使用大量重复的物体时,需要将该物体的材质的 Enable GPU Instancing 勾选上,这样Unity会将Mesh相同的物体合并处理,常用于树木,植被,粒子等。如图所以虽然我放置了5个cube,但是drawcalls依然只有3。
正在上传…重新上传取消
1.4 减少实时光照和阴影效果
当开启灯光,在没有开启阴影的时候drawcalls为3,开启了之后drawcalls变成了7。实时阴影会导致drawcalls大幅上升,建议关闭实时阴影,使用lightmap满足你想要的阴影效果。
1.5 合并Mesh和材质球
如果一个模型有2个或者以上的材质球的时候,drawcalls会直线上升,所以应该劲量将mesh和材质球合并成为一个,以减少drawcalls。如图所示我在一个测试cube中放置了3个material,这个时候drawcalls从3变成了10(和不同的material有关,每个material最低为1)。
1.6 渲染顺序调整
Unity的渲染是有顺序的,这个顺序我们可以自己调整,相机按照深度进行渲染。在图中我使用了3个cube梯次排序,其中第一个和第三个使用相同的材质球。当第二个cube在中间时,drawcalls为5,第二个cube在前或者后时,drawcalls为4。
出现图中情况的原因是在绘制第一个cube时候使用材质球A,绘制第二个cube的时候使用材质球B,绘制第三个cube的时候使用材质球A,这个时候第二个打断了材质球A的渲染使用,使第一个和第三个材质球分开渲染了。
2. 2D UI优化
2.1 图集制作
在制作UI的时候,文件夹中小的图片可以在Unity中制作成图集再给UI使用,这样可以减少drawcalls,具体操作为首先在Project Setting →Editor → Sprite Packer 中选择Mode 为Enable。然后在Assets中右键 Create → Sprite Altas,再将需要打包图集的图片资源拖到Altas中的Objects for Packing中,再点击Pack Preview,图集就制作好了。
如图所示,在没有使用图集的时候,3张图drawcalls为5,当使用的图集时,drawcalls变成了3,3个图使用同一张图集,drawcalls也就变成了3。
2.2 不同图集重叠会打断合批
不同的图集之间重叠会打断合批,在制作UI的时候应该注意不同的UI放置位置,避免出现这种问题,如图,2个图集的UI在分开放的时候drawcalls为4,重叠隔开之后drawcalls变成了5。
2.3 谨慎使用多canvas
每添加一个canvas会添加一个drawcalls,哪怕你下面的资源使用同一个图集也会添加。如图所示当我添加一个canvas,改变了第三个图的层次之后,drawcalls从3变成了4。
2.4 Mask会打断合批,新增drawcalls
当我给使用了图集的一个UI添加一个Mask组件之后,drawcalls从3变成了5。这里可以使用Rect Mask 2D,效果相同但是drawcalls不会增加,回到了3。当我在有Mask组件的UI下添加一个Image,sprite还是使用图集中的图片时,发现drawcalls从5变成了6,说明mask组件还会打断合批,使Mask内外资源分开渲染。这个时候将Mask改为Rect Mask 2D,drawcalls从6变回4,但是也比最初的3多了1,说明使用Rect Mask 2D 也会打断合批,分开渲染。
2.5 图文混排打断合批
新加入两个Text文本(Text Mesh Pro效果相同),drawcalls从3变到4,因为text和image有不同写渲染方式需要分开渲染,移动Text,当2个Text打断了图的连续深度排序时,图片的合批会被,打断,生成多的drawcalls,在图文排版是应特别注意。下图列举了一些错误排版方式和正确方法以及建议排版。注意,unity必须重叠,才会多产生drawcall
正确:
错误:
建议:
GPU instance 使用一次渲染调用来绘制多个物体,来节省每次绘制物体时CPU→GPU的通信
使用限制 | 优点 | |
静态合批 | 静止的问题,大内存,大包体 StaticBatchingUtility.Combine | 适用范围较广 |
动态合批 | * 顶点属性最大限制900,<br>* 使用lightmap的物体不行进行批处理<br>* 使用MultiplePass的shader也不会进行批处理 | 在一些动的物体上面,内存不会显著增长,不会影响打包的包体 |
GPUInstance | 一般用于大批生成的物体 | 没有动态合批那样对网格数量的限制,也没有静态网格那样需要这么大的内存 |
SRP Batcher | 不同mesh,只要使用相同shader且变体一样即可 缺点:constant Buffer显存固定开开销,不支持 | 节省Uniform Buffer的写入操作;按shader分 Batch,预先生存Uniform Buffer,Batch 内部无CPU Write |