UE 引擎初始化和主循环

概述

笔记
代码版本5.3
从宏观的角度看引擎的初始化和Tick。
https://www.yuque.com/chenweilin-tryw7/gbfomp/ffox34r27nwf9nte?singleDoc# 《UE 架构基础入门》

跨平台Main入口

首先我们能很容易的找到主函数在哪

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;
}

锁定源码目录
Launch/Private 下能找到各个平台的文件夹。其中Windows文件夹下的LaunchWindows.cpp就是win平台的入口
在 LaunchWindowsStartup 中,进行了一些windows的命令行参数环境变量的设置

ErrorLevel = GuardedMain( CmdLine );
各个操作系统的主函数入口最终都会走到这里面,实现跨平台入口的统一。
这个函数位于Launch目录下的Launch.cpp

/**
 * 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 )
{
    struct EngineLoopCleanupGuard 
    { 
        ~EngineLoopCleanupGuard()
        {
            if (!GUELibraryOverrideSettings.bIsEmbedded)
            {
                EngineExit();
            }
        }
    } CleanupGuard;

    int32 ErrorLevel = EnginePreInit( CmdLine );

#if WITH_EDITOR
    if (GIsEditor)
    {
        ErrorLevel = EditorInit(GEngineLoop);
    }
    else
#endif
    {
        ErrorLevel = EngineInit();
    }

    while( !IsEngineExitRequested() )
    {
        EngineTick();
    }

#if WITH_EDITOR
    if( GIsEditor )
    {
        EditorExit();
    }
#endif
    return ErrorLevel;
}

掐头去尾,结构很简单

int32 ErrorLevel = EnginePreInit( CmdLine );
ErrorLevel = EditorInit(GEngineLoop);
ErrorLevel = EngineInit();

while( !IsEngineExitRequested() )
{
    EngineTick();
}

EditorExit();
EngineExit();

预初始化,编辑器初始化,引擎初始化,一直tick,编辑器退出,引擎退出

在 int32 ErrorLevel = EnginePreInit( CmdLine ); 之前还有一点代码。主要是

  • 解析 waitforattach,WaitForDebugger 等待调试器附加命令,如果有就阻塞
  • BootTimingPoint("DefaultMain") 记录引擎启动时间点
  • FCoreDelegates::GetPreMainInitDelegate().Broadcast(); 调用“PreMainInit”的 core delegates。UE的CoreDelegate贯穿整个引擎生命周期,我们可以通过绑定各种core delegate来在引擎各个阶段插入代码
  • struct EngineLoopCleanupGuard 利用局部对象析构调用析构函数来调用 EngineExit
  • MiniDumpFilenameW 是UE 拼接的一个dump文件名字

如何梳理记忆引擎初始化流程

另外为方便对引擎初始化的大致流程做划分梳理。我们可以用
这个枚举

enum class EDelayedRegisterRunPhase : uint8
{
    StartOfEnginePreInit,
    FileSystemReady,
    TaskGraphSystemReady,
    StatSystemReady,
    IniSystemReady,
    EarliestPossiblePluginsLoaded,
    ShaderTypesReady,
    PreObjectSystemReady,
    ObjectSystemReady,
    DeviceProfileManagerReady,
    EndOfEngineInit,

    NumPhases,
};

在代码里面搜索类似这样的逻辑,标明引擎初始化到了 StartOfEnginePreInit 开始引擎预初始化这一阶段。
FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::StartOfEnginePreInit);

或者
或者找这个枚举 ELoadingPhase 和这种调用
IProjectManager::Get().LoadModulesForProject(ELoadingPhase::Default)
IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::Default)
引擎加载到xx阶段,开始加载 ELoadingPhase::Default 这个阶段的插件

EnginePreInit

int32 ErrorLevel = EnginePreInit( CmdLine );

/** 
 * PreInits the engine loop 
 */
int32 EnginePreInit( const TCHAR* CmdLine )
{
    int32 ErrorLevel = GEngineLoop.PreInit( CmdLine );

    return( ErrorLevel );
}

这个 GEngineLoop 是什么。这是一个全局变量。没什么特别的。
FEngineLoop GEngineLoop;
打开后是两个函数

int32 FEngineLoop::PreInit(const TCHAR* CmdLine)
{
    const int32 rv1 = PreInitPreStartupScreen(CmdLine);
    const int32 rv2 = PreInitPostStartupScreen(CmdLine);
}

PreInitPreStartupScreen

StartOfEnginePreInit

函数位于: int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine)

  • ON_SCOPE_EXIT { GEnginePreInitPreStartupScreenEndTime = FPlatformTime::Seconds(); }; 记录作用域最后完成时间

  • FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::StartOfEnginePreInit);

    • 表示 进入 EDelayedRegisterRunPhase::StartOfEnginePreInit 阶段了
  • 如果GLog对象存在,则 SetCurrentThreadAsPrimaryThread 设置当前线程为主线程

  • FApp::SetDebugGame(true); 如果需要就设置DebugGame

  • 命令处理:statnamedevents,verbosenamedevents

  • FWindowsPlatformMisc::SetGracefulTerminationHandler(); 注册ctrl+c注册程序

  • FMemory::SetupTLSCachesOnCurrentThread();

  • 命令处理:UTF8Output,IgnoreDebugger

  • FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir(); 当前工作目录切到exe目录

  • 从环境变量中获取 "UE-CmdLineArgs"如果有就加进来命令处理

  • LaunchSetGameName(CmdLine, GameProjectFilePathUnnormalized) 设置当前游戏名称

  • FTraceAuxiliary::Initialize(CmdLine); 初始化追踪(堆栈,版本之类的)

  • LLM(FLowLevelMemTracker::Get().ProcessCommandLine(CmdLine)); LLM 初始化

  • FCoreUObjectDelegates::PostGarbageCollectConditionalBeginDestroy.AddStatic(DeferredPhysResourceCleanup); 注册垃圾回收后执行这个什么物理资源回收的函数

  • FCoreDelegates::OnSamplingInput.AddStatic(UpdateGInputTime); 输入采用事件

  • GMalloc = FStatsMallocProfilerProxy::Get(); 内存profile对象,如果在profile模式

  • GScopedLogConsole = TUniquePtr(FPlatformApplicationMisc::CreateConsoleOutputDevice()); // 初始化控制台输出设备

  • stdout命令 InitializeStdOutDevice();

  • if FPlatformProperties::SupportsQuit() 解析命令 testexit= 然后丢到输出设备里面去

  • FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir(); // 又来一次

  • SCOPED_BOOT_TIMING("Fix up the relative project path"); // 非 Program 模式下修复路径

  • SCOPED_BOOT_TIMING("Init Output Devices"); // 初始化输出设备,这里GError,GWarn 对象被创建。这个代理被广播 OnOutputDevicesInit

  • SCOPED_BOOT_TIMING("Command Line Adjustments"); // 非发布版本,检查命令行参数的准确性

  • FIoDispatcher::Initialize(); // 初始化IO调度器

  • BeginPreInitTextLocalization(); // 文本本地化初始化

  • FShaderCodeLibrary::PreInit(); // Shader初始化

  • SCOPED_BOOT_TIMING("LaunchCheckForFileOverride"); // 命令行参数是否有文本覆盖的选项

  • FModuleManager::Get().AddExtraBinarySearchPaths(); // 模块添加二进制搜索路径,保证某些库能被找到

  • IFileManager::Get().ProcessCommandLineOptions(); // 文件管理器,解析命令行,删除目录 ScreenShotDir ProjectLogDir

  • FPlatformFileManager::Get().InitializeNewAsyncIO(); // 初始化异步IO

FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::FileSystemReady);
看到这里可以对上面一大段做一个总结:
文件系统初始化已经准备好了。

FileSystemReady

ps: 这个标题并没有归类的意思,只是划分这里,已经执行完了 FileSystemReady 这个代理,一个分割线的作用

线程相关

GIsGameAgnosticExe, LaunchHasIncompleteGameName() 检测游戏名字的完整性
GGameThreadId = FPlatformTLS::GetCurrentThreadId(); // 当前主线程的ID
FPlatformProcess::SetupGameThread(); // 主线程名字改为 GameThread
  • 有几个lambda。一堆判断来设置程序运行环境,编辑器,DS,还是客户端

SetIsRunningAsCommandlet
SetIsRunningAsRegularClient
SetIsRunningAsDedicatedServer
SetIsRunningAsEditor
GIsClient, -Game -Server 这种

  • BENCHMARK 是否启动基准测试

  • // Initialize random number generator. // 随机数种子初始化

  • FPaths::SetProjectFilePath(ProjectFilePath); // 没有设置项目名字,就会默认一个

  • for => PlatformFileChainElement->InitializeAfterProjectFilePath(); // 初始化平台的文件管理器

  • LaunchFixProjectPathCase 修复路径大小写

  • // Now verify the project file if we have one 验证项目文件,并加载一些目录

IProjectManager::Get().LoadProjectFile(FPaths::GetProjectFilePath()) // 加载项目文件,企业目录是否存在,在就额外加载一个Binaries进来
AddDllDirectory,SetGameBinariesDirectory // 设置DLL和二进制目录

AddShaderSourceDirectoryMapping(TEXT("/Engine"), FPlatformProcess::ShaderDir()); // 添加Shader目录 GShaderSourceDirectoryMappings.Add
AddShaderSourceDirectoryMapping(TEXT("/ShaderAutogen"), AutogenAbsolutePath);
  • 创建线程
FTaskGraphInterface::Startup(FPlatformMisc::NumberOfWorkerThreadsToSpawn());
FTaskGraphInterface::Get().AttachToThread(ENamedThreads::GameThread);
GLargeThreadPool
GThreadPool 
GBackgroundPriorityThreadPool // 创建各种线程池

FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::TaskGraphSystemReady);

TaskGraphSystemReady

FThreadStats::StartThread(); // 启动线程统计
FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::StatSystemReady);

StatSystemReady

LoadCoreModules() // 加载核心模块,加载 CoreUObject 这个 Module
InitializeRenderingCVarsCaching(); // 渲染相关后面再看
FOodleDataCompression::StartupPreInit(); // OODLE 初始化,这是一个UE的压缩模块
LoadPreInitModules() // 加载 PreInit的模块,比如Engine, Renderer, Landscape, RenderCore 等。。。
FCsvProfiler::Get()->Init(); // CSV分析器
AppLifetimeEventCapture::Init(); // 程序生命周期事件捕获

AppInit

    // Start the application
    {
        SCOPED_BOOT_TIMING("AppInit");
        if (!AppInit())
        {
            return 1;
        }
    }

bool FEngineLoop::AppInit( ) 位于LaunchEngineLoop。是一个很重要的调用流程

  • BeginInitTextLocalization(); // 初始化文本本地化

  • FPlatformMisc::PlatformPreInit(); // 平台特定的初始化内容

  • FPlatformApplicationMisc::PreInit(); // 平台对于预初始化

  • FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir(); // 看到第三次了

  • IFileManager::Get().ProcessCommandLineOptions(); // 处理命令行

  • 内存相关设置

  • 如果有命令 BUILDMACHINE 添加命令 FCommandLine::AddToSubprocessCommandline(TEXT(" -buildmachine"));。可以学一下

  • IFileManager::Get().MakeDirectory( *FPaths::ProjectLogDir(), true ); // 创建LOG保存路径文件夹

  • FCString::Strcpy(MiniDumpFilenameW, IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(FString::Printf(TEXT("%sunreal-v%i-%s.dmp"), FPaths::ProjectLogDir(), FEngineVersion::Current().GetChangelist(), FDateTime::Now().ToString())));

    • 构造一个dump文件名字
  • FPlatformOutputDevices::SetupOutputDevices(); // Init logging to disk

  • FConfigCacheIni::InitializeConfigSystem(); // 配置系统ini文件复制给全局变量,已经相关初始化

  • FModuleManager::Get().LoadModule("IoStoreOnDemand");

  • FTraceAuxiliary::InitializePresets(FCommandLine::Get()); // 最终初始化

FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::IniSystemReady);

