渲染提前期优化
下面以农场生产番茄为例
- Culling
- 去除妨碍生长的树枝、叶子
- 摘番茄时去掉那些品质坏、腐烂的
- 去除不适合生产的
- Simplization
- 将番茄分类,按大小、用途、品质等
- 为了适应不同的番茄产品或销售渠道
- Batching
- 装载发货的过程
- 近距离、远距离、保鲜需求、时间需求
- 总之要保证各地方有源源不断的货物,从而不会影响下游
Unity中的Culling
哪些是需要剔除的内容
gpu方案要考虑硬件兼容,cpu方案需要考虑会不会造成负优化
广义上讲:
- 看不见的像素、网格和对象
- 重复的、用不到的资源
- 不需要、不执行的代码
unity中提供的剔除手段
- 像素剔除:摄像机平截头体剔除、Back-face Culling、Early-Z、Pre-Z Pass
- 网格剔除:Layer Mask、可见距离剔除、Occlusion(内存和cpu开销)
- 灯光剔除:Tile-Based Deferred Rendering、Forward+
- 场景剔除:Additive Scene
用户扩展剔除
- 场景数据结构:Octree、BSP Tree、Portal、Voxelization、SDF等
- GPU Culling: Hi-Z Pass、Temporal Reprojection Culling、Cluster、Tile-based Visible Buffer等
Unity中的Simplization
简化:基于现有资源进行简化操作
哪些是需要简化的内容
广义上讲:
- 运行效率较重的资源(运行时占用内存较高,处理耗时较长的资源)
- 低效、不合适功能(平台的兼容性、功能的通用性)
Unity下的简化
- Quality Settings
- 通过烘焙光照简化实时光照
- 通过Bounding Box或替代体碰撞代替Mesh碰撞
- 通过Local Volume代替Global Volume
- RayCast代替Spherecast、CapsuleCast等
- 纹理文字代替系统文字
- Mesh LOD
- Shader LOD
- HLOD(针对大世界,减少对象个数和drawcall,有一定cpu和内存开销)
- 通过Camer override代替URP管线中的一些通用设置,只在某些摄像机开启
- 各种OnDemand更新或分级设置接口
- …
用户扩展简化
- 场景简化数据结构,特别光追时
- 第三方LOD方案
- Mesh Impostor,虚假替代体
- Animation LOD
- 骨骼LOD
- 2D寻路代替Navigation Mesh
- 扩展类似OnDemand接口
Unity中的Batching
何为Batching
- 本质是按需求组织从cpu发给gpu的过程的数据,包括网格、纹理、shader变量、材质属性等
- batching要研究这些数据怎么组织、以什么数据结构组织、以什么频率发送、以及每次发送多少
哪些内容需要Batching
广义上讲:
- 资源Batching (Mesh、Texture、Shader参数、材质属性)
- Draw call Batching (Static Batching. Dynamic Batching)
- GPU Instancing (直接渲染、间接渲染、程序化问接渲染)
- Set Pass call Batching. ( SRP Batching)减少渲染状态切换的次数
资源的Batching
Mesh:
- Mesh.CombineMesh,合并静态网格对象(剔除问题和内存开销)
- Submeshes->Single Mesh,合并材质与贴图,不同材质通过通道图标记(不合并的话每个不同材质就是drawcall)
Texture:
- Atlas Texture,通过纹理坐标映射多张贴图
- TextureArray纹理数组,向GPU同时传递多张设置相同的资源
Shader变量与材质属性
- Material Property Block (Build In管线)
- Const buffer(SRP管线,SRP batch 与 Material Property Block 不兼容)
- 不同的 Const buffer控制提交到Gpu的频率
- 一般关注UnityPerMatrial
DrawCall Batching
Static Batching
- 引擎自动合并临近可合并的静态网格对象,并将合并后的网格转换到世界空间下,使用顶点信息构建一个共享的顶点缓冲和索引缓冲区,对可见网格进行绘制调用
- 运行时合批可以调用StaticBatchingUtility.Combine,不需要勾选static batch
- 额外的内存开销,合批的网格会有额外拷贝
Dynamic Batching(一般用于低端机,现代CPU可能合批调用开销更大)
- Unity会将可动态合批的对象构建到一块大的顶点缓冲区中
- 根据合批后的数据设置渲染器材质状态
- 再将缓冲区绑定到GPU上
- 对于每个MeshRenderer是根据缓冲区偏移量来更新提交绘制内容
GPU Instancing
用于渲染网格的多个实例化副本,将基础网格对象传递给GPU后,充分利用InstaningBuffer传递多个网格实例位置、朝向、颜色等到GPU,避免反复传递多个基础对象。与SRP batch不兼容,对于顶点数较少的网格优化可能不是很明显
- DrawMeshInstanced
- DrawMeshInstancedIndirect
- DrawMeshInstancedProcedural
Set Pass call Batching
- SRP Batcher(显著减少Unity为相同着色器的材质:准备和调度绘制的CPU时间开销)
- Const Buffer(以什么时机、什么频率提交,一般是属性变化时才提交)
- UnityPerCamera
- UnityPerFrame
- UnityPerPass
- UnityPerDraw
- UnityPerDrawRare
- ++UnityPerMaterial++ (开发者一般定义这个)
Batching优化顺序
资源Batching > SRP Batching = Static Batching > GPU Instancing > Dynamic Batching
Batching使用限制
Static Batching限制
- 额外内存开销
- 64000个顶点限制,超过会开启下一个合批
- 影响Culling剔除
Dynamic Batching限制
- 合批不超辻900个顶点属性(注意不是900个顶点,是诸如法线、切线这种寄存器属性)
- 除了渲染阴影对象外,相同材质,不同材质实例也不能合并
- 具有光照贴图的游戏对象如果有附加渲染器参数时,如果需要动态合批这些对象,他们必须指向相同的光照贴图位置。(非常苛刻)
- 有多Shader Pass的游戏对象无法做动态合批
- 受多个光照影响的游戏对象,满足动态合批条件合批后,只会受一个光源的影响
- 延迟渲染下不支持动态合批
- CPU开销可能会增大,需要测试开启使用
GPU Instancing限制
- 图形API版本要求
- 与SRPBatcher不兼容
- 不同绘制API的参数与绘制个数不同
- 渲染顶点数较少的网格时,效率可能较差(需要测试使用)
Set Pass call Batching
- 图形API版本要求
- 必须是SRP渲染管线
- 粒子对象不能合批
- 使用MaterialPropertyBlocks的游戏对象不能合批
- Shader必须是compatible的
合批失败原因汇总2.0
1.0版本:https://github.com/Unity-Technologies/BatchBreakingCause
- “An object is affected by multiple forward lights.” 此物体受到多个前向灯光的影响,多个灯关pass了
- “Objects have different materials.” 此物体有不同的材质,即使材质属性设置完全相同
- “An object is using a multi-pass shader.” 此物体使用了多pass着色器
- “An object has odd negative scaling.” 此物体Trasform的Scale使用了负数(奇数个负数,会导致culling state的改变)
- “Either objects have different \”Receive Shadows\“ settings, or some objects are within the shadow distance, while some other objects are not.” 此物体接收阴影的设置不同,或者物体有不同的的阴影距离设置
- “Objects are affected by different forward lights.” 此物体受到不同的前向灯光影响
- “Objects are on different lighting layers.” 物体在不同的Lighting Layer上
- “Objects have different \”Cast Shadows\“ settings.” 此物体有不同的投影体设置,指的在shadow pass中的合批
- “Objects either have different shadow caster shaders, or have different shader properties / keywords that affect the output of the shadow caster pass.” 此物体有不同的投影着色器设置或者有不同的着色器属性或者关键字,影响Shadow Caster Pass的输出
- “The shader explicitly disables batching with the \”DisableBatching\“ tag.” 着色器中显式设置了DisableBatching的标记
- “Objects have different MaterialPropertyBlock set.” 此物体有不同的MaterialPropertyBlock集合
- “Non-instanced properties set for instanced shader.” Instanced的着色器有非instanced属性集
- “Objects are lightmapped.” 物体使用了不同的LightMap或者虽然使用相同的LightMap但使用的UV不同
- “Objects are affected by different light probes.” 此物体受到不同的光照探针影响
- “Objects are shadowed by baked occlusions and have different occlusion factors.” 此物体烘焙了遮挡,并且设置了不同的遮挡因子
- “Objects are affected by different reflection probes.” 此物体受到不同的反射探针影响
- “Rendering different meshes or submeshes with GPU instancing.” 使用GPU实例化渲染不同的网格或子网格
- “Objects have different batching-static settings.” 此物体有不同的静态合批设置
- “Objects belong to different static batches.” 此物体归属不同的Static Batches,顶点数超过上限拆成两个合批了
- "Dynamic Batching is turned off in the Player Settings or is disabled temporarily in the current context to avoid z-fighting.” 在Player Settings中关闭了动态合批,或者在当前的环境中为了避免深度冲突而临时关闭了合批
- “There are too many indices (more than 32k) in a dynamic batch.” 动态合批中有太多的索引(大于32k)
- “A mesh renderer has additional vertex streams. Dynamic batching doesn‘t support such mesh renderers.” Mesh Renderer具有其他顶点流。动态批处理不支持此类网格渲染器。一般发生在粒子对象定义了additional vertex streams时
- “A submesh we are trying to dynamic-batch has more than 300 vertices.” 动态合批超过300个顶点
- “A submesh we are trying to dynamic-batch has more than 900 vertex attributes.” 动态合批超过900个顶点属性
- “This is the first draw call of a new shadow cascade.” 新阴影级联的第一次绘制调用,这时没有对象可以合批
- “The material doesn‘t have GPU instancing enabled.” 材质未启用GPU Instancing功能
- “Objects are rendered using different rendering functions. This can happen if the type of renderer is different (eg Mesh/Skinned Mesh) or when using different settings within the same renderer, such as Sprite Masking.” 使用不同的渲染。如果渲染器的类型不同(例如网格/蒙皮网格),或者在同一渲染器中使用不同的设置(例如精灵遮罩),则可能会发生这种情况。
- “Objects have different batching keys. This is usually caused by using different vertex streams on Particle Systems, or by mixing Lines and Trails, or by mixing lit and unlit geometry.” 此对象具有不同的Batching Keys。 这通常是由于在粒子系统上使用不同的顶点流,或混合线和轨迹,或混合Lit和Unlit的几何体造成的。"
- “Mesh uses 32 bit index buffer.” Mesh使用了32位的索引缓冲
- “Submesh has non-zero base vertex.” 子网格对象有非0的基础顶点, submesh.BaseVertexLocation != 0
- “The previous instanced draw call has reached its maximum instance count.” 先前的InstanceDrawCall已经达到了Instance的最大数量
- “Motion Vector rendering doesn‘t support batching.” Motion Vector的渲染不支持Batching
- “When using late latching, children of an XR late latched GameObject do not use batching.” 使用late latching时,XR late latched GameObject的子级不能合批
- “Objects have different bounds and bounds instancing is disabled.” 对象具有不同的包裹体,那么包裹体实例化被禁用
- “SRP: Node have different shaders.” 节点具有不同的着色器
- “SRP: Node use multi-pass shader” 节点使用了多Pass着色器
- “SRP: Node use different shader keywords” 节点使用了不同的着色器关键字
- “SRP: End of the batch flush” Batch Flush结束,C Buffer达到上限时
- “SRP: Node is not compatible with SRP batcher” 节点与SRP Batcher不兼容
- “SRP: Node material requires device state change” 节点材质需要改变渲染设备状态
- “SRP: First call from ScriptableRenderLoopJob” ScriptableRenderLoopJob第一次调用
- “SRP: This material has custom buffer override” 材质有自定义重写的Buffer, 一般是修改了管线默认的顶点数据结构,两个不同顶点结构对象的网格不能进行合批