(学习记录)Unity&Shader基础教程4------衡量性能(一)

发布于:2024-12-18 ⋅ 阅读:(169) ⋅ 点赞:(0)

默认渲染管线和URP渲染管线的问题

        教程中作者采用的Unity版本为Unity 2020.3.6f1,我采用的版本为2022.3.27f1c1,渲染管线的更改上因为版本的不同有一些差别,一些默认渲染管线的设置更改了位置。

        切换渲染管线,通过以下操作:

        打开 Edit > Project Settings。

        在 Quality 面板中,有 Render Pipeline Asset 选项,这就是用于切换渲染管线的设置位置。

                                

        将 Render Pipeline Asset 的字段选择None,以切换回Built-in渲染管线,如果未成功切换,把Graphic > Scriptable Render Pipeline Settings处的字段也选择None。

                                

        如果需要重新启用URP渲染管线,只需将 Render Pipeline Asset 设置为URP资源文件即可。

        在Unity 2022.3版本中,如果没有找到SRP Batcher以及Dynamic Batching这个选项,可以参照如下设置修改,打开Edit > Preferences > Core Render Pipeline > Additional Properties > Visibility,改为All Visible(此处参考Unity 在URP中显示动态批处理 Dynamic Batching 选项)。
                                

        

分析Unity性能

1. 帧速率 FPS(frame per second)

        Unity在Play Mode的时候会持续不断的渲染帧,但是每一帧都是静止的一张图片,如果希望通过人眼观察到的是一个流畅的画面,那么每秒至少要渲染30帧,即FPS(每秒渲染的帧数)(帧速率)要大于等于30,我们观察到的画面才会比较流畅。

        帧速率也有另一种表达方式,帧率(frame rate)单位为赫兹(Hz),表示的也是每秒可以产生多少帧。

        能否达到目标帧率取决于处理单个帧所需要的时间。要到达60FPS,我们需要在六十分之一秒,即16.67ms,的时间内渲染和更新每一帧。

        仅通过观察运行时候的流畅程度(帧率)来进行性能分析是非常不精确的,有很多因素会造成流畅程度时好时坏,可能是应用程序引起的,也有可能是本机其他应用程序运行所造成的,或是Unity的编译器性能受到其他模块的占用,因此我们需要其他工具来帮助我们了解更多信息。

2. 游戏窗口数据(Stats)

        游戏窗口有一个统计信息面板,它显示对于最后渲染帧的测量结果。如图:

        

