四时宝库

程序员的知识宝库

来讲讲URP 系列教程

今天带大家了解下什么是 SRP Batcher 与它的工作原理、如何在 URP 工程中去使用 SRP Batcher 以及针对大家在 URP 学习中所遇到的一些问题的解答。

SRP Batcher 的定义与工作原理

我们来看下定义:SRP Batcher是一个渲染循环,它可以在使用很多相同的 Shader Variant 的材质的场景中,加速你的 CPU 渲染。也就是说:我们不但可以通过使用尽可能少的 Shader Variant 来加速,而且也可以使尽可能多的不同材质来使用相同的 Shader Variant 来实现这一性能提升。


那为什么 SRP Batcher 可以实现加速呢?我们来看下它的工作原理。


通常情况下,在 Unity 中,可以在一帧内的任何时间修改任何材质的属性。但是,这种做法有一些缺点。例如,DrawCall 使用新材质时,要执行许多工作。因为场景中的材质越多,Unity 必须用于设置 GPU 数据的 CPU 的工作也就越多。


解决此问题的传统方法是减少 DrawCall 的数量以优化 CPU 渲染成本,因为 Unity 在发出 DrawCall 之前必须进行很多设置。实际的 CPU 成本便来自这些设置,而不是来自 GPU DrawCall 本身(DrawCall 只是 Unity 需要推送到 GPU 命令缓冲区的少量字节)。


而我们的 SRP Batcher 通过批处理一系列 Bind 和 Draw GPU 命令来减少 DrawCall 之间的 GPU 设置的工作量。也就是之前一堆绑定和绘制的 GPU 命令,我们可以使用批处理减少绘制调用之间的 GPU 设置。


Bind和Draw命令的批处理可以减少绘制调用之间的GPU设置

我们可以在 XCode 抓取一帧的数据上也可以明显的看到,如下图所示左边为未开启 SRP Batcher,右边为开启 SRP Batcher;可以看到开启 SRP Batcher 后对比开启前绘制调用之间的 GPU 设置明显的减少。



如何在 URP 工程中使用 SRP Batcher


要使用 SRP Batcher,项目必须使用可编程渲染管线。可编程渲染管线可以是:


通用渲染管线 (URP)

高清渲染管线 (HDRP)

自定义 SRP


因为本篇文章主题是 URP 系列教程,所以接下来我们就手把手带大家一起学习如何在 URP 中使用 SRP Batcher。还是跟往常一样,首先得有自己的 URP 工程。

01 新建 URP 工程

02 创建并设置 URP Assets

在 URP 工程 Assets 目录下 Create > Rendering > Universal Render Pipeline > Pipeline Asset。


然后点击 Edit > Project Settings > Graphics,将这里的 Pipeline Settings 设置为我们已经创建好的 URP Assets。

03 在 URP 中激活 SRP Batcher

点击我们创建并设置好的 URP Assets,你会在 Inspector 面板中 Advanced 选项,展开发现 SRP Batcher 选项,点击勾选(默认已经勾选)。

04 URP Shader 兼容性


不是所有的对象都可以用 SRP Batcher 来渲染,对于渲染对象是有条件限制的。这个对象必须是 mesh 或者 Skinned Mesh,而不能是个 particle。Shader 也必须与 SRP Batcher 兼容,它封装了 URP 中的所有 lit 和 unlit Shader(除了这些 Shader 的粒子版本)。我们可以在 Inspector 中查看 Shader 的兼容性。


05 如何使 Shader 与 SRP 匹配

根据手册可知,要想匹配是有条件的

所有的 Material 属性都要在一个名为 UnityPerMaterial 的 CBUFFER 中声明。

Shader 中所有的内置属性例如unity_ObjectToWorld,unity_WorldTransformParams等,都要在一个名为 UnityPerDraw 的 CBUFFER 中声明。


也就是 Properties 中的参数在声明时,要被包含在了一个这样的语法中:

举个栗子:


当然即使在使用不兼容的对象时,Unity 也会正确渲染场景。这是因为兼容对象使用 SRP Batcher 代码路径,而其他对象则使用标准 SRP 代码路径。


其实到这里,如何在 URP 工程中使用 SRP Batcher 就已经讲完了。但是关于 SRP Batcher 的故事还没有结束。有一位网友在 URP 学习交流群中问了一个问题,我在 SRP Batcher Demo 制作过程中也遇到了同样的问题:URP 在开启 SRP Batcher 后,我们的统计面板会有一些奇怪的变化:比如开启前后 Batches 没有变化、Saved by batching 变成了0、Shadow Casters 有时候也会为负数等?


