Cocos 中文社区第4期有奖征稿活动火热进行中,iWatch SE、坚果投影仪等丰厚奖品等你来拿,点击文末【阅读原文】进入社区专贴,把你的聪明才智向我们砸来吧!

点击查看活动详情

本文即为此次社区征稿的参与稿件,作者 Blake 基于自己多年来从事游戏行业的实践经历,同大家分享他在 Cocos Creator 3.x 中进行 DrawCall 性能优化的经验与心得。


游戏渲染是性能开销的大头,掌握渲染优化相关技巧对性能调优而言至关重要。渲染优化又可以从很多方面来入手,其中,减少 DrawCall 是一个非常重要的手段。本文将介绍为什么要减少 DrawCall,以及在 Cocos Creator 3.x 中应该如何减少 DrawCall

目录

1. 为什么要减少 DrawCall

2. 常用合批技术的原理及优缺点

3. v3.x 如何优化 3D 物体 DrawCall

4. v3.x 如何优化 2D UI 物体 DrawCall

一、为什么要减少 DrawCall 

游戏引擎绘制一个画面,首先需要遍历场景中的物体,然后把这些物体提交给 GPU 来进行绘制。假设游戏场景中有100个需要绘制的物体,当游戏引擎把这100个物体一个一个提交给 GPU 绘制时,引擎底层会进行以下操作:

  1. CPU 传递物体渲染所需要的数据给 GPU(如网格模型、材质参数、纹理对象、世界矩阵等)。

  2. CPU 把数据准备好以后,向 GPU 下达绘制命令(Draw cmd)。

  3. GPU 开机渲染,将 CPU 提交过来的数据渲染到我们的显示目标上。

这里首先和大家介绍几个概念:

  • Drawcall:CPU 提交数据给 GPU,然后向 GPU 下达渲染命令的这个过程即为 DrawCall,又叫同一个批次渲染。

  • DrawCall 数目(批次数):指的是游戏引擎绘制一个游戏场景中的所有物体需要向 GPU 提交的渲染命令(DrawCall)次数。若有100个物体逐个提交给 GPU 绘制,那么就要提交100次渲染命令,DrawCall 数目即为100。也可以理解为:一个游戏场景中的物体被分成几批次来进行渲染。

  • 合批(减少 DrawCall 数目):把几个物体合在一起提交给 GPU 渲染绘制叫做合批。比如100个物体,把前50个物体、后50个物体分别合在一起提交给 GPU 渲染,那么这100个物体完成渲染只需提交2批次,也就是2次 DrawCall。合批可以减少 DrawCall 数目,我们通常说的减少 DrawCall 指的就是合批。

在 Cocos Creator 中,我们可以通过调试参数看到当前的 DrawCall 数目。

DrawCall 导致的性能开销主要在 CPU 端的命令组装,DrawCall 越多,需要组装的命令就越多,消耗的 CPU 资源越多。特别对于低端机和 iOS 这类不能 JIT 的设备,DrawCall 对性能影响很大。

接着来分析一下为什么合批(减少 DrawCall)可以提升渲染性能:

  1. CPU 把能合批的物体一起提交给 GPU,避免重复的数据提交,如同一个模型的多个物体。CPU 给 GPU 提交数据开销比较大,大于 CPU 对内存的数据拷贝。

  2. 100个物体逐个绘制就要下达100次 Draw Cmd,而把100个物体合成一批提交给 GPU,就只需下达一次 Draw Cmd,也就是 GPU 只要开机一次就能完成渲染。

  3. GPU 每次开机绘制都有一个吞吐量,如果我们每次能尽可能多提交一些三角形的面数,GPU 一次渲染能吃更多的面。这就像工厂的流水线,只要开机,产100个商品和产1个商品的代价一样,那么在安排订单的时候,尽量每次开机凑够100个。所以合批提交渲染,能提升 GPU 的吞吐量,提高效率。