BRP统计信息窗口

        该面板并未给出太多信息,但它是较为简单的工具,可以用来了解正在发生的事情。

        在本次运行中,我采用的是圆环函数和分辨率为100的图形,打开了游戏窗口的VSync功能,使用默认渲染管线(Build-In Render Pipeline),之后简称为BRP。

        小插曲------什么是VSync功能?

        在Unity中,VSync(Vertical Synchronization)是一项用于同步游戏的帧率与显示器刷新率的功能。它的主要目的是防止屏幕撕裂(Screen Tearing),一种由于显示器刷新与游戏帧率不同步而导致的画面分裂现象。

        具体来说,当VSync开启时,Unity会在渲染新帧时等待显示器的垂直刷新信号,确保每一帧都完整显示,而不是在刷新中途更新显示内容。这样可以让画面更流畅并避免撕裂,但也会带来一些额外的延迟,尤其在帧率和刷新率不匹配时可能出现卡顿。

        根据统计数据显示,在某一帧中,CPU主线程(CPU main)耗时为19.6ms,渲染线程(render thread)耗时15.3ms,这表明整个帧的渲染耗时为34.9ms,但是在FPS处注意到它的值是51.1(19.6ms),与CPU主线程耗时是匹配的,此处FPS显示的值看起来是采用了主线程和渲染线程中耗时最多的,并且假设它与目标帧率匹配。这种过度简化只考虑了CPU,忽略了GPU和显示,真实帧率很可能要更低。

        除了持续时间和FPS指示以外,统计面板提供了其他信息。在批次处(Batches)显示共有40003个批次(原作者是30003个批次,从Shadow casters:20000来看,我应该是多了一次Shadow Caster Pass),同时显示节省的批次(Saved by batching)是0,这些是发送到GPU的绘制命令。我的分辨率是100,一共存在100*100个立方体,因此看起来每个立方体渲染了4次,按照默认的BRP渲染步骤分析,每个立方体可能经历了以下步骤:

        一次深度通道(Depth Pass):这主要是为了生成深度缓冲,用于优化后续的渲染阶段。

        两次阴影投射器(Shadow Caster Pass):生成一次阴影贴图,另一次原因暂不明(可能是Cascaded Shadow Maps的分区为2)。

        一次基础通道渲染(Base Pass):渲染立方体的颜色和材质,这是最终可见的部分。

        一次后处理或者反射(Post-Processing Pass、Reflection/Light Probe Pass):用于处理后处理效果、反射探针或者光照探针。

        另外的三个批次是处理额外工作,像独立于我们Graph的天空盒,阴影处理。

        此外,还有7次 set-pass 调用(set-pass call),可以认为是 GPU 被重新配置,以不同的方式进行渲染,比如使用不同的材质。

        如果我们采用URP管线,我更改为Ultra_PipelineAsset,发现它的渲染速度更快,可以观察到渲染批次(batches)只有30003,比BRP减少了10000个,这是因为URP不使用单独的深度通道来处理定向阴影,它确实有更多的set-pass调用,但是这不影响其速度。

                                       

                                                          URP统计窗口

        可以看出Saved by batching显示为0,但URP其实默认使用了SRP的批处理程序,SRP批处理程序不会消除单个绘制命令,但可以使它们更加高效,为了说明这一点,  打开Ultra_Pipeline Asset的Inspector窗口,取消其SRP Batcher,可以观察到下图在禁用掉SRP批处理器后(SRP Batcher),会导致URP的性能变差。

                                               

                                                        SRP和动态SRP位置

                                        

                                                没有 SRP 批处理器的 URP 统计数据

        

3. 动态批处理

        除了SRP Bathcer,URP还有另一个用于动态批处理的开关。这是一种古老的技术,可以动态地将小网格组合成一个较大的网格,然后进行渲染。从图上可以看出,为URP启用动态批处理减少到了20019,消除了9984次绘制。

                                        

                                               具有动态批处理的URP统计数据

        在作者看来,SRP批处理器和动态批处理具有相对不错的性能,因为我们图形点的立方体网格是动态批处理的理想作用对象。

        SRP批处理不适用于BRP,但是也可以为它启用动态批处理,在Edit > Project Settings > Player > Other Settings 中可以找到。可以发现动态批处理对于BRP来说效率更高,消除到51个批次,但对实际效率没有太大帮助。


                                        

                                                具有动态批处理的BRP统计数据

4. GPU Instancing(GPU实例化)

        提高渲染性能的另一种方法是启用GPU实例化,使用这种方法可以用一个draw command让GPU 绘制具有相同材质的一个网格的多个实例,并提供一个变换矩阵数组和其他可选的实例数据。在这种情况下,我们需要针对每个材质启用它,打开Enable GPU Instancing。

                              

        URP更倾向于使用SRP批处理程序而不是GPU实例化,我们要观察GPU实例化的效果,需要先关闭SRP batcher,我们可以看出批处理数量减少到只有99个,要比动态批处理好的多。对于BRP来说,GPU实例化产生的批次比动态批处理更多,但帧速率略高。

                                   

                                           具有 GPU 实例的 URP 统计数据

                                   

                                            具有 GPU 实例的BRP统计数据

        我们可以从这些数据中得出结论:对于URP,GPU实例化是最好的,其次是动态批处理,然后是SRP批处理器,但差异很小,因此在表中看起来实际上是等效的。唯一明确的是我们应该使用GPU实例化,或者SRP批处理器。

