动态批处理、静态批处理、GPU Instancin 和SRP Batcher

  • 批处理原理
  • 批处理设置
  • Frame Debugger
  • 1. 动态批处理
    • 动态批处理使用场景
    • 动态批处理优势:
    • 动态批处理缺点:
    • 动态批处理要求:
    • 动态批处理总结:
  • 2. 静态批处理
    • 静态批处理的缺点:
    • 静态批处理的要求:
    • 静态批处理的内存需求
    • 在运行时实例化静态网格
    • 静态批处理总结
  • 3. GPU Instancin
    • GPU Instancin应用场景
    • GPU Instancin优点
    • GPU Instancin缺点
  • 4. SRP Batcher
  • 总结
  • 参考资料

批处理原理

批处理:在3D图形中,批处理是一个非常通用的术语,描述了将大量任意数据块组合在一起并将它们作为单个大数据块进行处理的过程。本文中的批处理通常指用于批处理网格数据的机制。
动态批处理和静态批处理这两种方法本质上是几何体合并的两种不同形式,用于将多个对象的网格数据合并到一起,并在单一指令中渲染它们,而不是单独准备和绘制每个几何体。

很多地方模糊的说 批处理原理是是减少Draw Call,其实严格来说并不对。
其中静态批处理并没有减少DC数量。那静态批处理是优化的什么呢?
首先DC是什么就不赘述了,DC之所以造成性能瓶颈是因为CPU每次为提交GPU所准备的数据和设置渲染状态而消耗了太多的时间。所以如果渲染状态不变,在多次DC调用之间并没有渲染状态的切换,渲染API(Command Buffer)会缓存绘制命令,起到了渲染优化的目的 。

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

动态批处理的工作原理:将所有游戏对象顶点转换到 CPU 上的世界空间,所以仅在该工作小于进行绘制调用的情况下,才有优势。绘制调用的资源需求取决于许多因素,主要是使用的图形 API。例如,对于游戏主机或诸如 Apple Metal 之类的现代 API,绘制调用的开销通常低得多,通常动态批处理根本没有优势。------引自2018.4官方文档

静态批处理的工作原理:将静态游戏对象转换到世界空间并为它们构建一个共享的顶点和索引缓冲区。如果已启用 Optimized Mesh Data__(in the Player settings),则 Unity 会在构建顶点缓冲区时删除任何着色器变体未使用的任何顶点元素。为了执行此操作,系统会进行一些特殊的关键字检查;例如,如果 Unity 未检测到 LIGHTMAP_ON 关键字,则会从批处理中删除光照贴图 UV。然后,针对同一批次中的可见游戏对象,Unity 会执行一系列简单的绘制调用,每次调用之间几乎没有状态变化。在技术上,Unity 不会减少 API 绘制调用,而是减少它们之间的状态变化(这正是消耗大量资源的部分)。在大多数平台上,批处理限制为 64k 个顶点和 64k 个索引(OpenGLES 上为 48k 个索引,在 macOS 上为 32k 个索引)。------引自2018.4官方文档

手动组合彼此接近的游戏对象,比批处理效果更好。例如,一个带有大量抽屉的静态橱柜通常只需在 3D 建模应用程序中或者使用 Mesh.CombineMeshes 来组合成一个网格。----引自2018.4官方文档
https://docs.unity3d.com/2018.4/Documentation/Manual/DrawCallBatching.html

注意:批次不是越少越好,过大的渲染数据会给内存带宽带来压力,拉高峰值波动上限。

批处理设置

Edit->Project Settings->Player->Other Settings-> disable Static Batching and Dynamic Batching
如果没有开启,可以看看上面设置是否禁用了批处理。
如果禁用了动态批处理,那么多个共用材质的物体并没有减少dc数量,这是因为渲染状态变更的数量并没有真正减少,也没有合并网格信息。渲染管线不知道我们在重复的写入相同的渲染状态,一次一次渲染相同的网格。

Frame Debugger


这个工具直观的区分两个连续的Draw Call。很容易准确地指出给定的Draw Call渲染了哪些对象。这可以通过查看在Draw Call期间出现了多少个对象,来帮助确定是否对一组对象进行批处理。

单击Frame Debugger中的一个Draw Call项,就会显示标签为“Why this draw call can’t be batched with the previous one(这个Draw Call为什么不能与前一个Draw Call批处理)”的部分。大多数情况下,下方的解释文本说明了哪个条件没有满足(至少是它检测到的首个条件),以及有什么调试批处理行为的有用方法。

1. 动态批处理