IniSystemReady

  • // 开始加载 ELoadingPhase::EarliestPossible 这个阶段的插件了
    ProjectManager.LoadModulesForProject(ELoadingPhase::EarliestPossible)
    PluginManager.LoadModulesForEnabledPlugins(ELoadingPhase::EarliestPossible)

FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::EarliestPossiblePluginsLoaded);

EarliestPossiblePluginsLoaded

  • FPlatformStackWalk::Init(); // Now that configs have been initialized, setup stack walking options

  • CheckForPrintTimesOverride();

  • // 加载 ELoadingPhase::PostConfigInit 阶段的插件

    ProjectManager.LoadModulesForProject(ELoadingPhase::PostConfigInit)
    PluginManager.LoadModulesForEnabledPlugins(ELoadingPhase::PostConfigInit)

检查是否有插件需要更新

  • PreInitHMDDevice(); // 头显
  • FAutomationTestFramework::Get().SetForceSmokeTests(bForceSmokeTests); // 是否冒烟测试
  • FApp::InitializeSession();
  • FCoreDelegates::OnInit.Broadcast();

AppInit 结束

    // Start the application
    {
        SCOPED_BOOT_TIMING("AppInit");
        if (!AppInit())
        {
            return 1;
        }
    }

回到上一层从这里继续

  • NoLogThread 命令

  • SCOPED_BOOT_TIMING("GIOThreadPool->Create"); // 创建IO线程

  • GSystemSettings.Initialize(bHasEditorToken); // 初始化系统设置

  • UE::ConfigUtilities::ApplyCVarSettingsFromIni // 从ini读取一些渲染设置

  • UGameUserSettings::PreloadResolutionSettings(); // 预加载分辨率设置

  • Scalability::InitScalabilitySystem(); // 控制台变量数值变化的委托绑定

  • UDeviceProfileManager::InitializeCVarsForActiveDeviceProfile(); //

  • FConfigCacheIni::LoadConsoleVariablesFromINI(); // 从ini 读取配置变量

  • FPlatformMisc::PlatformInit(); FPlatformApplicationMisc::Init(); FPlatformMemory::Init(); // 平台杂项初始化,平台应用程序初始化,平台内存初始化

  • UE::DerivedData::IoStore::InitializeIoDispatcher(); // 初始化DerivedData模块的IoDispatcher

  • 如果上面有问题,这里就会强退了

    if (!FPlatformMisc::CommandLineCommands())
    {
        FPlatformMisc::RequestExit(false, TEXT("FEngineLoop::PreInitPreStartupScreen.CommandLineCommands"));
    }
  • InitGamePhys() // 初始化物理系统

  • FPlatformProcess::CleanShaderWorkingDir(); // 清除shader工作目录

  • InitEngineTextLocalization(); // 字体本地化

  • FSlateApplication::InitHighDPI(bForceEnableHighDPI); // 设置DPI

  • FAudioThread::SetUseThreadedAudio(bUseThreadedAudio); // 音频线程

  • FPlatformSplash::Show();

  • FSlateApplication::Create(); // 展示Splash,就是那个加载小框

这一小段基本都是渲染的。

  • RHIInit(bHasEditorToken); RHI
  • RenderUtilsInit();
  • InitializeShaderHashCache(); //
  • GetRendererModule(); // Cache the renderer module in the main thread
  • InitializeShaderTypes(); //

FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::ShaderTypesReady);

ShaderTypesReady

  • CompileGlobalShaderMap(false); // 编译全局shader map

  • CreateMoviePlayer(); // 播放电影,如果有

  • FPreLoadScreenManager::Create();

  • CurrentSlateApp.InitializeRenderer(SlateRendererSharedRef); // 初始化slate渲染 比如D3D

  • FEngineFontServices::Create();

  • ELoadingPhase::PostSplashScreen 这个阶段的插件

保存一些变量

    // Save PreInitContext
    PreInitContext.bDumpEarlyConfigReads = bDumpEarlyConfigReads;
    PreInitContext.bDumpEarlyPakFileReads = bDumpEarlyPakFileReads;
    PreInitContext.bForceQuitAfterEarlyReads = bForceQuitAfterEarlyReads;
    PreInitContext.bWithConfigPatching = bWithConfigPatching;
    PreInitContext.bHasEditorToken = bHasEditorToken;

PreInitPostStartupScreen

// 屏幕加载

  • GetMoviePlayer()->SetupLoadingScreenFromIni(); // 从ini中加载屏幕的电影配置到内存
  • ELoadingPhase::PreEarlyLoadingScreen 加载这个阶段的插件
  • if GetMoviePlayer()->HasEarlyStartupMovie()
    • GetMoviePlayer()->PlayEarlyStartupMovies(); // 有动画要播放咯
    • else FPreLoadScreenManager 。。。// 一些是否有加载进度之类的逻辑

// pak 和 shader

  • SCOPED_BOOT_TIMING("MountPaksAfterEarlyStartupScreen"); // pak 相关

  • SCOPED_BOOT_TIMING("FShaderCodeLibrary::OpenLibrary"); // 全局 Shader 加载

    • Handle opening shader library after our EarlyLoadScreen
  • FShaderPipelineCache::OpenPipelineFileCache(GMaxRHIShaderPlatform);

  • InitGameTextLocalization();

  • FModuleManager::Get().LoadModule("AssetRegistry"); // AssetRegistry

  • IPackageResourceManager::Initialize(); // 资源管理器,后面读取Object依赖这个

  • IBulkDataRegistry::Initialize(); // 数据表,大块数据结构

FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::PreObjectSystemReady);

PreObjectSystemReady

  • ProcessNewlyLoadedUObjects // UObject 注册

  • UE::Virtualization::Initialize(UE::Virtualization::EInitializationFlags::None);

  • FTextLocalizationManager::Get().WaitForAsyncTasks();

FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::ObjectSystemReady);

ObjectSystemReady

  • PIEPreviewDeviceProfileSelector // 不同平台的一些渲染配置

  • 初始化默认材质

    UMaterialInterface::InitDefaultMaterials();
    UMaterialInterface::AssertDefaultMaterialsExist();
    UMaterialInterface::AssertDefaultMaterialsPostLoaded();
  • IStreamingManager::Get(); // 纹理流系统

  • FModuleManager::Get().StartProcessingNewlyLoadedObjects(); // 通知模块管理,现在可以处理 newly-loaded UObjects 了

  • GUObjectArray.DisableDisregardForGC() // 设置GC优化

