简单来说,Drawcall就是屏幕渲染一次所需要的开销,为了较少消耗,提高性能.

什么是DrawCall以及如何对DrawCall进行优化操作。

一、什么是DrawCall?

在unity中,每次CPU准备数据并通知GPU的过程就称之为一个DrawCall。

具体过程就是:设置颜色-->绘图方式-->顶点坐标-->绘制-->结束,所以在绘制过程中,如果能在一次DrawCall完成所有绘制就会大大提高运行效率,进而达到优化的目的。

二、DrawCall为什么会影响效率?

说道为什么会影响效率,就首先要了解一下他的工作原理:为了CPU和GPU可以进行并行工作,就需要一个命令缓冲区,就是由CPU向其中添加命令,然后又GPU从中读取命令,这样就实现了通过CPU准备数据,通知GPU进行渲染。

在每次调用DrawCall之前,CPU需要向GPU发送很多内容,主要是包括数据,渲染状态(就是设置对象需要的材质纹理等),命令等。CPU进行的操作具体就是:

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

所以如果DrawCall数量过多就会导致CPU进行大量计算,进而导致CPU的过载,影响游戏运行效率。

注意:

场景中有100个gameobject,它们拥有完全一样的Material,那么这100个物体很可能会被Unity里的Batching机制结合成一个Batch。所以用“Batches”来描述Unity的渲染性能是不太合适的,它只能反映出场景中需要批处理物体的数量。那么可否用“Draw calls”来描述呢?答案同样是不适合。每一个“Draw calls”是CPU发送个GPU的一个渲染请求,请求中包括渲染对象所有的顶点参数、三角面、索引值、图元个数等,这个请求并不会占用过多的消耗真正消耗渲染资源的是在GPU得到请求指令后,把指令发送给对应物体的Shader,让Shader读取指令并通知相应的渲染通道(Pass)进行渲染操作。

       我的上篇博文【U3D】进入shader编程的世界里有简单介绍过Shader,不懂的童鞋可以去看看。接着上边的问题进行说明,场景上有1个gameobject,希望能显示很酷炫的效果,它的Material上带有许多特定的Shader。为了实现相应的效果,Shader里或许会包含很多的Pass,每当GPU即将去运行一个Pass之前,就会产生一个“SetPass call”,因此在描述渲染性能开销上,“SetPass calls”更加有说服力。 

三、如何优化DrawCall?

1.渲染顺序:图集、材质、层级的处理

想看这些如何进行优化,就需要对他们的工作原理进行理解一下。下面我们以NGUI为例,讲解一下他们之间的关系:

NGUI主要是有三大模块组成:UIPanel,UIWidget,UIDrawcall组成,其中UIPanel是用来管理UIWidget控件和UIDrawCall,而UIWidget是所有组件的基类。

在NGUI框架中,会有一个静态的list用来存放所有的Panel,然后每个单独的Panel下会保存自己的UIWidget和UIDrawCall,就是在每次绘制的时候panle会遍历自己下面的所有层级下的子物体,直到查找结束,或者遇到新的panel会跳出当前分支,继续寻找其他分支,直到全部查找结束。所以说在实际运行中,每次都会为一个UIWidget绘制一个DrawCall,如果这时候连续的多个UIWidget使用的材质和纹理一致,就会公用一个DrawCall,下面给大家看下具体的情形:

这是使用不同材质和纹理的情况

这是使用相同的材质和纹理的情况​​​​​​

所以并不是好多人的认知是只要同一个图集就会占用同一个DrawCall,通过上图分析发现不光是要使用同一个图集,还要使用同样的材质在同一个panel下才可以,否则就会重新进行调用一次DrawCall。另外需要注意的是,如果使用同一个图集、材质,但是中间夹杂了其他的渲染状态,也会导致重新调用一次DrawCall

另外还需要注意一点就是在panel下如果动态的物体,就是为了实现某种效果,需要UI 进行位置移动,这种情况下,最好做成动态分离,因为只要panle下UI有移动,panle就会对清空之前的保存的UIWidget和UIDrawCall,重新进行渲染,这样就会造成性能浪费,有的同学会说这样不是增加了DrawCall吗,但是相对于每次都重新绘制,应该还是会更加节省性能的吧,你说呢?

所以说在对UI进行界面排布就需要对图集和层级做好规划,进而减少DrawCall次数。

关于渲染顺序

U3D的渲染是有顺序的,U3D的渲染顺序是由我们控制的,控制好U3D的渲染顺序,你才能控制好DrawCall

