48.SRP Batcher

48.性能优化-CPU-图形-SRP Batcher


48.1 知识点

SRP Batcher 是什么

SRP Batcher(Scriptable Render Pipeline Batcher) 是 Unity 在可编程渲染管线(URP、HDRP)中提供的一项 CPU 优化技术,主要用于降低材质切换带来的开销。

主要解决的问题:

  1. 每次 Draw Call 不仅要告诉 GPU 画哪个网格,还要上传材质常量(Shader 参数)。
  2. 不同物体即使使用同一个 Shader,只要材质不同,CPU 就要重新设置常量缓冲区(Constant Buffer)。
  3. 这一步称为 SetPass,开销很大,复杂场景里 CPU 花在切换材质的时间常常比真正发 Draw Call 还多。
  4. SRP Batcher 用于缓解这部分开销。

流程图对比:

主要原理:

  1. Unity 在 GPU 显存中为 Shader 的常量缓冲区分配两个区域:
    • 材质缓冲区:存储材质相关参数(颜色、贴图索引等),放在 GPU 可缓存的大缓冲池里,避免每次切换材质都重新上传。
    • 物体缓冲区:存储物体相关参数(变换矩阵、光照探针等),只更新每个物体独有的少量数据。
  2. 在 CPU 侧只有内容变化时才更新数据,避免每帧为每个物体和材质重复准备和上传参数。
  3. Draw Call 数量不变,但 CPU 不再频繁计算和上传数据,显著降低 CPU 到 GPU 的通信开销。

说人话:
SRP Batcher 是 SRP 下的 CPU 优化机制,通过把物体和材质数据缓存到 GPU,减少计算和上传开销,让同一 Shader 的不同材质也能高效绘制。严格意义上它不是合批处理,因为 Draw Call 并不减少,而是替代动态批处理的 CPU 优化方案,是一种用显存换性能的方案。

注意:

  1. SRP Batcher 不会直接减少 Draw Call 数量。
    它不合并 Draw Call,每个物体依然会产生 Draw Call。
    本质上是让同一 Shader 的不同材质对象共用已缓存的 GPU 常量缓冲,避免 CPU 反复上传数据。
    所以 Draw Call 数量不变,但 CPU 渲染线程时间(Render Thread)会显著下降。
  2. SRP Batcher 只能在 SRP 渲染管线(URP、HDRP)中使用。
  3. 优先级问题:
    • 内置渲染管线:静态批处理 > GPU Instancing > 动态批处理 > 单独绘制
    • SRP 管线:静态批处理 > SRP Batcher > GPU Instancing > 单独绘制
  4. 虽然 SRP Batcher 不会实际减少 Draw Call,但在 Frame Debugger 里只会显示 SRP Batch 条目;GPU 仍在分别绘制这些物体,只是 CPU 端提交被优化与合并。

如何开启 SRP Batcher

  1. Unity 2021 之后,在 SRP 管线(URP、HDRP)中默认开启且不可关闭。
  2. 更早版本可在 URP Asset 的 Inspector 中勾选开启。
  3. 观察 SRP Batcher 是否工作:使用 Frame Debugger 查看渲染事件是否为 SRP Batch

SRP Batcher 的使用限制

Shader 兼容性:

  1. 对象使用的 Shader 必须与 SRP Batcher 兼容。
    在 HDRP 和 URP 中,所有 Lit Shader 和 Unlit Shader 都符合这一要求(粒子版本除外)。
  2. 自定义 Shader 需满足:
    • Shader 必须将所有引擎内置属性声明在名为 UnityPerDraw 的常量缓冲区中。
    • Shader 必须将所有材质属性声明在名为 UnityPerMaterial 的常量缓冲区中。

其它限制:

  1. 不能使用 MaterialPropertyBlock
    它用于在不复制材质的情况下给某个物体单独设置材质参数,会破坏 SRP Batcher 的批次一致性。
  2. 对象不能是粒子。
    需要是 MeshRendererSkinnedMeshRenderer(注意:蒙皮网格在新版本支持,2019 之前不支持)。

适合使用 SRP Batcher 的情况

适合的场景:

  1. 物体种类多(材质多),但 Shader 相同的场景。
    这也是 SRP 管线适合移动平台的原因之一。
  2. 移动平台开发中,游戏对象往往 Shader 相同但材质不同。
    SRP Batcher 虽不减少 Draw Call,但降低了 CPU 在 SetPass 时的开销。

举例:

  1. 大量使用同一 Shader 的物体:
    场景里有上百种建筑、石头、家具,材质不同(颜色/贴图不同),但 Shader 相同。
  2. 大量动态物体但材质切换频繁:
    大量移动的敌人、NPC、动态场景物体(破碎瓦片、掉落武器),每个都有自己材质但 Shader 相同。
  3. 使用 URP/HDRP 项目的大中型游戏:
    特别是移动端,CPU 渲染线程常成为瓶颈时,SRP Batcher 能显著降低 CPU 耗时。

不适合的场景:

  1. 非 SRP 管线项目。
  2. Shader 不兼容的项目。

48.2 知识点代码

Lesson48_性能优化_CPU_图形_SRPBatcher.cs

public class Lesson48_性能优化_CPU_图形_SRPBatcher
{
    #region 知识点一 SRP Batcher是什么?

    // SRP Batcher(Scriptable Render Pipeline Batcher)
    // 是 Unity 在可编程渲染管线,比如 URP、HDRP 中
    // 提供的一项 CPU 优化技术
    // 主要目的是用于降低材质切换带来的开销

