- 原理
- 使用条件
- 对 Shader 的要求
- 成功与失败的判断标准
- 优缺点
- 与其他技术的关系
🚀 Unity SRP Batcher 深度解析:原理、优势、限制与最佳实践
Unity 的 SRP Batcher 是现代渲染管线(URP/HDRP)中最重要的性能优化技术之一,合理使用可显著减少 CPU 开销、提升大规模场景下的渲染效率。
📌 什么是 SRP Batcher?
SRP Batcher(Scriptable Render Pipeline Batcher) 是 Unity 在 URP 和 HDRP 渲染管线中提供的一种批处理优化机制。
它的目标是:
✅ 减少 CPU 与 GPU 之间频繁的材质状态(Shader 常量)设置,从而降低 SetPass Call 数量,提升渲染效率。
🧠 它是怎么工作的?
在传统渲染中,每个物体渲染前,CPU 都需要将该物体使用的材质属性上传给 GPU:
[每个物体] → [Shader 参数打包 → 上传 GPU] → [Draw]
这种频繁的数据上传,会导致:
- 大量 CPU 时间耗在 SetPass 阶段
- GPU 等待 CPU 指令完成
SRP Batcher 的优化方式是:
[多个物体使用同 Shader/Variant] → [预缓存参数块] → [只更新偏移指针] → [Draw]
即:将每个材质使用的属性缓存在 GPU 的统一结构中,同一 Shader Variant 的材质只切换缓存偏移,无需重新上传整个参数列表!
✅ SRP Batcher 的核心优化点
优化项 | 描述 |
---|---|
✅ 减少 SetPass Call | 合并多个材质的上传流程 |
✅ 材质常量重用 | 避免重复上传一致结构的参数 |
✅ 批量提交 Draw Call | 保持每个材质独立,但处理统一结构更高效 |
📦 开启 SRP Batcher 的方式
使用 URP 或 HDRP 管线(即 SRP 系列)
在对应 Render Pipeline Asset 中勾选 ✅
SRP Batcher
Project Settings > Graphics > Render Pipeline Asset > SRP Batcher
SRP Batcher 默认是启用的,但不是所有 Shader 都自动兼容它!
🎨 Shader 的要求
要让 Shader 支持 SRP Batcher,需要满足以下结构和编写方式:
要求 | 说明 |
---|---|
✅ 使用 HLSL + ShaderLab 写法 | Surface Shader 不支持 SRP Batcher |
✅ 所有材质属性必须在 CBUFFER_START(UnityPerMaterial) 内声明 |
SRP Batcher 依赖此结构统一参数布局 |
✅ 不使用全局变量、uniform 单独声明 | 所有属性必须集中封装 |
✅ 不使用 Material.SetXXX() 修改属性 |
会破坏 SRP 缓存结构,打断合批 |
✅ 不使用 MaterialPropertyBlock 设置非 instanced 属性 |
也会打断 SRP Batcher 合批(可以用 instancing) |
✅ 示例 Shader(支持 SRP Batcher)
Shader "MySRP/Simple"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderPipeline"="UniversalRenderPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// ✅ SRP Batcher 要求
CBUFFER_START(UnityPerMaterial)
float4 _Color;
CBUFFER_END
struct Attributes { float4 positionOS : POSITION; };
struct Varyings { float4 positionHCS : SV_POSITION; };
Varyings vert (Attributes v)
{
Varyings o;
o.positionHCS = TransformObjectToHClip(v.positionOS);
return o;
}
half4 frag (Varyings i) : SV_Target
{
return _Color;
}
ENDHLSL
}
}
}
🧪 如何确认 SRP Batcher 生效?
✅ 使用 Frame Debugger 检查:
- 打开
Window > Analysis > Frame Debugger
- 启用调试
- 查看某个
Draw Call
- 在右侧详情中查找:
SRP Batcher enabled: true
如为 false
,说明你的 Shader/材质不满足条件。
❌ SRP Batcher 失效的常见原因
原因 | 描述 |
---|---|
❌ Shader 使用 Surface Shader 写法 | Surface Shader 不支持 SRP 架构 |
❌ 材质属性未放入 CBUFFER |
SRP Batcher 识别不到统一结构 |
❌ 使用了 Material.SetFloat() |
会创建一个新的 GPU 实例缓存,打断 SRP 合批 |
❌ 使用了 Shader Keywords 但 Variant 不一致 | 变体不同视为不同 Shader,不可合批 |
❌ 使用 ShaderGraph 但未设为 SRP 兼容结构(极少见) | 可通过 ShaderGraph 默认行为规避 |
✅ 优点总结
优点 | 描述 |
---|---|
✅ 极大减少 SetPass Calls | 同一 Variant 的材质只上传一次结构描述 |
✅ 保留每个材质独立参数 | 不需要合并网格、材质等 |
✅ 和 GPU Instancing 可并用 | 双重优化,性能爆发 |
✅ 无需修改场景结构 | 比 Static Batching 灵活、安全 |
⚠️ 局限与限制
局限点 | 说明 |
---|---|
❌ 不支持 Surface Shader | 完全不兼容 |
❌ 不支持非 SRP 管线(内置管线) | Built-in 无法使用 SRP Batcher |
⚠️ 不支持动态材质修改 | 不能在运行时用 material.SetFloat() ,否则打断合批 |
⚠️ 不支持关键字变化材质 | Shader Variant 不一致会被视作不同 Program |
⚠️ 不支持用非 CBUFFER 定义属性 |
使用 uniform float _X 会被视为不兼容 |
🔥 与其他优化方式的关系
技术 | 可组合使用? | 优化目标 |
---|---|---|
✅ GPU Instancing | ✔ 可以 | 减少 Draw Call |
✅ SRP Batcher | ✔ 可以 | 减少 SetPass Call |
❌ Static Batching | ✖️ 不建议配合使用 | 会破坏 SRP Batcher 性能优势 |
⚠️ MaterialPropertyBlock | ✔ 仅用于 instanced 属性 | 不要修改非 instanced 参数,否则打断合批 |
✅ 最佳实践建议
实践建议 | 原因 |
---|---|
使用 ShaderGraph 或 HLSL + CBUFFER 写法 | 自动兼容 SRP Batcher |
Instanced 属性不要放进 CBUFFER |
它属于实例缓冲,另处理 |
尽量减少 Shader Keyword 变体组合 | Variant 不一致也会导致拆批 |
用 MaterialPropertyBlock 配合 instanced 属性 | 可实现 per-instance 参数变化而不打断合批 |
构建前启用 Variant Stripping(Shader Settings) | 减少冗余变体,构建更快,加载更轻 |
✅ 总结
**SRP Batcher 是现代 Unity 渲染系统中最重要的 CPU 性能优化利器之一。**它不减少 Draw Call,但能显著减少 CPU 到 GPU 的材质状态开销,是大规模场景、高并发渲染中不可或缺的优化手段。
✅ 快速判断能否进入 SRP Batcher:
项目 | 能否进入 SRP Batcher |
---|---|
使用 URP / HDRP | ✅ 是 |
Shader 用 CBUFFER 编写 | ✅ 是 |
不使用动态 SetXXX() 改属性 | ✅ 是 |
相同 Shader + Variant | ✅ 是 |
材质值不同(颜色等) | ✅ 可以 |