一个DrawCall,表示U3D使用这个材质/纹理,来进行一次渲染,那么这次渲染假设有3个对象,那么当3个对象都使用这一个材质/纹理的 时候,就会产生一次DrawCall,可以理解为一次将纹理输送到屏幕上的过程,(实际上引擎大多会使用如双缓冲,缓存这类的手段来优化这个过程,但在这 里我们只需要这样子认识就可以了),假设3个对象使用不同的材质/纹理,那么无疑会产生3个DrawCall

接下来我们的3个对象使用2个材质,A和B使用材质1,C使用材质2,这时候来看,应该是有2个DrawCall,或者3个DrawCall。 应该是2个DrawCall啊,为什么会有3个DrawCall???而且是有时候2个,有时候3个。我们按照上面的DrawCall分析流程来分析一 下:

1.渲染A,使用材质1
2.渲染B,使用材质1
3.渲染C,使用材质2

在这种情况下是2个DrawCall,在下面这种情况下,则是3个DrawCall

1.渲染A,使用材质1
2.渲染C,使用材质2
3.渲染B,使用材质1

因为我们没有控制好渲染顺序(或者说没有去特意控制),所以导致了额外的DrawCall,因为A和B不是一次性渲染完的,而是被C打断了,所以导致材质1被分为两次渲染

那么是什么在控制这个渲染顺序呢?首先在多个相机的情况下,U3D会根据相机的深度顺序进行渲染,在每个相机中,它会根据你距离相机的距离,由远到近进行渲染,在UI相机中,还会根据你UI对象的深度进行渲染

那么我们要做的就是,对要渲染的对象进行一次规划,正确地排列好它们,规则是,按照Z轴或者深度,对空间进行划分,然后确定好每个对象的Z轴和深度,让使用同一个材质的东西,尽量保持在这个空间内,不要让其他材质的对象进入这个空间,否则就会打断这个空间的渲染顺序

在这个基础上,更细的规则有:

场景中的东西,我们使用Z轴来进行空间的划分,例如背景层,特效层1,人物层,特效层2
NGUI中的东西,我们统一使用Depth来进行空间的划分
人物模型,当人物模型只是用一个材质,DrawCall只有1,但是用了2个以上的材质,DrawCall就会暴增(或许对材质的RenderQueue 进行规划也可以使DrawCall只有2个,但这个要拆分好才行),3D人物处于复杂3D场景中的时候,我们的空间规则难免被破坏,这只能在设计的时候尽 量去避免这种情况了
使用了多个材质的特效,在动画的过程中,往往会引起DrawCall的波动,在视觉效果可以接受的范围内,可以将特效也进行空间划分,假设这个特效是2D显示,那么可以使用Z轴来划分空间

2.动态与静态合批

批处理从字面意思就是一块处理多个物体的意思,但是是什么样的都可以进行批处理吗?答案就是使用同一个材质的物体才可以。unity中有个两种批处理方式,动态批处理和静态批处理。对于动态批处理来说,好处就是一切都是自动处理的,并且物体是可以移动的,但是限制颇多,具体有哪些限制下面会进行分析。对于静态批处理来说,好处就是自由度很高,限制条件少,但是它会占用更多的内存,并且经过批处理的物体不可以在进行移动。

首先说一下动态批处理,条件是物体使用同一个材质,并且满足对应的特定条件,unity就会自动为我们做动态批处理。

这里可以看到动态批处理中,四个物体但是只占用了三个DrawCall,就是unity进行了动态批处理,两个cube只占用了一个DrawCall。

下面说下动态批处理限制:

  • 顶点属性最大限制900,
  • 使用lightmap的物体不行进行批处理
  • 使用MultiplePass的shader也不会进行批处理
  • 接受实时阴影的物体也不会进行批处理

下面说下静态批处理, 静态批处理前提当然也是使用了同一个材质,然后就是讲对应的对象设置为static:

这时你会发现DrallCall变为1了,这就是静态批处理的作用,但是这时候你会发现VBO Total比刚才大了,这就是静态批处理坏处,通过内存来换取性能,下面我们看下官方的解释:

如果在静态批处理前有一些物体共享了相同的网格(例如这里的两个箱子),那么每一个物体都会有一个该网格的复制品,即一个网格会变成多个网格被发送给GPU。在上面的例子看来,就是VBO的大小明显增大了。如果这类使用同一网格的对象很多,那么这就是一个问题了,这种时候我们可能需要避免使用静态批处理,这意味着牺牲一定的渲染性能。例如,如果在一个使用了1000个重复树模型的森林中使用静态批处理,那么结果就会产生1000倍的内存,这会造成严重的内存影响。

关于合批

1.动态批处理