动态批处理通过将所有物体的顶点转换为CPU上的世界空间来工作,所以它只能在渲染Draw Call的工作量小于CPU顶点转换工作量的时候,才会起到提高性能的作用。

动态批处理使用场景

  1. 渲染大量的简单网格时,使用大量外观几乎相同的简单物体时。比如:到处是石头、树木和灌木的森林。
  2. 有很多简单而常见的元素(计算机、走廊、管道等)的建筑、工厂等。
  3. 包含很多动态的非动画对象,还包含简单的几何体和粒子特效。

动态批处理优势:

  1. 批处理在运行时动态生成。
  2. 批处理中包含的对象在不同的帧之间可能有所不同,这取决于哪些网格在主摄像机视图中当前是可见的(批处理的内容是动态的)。
  3. 可以在场景中移动的对象也可以进行批处理(对动态对象有效)

动态批处理缺点:

  1. 数量过大时扛不住。
  2. 条件要求限制多(9条要求要求)

动态批处理要求:

  1. 所有网格实例必须使用相同的材质引用。
  2. 只有ParticleSystem和MeshRenderer组件进行动态批处理。SkinnedMeshRenderer组件和所有其他可渲染的组件类型不能进行批处理。
  3. 每个网格至多有300个顶点。
  4. 着色器使用的顶点属性数不能大于900。
  5. 所有网格实例要么使用等比缩放,要么使用非等比缩放,但不能两者混用。
  6. 网格实例应该引用相同的光照纹理文件。
  7. 材质的着色器不能是多Pass的。
  8. 网格实例不能接受实时投影。
  9. 整个批处理中网格索引的总数有上限,这与所用的Graphics API和平台有关,一般索引值在32~64K之间。

解释:
一. 顶点属性包含:定点位置,法线向量,UV坐标,定点颜色等。属性数据越多,900个预算消耗的越多,从而减少了网格允许拥有的定点数量。因此只有相对简单的对象才适合动态批处理。如下图,查看verts值:(只有在引擎内看到的数据为最终数量,在从建模工具中导入时会改变)

例1:cube 仅仅包含8个顶点,每个顶点有 位置、法线、UV数据,总共24个。远低于300个顶点和900个顶点上限。 可以批处理。
例2:球体包含515个顶点,总共1545个顶点属性,所以不能动态批处理。
二. 网格缩放:
等比缩放:(1,1,1) 和(2,2,2)可以合并,因为是等比缩放,虽然比例不同。
非等不缩放:(2,1,1)(2,2,1)可以合并为另一个,因为是非等比缩放。
注意:负数缩放会导致奇怪情况,这与三个值中哪个是负数无关,与负数值是否为奇数或者偶数有关。同时负数时,渲染顺序还会导致合批失败

动态批处理总结:

两个对象使用不同的纹理,最好合并纹理(通常称为图集),并重新生成网格UV,以便进行动态批处理。这虽然会牺牲纹理的质量或者文件变大,或者纹理文件会变大GPU内存带宽压力大,但这是值得的。

2. 静态批处理

unity静态批处理不是真正意义上的"静态批处理"把勾上的合并成一个mesh,而是首先unity会先按照渲染顺序动态排序以及静态批处理的可视处理。因为球没有勾选static,然后插在了4个cube中间的渲染顺序(或者说挡住了某一个cube时,处于不可视的时候),这样unity就会先渲染2次合并后的Combined Mesh,第一次渲染球前面的Combined Mesh,第二次渲染球后面的combined mesh,因为这个球插足了破坏了合批,所以就是3个batches。而更换角度后,球没有插足cube的渲染顺序,所以就不会破坏合批。(背景,不透明。渲染顺序基本稳定的,先渲染的。)

静态批处理的缺点:

  1. 只能在运行时才能看到效果
  2. 在运行时添加时无效,即使标记为Static对象。
  3. 项目后期启用会花费大量的时间启动 调整 重启 场景,以确保节省了期待节省的DC。最好在早期进行静态批处理的优化。

静态批处理的要求:

  1. 网格必须标记为“静态”。
  2. 每个被静态批处理的网格都需要额外的内存。
  3. 合并到静态批处理中的顶点数量是有上限的,并随着Graphics API和平台的不同而不同,一般为32~64K个顶点。
  4. 网格实例可以来自任何网格数据源,但是它们必须使用相同的材质引用。

静态批处理的内存需求

静态批处理将所有标记为Static的可见网格数据复制到一个更大的网格数据缓冲中,并通过一个Draw Call传到管线渲染中,同时忽略原始网格。如果所有进行静态批处理的网格都各不相同,那么与正常渲染对象相比,这不会增加内存使用量。

