引擎版本: 4.18
部分耗时流程结构概述:
Winmain
└GuardedMain
└EnginePreInit
└FEngineLoop::PreInit
└LoadPreInitModules()
└AppInit()
└ProcessNewlyLoadedUObjects()
└LoadStartupCoreModules()
└LoadPreLoadingScreedModules
└LoadStartupModules()
└FStartupPackages::LoadAll()
└EditorInit( IEngineLoop& EngineLoop )
└EngineLoop.Init()
└FUnrealEdMisc::Get().OnInit()
└MainFrameModule.CreateDefaultMainFrame()
EnginePreInit()
int32 FEngineLoop::PreInit( const TCHAR* CmdLine )
{
// 进行一些硬件/平台相关的设置
//
LoadPreInitModules();
// 11.10
AppInit();
ProcessNewlyLoadedUObjects();
LoadStartupCoreModules();
// Load up all modules that need to hook into the loading screen
if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PreLoadingScreen) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreLoadingScreen))
{
return 1;
}
// Load PreDefault/Default/PostDefault Modules For Project and Enabled Plugins
if (!LoadStartupModules())
{
return 1;
}
// load up the seek-free startup packages
if ( !FStartupPackages::LoadAll() )
{
// At least one startup package failed to load, return 1 to indicate an error
return 1;
}
}
FEngineLoop::PreInit()
整个流程有2000+行, 所以以上代码仅为简述, 列出了一些比较耗时的步骤以及关键步骤.
AppInit()
bool FEngineLoop::AppInit()
{
// 解析Cmd命令
// 执行平台对应的PreInit
// 加载PostConfigInit模块
if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostConfigInit) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostConfigInit))
{
return false;
}
FCoreDelegates::OnInit.Broadcast();
}
该部分耗时为6~6.5s左右.
ProcessNewlyLoadedUObjects()
参考: 《InsideUE4》UObject(十)类型系统构造-再次触发
LoadStartupCoreModules()
bool FEngineLoop::LoadStartupCoreModules()
{
FModuleManager::Get().LoadModule(TEXT("Core"));
FModuleManager::Get().LoadModule(TEXT("Networking"));
FWindowsPlatformApplicationMisc::LoadStartupModules();
FModuleManager::LoadModuleChecked<IMessagingModule>("Messaging");
FModuleManager::LoadModuleChecked<IMRMeshModule>("MRMesh");
FModuleManager::LoadModuleChecked<IEditorStyleModule>("EditorStyle");
FModuleManager::Get().LoadModule("Slate");
FModuleManager::Get().LoadModule("SlateReflector");
FModuleManager::Get().LoadModule("UMG");
FModuleManager::Get().LoadModule("MessageLog");
FModuleManager::Get().LoadModule("CollisionAnalyzer");
FModuleManager::Get().LoadModule("FunctionalTesting");
FModuleManager::Get().LoadModule(TEXT("BehaviorTreeEditor"));
FModuleManager::Get().LoadModule(TEXT("GameplayTasksEditor"));
FModuleManager::LoadModuleChecked<IAudioEditorModule>("AudioEditor")->RegisterAssetActions();
FModuleManager::Get().LoadModule("StringTableEditor");
FModuleManager::Get().LoadModule(TEXT("VREditor"));
FModuleManager::Get().LoadModule(TEXT("EnvironmentQueryEditor"));
FModuleManager::Get().LoadModule(TEXT("IntroTutorials"));
FModuleManager::Get().LoadModule(TEXT("Blutility"));
FModuleManager::Get().LoadModule(TEXT("Overlay"));
FModuleManager::Get().LoadModule(TEXT("MediaAssets"));
FModuleManager::Get().LoadModule(TEXT("ClothingSystemRuntime"));
//FModuleManager::Get().LoadModule(TEXT("ClothingSystemEditor"));
FModuleManager::Get().LoadModule(TEXT("PacketHandler"));
return bSuccess;
}
该部分显式加载了一些核心模块.
裁剪估计会对核心功能产生影响.
LoadStartupModules()
bool FEngineLoop::LoadStartupModules()
{
// 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;
}
// Load modules that are configured to load in the default phase
if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::Default) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::Default))
{
return false;
}
// 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;
}
在引擎启动过程中, 相关的模块通过LoadingPhase来控制加载时机:
Name | Description |
---|---|
EarliestPossible | As soon as possible - in other words, uplugin files are loadable from a pak file (as well as right after PlatformFile is set up in case pak files aren't used) Used for plugins needed to read files (compression formats, etc) |
PostConfigInit | Loaded before the engine is fully initialized, immediately after the config system has been initialized. |
PostSplashScreen | The first screen to be rendered after system splash screen |
PreEarlyLoadingScreen | Loaded before coreUObject for setting up manual loading screens, used for our chunk patching system |
PreLoadingScreen | Loaded before the engine is fully initialized for modules that need to hook into the loading screen before it triggers |
PreDefault | Right before the default phase |
Default | Loaded at the default loading point during startup (during engine init, after game modules are loaded.) |
PostDefault | Right after the default phase |
PostEngineInit | After the engine has been initialized |
None | Do not automatically load this module |
Max |
通过日志打点可以观察到, PreDefault|Default|PostDefault
这三种类型的模块在PreInit()阶段就已经加载完毕了, 而PostEngineInit
类型的模块将在EditorInit()阶段加载, 除去None以外的LoadingPhase都是在编辑器加载完成前执行的, 在当前的编辑器中, 不同时机的加载耗时如下(引擎启动时间波动较大, 所以引擎加载时间此处做定性分析而非定量分析):
Name | 优化前耗时(s) | 优化后耗时(s) | 差值(s) |
---|---|---|---|
PostConfigInit | 0.449 | 0.373 | 0.08 |
PreLoadingScreen | 0.319 | 0.272 | 0.05 |
PreDefault | 2.131 | 4.662 | -2.53 |
Default | 25.232 | 17.721 | 7.51 |
PostDefault | 0.125 | 0.120 | 0.00 |
PostEngineInit | 8.075 | 5.813 | 2.26 |
本次引擎在PreInit()阶段耗时为68.757秒, LoadStartupModules()为27.8秒, 占40.43%, 通过观察不难发现, 一些模块/插件为仅移动平台下加载的模块, 或美术人员对模型进行优化耗时的模块, 或引擎原生但项目中未使用的模块, 可以对此处进行一些策略配置, 来减少引擎启动耗时.
EditorInit()
int32 EditorInit( IEngineLoop& EngineLoop )
{
// 加载一些模块, 耗时8.06s
int32 ErrorLevel = EngineLoop.Init();
// 发送编辑器已启动事件: 耗时0.0s
FEngineAnalytics::GetProvider().RecordEvent(TEXT("Editor.ProgramStarted"), EventAttributes);
/** The public interface for the unreal editor misc singleton. */
// 注册编辑器事件
// 注册资源编辑器/材质编辑器/寻路网格/曲线命令
// 记录操作
// 加载一些编辑器模块,详见(FEditorModeRegister::Initialize())
// 根据配置加载版本控制模块(P4 or Git)
// 加载地图
// 初始化一些构建配置
// 绑定一些日志事件监听
// 耗时0.72s
FUnrealEdMisc::Get().OnInit();
// 从.ini文件中读取默认路径, 用来加载与保存: 耗时0.0s
FEditorDirectories::Get().LoadLastDirectories();
// 设置actor folders单例
FActorFolders::Init();
// =================== CORE EDITOR INIT FINISHED ===================
// 从命令行判断是否是immersive mode以设置编辑器
// 显示编辑器窗口
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
MainFrameModule.CreateDefaultMainFrame( bStartImmersive, bStartPIE );
// 判断是否是VRMode, 是则加载对应编辑器: 耗时0.0s
VREditorModule.EnableVREditor( true );
// 提示是否需要更新游戏工程文件: 耗时0.0s
FGameProjectGenerationModule::Get().CheckForOutOfDateGameProjectFile();
FGameProjectGenerationModule::Get().CheckAndWarnProjectFilenameValid();
// 在stat中打点
// 加载HierarchicalLODOutliner模块
return 0;
}
EngineLoop.Init()
int32 FEngineLoop::Init()
{
InitTime();
GEngine->Init(this);
if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostEngineInit) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostEngineInit))
{
GIsRequestingExit = true;
return 1;
}
GEngine->Start();
// Ready to measure thread heartbeat
FThreadHeartBeat::Get().Start();
}
FUnrealEdMisc::Get().OnInit()
该部分耗时在0.7s左右.
FEditorModeRegister::Initialize()
不同版本加载模块对比:
序号 | 4.18 | 4.27 | 5.0 |
---|---|---|---|
1 | PlacementMode | PlacementMode | |
2 | BspMode | ||
3 | TextureAlignMode | ||
4 | GeometryMode | ||
5 | ActorPickerMode | ActorPickerMode | ActorPickerMode |
6 | SceneDepthPickerMode | SceneDepthPickerMode | SceneDepthPickerMode |
7 | MeshPaintMode | MeshPaintMode | |
8 | LandscapeEditor | LandscapeEditor | LandscapeEditor |
9 | FoliageEdit | FoliageEdit | FoliageEdit |
10 | VirtualTexturingEditor | VirtualTexturingEditor |
4.18(我们项目编辑器也是这样)中有一行注释:
//@TODO: ROCKET: These are probably good plugin candidates, that shouldn't have to be force-loaded here but discovery loaded somehow
说明这里加载的Module可以在此后的某个时间段按需加载, 从4.25版本开始BspMode, TextureAlignMode, GeometryMode不在此处加载.
MainFrameModule.CreateDefaultMainFrame
该部分耗时在14s~15s左右, 主要负责创建编辑器窗口:
void FMainFrameModule::CreateDefaultMainFrame( const bool bStartImmersive, const bool bStartPIE )
{
//...
RootWindow->SetContent(MainFrameContent.ToSharedRef());
// Initialize the main frame window
MainFrameHandler->OnMainFrameGenerated( MainTab, RootWindow );
// Show the window!
MainFrameHandler->ShowMainFrameWindow( RootWindow, bStartImmersive, bStartPIE );
MainFrameCreationFinishedEvent.Broadcast(RootWindow, ShouldShowProjectDialogAtStartup());
}