您现在的位置是:首页 >技术交流 >Unity Batching 批处理网站首页技术交流
Unity Batching 批处理
Unity Batching 批处理
了解批处理前先了解下 Draw Call
要了解 Draw Call 需要先了解游戏引擎是如何把物体图像绘制到屏幕上的。
(1):渲染流水线
渲染流水线的任务为从一个 二维、三维场景开始,最终渲染为一张二维图像,显示在屏幕上。
计算机需要处理一系列的顶点、纹理、光照 等信息,把这些信息转化为肉眼可视的图像。这个工作通常是由 CPU 和 GPU 共同完成的。
Runder-Time Rendering 一书中将一个渲染流程分为 3 个阶段:应用阶段(Application Stage)、几何阶段(Geometry Stage)、光栅化阶段(Rasterizer Stage).
每个阶段本身也是一个流水线系统,包含了各种子流水线系统。
应用阶段 -> 几何阶段 -> 光栅化阶段
(2) 应用阶段
应用阶段由 CPU 负责实现,大概分为 3 个阶段
(2.1)把数据加载到显存中(把数据从硬盘加载到内存,然后网格、纹理等数据加载到显卡存储空间显存)
(2.2)设置渲染状态(状态定义了使用什么网格、纹理贴图、光照数据、材质等)
(2.3) 调用 DrawCall
设置过渲染状态,CPU 需要调用一个渲染命令,告诉GPU开始渲染,这个命令就是 Draw Call
Draw Call 就是 CPU 调用图像编程接口,如 OpenGL中的 glDrawElements 命令,或者 DirectX 中的 DrawIndexedPrimitive 命令,然后 GPU 进行渲染操作
当 GPU 拿到一个 Draw Call 时,GPU 会根据渲染状态(材质、纹理、着色器等)和所有输入的顶点数据进行计算,最终输出成屏幕上显示的图像,这个过程是GPU流水线。
(3) CPU 和 GPU 是如何实现并行工作的
答案是命令缓冲区(Command Buffer),命令缓冲区包含一个命令队列,由CPU向其中添加命令,GPU 从中读取命令,添加和读取是相互独立的,命令缓冲区的命令有多种,Draw Call 是其中一种。
(4)Draw Call 多了会影响性能
(4.1)创建 10000 个文件,每个文件大小 1KB总大小为 10MB,从一个文件夹复制到另一个文件夹
(4.2)创建 一个文件,大小为 10 MB,从一个文件夹复制到另一个文件夹
了解的应该都知道 (4.1)的操作要比(4.2)慢很多,因为每复制一个文件,要分配内存,创建操作句柄等额外工作,如果复制文件很多,会造成很大的性能开销。虽然渲染过程跟文件操作有很大不同,但也是很类似的,每次调用 Draw Call前,CPU 需要完成很多工作,当这些工作完成后,GPU 才能拿到渲染命令,其实GPU 的渲染能力是很强的,渲染 200个还是2000 个三角形通常没太大区别,因此GPU渲染速度往往快于CPU,所以当有大量 Draw Call 时CPU 往往消耗过载。
(5) 如何减少 Draw Call
尽管减少 Draw Call 方法有很多,我们在此只讨论 使用 批处理(Batching)、批处理(Batching)、批处理(Batching) 重要的事情 说三遍,到此才真正进入了正题。
(5.1)减少 Draw Call 的一个思路,把多个小的 DrawCall 合并为一个大的 Draw Call,这就是批处理(Batching)思想
(5.2)合批优化的是CPU还是GPU?
合批节省了CPU相关准备工作量,只是对CPU的优化,对GPU的影响不大
(6) Unity 提供了两种 批处理(Batching)方式
动态批处理(Dynamic Batching)
静态批处理(Static Batching)
需要在 Edit -> Player-> Other Settings 打开 Static Batching、Dynamic Batching 设置
Dynamic Batching 默认是不开启的
(6.1)动态批处理(Dynamic Batching)
开启 Edit -> Player-> Other Settings->Dynamic Batching 后,物体不需要做配置,复合条件的物体自动生效
原理:动态合批是为场景内共享同一材质的可移动GameObject 渲染的,在进行场景绘制之前将所有复合条件的模型的顶点信息转换到世界空间,然后通过一次 Draw Call 绘制多个模型,此操作是由 CPU 完成的,会带来一些 CPU 的性能消耗
限制条件:
(6.1.1)必须使用相同的材质(材质相同的物体,不同的只有顶点数据,可以将顶点数据合并在一起,再发送给GPU)
(6.1.2)顶点属性不能超过 900 个(不是Mesh 的顶点数,而是Shader使用的 顶点坐标、法线、UV坐标等属性总和不能超过 900,如果同时使用了顶点坐标、法线、UV坐标属性,则顶点数不能超过 900 除以 3 = 300个)
(6.1.3)多个 Pass 的 Shader 会破坏批处理,因为多个 Pass 的 Shader 通常会导致一个物体要连续绘制多次,并切换渲染状态,这会打破其跟其他物体进行动态合批的机会
(6.14)延迟渲染是无法合批的
(6.2) 静态批处理(Static Batching)
开启:将物体 Inspector 面板右上角的 Static 勾选(实际只需要勾选 Batching Static)
原理:在运行开始阶段,把需要进行静态批处理的模型合并到一个新的网格中,意味着这些模型数据在运行时不能被移动
优点:只需要一次合并操作,效率高
缺点:占用更多的内存来存储合并后的数据
动态加载并实例化一个带静态标记的GameObject到场景中,是不会被当做静态合批处理的。换言之,静态标记只被作为是打包时的考虑参数,并不会在运行时被引擎处理。如果在场景加载后,希望对手动实例化的单位进行静态合批,可以使用手动静态合批
StaticBatchingUtility.Combine
(7)查看
前提,在Unity 运行状态下查看
Unity 改版后 Game 视图 Stats 窗口
Batches:相当于 DrawCall
Saved by batching:通过批处理节省的 Draw Call
SetPass calls:跟Shader 中Pass 数量有关,Pass 越多,该值越大
另外一个查看
打开 Window->Analysis->Profiler
然后打开 Rendering 部分可以查看更多详细信息
还有一个查看
打开 Window->Analysis->Frame Debuger
点击面板上 Enable 按钮
可以看到中间上方滑动条 还有 1 of 15
15 既是 当前屏幕是由 15 个步骤将场景渲染到屏幕上的,可以拖动滑动条看每一步具体绘制出了那些物体,也可以在左侧一个一个选择查看绘制的物体
(8) 离线合批
上面说的 动态批处理(Dynamic Batching) 、静态批处理(Static Batching) 都是在运行时进行的,动态合批是需要实时合批,静态合批是合一次,但是占用双份内存
离线合批是在游戏制作期间,把需要合在一起的物体进行合并,这样就减少了运行时的计算处理
离线合批方式
(8.1)美术利用建模软件:3D Max、Maya 等 把一些可以合并的物体进行合并
(8.2)利用引擎插件工具,Unity 的一些合并工具
能提前做好合批的尽量提前做。
比如场景中有很多花草树木,他们模型面数一般都不多,贴图基本都是相同的,尽量不要做成 一个一个的花、草、树、木 放入场景,而是让美术在制作模型软件中提前把这些模型合并,虽然打开动态合批也是可以减少 Draw call 但是还是会消耗一些性能的
测试动态批处理
开启动态批处理,新建一个材质球,选择Shader:Unlit/Texture,添加贴图
创建六个 Cube 添加这个材质球,运行查看
场景中只添加了一个 Camera 和 Light, Cube 关闭产生阴影和接收阴影
从 Stats 面板查看
Batches: 3 个 Draw Call
Saved By batching: 5 表示批处理节省了 5 个 Draw Call,总共 6 个 Cube,共用了 1 个 Draw Call 所以节省 6-1= 5 次 Draw Call
Profiler 面板查看
Draw Calls:3
Dynamic Batching Batched Draw Call:6 动态合批 6 个
从 Frame Debug 面板查看 一次绘制了 6 个立方体
然后隐藏 3 个 Cube 查看
Stats 面板
Batches:3 还是 3 个 Draw Call 跟 6 个 Cube 时一致
Saved by batching:2 表示合批减少了 2 个 Draw Call,总共 3 个 Cube,共用了 1 个 Draw Call 所以节省 3-1= 2 次 Draw Call
现在关闭动态批处理的设置
Stats 面板
Batches:8 个 Draw Call
Saved by batching:0 表示没有合批操作
Profiler面板
Draw Calls:8
Dynamic Batched Draw Calls:0 没有动态合批
Frame Debug 面板
看到总计 6 次将 6 个 Cube 绘制
参考
图形渲染及优化-Batch-腾讯游戏学院
图形渲染及优化—Unity合批技术实践-腾讯游戏学院
游戏图形批量渲染及优化:Unity静态合批技术