这是一篇关于使用UE的PSO缓存优化安卓应用的一些经验技巧
0、前言
在DrawCall改变GPU渲染状态时会带来额外开销,在头显或配置低的设备体验会更加明显,比如某些材质和特效的首次渲染,或第一次看向资源密集的地方时,会出现明显的卡顿现象。为应对这种难题现代图形API(D3D12、Vulkan、Metal)提供PSO机制来减少硬件改变渲染状态的开销。其原理是把单次渲染所需要的Shader和渲染状态合并成一个对象,称为Pipeline State Object(PSO),由图形API解决各种硬件状态之间的依赖和冗余,给出最优的状态集合和设置方法(称为编译),再统一交给硬件设置渲染状态。
1、机制
PSO (管线状态对象,Pipeline State Object)是在运行时(Runtime)编译生成的二进制代码,运行时编译的二进制代码会被读取回应用程序并缓存为 PSO 缓存。当运行时再次尝试生成相应的 PSO 时,可以直接从 PSO 缓存中进行反序列化读取和加载,从而节省了运行时的编译过程耗时。
具体参考:UE PSO Cache机制、使用与优化
2、用法
要设置你的项目以记录PSO缓存,请执行以下步骤:
- 打开项目的
DefaultEngine.ini或其(Platform)Engine.ini。
(Platform)Engine.ini文件通常位于(Project)/Config/(Platform)目录下。使用此目录可以防止其他可能无需使用PSO缓存的平台获取设置。
-
在
Engine.ini文件中设置以下值:[DevOptions.Shaders] NeedsShaderStableKeys=true -
在
DefaultGame.ini文件中,设置以下值:[/Script/UnrealEd.ProjectPackagingSettings] bShareMaterialShaderCode=True bSharedMaterialNativeLibraries=True -
如果你是从头开始,请确保
(Project)/Build/(Platform)/PipelineCaches中没有文件。烘焙器会在这个位置寻找记录的缓存文件。如果这是你首次采集PSO,此文件夹可能根本不存在。 -
确保控制台变量
r.ShaderPipelineCache.Enabled设置为1。- 如使用
PICO VR设备参考其官方文档:PSO 缓存在 “DefaultDeviceProfiles” 文件中添加 CVar。该文件位于\ {工程名称} \ Config路径下,如果没有自行添加。重启后在Device Profiles中看到就可以了[Android DeviceProfile] DeviceType=Android BaseProfileName= +CVars=r.ProgramBinaryCache.RestartAndroidAfterPrecompile=0 +CVars=r.ShaderPipelineCache.Enabled=1 +CVars=r.ShaderPipelineCache.ReportPSO=1 +CVars=r.ShaderPipelineCache.StartupMode=1 +CVars=r.ShaderPipelineCache.GameFileMaskEnabled=0 +Cvars=r.ShaderPipelineCache.LazyLoadShadersWhenPSOCacheIsPresent=1 +CVars=r.ShaderPipelineCache.BatchSize=10 +CVars=r.ShaderPipelineCache.BatchTime=0.0 +CVars=r.Vulkan.PipelineCacheFromShaderPipelineCache=1
收集运行时的PSO并重新打包
-
在
{工程名称} \工程目录下,新增一个“PSOCaching”文件夹,用来放PSO流程中的各种中间文件后续有用(放哪都可以)。 -
打包应用程序,烘焙所有相关地图,然后在 ` {工程名称} \ Saved \ Cooked \ Android_ASTC \ {工程名称} \ Metadata \ PipelineCaches` 路径下找到两个 .shk 文件以及 .upipelinecache文件一并拷贝到PSOCaching。
-
在Pico设备上安装刚才打包出来的程序,将命令行参数-logPSO加入UECommandLine.txt,将UECommandLine.txt放在Pico头显文件夹中的
UnrealGame \ {工程名称}目录下,运行该 APK,体验每一个关卡。 -
生成的后缀为 .rec.upipelinecache 的 PSO 缓存在Pico设备的
UnrealGame \ {工程名称}\ Saved \ CollectedPSOs路径下,将它们拷贝至 PSOCaching 文件夹内。 -
在PSOCaching中编写一个 CreateSpc.bat 文件,然后执行它以生成最终打包所需的 *.spc 文件。格式类似于
Engine\Binaries\Win64\UnrealEditor-Cmd.exe [ProjectName] -run=ShaderPipelineCacheTools expand C:\PSOCache\*.rec.upipelinecache C:\PSOCache\*.shk C:\PSOCache\[YourPrefix][ProjectName][ShaderFormatName].spc示例如下代码所示,具体的引擎、工程目录按照实际情况修改。D:\UE5_4\Engine\Binaries\Win64\UnrealEditor-Cmd.exe D:\Projs\PSOTest\PSOTest.uproject -run=ShaderPipelineCacheTools expand D:\Projs\PSOTest\PSOCaching\*.rec.upipelinecache D:\Projs\PSOTest\PSOCaching\*.shk D:\Projs\PSOTest\PSOCaching\PSO0318_PSOTest_SF_VULKAN_ES31_ANDROID.spc -
执行bat文件后,会生成一个.spc文件,将.spc添加至 {工程名称} \ Build \ Android \ PipelineCaches 路径下,然后将这个工程重新打包。
-
新的 PSO 缓存(* .stable.upipelinecache)位于 {工程名称} \ Saved \ Cooked \ Android_ASTC \ {工程名称} \ Content \ PipelineCaches \ Android 路径下。可以查看文件大小确定PSO是不是正常被打包了
-
可以通过日志查看被加载的PSO缓存:

-
至此,PSO缓存的应用完成
-
注意事项
-
收集PSO可以增量收集,但是发生着色器缩量、着色器变化会发生无法预知的问题,需要重新收集
-
收集PSO的话,需要按菜单键手动退出。程序崩溃/调用QuitGame函数退出/被踢下线都会导致PSO未能成功收集
-
其他
`NumPrecompilingPSOsRemaining`函数可以查看剩余需要加载的PSO缓存(UE5.6后)
- 如使用
3、注意事项
粒子特效如果是GPU模拟的话PSO是录制不上去的,第一次渲染这个粒子还是会卡顿,应该改为CPU模拟
参考
- PSO Caching
- PSO Doc:
- PSO Video:
- YouTube - PSO Caching in UE4.27 to reduce hitching in Android
-
[[UFSH2024]虚幻引擎移动端开发最新进展 Jack Porter Epic Games 引擎移动平台开发主管](https://www.bilibili.com/video/BV18F2oYAEJ4?spm_id_from=333.788.videopod.sections&vd_source=30b5d6dd5435961e25b24fcc71e401d8)
- PSO Pre-caching
- 采用PSO预缓存方式,在尽可能早的时间,预加载当前场景物体所有可能关联的物体的PSO(存在一些预测算法)
- PSO预缓存方式目前5.6版本才有,且不支持Mobile