Unity:Resource Merging、Static Batching、Dynamic Batching、GPU Instancing

 以下总结可能会出现阐述有误的地方,还望指出。

一、前提知识引入

这几个前提知识还是蛮重要的,理解了老半天了。

该部分参考自:

图形渲染及优化—Batch-腾讯游戏学堂

Unity优化----drawcall系列 - 走看看

Batch, Draw Call, Setpass Call - 知乎

Unity - DrawCall, Batch, SetPassCall区别_Jave.Lin的博客-CSDN博客

GPU架构和渲染 - 知乎

浅谈Draw Call和Batch的区别_产品老唐的博客-CSDN博客_batch和drawcall

Unity中SetPassCall, Batches的区别,它们与Draw call 的关系_q568360447的博客-CSDN博客_batches unity

1.Draw call与Batch

1)定义:Draw call是CPU调用图形接口在屏幕上绘制对应的东西。(着重点关注接口调用)

Draw call严格意义上指调用一次渲染API的绘制接口(如:Direct3D的DrawPrimitive/DrawIndexedPrimitive,OpenGL的glDrawArrays/glDrawElements)都算作一次Draw Call,但是对于Unity而言,它可以多个Draw Call合并成一个Batch去渲染。

而Batch翻译成中文一般我们称之为“批次”。我们经常用引擎每帧提交的批次数量来作为衡量渲染压力的指标。向GPU提交使用相同渲染状态的一定数量的三角形的行为为一个渲染批次。从API调用的角度来看,Batch和Draw call是等价的,但是在游戏引擎中他们的实际意义是不一样的:Batch一般指代经过打包之后的Draw call。

2)Command Buffer

CPU和GPU之间的数据传输是一个异步的过程,类似于服务器和客户端之间的数据传输。CPU和GPU构造了一种生产者/消费者异步处理模型。CPU生产“命令”,GPU消费“命令”,通过这种关系CPU就可以将数据和行为传输到GPU,GPU来执行对应动作。

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

下图为渲染指令在CPU和GPU中的流转过程。

(主要性能消耗的地方)命令从Runtime到Driver的过程中,CPU要发生从用户模式到内核模式的切换。模式切换对于CPU来说是一件非常耗时的工作,所以如果所有的API调用Runtime都直接发送渲染命令给Driver,那就会导致每次API调用都发生CPU模式切换,这个性能消耗是非常大的。Runtime中的Command Buffer可以将一些没有必要马上发送给Driver的命令缓冲起来,在适当的时机一起发送给Driver,进而在Video Card执行。以这样的方式来寻求最少的CPU模式切换,提升效率。

3)将Draw Call串起来

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

a.准备渲染对象,然后将渲染对象从硬盘加载到内存,然后从内存加载到显存,进而方便GPU高速处理(调用draw call的准备)
b.设置每个对象的渲染状态,也就是设置对象的材质、纹理、着色器等(调用draw call的准备,即setpass call)
c.输出渲染图元,然后向GPU发送DrawCall命令,并将渲染图元传递给GPU(真的调用了draw call接口)


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

2.SetPass Call

Set Pass Call代表渲染状态切换,主要出现在材质不一致的时候,进行渲染状态切换。我们知道一个batch包括,提交vbo,提交ibo,提交shader,设置好硬件渲染状态,设置好光源属性等(注意提交纹理严格意义上并不包括在一个batch内,纹理可以被缓存并多帧复用)。

最简单的理解 SetPassCall :在绘制此 Pass 前,需要设置的所有状态配置、或是BUFFER设置,都算是 SetPassCall 的内容,或是叫:SetGPUDataBeforeDraw 会更适合理解(在绘制前设置GPU数据,这些数据包括渲染系统,如:DX 或是 OpenGL 的状态值,或是 Buffer 数据)

所以 Unity 多了个:SetPassCall,SetPassCall = SetStateBeforeDraw

举例:如果一个batch和另一个batch使用的不是同种材质或者同一个材质的不同pass,那么就要触发一次set pass call来重新设定渲染状态。例如,Unity要渲染20个物体,这20个物体使用同种材质(但不一定mesh等价),假设两次dynamic batch各自合批了10个物体,则对于这次渲染,set pass call为1(只需要渲染一个材质),batch为2(向GPU提交了两次VBO,IBO等数据)。

 

