Unreal 资源加载和卸载原理机制源码剖析

Viewed 12

概要

  • 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 的调用)


6rv528i1hj3mst5gvc5b1rmikc.png

02vog3u8haheebqhvtj7scs71c.png

流程

加载

流程图

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


6v92bnapbgacju7tle2cjtha2f.png

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


loading_flow.png

说明

  • 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 。


5plknidoeuq1o5pcs61ocmffua.png

  • FPrimaryAssetLoadState::Reset 中主要是对 Handle(FStreamableHandle)执行 CancelHandle


0pq9023dd13d0vmhjjn20ncc6t.png

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


2b9529rlajobm6bffvrgg0bl2k.png

关键类

类图


loading_class.png

说明

  • UAssetManager
  • 负责加载和卸载主要资产并维护游戏特定资产引用的单例对象,可覆盖以做项目自定义。
  • FStreamableManager
  • 用于管理流式资产并将其保存在内存中的本机类。 AssetManager 是本类的具有蓝图访问权限的全局单例版本
  • FStreamable
  • 内部对象,存储有Object的引用、加载中及激活中的句柄。
  • FStreamableHandle
  • 同步或异步加载的句柄。 只要句柄处于活动状态,加载的资产就会保留在内存中。内有优先级、请求的资源列表、各种回调等。
  • FAsyncPackage
  • 包含异步加载 FLinkerLoad 的所有导入和导出所需的中间数据的结构。TickAsyncPackage 是 Package 的异步加载的关键函数。
  • FAsyncLoadingThread
  • 异步加载线程。 在异步加载线程上预加载/序列化包。 在游戏线程上 Postloads 对象。存储和更新加载中的以及完毕的资源和包列表等。
  • FLinkerLoad
  • 处理加载虚幻包文件,包括从磁盘读取和反序列化 UObject 数据。祖父类 FLinkerTables 中存储了从文件中反序列化而来的 ImportMap 、 ExportMap 、 DependsMap 等数据,这些是获取和创建类的关键。
  • FArchive
  • 序列化和反序列化的关键类,详见文末“Unreal 序列化和反序列化原理机制源码解析”,此处主要关注加载卸载,不对此进行赘述。

相关链接

卡德卡斯基:Unreal 序列化和反序列化原理机制源码解析

**声明:**本文来自公众号:GameDevLearning,转载请附上原文链接(https://mp.weixin.qq.com/s/JDhEuIXxMnuCyFroTUPr1w)及本声明。

0 Answers