【UE4】虚幻引擎运行流程

前言

  • 当前版本:4.26.2

  • 本文原创

  • 说明:有些名词保持英文可能更容易理解

  • 目录

    • 游戏流程总览

    • 各个平台启动引擎的入口函数

    • 引擎入口函数 GuardedMain()

    • EnginePreInit 引擎预初始化

    • EngineInit 引擎初始化

    • EngineLoop 引擎主循环

    • EngineExit 引擎退出


游戏流程总览

  • 解释启用引擎和运行游戏的过程。

  • 此处说明两种主要路径:编辑器路径、standalone 路径。

  • 事件的一般顺序为初始化引擎、创建并初始化 GameInstance、加载关卡,最后开始游戏。然而在部分被调用函数的准确排序和函数被调用的位置方面,standalone 模式和编辑器模式之间存在差异。以下流程图展示两个平行运行的路径,它们在游戏开始之前汇聚。
    image


各个平台启动引擎的入口函数

  • 代码路径:Engine\Source\Runtime\Launch\Private\

  • 作为入口函数,然后进入引擎的入口函数GuardedMain。如下图所示

    image


引擎入口函数 GuardedMain()

  • 代码路径:Runtime/Launch/Private/Launch.cpp

  • 执行引擎四大阶段的流程:EnginePreInit 、EngineInit 、EngineLoop、EngineExit 。执行流程图如下所示

    image


EnginePreInit 引擎预初始化

实际上调用 FEngineLoop::PreInit ,再调用 PreInitPreStartupScreenPreInitPostStartupScreen ,从而实现许多初始化和核心模块相关的事情

PreInitPreStartupScreen 里的执行流程

  • 先进行各种初始化,如解析command参数、trace、stat(性能分析工具)、FModuleManager、IFileManager、FPlatformFileManager、各种路径等

  • 加载CoreUObject模块,启动FTaskGraphInterface

  • 通过LoadPreInitModules()

    • 加载 Engine、Renderer、AnimGraphRuntime 模块

    • 通过 FPlatformApplicationMisc::LoadPreInitModules()加载 D3D12RHI、D3D11RHI、OpenGLDrv等模块(根据所在平台)

    • 加载 SlateRHIRenderer模块(非服务器下)

    • 加载 Landscape、RenderCore 模块

    • 加载 TextureCompressor、AudioEditor、AnimationModifiers 模块

  • 通过 FEngineLoop::AppInit() 进行

    • 本地化文本
    • Init logging to disk
    • init config system
      • 通过 FConfigCacheIni::LoadGlobalIniFile 加载Engine、Game、Input 配置文件
      • 其他配置文件还有:Editor、EditorPerProjectUserSettings、EditorSettings、EditorKeyBindings、EditorLayout、Lightmass、Scalability、Hardware、DeviceProfiles、GameUserSettings等
    • Load "asap" plugin modules,为项目和插件加载类型为 EarliestPossible 的模块
    • 检查项目和插件模块数据,是否需要编译
    • 为项目和插件加载类型为 PostConfigInit 的模块
    • 初始化头显 PreInitHMDDevice();
    • 打印初始化日志
    • 初始化其他系统 FCoreDelegates::OnInit.Broadcast();
  • 设置游戏主线程

  • 线程池初始化(如果支持多线程)

  • 异步IO系统初始化(如果支持多线程)

  • 系统设置system settings和 cvar 初始化,cvar从 ini 文件获取,含渲染参数、分辨率、窗口模式等

  • Scalability系统初始化 InitScalabilitySystem()

  • 渲染线程 UseRenderThread()

  • 平台特定的初始化,如 FPlatformMisc::PlatformInit()FPlatformApplicationMisc::Init()FPlatformMemory::Init()

  • IPlatformFeaturesModule::Get()

  • 物理引擎初始化 InitGamePhys()

  • 引擎本地化初始化 InitEngineTextLocalization()

  • 开启音频线程 UseAudioThread()

  • 启动界面显示 FPlatformSplash::Show();

  • 平台应用程序创建 FSlateApplication::Create();服务器模式下EKeys::Initialize()、FCoreStyle::ResetToDefault();

  • RHI 初始化 RHIInit()

  • RenderUtils初始化 RenderUtilsInit()

  • FShaderCodeLibrary::InitForRuntime()

  • FShaderPipelineCache::Initialize()

  • Shader hash cache: InitializeShaderHashCache()

  • GetRendererModule

  • InitializeShaderTypes

  • CompileGlobalShaderMap

  • CreateMoviePlayer

  • etc

