Unity —— DrawCall

560 阅读4分钟

1.什么是DrawCall

在unity中,每次CPU调用图形命令接口(如Direct/OpenGL)来命令GPU进行渲染就是一次DrawCall.

2.为什么 Draw Call多了会影响帧率

1.了解CPU和GPU的并行工作原理

为了实现CPU和GPU的并行工作,需要一个命令缓冲区,命令缓冲区中包含了一个命令队列,CPU可以向其中添加命令,GPU从中读取命令,使得CPU和GPU相互独立工作。当CPU需要渲染工作时,向缓冲区添加命令,GPU读取命令进行下一次渲染,DrawCall就是命令缓冲区的命令之一。

2.多次调用DrawCall的后果

在每次调用DrawCall之前,CPU需要向GPU发送数据、渲染状态(材料纹理等)、命令等。CPU进行的操作具体就是:

  • 准备渲染对象,再将渲染对象从硬盘加载到内存,然后从内存加载到显存,进而方便GPU高速处理
  • 设置每个对象的渲染状态,也就是设置对象的材质、纹理、着色器等
  • 输出渲染图元,然后向GPU发送DrawCall命令,并将渲染图元传递给GPU

CPU完成了这次准备工作后,GPU就可以开始本次的渲染,然而GPU的渲染速度比CPU提交命令的速度快得多,如果DrawCall次数过多,就会把大量的时间浪费在提交DrawCall命令上,造成CPU的过载,影响游戏运行效率。

3.如何减少Draw Call(合批优化策略)

为了减少DrawCall次数,我们往往将多个小DrawCall合并成一个大的DrawCall,这就是批处理思想。 在Unity中,连续多个UI的材质、纹理一致就会公用一次DrawCall,我们往往把这些一致的打包成一个图集,但需要注意的是,如果中间有不同的渲染状态,也会重新调用一次DrawCall。

  • 静态批处理(Static batching):

    • 使用: 静态批处理的前置条件是使用同一个材质,将对应的对象设置为static。Unity在Build的时候,会自动下生成合并的网格,并将它以文件形式存储合并后的数据,这样在当场景被加载时,一次性提交整个合并模型的顶点数据。因此适用于不能移动、旋转、缩放的物体。
    • 缺点: 静态批处理实际上是牺牲内存来换取渲染性能。静态批处理中一些物体共享了相同的网格,每个物品都有这个网格的复制品,即有多少个物品就有多少个网格,如果一个物品需要多次使用就会消耗多倍的内存,此时应该避免使用静态批处理,可以使用GPU Instance来代替。
    • 优点: 静态合批并没有减少Draw Call的数量(会显示减少),此时所有的子模型顶点切换到了世界空间下,且共享材质,所以Draw Call之间没有切换渲染状态,不需要重新计算顶点位置,起到了渲染优化的目的。
  • 动态批处理(Dynamic batching):

    • 介绍: 动态合批是专门为优化场景中共享同一材质的动态GameObject的渲染设计的。目标是以最小的代价合并小型网格模型,减少Drawcall。
    • 原理: 在进行场景绘制之前将所有的共享同一材质的模型的顶点信息变换到世界空间中,然后通过一次Draw call绘制多个模型,达到合批的目的。模型顶点变换的操作是由CPU完成的,所以这会带来一些CPU的性能消耗。
    • 使用: Unity自动处理
    • 前提: 共享相同的材质
    • 限制:
      • 顶点属性最大限制900。
      • 如果我们使用了顶点坐标,法线,UV,那么就只能最多300个顶点。
      • 如果我们使用了UV0,UV1,和切线,又更少了,只能最多150个顶点。
      • 模型缩放必须一致才能进行合批。
      • 使用lightmap的物体不行进行批处理,除非Lightmap数据相同。
      • 使用MultiplePass的shader一定不会被合批。因为Multi-pass Shader通常会导致一个物体要连续绘制多次,并切换渲染状态。打破其跟其他物体进行Dynamic batching的机会。
      • 接受实时阴影的物体不会进行批处理。
      • 延迟渲染是无法被合批。