经过上述分析不难得出,尽可能把物体合批到一起渲染(减少 DrawCall 数目)对渲染优化有着非常重要的作用。

二、常用合批技术

先来看一个概念——以下称「能够合批」。我们把使用同一个材质+使用能够合批的渲染组件(如 MeshRenderer)的物体称作「能够合批」。合批的物体首先要满足「能够合批」的条件,否者任何技术手段都无法合批。

合批常用的技术手段有静态合批、动态合批、GPU Instancing 合批。静态合批和动态合批要求:模型可以不同,但材质必须相同的渲染对象;而 GPU Instancing 合批则要求模型和材质都必须相同的渲染对象。

静态合批

静态合批是将能够合批物体的网格按照它的位置预先重新合并生成一个大的新网格,然后进行绘制。由于这些物体满足合批条件,都是同一个材质球,因此渲染这些物体只要把这个合并后的新网格对象一次提交给 GPU,就能实现这些物体的合批处理,降低 DrawCall。

但静态合批也有它的缺点:

  1. 静态合批需要预先计算合并网格,增加了运行初始化时的时间;

  2. 静态合批一但预先计算好合并后的网格,这些物体就不能再「移动」了。因此静态合批并不适合用在经常移动的物体的合批上,移动的物体可以使用动态合批。

  3. 静态物体网格合并后可能会增大内存开销。

可能有的小伙伴对上述第3点有所疑问:假设有100个物体的 Mesh 数据,合并后还是100个物体的 Mesh 数据,为什么合并后还可能会增大内存开销呢?

这里首先有「可能」两个字,也就是说某些情况下会增加,某些情况下不会增加。试想一下,如果100个物体完全不一样,那么合并后的 Mesh 顶点的内存开销和合并前都是一样的;但如果100个物体的 Mesh 完全一样,那么合并后就会有100个同一个 Mesh 不同位置的顶点数据了,这种合并就会增大内存开销。所以在实际游戏开发中,我们在做森林(大量相同树木)等场景时,就不使用静态合批。

动态合批

动态合批是指在每次渲染之前,CPU 将能够合批的物体的每个顶点的世界坐标计算出来(模型顶点坐标*世界变化矩阵)后,提交给 GPU,然后世界矩阵用单位矩阵,来达到合批的效果。动态合批适合用于移动的物体,且不会产生额外的内存开销。

但也由于每次渲染之前 CPU 都要重新计算坐标,所以动态合批会增加 CPU 的负担。在实际的使用中,要把 CPU 的额外开销和合批带来的提升做一个权衡。所以动态合批不是万能的,不适合太多顶点数目的物体的渲染绘制。

GPU Instancing 合批

对于游戏场景中同一个物体的 N 个实例可以采用 GPU Instancing 合批技术,它的原理是提交一次物体的模型,然后将实例的位置、旋转、缩放等信息提交给 GPU,再由 GPU 绘制 N 个实例出来。

从技术原理来看 GPU Instancing 是一个非常好的合批手段,几乎不会带来额外的开销。不过 GPU Instancing 需要 Shader 的支持,且有些早期的显卡并不支持 GPU Instancing 的特性。

三、优化 3D 物体 DrawCall

了解了几种合批技术的原理和优缺点后,就可以考虑采用对应的合批技术来进行合批。但在这之前还有两个关键点摆在我们面前:

  1. 分析 DrawCall 消耗在哪些地方;

  2. 为尽可能多的物体创造「能够合批」的条件。

如何摸清具体的物体绘制消耗了多少 DrawCall?一般我们会通过场景的组织来判断分析估算,同时通过显示/隐藏物体查看 DrawCall 数目的变化来确认我们的分析。如下面这个例子,隐藏人物后 DrawCall 由7变为4,说明该人物角色绘制花费了3个 DrawCall。

显示人物角色

隐藏人物角色