PreInitPostStartupScreen 里的执行流程

  • 播放启动动画 GetMoviePlayer()->SetupLoadingScreenFromIni()
  • 加载PreEarlyLoadingScreen类型的项目模块
    • 通过 FPreLoadScreenManager::Get()->Initialize() 调用 UGameEngine::CreateGameWindow(); 创建窗口
  • Pak 文件获取和挂载
  • Config 文件获取和参数读取
  • SlateRenderer 初始化
  • 加载 AssetRegistry 模块
  • UObject classes 注册和初始化
  • PIEPreviewDeviceProfileSelector
  • InitDefaultMaterials
  • Initialize the texture streaming system
  • 加载核心启动模块 LoadStartupCoreModules,包括
    • Core、Networking、Messaging
    • MRMesh、UnrealEd、EditorStyle、LandscapeEditorUtilities
    • SlateCore、Slate、SlateReflector、UMG
    • MessageLog、CollisionAnalyzer、FunctionalTesting
    • BehaviorTreeEditor、GameplayTasksEditor、StringTableEditor、VREditor、IntroTutorials、Blutility
    • Overlay、MediaAssets、ClothingSystemRuntimeNv、ClothingSystemEditor、PacketHandler、NetworkReplayStreaming
  • 通过LoadStartupModules 加载PreDefault、Default、PostDefault类型的模块
  • 加载 PreLoadingScreen 类型的模块
  • 为项目和插件加载 PostEngineInit 类型的模块
  • Online模块
  • etc

EngineInit 引擎初始化

执行GEngineLoop.Init() 。如果事编辑器会执行 EditorInit() ,本质上事执行 GEngineLoop.Init() 后再进行Editor 的一些资源加载

GEngineLoop.Init()