5. Frame Debugger

        位置: Windows > Analysis > Frame Debugger。

        为了更好地理解使用动态批处理和使用GPU实例化有什么不同,我们可以使用Frame Debugger,点击Enable按钮启动Debugger以后,它会在左侧显示发送给GPU的所有绘制命令的列表,这些命令是游戏窗口最后一帧的,并且按照分析样本分组,右侧则显示特定选定绘制命令的详细信息。 此外,游戏窗口还会显示直至所选命令之后的渐进绘制状态。

        注:打开Frame Debugger以后直到它关闭,Unity会一直重复渲染相同的帧来展示绘制帧的中间状态,记得在不需要的时候就关闭Frame Debugger

        打开播放模式(Play Mode),然后启动Frame Debugger,此时Frame Debugger会暂停播放模式来检查当前的绘制命令层次结构。

        首先对BRP执行此操作,不使用动态批处理或者GPU实例化。

        

                                 BRP的Frame Debugger        

        如图所示一共是40007次Draw Call,比之前Statistics面板要多,因为有一些命令不计入批次,例如清除目标缓冲区。我们点的绘制被单独列为Draw Mesh Point(Clone),在DepthPass.Job、Shadows.RenderDirJob和RenderForward.RenderLoopJob下。   

        我发现我此处的Shadows.RenderDirJob次数要比作者多出10000次,在经过一番寻找以后,发现是我的场景启用了四级联阴影,因此导致了大量额外的阴影渲染调用(每个立方体需要多次参与级联生成)。

                        

                                           Four Shadow Cascades

什么是级联阴影映射(Cascaded Shadow Mapping):级联阴影将摄像机的视锥划分为多个范围(通常为近、中、远、极远4个区域),每个级联会生成一张独立的阴影贴图。每个级联会为阴影投射体(Shadow Caster)生成单独的渲染调用,从理论上来说,如果有10000个立方体,启用了4级联阴影,Unity需要为每个级联分别渲染这些立方体的阴影,理论上会有10000 * 4 =  40000次阴影渲染调用,但是实际观察图中有一个Cascade splits,有每个层级的百分比,Unity根据每个物体的距离动态确定其需要参与的级联层级,可以看出某些物体只在部分级联范围内可见,所以最终调用是20000次,不是40000次。

        在关闭Cascaded Shadow Mapping后,我的统计面板以及对应的Frame Debugger如下:

                                        

                                                关闭级联阴影映射的统计面板

                

                                                        Frame Debugger面板

        如果我们再次启用动态批处理,命令结构不会发生变化,每组的10000次绘制减少到12次的Draw Dynamic调用。这对于CPU-GPU通信开销是一个显著的改进。

                                        

                                                        具有动态批处理的BRP

        如果使用GPU实例化,每个组会减少到20个Draw Mesh(Instanced)Point(Clone)调用。这又是一个很大的改进,但是方法上不同。

                                        

                                                        使用了GPU实例化的BRP

        对于URP来说,我们可以看到情况差不多,但是命令层次不同。同样的是,我的MainLightShadow > Shadows.Draw要比原作者多出10000次,与BRP相同,是因为Shadows Cascade层级为4,改成1后,可以和原作者保持一致。

                                        

                                                        修改级联阴影为1

        在URP中,点被绘制了两次,第一次是在Shadows.Draw下,第二次是在RenderLoop.Draw下。一个显著的区别是,动态批处理似乎对阴影贴图不起作用,这也解释了为什么动态批处理对 URP 效果较差。 在开启动态批处理后,相比较BRP,在RenderLoop.Draw中,URP使用了 16 个批次,BRP只有 12 个批次,这表明 URP 材质比标准 BRP 材质依赖更多的网格顶点数据,因此单个批次可容纳的点数更少。 与动态批处理不同的是,GPU 实例化对阴影有效,因此在这种情况下更有优势。

                                        

                                                        URP的Frame Debugger

                                                

                                                           开启动态批处理的URP

                                                

                                                        开启GPU实例化的URP

        启用SRP批处理器是,绘制10000个立方体会被列为16个SRP批处理命令,但这些是单独的绘制调用,只是效率比较高。

                                          

                                                          开启SRP batcher的URP


网站公告

今日签到

点亮在社区的每一天
去签到