// 加载核心模块了,里面包括Core, Networking, LiveCoding, UnrealEd, Slate 等

  • LoadStartupCoreModules

    • 可以会议下,上面 StatSystemReady的后面的,LoadPreInitModules() 加载预初始化模块
  • ELoadingPhase::PreLoadingScreen 加载这个阶段的插件

  • GetMoviePlayer()->Initialize 初始化电影播放器

// 渲染

  • PostInitRHI();

  • StartRenderingThread(); // 开启渲染线程了

  • GetMoviePlayer()->PlayMovie(); // 播放电影

  • FUnrealEdMisc::Get().MountTemplateSharedPaths(); // 安装共享shader路径

  • LoadStartupModules // 又是加载模块,这次是对所有 PreDefault,Default,PostDefault 阶段的插件做加载了。

  • UOnlineEngineInterface

  • ELoadingPhase::PostEngineInit 阶段的插件

  • GetHighResScreenshotConfig().Init(); // 高清截图配置初始化

  • PrecacheComputePipelineStatesForGlobalShaders

  • FAutomationTestFramework::Get().RunSmokeTests();

EditorInit

》EditorInit

调用位置大概在这里

int32 GuardedMain( const TCHAR* CmdLine )
{

    int32 ErrorLevel = EnginePreInit( CmdLine );

    ...

    ErrorLevel = EditorInit(GEngineLoop);

}

int32 EditorInit( IEngineLoop& EngineLoop )

  • GDebugToolExec = new FDebugToolExec; // debug工具

int32 ErrorLevel = EngineLoop.Init();

FEngineLoop::Init

》EditorInit》FEngineLoop::Init

    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
    }

这一段可以看到,
GEngine,GEditor,GUnrealEd 的对象初始化
编辑器模式下默认是 UUnrealEdEngine,游戏模式下默认是 GameEngine。
并且 GConfig 的 配置,可以设置我们自己的引擎类覆盖上去

  • GetMoviePlayer()->PassLoadingScreenWindowBackToGame(); // 将加载的窗口丢给game,GameEngine->GameViewportWindow = MainWindow;

  • FPreLoadScreenManager::Get()->PassPreLoadScreenWindowBackToGame();

  • GEngine->ParseCommandline(); // 解析 noailogging,enableailogging,MaxAlloc 命令

  • InitTime(); // 时间相关的变量初始化

GEngine->Init(this);
根据上面,这里有可能的是
void UGameEngine::Init(IEngineLoop InEngineLoop)
void UUnrealEdEngine::Init(IEngineLoop
InEngineLoop)

继承关系
class UUnrealEdEngine : public UEditorEngine
class UEditorEngine : public UEngine

class UGameEngine : : public UEngine

我们看更复杂的编辑器的版本。所以这里是 UUnrealEdEngine::Init

UUnrealEdEngine::Init

》EditorInit》FEngineLoop::Init》UUnrealEdEngine::Init

void UUnrealEdEngine::Init(IEngineLoop* InEngineLoop)
一开始就Super::Init(InEngineLoop); // UEditorEngine::Init

UEditorEngine::Init

》EditorInit》FEngineLoop::Init》UUnrealEdEngine::Init》UEditorEngine::Init

void UEditorEngine::Init(IEngineLoop* InEngineLoop)

  • RF_ClassDefaultObject // 检查对象是否不是类的默认对象
  • 绑定了一堆代理,PIE流程相关,level add remove to word, on asset loaded 之类的
  • GEditor = this;
  • InitEditor(InEngineLoop);
UEditorEngine::InitEditor

》EditorInit》FEngineLoop::Init》UUnrealEdEngine::Init》UEditorEngine::Init》UEditorEngine::InitEditor

void UEditorEngine::InitEditor(IEngineLoop* InEngineLoop)

  • InitDerivedDataBuildWorkers(); // 初始化派生数据构建的工作线程?
  • UEngine::Init(InEngineLoop);
UEngine::Init

》EditorInit》FEngineLoop::Init》UUnrealEdEngine::Init》UEditorEngine::Init》UEditorEngine::InitEditor》UEngine::Init
void UEngine::Init(IEngineLoop* InEngineLoop)

// 一些错误处理初始化

  • ErrorsAndWarningsCollector = MakePimpl(); // 一个错误警告收集器

  • if(!FEngineBuildSettings::IsInternalBuild())

    • 遍历所有enable的插件,把插件的定义json取出来
    • FGenericCrashContext::AddPlugin(DescStr); 丢给这个,用于后续奔溃报告
  • FPlatformMisc::SetMemoryWarningHandler(EngineMemoryWarningHandler); // 内存警告处理程序

  • EngineLoop = InEngineLoop;

  • RegisterEngineElements();

    • 加载 TypedElementFramework,TypedElementRuntime 模块
    • RegisterEngineObjectElements
    • RegisterEngineActorElements
    • RegisterEngineComponentElements
    • RegisterEngineSMInstanceElements
    • OnRegisterEngineElementsDelegate.Broadcast();
  • FURL::StaticInit();

  • EngineSubsystemCollection.Initialize(this); // 所有UDynamicSubsystem子类的subsystem初始化

  • UGameMapsSettings::SetGameDefaultMap(MapString); // 默认地图名字的处理

  • InitializeRunningAverageDeltaTime(); // 初始化平均帧时间

  • AddToRoot();

  • InitializeHMDDevice InitializeEyeTrackingDevice // 头显和眼动追踪

  • EnableScreenSaver( false ); // 禁用屏保