3.小结

真正造成开销较大的地方,第一个在于在于切换渲染状态,第二在于整理和提交数据。在真正的实践过程当中,可以不用过于介意Draw call这个数字(因为没有提交数据或者切换渲染状态的话,其实多来几个draw call没什么所谓),但是Set Pass Call和Batch两个数字都要想办法降低。由于二者存在强相关性,那么通常降低一个,就一并可以降低第二个。

总的来说:Set pass call 的数值低,Draw call 不一定低,但是SetPass call 的数值高的花,Draw call 一定高。我们的优化通常也是从减少Set pass call 的数量和减少Draw Call的数量来做优化。

而批量渲染是通过减少CPU向GPU发送渲染命令(DrawCall)的次数,以及减少GPU切换渲染状态的次数,尽量让GPU一次多做一些事情,来提升逻辑线和渲染线的整体效率。但这是建立在GPU相对空闲,而CPU把更多的时间都耗费在渲染命令的提交上时,才有意义。

二、Resource Merging

该部分参考自:Unity使用UGUI制作图集_画个小圆儿的博客-CSDN博客_ugui 生成图集

Unity图集简介及使用_z2014z的博客-CSDN博客_unity 图集

1.原理

主要就是图集合并,如果一些模型引用的材质除了Texture其他参数都相同,我们可以将这些模型引用的Texture进行合并,将它们合并成一张更大的Texture。这样所有的模型都可以引用相同的材质,然后通过只设置一次渲染状态进行绘制了。虽然Draw call数量没有减少,但是避免了渲染状态的切换,同样达到了合批渲染的目的。

其本质上应该就是动态合批的一个典型例子。

2.注意事项

1)注意控制图集的大小,不要让图集太大,一个超级大图集的DrawCall消耗或许顶的上十几个小图集的消耗

2)尽量紧凑,大小不要超过512x512

3)Draw Call尽量少,同一个界面的小图尽量在一个图集里

4)内存管理方便,加载性能好,打开一个界面时只加载必要的图集,关闭时可以方便地释放图集

5)AssetBundle打包、热更粒度合理,不能出现“热更一个新界面,大量图集都需要热更”的情况

6)设计UI时要考虑重用性,将边框、按钮等共享资源,放在1~3张大图集中,作为重用图集;

7)其它非重用UI按照功能模块进行划分,每个模块使用1~2张图集,作为功能图集;

8)对于部分UI,如果同时用到功能图集与重用图集,但是其功能图集剩下的“空位”较多,则可以将重用图集中用到的元素单独拎出来,合入功能图集中,让UI只依赖于功能图集。通过一定的数据冗余,来实现性能的提升;

三、Static Batching

参考自:

Batch, Draw Call, Setpass Call - 知乎

【Unity游戏开发】静态、动态合批与GPU Instancing - 知乎

1.原理:

静态合批是勾选Static,Unity在Build的时候,会自动下生成合并的网格,并将它以文件形式存储合并后的数据,这样在当场景被加载时,一次性提交整个合并模型的顶点数据,根据引擎的场景管理系统判断各个子模型的可见性。然后设置一次渲染状态,调用多次Draw call分别绘制每一个子模型。

Static Batching将静态物体集合成一个大号vbo提交,但是只对要渲染的物体提交其IBO。这么做不是没有代价。比如说,四个物体要静态批次合并前三个物体每个顶点只需要位置,第一套uv坐标信息,法线信息,而第四个物体除了以上信息,还多出来切线信息,则这个VBO会在每个顶点都包括所有的四套信息,毫无疑问组合这个VBO是要对CPU和显存有额外开销的。

2.要求

要求每一次Static Batching使用同样的material,但是对mesh不要求相同。并且强调手动设置。

3.性能分析

静态合批并不减少Draw call的数量但是在编辑器时由于计算方法区别Draw call数量是会显示减少了的,但是由于我们预先把所有的子模型的顶点变换到了世界空间下,并且这些子模型共享材质,所以在多次Draw call调用之间并没有渲染状态的切换减少了Set Pass Call ,渲染API会缓存绘制命令,起到了渲染优化的目的(感觉可以理解为多个Draw 共用一个Set Pass Call)。另外,在运行时所有的顶点位置处理不再需要进行计算,节约了计算资源。

