Unity3D SRP Batcher原理分析

前言

Unity的SRP Batcher是一种高性能渲染批处理机制,专为可编程渲染管线(Scriptable Render Pipeline, SRP)设计,旨在显著降低Draw Call带来的CPU开销,提升渲染性能。它的核心原理与传统批处理(静态/动态批处理)和GPU Instancing不同,更侧重于减少CPU与GPU之间的通信开销状态切换

对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀!

核心问题:传统批处理的瓶颈

  1. Draw Call开销: 每次调用DrawMeshDrawRenderer,CPU都需要向GPU发送大量指令和数据(顶点、索引、材质属性、纹理、着色器状态等),涉及昂贵的线程同步和驱动层开销。
  2. 材质切换开销: 当相邻渲染对象使用不同的材质时,GPU需要切换状态(Shader程序、纹理、材质属性常量缓冲区等)。这种切换非常耗时。
  3. 传统批处理的限制:
  • 静态批处理: 合并网格顶点,但仅适用于静态不动的对象,增加内存和磁盘占用。
  • 动态批处理: 运行时合并顶点简单的网格,限制极多(顶点数、蒙皮、光照贴图等),且合并本身也有CPU开销。
  • GPU Instancing: 高效绘制大量相同网格和材质的对象,但对材质变体(不同属性值)的支持有限且需要特定Shader编写。

SRP Batcher 的解决之道

SRP Batcher的核心思路是:将频繁变化的数据(每个对象特有的数据)与不频繁变化的数据(材质数据)分离管理,并利用GPU的持久化内存(Persistent Data)来最小化CPU-GPU通信和状态切换。

  1. 数据分离与重组:
  • 每对象属性(Per-Object Data): 包括模型矩阵(unity_ObjectToWorld)、法线矩阵(unity_WorldToObject)、光照探针数据、Lightmap索引/偏移等。这些数据每个渲染对象实例都不同
  • 每材质属性(Per-Material Data): 包括_BaseColor_Metallic_Smoothness_MainTex_ST以及纹理引用等。这些数据在共享同一材质的多个对象之间是相同的(或变化不频繁)。
  • 常量缓冲区(Constant Buffer, CBUFFER)优化:
  • 专用CBUFFER: SRP Batcher要求Shader必须使用特定的CBUFFER宏来声明这些属性:
    • UnityPerDraw: 包含每对象属性
    • UnityPerMaterial: 包含每材质属性
  • 示例Shader代码片段:
// Per-Object数据 (每个实例不同)
CBUFFER_START(UnityPerDraw)
    float4x4 unity_ObjectToWorld;
    float4x4 unity_WorldToObject;
    // ... 其他每对象数据 (如LightProbe SH, Lightmap ST)
CBUFFER_END

// Per-Material数据 (同一材质相同)
CBUFFER_START(UnityPerMaterial)
    float4 _BaseColor;
    float _Metallic;
    float _Smoothness;
    float4 _MainTex_ST;
CBUFFER_END
    sampler2D _MainTex; // 纹理在另一个特殊块中声明
    1. GPU端的持久化内存:
  • SRP Batcher在GPU显存中开辟了一块持久化的内存区域
  • 材质数据(UnityPerMaterial): 当一个材质首次被渲染时,它的所有UnityPerMaterial数据(包括纹理绑定)会被上传到这个持久化区域,并长期驻留在GPU内存中。之后使用该材质的对象渲染时,不再需要重新上传这些数据
  • 对象数据槽(Per-Object Data Slots): 这块持久化内存也被组织成一个大的对象数据列表。每个可以被SRP Batcher批处理的对象实例,在这个列表中都有一个预分配的固定槽位(Slot)
  • 渲染流程优化:
  1. 准备阶段(CPU):
  • SRP(如URP/HDRP的Culling过程)收集所有符合SRP Batcher条件的渲染对象(使用兼容Shader)。
  • 材质对这些对象进行排序(尽量减少材质切换)。
  • 对于每个对象,CPU只准备它自己的每对象属性数据UnityPerDraw内容)。
  • 批处理提交(CPU -> GPU):
  • 单次大块数据传输: CPU将当前帧所有需要渲染对象的每对象属性数据(仅仅是UnityPerDraw数据)一次性打包上传到GPU持久化内存中它们各自预分配的槽位里。这是一个非常高效的大块内存拷贝操作。
  • 状态绑定: 对于一批共享同一材质的对象:
    • GPU绑定该材质的顶点着色器(VS)和片元着色器(FS)(第一次或材质变化时绑定)。
    • GPU绑定该材质在持久化内存中的材质属性数据地址UnityPerMaterial)。这个绑定在材质未改变时保持不变。
  • 绘制调用(Draw Call): CPU发出一个DrawMesh指令序列(可能包含多个子网格SubMesh)。关键点在于:
    • 这个DrawMesh调用不是为单个对象,而是为当前材质批内的所有对象
    • 在绘制每个对象时,顶点着色器(VS)知道如何根据对象的InstanceID(或一个专门的索引)去GPU持久化内存的对象数据列表对应的预分配槽位里读取该对象特有的UnityPerDraw数据(如unity_ObjectToWorld)。
    • 片元着色器(FS)则直接访问绑定好的材质数据(UnityPerMaterial)和纹理。

