游戏优化3——批处理的优势

它描述了将大量任意数据块组合在一起并将它们作为单个大数据块进行处理的过程。

在某些情况下,批处理的对象指的是网格、顶点、边、UV坐标和其他用于描述3D对象的不同数据类型的大集合。然而,该术语也可以简单代表批处理音频文件、精灵、纹理文件和其他大数据集的行为。

3.1 Draw Call

Draw Call只是一个从CPU发送到GPU中用于绘制对象的请求。
Draw Call是这一过程的通用行业术语,但在Unity中有时也称为SetPass Call,因为一些底层方法也命名为SetPass Call。可以将Draw Call理解为初始化当前渲染过程之前的配置选项。本书剩余部分将其统称为Draw Call。在请求Draw Call之前,需要完成一些工作。首先,网格和纹理数据必须从CPU内存(RAM)推送到GPU内存(VRAM)中更改渲染状态是一个耗时的过程。例如,如果将渲染状态设置为使用一个蓝色纹理文件,然后要求它渲染一个巨大的网格,那么渲染会非常快,整个网格都显示为蓝色。然后,可以再渲染9个完全不同的网格,它们都显示为蓝色,因为没有改变所使用的纹理。然而,如果想用10种不同的纹理渲染10个网格,就将花费更长的时间。这是因为在为每个网格发送Draw Call指令之前,需要使用新的纹理来准备渲染状态。请求改变渲染状态的次数越少,Graphics API越能更快地处理请求。一旦配置了渲染状态,CPU就必须决定绘制哪个网格,使用什么纹理和着色器,以及基于对象的位置、旋转和缩放(这些都在一个名为变换的4×4矩阵中表示,这正是Transform组件名字的由来)决定在何处绘制对象,然后发送指令到GPU以绘制它。为了使CPU和GPU之间的通信保持活跃,新指令被推入一个名为Command Buffer的队列中。这个队列包含CPU创建的指令,以及GPU每次执行完前面的命令后从中提取的指令。

批处理提升此过程的性能的诀窍在于,新的Draw Call不一定意味着必须配置新的渲染状态。如果两个对象共享完全相同的渲染状态信息,那么GPU可以立刻开始渲染新对象,因为在最后一个对象完成渲染之后,还维护着相同的渲染状态,这消除了由于同步渲染状态而浪费的时间,也减少了需要推入Command Buffer中的指令数,减少了CPU和GPU上的工作负载。

3.2 材质和着色器

所以,如果想要最小化渲染状态修改的频率,可以减少场景中使用的材质数量。这将同时提升两个性能;CPU每帧将花费更少的时间生成指令,并传输给GPU;而GPU不需要经常停止,重新同步状态的变更。

剩余8个批处理用于绘制8个对象。对于每个对象,Draw Call需要使用材质的属性准备管线渲染,并请求GPU以对象当前的变换设置渲染给定网格。给每个对象提供不同的纹理文件用于渲染,来确保材质是唯一的。因此,每个网格需要不同的渲染状态,所以这8个网格都需要各不相同的Draw Call。

3.3 Frame Debugger

要打开Frame Debugger,在主窗口中选择Window | Frame Debugger或者在Profiler的Rendering区域中单击Breakdown View Options中的Frame Debugger按钮,这两个操作都可以打开Frame Debug窗口。 单击Frame Debug窗口的Enable按钮,可以观察场景是如何构建的,每次执行一个Draw Call。

3.4 动态批处理

给定网格执行动态批处理的要求: 所有网格实例必须使用相同的材质引用。 只有ParticleSystem和MeshRenderer组件进行动态批处理。SkinnedMeshRenderer组件(用于角色动画)和所有其他可渲染的组件类型不能进行批处理。 每个网格至多有300个顶点。 着色器使用的顶点属性数不能大于900。 所有网格实例要么使用等比缩放,要么使用非等比缩放,但不能两者混用。 网格实例应该引用相同的光照纹理文件。 材质的着色器不能依赖多个过程。 网格实例不能接受实时投影。

3.4.1 顶点属性

顶点属性只是网格文件中基于每个顶点的一段信息,每一段通常表示为一组浮点数。它包括但不限于顶点位置(相对于网格的根),法线向量(一个从对象表面指向外面的向量,通常用于光照计算),一套或多套纹理UV坐标(用于定义一张或多张纹理如何包裹网格),甚至可能包括每个顶点的颜色信息只有着色器使用的顶点属性总数小于900的网格才会进行动态批处理。