// slate声音和测试

  • CurrentSlateApp.InitializeSound( TSharedRef( new FSlateSoundDevice() ) );

  • RestoreSlateTestSuite();

  • FObjectThumbnail::SetThumbnailCompressors // 缩略图的压缩解压器

  • LoadObject(UEngine::StaticClass()->GetOuter(), *UEngine::StaticClass()->GetName(), NULL, LOAD_Quiet|LOAD_NoWarn, NULL ); // 加载引擎默认对象

  • LoadConfig

  • SetConfiguredProcessLimits // 进程限制配置

  • FModuleManager::Get().LoadModule("WorldPartitionEditor");

  • InitializeObjectReferences(); //

  • FBlueprintCoreDelegates::SetScriptMaximumLoopIterations( GEngine->MaximumLoopIterationCount ); // 蓝图最大循环

  • SetNearClipPlaneGlobals(NearClipPlane); // 全局近景层面

  • UTextRenderComponent::InitializeMIDCache(); // 初始化文本渲染组件的材质实例动态缓存

  • GReadOnlyCVARCache.Init()

// !!!重要 World的创建,链接到其他笔记

    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();
    }
  • const UGeneralProjectSettings& ProjectSettings = *GetDefault(); // 加载项目通用设置

  • FNetworkVersion::SetProjectVersion(*ProjectSettings.ProjectVersion); // 设置网络版本

  • 命令处理,垂直同步命令

  • 一些代理

  • GetBufferVisualizationData().Initialize();

  • GetNaniteVisualizationData().Initialize();

  • GetVirtualShadowMapVisualizationData().Initialize();

  • InitializePortalServices(); // 门户服务

  • FEngineAnalytics::Initialize(); // 引擎分析工具

  • InitializeAudioDeviceManager(); // 音频设备管理

  • 加载一些模块

  • AssetManager->FinishInitialLoading();

  • RecordHMDAnalytics();

  • InitThreadConfig();

UEngine::Init end

》EditorInit》FEngineLoop::Init》UUnrealEdEngine::Init》UEditorEngine::Init》UEditorEngine::InitEditor
回退

void UEditorEngine::InitEditor(IEngineLoop* InEngineLoop)
{
    // Call base.
    UEngine::Init(InEngineLoop);
    // ...
InitEditor end

》EditorInit》FEngineLoop::Init》UUnrealEdEngine::Init》UEditorEngine::Init
回退到这一层继续下去

void UEditorEngine::Init(IEngineLoop* InEngineLoop)
{

    InitEditor(InEngineLoop);

    // 。。。
}
  • LoadEditorFeatureLevel(); // 编辑器特性级别

  • Trans = CreateTrans(); // 事务性操作,undo redo 是如何设计的

  • 加载了一堆模块

  • GameUserSettings Load and Apply

  • Cleanse( false, 0, NSLOCTEXT("UnrealEd", "Startup", "Startup") ); // 清理垃圾

  • FEditorCommandLineUtils::ProcessEditorCommands(FCommandLine::Get()); // 编辑器命令

  • CheckForMissingAdvancedRenderingRequirements // 检查缺失的高级渲染要求

  • bIsInitialized = true;

UEditorEngine::Init end

》EditorInit》FEngineLoop::Init》UUnrealEdEngine::Init

回到这一层

void UUnrealEdEngine::Init(IEngineLoop* InEngineLoop)
{
    Super::Init(InEngineLoop);
    // ...
}
  • RegisterEditorElements(); // 注册编辑器元素。

    • RegisterEditorObjectElements();
    • RegisterEditorActorElements();
    • RegisterEditorComponentElements();
    • RegisterEditorSMInstanceElements();
  • RebuildTemplateMapData(); // 重建模板映射数据,不知道拿来干啥的

  • ValidateFreeDiskSpace(); // 是否有空闲磁盘空间

  • FSourceCodeNavigation::Initialize(); // 源码导航,就是编辑器点一下能跳去VS的那个功能

  • PackageAutoSaver->LoadRestoreFile(); // 自动保存

  • PerformanceMonitor = new FPerformanceMonitor;

  • VerifyMountPointWritePermission(*RootPath); // 验证Content目录有读写权限,非常暴力,write一个文件,成功就有权限

  • 一堆代理

  • FSnappingUtils::InitEditorSnappingTools(); // ViewportSnapping 视口对齐工具

TODO 。。。

UUnrealEdEngine::Init End

》EditorInit》FEngineLoop::Init》
回退到这里继续

int32 FEngineLoop::Init()
{
    {
        SCOPED_BOOT_TIMING("GEngine->Init");
        GEngine->Init(this);
    }
    //。。。
}
  • SessionServices

  • EngineService = new FEngineService(); // 引擎服务实例,初始化消息传递,并指定了处理不同类型消息的回调函数和接收线程

  • ELoadingPhase::PostEngineInit 这个阶段的插件

  • GEngine->Start(); // 子类空接口

  • 引擎节目加载。等待电影结束

  • FTraceAuxiliary::EnableChannels();

  • Media Module

  • 加载一些模块。。。

  • GIsRunning = true;

  • FViewport::SetGameRenderingEnabled(true, 3); // 渲染启用,并隐藏3帧数

  • FThreadHeartBeat::Get().Start(); // 心跳其他线程是否活着

  • FShaderPipelineCache::PauseBatching(); // 暂停shader缓存处理

  • FCoreDelegates::OnFEngineLoopInitComplete.Broadcast();

  • FShaderPipelineCache::ResumeBatching(); // 恢复shader缓存处理

  • 初始化分析器

FDelayedAutoRegisterHelper::RunAndClearDelayedAutoRegisterDelegates(EDelayedRegisterRunPhase::EndOfEngineInit);
上面提到的枚举的最后一个阶段。

EDelayedRegisterRunPhase::EndOfEngineInit

FEngineLoop::Init End

》EditorInit》
回退到这里

int32 EditorInit( IEngineLoop& EngineLoop )
{
    // ...
    int32 ErrorLevel = EngineLoop.Init();
    if( ErrorLevel != 0 )
    {
        FPlatformSplash::Hide();
        return 0;
    }

    //。。。
  • if ( FEngineAnalytics::IsAvailable() )

    • FEngineAnalytics::GetProvider().RecordEvent(TEXT("Editor.ProgramStarted"), EventAttributes);
    • Let the analytics know that the editor has started
  • FActorFolders::Get(); // Set up the actor folders singleton, 绑定actor文件变化的事件,actor文件上下文系统

  • FUnrealEdMisc::Get().OnInit();

    • void FUnrealEdMisc::OnInit() // 杂项初始化
  • FEditorDirectories::Get().LoadLastDirectories();

  • FDesktopPlatformModule::Get()->GetTargetsForCurrentProject(); // 缓存当前项目可用图标

  • FPlatformSplash::Hide(); // ok了,隐藏加载框了

// Main Frame

  • FModuleManager::LoadModuleChecked(TEXT("MainFrame"));

  • MainFrameModule.CreateDefaultMainFrame(bStartImmersive, bStartPIE);

  • CheckAndMaybeGoToVRModeInternal(bIsImmersive); // check VR

  • 否需要更新游戏项目文件到当前版本, 检查并警告项目文件名是否有效

// =================== EDITOR STARTUP FINISHED ===================

  • Stat tracking
  • FModuleManager::LoadModuleChecked(TEXT("HierarchicalLODOutliner")); // 加载模块-层次结构LOD大纲视图
  • we have to remove invalid keys. 移除无效按键

EngineInit

FEngineLoop::Init

和上面的是同一个函数,在GIsEditor的地方做了区分

EngineTick

    // 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();
        }
    }

