UE4啟動過程

這個文件夾中有各個平台的入口函數,最終都會調用Launch.cpp中的函數。
LaunchWindows.cpp
int32 WINAPI WinMain(_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ char* pCmdLine, _In_ int32 nCmdShow)
{
//這里進入引擎
int32 Result = LaunchWindowsStartup(hInInstance, hPrevInstance, pCmdLine, nCmdShow, nullptr);
LaunchWindowsShutdown();
return Result;
}
LaunchWindowsStartup調用Launch.cpp的GuardedMain函數進行引擎的初始化和循環等。
GuardedMain函數的執行
- 引擎循環的預先加載
int32 EnginePreInit(const TCHAR* CmdLine) - 引擎循環的初始化
int32 EngineInit()或int32 EditorInit(IEngineLoop& EngineLoop) - Tick引擎循環
void EngineTick(void) - 關閉引擎
void EngineExit(void)
這些函數都定義在Launch.cpp中,只有做了更多操作的int32 EditorInit(IEngineLoop& EngineLoop)定義在Engine\Source\Editor\UnrealEd\Private\UnrealEdGlobals.cpp中。

FEngineLoop GEngineLoop是當前Launch.cpp下的一個全局變量,定義在LaunchEngineLoop.h中。
注意只是把GEngineLoop傳入到不同的Init函數里面,一個是EngineInit,一個是EditorInit,EditorInit則定義在不同的文件中。
class FEngineLoop
#if WITH_ENGINE
:public IEngineLoop
#endif
{
public:
//main loop的預先初始化,並且從main()的標准ArgC/ArgV生成命令行
int32 PreInit(int32 ArgC, TCHAR* ArgV[], const TCHAR* AdditionalCommandline = nullptr);
//預先初始化main loop,分析命令行,設置GIsEditor
int32 PreInit(const TCHAR* CmdLine);
//PreInit的第一部分
int32 PreInitPreStartupScreen(const TCHAR* CmdLine);
//PreInit的第二部分
int32 PreInitPostStartupScreen(const TCHAR* CmdLine);
//在Init之前加載所有需要的模塊
void LoadPreInitModules();
//加載核心模塊
bool LoadCoreModules();
//清除PreInit context
void CleanupPreInitContext();
#if WITH_ENGINE
//加載所有需要的核心模塊在一開始
bool LoadStartupCoreModules();
//加載所有的模塊在一開始
bool LoadStartupModules();
//初始化主循環(剩余的初始化)
virtual int32 Init() override;
//從命令行初始化timing選項
void InitTime();
//執行關閉函數
void Exit();
//引擎是否應當在一個idle模式操作,不使用CPU或者GPU時間
bool ShouldUseIdleMode() const;
//促進主循環
virtual void Tick() override;
//通過刪除對任何待清理對象的引用來刪除這些對象
virtual void ClearPendingCleanupObjects() override;
#endif
//RHI后初始化
static void PostInitRHI();
//預初始化HMD設備(如果需要的話)
static void PreInitHMDDevice();
//初始化應用
static bool AppInit();
//准備應用的關閉
static void AppPreExit();
//關閉應用
static void AppExit();
private:
//工具函數處理Slate操作
void ProcessLocalPlayerSlateOperations() const;
protected:
//以毫秒為單位保存動態擴展的幀時間數組(如果FApp::IsBenchmarking()被設置)
TArray<float> FrameTimes;
//保存勾選引擎所花的時間
double TotalTickTime;
//保存引擎應當tick的最大數量的秒數
double MaxTickTime;
//保存需要渲染的最大數量的幀數在benchmarking模式下
uint64 MaxFrameCounter;
//保存上一幀cycles的數量
uint32 LastFrameCycles;
#if WITH_ENGINE
//保存需要清理的對象,當渲染線程完成上一幀
FPendingCleanupObjects* PendingCleanupObjects;
#endif
private:
#if WITH_ENGINE
//保存engine service
FEngineService* EngineService;
//保存應用session服務
TSharedPtr<ISessionService> SessionService;
#endif
//這個用來保存預先初始化的時候,一些模塊的加載返回過來的狀態的bool變量,或者是否開啟一些模塊的bool變量
FPreInitContext PreInitContext;
};
WITH_ENGINE,對於那些包含引擎項目的所有構建才會開啟,只有在構建獨立APP鏈接引擎的核心的時候才會關閉。
打包好后的項目不包含這個宏。
GEngineLoop的PreInit過程
GEngineLoop的函數定義在LaunchEngineLoop.cpp中,先調用PreInitPreStartupScreen函數,再調用PreInitPostStartupScreen函數。
PreInitPreStartupScreen函數的簡化調用過程
int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine)
{
//暫時不知道做啥的一個函數
FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::StartOfEnginePreInit);
//GLog單例的惰性初始化
GLog->SetCurrentThreadAsMasterThread();
//設置TLS的緩存在當前線程
FMemory::SetupTLSCachesOnCurrentThread();
//解析命令行,設置UTF8的輸出
if (FParse::Param(CmdLine, TEXT("UTF8Output")))
{
FPlatformMisc::SetUTF8Output();
}
//轉入可執行目錄,設置當前的工作目錄為基目錄
FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir();
//初始化trace
FTraceAuxiliary::Initialize(CmdLine);
//關閉/開啟LLM基於命令行
LLM(FLowLevelMemTracker::Get().ProcessCommandLine(CmdLine));
//創建stats malloc分析代理
if (FStatsMallocProfilerProxy::HasMemoryProfilerToken())
{
if (PLATFORM_USES_FIXED_GMalloc_CLASS)
{
UE_LOG(LogMemory, Fatal, TEXT("Cannot do malloc profiling with PLATFORM_USES_FIXED_GMalloc_CLASS."));
}
//假設這里沒有並行
GMalloc = FStatsMallocProfilerProxy::Get();
}
//初始化log命令控制台去避免靜態初始化問題,當從命令行登錄的時候
GScopedLogConsole = TUniquePtr<FOutputDeviceConsole>(FPlatformApplicationMisc::CreateConsoleOutputDevice());
//總是開啟backlog,那么我們可以得到所有的信息,我們將關閉和清理它在游戲中,
//一旦我們決定是否GIsEditor == false
GLog->EnableBacklog(true);
//初始化std out設備盡可能早,如果在命令行中要求
if (FParse::Param(FCommandLine::Get(), TEXT("stdout")))
{
InitializeStdOutDevice();
}
//設置當前的工作目錄為基目錄
FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir();
//初始化輸出設備
GError = FPlatformApplicationMisc::GetErrorOutputDevice();
GWarn = FPlatformApplicationMisc::GetFeedbackContext();
//開始預先初始化文本本地化
BeginPreInitTextLocalization();
//預先初始化着色器庫
FShaderCodeLibrary::PreInit();
//允許命令行去重寫平台文件單例
bool bFileOverrideFound = false;
{
SCOPED_BOOT_TIMING("LaunchCheckForFileOverride");
if (LaunchCheckForFileOverride(CmdLine, bFileOverrideFound) == false)
{
// if it failed, we cannot continue
return 1;
}
}
//初始化文件管理器
{
SCOPED_BOOT_TIMING("IFileManager::Get().ProcessCommandLineOptions");
IFileManager::Get().ProcessCommandLineOptions();
}
//初始化異步IO
{
SCOPED_BOOT_TIMING("InitializeNewAsyncIO");
FPlatformFileManager::Get().InitializeNewAsyncIO();
}
#if !UE_BUILD_SHIPPING
// Benchmarking.
FApp::SetBenchmarking(FParse::Param(FCommandLine::Get(), TEXT("BENCHMARK")));
#else
FApp::SetBenchmarking(false);
#endif
//初始化task graph子系統,使用潛在的多線程
FTaskGraphInterface::Startup(FPlatformMisc::NumberOfCores());
FTaskGraphInterface::Get().AttachToThread(ENamedThreads::GameThread);
//加載所有需要工作的核心模塊(需要在InitializeRenderingCVarsCaching之前加載)
if (!LoadCoreModules())
{
UE_LOG(LogInit, Error, TEXT("Failed to load Core modules."));
return 1;
}
//從Ini讀取配置
if (bDumpEarlyConfigReads)
{
RecordConfigReadsFromIni();
}
//從Pak讀取文件
if (bDumpEarlyPakFileReads)
{
RecordFileReadsFromPaks();
}
//開始記錄配置補丁的CVar更改
RecordApplyCVarSettingsFromIni();
//初始化渲染CVarsCaching
InitializeRenderingCVarsCaching();
//得到log輸出設備的指針
GLogConsole = GScopedLogConsole.Get();
//加載預初始化模塊
LoadPreInitModules();
//開始應用
{
SCOPED_BOOT_TIMING("AppInit");
if (!AppInit())
{
return 1;
}
}
//初始化系統設置在任何人嘗試使用它的時候
GSystemSettings.Initialize(bHasEditorToken);
//從存儲在INIT文件中的控制台變量應用渲染設置
ApplyCVarSettingsFromIni(TEXT("/Script/Engine.RendererSettings"), *GEngineIni, ECVF_SetByProjectSetting);
ApplyCVarSettingsFromIni(TEXT("/Script/Engine.RendererOverrideSettings"), *GEngineIni, ECVF_SetByProjectSetting);
ApplyCVarSettingsFromIni(TEXT("/Script/Engine.StreamingSettings"), *GEngineIni, ECVF_SetByProjectSetting);
ApplyCVarSettingsFromIni(TEXT("/Script/Engine.GarbageCollectionSettings"), *GEngineIni, ECVF_SetByProjectSetting);
//預加載分辨率設置
UGameUserSettings::PreloadResolutionSettings();
//初始化scalability系統和默認值
Scalability::InitScalabilitySystem();
//設置設備配置文件中已經設置的控制台變量
//將加載並應用游戲用戶設置
UDeviceProfileManager::InitializeCVarsForActiveDeviceProfile();
//盡早避免昂貴的子系統重新初始化
//在SystemSettings.ini文件加載,那么我們可以得到正確的狀態
Scalability::LoadState((bHasEditorToken && !GEditorSettingsIni.IsEmpty()) ? GEditorSettingsIni : GGameUserSettingsIni);
//使用渲染線程
if (FPlatformMisc::UseRenderThread())
{
GUseThreadedRendering = true;
}
//從INIT加載控制台變量
FConfigCacheIni::LoadConsoleVariablesFromINI();
//SystemSettings加載的時候,平台相關的進行初始化
FPlatformMisc::PlatformInit();
FPlatformApplicationMisc::Init();
FPlatformMemory::Init();
//允許平台去開啟它希望使用的特性
IPlatformFeaturesModule::Get();
//初始化物理引擎系統
InitGamePhys();
//初始化引擎文本本地化
InitEngineTextLocalization();
//Slate初始化高DPI
FSlateApplication::InitHighDPI(bForceEnableHighDPI);
//初始化平台應用
FSlateApplication::Create();
//初始化所有UniformBuffer結構體
FShaderParametersMetadata::InitializeAllUniformBufferStructs();
//初始化RHI
RHIInit(bHasEditorToken);
//基於引擎配置初始化全局變量
RenderUtilsInit();
//將打開材質着色代碼存儲庫,如果項目打包了它
FShaderCodeLibrary::InitForRuntime(GMaxRHIShaderPlatform);
//着色器相關的進行初始化
GDistanceFieldAsyncQueue = new FDistanceFieldAsyncQueue();
GShaderCompilerStats = new FShaderCompilerStats();
GShaderCompilingManager = new FShaderCompilingManager();
InitializeShaderHashCache();
//在主線程Cache渲染模塊,那么我們之后可以從渲染模塊安全地取回它
GetRendererModule();
//在加載任何着色器之前初始化着色器類型
InitializeShaderTypes();
//加載全局着色器
CompileGlobalShaderMap(false);
//SLATE模塊的加載
...
//用之前的PreInitContext保存那些bool變量
PreInitContext.bDumpEarlyConfigReads = bDumpEarlyConfigReads;
PreInitContext.bDumpEarlyPakFileReads = bDumpEarlyPakFileReads;
PreInitContext.bForceQuitAfterEarlyReads = bForceQuitAfterEarlyReads;
PreInitContext.bWithConfigPatching = bWithConfigPatching;
PreInitContext.bHasEditorToken = bHasEditorToken;
PreInit初始化過程總結
- GLog設置當前創建它的線程為它的主線程。
- 如果是Windows平台,則為Ctrl-C注冊一個處理程序。
- FMemory設置當前線程的TLS Cache。
- 分析命令行參數是否有UTF8Output來調用
FPlatformMisc::SetUTF8Output()。 - 設置當前的工作目錄為根目錄。
- 如果是發行版本的游戲,則重寫命令行,防止被潛在的漏洞利用。
- 初始化trace。//1503