答案是这里有可能是显示的问题。目前我们的统计面板只能显示默认渲染管线中的这些数据,它“不理解” URP 默认使用的 SRP Batcher。统计面板只能够简单地显示一些数据,我们该如何对 SRP Batcher 进行数据分析呢?这就是我们接下来要讨论的问题。


对 SRP Batcher 进行数据分析


在这里我将为大家演示两种对 SRP Batcher 开启前后进行数据分析的手段,第一个是使用 Frame Debugger 对 SRP Batcher 进行数据分析,第二个是添加 SRPBatcherProfiler.cs 脚本对 SRP Batcher 数据进行分析。我们一起来看一下具体步骤。

使用 Frame Debugger 对 SRP Batcher

进行数据分析

我们可以使用 Frame Debugger 来对 SRP Batcher 时的数据和程序进行分析或者调试。


01 打开帧调试器窗口,选择下拉菜单:Windows > Analysis > Frame Debugger


02 通过选择 Enable 启用 Frame Debugger


03 我们来实际的测试下。我们新建一个 URP 工程,创建并分配好了 URP Assets。


首先我们先通过 Create > Shader > lit Shader 创建一个简单的 Lit Shader。接着我们创建两个 Material,分别命名为 Cube_Drywall_Lit_v1,Cube_Drywall_Lit_v2。这两个 Material 都使用我们刚刚创建好的 Lit Shader,并分别引用两种的 Texture V1、V2。

04 然后我们创建 100 个 Cube,然后分别引用上面我们创建好的材质 Cube_Drywall_Lit_v1 与 Cube_Drywall_Lit_v2。



05 我们先尝试关闭下我们的 SRP Batcher,接着我们启用 Frame Debugger 看看数据:我们发现 100 Cube 产生了 100 个 DC !



06 接着我们尝试打开 SRP Batcher(这里的 URP Assets 是默认使用的,我没有做修改),看看数据:哦吼,直接由 100 降到了 1!



但是这里值得注意的是:这不代表只使用了一个 DrawCall 来显示了这些内容,而是对它们进行了序列上的优化。DrawCall 的数量还是 100 没有变化,我们只是提高了他的效率。



添加 SRPBatcherProfiler.cs 脚本对 SRP Batcher 进行数据分析


01 在你的 URP 场景中创建一个空的 GameObject(这里我将其命名为 Profiler);



02 将 SRP Batcher 模板中的 SRPBatcherProfiler.csC# 脚本(详情请见文末参考资料[4])添加给 Profiler。运行这个脚本:会在场景中出现 SRP Batcher 叠加层,我们就可以找到有关 SRP Batcher 的一些详细信息。



03 SRP Batcher 叠加层中的时间量度以毫秒(ms)为单位,显示 CPU 在 Unity SRP 渲染循环中花费了多少时间。注意:这里的时间为一帧内调用的所有 RenderLoop.Draw 和 Shadows.Draw 标记的累积时间。我们来过下:

CPU Rendering time


指示 SRP 循环在 CPU 中花费的总的累计时间量,而不管使用的是哪种多线程模式,例如单线程,在这里,我们可以最大程度地看到 SRP Batcher 的效果。要查看批处理程序的优化,请尝试将其打开和关闭以查看 CPU 使用率的差异。在此的示例总计为 0.90 毫秒。RT idle:指示 SRP 在渲染线程中占用的空闲时间。在此示例中,渲染线程空闲了 0.03 毫秒。

SRP Batcher code path (flushes)


指示我们的游戏花费在 SRP Batcher 代码路径中的时间。这分为游戏渲染所有对象(shadow passes除外)(0.29ms)和阴影(0.61ms)所花费的时间。如果“阴影”数量很高,请尝试减少“场景”中的阴影投射灯数量,或者在“渲染管线资源”中选择较低的级联数量。flushes 数字表示 Unity 刷新场景的次数,因为它遇到了新的 Shader Variant(在此示例中:89)。较少数量的 flushes 总是更好的,因为这意味着这帧中的着色器变体数量较少。


Standard code path (flushes)


指示 Unity 花费在渲染 SRP Batcher 不兼容的对象(例如粒子)上的时间。


我们看看另外一个例子如下,SRP Batcher 在 0.80 毫秒内刷新了 81 个 Objects:shadow passes 为 0.09 毫秒,所有其他 pass 为 0.71 毫秒。



Global Main Loop: (FPS)


指示全局主循环时间(以毫秒为单位),以及对应的帧率(FPS)。在这里如果您看到 FPS 增加了 20,这不一定意味着您已经优化了场景。要查看 SRP Batcher 是否优化了场景渲染,我们将 SRP Batcher 切换为“开”和“关”,然后在“CPU Rendering Time”下比较数字。

转载声明:作品来源于网络,不作任何商业用途

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接