【从UnityURP开始探索游戏渲染】专栏-直达
对渲染指令和渲染状态设置的介绍,可以先看这里:【渲染流水线】[应用阶段]-[渲染命令队列]以UnityURP为例-CSDN博客
(⚠️注:大家最注意的看指令DrawCall的数量,其实对于切换渲染状态的SetPassCalls也是非常耗时的,可以类比为多线程中不断切换线程导致的大量开销。所以除了要优化DrawCall的数量,还要注意SetPassCalls不要过多,不能只对DrawCall优化忽视了SetPassCalls,达到平衡才能有效提升性能。)
渲染优化能大幅提升渲染效率,有效提升帧率。本文的渲染优化主要通过在渲染命令队列中的指令和状态切换的优化,即提升渲染管线效率的合批操作,来大幅提升帧率。
本文先快速介绍了各种合批,对不同合批优化点和适用场景做出介绍。综合不同合批的适用场景,给出实现与优化的建议策略。最后通过实例介绍带着手动计算优化效果,直观了解并将优化能力带到实际项目中。
URP中主要四种合批技术优化渲染指令(DrawCall)和渲染状态设置(SetPassCalls)。
SRPBatcher、静态合批、动态合批、GPU Instancing 这几种方式分别优化的目标、使用场景、使用限制等都不同。下面介绍每种方式,以在不同情况下使用不同技术优化合批。
目录
优化目标:优化原理:限制:实现:适用场景:
优化目标:优化原理:限制:实现:适用场景:
优化目标:优化原理:限制:实现:适用场景:
核心原理与影响、破坏SRP Batcher合批、与GPU Instancing的兼容性、SetPass calls上升场景、优化建议
GPU Instancing创建1000个物体的SetPass Calls开销计算
核心结论、GPU Instancing 合批有效:、SetPass calls 数量公式:
分场景计算、理想情况(现代平台)、保守情况(中端移动设备)、极限情况(低端设备)
性能优化建议、查询平台上限:、主动控制批次分割:、优先级规则:
1. SRP Batcher
优化目标:
降低CPU与GPU间的通信开销,通过减少 SetPass calls(而非直接减少DrawCall)提升动态物体的渲染效率。
优化原理:
- 将材质属性持久化到GPU常量缓冲区(UBO),仅当Shader变体变化时触发完整SetPass call。
- 相同Shader变体的材质共享渲染状态,动态数据(如模型矩阵)单独批量更新。
限制:
- 需Shader兼容SRP Batcher(使用
cbuffer封装材质属性)。 - 不支持
MaterialPropertyBlock和粒子系统。
实现:
- 在URP中默认启用,Shader需声明
cbuffer unitypermaterial。
适用场景:
动态物体(如角色、特效)且材质属性频繁变化。
2. 静态合批(Static Batching)
优化目标:
- 合并静态物体网格,减少DrawCall数量。
优化原理:
- 离线或运行时合并标记为
Static的网格为单一网格,通过索引偏移绘制可见部分。
限制:
- 物体必须为静态(不可变换)。
- 单批次顶点数限制(通常64k顶点),内存开销大。
实现:
- 勾选物体Inspector中的
Static选项,或调用StaticBatchingUtility.Combine。
适用场景:
- 大量静态重复物体(如建筑、地形)。
3. 动态合批(Dynamic Batching)
优化目标:
运行时合并小型动态物体网格,减少DrawCall。
优化原理:
- 每帧合并顶点数少于900的网格,使用同一材质实例渲染。
限制:
- 顶点数严格受限(含法线/UV时仅300顶点)。
- 不支持多Pass Shader和延迟渲染。
实现:
- URP中默认启用,需满足顶点数和材质一致性。
适用场景:
少量低模动态物体(如UI元素、小道具)。
4. GPU Instancing
优化目标:
通过单次DrawCall渲染相同网格的多个实例。
优化原理:
- 将实例属性(位置、颜色等)打包至GPU缓冲区,按索引批量绘制。
限制:
- 需相同网格和材质(参数可不同)。
- 部分低端设备不支持。
实现:
- 启用材质
Enable GPU Instancing,或通过MaterialPropertyBlock设置属性。
适用场景:
高频重复物体(如植被、子弹)。
合批优先级与选择建议
| 技术 | 优先级 | 推荐场景 |
|---|---|---|
| SRP Batcher | 最高 | 动态物体且材质多变 |
| GPU Instancing | 高 | 重复动态物体(草、树) |
| 静态合批 | 中 | 大量静态物体 |
| 动态合批 | 低 | 少量低模动态物体 |
注意:SRP Batcher与GPU Instancing可同时启用,但前者优先级更高
注意MaterialPropertyBlock修改材质
在Unity URP中直接使用MaterialPropertyBlock修改材质属性可能导致SetPass calls显著上升,具体取决于以下机制和限制:
核心原理与影响
破坏SRP Batcher合批
MaterialPropertyBlock绕过材质属性的常量缓冲区(UBO)管理,直接通过独立路径设置属性值。这会强制Unity跳过SRP Batcher的优化流程,导致相同Shader变体的物体无法合批,每个渲染器触发独立的SetPass call。
与GPU Instancing的兼容性
- ✅ 支持GPU Instancing合批:若材质启用GPU Instancing,即使使用
MaterialPropertyBlock修改属性(如位置、颜色),同一网格的多个实例仍可被合批(单次DrawCall)。 - ❌ 破坏SRP Batcher:如前所述,SRP Batcher要求属性通过
cbuffer统一管理,而MaterialPropertyBlock破坏了此机制。
SetPass calls上升场景
当以下条件同时满足时,SetPass calls会显著增加:
- 物体使用相同材质但通过
MaterialPropertyBlock设置不同属性; - 未启用GPU Instancing(或物体不满足Instancing条件);
- SRP Batcher因属性修改方式失效。
优化建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 高频修改属性且需合批 | 启用GPU Instancing | MaterialPropertyBlock可保持Instancing合批能力 |
| 依赖SRP Batcher的动态物体 | 避免使用MaterialPropertyBlock | 改用结构化缓冲区(如ComputeBuffer)或合并材质属性到Shader变体 |
| 少量物体个性化 | 可接受SetPass calls上升 | 性能损耗可控,优于创建独立材质实例 |
- 是否导致SetPass calls上升取决于合批技术组合:
- 若仅依赖SRP Batcher,使用
MaterialPropertyBlock会强制合批失效,引发SetPass calls飙升。 - 若启用GPU Instancing,则可通过Instancing合批抑制SetPass calls增长。
- 因此,在URP中需根据项目使用的合批策略权衡
MaterialPropertyBlock的使用,优先搭配GPU Instancing以维持性能
- 若仅依赖SRP Batcher,使用
GPU Instancing创建1000个物体的SetPass Calls开销计算
-
Q:使用GPU Instancing创建1000个物体,每个物体都用MaterialPropertyBlock修改同一个材质的颜色。那么绘制这1000个物体会需要多少个setpass calls?
-
A:SetPass calls 的数量取决于渲染批次的分割情况,具体规则如下:
核心结论
GPU Instancing 合批有效:
使用
MaterialPropertyBlock修改颜色 不会破坏 GPU Instancing 的合批机制,只要所有物体满足:- 相同网格(Mesh)
- 相同材质(Material)
- 启用 GPU Instancing
SetPass calls 数量公式:
SetPass calls = ceil(1000 / 每批次最大实例数)- 每批次最大实例数 由目标平台决定(通过
SystemInfo.maximumDrawMeshInstanceCount查询)
分场景计算
理想情况(现代平台)
- 最大实例数:1024(PC/主机/移动高端设备)
- SetPass calls:
ceil(1000 / 1024) = 1仅需 1 次 SetPass call
保守情况(中端移动设备)
- 最大实例数:500(如 Adreno 600 系列)
- SetPass calls:
ceil(1000 / 500) = 2需要 2 次 SetPass calls
极限情况(低端设备)
- 最大实例数:256(部分 OpenGL ES 3.0 设备)
- SetPass calls:
ceil(1000 / 256) = 4需要 4 次 SetPass calls
技术原理验证
机制 是否影响 SetPass calls 原因 GPU Instancing ✅ 合批有效 颜色数据通过逐实例缓冲区传递,单次 DrawCall 支持多实例 MaterialPropertyBlock ❌ 不破坏 Instancing 属性修改在 GPU Instancing 流程中被正确处理 SRP Batcher ❌ 被绕过 MaterialPropertyBlock强制跳过 SRP Batcher,但 Instancing 仍生效
额外影响因素
- 视锥体裁剪
- 仅 可见 的实例会被提交渲染(不可见实例不增加 SetPass calls)
- LOD 分组
- 不同 LOD 级别的实例会被拆分成独立批次
- 阴影绘制
- 若物体投射阴影,阴影通道会触发额外 SetPass calls(与主通道独立)
性能优化建议
查询平台上限:
csharp int maxInstances = SystemInfo.maximumDrawMeshInstanceCount;// 运行时动态判断主动控制批次分割:
csharp // 手动分割实例数组,确保每批 <= maxInstances Graphics.DrawMeshInstanced(mesh, submeshIndex, material, matrices, countPerBatch);优先级规则:
- 优先保证 GPU Instancing 合批(比 SRP Batcher 更适合动态修改属性)
- 避免在
MaterialPropertyBlock中修改非 Instancing 支持的属性(如纹理)
📌 最终结论:在 2025 年的主流设备上(如支持 Vulkan/Metal 的移动设备或 PC),SetPass calls 通常为 1 次,低端设备最多不超过 4 次。
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)
4060

被折叠的 条评论
为什么被折叠?