SRP Batcher 的关键优势

  1. 大幅减少Draw Call: 将共享同一材质的多个对象的渲染合并到一个或少数几个Draw Call中提交,显著降低了Draw Call数量带来的CPU开销。
  2. 消除材质切换开销: 因为材质数据(UnityPerMaterial + 纹理)已持久化在GPU内存中,切换材质时只需要绑定一次Shader程序和材质数据地址,避免了传统管线中逐个设置大量材质属性和纹理绑定的巨大开销。绑定改变只发生在材质实际发生变化时。
  3. 高效的对象数据更新: 每帧只更新变化的每对象数据,并且是高效的大块内存传输到GPU预定义槽位,避免了传统方式中为每个对象单独设置常量缓冲区的开销。
  4. 内存友好: 相比静态批处理合并顶点数据导致内存膨胀,SRP Batcher只额外存储了每对象属性数据(相对较小),内存占用更优。
  5. 兼容动态对象: 对象可以自由移动、旋转、缩放(动态修改Transform),不受静态批处理的限制。

启用SRP Batcher的条件

  1. 使用SRP: 必须在URP或HDRP(或自定义SRP)中使用。
  2. 兼容的Shader:
  • 必须使用CBUFFER_START(UnityPerDraw)CBUFFER_START(UnityPerMaterial)(或UnityPerMaterial的别名)来声明属性。
  • 不能使用材质属性块(MaterialPropertyBlock)。MPB会破坏材质数据的共享性。
  • 满足SRP Batcher对Shader代码结构的其他要求(如顶点着色器输入结构)。
  • 兼容的渲染对象:
  • 使用兼容Shader的材质。
  • 非蒙皮网格(SkinnedMeshRenderer通常不支持)。
  • 未使用MPB。
检查工具: Unity Frame Debugger 窗口会明确标识哪些Draw Call是通过 "SRP Batch" 提交的。

SRP Batcher vs. GPU Instancing

  • 目标场景不同: SRP Batcher主要优化不同材质数量有限,但共享材质的对象数量众多的场景(如场景中的大量不同位置的岩石、树木、家具)。GPU Instancing主要优化大量完全相同的网格和材质实例(如草、子弹、人群)。
  • 数据驱动方式不同: SRP Batcher依赖GPU持久化内存和预分配槽位。GPU Instancing通过InstanceID和实例数据缓冲区传递数据。
  • 兼容性: SRP Batcher对Shader的要求相对GPU Instancing更宽松一些(GPU Instancing需要更显式的支持)。两者可以共存,一个对象如果同时满足两者条件,通常会优先使用GPU Instancing(因为它更高效)。

总结

SRP Batcher是Unity SRP的核心优化技术,它通过分离对象数据与材质数据、利用GPU持久化内存预分配对象数据槽位以及按材质排序后批量提交Draw Call的方式,极大地减少了CPU在设置渲染状态和提交Draw Call上的开销,特别是显著降低了材质切换的成本。要利用好SRP Batcher,关键在于编写符合其规范的Shader(正确使用UnityPerDrawUnityPerMaterial CBUFFER)并避免使用MaterialPropertyBlock。它在优化拥有大量共享材质的动态对象场景时效果尤为显著。

更多教学视

Unity3D​www.bycwedu.com/promotion_channels/2146264125

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值