UE4 性能优化
因为自己从事UE4影视渲染工作,在使用虚幻4做项目的过程中性能一直是不可忽视的话题。为了画面效果我们至少需要每秒20帧左右。根据不同的项目要求这个帧数可以能要40或者60,甚至在某些情况下会要求更高。
本文根据官方文档博客等综合而来,意在发现问题所在更高效的解决问题,下文将根据CPU,GPU来分析。
·首先要确定我们的性能瓶颈出现在哪里,这时我们在控制台输入“stat unit”它会显示如下图所示。
这个很明显能看到性能方面的参数接下来可以通过视图显示出来
这里Farme有两个度量:
第一个是这个帧的当前帧时间(左)
第二个是在最后几秒的最差帧时间(右)
左侧的图是游戏、绘图和GPU的几个帧里的帧时间图
Frame时间是产生一帧花的总时间,注意GPU和CPU是同时执行的,所以帧花费的总时间不是它们时间的总和,但是任一项拖了后腿都可能是帧率降低的原因。游戏的实际单帧时间由这三者之一限制:Game(CPU 游戏逻辑线程),Draw(CPU 渲染线程)或者 GPU(GPU)。 您的帧时间指的是生成游戏中每一帧所需要花费的总体时间。 由于在完成一帧前会同时同步游戏和描画线程,帧时间常常接近于这些线程中的时间。 GPU时间衡量的是显卡需要多长时间来渲染场景。 由于GPU时间与帧同步,它的值很可能也类似于帧时间。
Game(CPU 游戏逻辑线程):这是一个任意的CPU逻辑,它不与渲染直接关联。如果该组很慢,通常的情况是程序员有需要修正的内容,但是它可以是与美术相关的,比如:屏幕上有太多颗粒。内置在CPU分析器中的Frontend工具可以用来研究CPU性能并且观察正运行缓慢代码。特定的、与复杂任务相关的游戏设置只能在CPU上执行,像A.I.和Navigation的设置。
Draw(绘图):这是一个与GPU上渲染设置相关的CPU逻辑。它包括图形API和绘图调用的设置,或者如果渲染代码已经以非最佳的方式修正了,它就可能与渲染代码相关。
GPU:GPU渲染帧花费多长时间。它包括:执行图形API调用、绘图调用、着色器和后过程着色器的执行、获取纹理……这里的问题通常与资源相关,它可能是场景中类似着色器这样非常复杂的东西,或者场景中有许多不同的网格,结果就是每帧中发生太多的绘制调用。这可能会让一个专业美工或程序员找到问题根源,但是通常情况下需要一些决策——为了达到预期性能,应该做哪些权衡。这里能帮到您的最棒的工具是GPU分析器和着色器复杂性视图,这些会在之后进行讨论,同时下面还会讨论“Advance”目录中显示的特定stats。
如果一帧花的时间跟逻辑线程的时间比较接近,那么瓶颈在逻辑线程,相反如果跟渲染线程的时间比较接近,那么瓶颈在渲染线程。如果两个时间 都不接近,但跟GPU时间比较接近,那么瓶颈在显卡上。图中我们可以看到 GPU 是限制主因(三者最大的一个)。为了取得更少的 单帧 时间,在这个情形下必须先优化 GPU 的负载。
GPU分析:
(一) 为确保引擎中最大的帧速没有被限制先设置下帧率(- r.VSync可以关闭垂直同步):
(二) 按Ctrl+shift+或者控制台命令: ProfileGPU,调查GPU查看器,也可以打开:
可以看出影响GPU瓶颈最主要的是BasePass和PrePass ,
PrePass / Depth only pass: RenderPrePass / FDepthDrawingPolicy 。渲染遮挡物,对景深缓冲区仅输出景深。该通道可以在多种模式下工作:禁用、仅遮蔽,或完全景深,具体取决于活动状态的功能的需要。该通道通常的用途是初始化 Hierarchical Z 以降低 Base 通道的着色消耗(Base 通道的像素着色器消耗非常大)。
Base pass : RenderBasePass / TBasePassDrawingPolicy。渲染不透明和遮盖的材质,向 GBuffer 输出材质属性。光照图贡献和天空光照也会在此计算并加入场景颜色。
Lighting : 阴影图将对各个光照渲染,光照贡献会累加到场景颜色,并使用标准延迟和平铺延迟着色。光照也会在透明光照体积中累加。
Fog : 雾和大气在延迟通道中对不透明表面进行逐个像素计算。
Post Processing : 多种后期处理效果均通过 GBuffers 应用。透明度将合成到场景中。
其中BasePass 0 =不透明网格。
BasePass 1 =用于Z深度的Alpha蒙版不透明网格。
BasePass Dynamic =动画顶点,如Skeletal,GeoCache(Alembic)等。
几个值得注意的数据项:
Base Pass
Deferred Decals
Lighting
SSR(环境反射)
Translucency(半透明)
Postprocessing(后期处理效果)
Particle(粒子)
**当Base Pass很高,可以使用命令行打开Early Z Pass 可以降低 Base Pass 但同时会少量增加DRAW CALL**
检查影响GPU效率的内容查看有无超标现象
比如分辨率、HMD SP、投影贴图大小
1:basepass消耗高的话,就需要了解下哪些模型,贴图,材质开销太大。 面数过高的模型就减面;半透明用的多的物件就斟酌下是否必要;材质是GPU消耗过高的一大元凶,比较耗的材质可以检查下节点,关闭一些非必要的效果。材质复杂程度在这里可以查看,越红的越消耗,原则上减少使用点动画和曲面细分等一些效果。
红色:意味着性能消耗非常高 绿色:意味着性能消耗最低半透明:意味着增加性能消耗
这个是我们项目的消耗问题:
然后是可以在材质着色器里面尽量减少:
另外,场景里摆放的模型如果不需要参与碰撞计算的话,最好关闭碰撞,减少运算消耗。
游戏运行时在控制台里使用showflag(隐藏)命令可以帮我们快速定位具体是模型?特效?光照?等等哪个消耗高,消耗高的就优化,列举几个常用的”showflag.”命令“0”关闭“1”打开:
ScreenSpaceReflections: 切换屏幕空间的反射效果,可能会非常影响性能,对那些达到一定粗造度的像素有效
AmbientOcclusion: 屏幕空间环境遮罩
AntiAliasing: 切换各种抗锯齿(TemporalAA 和 FXAA,FXAA更快,但效果较差)
Bloom: 影响那些受到 lensflares 和 bloom 功能的画面。
DeferredLighting: 切换所有延迟光照通道。
DirectionalLightsPointLightsSpotLights: 切换不同的光照类型(检查光照类型影响性能时有用)
DynamicShadows: 切换所有的动态阴影(阴影贴图的渲染,以及阴影的过滤和投影)
GlobalIllumination: 切换预烘培和动态间接光照(LPV)
LightFunctions: 切换光照函数渲染
PostProcessing: 切换所有后处理效果
ReflectionEnvironment: 切换环境反射效果
Refraction: 切换折射效果
Rendering: 切换整体渲染
Decals: 切换贴花渲染
LandscapeBrushes StaticMeshesSkeletalMeshes Landscape: 轮询切换几种不同的几何体的渲染
Translucency: 切换透明度渲染
Tessellation:切换曲面细分(仍将运行曲面细分 shader,但生成更多三角面)
IndirectLightingCache: 切换是否动态物体或者静态物体具有使用间接光照 Cache 时无效的光照贴图。
Bounds : 显示编辑器中当前选中物体的边界框。
VisualizeSSR :屏幕空间反射像素显示为亮橙色是计算较慢的区域
关闭stuff查看效率
r.SetRes: 调整渲染分辨率
r.VSync 开启/关闭垂直同步(可能依赖于是否原生全屏)。
r.AllowOcclusionQueries 用于禁用遮挡(可以让场景运行的更慢)。
r.TiledDeferredShading 能够关闭基于 Tile 的延迟光照技术(GPU粒子的光影则没有退回方法)
.TiledDeferredShading.MinimumCount 能够调整使用多少灯光应用在基于 Tile 的延迟光照技术(视觉上并没有差异但性能会有不同)
r.SeparateTranslucency 这是一个用于修复半透明情况下景深的问题的功能,如果不需要的时候可以把它关闭,并有其他影响(查阅 SceneColor)。
r.Tonemapper.GrainQuantization 用于关闭在 Tonemapper 中添加的噪点来避免 Color Banding,由于 8bit 量化和较小的质量改进在输出为 10:10:10 并不必须。
r.SceneColorFormat 能够选用不同的 SceneColor 格式(默认是 64bit 的最佳质量,并支持屏幕空间子表面散射)。
FX.AllowGPUSorting 禁用粒子排序(在大量粒子的使用可以妥协使用)。
FX.FreezeParticleSimulation 禁止粒子的更新。
r.MaxQualityMode: 最高质量
r.MipMapLODBias: Mipmap Bias
r.MobileContentScaleFactor: 画面缩放比
r.ScreenPercentage: 用于减小内部实际渲染分辨率,画面会在重新放大
r.ShadowQuality: 移动端Stationaary灯光动态阴影质量,调整其值查看帧速变化,以判断瓶颈
r.Shadow.MaxResolution: 移动端Movable灯光动态阴影质量,调整其值查看帧速变化,以判断瓶颈
StatMemory:提供关卡中内存使用情况
2:灯光消耗高的话,需要检查动态光照数量(固定光也可以投射动态光照),是否有过多重叠的照射区域,照射范围参数是否开的太大。由于静态光照Build后已将灯光信息存储进了Lightmap,游戏中不再计算,所以灯光的主要消耗来自动态光源。先在世界大纲里查看所有灯光类型,确定有几盏动态光和固定光,前面有红点的是动态,黄点的是固定。
再进一步查看固定光的照射范围的重叠部分是否太多,重叠的越多,交集处越亮越红。用灯的原则是能不用动态光就不用(消耗主要来自被投照射的Mesh),灯光照射范围尽量不重叠,且同一个地图里固定光不能超过4盏。
光照复杂度视图模式基于动态光源的数量来对场景进行着色。
黑色:意味着没有收到动态光源影响。
不同颜色:从绿到红,表示受到动态光源的影响逐步增加。
关闭灯光的投射动态阴影也可以降低一些消耗,甚至一些灯光可以直接关闭投射阴影功能。
3:后期处理是另一个GPU消耗过高的元凶,需要慎用,原则是尽可能的把一些不必要的参数关掉,尤其是SSR,后期AO,Bloom等。一些参数默认会自带一些数值,没必要的全部清零,抗锯齿模式切换成FXAA。
使用Alt+0/Light Map Density可以对场景中的光照贴图密度进行分析。
其他数据分析
ShadowDepths
这个生成通过光源进行阴影投射的深度数据的pass。
作用与这里的消耗主要受到开启了投影的光的数目、动态光照影响的面数、以及阴影的质量的影响。
阴影的质量可以通过Sg.shadow quality进行全局的调节。
Memory-bound
如果有大量的材质使用了不同的贴图,导致Texture Sample的数量爆炸的话,就会自然的变成瓶颈。UE4有使用Texture Streaming,如果存储空间爆炸了的话,就会出现贴图模糊的情况,这时候可以使用Stat Streaming指令进行分析。
PrePass DOM_…
EarlyZPass,对非透明物体进行的早期的深度计算。
数据似乎被用于遮蔽计算,如果不使用Dbuffer Decals的话可以关掉。但是早期的深度计算可以在BasePass之前进行遮蔽计算,能让basepass以及之后所有的通道的计算减少很多。而且即便在这里不进行深度计算,会影响这里的运算量的变量依然会作用与后面的深度计算阶段,因此关闭EarlyZPass还是需要多做考虑的。另外要使用DBuffer Decals的话必须使用Opaque and masked的zpass计算,否则应该会出现奇怪的现象
性能上受到非透明物体的面数的影响,同时根据上面的选项不同也受到Masked的材质的影响。
HZB
Hierarchical Z-Buffer,用于计算HZB遮蔽,同时也会被屏幕空间内的射线演算使用,例如屏幕空间反射计算、AO等。同时被用于Mip的设置。受屏幕空间的大小影响。据官方描述,HZB拥有较高的固定性能消耗,每个物体所造成的消耗较小。可以通过r.HZBOcclusion来调整运算的类型。
Base Pass
对非透明的物体进行演算并填充到GBuffer,使用缓冲区可视化模式可以在视图中看到效果。几乎所有的延迟渲染都受到其影响,因此才叫基础通道。
其计算结果包括base color, metallic, specular, roughness, normal, sss profile,并且Decals、Fog以及Velocity的计算也在此处。其开销受到屏幕空间尺寸、物体数量、面数、Decals的数量、Shader的复杂度,生成的过程中包含光照贴图的推送,因此也会受到光照贴图的大小的影响。可以通过Stat rhi指令检查各种贴图和triangle的消耗。
另外,前向渲染的光照也在这里进行,此时光照的数量也会影响到这里的消耗。
Translucency
半透明的材质以及光照演算,通过Stat gpu中的Translucency and Translucent Lighting可以进一步查看。消耗受到屏幕空间大小以及屏幕内的半透明物体的数量影响,半透明物体的光照计算要尽量减少过度绘制。以及避免过多的需要进行半透明光照计算的光的数量。
Particle Simulation/Injection
粒子模拟,这里只展示GPU粒子的消耗,性能主要受粒子数量以及是否开启了基于深度的粒子碰撞影响。粒子的优化主要通过LOD以及设计上的优化进行。
Post process
UE4的后期处理功能比较多,AA、DOF、自动曝光以及很多其他的功能都在其中。每种PP特效都会产生额外的性能消耗,如果使用了PP材质的话,其复杂度也会影响性能。
Relection Envirionment
反射捕捉控件的计算缓存可以将显示模式调整为Reflections来查看各个控件对缓存的影响通常的建议是,放一个大范围的低精度反射捕捉,然后在需要的地方尽量不重叠的放置高精度的捕捉控件。影响性能的主要就是捕捉控件的数量及范围,也受屏幕空间的大小影响。
Render Velocities
速度主要用于TAA以及Motion Blur,受到移动物体的数量以及其面数的影响。主要的优化策略是使用LOD。
Screen Space Reflections
屏幕空间反射通过以下连个指令来进行调节:
r.ssr.maxroughness 0.0-1.0
r.ssr.quality 0..4其中Maximum roughness决定着计算的范围的大小。
CPU分析
游戏线程分析
查看游戏线程的性能表现的最佳工具是使用统计数据分析程序。在控制台输入“stat startfile”来启动分析,等10秒左右输入 “stat stopfile”收集这10秒的平均值当然也可以等更多的时间。在路径Saved/Profiling/UnrealStats下,会有关于您项目文件夹的ue4stats文件。
也可以用“STAT SLOW”来获取实时的报告,它可以通过报告运行一帧中特定时间段(默认10毫秒)来逐步定位帧停顿的位置。
运行速度较慢的数据将会在HUD上显示一段时间,从而判断性能波动。“Stat stopslow”来关闭它.
参数以秒为单位(所以10ms也就是0.01秒)参数可设置持续的时间,默认值是 10秒。
例:STAT SLOW 0.01 10这将会渲染在过去的10秒内所有运行时间超过10毫秒的循环统计数据。
现在我们需要分析,需要打开编辑器中的Session Frontend(会话前端)
当您打开了会话前端选项卡后,您需要切换到Profiler(分析程序)的小选项卡。 在该处,您可以选择载入您最近捕获的ue4stats分析文件。
加载后会这样显示
1. 这是一个渲染线程vs游戏线程的简图,根据CPU逻辑与渲染的关系,一眼你就会知道你是否是CPU受限的,或者它是否是与游戏相关的且花费最多性能的逻辑。
2. 这个区域显示了抓取期间的整个CPU加载的简图。在这里,你可以沿着时间线单击任何部分来观察对应帧的CPU分析,或者你可以单击、拖拽来选择帧的范围并且查看均值。根据你这里的选择,函数时间(3)的层级列表中的分析数据会改变。
3. 这是调用的不同函数和所花时间的层级列表,花费时间最长的函数排在顶端。花费最多时间的函数以红色显示,其它用黑色显示。你可以通过单击左侧三角来展开对应层,你可以看到这个函数调用过程的分解以及执行花费的时间。
注意这里的CPU停转是CPU闲置等待其它线程结束的时间。
4. 如果你在函数时间(3)的层级列表中选择了特定的函数,你可以看到这里的显示变化,这里显示了什么函数调用了这个函数,以及该函数调用了哪些函数,同时可以看到这些调用和被调用函数执行时间的比例。
5. 左侧面板展示了stats和stat组。顶层是stat组,你可以展开它查看内部的独立stat。这些stat可以是整型、浮点型数字或者内存,你可以控制哪些显示在stat过滤器面板(6)中。如果你鼠标停留在一个stat上,会弹出该stat的分析信息(8)。
6. 在这里你可以通过搜索想要的stat、改变分组和排序、隐藏/显示不同类型的stat(浮点/整型/内存)以及启用/禁用层级视图控制stat面板的显示(5)。
7. 这些控件用于显示函数时间的层级列表和所选函数的分解信息(4)
a. 类型——如果在图像视图中你只选择了一帧(2),你唯一的选择就是显示信息那帧,但是如果你选择了一系列帧,你可以选择是否显示平均时间或者花费的最长时间。
b. 视图模式——这会改变函数时间分层的层级列表视图(3),或者改变单纯的函数列表,里面包括这些函数的子程序包括的或排除的时间。
c. 向前、向后按钮可以让你在图像视图的不同部分之间跳转(2)。所有你可以看到一系列信息,之后缩小你的选择范围直到一个帧,然后用这些按钮来在两者之间切换。下拉箭头显示了之前的选择。
d. 这里的火焰按钮是用来展开你当前选择函数的时间层级列表的(3),用来查找花费最多时间的路径,它也会用一个小火焰图标来标识该路径。
8. 鼠标在stat面板(5)的一个stat上面停留时,会显示关于该stat的分析信息,最重要的是最小值、平均值和最大值:
这里我们只关注几个选项,展开GameThread(游戏线程)项目,然后往下拉,直到您看到超过几毫秒的“Inc Time”(包含时间)条目,而且其不包含许多子项或不包含任何子项。 同时关注一下“Calls”(调用)数列,它显示了每帧调用的统计数据的平均次数。 不要被“CPU Stall”(CPU停滞时间)项目弄糊涂了。 它们显示的是线程等待处理其他内容时所花费的时间,所以不是主要数据,而且仅仅会在帧频率受限或者游戏进程不为瓶颈时才会显示出来。
还有一个重要项目TickFunctionTask。 此项目下是正在更新的每个actor和组件。 一般来说,降低每帧更新的actor和组件的数量都可以很好地加速游戏。
另一个要关注的是BlueprintTime(蓝图时间)。 找到这个值的最佳方法是切换到包含(合并)视图并在列表中找到它。 这样就可以把所有的BlueprintTime(蓝图时间)条目组合到单一行中。 如果您选择BlueprintTime(蓝图时间),然后切换回层次视图,则其会选择所有蓝图代码被执行的位置,这样能让您很好地了解花费时间进行处理的位置及其位于哪个蓝图中
另一个常见的问题位置是TickWidgets(更新控件)。 如果这个统计数据值很高,这表示您可能同时显示了太多控件,或者这些控件上的属性代理过于复杂。 一些slate属性,比如可见性,可能会在每帧被调用好几次,这样它们的值必须要小而且能及时返回。
您是不是在游戏中有很多骨架网格物体? SkinnedMeshComp更新时间有时也会消耗很多系统资源。 请尝试降低显示在分析文件中的骨架中的骨骼数量,或者降低动画蓝图的复杂度。 如果您不需要在无法看到骨架网格物体时更新动画,请考虑将骨架网格物体组件上的MeshComponentUpdateFlag(网格物体组件更新标识)正确设置为OnlyTickPoseWhenRendered(仅在渲染时更新姿势)。 请注意,将此标识设置为AnimNotifies(动画通知)将使得这些网格物体不被渲染时不再对其进行触发。
https://docs.unrealengine.com/en-us/Engine/Performance/Profiler 参考文档