赞
踩
更多内容请查看我的个人网站
NoCodeWorld 的小地盘
根据上面总流程图,我会分别一个个去讲解每一步的操作和需要额外处理的东西
首先,需要打一个可以实机收集PSO缓存文件的包
这里主要说一下录取的方式方法, 一种方式就是引擎自带的自动采集指令
Config/DefaultEngine.ini
[ConsoleVariables]
r.ShaderPipelineCache.Enabled=1
r.ShaderPipelineCache.LogPSO=1
r.ShaderPipelineCache.SaveBoundPSOLog=1
上述采集指令配置了以后直接打包,然后开始跑项目即可,会自动采集并且保存缓存文件到本地,如果你们是普通小场景项目上面方式就已经足够了,可直接跳到 采集完成
我自己做了一个PsoCacheGather的小工具插件,下面会大概说一下实现思路
我们小工具就如图所示,支持自动跑图录取和手动录取,因为我们是大世界,场景太大,如果让QA去全部跑一遍有点吃力,所以直接做了一个自动跑图收集,如果你们项目组是小副本或者小场景直接手动跑图录取或者拉一条Line,然后摄像机沿着跑即可,这个看自己需求
我们项目因为是大世界场景,测试发现长时间全部跑下来会有问题,可能是实时一直在保存太大太频繁,所以我改成了自动跑图完再保存一次,中间不会实时保存,测试没问题
[ConsoleVariables]
r.ShaderPipelineCache.Enabled=0
首先配置中关闭自动开启,我们在开始录取的时候代码中开启
bool UPSOCacheGatherHelper::EnableShaderPipelineCache(bool bEnable)
{
UE_LOG(LogPSO, Display, TEXT("EnableShaderPipelineCache %s"), bEnable ? TEXT("true") : TEXT("false"));
auto Var = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ShaderPipelineCache.Enabled"));
if (Var)
{
Var->Set(bEnable ? 1 : 0);
}
return !!Var;
}
首先在一开始先开启总开关 r.ShaderPipelineCache.Enabled
bool UPSOCacheGatherHelper::EnableLogPSO(bool bEnable)
{
auto Var = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ShaderPipelineCache.LogPSO"));
if (Var)
{
Var->Set(bEnable ? 1 : 0);
}
return !!Var;
}
bool UPSOCacheGatherHelper::LoadShaderPipelineCache(const FString& Name)
{
UE_LOG(LogPSO, Display, TEXT("Load Shader pipeline cache %s for platform"), FApp::GetProjectName());
return FShaderPipelineCache::OpenPipelineFileCache(FApp::GetProjectName(), GMaxRHIShaderPlatform);
}
然后开始录制时先调用EnableLogPSO(true),再LoadShaderPipelineCache(),就会开始录制了
bool UPSOCacheGatherHelper::SavePipelineFileCache(EPSOCacheSaveMode Mode)
{
return FShaderPipelineCache::SavePipelineFileCache((FPipelineFileCache::SaveMode)Mode);
}
开始录制后在内存中不会自动保存本地,在我们录制结束的时候手动调用SavePipelineFileCache(EPSOCacheSaveMode::BoundPSOsOnly)保存到本地,至此手动采集结束.
当你手动收集完成或者自动收集完成以后可从手机磁盘目录 /SDCard/UE4Game/{ProjectName}/Saved/CollectedPSOs 下取得 后缀名为 .upipelinecache的文件
特别说明一下,采集文件可分开录取到多个文件中,这些文件在生成最终PSO缓存时会自动合并的
我自己在项目目录下建了一个Tools/PSOCache 目录,专门作为PSO相关的工作目录
之前采集到的.rec.upipelinecache文件放到对应文件夹下,然后我们就要生成SHK文件了,这个文件是在打包时Cook生成的,直接在DefaultEngine.ini中加上
[DevOptions.Shaders]
NeedsShaderStableKeys=true
然后打包项目,在 %ProjectPath%\Saved\Cooked%%a\Client\Metadata\PipelineCaches\目录下就会找到.SHK文件(4.27版本以上引擎是这个格式,之前是.scl.csv)
如果你的项目在生成APK时会COOK到所有文件,那你就忽略下面手动生成,直接跳到下一段
这一步按正常流程应该是打包时自动生成全部.shk文件,但是因为这个文件的信息录入跟cook的文件列表挂钩,我们的首包apk又不是打的完整的文件列表,是排除掉大部分文件的,所以会导致没法自动生成,我只能写了个工具去专门处理这种情况,通过测试,有两种方案可以达到这一步的效果
1.首包不做排除方案,通过引擎自带的配哪些打哪些进去以做到只打指定资源进首包,但是测试发现在我们项目中,因为存在软引用的情况,所以无法直接做到,需要修改引擎才行,所以目前暂用第二种方案
2.我通过工具修改ini文件,生成完shk以后再把ini还原的方式去生成,操作上直接执行 ProjectDir/Tools/PSOCache/CookFullAssert.bat 即可,等他执行完成,会在 当前目录下的 PipelineCaches/对应平台下生成上图中的两个SHK文件,名字什么的都不要动,然后上传到svn即可,这个SHK文件理论上只需要在资源有修改时才去重新生成即可
脚本中其实就是在ini里面加上这个NeedsShaderStableKeys和去掉排除的文件夹, 然后直接执行cook指令
%EnginePath% %ProjectPath%\Client.uproject -run=Cook -TargetPlatform=%%a -fileopenlog -ddc=InstalledDerivedDataBackendGraph -unversioned -stdout -CrashForUAT -unattended -NoLogTimes -UTF8Output -CookAll
最后把生成的两个SHK文件拷出来即可,完事以后会把ini置回,至于为什么要置回,后面使用时会讲到
现在已经有了SHK文件和rec.upipelinecache文件,我们现在要做的就是把这些文件合并生成出最后的二进制PSO文件
ProjectDir/Tools/PSOCache/PSO_Expand.bat
call PublicParam.bat
for %%a in (%Platform%) do (
if %%a==Android_ASTC %EnginePath% %ProjectPath%\Client.uproject -run=ShaderPipelineCacheTools expand "%ToolsPath%\rec.upipelinecache\%%a\*.rec.upipelinecache" "%ToolsPath%\PipelineCaches\%%a\*.shk" "%ToolsPath%\stablepc.csv\%%a\Client_GLSL_ES3_1_ANDROID.stablepc.csv"
if %%a==IOS %EnginePath% %ProjectPath%\Client.uproject -run=ShaderPipelineCacheTools expand "%ToolsPath%\rec.upipelinecache\%%a\*.rec.upipelinecache" "%ToolsPath%\PipelineCaches\%%a\*.shk" "%ToolsPath%\stablepc.csv\%%a\Client_SF_METAL.stablepc.csv"
)
这个脚本就是合并导出所有rec.upipelinecache文件为一个.csv文件,这个stablepc.csv文件很重要,他就是最终PSO文件的源文件,这个可以当成保存文件上传到svn去,执行完这个批处理后会生成到 /stablepc.csv/对应平台/ *.stablepc.csv
ProjectDir/Tools/PSOCache/PSO_Build.bat
call PublicParam.bat
for %%a in (%Platform%) do (
if %%a==Android_ASTC xcopy %ToolsPath%\stablepc.csv\%%a\Client_GLSL_ES3_1_ANDROID.stablepc.csv %ProjectPath%\Build\Android\PipelineCaches\Client_GLSL_ES3_1_ANDROID.stablepc.* /s /i /y
if %%a==IOS xcopy %ToolsPath%\stablepc.csv\%%a\Client_SF_METAL.stablepc.csv %ProjectPath%\Build\IOS\PipelineCaches\Client_SF_METAL.stablepc.* /s /i /y
if %%a==Android_ASTC call %EnginePath% %ProjectPath%\Client.uproject -run=ShaderPipelineCacheTools build "%ProjectPath%\Build\Android\PipelineCaches\*Client_GLSL_ES3_1_ANDROID.stablepc.csv" "%ToolsPath%\PipelineCaches\%%a\*.shk" "%ProjectPath%\Content\PipelineCaches\Android\Client_GLSL_ES3_1_ANDROID.stable.upipelinecache"
if %%a==IOS call %EnginePath% %ProjectPath%\Client.uproject -run=ShaderPipelineCacheTools build "%ProjectPath%\Build\IOS\PipelineCaches\*Client_SF_METAL.stablepc.csv" "%ToolsPath%\PipelineCaches\%%a\*.shk" "%ProjectPath%\Content\PipelineCaches\IOS\Client_SF_METAL.stable.upipelinecache"
)
这个就是生成最终的.upipelinecache文件,这个文件是要打到包里去的,所以这个批处理就是把前面生成的.csv拷贝到ProjectDir/Build/对应平台/PipelineCaches 下,然后执行命令生成出.upipelinecache文件到ProjectDir/Content/PipelineCaches/ 下,然后可以把build下面的scv文件和Tools下面的都提交到svn上去
这里可以发现我们其实使用的就是最终content下的.upipelinecache文件,为什么还要保留中间文件.csv呢,其实这里就要说一下一个很重要也很鸡肋的东西,我们前面有说过NeedsShaderStableKeys=true加了这个以后就会自动生成SHK文件,其实这个.upipelinecache文件加了这个也会自动生成,前提是你要把.csv文件拷到build下面以后,打包的时候引擎会自动生成.upipelinecache并且打包,但是有个问题是引擎没考虑过会有热更的情况,如果走这个自动的流程,这个.upipelinecache文件是没法管理的,都是引擎那边处理死了,即使我通过指令手动生成到content下面,引擎有个恶心的地方是他会在打包的时候强制删除掉content下面的,所以这里想热更的话只能不走自动的流程,后面打包和热更步骤还会有说明
如果你不需要热更PSO文件,请忽略这一步,直接跳到运行时加载
通过前面生成出.upipelinecache文件以后,我们知道要把这个文件打包进平台包里去,以供游戏运行使用,可自由选择首包中要不要打这个文件,这个文件就是实际运行时会实时编译成一个缓存文件到手机本地去,然后在运行时需要新建PSO是他就会到本地寻找缓存并直接使用,以减少创建的消耗,因为大世界场景等资源会有热更的情况,当后续版本资源增加了,自然也要把这个upipelinecache文件热更到手机上去
打包进首包 可以选择打到首包也可以选择不打,按道理不会影响,需要的话就只需要在ProjectSetting的Package选项配置一下PipelineCaches文件夹即可
这里有个小问题就是这个文件夹下是包含安卓和IOS的资源的,但是这个打包配置每个平台都是一份,所以只能全部打进去,好在这个文件一般来说都很小,所以可自己选择要不要打进首包
热更打包 我这边打包工具是用的是HotPatcher,下面演示是基于这个插件的,现在项目热更是按照文件夹打到每个pak中去的,所以只需要选择额外增加一个pak放这个资源。或者直接放到之前的某个pak中去,无非就是选择配置非assert文件即可
这里是总的非Assert文件配置,可选择对应平台对应的资源,我这里是全部都配进去了,在这里配置应该是会打到default.pak中去
在每个chunk里面都有一个非assert的配置,这里演示的是比较正确的配法,直接AddExternFileToPak,把对应平台的这个文件打到对应平台的pak中即可
按路径划分就两个重要的东西
@echo off
set "EnginePath=XXX\Engine\Binaries\Win64\UE4Editor-Cmd.exe"
set "ProjectPath=XXXX"
set "ToolsPath={ProjectPath}\Tools\PSOCache"
REM Platform=Android_ASTC IOS
set Platform=Android_ASTC
前面说完了生成和打包,运行时也有非常重要的处理,首先这个PSO的运行时编译缓存需要用到ShaderCode,那也就是说我们需要先处理ShaderCode
新版的HotPatcher已经很方便的集成了,只需要这边开关开一下就会自动打包进pak里了
每个pak里面都会生成自己的ShaderLibrary,因为引擎启动时默认加载了首包中的ShaderCode,所以我们需要在游戏热更之后手动open一下所有的ShaderLibrary
在HotPatcher这个插件中有实现加载方法,UFlibPakHelper::LoadShaderbytecodeInDefaultDir ,只需要在热更过后的时机遍历加载每个Chunk即可,在没有自定义路径和名称的情况下,只需要传ChunkName就可以了,不用后缀名和路径,只需要Chunk名
PSO加载之前必须已经操作完前面的ShaderCode加载
这里加载规则有一些特殊的情况,首先因为引擎起来的时候会自动去编译PSO,并且后面不会再去编译了,即使你手动去编译也无效,但是我们PSO热更的话,在引擎初始化的时候我们都还没开始热更,所以我们需要先关闭自动编译,然后在等热更完成以后动态的去编译并且加载缓存文件
安卓的Program Binary Cache文件所在目录
IOS下所在的目录
[ConsoleVariables]
r.ShaderPipelineCache.Enabled=0
r.ProgramBinaryCache.RestartAndroidAfterPrecompile=0
把这个添加到DefaultEngine.ini中就行了
可以看到,PSO的整个生成加使用的流程即使在工具的帮助下依然是非常麻烦的,所以如果大家项目没有明显的Shader编译卡顿的情况,可以考虑不用使用这个东西,本文章基本从实用角度详细介绍了整个流程,网上看了不少文章只点出了部分细节,在实际接入中碰到不少坑,这里最主要记录了主要步骤和坑点,当然其中可能还缺少了一些细节,大家可参考最下方的其他文章,有什么问题可在下方评论留言,Enjoy it~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。