在Project窗口中找到MeshFilter组件,在Inspector窗口的Preview子区域中查看verts值。900个属性预算就消耗得越多,从而减少了网格允许拥有的顶点数量,这些顶点不再能用于动态批处理。例如,简单的漫反射着色器只能给每个顶点使用3个属性:位置、法线和一组UV坐标。因此,动态批处理可以使用这个着色器来支持总共有300个顶点的网格。然而,在更复杂的着色器中,每个顶点需要5个属性,只能支持不超过180个顶点的网格的动态批处理。另外,请注意,即使在着色器中每个顶点使用不到3个顶点属性,动态批处理仍然只支持最多300个顶点的网格,因此只有相对简单的对象才适合动态批处理。 这些限制正是场景开启动态批处理之后,尽管所有对象共享相同的材质引用,也仅节省3个Draw Call的原因。Unity自动生成的立方体网格仅包含8个顶点,每个顶点都带有位置、法线和UV数据,总共24个属性,远低于300个顶点和900个顶点属性的上限。然而,自动生成的球体包含515个顶点,因此总共有1545个顶点属性,明显超过300个顶点和900个顶点属性的限制,所以不能动态批处理。 如果单击Frame Debugger中的一个Draw Call项,就会显示标签为“Why this draw call can't be batched with the previous one(这个Draw Call为什么不能与前一个Draw Call批处理)”的部分。

3.4.2 网格缩放

对象应使用统一的等比缩放,或每个对象都有不一样的非等比缩放,才能包含在动态批处理中。等比缩放意味着缩放向量的3个分量(x,y,z)都是相同的(但不同的网格不需要满足这个条件),非等比缩放意味着这些值中最少有一个和其他是不同的,而属于这两组的对象会放到两个不同的批处理中。

接下来举例说明。假设有以下4个对象:A以(1,1,1)缩放,B以(2,1,1)缩放,C以(2,2,1)缩放,D以(2,2,2)缩放。对象A和D是等比缩放,因为3个分量的值都一样。尽管A、D两个网格的缩放比例不同,但它们依然是等比缩放,会放在同一个动态批处理中。

3.4.3 动态批处理总结

如果阻止两个对象动态批处理的唯一条件是,它们使用了不同的纹理,就应该花点时间和精力合并纹理(通常称为图集),并重新生成网格UV,以便进行动态批处理。这可能会牺牲纹理的质量,或者纹理文件会变大(这是需要知道的缺点,第6章深入讨论GPU内存带宽时详细论述),但这是值得的。 动态批处理可能对性能造成损害的唯一情况是,设置一个场景,其中有数百个简单对象,而每个批处理中只有几个对象。在这种情况下,检测和生成这么多小批处理组的开销成本可能比为每个网格单独执行Draw Call所节省的时间还要多。即便如此,一般也不会发生这种情况。

当这些意外发生时,并没有发出警告,只是指出在做出修改后Draw Call的数量在增加,性能也进一步下降了。为了使场景中动态批处理的数量保持合适的水平,需要连续不断地检查Draw Call数量,并观察Frame Debugger数据,以确保最新的修改不会意外取消对象的动态批处理资格。然而,与往常一样,如果证实这会造成性能瓶颈,那么仅需要关心Draw Call性能。

 

3.5 静态批处理

它只处理标记为Static的对象,因此命名为静态批处理。

3.5.1 Static标记

静态批处理只能应用于开启static标记的对象,具体而言是Batching Static子标记(这些子标记称为StaticEditorFlags)。单击GameObject的Static选项旁边的小下三角,会出现一个StaticEditorFlags下拉框,该框可以为不同的Static处理过程修改对象的行为。 该标记的一个明显的副作用是不能修改对象的变换。因此,任何想要使用静态批处理的对象都不能通过任何方式移动、旋转和缩放。

3.5.2 内存需求

静态批处理在工作时,将所有标记为Static的可见网格数据复制到一个更大的网格数据缓冲中,并通过一个Draw Call传到管线渲染中,同时忽略原始网格。静态批处理在工作时,将所有标记为Static的可见网格数据复制到一个更大的网格数据缓冲中,并通过一个Draw Call传到管线渲染中,同时忽略原始网格。

这些静态批处理的副本会消耗额外的内存,其数量等于网格的数量乘以原始网格的大小。通常,渲染一个、十个或一百万个相同对象,消耗的内存是相同的,因为它们都引用相同的网格数据。

3.5.3 材质引用

3.5.4 静态批处理的警告

它只处理标记为Static的对象,因此命名为静态批处理。 静态批处理系统有自己的要求: 顾名思义,网格必须标记为Static(具体而言是Batching Static)
 

如前所述,共享材质引用是减少渲染状态变更的一种方式,因此该要求显而易见。Draw Call减少了,但不能直接在Stats窗口中看到,要在运行时才能看到。 在运行时向场景中引入标记为Batching Static的对象,不能自动包含进静态批处理中。

 

只有深刻理解了这些批处理系统以及它们的工作原理,才能帮助我们确定这项特性可以在何时何地使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值