首页
社区
课程
招聘
[原创] UE4的启动流程分析
发表于: 3天前 636

[原创] UE4的启动流程分析

3天前
636

本文基于Unreal Engine版本为:

  • 当前分支:4.27
  • 最新提交:3abfe77d0b Branch snapshot for CL 19160214

本文主要是理清楚在 Ue4 启动阶段做了些什么工作,方便我们后续拆分逻辑进行分析。然后本文可能会略显啰嗦,虽然可能一些知识点用概括性话总结一下就可以了,但是本人还是比较喜欢有代码详细情况分析(让人更安心一些),所以整体还是基于源码片段进行分析辅助理解的。

参考文章:

  • 160K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2U0L8X3u0D9L8$3N6K6i4K6u0W2j5$3!0E0i4K6u0r3N6r3!0E0j5i4c8G2i4K6u0V1K9r3q4Z5j5g2)9J5c8Y4m8Q4x3V1j5I4y4K6b7@1x3K6R3&6x3W2)9J5k6h3S2@1L8h3H3`.

架构

整体架构

图片描述

完整初始化流程

平台入口层

每个平台进入不太一样,主要都是针对于各个平台特性的一些处理。这里我们暂时不深入了解。

图片描述

GuardedMain(引擎入口)

大多数平台都是通过 GuardedMain 进行启动的。也就是我们的引擎入口函数。

我们主要分析在 GuardedMain 阶段执行了哪些函数.

根据函数分析我们可以知道最主要的流程就是:PreInit,Init,Tick循环,Exit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
 * Static guarded main function. Rolled into own function so we can have error handling for debug/ release builds depending
 * on whether a debugger is attached or not.
 */
int32 GuardedMain( const TCHAR* CmdLine )
{
#if !(UE_BUILD_SHIPPING)
    if (FParse::Param(CmdLine, TEXT("waitforattach")))
    {
        while (!FPlatformMisc::IsDebuggerPresent());
        UE_DEBUG_BREAK();
    }
#endif
 
    BootTimingPoint("DefaultMain");
 
    // Super early init code. DO NOT MOVE THIS ANYWHERE ELSE!
    FCoreDelegates::GetPreMainInitDelegate().Broadcast();
 
    // make sure GEngineLoop::Exit() is always called.
    struct EngineLoopCleanupGuard
    {
        ~EngineLoopCleanupGuard()
        {
            // Don't shut down the engine on scope exit when we are running embedded
            // because the outer application will take care of that.
            if (!GUELibraryOverrideSettings.bIsEmbedded)
            {
                EngineExit();
            }
        }
    } CleanupGuard;
 
    // Set up minidump filename. We cannot do this directly inside main as we use an FString that requires
    // destruction and main uses SEH.
    // These names will be updated as soon as the Filemanager is set up so we can write to the log file.
    // That will also use the user folder for installed builds so we don't write into program files or whatever.
#if PLATFORM_WINDOWS
    FCString::Strcpy(MiniDumpFilenameW, *FString::Printf(TEXT("unreal-v%i-%s.dmp"), FEngineVersion::Current().GetChangelist(), *FDateTime::Now().ToString()));
 
    GIsConsoleExecutable = (GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) == FILE_TYPE_CHAR);
#endif
 
    int32 ErrorLevel = EnginePreInit( CmdLine );
 
    // exit if PreInit failed.
    if ( ErrorLevel != 0 || IsEngineExitRequested() )
    {
        return ErrorLevel;
    }
 
    {
        FScopedSlowTask SlowTask(100, NSLOCTEXT("EngineInit", "EngineInit_Loading", "Loading..."));
 
        // EnginePreInit leaves 20% unused in its slow task.
        // Here we consume 80% immediately so that the percentage value on the splash screen doesn't change from one slow task to the next.
        // (Note, we can't include the call to EnginePreInit in this ScopedSlowTask, because the engine isn't fully initialized at that point)
        SlowTask.EnterProgressFrame(80);
 
        SlowTask.EnterProgressFrame(20);
 
#if WITH_EDITOR
        if (GIsEditor)
        {
            ErrorLevel = EditorInit(GEngineLoop);
        }
        else
#endif
        {
            ErrorLevel = EngineInit();
        }
    }
 
    double EngineInitializationTime = FPlatformTime::Seconds() - GStartTime;
    UE_LOG(LogLoad, Log, TEXT("(Engine Initialization) Total time: %.2f seconds"), EngineInitializationTime);
 
#if WITH_EDITOR
    UE_LOG(LogLoad, Log, TEXT("(Engine Initialization) Total Blueprint compile time: %.2f seconds"), BlueprintCompileAndLoadTimerData.GetTime());
#endif
 
    ACCUM_LOADTIME(TEXT("EngineInitialization"), EngineInitializationTime);
 
    BootTimingPoint("Tick loop starting");
    DumpBootTiming();
 
    // Don't tick if we're running an embedded engine - we rely on the outer
    // application ticking us instead.
    if (!GUELibraryOverrideSettings.bIsEmbedded)
    {
        while( !IsEngineExitRequested() )
        {
            EngineTick();
        }
    }
 
    TRACE_BOOKMARK(TEXT("Tick loop end"));
 
#if WITH_EDITOR
    if( GIsEditor )
    {
        EditorExit();
    }
#endif
    return ErrorLevel;
}

调用链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
GuardedMain (Launch.cpp:93)
├─ 调试器等待 (96-100行)
│  └─ FParse::Param(CmdLine, TEXT("waitforattach"))
│     └─ while (!FPlatformMisc::IsDebuggerPresent())
├─ PreMainInit委托广播 (106行)
│  └─ FCoreDelegates::GetPreMainInitDelegate().Broadcast()
├─ 创建清理守卫 (109-120行)
│  └─ EngineLoopCleanupGuard (RAII)
│     └─ ~EngineLoopCleanupGuard() → EngineExit()
├─ EnginePreInit (132行)
│  └─ GEngineLoop.PreInit (LaunchEngineLoop.cpp:3647)
│     │
│     ├─ PreInitPreStartupScreen (1434行)
│     │  ├─ GLog->SetCurrentThreadAsMasterThread() (1446行)
│     │  ├─ FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir() (1476行)
│     │  ├─ FCommandLine::Set(CmdLine) (1480行)
│     │  ├─ FTraceAuxiliary::Initialize(CmdLine) (1501行)
│     │  ├─ FLowLevelMemTracker::Get().ProcessCommandLine() (1506行)
│     │  ├─ LaunchSetGameName() (1555行)
│     │  ├─ FPlatformApplicationMisc::GetErrorOutputDevice() (1660行)
│     │  ├─ IFileManager::Get().ProcessCommandLineOptions() (1723行)
│     │  ├─ FParse::Token() 解析命令行Token (1761行)
│     │  ├─ IProjectManager::Get().LoadProjectFile() (2020行)
│     │  ├─ FTaskGraphInterface::Startup() (2059行)
│     │  └─ LoadCoreModules() (2076行)
│     │     └─ FModuleManager::Get().LoadModule(TEXT("CoreUObject")) (3674行)
│     │
│     └─ PreInitPostStartupScreen (2819行)
│        ├─ GetMoviePlayer()->SetupLoadingScreenFromIni() (2863行)
│        ├─ IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PreEarlyLoadingScreen) (2869行)
│        ├─ GetMoviePlayer()->PlayEarlyStartupMovies() (2891行)
│        └─ FPreLoadScreenManager::Get()->PlayFirstPreLoadScreen() (2970行)
├─ 检查PreInit结果 (135-138行)
│  └─ if (ErrorLevel != 0 || IsEngineExitRequested())
├─ EngineInit (158行) [游戏模式]
│  └─ GEngineLoop.Init() (LaunchEngineLoop.cpp:3946)
│     │
│     ├─ 创建GEngine对象 (3958-3988行)
│     │  ├─ GConfig->GetString(TEXT("/Script/Engine.Engine"), TEXT("GameEngine"), ...) (3965行)
│     │  └─ NewObject<UEngine>(GetTransientPackage(), EngineClass) (3971行)
│     │
│     ├─ GEngine->ParseCommandline() (4003行)
│     │
│     ├─ InitTime() (4010行)
│     │
│     ├─ GEngine->Init(this) (4017行)
│     │  └─ UGameEngine::Init() / UUnrealEdEngine::Init()
│     │
│     ├─ FCoreDelegates::OnPostEngineInit.Broadcast() (4021行)
│     │
│     ├─ IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostEngineInit) (4045行)
│     │
│     ├─ SessionService->Start() (4035行)
│     │
│     ├─ EngineService = new FEngineService() (4039行)
│     │
│     ├─ GEngine->Start() (4054行)
│     │  └─ UGameEngine::Start() / UUnrealEdEngine::Start()
│     │
│     └─ GetMoviePlayer()->WaitForMovieToFinish() (4068行)
└─ EngineTick循环 (178-182行)
   └─ while (!IsEngineExitRequested())
      └─ EngineTick() (180行)
         └─ GEngineLoop.Tick() (每帧调用)
            └─ GEngine->Tick() (引擎主循环)

调试器等待

主要是方便开发者调试,也就是命令行如果包含了 waitforattach 关键字,那么就会等待调试器附加后再继续执行。

1
2
3
4
5
if (FParse::Param(CmdLine, TEXT("waitforattach")))
{
    while (!FPlatformMisc::IsDebuggerPresent());
    UE_DEBUG_BREAK();
}

PreMainInit (委托广播)

广播 preMaininit 委托,允许引擎在初始化前执行代码。

在引擎主初始化之前执行,此时基础系统(日志、文件系统等)可能尚未完全初始化,允许平台特定代码或者特殊模块在启动前进行必要的初始化

也就是这里会去触发所有已经注册回调函数。

这里我们可以了解一下 UE 中模块的概念。

1
FCoreDelegates::GetPreMainInitDelegate().Broadcast();

什么是模块?

在 UE 中,Module 是一个独立的代码单元,通常对应一个 DLL或者静态库。每个模块都有自己的生命周期。

这是模块的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
 * Interface class that all module implementations should derive from.  This is used to initialize
 * a module after it's been loaded, and also to clean it up before the module is unloaded.
 */
class IModuleInterface
{
 
public:
 
    /**
     * Note: Even though this is an interface class we need a virtual destructor here because modules are deleted via a pointer to this interface                  
     */
    virtual ~IModuleInterface()
    {
    }
 
    /**
     * Called right after the module DLL has been loaded and the module object has been created
     * Load dependent modules here, and they will be guaranteed to be available during ShutdownModule. ie:
     *
     * FModuleManager::Get().LoadModuleChecked(TEXT("HTTP"));
     */
    virtual void StartupModule()
    {
    }
 
    /**
     * Called before the module has been unloaded
     */
    virtual void PreUnloadCallback()
    {
    }
 
    /**
     * Called after the module has been reloaded
     */
    virtual void PostLoadCallback()
    {
    }
 
    /**
     * Called before the module is unloaded, right before the module object is destroyed.
     * During normal shutdown, this is called in reverse order that modules finish StartupModule().
     * This means that, as long as a module references dependent modules in it's StartupModule(), it
     * can safely reference those dependencies in ShutdownModule() as well.
     */
    virtual void ShutdownModule()
    {
    }
 
    /**
     * Override this to set whether your module is allowed to be unloaded on the fly
     *
     * @return  Whether the module supports shutdown separate from the rest of the engine.
     */
    virtual bool SupportsDynamicReloading()
    {
        return true;
    }
 
    /**
     * Override this to set whether your module would like cleanup on application shutdown
     *
     * @return  Whether the module supports shutdown on application exit
     */
    virtual bool SupportsAutomaticShutdown()
    {
        return true;
    }
 
    /**
     * Returns true if this module hosts gameplay code
     *
     * @return True for "gameplay modules", or false for engine code modules, plugins, etc.
     */
    virtual bool IsGameModule() const
    {
        return false;
    }
};

模块加载

  • PreInitPreStartupScreen阶段:加载CoreUObject等核心模块
// Load Core modules required for everything else to work (needs to be loaded before InitializeRenderingCVarsCaching)
{
    SCOPED_BOOT_TIMING("LoadCoreModules");
    if (!LoadCoreModules())
    {
        UE_LOG(LogInit, Error, TEXT("Failed to load Core modules."));
        return 1;
    }
}
  • PreInitPostStartupScreen阶段:加载PreEarlyLoadingScreen阶段的模块
  • Init阶段:加载PostEngineInit阶段的模块
    图片描述

模块的类型

  • 引擎模块(Engine Modules)
    • 位置:Engine/Source/Runtime/ 或 Engine/Source/Editor/
    • 示例:Core、CoreUObject、Engine、Renderer
    • 特点:引擎核心功能
  • 游戏模块(Game Modules)
    • 位置:项目目录下的 Source/
    • 示例:项目自己的模块
    • 特点:游戏特定代码
  • 插件模块(Plugin Modules)
    • 位置:Plugins/ 目录
    • 示例:第三方插件
    • 特点:可选的扩展功能

模块注册方式

也就是不同模块,可以将自己的回调函数注册到 PreMainInit 委托上.

然后在 GuardedMain 调用Broadcast的时候就会依次去执行。比如这里就是windows平台崩溃报告系统中的一个列子。

  • Addraw
1
FCoreDelegates::GetPreMainInitDelegate().AddRaw(this, &FCrashReportingThread::RegisterUnhandledExceptionHandler);
  • addStatic
1
2
3
4
5
6
#if !CSV_PROFILER_USE_CUSTOM_FRAME_TIMINGS
    FCoreDelegates::OnBeginFrame.AddStatic(CsvProfilerBeginFrame);
    FCoreDelegates::OnEndFrame.AddStatic(CsvProfilerEndFrame);
    FCoreDelegates::OnBeginFrameRT.AddStatic(CsvProfilerBeginFrameRT);
    FCoreDelegates::OnEndFrameRT.AddStatic(CsvProfilerEndFrameRT);
#endif
  • AddLambda - 绑定Lambda表达式
1
2
3
4
FCoreDelegates::GetOutOfMemoryDelegate().AddLambda([this]()
{
    PanicDump(TYPE_Malloc, nullptr, nullptr);
});
  • AddUFunction - 绑定UObject的UFUNCTION
1
ExternalNotifyHandlers.FindOrAdd(NotifyEventName).AddUFunction(ExternalHandlerObject, NotifyEventName);

创建清理守卫

确保退出的时候能够调用 EngineExit (非嵌入式模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
// make sure GEngineLoop::Exit() is always called.
struct EngineLoopCleanupGuard
{
    ~EngineLoopCleanupGuard()
    {
        // Don't shut down the engine on scope exit when we are running embedded
        // because the outer application will take care of that.
        if (!GUELibraryOverrideSettings.bIsEmbedded)
        {
            EngineExit();
        }
    }
} CleanupGuard;

PreInit (引擎预初始化)

1
2
3
4
5
6
int32 ErrorLevel = EnginePreInit( CmdLine );
// exit if PreInit failed.
if ( ErrorLevel != 0 || IsEngineExitRequested() )
{
    return ErrorLevel;
}

EngineInit/EditorInit (引擎初始化)

根据情况看进入的是编辑器模式还是引擎模式

1
2
3
4
5
6
7
8
9
10
11
12
SlowTask.EnterProgressFrame(20);
 
#if WITH_EDITOR
if (GIsEditor)
{
    ErrorLevel = EditorInit(GEngineLoop);
}
else
#endif
{
    ErrorLevel = EngineInit();
}

Tick 循环

主循环,持续调用 EngineTick 直到请求退出

1
2
3
4
5
6
7
if (!GUELibraryOverrideSettings.bIsEmbedded)
{
    while( !IsEngineExitRequested() )
    {
        EngineTick();
    }
}

循环层

我们可以发现在 GuardedMain 调用的函数本质是调用的 FEngineLoop 的函数

接下来我们重点分析每个函数具体做了些什么内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
 * PreInits the engine loop
 */
int32 EnginePreInit( const TCHAR* CmdLine )
{
    int32 ErrorLevel = GEngineLoop.PreInit( CmdLine );
 
    return( ErrorLevel );
}
 
/**
 * Inits the engine loop
 */
int32 EngineInit()
{
    int32 ErrorLevel = GEngineLoop.Init();
 
    return( ErrorLevel );
}
/**
 * Ticks the engine loop
 */
LAUNCH_API void EngineTick( void )
{
    GEngineLoop.Tick();
}
/**
 * Shuts down the engine
 */
LAUNCH_API void EngineExit( void )
{
    // Make sure this is set
    RequestEngineExit(TEXT("EngineExit() was called"));
 
    GEngineLoop.Exit();
}
extern FEngineLoop GEngineLoop;

数据结构

  • PreInitContext: 保存 PreInit 阶段的上下文信息
  • EngineService: 引擎服务,用于远程调试等
  • SessionService: 会话服务,用于多实例通信
  • PendingCleanupObjects: 待清理对象列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/**
 * Implements the main engine loop.
 */
class FEngineLoop
#if WITH_ENGINE
    : public IEngineLoop
#endif
{
public:
 
    /** Default constructor. */
    FEngineLoop();
 
    virtual ~FEngineLoop() { }
 
public:
 
    /**
     * Pre-Initialize the main loop, and generates the commandline from standard ArgC/ArgV from main().
     *
     * @param ArgC The number of strings in ArgV.
     * @param ArgV The command line parameters (ArgV[0] is expected to be the executable name).
     * @param AdditionalCommandLine Optional string to append to the command line (after ArgV is put together).
     * @return Returns the error level, 0 if successful and > 0 if there were errors.
     */
    int32 PreInit(int32 ArgC, TCHAR* ArgV[], const TCHAR* AdditionalCommandline = nullptr);
 
    /**
     * Pre-Initialize the main loop - parse command line, sets up GIsEditor, etc.
     *
     * @param CmdLine The command line.
     * @return The error level; 0 if successful, > 0 if there were errors.
     */
    int32 PreInit(const TCHAR* CmdLine);
     
    /** First part of PreInit. */
    int32 PreInitPreStartupScreen(const TCHAR* CmdLine);
 
    /** Second part of PreInit. */
    int32 PreInitPostStartupScreen(const TCHAR* CmdLine);
 
    /** Load all modules needed before Init. */
    void LoadPreInitModules();
 
    /** Load core modules. */
    bool LoadCoreModules();
 
    /** Clean up PreInit context. */
    void CleanupPreInitContext();
 
#if WITH_ENGINE
     
    /** Load all core modules needed at startup time. */
    bool LoadStartupCoreModules();
     
    /** Load all modules needed at startup time. */
    bool LoadStartupModules();
 
    /**
     * Initialize the main loop (the rest of the initialization).
     *
     * @return The error level; 0 if successful, > 0 if there were errors.
     */
    virtual int32 Init() override;
 
    /** Initialize the timing options from the command line. */
    void InitTime();
 
    /** Performs shut down. */
    void Exit();
 
    /** Whether the engine should operate in an idle mode that uses no CPU or GPU time. */
    bool ShouldUseIdleMode() const;
 
    /** Advances the main loop. */
    virtual void Tick() override;
 
    /** Removes references to any objects pending cleanup by deleting them. */
    virtual void ClearPendingCleanupObjects() override;
 
#endif // WITH_ENGINE
 
    /** RHI post-init initialization */
    static void PostInitRHI();
 
    /** Pre-init HMD device (if necessary). */
    static void PreInitHMDDevice();
 
public:
 
    /** Initializes the application. */
    static bool AppInit();
 
    /**
     * Prepares the application for shutdown.
     *
     * This function is called from within guarded exit code, only during non-error exits.
     */
    static void AppPreExit();
 
    /**
     * Shuts down the application.
     *
     * This function called outside guarded exit code, during all exits (including error exits).
     */
    static void AppExit();
 
private:
 
    /** Utility function that processes Slate operations. */
    void ProcessLocalPlayerSlateOperations() const;
 
protected:
 
    /** Holds a dynamically expanding array of frame times in milliseconds (if FApp::IsBenchmarking() is set). */
    TArray<float> FrameTimes;
 
    /** Holds the total time spent ticking engine. */
    double TotalTickTime;
     
    /** Holds the maximum number of seconds engine should be ticked. */
    double MaxTickTime;
     
    /** Holds the maximum number of frames to render in benchmarking mode. */
    uint64 MaxFrameCounter;
     
    /** Holds the number of cycles in the last frame. */
    uint32 LastFrameCycles;
 
#if WITH_ENGINE
 
    /** Holds the objects which need to be cleaned up when the rendering thread finishes the previous frame. */
    FPendingCleanupObjects* PendingCleanupObjects;
 
#endif //WITH_ENGINE
 
private:
 
#if WITH_ENGINE
 
    /** Holds the engine service. */
    FEngineService* EngineService;
 
    /** Holds the application session service. */
    TSharedPtr<ISessionService> SessionService;
 
#endif // WITH_ENGINE
    FPreInitContext PreInitContext;
};

PreInit 阶段

PreInit 有两个核心函数进行初始化,其实主要是分为屏幕显示之前和屏幕显示之后。

PreInitPreStartupScreen主要是完成基础系统(内存,文件,核心模块等)+图像系统基础初始化。让用户更快进入到图像界面,后续更高级的chu shi hua

PreInitPostStartupScreen主要是完成图形系统的高级功能+UObject系统的初始化

并且两个核心函数通过 PreInitContext 保存状态(包括 SlateEnderer)避免重复创建

我们这里主要是了解这些就行,更具体的可以单独出一篇文章来分析。

图片描述

PreInitPreStartupScreen(启动屏幕前)

这个函数代码量巨大,只能写个函数头了:-<

  • 基础系统初始化(日志、命令行、文件系统)
  • 内存和追踪系统初始化
  • 项目文件加载
  • TaskGraph 和 Stats 系统初始化
  • 核心模块加载(CoreUObject)
  • RHI 初始化(渲染硬件接口)
  • SlateRenderer 创建
  • 显示启动屏幕(Splash Screen)
1
int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine)

PreInitPostStartupScreen(启动屏幕后)

  • 恢复 PreInitContext(包括 SlateRenderer)
  • 加载需要图形系统的模块(PreEarlyLoadingScreen)
  • 播放早期启动电影
  • 挂载 Pak 文件
  • 打开着色器库
  • 初始化 UObject 系统
  • 初始化默认材质和流式管理器
1
int32 FEngineLoop::PreInitPostStartupScreen(const TCHAR* CmdLine)

核心数据结构

PreInitContext 保存上下文

我们可以清晰看见这里保存了 SlowTaskPtr,SlateRenderer,CommandLineCopy等重要资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct FPreInitContext
{
    bool bDumpEarlyConfigReads = false;
    bool bDumpEarlyPakFileReads = false;
    bool bForceQuitAfterEarlyReads = false;
    bool bWithConfigPatching = false;
    bool bDisableDisregardForGC = false;
    bool bHasEditorToken = false;
    bool bIsRegularClient = false;
    bool bTokenDoesNotHaveDash = false;
 
    FString Token;
    const TCHAR* CommandletCommandLine = nullptr;
    TCHAR* CommandLineCopy = nullptr;
 
    FScopedSlowTask* SlowTaskPtr = nullptr;
 
    void Cleanup();
 
#if WITH_ENGINE && !UE_SERVER
    TSharedPtr<FSlateRenderer> SlateRenderer;
#endif // WITH_ENGINE && !UE_SERVER
};

CommandLineCopy(命令行副本)

1
PreInitContext.CommandLineCopy = CommandLineCopy;

SlateRenderer(图形渲染器)

为什么需要保存捏?

  • 创建成本高(需要加载模块、初始化 RHI)
  • 需要在两个函数间复用
  • 避免重复创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
TSharedPtr<FSlateRenderer> SlateRenderer = GUsingNullRHI ?
        FModuleManager::Get().LoadModuleChecked<ISlateNullRendererModule>("SlateNullRenderer").CreateSlateNullRenderer() :
        FModuleManager::Get().GetModuleChecked<ISlateRHIRendererModule>("SlateRHIRenderer").CreateSlateRHIRenderer();
TSharedRef<FSlateRenderer> SlateRendererSharedRef = SlateRenderer.ToSharedRef();
 
{
        SCOPED_BOOT_TIMING("CurrentSlateApp.InitializeRenderer");
        // If Slate is being used, initialize the renderer after RHIInit
        FSlateApplication& CurrentSlateApp = FSlateApplication::Get();
        CurrentSlateApp.InitializeRenderer(SlateRendererSharedRef);
}
 
{
        SCOPED_BOOT_TIMING("FEngineFontServices::Create");
        // Create the engine font services now that the Slate renderer is ready
        FEngineFontServices::Create();
}
 
{
        SCOPED_BOOT_TIMING("LoadModulesForProject(ELoadingPhase::PostSplashScreen)");
        // Load up all modules that need to hook into the custom splash screen
        if (!IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostSplashScreen) || !IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostSplashScreen))
        {
                return 1;
        }
}
 
{
        SCOPED_BOOT_TIMING("PlayFirstPreLoadScreen");
 
        if (FPreLoadScreenManager::Get())
        {
                {
                        SCOPED_BOOT_TIMING("PlayFirstPreLoadScreen - FPreLoadScreenManager::Get()->Initialize");
                        // initialize and present custom splash screen
                        FPreLoadScreenManager::Get()->Initialize(SlateRendererSharedRef.Get());
                }
 
                if (FPreLoadScreenManager::Get()->HasRegisteredPreLoadScreenType(EPreLoadScreenTypes::CustomSplashScreen))
                {
                        FPreLoadScreenManager::Get()->PlayFirstPreLoadScreen(EPreLoadScreenTypes::CustomSplashScreen);
                }
#if PLATFORM_XBOXONE && WITH_LEGACY_XDK && ENABLE_XBOXONE_FAST_ACTIVATION
                else
                {
                        UE_LOG(LogInit, Warning, TEXT("Enable fast activation without enabling a custom splash screen may cause garbage frame buffer being presented"));
                }
#endif
        }
}
 
PreInitContext.SlateRenderer = SlateRenderer;

SlowTaskPtr(进度条对象)

  • 需要在两个函数间保持进度连续性
  • 避免重复创建进度条
1
2
PreInitContext.SlowTaskPtr = new FScopedSlowTask(100, NSLOCTEXT("EngineLoop", "EngineLoop_Initializing", "Initializing..."));
FScopedSlowTask& SlowTask = *PreInitContext.SlowTaskPtr;

调用链

PreInitPre

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
FEngineLoop::PreInitPreStartupScreen (1434行)
├─ 1. 基础初始化阶段
│  ├─ FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates (1436行)
│  ├─ GLog->SetCurrentThreadAsMasterThread() (1446行)
│  ├─ FWindowsPlatformMisc::SetGracefulTerminationHandler() (1456行) [Windows]
│  ├─ FMemory::SetupTLSCachesOnCurrentThread() (1468行)
│  ├─ FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir() (1476行)
│  └─ FCommandLine::Set(CmdLine) (1480行) → 失败返回-1
├─ 2. Trace和内存系统
│  ├─ FTraceAuxiliary::Initialize(CmdLine) (1501行)
│  ├─ FLowLevelMemTracker::Get().ProcessCommandLine() (1506行)
│  └─ FPlatformMisc::InitTaggedStorage(1024) (1515行)
├─ 3. 委托注册
│  ├─ FCoreUObjectDelegates::PostGarbageCollectConditionalBeginDestroy.AddStatic() (1519行)
│  └─ FCoreDelegates::OnSamplingInput.AddStatic() (1521行)
├─ 4. 游戏名称设置
│  └─ LaunchSetGameName(CmdLine, ...) (1555行) → 失败返回1
├─ 5. 输出设备初始化
│  ├─ FPlatformApplicationMisc::CreateConsoleOutputDevice() (1566行)
│  ├─ GLog->EnableBacklog(true) (1572行)
│  └─ InitializeStdOutDevice() (1580行) [如果stdout参数]
├─ 6. 文件系统初始化
│  ├─ LaunchCheckForFileOverride() (1705行) → 失败返回1
│  ├─ FModuleManager::Get().AddExtraBinarySearchPaths() (1716行)
│  ├─ IFileManager::Get().ProcessCommandLineOptions() (1723行)
│  └─ FPlatformFileManager::Get().InitializeNewAsyncIO() (1729行)
├─ 7. 线程设置
│  ├─ GGameThreadId = FPlatformTLS::GetCurrentThreadId() (1749行)
│  ├─ FPlatformProcess::SetThreadAffinityMask() (1752行)
│  └─ FPlatformProcess::SetupGameThread() (1753行)
├─ 8. 命令行解析
│  ├─ FParse::Token() (1761行)
│  ├─ AddShaderSourceDirectoryMapping() (1765行)
│  └─ UCommandlet::ParseCommandLine() (1769行)
├─ 9. 项目文件加载
│  └─ IProjectManager::Get().LoadProjectFile() (2020行) → 失败返回1
├─ 10. TaskGraph和Stats
│  ├─ FTaskGraphInterface::Startup() (2059行)
│  └─ FThreadStats::StartThread() (2066行)
└─ 11. 核心模块加载
    └─ LoadCoreModules() (2076行)
       └─ FModuleManager::Get().LoadModule("CoreUObject") (3674行) → 失败返回1

PreInitPost

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
FEngineLoop::PreInitPostStartupScreen (2819行)
├─ 1. 检查退出请求
│  └─ IsEngineExitRequested() (2823行) → 如果已请求则返回0
├─ 2. 图形系统初始化 [非服务器且非Commandlet]
│  ├─ GetMoviePlayer()->SetupLoadingScreenFromIni() (2863行)
│  ├─ IProjectManager::Get().LoadModulesForProject(PreEarlyLoadingScreen) (2869行)
│  └─ IPluginManager::Get().LoadModulesForEnabledPlugins(PreEarlyLoadingScreen) (2869行) → 失败返回1
├─ 3. 启动屏幕处理
│  ├─ GetMoviePlayer()->PlayEarlyStartupMovies() (2891行) [如果有早期电影]
│  └─ FPreLoadScreenManager::Get()->PlayFirstPreLoadScreen() (2970行) [否则]
├─ 4. Pak文件和配置
│  ├─ FCoreDelegates::OnMountAllPakFiles.Execute() (3006行) [如果没有Bundle管理器]
│  ├─ DumpEarlyReads() (3013行)
│  └─ HandleConfigReload() (3019行) [如果有配置补丁]
├─ 5. 着色器系统
│  ├─ FShaderCodeLibrary::OpenLibrary() (3028行)
│  └─ FShaderPipelineCache::OpenPipelineFileCache() (3035行)
├─ 6. UObject系统初始化
│  ├─ BeginInitGameTextLocalization() (3052行)
│  ├─ FPackageName::RegisterShortPackageNamesForUObjectModules() (3058行)
│  ├─ ProcessNewlyLoadedUObjects() (3078行)
│  └─ EndInitGameTextLocalization() (3080行)
└─ 7. 材质和流式管理
    ├─ UMaterialInterface::InitDefaultMaterials() (3109行)
    └─ IStreamingManager::Get() (3118行)

Init 阶段

算是 UE 的真正初始化,会进行引擎的配置各种初始化和核心的初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
int32 FEngineLoop::Init()
{
    LLM_SCOPE(ELLMTag::EngineInitMemory);
    SCOPED_BOOT_TIMING("FEngineLoop::Init");
 
    DECLARE_SCOPE_CYCLE_COUNTER( TEXT( "FEngineLoop::Init" ), STAT_FEngineLoop_Init, STATGROUP_LoadTime );
 
    FScopedSlowTask SlowTask(100);
    SlowTask.EnterProgressFrame(10);
 
    FEmbeddedCommunication::ForceTick(10);
 
    // Figure out which UEngine variant to use.
    UClass* EngineClass = nullptr;
    if( !GIsEditor )
    {
        SCOPED_BOOT_TIMING("Create GEngine");
        // We're the game.
        FString GameEngineClassName;
        GConfig->GetString(TEXT("/Script/Engine.Engine"), TEXT("GameEngine"), GameEngineClassName, GEngineIni);
        EngineClass = StaticLoadClass( UGameEngine::StaticClass(), nullptr, *GameEngineClassName);
        if (EngineClass == nullptr)
        {
            UE_LOG(LogInit, Fatal, TEXT("Failed to load UnrealEd Engine class '%s'."), *GameEngineClassName);
        }
        GEngine = NewObject<UEngine>(GetTransientPackage(), EngineClass);
    }
    else
    {
#if WITH_EDITOR
        // We're UnrealEd.
        FString UnrealEdEngineClassName;
        GConfig->GetString(TEXT("/Script/Engine.Engine"), TEXT("UnrealEdEngine"), UnrealEdEngineClassName, GEngineIni);
        EngineClass = StaticLoadClass(UUnrealEdEngine::StaticClass(), nullptr, *UnrealEdEngineClassName);
        if (EngineClass == nullptr)
        {
            UE_LOG(LogInit, Fatal, TEXT("Failed to load UnrealEd Engine class '%s'."), *UnrealEdEngineClassName);
        }
        GEngine = GEditor = GUnrealEd = NewObject<UUnrealEdEngine>(GetTransientPackage(), EngineClass);
#else
        check(0);
#endif
    }
 
    FEmbeddedCommunication::ForceTick(11);
 
    check( GEngine );
 
    GetMoviePlayer()->PassLoadingScreenWindowBackToGame();
     
    if (FPreLoadScreenManager::Get())
    {
        FPreLoadScreenManager::Get()->PassPreLoadScreenWindowBackToGame();
    }
 
    {
        SCOPED_BOOT_TIMING("GEngine->ParseCommandline()");
        GEngine->ParseCommandline();
    }
 
    FEmbeddedCommunication::ForceTick(12);
 
    {
        SCOPED_BOOT_TIMING("InitTime");
        InitTime();
    }
 
    SlowTask.EnterProgressFrame(60);
 
    {
        SCOPED_BOOT_TIMING("GEngine->Init");
        GEngine->Init(this);
    }
 
    // Call init callbacks
    FCoreDelegates::OnPostEngineInit.Broadcast();
 
    SlowTask.EnterProgressFrame(30);
 
    // initialize engine instance discovery
    if (FPlatformProcess::SupportsMultithreading())
    {
        SCOPED_BOOT_TIMING("SessionService etc");
        if (!IsRunningCommandlet())
        {
            SessionService = FModuleManager::LoadModuleChecked<ISessionServicesModule>("SessionServices").GetSessionService();
             
            if (SessionService.IsValid())
            {
            SessionService->Start();
        }
        }
 
        EngineService = new FEngineService();
    }
 
    {
        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;
        }
    }
 
    {
        SCOPED_BOOT_TIMING("GEngine->Start()");
        GEngine->Start();
    }
 
    FEmbeddedCommunication::ForceTick(13);
 
    if (FPreLoadScreenManager::Get() && FPreLoadScreenManager::Get()->HasActivePreLoadScreenType(EPreLoadScreenTypes::EngineLoadingScreen))
    {
        SCOPED_BOOT_TIMING("WaitForEngineLoadingScreenToFinish");
        FPreLoadScreenManager::Get()->SetEngineLoadingComplete(true);
        FPreLoadScreenManager::Get()->WaitForEngineLoadingScreenToFinish();
    }
    else
    {
        SCOPED_BOOT_TIMING("WaitForMovieToFinish");
        GetMoviePlayer()->WaitForMovieToFinish();
    }
 
    FTraceAuxiliary::EnableChannels();
 
#if !UE_SERVER
    // initialize media framework
    IMediaModule* MediaModule = FModuleManager::LoadModulePtr<IMediaModule>("Media");
 
    if (MediaModule != nullptr)
    {
        MediaModule->SetTimeSource(MakeShareable(new FAppMediaTimeSource));
    }
#endif
 
    FEmbeddedCommunication::ForceTick(14);
 
    // initialize automation worker
#if WITH_AUTOMATION_WORKER
    FModuleManager::Get().LoadModule("AutomationWorker");
#endif
 
    // Automation tests can be invoked locally in non-editor builds configuration (e.g. performance profiling in Test configuration)
#if WITH_ENGINE && !UE_BUILD_SHIPPING
    FModuleManager::Get().LoadModule("AutomationController");
    FModuleManager::GetModuleChecked<IAutomationControllerModule>("AutomationController").Init();
#endif
 
#if WITH_EDITOR
    if (GIsEditor)
    {
        FModuleManager::Get().LoadModule(TEXT("ProfilerClient"));
    }
 
    FModuleManager::Get().LoadModule(TEXT("SequenceRecorder"));
    FModuleManager::Get().LoadModule(TEXT("SequenceRecorderSections"));
#endif
 
    GIsRunning = true;
 
    if (!GIsEditor)
    {
        // hide a couple frames worth of rendering
        FViewport::SetGameRenderingEnabled(true, 3);
    }
 
    FEmbeddedCommunication::ForceTick(15);
 
    FCoreDelegates::StarvedGameLoop.BindStatic(&GameLoopIsStarved);
 
    // Ready to measure thread heartbeat
    FThreadHeartBeat::Get().Start();
 
    FShaderPipelineCache::PauseBatching();
    {
#if defined(WITH_CODE_GUARD_HANDLER) && WITH_CODE_GUARD_HANDLER
         void CheckImageIntegrity();
        CheckImageIntegrity();
#endif
    }
     
    {
        SCOPED_BOOT_TIMING("FCoreDelegates::OnFEngineLoopInitComplete.Broadcast()");
        FCoreDelegates::OnFEngineLoopInitComplete.Broadcast();
    }
    FShaderPipelineCache::ResumeBatching();
 
#if BUILD_EMBEDDED_APP
    FEmbeddedCommunication::AllowSleep(TEXT("Startup"));
    FEmbeddedCommunication::KeepAwake(TEXT("FirstTicks"), true);
#endif
     
#if UE_EXTERNAL_PROFILING_ENABLED
    FExternalProfiler* ActiveProfiler = FActiveExternalProfilerBase::InitActiveProfiler();
    if (ActiveProfiler)
    {
        ActiveProfiler->Register();
    }
#endif      // UE_EXTERNAL_PROFILING_ENABLED
 
    FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::EndOfEngineInit);
 
    // Emit logging. Don't edit! Automation looks for this to detect failures during initialization.
    UE_LOG(LogInit, Display, TEXT("Engine is initialized. Leaving FEngineLoop::Init()"));
    return 0;
}

函数调用链

FEngineLoop::Init() (LaunchEngineLoop.cpp:3946)
│
├─ 模块1: 创建引擎对象 (3958-3988行)
│  ├─ [游戏模式]
│  │  ├─ GConfig->GetString(TEXT("/Script/Engine.Engine"), TEXT("GameEngine"), ...) (3965行)
│  │  ├─ StaticLoadClass(UGameEngine::StaticClass(), ...) (3966行)
│  │  └─ NewObject<UEngine>(GetTransientPackage(), EngineClass) (3971行)
│  │
│  └─ [编辑器模式]
│     ├─ GConfig->GetString(TEXT("/Script/Engine.Engine"), TEXT("UnrealEdEngine"), ...) (3978行)
│     ├─ StaticLoadClass(UUnrealEdEngine::StaticClass(), ...) (3979行)
│     └─ NewObject<UUnrealEdEngine>(GetTransientPackage(), EngineClass) (3984行)
│
├─ 模块2: 传递窗口控制 (3994-3999行)
│  ├─ GetMoviePlayer()->PassLoadingScreenWindowBackToGame() (3994行)
│  └─ FPreLoadScreenManager::Get()->PassPreLoadScreenWindowBackToGame() (3998行)
│
├─ 模块3: 引擎配置初始化 (4003-4010行)
│  ├─ GEngine->ParseCommandline() (4003行)
│  └─ InitTime() (4010行)
│     ├─ FApp::SetCurrentTime(FPlatformTime::Seconds())
│     ├─ 解析-SECONDS=、-BENCHMARKSECONDS=、-FPS=参数
│     └─ 计算MaxFrameCounter
│
├─ 模块4: 引擎核心初始化 (4017行)
│  └─ GEngine->Init(this) (4017行)
│     │
│     ├─ [UGameEngine::Init] (GameEngine.cpp:1066)
│     │  ├─ UEngine::Init(InEngineLoop) [基类]
│     │  ├─ GetGameUserSettings()->LoadSettings() (1083行)
│     │  ├─ NewObject<UGameInstance>(this, GameInstanceClass) (1097行)
│     │  ├─ GameInstance->InitializeStandalone() (1099行)
│     │  ├─ NewObject<UGameViewportClient>(this, GameViewportClientClass) (1121行)
│     │  ├─ ViewportClient->Init(...) (1122行)
│     │  ├─ CreateGameWindow() (1137行)
│     │  ├─ CreateGameViewport(ViewportClient) (1140行)
│     │  └─ ViewportClient->SetupInitialLocalPlayer(Error) (1148行)
│     │
│     └─ [UUnrealEdEngine::Init] (UnrealEdEngine.cpp:75)
│        ├─ Super::Init(InEngineLoop) [基类]
│        ├─ ValidateFreeDiskSpace() (80行)
│        ├─ FSourceCodeNavigation::Initialize() (83行)
│        └─ PackageAutoSaver = new FPackageAutoSaver() (85行)
│
├─ 模块5: 服务与模块加载 (4021-4050行)
│  ├─ FCoreDelegates::OnPostEngineInit.Broadcast() (4021行)
│  ├─ SessionService初始化 (4026-4037行)
│  │  ├─ FModuleManager::LoadModuleChecked<ISessionServicesModule>("SessionServices") (4031行)
│  │  └─ SessionService->Start() (4035行)
│  ├─ EngineService = new FEngineService() (4039行)
│  └─ PostEngineInit模块加载 (4043-4050行)
│     ├─ IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostEngineInit) (4045行)
│     └─ IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostEngineInit) (4045行)
│        └─ 失败则RequestEngineExit并返回1 (4047行)
│
├─ 模块6: 启动引擎 (4054行)
│  └─ GEngine->Start() (4054行)
│     │
│     └─ [UGameEngine::Start] (GameEngine.cpp:1162)
│        └─ GameInstance->StartGameInstance() (1166行)
│
├─ 模块7: 等待加载完成 (4059-4069行)
│  ├─ {有EngineLoadingScreen?}
│  │  ├─ [是] FPreLoadScreenManager::Get()->WaitForEngineLoadingScreenToFinish() (4063行)
│  │  └─ [否] GetMoviePlayer()->WaitForMovieToFinish() (4068行)
│
└─ 模块8: 后续初始化 (4071-4148行)
   ├─ FTraceAuxiliary::EnableChannels() (4071行)
   ├─ IMediaModule->SetTimeSource(...) (4079行) [非服务器]
   ├─ FModuleManager::Get().LoadModule("AutomationWorker") (4087行)
   ├─ FModuleManager::Get().LoadModule("AutomationController") (4092行) [非Shipping]
   ├─ FModuleManager::Get().LoadModule("ProfilerClient") (4099行) [编辑器]
   ├─ GIsRunning = true (4106行)
   ├─ FViewport::SetGameRenderingEnabled(true, 3) (4111行) [非编辑器]
   ├─ FCoreDelegates::StarvedGameLoop.BindStatic(&GameLoopIsStarved) (4116行)
   ├─ FThreadHeartBeat::Get().Start() (4119行)
   ├─ CheckImageIntegrity() (4125行) [如果WITH_CODE_GUARD_HANDLER]
   ├─ FCoreDelegates::OnFEngineLoopInitComplete.Broadcast() (4131行)
   └─ FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::EndOfEngineInit) (4148行)

创建引擎实列

  • 根据 GIsEditor 决定创建 UGameEngine 或 UUnrealEdEngine
  • 从配置文件读取引擎类名(默认 GameEngine/UnrealEdEngine)
  • 使用 StaticLoadClass 加载类,NewObject 创建实例
  • 编辑器模式下同时设置 GEditor 和 GUnrealEd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Figure out which UEngine variant to use.
UClass* EngineClass = nullptr;
if( !GIsEditor )
{
        SCOPED_BOOT_TIMING("Create GEngine");
        // We're the game.
        FString GameEngineClassName;
        GConfig->GetString(TEXT("/Script/Engine.Engine"), TEXT("GameEngine"), GameEngineClassName, GEngineIni);
        EngineClass = StaticLoadClass( UGameEngine::StaticClass(), nullptr, *GameEngineClassName);
        if (EngineClass == nullptr)
        {
                UE_LOG(LogInit, Fatal, TEXT("Failed to load UnrealEd Engine class '%s'."), *GameEngineClassName);
        }
        GEngine = NewObject<UEngine>(GetTransientPackage(), EngineClass);
}
else
{
#if WITH_EDITOR
        // We're UnrealEd.
        FString UnrealEdEngineClassName;
        GConfig->GetString(TEXT("/Script/Engine.Engine"), TEXT("UnrealEdEngine"), UnrealEdEngineClassName, GEngineIni);
        EngineClass = StaticLoadClass(UUnrealEdEngine::StaticClass(), nullptr, *UnrealEdEngineClassName);
        if (EngineClass == nullptr)
        {
                UE_LOG(LogInit, Fatal, TEXT("Failed to load UnrealEd Engine class '%s'."), *UnrealEdEngineClassName);
        }
        GEngine = GEditor = GUnrealEd = NewObject<UUnrealEdEngine>(GetTransientPackage(), EngineClass);
#endif
}

传递加载画面窗口

将加载画面窗口所有权交还给游戏窗口系统。

这里其实很好理解因为我们刚启动的时候是一个窗口主要复杂启动等一系列工作,然后游戏画面照理来说会是一个崭新的窗口,但是创建多个窗口太割裂了。于是我们就复用加载画面的窗口来作为游戏主窗口。

图片描述

1
2
3
4
5
6
GetMoviePlayer()->PassLoadingScreenWindowBackToGame();
 
if (FPreLoadScreenManager::Get())
{
        FPreLoadScreenManager::Get()->PassPreLoadScreenWindowBackToGame();
}

解析命令行

让引擎实例解析命令行参数,设置相关配置。方便后续系统进行读取命令行参数

1
2
3
4
{
    SCOPED_BOOT_TIMING("GEngine->ParseCommandline()");
    GEngine->ParseCommandline();
}

初始化时间系统

  • 初始化时间相关变量(当前时间、帧计数器、Tick 时间限制)
  • 从命令行读取基准测试参数(-SECONDS=、-BENCHMARKSECONDS=、-FPS=)
  • 设置固定帧率(如果指定)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
        SCOPED_BOOT_TIMING("InitTime");
        InitTime();
}
     
void FEngineLoop::InitTime()
{
        // Init variables used for benchmarking and ticking.
        FApp::SetCurrentTime(FPlatformTime::Seconds());
        MaxFrameCounter                                = 0;
        MaxTickTime                                        = 0;
        TotalTickTime                                = 0;
        LastFrameCycles                                = FPlatformTime::Cycles();
 
        float FloatMaxTickTime                = 0;
#if (!UE_BUILD_SHIPPING || ENABLE_PGO_PROFILE)
        FParse::Value(FCommandLine::Get(),TEXT("SECONDS="),FloatMaxTickTime);
        MaxTickTime                                        = FloatMaxTickTime;
 
        // look of a version of seconds that only is applied if FApp::IsBenchmarking() is set.
        if (FApp::IsBenchmarking())
        {
                if (FParse::Value(FCommandLine::Get(),TEXT("BENCHMARKSECONDS="),FloatMaxTickTime) && FloatMaxTickTime)
                {
                        MaxTickTime                        = FloatMaxTickTime;
                }
        }
 
        // Use -FPS=X to override fixed tick rate if e.g. -BENCHMARK is used.
        float FixedFPS = 0;
        FParse::Value(FCommandLine::Get(),TEXT("FPS="),FixedFPS);
        if( FixedFPS > 0 )
        {
                FApp::SetFixedDeltaTime(1 / FixedFPS);
        }
#endif
 
        // convert FloatMaxTickTime into number of frames (using 1 / FApp::GetFixedDeltaTime() to convert fps to seconds )
        MaxFrameCounter = FMath::TruncToInt(MaxTickTime / FApp::GetFixedDeltaTime());
}

Uengine::Init (引擎初始化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
{
    SCOPED_BOOT_TIMING("GEngine->Init");
    GEngine->Init(this);
}
//
// Initialize the engine.
//
void UEngine::Init(IEngineLoop* InEngineLoop)
{
    UE_LOG(LogEngine, Log, TEXT("Initializing Engine..."));
    DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Engine Initialized"), STAT_EngineStartup, STATGROUP_LoadTime);
 
    // Start capturing errors and warnings
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
    ErrorsAndWarningsCollector.Initialize();
#endif
 
#if WITH_EDITOR
    if(!FEngineBuildSettings::IsInternalBuild())
    {
        TArray<TSharedRef<IPlugin>> EnabledPlugins = IPluginManager::Get().GetEnabledPlugins();
 
        for (auto Plugin : EnabledPlugins)
        {
            const FPluginDescriptor& Desc = Plugin->GetDescriptor();
 
            // encode a minimal plugin description for the crash reporter
            FString DescStr;
            TSharedRef< TJsonWriter<> > WriterRef = TJsonWriterFactory<>::Create(&DescStr);
            TJsonWriter<>& Writer = WriterRef.Get();           
            Writer.WriteObjectStart();
            Writer.WriteValue(TEXT("Version"), Desc.Version);
            Writer.WriteValue(TEXT("VersionName"), Desc.VersionName);
            Writer.WriteValue(TEXT("FriendlyName"), Desc.FriendlyName);
            Writer.WriteObjectEnd();
            Writer.Close();
 
            FGenericCrashContext::AddPlugin(DescStr);
        }
    }
#endif
 
    // Set the memory warning handler
    if (!FPlatformMisc::HasMemoryWarningHandler())
    {
        FPlatformMisc::SetMemoryWarningHandler(EngineMemoryWarningHandler);
    }
 
    EngineLoop = InEngineLoop;
 
    // Subsystems.
    FURL::StaticInit();
    FLinkerLoad::StaticInit(UTexture2D::StaticClass());
    EngineSubsystemCollection->Initialize(this);
 
#if !UE_BUILD_SHIPPING
    // Check for overrides to the default map on the command line
    TCHAR MapName[512];
    if ( FParse::Value(FCommandLine::Get(), TEXT("DEFAULTMAP="), MapName, UE_ARRAY_COUNT(MapName)) )
    {
        UE_LOG(LogEngine, Log, TEXT("Overriding default map to %s"), MapName);
 
        FString MapString = FString(MapName);
        UGameMapsSettings::SetGameDefaultMap(MapString);
    }
#endif // !UE_BUILD_SHIPPING
 
    InitializeRunningAverageDeltaTime();
 
    // Add to root.
    AddToRoot();
 
    FCoreUObjectDelegates::GetPreGarbageCollectDelegate().AddStatic(UEngine::PreGarbageCollect);
 
    if (!FApp::IsProjectNameEmpty())
    {
        // Initialize the HMDs and motion controllers, if any
        InitializeHMDDevice();
 
        // Initialize attached eye tracking devices, if any
        InitializeEyeTrackingDevice();
    }
 
    // Disable the screensaver when running the game.
    if( GIsClient && !GIsEditor )
    {
        EnableScreenSaver( false );
    }
 
    if (!IsRunningDedicatedServer() && !IsRunningCommandlet())
    {
        // If Slate is being used, initialize the renderer after RHIInit
        FSlateApplication& CurrentSlateApp = FSlateApplication::Get();
        CurrentSlateApp.InitializeSound( TSharedRef<FSlateSoundDevice>( new FSlateSoundDevice() ) );
 
#if !UE_BUILD_SHIPPING
        // Create test windows (if we were asked to do that)
        if( FParse::Param( FCommandLine::Get(), TEXT("SlateDebug") ) )
        {
            RestoreSlateTestSuite();
        }
#endif // #if !UE_BUILD_SHIPPING
    }
 
    // Assign thumbnail compressor/decompressor
    FObjectThumbnail::SetThumbnailCompressor( new FPNGThumbnailCompressor() );
 
    //UEngine::StaticClass()->GetDefaultObject(true);
    LoadObject<UClass>(UEngine::StaticClass()->GetOuter(), *UEngine::StaticClass()->GetName(), NULL, LOAD_Quiet|LOAD_NoWarn, NULL );
    // This reads the Engine.ini file to get the proper DefaultMaterial, etc.
    LoadConfig();
 
    SetConfiguredProcessLimits();
 
    bIsOverridingSelectedColor = false;
 
    // Set colors for selection materials
    SelectedMaterialColor = DefaultSelectedMaterialColor;
    SelectionOutlineColor = DefaultSelectedMaterialColor;
 
    InitializeObjectReferences();
 
    if (GConfig)
    {
        bool bTemp = true;
        GConfig->GetBool(TEXT("/Script/Engine.Engine"), TEXT("bEnableOnScreenDebugMessages"), bTemp, GEngineIni);
        bEnableOnScreenDebugMessages = bTemp ? true : false;
        bEnableOnScreenDebugMessagesDisplay = bEnableOnScreenDebugMessages;
    }
 
    // Update Script Maximum loop iteration count
    FBlueprintCoreDelegates::SetScriptMaximumLoopIterations( GEngine->MaximumLoopIterationCount );
 
    GNearClippingPlane = NearClipPlane;
 
    UTextRenderComponent::InitializeMIDCache();
 
    // Initialize scene cached cvars
    extern FReadOnlyCVARCache GReadOnlyCVARCache;
    GReadOnlyCVARCache.Init();
 
    if (GIsEditor)
    {
        // Create a WorldContext for the editor to use and create an initially empty world.
        FWorldContext &InitialWorldContext = CreateNewWorldContext(EWorldType::Editor);
        InitialWorldContext.SetCurrentWorld( UWorld::CreateWorld( EWorldType::Editor, true ) );
        GWorld = InitialWorldContext.World();
    }
 
    // Initialize the audio device after a world context is setup
    InitializeAudioDeviceManager();
 
    // Make sure networking checksum has access to project version
    const UGeneralProjectSettings& ProjectSettings = *GetDefault<UGeneralProjectSettings>();
    FNetworkVersion::SetProjectVersion(*ProjectSettings.ProjectVersion);
 
#if !(UE_BUILD_SHIPPING) || ENABLE_PGO_PROFILE
    // Optionally Exec an exec file
    FString Temp;
    if( FParse::Value(FCommandLine::Get(), TEXT("EXEC="), Temp) )
    {
        new(GEngine->DeferredCommands) FString(FString(TEXT("exec ")) + Temp);
    }
 
    // Optionally exec commands passed in the command line.
    FString ExecCmds;
    if( FParse::Value(FCommandLine::Get(), TEXT("ExecCmds="), ExecCmds, false) )
    {
        // Read the command array, ignoring any commas in single-quotes.
        // Convert any single-quotes to double-quotes and skip leading whitespace
        // This allows passing of strings, e:g -execcmds="exampleCvar '0,1,2,3'"
        TArray<FString> CommandArray;
        FString CurrentCommand = "";
        bool bInQuotes = false;
        bool bSkippingWhitespace = true;
        for (int i = 0; i < ExecCmds.Len(); i++)
        {
            TCHAR CurrentChar = ExecCmds[i];
            if (CurrentChar == '\'')
            {
                bInQuotes = !bInQuotes;
                CurrentCommand += "\"";
            }
            else if (CurrentChar == ',' && !bInQuotes)
            {
                if (CurrentCommand.Len() > 0)
                {
                    CommandArray.Add(CurrentCommand);
                    CurrentCommand = "";
                }
                bSkippingWhitespace = true;
            }
            else
            {
                if (bSkippingWhitespace)
                {
                    bSkippingWhitespace = FChar::IsWhitespace(CurrentChar);
                }
                if (!bSkippingWhitespace)
                {
                    CurrentCommand += CurrentChar;
                }
            }
        }
        if (CurrentCommand.Len() > 0)
        {
            CommandArray.Add(CurrentCommand);
        }
 
        for( int32 Cx = 0; Cx < CommandArray.Num(); ++Cx )
        {
            new(GEngine->DeferredCommands) FString(*CommandArray[Cx]);
        }
    }
 
    // optionally set the vsync console variable
    if( FParse::Param(FCommandLine::Get(), TEXT("vsync")) )
    {
        new(GEngine->DeferredCommands) FString(TEXT("r.vsync 1"));
    }
 
    // optionally set the vsync console variable
    if( FParse::Param(FCommandLine::Get(), TEXT("novsync")) )
    {
        new(GEngine->DeferredCommands) FString(TEXT("r.vsync 0"));
    }
#endif // !(UE_BUILD_SHIPPING) || ENABLE_PGO_PROFILE
 
    if (GetDerivedDataCache())
    {
        GetDerivedDataCacheRef().NotifyBootComplete();
    }
     
    // register the engine with the travel and network failure broadcasts
    // games can override these to provide proper behavior in each error case
    OnTravelFailure().AddUObject(this, &UEngine::HandleTravelFailure);
    OnNetworkFailure().AddUObject(this, &UEngine::HandleNetworkFailure);
    OnNetworkLagStateChanged().AddUObject(this, &UEngine::HandleNetworkLagStateChanged);
 
    UE_LOG(LogInit, Log, TEXT("Texture streaming: %s"), IStreamingManager::Get().IsTextureStreamingEnabled() ? TEXT("Enabled") : TEXT("Disabled") );
 
    // Initialize the online subsystem as early as possible
    FOnlineExternalUIChanged OnExternalUIChangeDelegate;
    OnExternalUIChangeDelegate.BindUObject(this, &UEngine::OnExternalUIChange);
    UOnlineEngineInterface::Get()->BindToExternalUIOpening(OnExternalUIChangeDelegate);
 
    // Initialise buffer visualization system data
    GetBufferVisualizationData().Initialize();
 
    // Initialize Portal services
    if (!IsRunningCommandlet() && !IsRunningDedicatedServer())
    {
        InitializePortalServices();
    }
 
    // Connect the engine analytics provider
    FEngineAnalytics::Initialize();
 
    // Dynamically load engine runtime modules
    {
        FModuleManager::Get().LoadModule("ImageWriteQueue");
        FModuleManager::Get().LoadModuleChecked("StreamingPauseRendering");
        FModuleManager::Get().LoadModuleChecked("MovieScene");
        FModuleManager::Get().LoadModuleChecked("MovieSceneTracks");
        FModuleManager::Get().LoadModule("LevelSequence");
    }
 
    // Enable the live coding module if it's a developer build
#if WITH_LIVE_CODING
    FModuleManager::Get().LoadModule("LiveCoding");
#endif
 
    // Finish asset manager loading
    if (AssetManager)
    {
        AssetManager->FinishInitialLoading();
    }
 
    bool bIsRHS = true;
    if (GConfig)
    {
        GConfig->GetBool(TEXT("DevOptions.Debug"), TEXT("bEngineStatsOnRHS"), bIsRHS, GEngineIni);
    }
 
    // Add the stats to the list, note this is also the order that they get rendered in if active.
#if !UE_BUILD_SHIPPING
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Version"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatVersion), FEngineStatToggle(), bIsRHS));
#endif // !UE_BUILD_SHIPPING
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_NamedEvents"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatNamedEvents), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatNamedEvents), bIsRHS));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_FPS"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatFPS), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatFPS), bIsRHS));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Summary"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatSummary), FEngineStatToggle(), bIsRHS));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Unit"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatUnit), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatUnit), bIsRHS));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_DrawCount"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatDrawCount), FEngineStatToggle(), bIsRHS));
    /* @todo Slate Rendering
    #if STATS
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_SlateBatches"), TEXT("STATCAT_Engine"), FText::GetEmpty(), &UEngine::RenderStatSlateBatches, NULL, true));
    #endif
    */
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Hitches"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatHitches), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatHitches), bIsRHS));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_AI"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatAI), FEngineStatToggle(), bIsRHS));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Timecode"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatTimecode), FEngineStatToggle(), bIsRHS));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_FrameCounter"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatFrameCounter), FEngineStatToggle(), bIsRHS));
 
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_ColorList"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatColorList), FEngineStatToggle()));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Levels"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatLevels), FEngineStatToggle()));
#if !UE_BUILD_SHIPPING
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_SoundMixes"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatSoundMixes), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatSoundMixes)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_SoundModulators"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatSoundModulators), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatSoundModulators)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_SoundModulatorsHelp"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender(), FEngineStatToggle::CreateUObject(this, &UEngine::PostStatSoundModulatorHelp)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_AudioStreaming"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatAudioStreaming), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatAudioStreaming)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_SoundReverb"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatSoundReverb), FEngineStatToggle()));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Sounds"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatSounds), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatSounds)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_SoundCues"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatSoundCues), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatSoundCues)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_SoundWaves"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatSoundWaves), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatSoundWaves)));
#endif // !UE_BUILD_SHIPPING
    /* @todo UE4 physx fix this once we have convexelem drawing again
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_LevelMap"), TEXT("STATCAT_Engine"), FText::GetEmpty(), &UEngine::RenderStatLevelMap, NULL));
    */
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Detailed"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender(), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatDetailed)));
#if !UE_BUILD_SHIPPING
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_UnitMax"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender(), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatUnitMax)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_UnitGraph"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender(), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatUnitGraph)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_UnitTime"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender(), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatUnitTime)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_Raw"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender(), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatRaw)));
    EngineStats.Add(FEngineStatFuncs(TEXT("STAT_ParticlePerf"), TEXT("STATCAT_Engine"), FText::GetEmpty(), FEngineStatRender::CreateUObject(this, &UEngine::RenderStatParticlePerf), FEngineStatToggle::CreateUObject(this, &UEngine::ToggleStatParticlePerf), bIsRHS));
#endif // !UE_BUILD_SHIPPING
 
    // Let any listeners know about the new stats
    for (int32 StatIdx = 0; StatIdx < EngineStats.Num(); StatIdx++)
    {
        const FEngineStatFuncs& EngineStat = EngineStats[StatIdx];
        NewStatDelegate.Broadcast(EngineStat.CommandName, EngineStat.CategoryName, EngineStat.DescriptionString);
    }
 
    // Command line option for enabling named events
    if (FParse::Param(FCommandLine::Get(), TEXT("statnamedevents")))
    {
        GCycleStatsShouldEmitNamedEvents = 1;
    }
 
#if UE_NET_TRACE_ENABLED
    uint32 NetTraceVerbosity;
    if(FParse::Value(FCommandLine::Get(), TEXT("NetTrace="), NetTraceVerbosity))
    {
        FNetTrace::SetTraceVerbosity(NetTraceVerbosity);
    }
#endif
 
    // Record the analytics for any attached HMD devices
    RecordHMDAnalytics();
 
#if !UE_BUILD_SHIPPING
    UE_CLOG(FPlatformMemory::IsExtraDevelopmentMemoryAvailable(), LogInit, Warning, TEXT("Running with %dMB of extra development memory!"),FPlatformMemory::GetExtraDevelopmentMemorySize()/1024ull/1024ull);
#endif
}

Init 后续

PostEngineInit (广播init结束委托)

通知模块系统已经init结束。

1
2
// Call init callbacks
FCoreDelegates::OnPostEngineInit.Broadcast();

初始化 SessionService

SessionService 是一个基于消息总线的服务,用于引擎实例发现和远程通信。它允许远程工具(如 UnrealFrontend、编辑器)发现并连接到运行中的引擎实例。

比如日志转发,ping/pong响应,远程通信等等。仅在支持多线程的平台启用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// initialize engine instance discovery
if (FPlatformProcess::SupportsMultithreading())
{
    SCOPED_BOOT_TIMING("SessionService etc");
    if (!IsRunningCommandlet())
    {
        SessionService = FModuleManager::LoadModuleChecked<ISessionServicesModule>("SessionServices").GetSessionService();
         
        if (SessionService.IsValid())
        {
        SessionService->Start();
    }
    }
 
    EngineService = new FEngineService();
}

加载 PostEngineInit 模块

也就是加载需要在引擎初始化后加载的项目和插件模块

1
2
3
4
5
6
7
8
9
{
    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;
    }
}

启动引擎

在这里就正式启动 GameInstance 开始游戏逻辑了。

1
2
3
4
{
    SCOPED_BOOT_TIMING("GEngine->Start()");
    GEngine->Start();
}

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 3天前 被Elenia编辑 ,原因: 修正
收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 104
活跃值: (7531)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
tql
1天前
0
游客
登录 | 注册 方可回帖
返回