UE Cook 数据结构
本篇讲非 MPCook 的数据结构
1. FPackageDatas
核心类
FPackageDatas
是管理 CookOnTheFlyServer 里的所有 PackageData
的列表的类。PackageDatas
是一个关联数组,存储 COTFS 需要的 package(如 cook results)的额外数据。FPackageData
只会被分配一次,后面永远不会被销毁或移动,直到 COTFS 被销毁。
FPackageData
上的内存根据其当前状态进行必要的分配和释放。
FPackageData
由 PackageName 和 FileName 映射。
这个类还管理所有对 FPackageData
的非临时引用,如 SaveQueue
和 RequestQueue
。
FPackageDatas
是 FGCObject
的派生类。
1.1 FPackageDatas
1.1.1 核心功能
-
包数据生命周期管理
-
持久化存储:
通过
FPackageData
管理每个包的 cook 状态、结果等元数据(依赖关系、平台数据)。这些数据在服务器生命周期内持续存在,避免重复加载/卸载。
-
高效分配:
使用
TTypedBlockAllocatorFreeList
分配器优化FPackageData
内存管理,减少内存碎片。
-
-
多维度索引
-
双映射:
通过
PackageName
和FileName
的哈希表(TMap
) 快速查找包数据,支持包名与文件路径的灵活映射。 -
线程安全:
通过读写锁
FRWLock
保护并发访问,确保 多线程环境下的数据一致性。
-
1.1.2 状态与队列管理
- cook 流程协调
- 队列系统
- 请求队列 RequestQueue: 管理待 cook 的包(FIFO 或优先级排序)。
- 加载队列 LoadQueue: 处理包加载状态以及依赖解析。
- 保存队列 SaveQueue: 按性能排序的保存任务,优化 I/O 吞吐。
- 状态集合:
AssignedToWorkerSet
: 记录分配给工作线程的包。SaveStalledSet
: 跟踪因资源竞争等原因暂停保存的包。
- 队列系统
- 异步任务处理
- 异步平台数据:管理
PendingCookedPlatformData
列表,轮询PollPendingCookedPlatformDatas
异步任务完成状态,释放资源。
- 异步平台数据:管理
1.1.3 平台与依赖管理
- 多平台支持
- 平台数据缓存:维护每个报的跨平台 Cook 结果,支持动态添加/移除平台(如
OnRemoveSessionPlatform
)。 - 平台重映射:通过
RemapTargetPlatforms
更新平台引用,支持热重载或配置变更。
- 平台数据缓存:维护每个报的跨平台 Cook 结果,支持动态添加/移除平台(如
- 依赖解析
- 层级排序:
LeafToRootRank
管理包依赖的拓扑排序,确保依赖包按正确顺序处理。
- 层级排序:
1.1.4 工具与调试
- 调试与监控
- 引用追踪:集成
FGCObject
接口,防止 UObject 资源泄露。 - 监控统计:通过
FPackageDataMonitor
聚合性能数据(如加载/保存时间、内存占用)。
- 引用追踪:集成
- 异常处理:
- 错误恢复:支持清除指定包的 Cook 状态(
ClearCookResultsForPackages
),处理失败或无效包。
- 错误恢复:支持清除指定包的 Cook 状态(
1.1.5 关键操作
- 动态包发现:
- 通过
TryAddPackageDataByFileName
或AddPackageDataByPackageNameChecked
按需创建包数据,处理编辑器中的动态包创建/移动。
- 通过
- 路径标准化:
GetStandardFileName
规范化文件路径,确保索引一致性(如处理大小写、路径分隔符)。
1.1.6 应用场景
- 增量 Cook:跟踪已经处理的包,跳过无需重新 Cook 的资源。
- 分布式 Cook: 通过
AssignedToWorkerSet
协调多工作线程的任务分配。 - 资源热更新: 动态更新包路径(
UpdateFileName
),响应资源移动或重命名。
1.1.7 总结
FPackageDatas
是 COTFS 的中央数据枢纽,负责协调包的整个 Cook 生命周期——从请求调度、依赖解析、异步任务处理到结果缓存。其设计兼顾性能(高效数据结构、异步轮询)和可靠性(线程安全、资源监控),为复杂的大规模资源 Cook 提供底层支持。
1.2 FPackageDataMonitor
一个被 FPackageDatas
持有的监视器类,基于已完成的 FPackageData
或 InProgress 的汇总数据提供报告和决定制定。
2. FPackageData
<待补充>
3. IAssetRegistry
IAssetRegistry
是 UE 资源管理的核心抽象接口,承担以下核心职责:
3.1 全局资源目录管理
-
跨平台数据聚合
作为单例接口,统一管理 Editor 与 Runtime 资源信息。Editor 通过磁盘扫描手机包信息,Runtime 依赖 Cook 生成的 序列化数据,确保不同环境下资源数据的一致性。
-
多源数据整合
- 支持同时处理磁盘持久化数据
bIncludeOnlyOnDiskAssets=true
与内存中的动态资源(如未保存的编辑结果),智能合并标签等元数据。
- 支持同时处理磁盘持久化数据
3.2 高效资源检索
-
多维查询接口
提供按包名
GetAssetsByPackageName
、路径GetAssetsByPath
、类GetAssetsByClass
、标签GetAssetsByTags
等条件查询资源,支持复杂过滤FARFilter
与 编译优化FARCompiledFilter
。 -
高性能遍历
EnumerateAllAssets
等方法允许回调式遍历,避免大规模数据拷贝,适合处理海量资源。
4. Instigator 发起者
见 MPCook 中的多线程
1.3.2 FInstigator
FFilePlatformRequest
中的构造函数中用到了 FInstigator
,也就是记录了 策动者
- instigator
- n. 策动者;煽动者;教唆者
- instigate v. 使(正式)开始,使发生;鼓动
FInstigator
结构体用于 追踪资源(Package)被发现并加入 Cook 队列的原因和来源。它结合 EInstigator
枚举的类别(Category)和可选的引用者(Referencer),记录资源被 Cook 的上下文信息。
核心作用
- 记录资源被发现的来源
Category
: 类型EInstigator
: 表示资源被发现的 “原因” 或 “触发途径”。例如:StartupPackage
: 引擎启动时必须加载的核心资源。CommandLinePackage
: 通过命令行参数显式指定的资源。Dependency
: 因其它资源依赖而被间接发现。
Referencer
: 类型FName
: 可选的引用者名称,用于标识触发该资源发现的直接源头(如依赖他的父资源路径)。
- 调试与 log
- 当资源 cook 出现问题时,通过
FInstigator::ToString()
生成的字符串可以快速定位触发该资源 cook 的代码路径或外部输入。 - 例如:若资源因资源 B 的依赖被 cook,log 会显示类似
Dependency(Referencer=/Game/B)
。
- 当资源 cook 出现问题时,通过
- 依赖链追踪
- 在复杂资源依赖场景中,通过
Referencer
字段可以构建依赖链,帮助解决循环依赖或优化不必要的依赖。
- 在复杂资源依赖场景中,通过
- 优先级与调度决策
- 不同
Category
可能影响资源处理的优先级。例如:AlwaysCookMap
类别的资源可能被优先处理。Unsolicited
(未请求的资源)可能需要额外验证。
- 不同
结合 EInstigator
枚举的具体场景
EInstigator 值 | 场景 |
---|---|
StartupPackage |
引擎启动时自动加载的核心资源(如默认材质类、基础蓝图类) |
CommandLinePackage |
通过命令行参数 -cookpackage=/Game/Asset 显式指定需要 Cook 的资源。 |
Dependency / HardDependency |
资源因其它资源的硬依赖被引用(如静态引用的材质或网格)。Referencer 记录父资源路径。 |
CookOnTheFly |
动态运行时按需 Cook 触发的资源请求。 |
AssetManagerModifyCook |
资源管理器(AssetManager)在 cook 过程中动态添加的资源(如主资产列表中的条目)。IniMapSection |
IterativeCook |
增量 cook 模式下,仅处理已修改的资源。 |
5. GC 需要的数据结构
5.1 TickStackData
struct FTickStackData
{double LoopStartTime = 0;// A bitmask of flags of type enum ECookOnTheSideResult that were set during the tick.uint32 ResultFlags = 0;FCookerTimer Timer;ECookTickFlags TickFlags;bool bCookComplete = false;bool bCookCanceled = false;explicit FTickStackData(float TimeSlice, ECookTickFlags InTickFlags): Timer(TimeSlice), TickFlags(InTickFlags){}
};
FTickStackData
里用到了 ECookOnTheSideResult
也就是 COSR
这里有各种结果,主要是和 GC 相关的,如 RequireGC
, RequiresGC_OOM
, RequiresGC_PackageCount
, RequiresGC_IdleTimer
, RequiresGC_Soft_OOM
等。
即TickStackData.ResultFlags
是一个 enum,主要是用来进行逻辑于或操作,表示结果标志。
6. Cook 模式
6.1 ECookMode
ECookMode::CookOnTheFly
- 不常用
ECookMode::CookByTheBook
- 普通的 Cook 就是这种模式
ECookMode::CookWorker
- 注释:给一个单独的 director 进程用到 命令行助手。 Director 可能是其他模式。
6.2 CurrentCookMode 和 DirectorCookMode
分别代表当前的 Cook 模式和 Director 的 Cook 模式。
其中,CurrentCookMode
在构造函数中已经被初始化一次
然后在 Initialize
函数中又被赋值一次
那很明显,走 CookByTheBook 的时候 DesiredCookMode
是 CookByTheBook
,走 CookWorker
模式的时候 DesiredCookMode
是 CookWorker
。
- CookByTheBook
- CookWorker
这里稍稍深入一下 TryInitializeCookWorker
是怎么被调用的
它在 CookCommandlet.cpp
中被 CookAsCookWorker
被调用
CookAsCookWorker
在 Main 函数中,当参数含有 COOKWORKER
时被调用
那什么时候命令行参数含有 COOKWORKER
呢?注意不是用户手动添加的,而是开启了 MPCook 之后,FCookDirector 启动,创建 Socket 连接,注册 Log
, AssetRegistry
, PackageWriter
,然后 CookDirector::InitializeWorkers
也就是初始化 Workers 之后,创建 CookWorkerServer
,CookWorkerServer
调用 LaunchProcess
之后,启动进程
FPlatformProcess::CreateProc(UE5Editor.exe, -COOKWORKER, -CookProfield=RemoteIndex -MultiprocessId=RemoteIndex+1)
7. 沙盒 SandBox
CookOnTheFlyServer
类中,有一个独占指针 TUniquePtr<UE::Cook::FCookSandbox> SandboxFile
所谓沙盒,就是指一个隔离的运行环境,用于 限制程序或进程对系统资源的访问,以提高安全性和可控性。
在 UE 语境中,沙盒文件系统的具体含义有:
- 文件访问隔离
- 安全性
- 虚拟化文件系统
- 开发和测试便利
- 跨平台一致性
7.1 FSandBoxPlatformFile
大致就这些,它继承自 IPlatformFile
。IPlatformFile
是一个抽象基类,定义了一个平台无关的 文件系统接口,用于处理文件和目录操作。
-
IPlatformFile
-
提供平台无关的文件系统操作接口
-
文件操作
-
目录操作
-
异步 I/O
-
文件句柄管理
-
文件系统特性
-
-
支持文件系统连
- 沙盒文件系统
- Pak 文件支持
- 文件系统扩展
-
跨平台文件系统适配
- PhysicalPlatformFile
- PakFile
- SandboxFile
- 限制文件访问到特定目录,增强安全性
- NetworkFile
- 支持通过网络访问文件
-
FSandboxPlatformFile 的作用
-
限制文件访问范围(沙盒化)
-
支持虚拟化文件系统
-
文件盒目录过滤
-
跨平台一致性
-
支持特定场景
- 游戏打包:在打包游戏时,
FSandboxPlatformFile
可以用来限制资源访问,防止意外包含非项目文件。
- 游戏打包:在打包游戏时,
7.2 FCookSandBox
FCookSandbox
是对 FSandboxPlatformFile
的进一步封装,主要管理 Cook 过程中的 文件路径转换、插件重映射以及特定平台的沙盒路径处理,确保资源在不同平台上的正确打包和访问。
作用
- 多平台支持
- 为不同目标平台 如 Windows, PS5, Android 等生成正确的沙盒路径。
- 插件重映射
- 支持在 Cook 过程中将插件的资源路径重新映射到目标路径(如将插件内容 “注入” 到项目目录)。
- 路径转换
- 将未 Cook 路径 (
UnCooked Path
) 与 CookedPath 相互转换,并处理与包名称 Package Name 之间的映射。
- 将未 Cook 路径 (
- 沙盒隔离
- 确保 Cook 过程中的文件在沙盒环境中进行,防止意外访问非项目文件。
FCookSandbox
的设计目标是提供一个更高层次的接口,简化 Cook 过程中文件路径的管理,同时保持底层 FSandboxPlatformFile
的沙盒化特性。
8. Cook 上下文
这里把 Cook 过程中用到的配置类、Context 类等 CookTypes.h
或 CookOnTheFlyServer.cpp
中定义的统一称为 上下文,可以理解为 配置、选项、环境等。
8.1 FCookByTheBookStartupOptions
struct FCookByTheBookStartupOptions
{TArray<ITargetPlatform*> TargetPlatforms; // 目标平台TArray<FString> CookMaps; // 待烘焙地图 TArray<FString> CookDirectories; // 带烘焙目录TArray<FString> NeverCookDirectories; // 永不烘焙目录TArray<FString> CookCultures; // 需要烘焙的 CulturesTArray<FString> IniMapSections;// list of packages we should cook, used to specify specific packages to cookTArray<FString> CookPackages;ECookByTheBookOPtions CookOptions = ECookByTheBookOptions::None;FString DLCName; // DLC 名字FString CreateReleaseVersion; // 当前版本号FString BasedOnReleaseVersion; // 基版本号bool bGenerateStreamingInstallManifests = false;bool bGenerateDependenciesForMaps = false;// this is a flag for dlc, will cause the cooker to error if the dlc references engine contentbool bErrorOnEngineContentUse = false;
};
8.1 FBeginCookContext
包含上面的 FCookByTheBookStartupOptions
和平台相关的一些信息