如果动态物体共用着相同的材质,那么Unity会自动对这些物体进行批处理。动态批处理操作是自动完成的,并不需要你进行额外的操作,你可以在buildsetting中设置他。

动态批处理是消耗2倍的内存来提升显示的速度,也就是空间换时间,如果内存消耗过大,需要考虑时间和空间的平衡。

如果发现动态批处理后DC并没有减少,你可以检查以下方面:

<1>批处理动态物体需要在每个顶点上进行一定的开销,所以动态批处理仅支持小于900顶点的网格物体。

<2>如果你的着色器使用顶点位置,法线和UV值三种属性,那么你只能批处理300顶点以下的物体;如果你的着色器需要使用顶点位置,法线,UV0,UV1和切向量,那你只能批处理180顶点以下的物体。请注意:属性数量的限制可能会在将来进行改变。

<3>不要使用缩放尺度(scale)。分别拥有缩放尺度(1,1,1)和(2,2,2)的两个物体将不会进行批处理。

<4>统一缩放尺度的物体不会与非统一缩放尺度的物体进行批处理。使用缩放尺度(1,1,1)和 (1,2,1)的两个物体将不会进行批处理,但是使用缩放尺度(1,2,1)和(1,3,1)的两个物体将可以进行批处理。

<5>使用不同材质的实例化物体(instance)将会导致批处理失败。

<6>拥有lightmap的物体含有额外(隐藏)的材质属性,比如:lightmap的偏移和缩放系数等。所以,拥有lightmap的物体将不会进行批处理(除非他们指向lightmap的同一部分)。

<7>多通道的shader会妨碍批处理操作。比如,几乎unity中所有的着色器在前向渲染中都支持多个光源,并为它们有效地开辟多个通道。

2.静态批处理

只要物体不移动,并且拥有相同的材质,那么就可以进行静态批处理。因此,静态批处理比动态批处理更加有效,你应该尽量低使用它,因为它需要更少的CPU开销。

为了更好地使用静态批处理,你需要明确指出哪些物体是静止的,并且在游戏中永远不会移动、旋转和缩放。想完成这一步,你只需要在检测器(Inspector)中将Static复选框打勾即可,如下图所示:

使用静态批处理操作需要2倍的内存开销来储存合并后的几何数据。

3.减少实时光和阴影

同样的设置,但是如果你将灯光的阴影效果打开,你会发现DrawCall大幅增加:

所以在项目中,如果想让场景更加完美,可以使用lightmap满足你想要的阴影效果。

综上所述就是要对图集进行和层级处理要做好整体规划,尽量将材质纹理合并,对于灯光的根据当前情况做好相应处理。

四、 UI优化Drawcall 与 Overdraw.

降低Drawcall, 主要是通过UGUI内部智能排序完成. 因此我们的优化方向主要是不要打乱UGUI排序的判定依据.
能否排序的两个要点:

1. Z轴是否为0,

2. UI是否有重叠

优化点:

 1. Z轴归零 

 对象实例化后, Z轴不为0. 代码中强制为0.
原理: Z轴不为0的UI, UGUI 不会对其自动排序.

2. 头像UI去除Mask, 降低DrawCall与Overdraw

使用Custom Image 直接截取其UV 值, 而不是通过 Mask 对其图片进行截取.
不但将降低了Mask 的DrawCall,  而且图形在网格部分保证了不会出现重叠导致UGUI 对其排序产生影响.

3. 优化会频繁Active 的界面

在购买完英雄后, 会覆盖变灰的UI. 节点Recruited 会设置为True. 
现在将该节点长设为True, 然后透过透明度将该节点隐藏. 可提升设置该节点被设置为Active后照成的UI 合批中断与SetActive本身的消耗.

4.  Image 使用PolygonImage 代替

对Buttom 的Image 使用PolygonImage 代替,因为尽管这个Image不显示. 但还是会产生DrawCall. 
   使用PolygonImage可以不产生UI 的 Mesh , 这样完全不产生任何渲染消耗

5.    FillCenter 勾选去除.

对头像边框的Image控件中的FillCenter 勾选去除.
由于9宫格头像边框中间是透明的, 因此中间部分不用渲染, 效果是一样的. 这样可以显著降低其Overdraw

6.  Raycast Target 勾选去除.

对不参与交互的Image中的Raycast Target 勾选去除.

7. 回收减少UI 对象的创建数量

回收角色对象上的UI,  减少该类对象的创建数量, 比如选人的特效

