SRP Batcher 是一个渲染循环,可通过许多使用同一着色器变体的材质来加快场景中的 CPU 渲染速度。
要使用 SRP Batcher,项目必须使用可编程渲染管线。可编程渲染管线可以是:
要在 URP 中激活 SRP Batcher,请执行以下操作:
在 HDRP 中,SRP Batcher 默认为启用状态,不应将其禁用。不过,如果需要暂时禁用 SRP Batcher 以进行调试:
也可以在运行时启用或禁用 SRP Batcher。为此,请在 C# 代码中切换以下全局变量:
GraphicsSettings.useScriptableRenderPipelineBatching = true;
SRP Batcher 适用于几乎所有平台。下表显示了支持的平台以及所需的最低 Unity 版本。
Platform | Minimum Unity version required |
Windows DirectX 11 | 2018.2 |
PlayStation 4 | 2018.2 |
Vulkan | 2018.3 |
OSX Metal | 2018.3 |
iOS Metal | 2018.3 |
Nintendo Switch | 2018.3 |
Xbox One DirectX 11 | 2019.2 |
OpenGL 4.2 and higher | 2019.1 |
OpenGL ES 3.1 and higher | 2019.1 |
Xbox One DirectX 12 | 2019.1 |
Windows DirectX 12 | 2019.1 |
注意:对于 XR,只能以 SinglePassInstanced
模式来使用 SRP Batcher。
Unity 中,可以在一帧内的任何时间修改任何材质的属性。但是,这种做法有一些缺点。例如,DrawCall 使用新材质时,要执行许多作业。因此,场景中的材质越多,Unity 必须用于设置 GPU 数据的 CPU 也越多。解决此问题的传统方法是减少 DrawCall 的数量以优化 CPU 渲染成本,因为 Unity 在发出 DrawCall 之前必须进行很多设置。实际的 CPU 成本便来自该设置,而不是来自 GPU DrawCall 本身(DrawCall 只是 Unity 需要推送到 GPU 命令缓冲区的少量字节)。
SRP Batcher 通过批处理一系列 Bind
和 Draw
GPU 命令来减少 DrawCall 之间的 GPU 设置。
为了获得最大渲染性能,这些批次必须尽可能大。为了实现这一点,可以使用尽可能多具有相同着色器的不同材质,但是必须使用尽可能少的着色器变体。
在内渲染循环中,当 Unity 检测到新材质时,CPU 会收集所有属性并在 GPU 内存中设置不同的常量缓冲区。GPU 缓冲区的数量取决于着色器如何声明其 CBUFFER。
为了在场景使用很多不同材质但很少使用着色器变体的一般情况下加快速度,SRP 在原生集成了范例(例如 GPU 数据持久性)。
SRP Batcher 是一个低级渲染循环,使材质数据持久保留在 GPU 内存中。如果材质内容不变,SRP Batcher 不需要设置缓冲区并将缓冲区上传到 GPU。实际上,SRP Batcher 会使用专用的代码路径来快速更新大型 GPU 缓冲区中的 Unity 引擎属性,如下所示:
在此处,CPU 仅处理上图中标记为 Per Object large buffer 的 Unity 引擎属性。所有材质在 GPU 内存中都有持久的 CBUFFER,可供随时使用。这样会加快渲染速度,原因是: 现在,所有材质内容都持久保留在 GPU 内存中。 专用代码针对所有每对象属性,管理着一个大型的每对象 GPU CBUFFER。
在任何给定场景中,有些对象与 SRP Batcher 兼容,而有些对象则不兼容。即使在使用不兼容的对象时,Unity 也会正确渲染场景。这是因为兼容对象使用 SRP Batcher 代码路径,而其他对象则使用标准 SRP 代码路径。
为了使 SRP Batcher 代码路径能够渲染对象:
为了使着色器与 SRP Batcher 兼容:
unity_ObjectToWorld
或 unity_SHAr
。UnityPerMaterial
的 CBUFFER 中声明所有材质属性。可以在 Inspector 面板中查看着色器的兼容性状态。
为了测量使用 SRP Batcher 时场景中的增速,请将 SRPBatcherProfiler.cs
C# 脚本从 SRP Batcher 模板添加到您的场景。当此脚本正在运行时:
使用 F8 键来切换覆盖显示。
也可以在运行过程中使用 F9 键来打开和关闭 SRP Batcher。
此覆盖如下所示:
时间测量值以毫秒 (ms) 为单位,并显示 CPU 在 Unity SRP 渲染循环中花费的时间。
注意:此处的时间等于在一帧内调用的所有 RenderLoop.Draw
和 Shadows.Draw
标记的累积时间(与线程所有者无关)。例如,如果看到 1.31ms SRP Batcher 代码路径,表示绘制调用可能在主线程上花费了 0.31ms,并且 1ms 分散在所有图形作业中。
下表描述了在运行模式下可见的 SRP Batcher 覆盖中的每个设置:
覆盖中的名称 | 描述 | |
---|---|---|
(SRP batcher ON)/(SRP batcher OFF) | 指示是否已启用当前的 SRP Batcher。要打开或关闭 SRP Batcher,请按 F9。 | |
CPU Rendering time | 指示 SRP 循环在 CPU 中花费的总累积时间,无论使用的是哪种多线程模式(例如单个客户端/工作线程或图形作业)。在这里可以最大限度看到 SRP Batcher 的效果。要查看 SRP Batcher 的优化,请尝试将其打开和关闭来查看 CPU 使用率的差异。在上述示例中,总时间为 2.11 毫秒。 (incl RT idle):__指示 SRP 在渲染线程中花费的空闲时间。这可能意味着应用程序处于没有任何图形作业的客户端/工作模式;在渲染线程必须等待主线程上的图形命令时会发生此情况。在以上示例中,渲染线程空闲了 0.36 毫秒。| ||SRP Batcher code path (flushes)|指示您的游戏或应用程序花费在 SRP Batcher 代码路径中的时间。此时间包括游戏渲染 All objects__(阴影通道除外)(1.18ms) 和 Shadows (1.13ms) 这两个方面。如果 Shadows 数量较大,请尝试减少场景中阴影投射光源的数量,或在渲染管线资源中选择较少数量的级联。在以上示例中,该时间为 1.31ms。 (flush) 数字表示 Unity 由于遇到新着色器变体而刷新场景的次数(在此示例中为 89)。较少的刷新次数总是更好一些,因为这意味着帧中的着色器变体数量较少。 |
|
Standard code path (flushes) | 指示 Unity 在渲染与 SRP Batcher 不兼容的对象(如粒子)方面花费的时间。 在以上示例中,SRP Batcher 在 0.80 毫秒内刷新了 81 个对象:阴影通道为 0.09 毫秒,所有其他通道为 0.71 毫秒。 |
|
Global Main Loop: (FPS) | 指示全局主循环时间(以毫秒为单位)以及等效的每秒帧数 (FPS)。注意:FPS 并非线性,因此,如果发现 FPS 增加 20,不一定意味着已经优化场景。若要查看 SRP Batcher 是否优化了场景渲染,请开启和关闭该功能,然后比较 CPU Rendering time 下的数字。 |
可以在 Frame Debugger 窗口中检查 SRP Batcher“批次”的状态。
要检查 SRP Batcher 批次的状态,请执行以下操作:
SRP Batch 详细信息可以显示使用了多少个绘制调用、着色器附加了哪些关键字以及该特定绘制调用未与前一个调用一起接受批处理的原因。在以下示例中,所述原因是:_Node use different shader keywords_。这意味着该批次的着色器关键字不同于前一个批次中的关键字。由于 SRP Batcher 使用了另一个不同的着色器变体,因此这一批次已破坏。如果一个 SRP 批次的绘制调用数量较少,您很可能使用了太多的着色器变体。
如果您要编写自己的 SRP 而不是使用 URP 或 HDRP,请尝试使用最少的关键字来编写通用的“超级”着色器(但可以在每个材质中使用尽可能多的材质参数和材质属性)。