让更多的物体有合并的可能,我采取的方式一般是将多个物体的材质球尽可能合并。材质球主要包含 Shader+纹理,我们应尽可能让更多物体使用同一个 Shader,再将不同物体的纹理合并到一起,让这些物体使用同一个材质球。此外还需要改变渲染组件类型,让更多的物体满足合批所支持的渲染组件,如将 SkinnedMeshRenderer 组件变成 MeshRenderer 组件。

摸清楚了 DrawCall 分布,并尽可能创造合批条件后,接下来就看看如何在 Cocos Creator 3.x 中实现这几种合批技术。

静态合批

把需要静态合批的物体放到一个节点下,然后在初始化接口里面调用静态合批 API 接口,预先计算好新合批后的物体,这样就可以做到合批了。

以下面的「1锥体+1cube」为例,首先在初始化中预先计算好节点下面所有物体的新的 Mesh;可以看到,没有开启静态合批之前需要2个 DrawCall,开启后只要1个 DrawCall,节约了1个 DrawCall。

开启前

开启后

动态合批

动态合批只需要在材质球上勾选 USE BATCHING 即可:

GPU Instancing 合批

GPU Instancing 合批也是一样,在材质球上勾选 USE INSTANCING 即可:

但是由于 GPU Instancing 只对一个 Mesh 的多个实例有用,所以作用在「1锥体+1cube」上仍然还是3个 DrawCall,但若是「2cube」,开启 GPU Instancing 后则会合并成1个 DrawCall。

「1锥体+1cube」不是同一个网格对象,不能合批

「2cube」是同一个网格对象,可以合批

四、优化 2D UI 物体 DrawCall

2D 是特殊的 3D,所以上述 3D 部分的合批手段在 2D 也都适用。

2D 的 DrawCall 优化会更简单些,只要 UI 元素使用同一个图集(同一个纹理)就可以合批。UI 元素使用的 Shader 都使用了 Buildin-Sprite Shader(包括 Label 也使用这个 Shader),所以是否「能够合批」就看 UI 物体是否为同一个图集。

精灵图集大家比较熟悉了,至于 Label 图集,引擎会自动生成,可以理解为 Label 的图集和精灵的图集不是同一个图集。如果开启了文本缓存模式,多个不同的 Label 文字就有可能被引擎生成到同一个图集。所以对于 2D 而言同一个图集的「能够合批」,Label 会中断打乱「精灵的合批」。

2D UI DrawCall 优化的核心就3点:

  1. 打图集,尽量将同一个界面的 UI 元素打入同一图集。

  2. 优化 2D 的节点组织层级,UI 是按照层级来渲染的,尽量防止不同图集的 UI 元素互相打乱、以及 Label 的打断。比如组织 UI 元素尽可能是:A1A2A3/B1B2B3/C1C2C3……A/B/C 不同的图集放在一起,避免以 A1B1C1A2B2C2A3B3C3……这样的方式来组织 UI 元素或图集。

  3. 注意 Label 打乱的 UI 物体的合批。


今天的分享就到这里,希望可以对大家有所帮助!欢迎前往论坛专贴一起讨论交流,地址:

https://forum.cocos.org/t/topic/132490

往期精彩

