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