分析主要以Game非编辑器模式为主

  • 非编辑器模式:GEngine = NewObject<UEngine>(GetTransientPackage(), EngineClass);
    编辑器模式:GEngine = GEditor = GUnrealEd = NewObject<UUnrealEdEngine>(GetTransientPackage(), EngineClass);
  • GEngine->Init()
    • UGameEngine::Init() 非编辑器模式
      • UEngine::Init()
        • Subsystems 初始化 EngineSubsystemCollection.Initialize(this);
        • 头显设备初始化 InitializeHMDDevice();
        • 眼动追踪设备初始化 InitializeEyeTrackingDevice();
        • FSlateSoundDevice
        • 加载引擎类 LoadObject<UClass>(UEngine::StaticClass()->GetOuter()...
        • Engine.ini LoadConfig();
        • 初始化 Object 引用 InitializeObjectReferences();
        • 为编辑器 CreateNewWorldContext()
        • InitializeAudioDeviceManager();
        • networking 相关的一些东西
        • Initialise buffer visualization system data GetBufferVisualizationData().Initialize();
        • FEngineAnalytics::Initialize();
        • 加载引擎 runtime modules:ImageWriteQueue、StreamingPauseRendering、MovieScene、MovieSceneTracks、LevelSequence
        • Finish asset manager loading AssetManager->FinishInitialLoading();
        • RecordHMDAnalytics();
      • GetGameUserSettings()->LoadSettings();
        GetGameUserSettings()->ApplyNonResolutionSettings();
      • 创建 game instance
        GameInstance = NewObject<UGameInstance>(this, GameInstanceClass);
        GameInstance->InitializeStandalone(); —— CreateNewWorldContext
      • GameInstance->Init();
        • OnlineSession = NewObject<UOnlineSession>(this, SpawnClass);
          OnlineSession->RegisterOnlineDelegates();
        • SubsystemCollection.Initialize(this);
      • 初始化 viewport client
  • 加载 PostEngineInit 类型的模块
  • GEngine->Start()
    • UGameEngine::Start() 非编辑器模式
      • GameInstance->StartGameInstance();
      • const UGameMapsSettings* GameMapsSettings = GetDefault<UGameMapsSettings>();
      • const FString& DefaultMap = GameMapsSettings->GetGameDefaultMap();
      • Engine->Browse() 服务器相关
      • UEngine->LoadMap
        • 做一些清理工作,Unload 当前world、显示loading screen、清理 networking等
        • LoadPackage() ,加载level
        • 设置new world、CurrentWorld
          • WorldContext.SetCurrentWorld(NewWorld);
          • WorldContext.World()->WorldType = WorldContext.WorldType;
        • WorldContext.World()->InitWorld();
          • InitializeSubsystems()
          • FWorldDelegates::OnPreWorldInitialization.Broadcast(this, IVS);
          • AWorldSettings* WorldSettings = GetWorldSettings();
          • CreatePhysicsScene(WorldSettings);
          • CreateAISystem();
          • SetupParameterCollectionInstances();
          • Levels.Add( PersistentLevel );
          • 场景物理特性的一些设置,如重力、碰撞
        • 一些网络相关如 NetDriver、listen 等
        • WorldContext.World()->SetGameMode();
        • GShaderCompilingManager->ProcessAsyncResults()
        • WorldContext.World()->CreateAISystem();
        • WorldContext.World()->InitializeActorsForPlay();
        • FNavigationSystem::AddNavigationSystemToWorld()
        • SpawnPlayActor()
        • WorldContext.World()->BeginPlay();
          • GameMode->StartPlay();
            • GameState->HandleBeginPlay();
              • GetWorldSettings()->NotifyBeginPlay();
                为每个Actor调用BeginPlay(), FActorIterator It(World); It->DispatchBeginPlay(bFromLevelLoad);
              • GetWorldSettings()->NotifyMatchStarted();
          • GetAISystem()->StartPlay();
          • PhysicsScene->OnWorldBeginPlay();
        • WorldContext.World()->bWorldWasLoadedThisTick = true;
        • WorldContext.OwningGameInstance->LoadComplete();
  • WaitForEngineLoadingScreenToFinish()
  • WaitForMovieToFinish()
  • 加载模块: Media、AutomationWorker、AutomationController、ProfilerClient 、SequenceRecorder、SequenceRecorderSections
  • 线程心跳包 FThreadHeartBeat::Get().Start();
  • 外部分析器
  • etc

EditorInit()

调用GEngineLoop.Init() 后执行

  • 一些命令行、通知、消息、日志、ui等注册
  • 加载关卡 startup map
  • Process global shader results before we try to render anything
  • InitEngineAnalytics
  • etc

EngineLoop 引擎主循环

执行 FEngineLoop::Tick()

  • TickRenderingTickables();
    Make sure something is ticking the rendering tickables in -onethread mode to avoid leaks/bugs.
  • ActiveProfiler->FrameSync();
    外部分析器帧同步
  • FCoreDelegates::OnBeginFrame.Broadcast();
  • GLog->FlushThreadedLogs();
    刷新线程日志
  • FlushRenderingCommands()
    渲染命令更新
  • Scene->UpdateAllPrimitiveSceneInfos()
  • BeginFrameRenderThread()
    beginning of RHI frame
  • Scene->StartFrame();
  • FlushPendingDeleteRHIResources_RenderThread();
    渲染线程里一些需要帧处理的任务
  • 一些 stats 相关的事情
  • GEngine->Tick()
    main game engine tick (world, game objects, etc.)
    • log 更新
    • CleanupGameViewport();
    • 更新 subsystems
    • FEngineAnalytics、FStudioAnalytics 的 tick() 更新
    • Context.World()->Tick()
    • USkyLightComponent::UpdateSkyCaptureContents(Context.World());
      UReflectionCaptureComponent::UpdateReflectionCaptureContents(Context.World());
    • ULocalPlayer 处理
    • LevelStreaming 处理
    • FTickableGameObject::TickObjects()
    • MediaModule->TickPostEngine();
    • GameViewport->Tick(DeltaSeconds);
    • RedrawViewports();
      Render everything.
    • IStreamingManager、GameAudioDeviceManager 更新
    • 渲染线程命令相关更新 GRenderingRealtimeClock、GRenderTargetPool、FRDGBuilder、ICustomResourcePool
  • GShaderCompilingManager->ProcessAsyncResults(true, false);
  • GDistanceFieldAsyncQueue->ProcessAsyncTasks();
  • MediaModule->TickPreSlate();
    tick media framework
  • slate 的 tick 和 task
  • ReplicatedProperties 属性同步
  • FTaskGraphInterface 一些并行任务的处理
  • RHITick()
  • 帧计数、帧间隔
  • Objects 下一帧回收比标记
  • FrameEndSync 帧结束同步事件
  • EndFrameRenderThread()
  • FCoreDelegates::OnEndFrame.Broadcast();

EngineExit 引擎退出

  • 执行 GEngineLoop.Exit() ,主要是关闭和释放引擎的各个模块,具体可看源码

  • 最后调用 FEngineLoop::AppExit(); 退出应用

  • 源码

    
    void FEngineLoop::Exit()
    {
    	STAT_ADD_CUSTOMMESSAGE_NAME( STAT_NamedMarker, TEXT( "EngineLoop.Exit" ) );
    	TRACE_BOOKMARK(TEXT("EngineLoop.Exit"));
    
    	GIsRunning	= 0;
    	GLogConsole	= nullptr;
    
    	IInstallBundleManager::InstallBundleCompleteDelegate.RemoveAll(this);
    
    	// shutdown visual logger and flush all data
    #if ENABLE_VISUAL_LOG
    	FVisualLogger::Get().Shutdown();
    #endif
    
    	// Make sure we're not in the middle of loading something.
    	{
    		bool bFlushOnExit = true;
    		if (GConfig)
    		{
    			FBoolConfigValueHelper FlushStreamingOnExitHelper(TEXT("/Script/Engine.StreamingSettings"), TEXT("s.FlushStreamingOnExit"), GEngineIni);
    			bFlushOnExit = FlushStreamingOnExitHelper;			
    		}
    		if (bFlushOnExit)
    		{
    	FlushAsyncLoading();
    		}
    		else
    		{
    			CancelAsyncLoading();
    		}
    	}
    
    	// Block till all outstanding resource streaming requests are fulfilled.
    	if (!IStreamingManager::HasShutdown())
    	{
    		UTexture2D::CancelPendingTextureStreaming();
    		IStreamingManager::Get().BlockTillAllRequestsFinished();
    	}
    
    #if WITH_ENGINE
    	// shut down messaging
    	delete EngineService;
    	EngineService = nullptr;
    
    	if (SessionService.IsValid())
    	{
    		SessionService->Stop();
    		SessionService.Reset();
    	}
    
    	if (GDistanceFieldAsyncQueue)
    	{
    		GDistanceFieldAsyncQueue->Shutdown();
    		delete GDistanceFieldAsyncQueue;
    	}
    #endif // WITH_ENGINE
    
    	if ( GEngine != nullptr )
    	{
    		GEngine->ReleaseAudioDeviceManager();
    	}
    
    	if ( GEngine != nullptr )
    	{
    		GEngine->PreExit();
    	}
    
    	FAudioDeviceManager::Shutdown();
    
    	// close all windows
    	FSlateApplication::Shutdown();
    
    #if !UE_SERVER
    	if ( FEngineFontServices::IsInitialized() )
    	{
    		FEngineFontServices::Destroy();
    	}
    #endif
    
    #if WITH_EDITOR
    	// These module must be shut down first because other modules may try to access them during shutdown.
    	// Accessing these modules at shutdown causes instability since the object system will have been shut down and these modules uses uobjects internally.
    	FModuleManager::Get().UnloadModule("AssetTools", true);
    
    #endif // WITH_EDITOR
    	FModuleManager::Get().UnloadModule("WorldBrowser", true);
    	FModuleManager::Get().UnloadModule("AssetRegistry", true);
    
    #if !PLATFORM_ANDROID || PLATFORM_LUMIN 	// AppPreExit doesn't work on Android
    	AppPreExit();
    
    	TermGamePhys();
    	ParticleVertexFactoryPool_FreePool();
    #else
    	// AppPreExit() stops malloc profiler, do it here instead
    	MALLOC_PROFILER( GMalloc->Exec(nullptr, TEXT("MPROF STOP"), *GLog);	);
    #endif // !ANDROID
    
    	// Stop the rendering thread.
    	StopRenderingThread();
    	
    	// Disable the PSO cache
    	FShaderPipelineCache::Shutdown();
    
    	// Close shader code map, if any
    	FShaderCodeLibrary::Shutdown();
    
    #if !PLATFORM_ANDROID || PLATFORM_LUMIN // UnloadModules doesn't work on Android
    #if WITH_ENGINE
    	// Save the hot reload state
    	IHotReloadInterface* HotReload = IHotReloadInterface::GetPtr();
    	if(HotReload != nullptr)
    	{
    		HotReload->SaveConfig();
    	}
    #endif
    
    	// Unload all modules.  Note that this doesn't actually unload the module DLLs (that happens at
    	// process exit by the OS), but it does call ShutdownModule() on all loaded modules in the reverse
    	// order they were loaded in, so that systems can unregister and perform general clean up.
    	FModuleManager::Get().UnloadModulesAtShutdown();
    #endif // !ANDROID
    
    	IStreamingManager::Shutdown();
    
    	// Tear down the RHI.
    	RHIExitAndStopRHIThread();
    
    	DestroyMoviePlayer();
    
    	// Move earlier?
    #if STATS
    	FThreadStats::StopThread();
    #endif
    
    	FTaskGraphInterface::Shutdown();
    
    	FPlatformMisc::ShutdownTaggedStorage();
    
    	TRACE_CPUPROFILER_SHUTDOWN();
    }
    

参考

  • 本文原创地址 https://www.cnblogs.com/shiroe/p/15547566.html
  • 游戏流程总览
  • \UE_4.26\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp
  • \UE_4.26\Engine\Source\Runtime\Launch\Private\Launch.cpp
  • \UE_4.26\Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp
  • \UE_4.26\Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp
  • \UE_4.26\Engine\Source\Runtime\Engine\Private\GameEngine.cpp
  • \UE_4.26\Engine\Source\Editor\UnrealEd\Private\UnrealEdGlobals.cpp
  • \UE_4.26\Engine\Source\Runtime\Engine\Private\GameEngine.cpp
posted @ 2021-11-13 11:20  砥才人  阅读(3301)  评论(0编辑  收藏  举报