Notifications
Article
SRP Batcher:加速渲染
Published 5 months ago
622
0
Unity 2018引入了可编程渲染管线SRP,其中包含新的底层渲染循环SRP Batcher批处理器,它可以大幅提高CPU在渲染时的处理速度,根据场景内容的不同,提升效果为原来的1.2~4倍不等。本文将介绍如何充分使用SRP Batcher。
下面的视频展示了Unity最难处理的情况:每个对象都是动态的,并且使用了不同颜色,纹理的材质。场景有很多外观相似的网格,但场景会以每个对象有不同网格的方式来运行,因此无法使用GPU Instancing功能。
在使用SRP Batcher后,在PlayStation 4平台上约有4倍的提速效果。
小提示:上面提到4倍的提速效果时,我们指的是CPU渲染代码,即“RenderLoop.Draw”和“ShadowLoop.Draw”分析器标记,并非全局帧率FPS。

Unity与材质

Unity编辑器拥有非常灵活的渲染引擎。我们可以在一帧中随时修改任意材质属性。此外Unity一直面向非常量缓冲区开发,支持DirectX9等图形API,但这些不错的功能有一些缺点,例如:当Draw Calls使用新材质时,需要进行很多处理。场景内的材质越多,设置GPU数据所需的CPU资源就越多。
在内部渲染循环期间,当检测到新材质时,CPU会收集所有属性,并在GPU内存中设置不同的常量缓冲区,GPU缓冲区的数量取决于着色器如何声明其CBUFFER。

SRP Batcher的工作流程

在开发SRP技术时,我们必须重写部分底层引擎。我们发现了在本地集成新范例的机会,例如:GPU数据持久化。
我们的目标是提高常见用例的速度,在常见用例中,场景会使用大量不同材质和少量着色器变体。
现在,底层渲染循环可以使材质数据在GPU内存中具有持久性。如果材质内容没有改变,则不需要设置并上传缓冲区到GPU。此外我们还使用了专用代码路径,从而快速更新大型GPU缓冲区的内置引擎属性。
新的渲染工作流程如下图所示。
在这个工作流程中,CPU只处理内置引擎属性,它们被标记为对象矩阵变换。所有材质都有位于GPU内存的持久性CBUFFER,可以随时使用。
提速效果源于二个方面:
  • 每个材质内容现在都会一直保留在GPU内存中
  • 专用代码会管理大型“per object” GPU CBUFFER

启用SRP Batcher

你的项目必须使用轻量级渲染管线LWRP,高清晰渲染管线HDRP或自定义SRP。为了在HDRP或LWRP中启用SRP Batcher,请在SRP资源的检视窗口勾选SRP Batcher复选框。
如果想在运行时启用或禁用SRP Batcher,以测量基准性能增益效果,我们也可以使用C#代码启用该全局变量。
GraphicsSettings.useScriptableRenderPipelineBatching = true;

SRP Batcher兼容性

为了使对象通过SRP Batcher代码路径渲染,要注意二个要求:
  • 对象必须处于网格中。对象不可以是粒子或蒙皮网格。
  • 必须使用兼容SRP Batcher的着色器。HDRP和LWRP中的所有Lit Shader受光着色器和Unlit Shader无光照着色器都符合此要求。
为了让着色器兼容SRP,需要进行以下处理:
  • 所有内置引擎属性必须在名为“UnityPerDraw”的CBUFFER中声明。例如:unity_ObjectToWorld或unity_SHAr。
  • 所有材质属性必须在名为“UnityPerMaterial”的单个CBUFFER中声明。
我们可以在检视窗口查看着色器的SRP Batcher兼容状态,该兼容状态只会在使用SRP的项目中显示。
在特定场景中,某些对象兼容SRP Batcher,有些则不兼容。但场景仍会正常渲染。兼容对象会使用SRP Batcher代码路径,而其它对象仍会使用标准SRP代码路径。

性能分析

SRPBatcherProfiler.cs

如果想要测量SRP Batcher对特定场景的速度提升效果,可以使用SRPBatcherProfiler.cs脚本,只要将该脚本添加到场景即可。
获取SRPBatcherProfiler.cs,请访问SRP Batcher项目模板页:
https://github.com/Unity-Technologies/SRPBatcherBenchmark.git
当脚本运行时,我们可以使用F8键切换覆盖显示画面。我们也可以使用F9键开启或关闭SRP Batcher。如果在运行模式启用覆盖画面,我们会看到许多有用信息。
下图中所有的时间都以毫秒为单位,这些时间测量显示CPU在Unity SRP渲染循环中花费的时间。
小提示:这些时间值表示在一帧中调用的所有“RenderLoop.Draw”和“Shadows.Draw”标记的累计时间,无论线程所有者是谁。当看到“1.31ms SRP Batcher code path”时,可能其中0.31毫秒用于主线程,1毫秒用于所有图形作业。

