当前位置: 首页 > backend >正文

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_ENDsampler2D _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

http://www.xdnf.cn/news/13509.html

相关文章:

  • DEM 地形分析与水文建模:基于 ArcGIS 的流域特征提取
  • Android 10.0 勿扰模式开启关闭功能实现
  • DevOps软件开发流程规范
  • 抖音授权登录-获取用户授权调用凭证
  • MySQL进阶之索引(1)索引结构分类语法和SQL性能分析
  • Guava常用工具类使用教程
  • 使用OpenCV和Python进行图像掩膜与直方图分析
  • Java基于局域网的聊天室系统设计与实现,附源码+论文
  • ACS的ExtendedSegmentArc1 方法说明
  • 代理模式:AOP 切面编程的底层实现基础
  • Hello Robot发布Stretch3机器人高保真模拟平台-Stretch MuJoCo v0.5-涵盖数百种Robocasa厨房应用测试场景
  • 零基础设计模式——行为型模式 - 中介者模式
  • 使用Jmeter做功能测试有哪些优点?
  • C++ 中的 iostream 库:cin/cout 基本用法
  • Python训练第五十天
  • milvus 总结
  • Uniapp实现多选下拉框
  • 微信小程序Echarts开发问题
  • Vue 数据代理机制对属性名的要求
  • 如何正确的用Trae 打开 Unity 3D 项目
  • 计算机视觉与深度学习 | 基于Matlab的低照度图像增强算法:全面总结与实现
  • 问题八、Articulation中的actuator(执行器)
  • PostgresSQL日常维护
  • Jenkins + Docker + Kubernetes(JKD)自动化部署全链路实践
  • Axure应用交互设计:文本输入计数、显示输入内容、AI对话
  • 适配器模式深度解析:Java设计模式实战指南与接口兼容性解决方案
  • SpringMVC(1)
  • 安全生产管理是什么?安全生产管理主要管什么?
  • Oracle查看锁表并终止会话
  • Hive面试题汇总