文章

Unreal 资源管理&加载

什么是Cook?

  • 资源的序列化
  • 将资源转换为引擎定义的组织形式
  • 在Unity中这环节是不可见的
    • 在编辑器模式下,Editor借助 Library来完成这个环节;
    • Resource、AssetBundle模式下都经过了序列化(源码中称为Transfer)

参与Cook的资源分类

>> \Engine\Source\Runtime\Engine\Public\Engine\ICookInfo.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
enum class EInstigator : uint8
{
	InvalidCategory,
	NotYetRequested,
	Unspecified,
	StartupPackage,
	AlwaysCookMap,
	IniMapSection,
	IniAllMaps,
	CommandLinePackage,
	CommandLineDirectory,
	DirectoryToAlwaysCook,
	FullDepotSearch,
	GameDefaultObject,
	InputSettingsIni,
	StartupSoftObjectPath,
	PackagingSettingsMapToCook,
	CookModificationDelegate,
	ModifyCookDelegate,
	AssetManagerModifyCook,
	AssetManagerModifyDLCCook,
	TargetPlatformExtraPackagesToCook,
	ConsoleCommand,
	CookOnTheFly,
	IterativeCook,
	PreviousAssetRegistry,
	RequestPackageFunction,
	Dependency,
	HardDependency,
	SoftDependency,
	Unsolicited,
	GeneratedPackage,
    Count,
};

那么UE中这些资源的 cook 的大致流程是怎样的?

  • 在代码中形如 Uxxx::Serialize(FArchive& Ar) 类比 Unity 中的 Transfer 函数
  • UE中这个是 UObject 的虚函数

不同资源的Cook流程是怎么样的?有什么需要注意和优化方向?

如何 Cook指定的资源?

UE里的依赖是怎么处理的?

资源的一些概念

去查阅一下文档里这些相关的信息

  • Blueprint
  • Primary Asset == 有点像打AssetBundle标签
  • Secondary Asset == 纹理或网格之类的

IO Store??

资源命名规范

  • 文件名 & 路径中不允许包含 . : 以及其他不可显字符

加载细节

  • 加载参数里有 SandBox 是否可以作为加速搜索的入参?
    • A list of packages to restrict the search for the object

加载设计

这篇写的不错 资源加载相关 对几个核心API进行了分析

不同的加载API有什么区别

https://github.com/Rootjhon/LoadTester

查找资源

  • FindObject
  • FindObjectFast
  • FindObjectChecked
  • FindObjectSafe
  • FSoftObjectPath::ResolveObject

同步

  • LoadObject
  • LoadClass
  • LoadPackage
  • FSoftObjectPath::TryLoad
  • FStreamableManager::RequestSyncLoad
  • FStreamableManager::LoadSynchronous
  • FlushAsyncLoading(异步转同步)

异步

  • LoadPackageAsync
  • FStreamableManager::RequestAsyncLoad

加载状态

  • GIsSavingPackage
  • IsGarbageCollectingOnGameThread
  • IsLoading
  • GetNumAsyncPackages
  • GetAsyncLoadPercentage
  • FStreamableManager::IsAsyncLoadComplete

非引擎反序列化资源的线程化

优先队列

lazy同步

  • 这个概念由 LoadObject,LoadClass,LoadPackage 这几个同步加载的底层实现改造
  • 最终调用的是LoadPackageAsync函数,这就是异步加载的入口,并且最后FlushAsyncLoading,内部阻塞等待,将异步加载转为同步
  • 因此,可以将返回 资源加载请求的对象 时,再进行 FlushAsyncLoading 的同步锁定;

当前加载的任务数

  • GGameThreadLoadCounter 引擎层面在加载的任务数
  • 自己维护一个任务数,可以监控 是否存在不规范调用

资源加载的进度

  • GetAsyncLoadPercentage

UObject上的PostLoad 函数似乎可以用来做热重载?

资源卸载

  • 常规方式:默认情况下,加载中的资源由引擎持有引用,不会被卸载,加载完成后的资源会依赖引擎的gc卸载。如果没有被使用到,会在下次gc的时候释放掉。如果需要立即释放可以手动强制引擎gc。
    • UEngine::ConditionalCollectionGrbage()
    • UEngine::ForceGarbageCollection()
  • 分类别释放
    • UObject : ConditionalBeginDestory => 调用后需保证对象不再被使用(内部逻辑等价于 BeginDestroy )
    • Texture\Mesh\Material : ReleaseResource => 会影响渲染队列,可以push到渲染队列里(实际需要去测试一下)
    • RenderTarget : FPooledRenderTarget::Release => 回收再利用
    • DynamicMaterial : 专用函数?
    • Actor\ActorComponent : DestroyActor \ DestroyComponent
    • SceneComponent : DestroyPhysicsState \ DestroyRenderState

加载框架设计

主要涉及的几个关键字:

  • FLinkerLoad
  • CreateLinker
  • ImportMap
  • ExportMap
  • DependsMap
  • Object->Serialize [同步加载,反序列化]
  • operator«( UObject*& Object )
  • Ar « Index [UObject 类型属性反序列化]

  • FAsyncPackage
  • EventDrivenSerializeExport
  • StaticAllocateObject
  • Object->Serialize

UClass

  • SerializeTaggedProperties
  • Ar « Tag [读取属性Tag,反序列化]

类:

  • UPackage
  • UObject
  • SerializeScriptProperties

  • FDuplicateDataWriter
  • FObjectInstancingGraph

具体修改内容:

  • UObject (Parent)
    • 【内存数据刷新】
      • RefreshResourceObject
    • Serialize
      • 记录当前反序列化Object
      • ThreadContext.SerializedObject = this
  • SerializeScriptProperties

  • FLinkerLoad
    • operator«( UObject*& Object )
    • Ar « Index [UObject 类型属性反序列化]
    • 抽取Object到Asset映射
    • 记录反序列化属性Value
  • KnownMissingPackageRefObjectMap
  • KnownMissingPackageRefObjectQuick

UClass

  • SerializeTaggedProperties
  • Ar « Tag [读取属性Tag,反序列化 记录反序列化属性Tag信息]

FDuplicateDataWriter

  • GetDuplicatedObject
  • 抽取Object到Asset映射

FObjectInstancingGraph

  • InstancePropertyValue
  • GetInstancedSubobject
  • 抽取Object到Asset映射

主要流程就是:

  1. 创建Package
  2. Object 反序列化,过程中遇到本地不存在资源,进行记录
  3. 下载所需资源
  4. 更新记录的Object对象数据,刷新显示
本文由作者按照 CC BY 4.0 进行授权