Published on

UE4模块启动顺序

前言

本文大致梳理一下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, EditorInitEngineInit, 然后循环EngineTick
  • Launch.cpp内有全局变量 FEngineLoop GEngineLoop;, 上述方法通过GEngineLoop执行
  • GEngineLoop
    • FEngineLoop::PreInit 会执行PreInitPreStartupScreen, PreInitPostStartupScreen
      • 前者会执行如命令行, 本地化等等大量初始化和逻辑, 先通过FEngineLoop::AppInit加载*EarliestPossiblePostConfigInit类型的模块, 随后加载*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 , PostSplashScreenPreEarlyLoadingScreen会在此之前

核心模块有Core, Networking, UnrealEd, Slate等等

编辑器资源初始化在75%之后, 也就是在PreLoadingScreen, PreDefault, Default, PostDefault之后, 所以如果需要对资源进行操作需要使用最后加载的PostEngineInit

另外, 引擎提供了几个全局代理, 可以来监听比如编辑器初始化完毕等事件 , 比如

FCoreDelegates::OnPostEngineInit.AddRaw(this, &FAdvancedFrameworkEditorModule::RegisterPlaceActors);

详情可以参考文件CoreDelegates.h