覆盖信息

下面的表格中,可以看到运行模式下覆盖画面每项设置的说明。
我们不愿意在覆盖画面底部添加FPS信息,因为优化时要对FPS指标非常小心,出于以下二方面考虑:
  • FPS不是线性的,因此FPS提高20%并不表示场景有对应的优化效果。
  • FPS表示的是全局帧数。FPS或全局帧时间值取决于渲染外的很多其它因素,例如:C#游戏性,物理和遮蔽等。

多场景基准

下面一些Unity场景截图,它们分别展示开启和关闭SRP Batcher的画面,我们可以查看不同情况下的速度提升效果。

死者之书

《死者之书》项目,使用HDRP,平台为PlayStation 4,具有1.47倍的提速效果
FPS并未改变,因为该场景与GPU绑定。我们得到剩余的12毫秒来在CPU进行其它处理,提速效果和PC几乎一致。

FPS示例项目

FPS示例项目,使用HDRP,平台为PC DirectX 11,具有1.23倍的提速效果
因为场景有部分内容不兼容SRP Batcher,仍有1.67毫秒用于标准代码路径。本示例中,不兼容的内容为蒙皮网格和使用材质属性块渲染的部分粒子。

Boat Attack

Boat Attack项目,使用LWRP,平台为PlayStation 4,具有2.13倍的提速效果
Boat Attack项目下载地址:
https://github.com/Verasl/BoatAttack

支持平台

SRP Batcher几乎适用于所有平台。下面表格展示了支持平台和所需最低Unity版本的信息。Unity 2019.2目前处于开放式Alpha测试阶段。

关于VR

SRP Batcher快速代码路径支持VR,但仅支持“SinglePassInstanced”模式。启用VR不会添加任何CPU时间,因为使用了SinglePassInstanced模式。

常见问题

如何知道是否已充分使用SRP Batcher?

请使用SRPBatcherProfiler.cs,首先检查SRP Batcher是否已经启用。然后查看“Standard code path”的时间值,该值应该接近于0,而且所有时间都应该用在“SRP Batcher code path”部分。
如果场景内使用了部分蒙皮网格或粒子,部分时间用于标准代码路径是正常现象。请访问SRP Batcher Benchmark项目了解详情:
https://github.com/Unity-Technologies/SRPBatcherBenchmark.git

为什么无论SRP Batcher是否开启,SRPBatcherProfiler都显示相似的时间使用情况?

首先,检查是否几乎所有渲染时间都通过新的代码路径。如果是,而且数值仍旧相似,那么请检查“flush”数值。
“flush”值应该在SRP Batcher开启时大幅减小。根据我们的经验,SRP Batcher开启后“flush”值变为原来的1/10是很理想的效果,变为原来1/2也很不错。
如果flush值没有减小太多,这意味着仍有很多着色器变体,请尝试减少着色器变体数量。如果使用了很多不同的着色器,请尝试制作带有更多参数的“特别”着色器。这样拥有大量不同材质参数不会有太大影响。

在启用SRP Batcher时,为什么全局FPS没有变化?

请先查看前面的二个问题。如果SRPBatcherProfiler显示“CPU Rendering time”(CPU渲染时间)是原来的2倍速度,而FPS没有变化,那么CPU渲染部分不是运行效果的瓶颈。
这并不意味着场景不和CPU绑定,相反,你也许使用太多C#游戏效果,或是太多物理元素。无论如何,如果“CPU Rendering time”是原来的2倍速度,它仍有积极的作用。
在前面的示例视频中,即使有3.5倍的提速效果,场景仍旧是60fps。那是因为我们开启了VSNYC。SRP Batcher节省了CPU部分的6.8毫秒时间,这些时间可用于其它任务,也可以节省移动设备的电池寿命。

检查SRP Batcher效率

