之前我写了一篇文章,介绍UE默认的拆包方式(UE 热更新:拆分基础包),但UE默认的拆包方式在大规模资源项目中不够灵活,无法满足对超大规模资源项目的精细化管理需求。
为了解决UE默认打包和资源拆分的痛点,我基于HotPatcherCore开发了一个扩展,能够能够解决默认拆包流程的缺点,足够地灵活与强大。博客中有篇文章,介绍它的整体实现机制: 一种灵活与非侵入式的基础包拆分方案。
HotChunker能够非侵入式地集成到UE默认的打包流程中,无需手动做任何的处理,只要UE默认打包,就会自动拉起HotChunker的拆包流程,实现资源的Cook、打包,以及自动Copy到StagedBuilds目录中。
本篇文章会着重介绍,使用HotChunker在进行资源拆分的配置方式和自动化构建、最大限度地降低资源冗余、以及进行并行打包的实现。
UE默认打包的问题
UE默认使用的CookerOnTheFly
-UnrealPak
的模式本身存在一些问题:
- 不能够精确地打包和灵活控制自定义拆包的行为
- 没有全平台通用的pak进包过滤机制(只有安卓的
ObbFilter
) - 强依赖Cook流程,无法单独打包某一个Chunk
- 无法方便地进行程序化的拆包
- 无法配置Chunk包含Not-uasset文件
- 所有Not-uasset文件均在0号Chunk
- 无法基于chunk拆分Metadatas
HotChunker的实现能够比较好的解决上面的问题,每个Chunk都是自包含的:
最终效果,UE打包的资源配置,只有那些最基础的资源由UE自己管理打包,其余的都可以通过HotChunker的配置来拆分。
文章的后续部分会介绍如何配置做到这一点。
Chunk配置
Chunk的配置,是进行包拆分的最小单元,在UE中,每个Chunk会打包为一个独立的pak文件。
对于HotChunker的实现,单个Chunk的配置可以包含以下这些属性:
Chunk的资源的配置提供了两种模式:Rule/Getter,用于指定打包的资源路径或从代码中通过程序化的方式获取。
每个Chunk都可以添加的外部文件,以及控制Shader的序列化方式、AssetRegistry的序列化等等。并且Chunk的metadta是自包含的,最显著的就是降低安装包内ShaderLirary的大小。
默认情况下,UE打包项目的Shader都被序列化到了同一个文件内,并且这个文件是被打包到pakchunk0之内的,也就是整个项目完整的Shader都在安装包之内。资源规模上来之后ShaderLibrary大小非常恐怖,可能会达到数百M。
让每个Chunk的Shader自包含,能够比较好地解决这个问题。并且配合上我之前的一种高效的 ZSTD Shader 字典训练方案,组合起来能够最大限度地降低ShaderLibrary的大小,实现Shader的按需下载。
Rule
Rule模式的配置和HotPatcher的配置类似,可以指定包含路径、资源、忽略的资源。
这种模式适合配置比较固定的资源,如打包某个目录、某个地图等。
Getter
除了直接指定资源之外,我还提供了一种Getter的配置模式,可以指定一个继承自UHPLGetter
的类,从代码中动态地获取Chunk管理的资源。
之所以会有这样一种模式,是期望与策划的配置建立起直接的对应关系。基于策划的配置来控制打包。
比如,当前版本包含了哪几个地图、哪些职业的角色等等,这些配置是会随着开发进程不断变化的,而数据源头在策划,在UE默认的方式中,还需要在工程中进行修改打包配置。
但,HotChunker通过Getter这种配置方式建立起打包与策划配置的对应关系,就能够策划来直接控制打包哪些资源。
Chunk管理
HPL资源
与PrimaryLabel类似,我给Chunk的配置增加了一个等价物,我将其称之为HPL
(HotPatcherPrimaryLabel)。
可以创建一个HPL资源用于编辑要打包的资源,每个HPL是一个独立的Chunk。
在打包中,需要在Project Settings
-Plugins
-HotChunker
中添加扫描路径:
在HotChunker的打包中,会自动扫描所有配置的目录下的HPL资源,进行打包。
DataTable
如果想要创建多个Chunk,可以创建多个HPL的资源,但是为了方便管理,我还支持了DataTable配置。
选择创建DataTable,RoleStructure
选择HPLChunkSetting
:
打开编辑,每个元素就是一个独立的HPL的Chunk配置:
并且我为HPL的DataTable添加了一个专门的打包按钮,可以直接在表格界面中选择打包哪个Chunk:
它会拉起一个单独的进程执行,不会阻塞当前编辑器。
同样的,当创建了一个HPL的DataTable,想要将其参与打包,需要在Project Settings
-Plugins
-HotChunker
中添加ImportRuleTables
配置:
动态注册
为了实现程序化自动进行分包的需要,我为拆分配置增加了动态注册的机制,只需要绑定该Delegate:
1 | FHotChunkerDelegates::OnNotyfyChunkRegister.AddStatic(&FGameEditorModule::OnNotyfyChunkRegister); |
在该函数中将Chunk注册进去:
通过动态注册或者前面提到的Getter机制,可以把策划配置与打包建立起一种对应关系,可以完全按照策划的配置,来自动控制要打包哪些资源,解放程序的配置任务。
编辑器支持
我为Chunk的DT配置提供了编辑器选项,能够比较方便地选择打包某个Chunk,在开发过程中,可以让美术同学自己方便地打包自己所需要的资源,避免出包的等待。
- 打包单个Chunk
- 打包DT中所有的Chunk
- 导入/导出Chunk的配置
- 不阻塞编辑器
全局配置与流程扩展
在Project Settings
-Plugins
-HotChunker
中可以进行全局配置,和逻辑替换(ChunkerManager)。
- HPL扫描路径
- Chunk规则DT配置
- Pak Setting
- 功能开关
- 替换ChunkerManager
- 全局重载
介入Chunker的各个阶段
为了实现足够的灵活性,在实现过程中,我为Chunk的打包留了很多可以介入到各个阶段的口子。
- 按需对资源资源分析、处理
- 获取打包状态、结果
- 获取完整的Manifest信息
- 进行包体审计
在框架之内,可以非侵入式地实现项目的各种分析需求。
冗余控制
我在UE热更新:拆分基础包#Priority 打包时失效中介绍了通过UE默认的拆包形式,优先级不生效的情况,并提供了解决方案。
HotChunker在实现时,就已经规避了这些问题,Chunk的配置也支持Priority
。
如果Chunk的Priority是平级的,则它们之间就没有依赖关系,统计资源会冗余一些。
打进安装包内的Chunk之间是没有资源冗余的,在安装包之外的Chunk,可以优先级来进行冗余控制。
Commandlet支持
我为HotChunker添加了多种Commandlet支持,能够比较方便地使用命令行打包。
- 打包工程里所有配置的Chunk
- 打包一组Chunks
- 打包单个Chunk
- 打包某个命名的Chunk
1 | -run=HotChunker -TargetPlatform=IOS |
打包控制与动态开关
Chunk的打包控制,分为三种类型:
- bUseHPL:是否启用HotChunker的配置打包chunk
- ALL:所有的chunk
- BASEBUILDS:安装包内的Chunk
- EXTERNAL:安装包之外的Chunk
在项目设置中可以控制默认打包的类型
除了固定配置之外,还可以在commandlet传递命令来覆盖引擎中的配置,能够比较方便地在CI/CD上进行控制。
目前支持了两个动态开关:
1 | # true/false |
在拉起UE的CookCommandlet时传递,就会自动地控制Chunker的打包开关和生成的Chunk类型。
如果是自己在CI/CD中调用HotChunkerCommandlet
,也可以传递这两个动态开关。
如,只打包非安装包内的Chunk:
1 | -run=HotChunker -TargetPlatform=IOS -GenerateType=EXTERNAL |
能够比较方便地集成到CI/CD系统中:
通过这种机制,可以实现打包时的精确控制。在一些测试场景中,可以实现快速出包,避免每次打包都要处理完整资源。
Chunk的加密
使用HotChunker方式管理并打包的Chunk,默认复用项目配置中里DefaultCrypto.ini
的配置。
在Project Settings
-Plugins
-HotChunker
-HPLPakSettings
-EnceyptSettings
中配置:
bUseDefaultCryptoIni
:使用项目设置中的配置。如果不复用,可以通过CryptoKeys
来指定crypto.json进行加密。
通用的包过滤方案
在UE 热更新:拆分基础包这篇文章里,我介绍了Android和IOS包过滤的方法。
引擎默认只为Android提供了ObbFilter
,但没有为IOS提供等价物,需要自己实现。
在HotChunker的实现中,我对包过滤的机制做了补充,可以在创建每个Chunk时,直接指定它是否进安装包。
并且,可以针对某个平台特殊处理:如,默认情况下进基础包,但在WindowsNoEditor平台不进。
如果不想要对每个Chunk都单独配置,可以在项目设置中配置全局重载:
这个配置意味着,在打包WindowsNoEditor平台时,HotChunker所有管理的chunk都会归档到安装包之内。
流程优化
默认情况下,UE使用CookerOnTheFly的过程,是要把当前工程中所有参与打包的资源都执行Cook之后,才进入到Pak打包过程。
本质上是一个串行的过程:
但是当资源规模庞大之后,这个过程非常之久。
但实际上,我们并不一定需要把所有的资源处理完,才能打安装包。因为在手游中,安装包内的资源只是一少部分,其余的资源都是热更动态下载下来的。
在HotChunker的实现中,可以把安装包内Chunk的打包与动态下载的Chunk进行并行执行:
这样就能够节省大量的时间,用最快的速度打出安装包,再用一个独立的打包任务并行执行动态下载的Chunk的打包。
结语
基于HotChunker的包管理方案,是一种强大和精确地包管理机制。能够在精确拆分资源的同时,保证配置的灵活性,并且支持强大的Commandlet能力。能够方便地非侵入式扩展流程和进行资源审计。
而且,该方案是非侵入式集成,无需对默认的打包过程做任何处理也不需要修改引擎,整个实现都是插件化的,只要启用插件后拉起UE默认的打包就会自动执行。