    // 主要解决的问题:
    // 每次 Draw Call 不仅要告诉 GPU 画哪个网格,还要上传材质常量(Shader 参数)
    // 不同物体哪怕使用同一个 Shader,但只要材质不同
    // CPU 就要重新设置常量缓冲区(Constant Buffer),这一步就叫做 SetPass
    // SetPass 开销很大,在复杂场景里,CPU 花在切换材质的时间常常比真正发 Draw Call 还多
    // 而 SRP Batcher 就是用来解决这一问题的

    // 主要原理:
    // Unity 在 GPU 显存中为 Shader 的常量缓冲区分配了两个区域
    // 1.材质缓冲区:
    //   存储材质相关的参数(颜色、贴图索引等数据)
    //   存放在 GPU 可缓存的大缓冲池里
    //   不用每次切换材质都重新上传
    // 2.物体缓冲区:
    //   存储物体相关的参数(变换矩阵、光照探针等数据)
    //   只更新每个物体独有的少量数据
    // 在 CPU 这边:
    // 只有内容变化时才更新数据
    // 避免每帧为每个物体和材质重复准备和上传参数
    // 达到的效果:
    // Draw Call 数量不变
    // 但 CPU 不再频繁计算和上传数据
    // 大幅降低 CPU 到 GPU 的通信开销

    // 说人话:
    // SRP Batcher 是 SRP 下的一种 CPU 优化机制
    // 通过把物体和材质数据缓存到 GPU,减少计算和上传开销
    // 从而让同一 Shader 的不同材质也能高效批量绘制
    // 严格意义上来说 SRP Batcher 并不是合批处理
    // 因为 Draw Call 并不会减少
    // 而是替代动态批处理的 CPU 优化方案
    // 是一种用显存换性能的方案

    // 注意:
    // 1.SRP Batcher 并不会直接减少 Draw Call 数量
    //   它并没有合并 Draw Call,每个物体依然会产生 Draw Call
    //   本质上是让同一 Shader 的不同材质对象
    //   共用已缓存的 GPU 常量缓冲
    //   避免 CPU 反复上传数据
    //   所以 Draw Call 数量不变
    //   但 CPU 渲染线程时间(Render Thread)会显著下降

    // 2.SRP Batcher 只能在 SRP 渲染管线,即 URP 和 HDRP 中使用

    // 3.优先级问题
    //   内置渲染管线中:静态批处理 > GPU Instancing > 动态批处理 > 单独绘制
    //   SRP 管线中:静态批处理 > SRP Batcher > GPU Instancing > 单独绘制

    // 4.虽然 SRP Batcher 并不会实际减少 Draw Call
    //   但在 Frame Debugger 里只会显示 SRP Batch 条目
    //   而不是每个物体的单独 Draw Call
    //   但是实际上 GPU 仍然在分别绘制这些物体,只是 CPU 端提交被优化、合并了

    #endregion

    #region 知识点二 如何开启SRP Batcher

    // 在 Unity 2021 后,在 SRP 管线(URP、HDRP)中该功能默认开启,不允许关闭了
    // 之前的版本中可以在 URP Asset 的 Inspector 窗口勾选开启

    // 观察 SRP Batcher 是否工作,只需要用 Frame Debugger 查看渲染事件是否为 SRP Batch 即可

    #endregion

    #region 知识点三 SRP Batcher的使用限制

    // 1.对象使用的 Shader 必须与 SRP Batcher 兼容
    //   在 HDRP 和 URP 中,所有 Lit Shader 和 Unlit Shader 都符合这一要求(除了它们的粒子版本)
    //   若要让一个自定义 Shader 与 SRP Batcher 兼容,它必须满足以下要求
    //   1-1.Shader 必须将所有引擎内置属性声明在一个名为 UnityPerDraw 的常量缓冲区(Constant Buffer)里
    //   1-2.Shader 必须将所有材质属性声明在一个名为 UnityPerMaterial 的常量缓冲区里

    // 2.对象不能使用 MaterialPropertyBlocks
    //   MaterialPropertyBlock 是一个特殊的容器
    //   用来在不复制材质的情况下,给某个物体单独设置材质参数
    //   之后在讲解 GPU Instancing 相关 API 时会专门讲解

    // 3.对象不能是粒子
    //   对象需要是 MeshRenderer 或 SkinnedMeshRenderer
    //   注意:蒙皮网格渲染器在新版本支持,2019 之前不支持

    #endregion

    #region 知识点四 适合使用SRP Batcher的情况

    // 适合的场景:
    // 它特别适合物体种类多(材质多),但是 Shader 相同的场景
    // 这也是为什么 SRP 管线非常适合移动平台开发的原因之一
    // 因为在移动平台开发时,游戏中的对象往往都是 Shader 相同,但是材质不同
    // 它虽然不减少 Draw Call 数量,但是减少了 CPU 在 SetPass 时的开销
    // 比如:
    // 1.大量使用同一 Shader 的物体
    //   场景里有上百种建筑、石头、家具,它们的材质不同(颜色/贴图不同),但是都用相同的 Shader
    // 2.大量动态物体,但材质切换频繁
    //   大量移动的敌人、NPC、动态场景物体(比如破碎的瓦片、掉落的武器),每个都有自己材质,但 Shader 相同
    // 3.使用 URP/HDRP 项目的大中型游戏
    //   特别是移动端,CPU 渲染线程经常成为瓶颈时,SRP Batcher 能显著降低 CPU 耗时

    // 不适合的场景:
    // 1.非 SRP 管线项目
    // 2.Shader 不兼容
    // 等等

    #endregion
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