什么是Draw Call?为什么Draw Call 多了会影响帧率?

什么是Draw Call?

Draw Call 本身的含义很简单,就是CPU调用图像编程接口,如OpenGL 中的glDrawElements 命令或者DirectX 中的DrawlndexedPrimitive命令,以命令GPU 进行渲染的操作。

一个常见的误区是, Draw Call 中造成性能问题的元凶是GPU,认为GPU 上的状态切换是耗时的,其实不是的,真正“拖后腿”其实的是CPU。

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

我们先来做一个实验:请创建10 000 个小文件,每个文件的大小为1KB,然后把它们从一个文件夹复制到另一个文件夹。你会发现,尽管这些文件的空间总和不超过10MB ,但要花费很长时间。现在,我们再来创建一个单独的文件,它的大小是10MB,然后也把它从一个文件夹复制到另一个文件夹。而这次复制的时间却少很多!这是为什么呢?明明它们所包含的内容大小是一样的。原因在于,每一个复制动作需要很多额外的操作,例如分配内存、创建各种元数据等。如你所见,这些操作将造成很多额外的性能开销,如果我们复制了很多小文件,那么这个开销将会很大。

渲染的过程虽然和上面的实验有很大不同,但从感性角度上是很类似的。在每次调用Draw Call 之前, CPU 需要向GPU 发送很多内容,包括数据、状态和命令等。在这一阶段, CPU 需要完成很多工作,例如检查渲染状态等。而一旦CPU 完成了这些准备工作, GPU 就可以开始本次的渲染。GPU 的渲染能力是很强的,渲染200 个还是2 000 个三角网格通常没有什么区别,因此渲染速度往往快于CPU 提交命令的速度。如果Draw Call 的数量太多, CPU 就会把大量时间花费在提交Draw Call 上,造成CPU 的过载。图2.20 显示了这样一个例子。


如何减少Draw Call?

尽管减少Draw Call 的方法有很多,但我们这里仅讨论使用批处理(Batching )的方法。

我们讲过,提交大量很小的Draw Call 会造成CPU 的性能瓶颈,即CPU 把时间都花费在准备Draw Call 的工作上了。那么,一个很显然的优化想法就是把很多小的DrawCall 合并成一个大的Draw Call ,这就是批处理的思想。图2.21 显示了批处理所做的工作。

需要注意的是,由于我们需要在CPU 的内存中合并网格,而合并的过程是需要消耗时间的。因此,批处理技术更加适合于那些静态的物体,例如不会移动的大地、石头等,对于这些静态物体我们只需要合并一次即可。当然,我们也可以对动态物体进行批处理。但是,由于这些物体是不断运动的,因此每一帧都需要重新进行合并然后再发送给GPU,这对空间和时间都会造成一定的影响。


在游戏开发过程中,为了减少Draw Call 的开销,有两点需要注意。
( 1) 避免使用大量很小的网格。当不可避免地需要使用很小的网格结构时,考虑是否可以合并它们。
(2)避免使用过多的材质。尽量在不同的网格之间共用同一个材质。

本文摘自《Unity Shader入门精要》
出处:https://blog.csdn.net/e295166319/article/details/78595008

推荐阅读