理解什么是SRP Batcher中的“批处理”非常重要。过去,开发者往往倾向减少Draw Calls的数量来优化CPU渲染成本。
这样做的原因是,引擎必须在发起Draw Calls前进行大量设置,真正的CPU成本来自这些设置过程,而不是来自GPU Draw Calls本身。
SRP Batcher不会Draw Calls的数量,它只会降低Draw Calls之间的GPU设置成本。
下图详述这个工作流程。
左侧是标准SRP渲染循环,右侧是SRP Batcher循环。在SRP Batcher中,“批处理”其实是指“绑定”,“绘制”这样的GPU指令序列。
在标准SRP中,会为每个新材质调用缓慢的SetShaderPass。在SRP Batcher中,会为每个新着色器变体调用SetShaderPass。
为了获得最佳性能,我们需要保持尽可能大的批处理。因此我们需要避免着色器变体的改动,但如果材质都使用相同的着色器,我们使用任何数量的不同材质。
我们可以使用Unity Frame Debugger来查看SRP Batcher“批处理”的长度。在Unity Frame Debugger中,每次批处理是被称为“SRP Batcher”的事件,如下图所示。
请查看左侧的SRP Batcher事件,同样要注意批处理的大小,即绘图调用的次数,此处为109次,这是非常高效的批处理。
我们也会在此看到之前批处理受损的原因:“节点使用了不同的着色器关键字”。这意味着,该批处理使用的着色器关键字和之前批处理中的关键字不同,而且它也表示着色器变体没有改变,我们必须破坏批处理。
在某些场景中,部分批处理大小会很小,如下图所示。
批处理大小只有2,这可能表示我们有太多不同的着色器变体。如果创建了自定义SRP,请尝试使用最少的关键字编写通用的“特别”着色器。不必担心在“属性”部分加入的材质参数数量。
小提示:显示Unity Frame Debugger的SRP Batcher信息需要使用Unity 2018.3或更高版本。

使用兼容着色器编写自定义SRP

这部分内容面向编写自定义可编程渲染循环Scriptable Render Loop和着色器库的高级用户提供。LWRP或HDRP用户可以跳过这部分,因为我们提供的所有着色器都已经兼容SRP Batcher。
如果你要编写自定义渲染循环,着色器必须遵循一些规则,才能通过SRP Batcher代码路径。

“Per Material”变量

首先,所有“Per material”(材质相关)数据都应该在名为“UnityPerMaterial”的单个CBUFFER中声明。
什么是“Per material”数据?通常是在“着色器属性”部分声明的所有变量,这都是“Per material”数据。即艺术家可以使用材质GUI检视窗口调整的所有变量。例如:我们看看下面这个简单的着色器。
Properties { _Color1 ("Color 1", Color) = (1,1,1,1) _Color2 ("Color 2", Color) = (1,1,1,1) } float4 _Color1; float4 _Color2;
如果编译该着色器,着色器检视窗口会如下图所示。
为了修复该问题,按照下面代码声明所有“Per material”数据即可。
CBUFFER_START(UnityPerMaterial) float4 _Color1; float4 _Color2; CBUFFER_END

“Per Object”变量

SRP Batcher还需要名为“UnityPerDraw”的特别CBUFFER,该CBUFFER应该包含所有Unity的内置引擎变量。
“UnityPerDraw” CBUFFER内的变量声明顺序也很重要,所有变量都应该遵循名为“Block Feature”的布局。例如:“Space Position block feature”应该包含所有变量,顺序如下。
float4x4 unity_ObjectToWorld; float4x4 unity_WorldToObject; float4 unity_LODFade; float4 unity_WorldTransformParams;
如果不需要,则不必声明部分块功能。“UnityPerDraw”中的所有内置引擎变量都应该为float4或float4x4类型。
在移动平台上,开发者可能想使用real4类型,以节省部分GPU带宽。不是所有UnityPerDraw变量都可以使用real4类型。详情请查阅下方表格的“Could be half”列。
下方的表格描述“UnityPerDraw” CBUFFER中可使用的所有块功能。
小提示:如果功能块的一个变量被声明为real4,即half,那么该功能块的所有可用变量都应该被声明为real4。
始终要检查检视窗口中新着色器的兼容状态。我们会检查到多个潜在错误,并显示不兼容的原因。
在编写自定义SRP着色器时,你可以参考LWRP或HDRP资源包的UnityPerDraw CBUFFER声明,以得到一些启发。

未来展望

我们会不断改进SRP Batcher,提高部分渲染通道的批大小,特别是阴影通道和深度通道。
MegaCity演示使用的全新DOTS渲染器。该演示在Unity编辑器中的提速效果非常惊人,从10fps提升到了50 fps。
下面的视频为编辑器内使用了SRP Batcher和DOTS渲染器的MegaCity演示。性能上的区别非常大,即使是全局帧率也提高了5倍。
小提示:确切的说,由于编辑器目前没有图形作业,因此启用SRP Batcher后的提速效果仅限于编辑器内,在Standalone模式下,提速效果为原来的2倍。
下面视频为MegaCity在编辑器内的效果。如果可以在60hz下播放该视频,你会感受到启用SRP Batcher时的提速效果。
现在,具有DOTS渲染器的SRP Batcher仍处于实验开发阶段。
更多Unity最新功能,尽在Unity Connect平台(Connect.unity.com)。
Unity China
641
Comments