百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

来讲讲URP 系列教程

csdh11 2024-12-12 11:19 23 浏览

今天带大家了解下什么是 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”下比较数字。

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

相关推荐

探索Java项目中日志系统最佳实践:从入门到精通

探索Java项目中日志系统最佳实践:从入门到精通在现代软件开发中,日志系统如同一位默默无闻却至关重要的管家,它记录了程序运行中的各种事件,为我们排查问题、监控性能和优化系统提供了宝贵的依据。在Java...

用了这么多年的java日志框架,你真的弄懂了吗?

在项目开发过程中,有一个必不可少的环节就是记录日志,相信只要是个程序员都用过,可是咱们自问下,用了这么多年的日志框架,你确定自己真弄懂了日志框架的来龙去脉嘛?下面笔者就详细聊聊java中常用日志框架的...

物理老师教你学Java语言(中篇)(物理专业学编程)

第四章物质的基本结构——类与对象...

一文搞定!Spring Boot3 定时任务操作全攻略

各位互联网大厂的后端开发小伙伴们,在使用SpringBoot3开发项目时,你是否遇到过定时任务实现的难题呢?比如任务调度时间不准确,代码报错却找不到方向,是不是特别头疼?如今,随着互联网业务规模...

你还不懂java的日志系统吗 ?(java的日志类)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

谈谈枚举的新用法--java(java枚举的作用与好处)

问题的由来前段时间改游戏buff功能,干了一件愚蠢的事情,那就是把枚举和运算集合在一起,然后运行一段时间后buff就出现各种问题,我当时懵逼了!事情是这样的,做过游戏的都知道,buff,需要分类型,且...

你还不懂java的日志系统吗(javaw 日志)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

Java 8之后的那些新特性(三):Java System Logger

去年12月份log4j日志框架的一个漏洞,给Java整个行业造成了非常大的影响。这个事情也顺带把log4j这个日志框架推到了争议的最前线。在Java领域,log4j可能相对比较流行。而在log4j之外...

Java开发中的日志管理:让程序“开口说话”

Java开发中的日志管理:让程序“开口说话”日志是程序员的朋友,也是程序的“嘴巴”。它能让程序在运行过程中“开口说话”,告诉我们它的状态、行为以及遇到的问题。在Java开发中,良好的日志管理不仅能帮助...

吊打面试官(十二)--Java语言中ArrayList类一文全掌握

导读...

OS X 效率启动器 Alfred 详解与使用技巧

问:为什么要在Mac上使用效率启动器类应用?答:在非特殊专业用户的环境下,(每天)用户一般可以在系统中进行上百次操作,可以是点击,也可以是拖拽,但这些只是过程,而我们的真正目的是想获得结果,也就是...

Java中 高级的异常处理(java中异常处理的两种方式)

介绍异常处理是软件开发的一个关键方面,尤其是在Java中,这种语言以其稳健性和平台独立性而闻名。正确的异常处理不仅可以防止应用程序崩溃,还有助于调试并向用户提供有意义的反馈。...

【性能调优】全方位教你定位慢SQL,方法介绍下!

1.使用数据库自带工具...

全面了解mysql锁机制(InnoDB)与问题排查

MySQL/InnoDB的加锁,一直是一个常见的话题。例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?下面是不同锁等级的区别表级锁:开销小,加锁快;不会出现死锁;锁定粒度...

看懂这篇文章,你就懂了数据库死锁产生的场景和解决方法

一、什么是死锁加锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制。任何事务都需要获得相应对象上的锁才能访问数据,读取数据的事务通常只需要获得读锁(共享锁),修改数据的事务需要获...