但是,通常渲染一万个相同对象,消耗的内存是相同的,因为引用相同的网格数据。在这种情况下,对象之间的唯一区别是每个对象的变换。然而静态批处理的引用会丢失,所以使用静态批处理渲染1000个相同的树对象,消耗的内存是不使用的1000倍。会导致严重的内存消耗。

在运行时实例化静态网格

如果需要动态实例化,或者使用叠加方式加载场景,可以使用StaticBatchUntility.Combine() 两个重载形式:

  1. 需要提供根GameObject,该对象中所有带网格的子GameObject对象都会转换到新的静态批处理组中(如果使用了多个材质,就会创建多个组)。
  2. 需要提供GameObject列表和一个根GameObject,该重载形式会自动将列表中的对象作为根对象的子节点,以相同的方式生成新的静态批处理组。
    注意:如果有许多顶点合并,性能开销会非常昂贵。并且它无法和任何预先存在的静态批处理合并

静态批处理总结

缺点:强大但危险的工具。很容易造成内存消耗(导致应用程序崩溃)。还需要大量手动调整和配置,以确保正确生成批处理。
优点:可以使用不同形状和巨大尺寸的网格,这是动态批处理无法提供的。

3. GPU Instancin

在使用相同材质球、相同Mesh(预设体的实例会自动地使用相同的网格模型和材质)的情况下,Unity会在运行时对于正在视野中的符合要求的所有对象使用Constant Buffer[5]将其位置、缩放、uv偏移、lightmapindex等相关信息保存在显存中的“统一/常量缓冲器”[6]中,然后从中抽取一个对象作为实例送入渲染流程,当在执行DrawCall操作后,从显存中取出实例的部分共享信息与从GPU常量缓冲器中取出对应对象的相关信息一并传递到下一渲染阶段,与此同时,不同的着色器阶段可以从缓存区中直接获取到需要的常量,不用设置两次常量。

不严谨的人话:GPU多例化技术原理简单,就是先把数据提交到显存中,然后在绘制时,修改一些属性,就能达到避免N次设置渲染状态造成的性能消耗。不同的物体靠参数属性的不同进行区分。

GPU Instancin应用场景

在某些场合是我已知唯一的处理优化手段。比如几千棵树,几万株草的草地。

GPU Instancin优点

GPU Instancing在减少DC的同时,也避免了内存的爆炸。
2018版本已经支持GI,不用再手写了。

GPU Instancin缺点

操作过多。如果使用自定义Shader,则需要修改代码
由于constant buffer的限制(默认是500),DC数量也会缓慢增加

4. SRP Batcher

在使用LWRP或者HWRP时,开启SRP Batcher的情况下,只要物体的Shader中变体一致,就可以启用SRP Batcher加速。它与上文GPU Instancing实现的原理相近,Unity会在运行时对于正在视野中的符合要求的所有对象使用“Per Object” GPU BUFFER(一个独立的Buffer) 将其位置、缩放、uv偏移、lightmapindex等相关信息保存在GPU内存中,同时也会将正在视野中的符合要求的所有对象使用Constant Buffer[5]将材质信息保存在保存在显存中的“统一/常量缓冲器”[6]中。
(LWRP是老版本的叫法,新的忘记了)

总结

批处理可以合并不同的mesh,而GPU Instancing主要是针对同一个mesh。

当物体顶点数量级别较大,重复度较低时,选择静态批处理(场景模型);
当物体顶点数较少且数量较少时,可以按需求选择动态批处理,当超过一定级别数时(参考:20个顶点的物体,数量超过3000)选择GPU Instancing将更有优势,且数量越多,优势越明显。

参考资料

必读: http://newhappy.com.cn/index.php/2020/05/14/batch/
必读:关于静态批处理/动态批处理/GPU Instancing /SRP Batcher的详细剖析
必读:利用GPU实现大规模动画角色渲染

1.《Unity游戏优化》第二版
2. 官方网站文档-批处理
3. 官方文档-GPU多例化
4. GPU多例化

