aixn 发表于 2023-6-20 09:09

Unreal 资源加载和卸载道理机制源码分解

首发地址:Unreal 资源加载和卸载道理机制源码分解

概要


[*]Unreal 主要基于 FStreamableManager 、FAsyncLoadingThread(按需也可为 FAsyncLoadingThread2)、 FLinkerLoad 等进行资源的异步加载。
[*]资源加载主要基于Package来进行,一个Package中包含多种类型的数据信息,Package的加载完成前提是这些资源全部加载完毕。加载完毕还涉及到此中各类型资源的类型构造、属性等的反序列化设置以及依赖资源的加载。全部完成后Package才算加载完毕。
说明


[*]本文基于 UE4.27.2 默认设置及 非 EDL 和 非 WITH_ASYNCLOADING2 总结而来,请按照实际引擎版本、源码及项目实际设置等来阅读理解。
[*]WITH_ASYNCLOADING2 宏的启用法则

[*]#define WITH_ASYNCLOADING2 (WITH_IOSTORE_IN_EDITOR || !WITH_EDITORONLY_DATA)

[*]关于 GEventDrivenLoaderEnabled 下的异同

[*]不异之处

[*]加载的触发、初始化、回调及反序列化等基本一致。
[*]加载的核心法式细节基本一致,如 FLinkerLoad 的各种具体操作。

[*]主要区别

[*]在 FAsyncLoadingThread::ProcessAsyncLoading 中不是逐个Package的法式进行判断和措置,而是主要基于 FAsyncLoadEventQueue 对 EventQueue 进行 PopAndExecute 来触发各法式(如 QueueEvent_FinishLinker 等,其又是对 FinishLinker 的调用)







流程

加载

流程图

简化版(以文字概述主要流程)





详细版(带关键调用函数)





说明


[*]UE中的加载接口有多个,本文基于 UAsyncActionLoadPrimaryAsset::AsyncLoadPrimaryAsset 来触发资源的加载,底层基于 FStreamableManager::RequestAsyncLoad 。此为主流异步加载方式。
[*]加载时会先从内存中找对应的资源是否已加载(FStreamableManager::FindInMemory),如果找到则返回,没有才需要加载。
[*]加载前先初始化,把加载通过 AddPendingRequest 插手 PendingRequests 队列。基于加载路径等信息创建 FAsyncPackageDesc2 ,插手 QueuedPackages 。
[*]加载时,通过 ProcessAsyncLoading 执行加载。其会调用 GPackageLoader 的 ProcessLoading (在 AsyncPackageLoader.cpp 的 InitAsyncThread 的时候,按照 WITH_ASYNCLOADING2 来决定 GPackageLoader 是 FAsyncLoadingThread 还是 FAsyncLoadingThread2)。
[*]加载涉及的关键方式有:FAsyncLoadingThread::ProcessAsyncLoading 、FAsyncPackage::TickAsyncPackage 以及 FLinkerLoad::Tick 等。 此中 FAsyncPackage::TickAsyncPackage 主要负责Package的加载流程,FLinkerLoad::Tick 则负责资源的类型获取及反序列化等。
[*]加载tick开始是,先把已经加载完毕的措置了,执行回调(FAsyncLoadingThread::ProcessLoadedPackages)。
[*]从 QueuedPackages 中获取和筹备要加载的资源包,对他们执行 FAsyncPackage::TickAsyncPackage 。
[*]资源加载主要基于 FLinkerLoad ,其在加载开始时会基于方针资源路径进行创建(FLinkerLoad::CreateLoader)。
[*]FLinkerLoad 创建后,执行反序列化,尤为重要的是 ImportMap 和 ExportMap 等的反序列化。
[*]在 FLinkerLoad::Tick 中,基于 Linker 的 ExportMap 循环创建 FObjectExport。
[*]通过反射等机制调用 StaticConstructObject_Internal 等,以及借助 FLinkerLoad::IndexToObject 等从反射出的数据中拿到资源对应的基础类型。
[*]对基础资源类型进行 FLinkerLoad::Preload ,主要做反序列化,给属性赋值等。
[*]资源加载完毕后,加到已加载队列(FUObjectSerializeContext::AddLoadedObject)
[*]Package 加载完毕后,加到已加载Package队列(PackageObjLoaded.Add(Object)),注意:一个Package可能包罗很多子资源。
[*]全部Package都加载完毕后,执行加载完毕的回调(FAsyncPackage::CallCompletionCallbacks)
卸载


[*]此处基于 UAssetManager::UnloadPrimaryAssets 进行资源的卸载的简单说明。
[*]UAssetManager::UnloadPrimaryAssets 的感化:卸载先前已加载的主要资产列表。如果将这些资产保留在内存中的独一方式是之前的加载调用,则它们将被释放。
[*]UnloadPrimaryAssets 其主要对方针资源列表中的资源的 CurrentState 和 PendingState 执行 FPrimaryAssetLoadState::Reset 。




[*]FPrimaryAssetLoadState::Reset 中主要是对 Handle(FStreamableHandle)执行 CancelHandle




[*]FStreamableHandle::CancelHandle :如果加载完毕的资源还没 release,则执行 ReleaseHandle ;执行 canceldelegate ;解绑所有回调;把本身从引用中移除;从子handle中移除本身。




关键类

类图





说明


[*]UAssetManager

[*]负责加载和卸载主要资产并维护游戏特定资产引用的单例对象,可覆盖以做项目自定义。

[*]FStreamableManager

[*]用于打点流式资产并将其保留在内存中的本机类。 AssetManager 是本类的具有蓝图访谒权限的全局单例版本

[*]FStreamable

[*]内部对象,存储有Object的引用、加载中及激活中的句柄。

[*]FStreamableHandle

[*]同步或异步加载的句柄。 只要句柄处于活动状态,加载的资产就会保留在内存中。内有优先级、请求的资源列表、各种回调等。

[*]FAsyncPackage

[*]包含异步加载 FLinkerLoad 的所有导入和导出所需的中间数据的布局。TickAsyncPackage 是 Package 的异步加载的关键函数。

[*]FAsyncLoadingThread

[*]异步加载线程。 在异步加载线程上预加载/序列化包。 在游戏线程上 Postloads 对象。存储和更新加载中的以及完毕的资源和包列表等。

[*]FLinkerLoad

[*]措置加载虚幻包文件,包罗从磁盘读取和反序列化 UObject 数据。祖父类 FLinkerTables 中存储了从文件中反序列化而来的 ImportMap 、 ExportMap 、 DependsMap 等数据,这些是获取和创建类的关键。

[*]FArchive

[*]序列化和反序列化的关键类,详见文末“Unreal 序列化和反序列化道理机制源码解析”,此处主要存眷加载卸载,不合错误此进行赘述。

相关链接

Unreal 序列化和反序列化道理机制源码解析

声明:本文来自公众号:Unity 与Unreal 游戏开发(GameDevLearning),转载请附上原文链接及本声明。
页: [1]
查看完整版本: Unreal 资源加载和卸载道理机制源码分解