Cocos Creator 性能调优:如何减少 2D/3D DrawCall?相关推荐

  1. Cocos Creator 性能调优优化集锦

    01 为什么要做性能优化? 性能:是一种优秀的能力.唤醒快.运行持久.稳定. 这种能力在游戏上能让你的用户感觉很爽,表征表现为加载快.手机不发热.运行流畅.不卡顿. 所以,性能优化的终极目标是让你的用 ...

  2. android性能调优的工具,神兵利器-Android 性能调优工具 Hugo

    在进行Android性能调优.减少应用卡顿时,寻找可优化的code是一个必要的过程.如何发现应用中的耗时任务甚至是耗时函数呢,如果可以在log中打印每个方法的执行时间,甚至把执行方法时的输入输出同时打 ...

  3. iOS应用性能调优的25个建议和技巧【转】

    转载自:http://blog.jobbole.com/37984/ 首页 最新文章 资讯 程序员 设计 IT技术 创业 在国外 营销 趣文 特别分享 更多 > - Navigation - 首 ...

  4. iOS应用性能调优建议

    本文来自iOS Tutorial Team 的 Marcelo Fabri,他是Movile的一名 iOS 程序员.这是他的个人网站:http://www.marcelofabri.com/,你还可以 ...

  5. iOS应用性能调优的25个建议和技巧

    写在前面 本文来自iOS Tutorial Team 的 Marcelo Fabri,他是Movile的一名 iOS 程序员.这是他的个人网站:http://www.marcelofabri.com/ ...

  6. CoreAnimation6-基于定时器的动画和性能调优

    基于定时器的动画 定时帧 动画看起来是用来显示一段连续的运动过程,但实际上当在固定位置上展示像素的时候并不能做到这一点.一般来说这种显示都无法做到连续的移动,能做的仅仅是足够快地展示一系列静态图片,只 ...

  7. hive性能调优实战pdf_Nginx 性能调优实战

    来自:Linux社区 1.Nginx运行工作进程数量 Nginx运行工作进程个数一般设置CPU的核心或者核心数x2.如果不了解cpu的核数,可以top命令之后按1看出来,也可以查看/proc/cpui ...

  8. Tomcat 和 JVM 的性能调优总结

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:http://rrd.me/enKbC Tomcat性能调优 ...

  9. JVM解读-性能调优实例

    2019独角兽企业重金招聘Python工程师标准>>> JVM性能调优 1 堆设置调优 年轻代大小选择 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选 ...

最新文章

  1. linux系统安装tv软件下载,达龙云电脑apk下载
  2. python学习必会知识点:对文件操作详解
  3. springboot转发http请求_网易后端实习生分享:Springboot异常和错误处理规范
  4. .NET单点登录实现方法----两种
  5. oracle gg 有两个字段没同步到,GG配置Oracle同步到SQLServer
  6. spring 连数据库的配置文件
  7. python 字典查询比列表快_为什么python字典要比列表快以及哈希查找解释。
  8. Jmater (十九) 分布式测试(性能测试大并发、远程启动解决方案)
  9. 方舟综合指令代码大全系统综合
  10. 电阻分压可以当作电源供电吗
  11. 航天信息上传服务器地址,金税盘上传参数设置怎么设置?
  12. 关于“学习金字塔理论”的所思所想
  13. 视觉数据集是基于物体和风景标记的巨大的图像库
  14. 组合优化- 均值方差、最大夏普、风险平价模型
  15. node.js对接支付宝沙箱,看这篇足够
  16. docker安装sharding-proxy
  17. 5G标准制定,华为未如愿,中国还靠大唐电信
  18. LeetCode 上升的温度
  19. 研一Python基础课程第二周课后习题分享(含代码)
  20. 地质学与计算机的应用,试述计算机技术在地质工作中的应用

热门文章

  1. Java入门——IO流
  2. 方舟加入服务器显示全球禁止怎么办,方舟很多服务器无法加入 | 手游网游页游攻略大全...
  3. 黑衣剑士查询账号所在服务器,刀剑神域手游黑衣剑士为什么登不了 刀剑神域黑衣剑士王牌服务器进不去...
  4. 新版gsp药店计算机操作规程,2017最新版GSP单体药店质量管理制度及岗位职责及操作规程.doc...
  5. 【混沌工程】什么是混沌工程?
  6. 【QT从零开始系列12】QT学习心得及资源汇总
  7. AR@制造业,拯救跨国公司的一根稻草
  8. ObjectARX如何锁定一个图层
  9. HTML/CSS/Javascript在线代码运行工具(网站汇总)
  10. mysql插入数据的时候丢失数据