关于是否减少Draw Call数量目前存在疑问,可参考下文的评论部分:关于静态批处理/动态批处理/GPU Instancing /SRP Batcher的详细剖析 - 知乎

好像是在较新的Unity版本当中已经能实现静态合批减少Draw Call了。

四、Dynamic Batching

该部分参考自:Unity基础知识回顾(一)DrawCall、Batch、SetPassCall的区别_大鲸鱼锅锅的博客-CSDN博客

 1.原理:

动态合批是专门为优化场景中共享同一材质的动态GameObject的渲染设计的。目标是以最小的代价合并小型网格模型,减少Drawcall。

动态合批的原理也很简单,在进行场景绘制之前将所有的共享同一材质的模型的顶点信息变换到世界空间中,然后通过一次Draw call绘制多个模型,达到合批的目的。模型顶点变换的操作是由CPU完成的,所以这会带来一些CPU的性能消耗。

2.要求

a.必须使用相同材质,且材质只能有一个pass;使用多个pass的Shader是绝对不会被合批。因为Multi-pass Shader通常会导致一个物体要连续绘制多次,并切换渲染状态。这会打破其跟其他物体进行Dynamic batching的机会。

b.mesh只能有900个顶点数据;如果我们使用了顶点坐标,法线,UV,那么就只能最多300个顶点;如果我们使用了UV0,UV1,和切线,又更少了,只能最多150个顶点。

c.transform的scale属性不能为负;如果两个模型缩放大小不同,不能被合批的,即模型之间的缩放必须一致。

d.动态批处理默认是关闭的,需要手动开启:Project Setting—Player—勾上Dynamic Batching;

e.如果他们有Lightmap数据,必须相同的才有机会合批。

f.延迟渲染是无法被合批

3.性能分析

Set Pass Call和draw Call都得到了减少。

五、GPU Instancing

1.原理

简单概括一句话就是:传递一个对象的Mesh,指定其绘制次数和材质,Unity就会为我们在GPU的统一/常量缓冲区开辟好必要的缓冲区,然后以我们指定的材质对Mesh进行我们指定次数的渲染,这样就可以达成一次Drawcall、一次Set Pass Call绘制海量对象的目的。

对于Unity的GPU Instance来说,从数据处理的角度其实也可以分为两类:

  • 第一种是使用了 支持并启用了GPU Instance的Shader 的材质的物体在进行渲染时(例如我们通过Gameobject.Instantiate实例化了100w个正方体),Unity会对所有渲染对象进行特殊处理,为所有的渲染目标在GPU的常量缓冲区(Constant Buffer中)准备各种缓冲区(顶点数据缓冲区,材质数据缓冲区,transform矩阵数据缓冲区等)
  • 第二种是我们自己调用GPU Instance API进行实例绘制,那么Unity只会根据我们所传递的参数为其准备顶点缓冲区,材质数据缓冲区,对于矩阵数据缓冲区或者其他自定义数据是不提供的,也就需要我们自己通过ComputeBuffer来传递这些数据,然后在Shader中根据instanceId进行处理。例如我们使用GPU Instance API绘制100w个三角形,那么Unity会控制GPU后端为我们准备一个能容纳300w个顶点的缓冲区和一个材质数据缓冲区。这一部分可参考:U3D优化批处理-GPU Instancing了解一下 - 知乎

2.要求

材质球虽然相同但材质球属性可以各自有各自的区别。

3.性能分析

1)传统渲染方式(无合批情形):绘制多少个对象就要整理和传递多少次数据,其中整理和传递数据的过程消耗极大,多数为性能瓶颈

2)GPU Instance:只用从CPU往GPU传递一次数据,把一些原本在CPU的运算转移到GPU当中去,大大提高了渲染效率。

4.与合批的优先级比较

1)Static batching的优先级要比GPU Instancing的优先级高,如果一个GameObject被标记为static物体并且在Build阶段成功地执行了静态合批,那么如果这个物体还要使用Instancing Shader渲染的话,Instancing会失效。

2)Dynamic batching的优先级要低于Instancing。如果一个GameObject使用Instancing渲染的话,那么对于它的Dynamic batching会失效。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值