欢迎光临散文网 会员登陆 & 注册

DrawCall的定义

2022-01-27 15:41 作者:Lucas_dudu  | 我要投稿

cpu对图形绘制接口的调用,CPU通过调用图形库(directx/opengl)接口,命令GPU进行渲染操作。

1.Draw Call与性能

经常,我们会提到DrawCall性能优化。很多时候我们会误以为DrawCall造成的性能问题是GPU切换渲染状态导致,其实这里的元凶是CPU。要理解这一点,我们就需要明白下面2个问题

  • CPU和GPU是如何进行并行工作和交互的?

试想,渲染流程没用采用流水线的工作方式:CPU发送一个渲染命令之后,GPU立即执行渲染命令绘制图形,等到渲染任务结束之后,CPU才可以继续发送下一个渲染命令,这样显然影响工作效率。

采用渲染流水线后,CPU与GPU并行工作,独立而不相互依赖。这是通过命令缓冲区来实现的:命令缓冲区维护一个命令队列,CPU向其中发送命令,GPU从中取出命令并执行。命令有很多种,DrawCall是一种,其他命令还有改变渲染状态、设置渲染数据流等。

这种方式就类似于游戏开发的网络通信:维持一个消息队列,网络线程接收解析消息并将之添加到消息队列,游戏主线程更新时从中取出消息并做派发处理。


通俗的来说就是Cpu:(#`O′)喂你好,是Gpu吗?快点醒醒我这里又有画画的任务了(Cpu调用Gpu的次数),打一个比方比如上传很多文件到百度云或其他地方时,都会把它压缩到一个文件夹里,不会把它们分开上传(当然还有原因就是它们数据是相关,比如是主题的一套ico文件或软件的安装文件),排除这些和文件整合的原因,假设网速没有波动,分开传和压缩包,压缩包速度一定快很多的(不仅仅是因为压缩包更小),主要是每次上传还有一些预备动作(比如与服务器链接,初始化Socket等等),细心的会发现文件当拖动到百度云会有几毫秒的延迟。其实优化DrawCall主要是Cpu的处理速度的优化,Cpu和Gpu是并行工作的,处理的方式有一个命令缓存区,具体如图所示:


命令缓存区

别看图中画的好像是Cpu在等待Gpu,实际上Cpu才是拖后腿的那个,现实中Gpu早就把命令缓存区里命令都处理完毕了,Cpu确还在准备DrawCall的命令,Cpu通过图像编程接口向命令缓存区添加命令,而Gpu通过缓存区获取命令处理。

  • DrawCall是如何影响性能的?

每一次绘制CPU都要调用DrawCall,而在调动DrawCall前,CPU还要进行很多准备工作:检测渲染状态、提交渲染所需要的数据、提交渲染所需要的状态。

而GPU本身具有很强大的计算能力,可以很快就处理完渲染任务。

当DrawCall过多,CPU就会很多额外开销用于准备工作,CPU本身负载,而这时GPU可能闲置了。

做个试验:拷贝1000个总大小1M的文件和单个大小为1M的文件,明显拷贝1000个文件要慢很多,DrawCall调用和这个很类似。

(在每次调用DrawCall之前,因为Cpu需要向Gpu发送很多内容,包括数据、状态和命令,在这个阶段Cpu需要完成很多工作,比如检查渲染状态等(有一堆工作要Cpu处理,才会存放到缓存区),存放到缓存区以后,Gpu就要开始工作了,Gpu渲染能力还是很强的,渲染200或2000个三角网格通常看不出区别,导致Gpu渲染速度大于Cpu的提交速度,影响渲染流水线速度就是提交比较慢的Cpu(现在知道玩游戏要买的电脑配置了吧,一般选择Cpu比较好的,Gpu一般的即可,当然游戏画面特别好的,还是建议把显卡买好点的,有些游戏硬性条件普通显卡根本渲染不了,并不是渲染速度的问题了),最后可想而知Cpu会花费大量的时间在提交DrawCall的路上,造成Cpu的过载,Gpu确会出现空闲。)

2.DrawCall优化:减少DrawCall


既然,我们已经知道DrawCall导致的性能问题在于DrawCall数量过多,那么我们优化的思路就是减少DrawCall。这里我们只讨论批处理(Batching)。

过多的DrawCall会造成CPU的性能瓶颈:大量时间消耗在DrawCall准备工作上。很显然的一个优化方向就是:尽量把小的DrawCall合并到一个大的DrawCall中,这就是批处理的思想。

使用批处理我们需要在CPU和RAM中合并网格,而合并网格本身是需要计算消耗,而且创建新网格也会占用内存。因此批处理的频次不宜太高,不然造成的消耗可能得不偿失。

使用批处理的注意事项:

  1. 合并的网格会在一次渲染任务中进行绘制,他们的渲染数据,渲染状态和shader都是一样的,因此合并的条件至少是:同材质、同贴图、同shader。最好网格顶点格式也一致。

  2. 尽量避免使用大量小的网格,当确实需要时,考虑是否要合并。

  3. 避免使用过多的材质,尽量共享材质。

  4. 网格合并的顶点数量有上限(Unity中好像是65535)

  5. 合并本身有消耗,因此尽量在编辑器下进行合并

  6. 确实需要在运行时合并的,将静态的物体和动态的物体分开合并:静态的合并一次就可以,动态的只要有物体发生变换就要重新合并。


DrawCall的定义的评论 (共 条)

分享到微博请遵守国家法律