Unity优化之减少Drawcall相关推荐

  1. 【摘录】UNITY优化-有关骨骼数量的上限问题

    [摘录]UNITY优化-有关骨骼数量的上限问题 1.顶点性能 一般来说,如果您想在iPhone 3GS或更新的设备上每帧渲染不超过40,000可见点,那么对于一些配备 MBX GPU的旧设备(比如,原 ...

  2. Unity优化之Graphics相关

    Unity优化之GraphicsCamera相关 // 像素px.分辨率.ppi.dpi.dp(dip) 屏幕尺寸(Screen Size): 屏幕对角线的长度.iPhone5屏幕尺寸为4英寸.iPh ...

  3. unity优化冷启动时间/加载时间总结

    本文一部分博主并未实践过,只是做一个总结,如有错误,请指正 目录 一.概念了解 二.优化目的 1.保证游戏流畅度的基础上DrawCall越小越好 2.Statistics统计面板参数 3.打包编译 三 ...

  4. Unity优化总结(持续更新)

    工欲善其事,必先利其器.优先利用性能分析工具快速找出性能瓶颈,从瓶颈入手分析性能问题产生原因,可以事半功倍. 尽量减少占用的内存(资源体积)和CPU(计算量),首先着重减少总量才能更好的进行后续细节的 ...

  5. Unity优化方面的一些小总结

    前言: 我做了3年的Unity了,但是却没有深入优化模块的内容,只能怪自己做项目的时候做的内容太杂乱了.去面试的时候被面试官问道优化方面的内容的时候自己一脸懵逼.本来有机会去MK的,让自己错失了. 所 ...

  6. 复习Unity优化技巧

    复习Unity优化技巧 一.引言 新的格局 有推荐的吗? 二.优化 第一章.优化图形性能 1.分析: A.GPU通常受填充率或者内存带宽制约. 填充率 渲染管线 B.CPU 通常受到需要渲染的批次数的 ...

  7. Unity3d面向英特尔 x86 平台的 Unity* 优化指南: 第 2 部分

    目录 优化 脚本优化 脚本视锥剔除和协同例程 智能内存管理 缓存频繁使用的对象和组件 使用 Unity 物理系统的最佳实践 禁用完全透明对象 返回至教程第 1 部分: 面向英特尔 x86 平台的 Un ...

  8. Unity3d面向英特尔® x86 平台的 Unity* 优化指南: 第 3 部分

    目录 编辑器优化 遮挡剔除 LOD:细节级别 阴影 使用一个摄像头 渲染队列排序 光照贴图 针对复杂模型,使用简单的碰撞器代替网格碰撞器 返回至第 2 部分教程: 面向英特尔® x86 平台的 Uni ...

  9. Unity优化 详谈GetComponent

    文章转自:http://www.manew.com/thread-104010-1-1.html?_dsign=aaa7cc41 0x00 前言 在很长一段时间里,Unity项目的开发者的优化指南上基 ...

最新文章

  1. 生产订单结算KKS1常见错误
  2. 混合云存储开启企业上云新路径--阿里云混合云备份容灾方案发布 1
  3. Angular @Hostbinding工作原理
  4. KnockoutJS + My97DatePicker
  5. 浅谈MVP与Model-View-ViewModel(MVVM)设计模式
  6. 计算机网络基础-目录
  7. Spark源码分析:多种部署方式之间的区别与联系
  8. 微信小程序模板消息群发解决思路
  9. 图解Android - Android GUI 系统 (1) - 概论
  10. php开源qq群,QQ群免IDKEY加群PHP源码
  11. vue 第一天(基本的结构)
  12. VS2015+WDK10+Win10 Win7以上系统驱动发开环境搭建
  13. 外企常用英语词汇或短语
  14. 6-1 插入法建立有序链表
  15. 关于Unity下载资源默认下载到C盘,更改到其他盘的方法
  16. Daily record-July
  17. i5 11320h怎么样 相当于什么水平
  18. 网站的seo以及它和站长工具之间的秘密
  19. iWO联通3G详单及套餐使用情况查询工具)更新至v0.8.2
  20. 弹出式窗口及网页对话框

热门文章

  1. Python导出为exe程序
  2. C语言网络编程(3)— 通过DNS连接到百度
  3. 求生之路2怎么联机显示服务器,求生之路2联机如何创建服务器?
  4. 小米集团发布任命文件 小米AIoT战略委员会正式成立
  5. jsp+dialog弹不出来_解决JSP页面无法使用EasyUI里面class=easyui-dialog的问题
  6. DotNetty完全教程(十)
  7. 区块链世界的“基建”之问——为行业的“黄金十年”做好准备了么?
  8. 5个Word简历模板下载网站,免费模板超多,下载超方便
  9. java之初识集合框架
  10. 如何快速掌握Go语言(落地版)