void FEngineLoop::Tick() 到了这里

  • FScopedSampleMallocChurn ChurnTracker; // 非发布,非测试。追踪内存分配和释放情况

  • LLM(FLowLevelMemTracker::Get().UpdateStatsPerFrame()); // let the low level mem tracker pump once a frame to update states 低内存追踪器每帧更新

  • BeginExitIfRequested(); // 是否有退出请求

  • TRACE_CPUPROFILER_EVENT_SCOPE(HeartBeat); // 用于在CPU性能分析器中记录心跳事件

  • FThreadHeartBeat::Get().HeartBeat(true); // 给诊断线程发一个心跳

  • FGameThreadHitchHeartBeat::Get().FrameStart(); // 游戏线程帧开始,卡顿检测

  • FPlatformMisc::TickHotfixables(); // tick 热修复,

  • TickRenderingTickables(); // 遍历渲染线程的tickable 对象,执行tick

  • FMoviePlayerProxy::BlockingForceFinished(); // 卡住,等待电影播放完才能继续

  • ActiveProfiler->FrameSync(); // 和外部的分析器协同,让外部分析器能够停止虚幻帧,大概这么一个东西

  • FPlatformMisc::BeginNamedEventFrame(); // 开始一个命名事件帧数,用于分析工具记录事件用

  • uint64 CurrentFrameCounter = GFrameCounter; // 当前帧计数

  • TraceNamedEventsToggler.Update(bTraceCpuChannelEnabled && UE::Trace::IsTracing()); // 更新自动命名事件开关的状态

  • IConsoleManager::Get().CallAllConsoleVariableSinks(); // 更新控制台变量

        #if WITH_PROFILEGPU && !UE_BUILD_SHIPPING
            // Issue the measurement of the execution time of a basic LongGPUTask unit on the very first frame
            // The results will be retrived on the first call of IssueScalableLongGPUTask
            if (GFrameCounter == 0 && IsFeatureLevelSupported(GMaxRHIShaderPlatform, ERHIFeatureLevel::SM5) && FApp::CanEverRender())
            {
                FlushRenderingCommands();

                ENQUEUE_RENDER_COMMAND(MeasureLongGPUTaskExecutionTimeCmd)(
                    [](FRHICommandListImmediate& RHICmdList)
                    {
                        MeasureLongGPUTaskExecutionTime(RHICmdList);
                    });
            }
        #endif

如果第一帧,特性级别支持,
FlushRenderingCommands(); // 刷新渲染命令列表,里面围栏等待渲染完成
ENQUEUE_RENDER_COMMAND 里面的东西在渲染线程执行。
MeasureLongGPUTaskExecutionTime(RHICmdList); // 测量长时间的GPU任务执行时间,命令添加到渲染命令队列中。

  • FCoreDelegates::OnBeginFrame.Broadcast();

  • GLog->FlushThreadedLogs(EOutputDeviceRedirectorFlushOptions::Async); // 刷新由其他线程缓存的LOG

  • GEngine->UpdateTimeAndHandleMaxTickRate(); // 更新时间,处理最大tick帧率。

  • GEngine->SetSimulationLatencyMarkerStart(CurrentFrameCounter); // 设置模拟延迟标记

// 渲染帧相关

  • ENQUEUE_RENDER_COMMAND: BeginFrameRenderThread(RHICmdList, CurrentFrameCounter); // 渲染线程调用开始帧
  • ENQUEUE_RENDER_COMMAND: for all Scene => Scene->StartFrame(); // 渲染线程,场景开始帧
  • GEngine->EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::BeginFrame); // 动态分辨率的开始帧

// 监视统计

  • GEngine->TickPerformanceMonitoring( FApp::GetDeltaTime() ); // 性能监视器的 tick

  • ResetAsyncLoadingStats // 重置异步加载的统计数据

  • GMalloc->UpdateStats(); // 内存统计

  • FStats::AdvanceFrame( false, FStats::FOnAdvanceRenderingThreadStats::CreateStatic( &AdvanceRenderingThreadStatsGT ) );

    • 推进帧状态,并执行 AdvanceRenderingThreadStatsGT
    • 会添加渲染命令 AdvanceRenderingThreadStats
  • CalculateFPSTimings(); // FPS

  • ENQUEUE_RENDER_COMMAND

    • FDeferredUpdateResource::ResetNeedsUpdate(); // 重置需要延迟更新的资源
    • FlushPendingDeleteRHIResources_RenderThread(); // 刷新待删除的渲染线程RHI资源
  • bIdleMode = ShouldUseIdleMode();

    • if bIdleMode: FPlatformProcess::Sleep(.1f); // 空闲模式sleep
  • GNewWorldToMetersScale // 世界到米的缩放

  • FPlatformFileManager::Get().TickActivePlatformFile(); // 更新活动平台文件,

  • FCoreDelegates::OnSamplingInput.Broadcast(); // 采样输入事件

  • SlateApp.PollGameDeviceState() // 处理累计的slate输入

  • FRDGBuilder::InitResourceDump(); // RDG初始化,这个是渲染相关的

  • GEngine->Tick(FApp::GetDeltaTime(), bIdleMode);