《Unity游戏优化》第三章 批处理相关推荐

  1. 游戏优化系列三:Unity游戏的黑屏问题解决方法

    作者 大家好,我叫Jack冯: 本人20年硕士毕业于广东工业大学,于2020年6月加入37手游安卓团队:目前主要负责海外游戏发行安卓相关开发. 系列目录 游戏优化系列一:海外谷歌应用适配相关 游戏优化 ...

  2. Unity游戏优化[第二版]学习记录6

    以下内容是根据Unity 2020.1.01f版本进行编写的 Unity游戏优化[第二版]学习记录6 第6章 动态图形 一.管线渲染 1.GPU前端 2.GPU后端 3.光照和阴影 4.多线程渲染 5 ...

  3. Unity游戏优化[第二版]学习记录4

    Unity游戏优化[第二版]学习记录4 第4章 着手处理艺术资源 一.音频 1.导入音频文件 2.加载音频文件 3.编码格式与品质级别 4. 音频性能增强 二.纹理文件 1.纹理压缩格式 2.纹理性能 ...

  4. Unity游戏优化指南大全(持续更新中!)

    Unity游戏优化指南大全 三个官方优化提示: 性能和优化 (Performance and Optimization) - 关于性能分析器以及性能和优化技巧的 Unity 学习教程. Best pr ...

  5. Unity游戏优化(第2版)学习记录8

    Unity游戏优化[第二版]学习记录8 第8章 掌握内存管理 一.Mono平台 1.垃圾回收 2.内存碎片 3.运行时的垃圾回收 4.多线程的垃圾回收 二.代码编译 三.分析内存 1.分析内存消耗 2 ...

  6. 【游戏优化】AOI算法、Unity游戏优化(一)

    Unity游戏优化.内存优化.资源优化.AOI算法.安全 AOI概念和设计 地图广播(地图消息同步) AOI解决的问题 AOI的设计 场景分析与方案设计(一) 改善方案 场景分析与方案设计(二) 场景 ...

  7. 《有限与无限的游戏》第三章 我是自己的天才:经典摘抄(1)

    我是自己的天才,是我说的话和做的事情的创造者.正在思考的我,是我,不是心灵本身.正在行动的我,是我,不是意志本身.正在感觉的我,是我,不是神经系统本身. 当我像天才般说话的时候,我是第一次说这些话.重 ...

  8. 《有限与无限的游戏》第三章 我是自己的天才:经典摘抄(3)

    对于无限游戏的参与者来说,性完全是另一种触动.人们触动之时,必然是性感地触动之际. 因为性是生命起源的一出传奇,所以它充分表达了准备加入这出传奇的所有人的天才.这便向政治理论家掷出了一个很大的挑战. ...

  9. 《有限与无限的游戏》第三章 我是自己的天才:经典摘抄(2)

    天底下并没有天才养成秘籍,一个小孩子也不能自动变成天才.天才来自于 触动.触动是无限游戏最典型的悖论现象. 触动(touch),并不是指两个人的距离减少至零.只有我从自己的心中,同时而原创的回应时,我 ...

最新文章

  1. linux常见问题解答汇总
  2. 游戏 AI 相关文章
  3. Linux系统中磁盘创建管理(一)
  4. 关于一些知名深度学习模型的转换
  5. SpringBatch之CompositeItemWriter详解
  6. ThinkPad X220i 刷白名单BIOS,改装第三方无线网卡
  7. php实现把es6转为es5,如何将ES6代码转化为ES5?
  8. linux 环境变量详解,Linux 环境变量详解及实例
  9. 微软自带的防反编译工具dotfuscator.exe的使用
  10. 苹果移动设备用什么管理比较好?有什么推荐?
  11. 香橙派更改中文界面以及安装输入法
  12. Hybrid App 混合app 开发
  13. kvm创建快照、查看快照、恢复快照、删除快照
  14. python中temp的用法_请问Python里temp是什么意思?
  15. P3545 [POI2012]HUR-Warehouse Store
  16. android html字体大小,android Html.fromHtml font 标签支持设置字体大小和颜色
  17. Android Studio如何建立VR视频
  18. STM32 PWM输出原理和直流电机PWM驱动原理详解及例程
  19. RocketMQ保姆级教程
  20. 2021年高考成绩查询梧州市,2021年梧州高考状元名单公布 今年梧州高考状元是谁资料和分数...

热门文章

  1. PLSQL Developer安装和配置
  2. js两种方法删除对象属性
  3. gtx1050双显卡 linux,ubuntu16.04+GTX1050-Ti+cuda8.0(解决桌面重复登录)
  4. 【廖雪峰 python教程 课后题改编】利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字
  5. 为何曾经的程序员不当程序员了?如果有一天你当了程序员了,能去干嘛?
  6. java toprimitive_JavaScript 强制类型转换
  7. “互联网+”:工业革命的需求与变革
  8. 一文理解分布式开发中的服务治理
  9. java项目上线下线_解决 MVC 用户上线下线状态问题
  10. selenium通过文本定位