- Published on
UE4模块启动顺序
- Authors

- Name
- 东哥
前言
本文大致梳理一下UE4编辑器启动后各个模块的加载顺序, 重点看一下ELoadingPhase枚举对应的类型的加载时机
入口
任何可执行程序都会有一个执行入口,在 UE 中,每一个 Target 都会编译出一个可执行程序。引擎启动是从 Launch 模块开始的,main 函数也是定义在其中的
简化代码表示
int32 GuardedMain( const TCHAR* CmdLine )
{
EnginePreInit( CmdLine );
//...........
if (GIsEditor)
{
EditorInit(GEngineLoop);
}
else
{
EngineInit();
}
//...........
while( !IsEngineExitRequested() )
{
EngineTick();
}
}
从代码的字面意思就可以理解为启动步骤大概就是 预初始化->初始化, 然后是Tick循环
这里不得不说到一个引擎类 FEngineLoop
FEngineLoop 管理了程序的初始化与主循环
因为整个UE是模块化的架构,引擎的实现就是靠各个模块组合驱动的, 那么就拿模块的启动来看一下引擎的启动顺序
模块启动
先看一下模块加载的类型
namespace ELoadingPhase
{
enum Type
{
EarliestPossible,
PostConfigInit,
PostSplashScreen,
PreEarlyLoadingScreen,
PreLoadingScreen,
PreDefault,
Default,
PostDefault,
PostEngineInit,
None,
Max,
}
}
先看一下PreDefault模式的插件到StartupModule的调用栈

先看下面代码
bool FEngineLoop::LoadStartupModules()
{
FScopedSlowTask SlowTask(3);
SlowTask.EnterProgressFrame(1);
// Load any modules that want to be loaded before default modules are loaded up.
if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PreDefault) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreDefault))
{
return false;
}
SlowTask.EnterProgressFrame(1);
// Load modules that are configured to load in the default phase
if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::Default) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::Default))
{
return false;
}
SlowTask.EnterProgressFrame(1);
// Load any modules that want to be loaded after default modules are loaded up.
if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostDefault) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostDefault))
{
return false;
}
return true;
}
上面是几种类型加载模式的模块启动都在编辑器引擎启动之前, 意味着UEditorEngine::InitEditor()还未执行.
那么其他类型比如常见的PostEngineInit是啥时候启动的呢?
在FEngineLoop::Init()中, 先执行了GEditor的初始化, 然后再执行PostEngineInit类型的模块的加载
{
SCOPED_BOOT_TIMING("GEngine->Init");
GEngine->Init(this);
}
//............
{
SCOPED_BOOT_TIMING("IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostEngineInit)");
// Load all the post-engine init modules
if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostEngineInit) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostEngineInit))
{
RequestEngineExit(TEXT("One or more modules failed PostEngineInit"));
return 1;
}
}
总结
简单梳理各类模块加载的顺序,以及部分常见的功能的初始化顺序
- 多种平台调用
GuardedMain() //Launch.cpp GuardedMain依次调用EnginePreInit,EditorInit或EngineInit, 然后循环EngineTickLaunch.cpp内有全局变量FEngineLoop GEngineLoop;, 上述方法通过GEngineLoop执行- GEngineLoop
FEngineLoop::PreInit会执行PreInitPreStartupScreen,PreInitPostStartupScreen- 前者会执行如命令行, 本地化等等大量初始化和逻辑, 先通过
FEngineLoop::AppInit加载*EarliestPossible和PostConfigInit类型的模块, 随后加载*PostSplashScreen类型的模块 (0%-18%), 退出函数时加载至39%** - 后者加载*
PreEarlyLoadingScreen类型的模块(39%); 随后播放开场动画等, 然后加载AssetRegistry模块, 然后通过LoadStartupCoreModules(45% - 55%)方法加载核心模块, 随后是PreLoadingScreen类型的模块(55% - 59%), 接着开始渲染线程StartRenderingThread(59% - 75%), 然后通过LoadStartupModules(75%)方法加载PreDefault,Default,PostDefault*类型的模块, - 随后创建几个单例并调用
Init(%79 - 86%)- 创建
GEditor并调用GEditorEngine::InitEditor, 该方法会加载编辑器资源 - 创建
GEngine并调用GEngine::Init, 该方法也有大量初始化比如创建FWorldContext,Config的加载, 音效设备的初始化, 加载一些系统模块(MovieScene,LevelSequence等等)
- 创建
- 前者会执行如命令行, 本地化等等大量初始化和逻辑, 先通过
- 然后通过执行
FEngineLoop::Init加载*PostEngineInit*类型的模块(86% - 90%)
补充
加载核心模块在 45%进度,
EarliestPossible,PostConfigInit,PostSplashScreen和PreEarlyLoadingScreen会在此之前核心模块有
Core,Networking,UnrealEd,Slate等等编辑器资源初始化在75%之后, 也就是在
PreLoadingScreen,PreDefault,Default,PostDefault之后, 所以如果需要对资源进行操作需要使用最后加载的PostEngineInit
另外, 引擎提供了几个全局代理, 可以来监听比如编辑器初始化完毕等事件 , 比如
FCoreDelegates::OnPostEngineInit.AddRaw(this, &FAdvancedFrameworkEditorModule::RegisterPlaceActors);
详情可以参考文件CoreDelegates.h