简单来说,绘制调用是呈现屏幕所需的开销,以便减少消耗并提高性能。
什么是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进行大量计算并影响游戏
注意:
由于场景中有100个gameobject,它们具有完全相同的材质,因此这100个物体很可能通过unity Batching机制组合成一个batch。 因此,用“Batches”表示Unity的渲染性能是不合适的,它只反映了出现在场景中所需的批处理对象的数量。 那么,可以用“Draw calls”记述吗? 答案不合适。 3358www.Sina.com/,请求中包含渲染对象的所有顶点参数、三角面、索引值、基元个数等,每一个“Draw calls”是CPU发送个GPU的一个渲染请求,http://www.Sina.com
进入我的上篇博文【U3D】shader编程世界简要介绍过shader,不懂的童鞋可以去看看。 接下来介绍上一个问题,场景中有一个gameobject,希望展现酷炫的效果。 那个材质带有很多特定的材质球。 为了实现这一效果,Shader可能包含很多Pass。 在GPU即将执行过程之前,会发生“设置过程调用”,因此在描述渲染性能开销方面,“设置过程调用”更有说服力。
三、如何优化DrawCall? 1 .渲染顺序:处理写真集、材质、层次如果你想看看这些是如何优化的,你需要了解他们的工作原理。 以NGUI为例,说明一下他们的关系吧。
NGUI主要由三个主要模块组成: ui面板、ui构件和UIDrawcall。 其中ui面板用于管理ui构件控件和UIDrawcall,ui构件是所有组件的基类。
NGUI框架具有一个包含所有面板的静态列表,每个面板下都存储自己的ui构件和UIDrawCall。 也就是说,每次绘制时,panle都会遍历它下面的所有级别的子对象,直到搜索结束;遇到新Panel时,它会跳出当前分支,继续搜索所有分支,因此在实际运行中,每次都是这样此时,如果多个连续UI小部件使用的材质和纹理匹配,则共享一个绘制调用。 让我给你看看具体情况。
这是使用不同的材质和纹理时的情况
这是使用相同的材质和纹理时的情况
所以,很多人并不是认为有相同的写真集会占用相同的DrawCall,而是通过上面的照片分析发现这个请求并不会占用过多的消耗就可以了。 否则,再次调用DrawCall。 另外,需要注意的是,虽然使用了相同的写真集、材质,但真正消耗
另外,还需要注意的是,如果在panel下有动态物体,则为了实现某种效果,需要UI的位置移动。 在这种情况下,最好是动态分离。 因为只要UI在面板下移动,panle就会对在清空之前保存的UI构件和UIDrawCall重新进行渲染,从而浪费性能。 一些同学会说,这是不是增加了DrawCall
因此,要在界面中布局UI,必须规划图形集和标高以减少绘制调用次数。
Shader读取指令并通知相应的渲染通道(Pass)
染顺序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 不会对其自动排序.
使用Custom Image 直接截取其UV 值, 而不是通过 Mask 对其图片进行截取.
不但将降低了Mask 的DrawCall, 而且图形在网格部分保证了不会出现重叠导致UGUI 对其排序产生影响.
在购买完英雄后, 会覆盖变灰的UI. 节点Recruited 会设置为True.
现在将该节点长设为True, 然后透过透明度将该节点隐藏. 可提升设置该节点被设置为Active后照成的UI 合批中断与SetActive本身的消耗.
对Buttom 的Image 使用PolygonImage 代替,因为尽管这个Image不显示. 但还是会产生DrawCall.
使用PolygonImage可以不产生UI 的 Mesh , 这样完全不产生任何渲染消耗
对头像边框的Image控件中的FillCenter 勾选去除.
由于9宫格头像边框中间是透明的, 因此中间部分不用渲染, 效果是一样的. 这样可以显著降低其Overdraw
对不参与交互的Image中的Raycast Target 勾选去除.
7. 回收减少UI 对象的创建数量回收角色对象上的UI, 减少该类对象的创建数量, 比如选人的特效