UUnrealEdEngine::Tick

void UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode)
{
    Super::Tick( DeltaSeconds, bIdleMode ); // void UEditorEngine::Tick( float DeltaSeconds, bool bIdleMode )
    // 。。。
}

UEditorEngine::Tick

UUnrealEdEngine::Tick 》UEditorEngine::Tick
void UEditorEngine::Tick( float DeltaSeconds, bool bIdleMode )

  • FCoreUObjectDelegates::ObjectsModifiedThisFrame.Empty(); // 清空本帧修改对象的列表

  • WorldList.Reserve(WorldList.Num() + 10); // 预留空间给这一帧要创建的World

  • IConsoleManager::Get().CallAllConsoleVariableSinks(); // 控制台变量的回调函数,(~的控制台,的变量,有些被修改了,要回调他)

  • FRemoteConfigAsyncTaskManager::Get()->Tick(); // 更新远程配置IO管理器

  • CleanupGameViewport(); // 清除已关闭的游戏视口

  • If all viewports closed, close the current play level.

    • EndPlayMap();
  • EditorContext.World()->ConditionallyBuildStreamingData(); // 根据条件重新构建流式数据,把需要加载的关卡加到流式管理器中

  • TimerManager->Tick(DeltaSeconds);

  • StaticTick(DeltaSeconds, !!GAsyncLoadingUseFullTimeLimit, GAsyncLoadingTimeLimit / 1000.f);

    • ProcessAsyncLoading(true, bUseFullTimeLimit, AsyncLoadingTime);
      • Serializes a bit of data each frame with a soft time limit. The function is designed to be able to fully load a package in a single pass given sufficient time.
    • Set name table stats.

// 分析工具

  • FEngineAnalytics::Tick(DeltaSeconds); // 分析工具tick

  • FStudioAnalytics::Tick(DeltaSeconds); // 啥也没有

  • 找有音频焦点的视口。可能会 FApp::SetVolumeMultiplier 设置声音,是否播放。

  • FTickableEditorObject::TickObjects( DeltaSeconds );

  • FAssetRegistryModule::TickAssetRegistry(DeltaSeconds); // 资产注册表,编辑器设置asset manager 里面可以设置。primary asset types to scan

  • SourceCodeAccessModule.GetAccessor().Tick(DeltaSeconds); // 点击跳到vs代码的那个模块tick

  • DirectoryWatcherModule.Get()->Tick(DeltaSeconds); // 目录监控的tick

  • EditorContext.World()->Tick(TickType, DeltaSeconds); // 编辑器 World 的Tick

// Perform editor level streaming previs if no PIE session is currently in progress.
更新流关卡的显示状态

  • 遍历所有视口
    • 对于 ViewportClient->IsPerspective() 透视视口和 bLevelStreamingVolumePrevis 开启了流式加载体积预览的视口
    • 计算视点是否在流式加载体积内,并根据计算结果更新流式加载级别的可见状态
  • EditorContext.World()->HasStreamingLevelsToConsider()
    • EditorContext.World()->UpdateLevelStreaming()

// PIE & SIE

  • bool bToggledBetweenPIEandSIE = bIsToggleBetweenPIEandSIEQueued; // 上一帧是否有PIE, SIE 状态

  • if (PlaySessionRequest.IsSet())

    • StartQueuedPlaySessionRequest(); // SIE 下点物体,右键从此处运行
  • bIsToggleBetweenPIEandSIEQueued

    • ToggleBetweenPIEandSIE(); // 切换PIE 和 SIE
  • AddPendingLateJoinClient(); // play as client 模式下,使用 add client 快捷键(keyboard shortcuts)添加新的客户端会跑到这,快捷键需要搜索add client设置一下

  • !bFirstTick

    • USkyLightComponent::UpdateSkyCaptureContents(EditorContext.World()); // 反射捕获和天光
    • UReflectionCaptureComponent::UpdateReflectionCaptureContents(EditorContext.World(), nullptr, false, false, bInsideTick); // 更新天光捕获内容
  • EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::BeginFrame); // 动态分辨率开始帧事件

  • if( FSlateThrottleManager::Get().IsAllowingExpensiveTasks() )

    • TArray<FWorldContext*> LocalPieContextPtrs; // 维护一个 LocalPieContextPtrs,大概就是找到所有PIE的World
    • 更新 bMovieSequenceTickWillBeHandled // 收集每一个World是否需要更新多媒体的Tick
  • MediaModule->TickPreEngine(); // 多媒体Tick,如果需要

  • *for (FWorldContext PieContextPtr : LocalPieContextPtrs) // 对所有PIE的World,Tick World, GameViewport 等等,具体一点**

    • TickWorldTravel(PieContext, TickDeltaSeconds); // Tick all travel and Pending NetGames (Seamless, server, client)
    • UpdateTransitionType(PlayWorld); // Updates 'connecting' message in PIE network games
    • PlayWorld->UpdateLevelStreaming(); // DS 更新关卡流
    • GameViewport->SetDropDetail(TickDeltaSeconds); // 根据帧率降低细节
    • PieContext.World()->Tick( LEVELTICK_All, TickDeltaSeconds ); // UWorld Tick
    • 非第一次tick,更新天光,反射
    • GameViewport->Tick(TickDeltaSeconds); // GameViewport Tick
  • FTickableGameObject::TickObjects(nullptr, TickType, false, DeltaSeconds);

  • MediaModule->TickPostEngine(); // 多媒体tick

  • GPlayInEditorID = INDEX_NONE;

  • CleanupGameViewport(); // 清理已经关闭的 Viewport

  • // If all viewports closed, close the current play level. 如果所有viewport关闭,就关闭关卡

    • EndPlayMap();
  • EditorWorldExtensionsManager->Tick( DeltaSeconds ); // 更新所有编辑器的扩展

  • AllViewportClients 对所有客户端视口做tick

  • !bIsMouseOverAnyLevelViewport // 鼠标是否悬停在关卡上

    • FLevelEditorViewportClient::ClearHoverFromObjects(); // 没有就清除悬停的效果,拖拽窗口悬停在其他窗口上的一个显示效果
  • EditorContext.World()->CommitModelSurfaces(); // 提交BSP模型的修改

  • ForEachObjectOfClass(UWorld::StaticClass(), [&WorldsToEOFUpdate](UObject* WorldObj)

    • WorldsToEOFUpdate.Add(World); // 遍历World,是否需要延迟就的末尾帧需要更新
  • World->SendAllEndOfFrameUpdates(); // 然后更新,把延迟更新的组件发送到渲染线程

===================

渲染相关

  • const bool bAllWindowsHidden = !bHasFocus && AreAllWindowsHidden(); // 编辑器窗口是否被隐藏,比如运行的时候

  • FPixelInspectorModule& PixelInspectorModule = FModuleManager::LoadModuleChecked(TEXT("PixelInspectorModule")); // tools->debug->pixel inspector 可以找到这个像素查看器工具

  • // Render view parents, then view children. GCurrentLevelEditingViewportClient 这个是否要被渲染

    • UpdateSingleViewportClient
  • for (int32 bRenderingChildren = 0; bRenderingChildren < 2; bRenderingChildren++)

    • UpdateSingleViewportClient
  • ENQUEUE_RENDER_COMMAND RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources); // 让渲染线程马上刷新RHI资源

  • if (!bAllWindowsHidden || bRunDrawWithEditorHidden)

    • GetRendererModule().PostRenderAllViewports();
      • ENQUEUE_RENDER_COMMAND CoarseMeshSM->UpdateResourceStates(); // 更新资源状态
  • ISourceControlModule::Get().Tick();

  • ILocalizationServiceModule::Get().Tick();

  • IsAllowingExpensiveTasks

    • 遍历所有PIE World
    • GameViewport->Viewport->Draw()
  • IStreamingManager::Get().Tick(DeltaSeconds);

  • AudioDeviceManager // 更新音频驱动

  • EditorContext.World()->UpdateConstraintActors(); // 物理约束角色

  • ENQUEUE_RENDER_COMMAND

    • GRenderingRealtimeClock.Tick(DeltaTime); 更新渲染时钟
    • GRenderTargetPool.TickPoolElements(); 渲染目标池
    • FRDGBuilder::TickPoolElements();
    • ICustomResourcePool::TickPoolElements(RHICmdList); // 资源池

