常见优化手段GPUInstance以及Batching总结
/ / 点击 / 阅读耗时 13 分钟Batching
- Static Batching
- Static Batching会在Build阶段提取多个使用相同材质的,且不会移动,旋转和缩放模型的Vertex buffer和Index buffer。
- 然后将顶点数据变换到世界空间下,存储到最终Static Batching所使用的Vertex buffer中,并记录每个子模型的Index buffer在Static Batching所使用的Index buffer中的位置。
- 在绘制阶段,会一次性提交合并后的模型顶点数据,最终引擎会根据子模型的可见性确定需要绘制的子模型,设置一次渲染状态,再调用多次Draw Call分别绘制子模型。
- 所以Static Batching并没有减少Draw Call的数量,但在绘制阶段避免了多次数据提交和渲染状态的切换。
- Dynamic Batching
Dynamic Batching会在运行时将使用同一材质的模型进行合并渲染,也就是将符合条件的GameObject放在一个Draw Call中绘制。使用Dynamic Batching有一些限制:- 模型最高900个顶点属性,300个顶点。假如我们的Shader中每个顶点使用了Position, Normal, UV,那么模型只能有300个顶点。如果在Shader中使用了Position, Normal, UV0, UV1, Tangent,那么顶点数就要减少到180个(5*180=900)。
- GameObject之间有镜像变换的不能进行合批
- 使用Multi-pass Shader的GameObject禁止合批
- 拥有lightmap的对象,无法合批
- GameObject接收实时阴影无法合批
- Dynamic Batching在降低Draw Call的同时会导致额外的CPU性能消耗,所以仅在合批操作的性能消耗小于不合批,Dynamic Batching才有意义。
GPUInstance
介绍
GPU Instancing是指由GPU和图形API支持的,用一个DrawCall同时绘制多个具有相同网格物体的技术。假如现在有一个包含大量模型的场景,而这些模型的网格数据都一样,不同的仅仅是世界空间下坐标不同。如果按照正常的渲染流程,DrawCall次数是和物件数量相同的,随着物件数量的上升CPU往GPU上传的数据就会越来越多,很快就会遇到性能的瓶颈。
使用GPU Instancing技术时,数据的上传是一次性打包上传至GPU的,紧接着调用GPU和图形的API利用这些数据绘制多个物件。Unity中的具体实现步骤如下:
- 将Per-Instance Data(世界矩阵,颜色等自定义变量)打包成Uniform Array,存储在Instance Constant Buffers中
- 对于可以使用Instancing的Batch,调用各平台图形API的Instancing DrawCall,为每个Instance生成一个不同的SV_InstanceID
- 在Shader中使用SV_InstanceID作为Uniform Array的索引来获取当前Instance的Per-Instance Data
GPU Instancing技术并不是总能提高性能的,如果场景中有大量使用相同材质和相同网格的物体并性能问题是由DrawCall次数过多导致的,这时使用GPU Instancing可以得到不错的性能提升。在实际的游戏项目中植被和树木是最适合使用的。这里要注意的是GPU Instancing是通过减少DrawCall来降低CPU开销的,但这同事也会为GPU带来额外的开销。适合的才是最好的,切勿沉迷性能优化无法自拔。
流程
Shader支持GPUInstance 并且启用
1 | Shader "Goat/LightMap/CutoutInstanceLightmap" |
限制
- 使用Lightmap的物体无法使用Instancing
- 受不同Light Probe / Reflection Probe影响的物体无法使用Instancing
- 使用包含多个Pass的Shader物体,只有第一个Pass可以Instancing
- 前向渲染时,受多个光源影响的物体只有Base Pass可以Instancing, Add Passes不行
- Instancing 适用于MeshRenderer组件和Graphics.DrawMesh()
- 需要物件使用相同的Material和Mesh
- 需要把Shader改成Instanced的版本
- 当所有条件均满足的情况下,Instancing是自动进行的,并且优先级高于 Static/Dynamic Batching
细节
GPU Instancing是指由GPU和图形API支持的,用一个DrawCall同时绘制多个具有相同网格物体的技术。假如现在有一个包含大量模型的场景,而这些模型的网格数据都一样,不同的仅仅是世界空间下坐标不同。如果按照正常的渲染流程,DrawCall次数是和物件数量相同的,随着物件数量的上升CPU往GPU上传的数据就会越来越多,很快就会遇到性能的瓶颈。
使用GPU Instancing技术时,数据的上传是一次性打包上传至GPU的,紧接着调用GPU和图形的API利用这些数据绘制多个物件。Unity中的具体实现步骤如下:
- 将Per-Instance Data(世界矩阵,颜色等自定义变量)打包成Uniform Array,存储在Instance Constant Buffers中
- 对于可以使用Instancing的Batch,调用各平台图形API的Instancing DrawCall,为每个Instance生成一个不同的SV_InstanceID
- 在Shader中使用SV_InstanceID作为Uniform Array的索引来获取当前Instance的Per-Instance Data
- GPU Instancing技术并不是总能提高性能的,如果场景中有大量使用相同材质和相同网格的物体并性能问题是由DrawCall次数过多导致的,这时使用GPU Instancing可以得到不错的性能提升。在实际植被和树木是最适合使用的。这里要注意的是GPU Instancing是通过减少DrawCall来降低CPU开销的,但这同事也会为GPU带来额外的开销。
1 | UNITY_VERTEX_INPUT_INSTANCE_ID和UNITY_SETUP_INSTANCE_ID。 |
UNITY_VERTEX_INPUT_INSTANCE_ID:在Shader输入/输出结构体中 声明一个Instance ID。
UNITY_SETUP_INSTANCE_ID:这个宏必须在Vertex Shader或Fragment Shader的一开始就调用,只有调用了这个宏以后,才可以在Shader中通过全局的InstanceID来访问到结构体数据。