SRP批处理器
SRP Batcher(SRP批处理器)是一个渲染循环,它可以加速CPU在场景中的渲染,同时场景可以有很多使用同一shader的材质
使用SRP批处理器
要使用SRP批处理器,你的工程中必须使用可编程渲染管线.当然,也可以是下列之一:
在URP中激活激活SRP批处理器:
1.在Project窗口,选择URP资源
2.在该资源的Inspector面板中,找到Advanced栏,然后勾上SRP Batcher选项
你可以分别在URP或者HDRP的资源中取消SRP批处理器,默认情况下,SRP批处理器是打开的
.
在HDRP中,SRP批处理器默认是打开的,你无需禁用它.然而,如果你是处于调试目的而短暂关闭SRP批处理器的话,你可以这么做:
1.在Project窗口下,选择HDRP资源
2.在该资源的Inspector面板中,进入Debug Mode,这样就改变了HDRP资源属性的显示方式进而使得SRP批处理器的属性被显示出来
3.取消对SRP Batcher选项的勾选
你还可以在运行时对SRP批处理器的激活或者取消,要做到这一点,只需要在C#代码中对以下全局变量进行控制:
GraphicsSettings.useScriptableRenderPipelineBatching = true;
支持的平台
SRP批处理器差不多支持所有平台,这张表格展示了所支持的平台以及要求的最小unity版本
平台 | 要求的最小unity版本 |
---|---|
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批处理器
SRP的工作原理
在unity中,你可以在一帧之间的任一时刻修改任一材质的属性,然而,这会导致一些缺陷.举个例子来说,当调用图形接口使用新的材质时,就会有很多工作需要做,所以,你场景中材质越多,unity就会越频繁使用CPU去构建GPU所需的数据.为了解决频繁使用CPU问题的传统的办法是减少Drawcall的次数,这样一来就可以优化CPU的渲染开销,因为在drawcall之前unity必须构建大量的东西,与此同时,真实的CPU开销往往都是来自drawcall前的构建,GPU drawcall本身开销并不大,drawcall本身的过程就仅仅是unity将需要的字节传递到GPU的command buffer
SRP批处理器在多次drawcall之间减少GPU的构建,这是通过批处理一系列Bind和Draw这样的GPU命令而实现的
为了达到最大的渲染性能,这些批处理要尽可能的大,要做到这一点,你可以使用用了相同shader的很多的不同材质,但是你必须尽可能的少用Shader变量
在这个内部的渲染循环中,当unity探测到新的材质时,CPU会收集所有属性,同时还会在GPU显存中构建不同而连续的buffer,GPU buffer的数量是依据Shader声明的CBUFFER而定
为了加速一个使用了大量不同的材质而少量shader变量的场景,SRP专门集成了比如GPU数据持久性的范例
SRP批处理器是低端渲染循环,这个循环可以让材质数据持续保存在GPU显存中,如果材质数据没有变化,SPR批处理器就不需要构建并且发送缓冲到GPU,相反的,unity会使用一个专门的代码路径去快速的更新GPU大缓冲区中的unity引擎属性,就像这样:
在这里,CPU仅仅处理unity引擎的属性,以及被标记在上图的Per Object large buffer,所有准备使用的材质都可以在GPU显存中找到持续的CBUFFER.因为此刻所有的材质都保存在GPU显存中,所以,这加快了渲染的速度.对于所有的per-object而言,专用代码管理着一块大的per-object GPU CBUFFER
SRP批处理器的兼容性
关于SRP批处理器代码路径去渲染物体:
- 被渲染的物体必须有包含mesh或者skinned mesh,粒子还不行.
- Shader必须和SRP批处理器相兼容.在HDRP和URP中,所有的光照或者无光照Shader都必须适应这点要求(除了这些shader的阉割版)
对于一个shader要去兼容SRP批处理器:
- 你需要在名为“UnityPerDraw”的单CBUFFER中声明所有内建的引擎属性.比如,
unity_ObjectToWorld
, 或者unity_SHAr
- 你需要在名为
UnityPerMaterial
的单CBUFFER中声明所有材质属性
你可以在Inspector面板中看到shader的兼容性状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0rhUQNYO-1605775695775)(
在任何给定的场景,总会有一些物体和SRP批处理器相兼容,而一些没有,甚至当使用非兼容性的物体上,unity也能正确的渲染场景,这是因为兼容SRP批处理器的物体使用的是SRP批处理器代码路径,而那些非兼容的使用的是标准的SRP代码路径
使用SRP批处理器代码路径 | 未使用SRP批处理器代码路径 |
---|---|
* Mesh使用SRP批处理器兼容的shader | * 任何非mesh的物体 (包括了 skinned meshe * 任何使用了非兼容性shader的mesh * 使用材质属性块进行渲染 |
SRP批处理器的分析
当你使用SRP批处理器时,为了判断场景中速度的增加,你可以从SRP批处理器模板中添加 SRPBatcherProfiler.cs
这个C#脚本到你的场景中,当这个脚本运行的时候:按F8进行切换图层的显示,你的图层看起来会像这样:
时间测量是以ms为单位的,同时也展示了在Unity SRP渲染循环中CPU花费了多少时间.
注意:这里的时间是一帧当中被调用的所有RenderLoop.Drawand
Shadows.Draw标识之和,不用管线程的拥有者是谁.举个例子来说,如果你看到1.31ms SRP Batcher code path,这可能意味着draw call在主线程上耗费了0.31ms,另外1ms分散消耗在所有图形作业中
图层详情
这个表格描述了SRP Batcher图层的每一项设置,这个图层在运行状态下是可见的:
图层上的名称 | 描述 |
---|---|
(SRP batcher ON) / (SRP batcher OFF) | 表明当前SRP Batcher是否被激活,按F9对SRP Batcher进行开/关 |
CPU Rendering time | 表明CPU花费在SRP循环中所花费的总累计时间,不管当下使用的是哪一种多线程模式,例如单个作业、客户端/工作机作业或者图形作业.这是你可以观察到SRP Batcher效果的地方,为了能看到批处理器的最优化,尝试着开关SRP Batcher以看到不同的CPU使用率.在这个例子种,总共消耗2.11ms.(incl RT idle): 表明SRP在渲染线程中所消耗的空闲时间,这也意味着app在客户端/工作机模式下没有图像作业,这主要是由于主线程上的渲染线程必须等待图形指令.在这个例子中,渲染线程空闲了0.36ms |
SRP Batcher code path (flushes) | 指明你的游戏或者app在SRP批处理器代码路径上耗费的时间,这些消耗的时间被分解成了你的游戏花费在渲染所有游戏物体(除了阴影)上的时间(1.18ms),以及阴影(1.13ms).如果阴影数量很多,可以尝试减少你场景中的产生阴影的灯光数量,或者可以在你的渲染管线资源中选择较少量的Cascade.在这个例子中花费了1.31ms。flush的数量表明由于unity遇到一个新的shader变量时unity刷新场景的时间(这个例子是:89).flush的数量越小越好,因为这意味着在引擎中有较少数的shader变量 |
Standard code path (flushes) | 表明unity渲染那些和SRP批处理器不兼容的物体所花费的时间,比如渲染skinned mesh或者粒子.在这个例子中,SRP批处理器在0.80ms内处理了81个物体:渲染阴影的过程耗费了0.09ms,渲染其它的花费了0.71ms |
Global Main Loop: (FPS) | 表明全局的主循环时间,以毫秒为单位,和每秒的帧率(FPS)是等价的.注意,FPS是非线性的,这也导致当你看到FPS线性增长时,并不一定意味着你优化了场景.想要知道SRP批处理器是否优化了你的场景渲染,开启和关闭SRP批处理器,然后对比在CPU渲染时间下的数量 |
Unity帧调试中窗口中的SRP批处理器数据
你可以在 Frame Debugger 窗口中检查SRP批处理器“批处理”的状态.
为了检测状态:
1.在Editor中,Window > Analysis > Frame Debugger > Render Camera > Render Opaques,然后展开 RenderLoopNewBatcher.Draw列表
2.点击你想要查看的SRP Batch
SRP Batch的具体详情向你展示了draw call被使用的数量,哪个关键字依附于Shader,以及为什么不能和前一个SRP batch进行批处理的具体原因.在下图的例子中,声明的原因是:节点使用了不同的shader关键字,这也意味着,那个批处理中shader关键字和前一个批处理中的shader的关键字不同.如果SRP batcher使用了不同的shader变量,那么批处理就会被打破.如果在一次SRP批处理中只有少量的draw call,你很有可能使用了大量的shader变量
在帧调试窗口,你可以找到有关每次批处理的详细信息,这包括了SRP批处理器为什么会创建一个新的批处理而不是在已有的批处理中继续处理
如果你正在写属于你自己的用于代替URP或者HDRP的SRP,试着写一个使用最少关键字的通用"uber"Shader(当然了,你可以使用任何你想用的在材质内部的材质参数和材质属性)