// 资产分析和性能分析

  • FUnrealEdMisc::Get().TickAssetAnalytics();

  • FUnrealEdMisc::Get().TickPerformanceAnalytics();

  • BroadcastPostEditorTick(DeltaSeconds); // 事件

UEditorEngine::Tick End

UUnrealEdEngine::Tick 》

void UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode)
{
    Super::Tick( DeltaSeconds, bIdleMode ); // void UEditorEngine::Tick( float DeltaSeconds, bool bIdleMode )
    // 。。。
}

回到这里

  • PackageAutoSaver->UpdateAutoSaveCount(DeltaSeconds); // 根据是否有耗时任务等等情况,更新自动保存技术

  • PackageAutoSaver->AttemptAutoSave(); // 尝试自动保存

  • AttemptModifiedPackageNotification(); // 尝试通知用户有修改的包需要签出和写入权限的警告

  • UpdateBuildLighting // 更新构建光照

UUnrealEdEngine::Tick End

FEngineLoop::Tick()

void FEngineLoop::Tick()
{

    GEngine->Tick(FApp::GetDeltaTime(), bIdleMode);

    // ...

}

回到这个位置继续往下

  • FPreLoadScreenManager // 电影相关
  • FAssetCompilingManager::Get().ProcessAsyncTasks(true); // FAssetCompilingManager 异步任务
  • FMoviePlayerProxy::BlockingForceFinished(); // 阻塞等待电影播完

Slate相关

  • ProcessLocalPlayerSlateOperations();

  • FSlateApplication::Get().Tick(ESlateTickType::PlatformAndInput);

  • // process concurrent Slate tasks

  • ENQUEUE_RENDER_COMMAND

    • FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly);
  • ClearPendingStatGroups // 则清除所有待处理的统计组通知

// 自动化模块,自动化测试

  • FModuleManager::GetModuleChecked(AutomationController).Tick();
  • FModuleManager::GetModuleChecked(AutomationWorkerModuleName).Tick();

RHITick( FApp::GetDeltaTime() ); // Update RHI.

void FD3D12DynamicRHI::RHITick(float DeltaTime)
{
    check(IsInGameThread());

    // Check if any swap chains have been invalidated.
    auto& Viewports = GetAdapter().GetViewports();
    for (int32 ViewportIndex = 0; ViewportIndex < Viewports.Num(); ViewportIndex++)
    {
        Viewports[ViewportIndex]->ConditionalResetSwapChain(false);
    }
}

更换交换链

  • GEngine->SetSimulationLatencyMarkerEnd(CurrentFrameCounter); // 设置模拟延迟结束

  • GFrameCounter++;

  • ENQUEUE_RENDER_COMMAND GFrameCounterRenderThread = CurrentFrameCounter; // 帧数传给渲染线程

  • if (GFrameCounter > 6)

    • TotalTickTime += FApp::GetDeltaTime();
  • PendingCleanupObjects = GetPendingCleanupObjects(); // 下一帧需要清理的对象

  • 是否有命令 r.OneFrameThreadLag

    • 有就用 static FFrameEndSync FrameEndSync; 同步渲染线程和主线程
  • DeleteLoaders(); // destroy all linkers pending delete

  • FTSTicker::GetCoreTicker().Tick(FApp::GetDeltaTime());

  • FThreadManager::Get().Tick();

  • GEngine->TickDeferredCommands();

  • // tick media framework

  • FCoreDelegates::OnEndFrame.Broadcast();

  • FRDGBuilder::EndResourceDump(); // 结束RDG 资源存储

  • GEngine->EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::EndFrame); // 动态分辨率结束事件

  • ENQUEUE_RENDER_COMMAND EndFrameRenderThread(RHICmdList, CurrentFrameCounter);

上一篇
下一篇