zoukankan      html  css  js  c++  java
  • UE4引擎主流程框架

    游戏启动初始化 

    ue4引擎通过传入不同命令行参数来充当很多角色。它可以是编辑器,用于集成、制作、编辑、管理游戏资源。可以是ds服务器,用于管理玩家连接,同步游戏状态,校验玩家操作等

    可以是游戏客户端,接受输入,呈现精美的画面,给玩家带来全方面的游戏体验。还也可以是commandlet工具,提供cook、打包、分发版本等自动化能力

    // 各种平台的main,流程大同小异,注意ios, android会在这里创建UE4游戏线程, UE4游戏线程不是App的主线程
      GEngineLoop.PreInit // FEngineLoop::PreInit(const TCHAR* CmdLine) 解析CmdLine,初始化是否编辑器
        FEngineLoop::PreInitPreStartupScreen
          GLog->AddOutputDevice(GScopedStdOut.Get())
          LLM(FLowLevelMemTracker::Get().ProcessCommandLine(CmdLine)) // LLM初始化
          GError = FPlatformApplicationMisc::GetErrorOutputDevice()
          GWarn = FPlatformApplicationMisc::GetFeedbackContext()
          IFileManager::Get().ProcessCommandLineOptions() // 初始化文件系统
          FTaskGraphInterface::Startup() // TaskGraph初始化,并根据当前机器cpu的核数来创建工作线程
          FTaskGraphInterface::Get().AttachToThread(ENamedThreads::GameThread)  // 将GameThread加入到TaskGraph中
          FThreadStats::StartThread()
          LoadCoreModules: CoreUObject
          FQueuedThreadPool: GThreadPool GBackgroundPriorityThreadPool GLargeThreadPool创建和初始化
          GLogConsole = GScopedLogConsole.Get() // 带-log参数显示出来的命令行窗口
          LoadPreInitModules: Engine Renderer AnimGraphRuntime 平台RHI SlateRHIRenderer Landscape RenderCore
          FCsvProfiler::Get()->Init()
          AppLifetimeEventCapture::Init()
          FTracingProfiler::Get()->Init()
          AppInit()
            BeginInitTextLocalization()
            FPlatformMisc::PlatformPreInit()
            FPlatformApplicationMisc::PreInit()
            FConfigCacheIni::InitializeConfigSystem()
            ProjectManager.LoadModulesForProject:  ELoadingPhase::EarliestPossible
            ELoadingPhase::EarliestPossible
            ProjectManager.CheckModuleCompatibility
            ProjectManager.LoadModulesForProject:  ELoadingPhase::PostConfigInit
          FQueuedThreadPool: GIOThreadPool创建和初始化
          GSystemSettings.Initialize
          Scalability::InitScalabilitySystem()
          FConfigCacheIni::LoadConsoleVariablesFromINI // 读取%EngineDir%EngineConfigConsoleVariables.ini中Startup标签下的命令并执行
          FPlatformMisc::PlatformInit()
          FPlatformApplicationMisc::Init()
          FPlatformMemory::Init()
          InitializeStdOutDevice()
            GLog->AddOutputDevice(GScopedStdOut.Get())
          InitGamePhys()
          InitEngineTextLocalization()
          FSlateApplication::Create()
          FShaderParametersMetadata::InitializeAllUniformBufferStructs()
          RHIInit(bHasEditorToken)
          RenderUtilsInit()
          FShaderCodeLibrary::InitForRuntime
          FShaderPipelineCache::Initialize
          CreateMoviePlayer()
          PostInitRHI()
          StartRenderingThread()
          FSlateApplication: InitializeRenderer
          LoadModulesForProject: ELoadingPhase::PostSplashScreen
          FPreLoadScreenManager初始化和播放
    
        FEngineloop:PreInitPostStartupScreen
          GetMoviePlayer()->SetupLoadingScreenFromIni()
          LoadModulesForProject: ELoadingPhase::PreEarlyLoadingScreen
          GetMoviePlayer()->Initialize
          GetMoviePlayer()->PlayEarlyStartupMovies() // 播放启动视频
          FPlatformMisc::PlatformHandleSplashScreen(true)
          FCoreDelegates::OnMountAllPakFiles.Execute(PakFolders) // 挂载pak
          LoadModulesForEnabledPlugins: ELoadingPhase::PreEarlyLoadingScreen
          FShaderCodeLibrary::OpenLibrary
          FShaderPipelineCache::OpenPipelineFileCache
          InitGameTextLocalization()
          LoadModule: AssetRegistry
          ProcessNewlyLoadedUObjects() // 注册所有UObject类和初始化缺省属性
          UMaterialInterface::InitDefaultMaterials() // 加载缺省材质
          IStreamingManager::Get() // 初始化texture streaming系统
          FModuleManager::Get().StartProcessingNewlyLoadedObjects() 
          LoadStartupCoreModules() // Core Networking Messaging SlateCore Slate UMG等
          LoadModulesForProject: ELoadingPhase::PreLoadingScreen
          FPreLoadScreenManager::Get()->Initialize(*Renderer)
          PostInitRHI()
          StartRenderingThread()
          FPreLoadScreenManager::Get()->PlayFirstPreLoadScreen
          FPlatformMisc::PlatformHandleSplashScreen // 显示splash screen
          LoadStartupModules
            LoadModulesForProject: ELoadingPhase::PreDefault
            LoadModulesForProject: ELoadingPhase::Default
            LoadModulesForProject: ELoadingPhase::PostDefault
          if (!bIsRegularClient)  // 非客户端时
            GEngine = NewObject<UEngine>(GetTransientPackage(), EngineClass) // 创建GEngine
            GEngine->ParseCommandline()
            GEngine->Init(this)
            FCoreDelegates::OnPostEngineInit.Broadcast()
            LoadModulesForProject: ELoadingPhase::PostEngineInit
            LoadModulesForEnabledPlugins: ELoadingPhase::PostEngineInit
            UCommandlet* Commandlet = NewObject<UCommandlet>(GetTransientPackage(), CommandletClass) // 创建Commandlet
            Commandlet->AddToRoot()
            Commandlet->ParseParms( CommandletCommandLine ) // 执行Commadlet
          GetHighResScreenshotConfig().Init() // 初始化高清截图系统
          InitEngineTextLocalization()
          InitGameTextLocalization()
          FPlatformApplicationMisc::PostInit()
          FAutomationTestFramework::Get().RunSmokeTests()
          PreInitContext.Cleanup()
    
      GEngineLoop.Init // FEngineLoop::Init()
        GEngine = NewObject<>(GetTransientPackage(), EngineClass)
        GetMoviePlayer()->PassLoadingScreenWindowBackToGame()
        FPreLoadScreenManager::Get()->PassPreLoadScreenWindowBackToGame()
        GEngine->ParseCommandline()
        InitTime()
        GEngine->Init() // 游戏用UGameEngine,编辑器用UEditorEngine
          UEngine->Init()  //基类,读Engine Config,加载默认资源等|
          GameInstance = NewObject<UGameInstance>(this, GameInstanceClass)
          GameInstance->InitializeStandalone()
            CreateNewWorldContext(EWorldType::Game)
            UWorld* DummyWorld = UWorld::CreateWorld(EWorldType::Game)
            WorldContext->SetCurrentWorld(DummyWorld)
            Init()
          ViewportClient = NewObject<UGameViewportClient>(this, GameViewportClientClass)
          ViewportClient->Init(*GameInstance->GetWorldContext(), GameInstance)
          GameViewportWindow = CreateGameWindow() // GameViewportWindow类型为TWeakPtr<class SWindow>
          CreateGameViewport( ViewportClient )
            CreateGameViewportWidget( GameViewportClient ) // GameViewportClient类型为UGameViewportClient*
              TSharedRef<SOverlay> ViewportOverlayWidgetRef = SNew( SOverlay )
              TSharedRef<SGameLayerManager> GameLayerManagerRef GameLayerManagerRef = SNew(SGameLayerManager) [ViewportOverlayWidgetRef]
              TSharedRef<SViewport> GameViewportWidgetRef = SNew( SViewport ) [GameLayerManagerRef]
            SceneViewport = MakeShareable( new FSceneViewport( GameViewportClient, GameViewportWidgetRef ) ) // SceneViewport类型为TSharedPtr<class FSceneViewport> 
            GameViewportWidgetRef->SetViewportInterface( SceneViewport.ToSharedRef() )
            FSceneViewport* ViewportFrame = SceneViewport.Get()
            GameViewport->SetViewportFrame(ViewportFrame) // GameViewport类型为UGameViewportClient*
            GameViewport->GetGameLayerManager()->SetSceneViewport(ViewportFrame)
          ViewportClient->SetupInitialLocalPlayer
            ViewportGameInstance->CreateInitialPlayer
              UGamelnstane::CreatelLocalPlayer  //这里创建玩家,分屏模式就有多个,也会创建多个Vlewport
          UGameViewportClient::OnViewportCreated().Broadcast()
        FCoreDelegates::OnPostEngineInit.Broadcast()
        GEngine->Start() // UGameEngine
          GameInstance->StartGameInstance()
            OnStart() // UGameInstance
        if (HasActivePreLoadScreenType(EPreLoadScreenTypes::EngineLoadingScreen))
          FPreLoadScreenManager::Get()->SetEngineLoadingComplete(true)
          FPreLoadScreenManager::Get()->WaitForEngineLoadingScreenToFinish()
        else
          GetMoviePlayer()->WaitForMovieToFinish()
        FThreadHeartBeat::Get().Start()
        FShaderPipelineCache::PauseBatching()
        FCoreDelegates::OnFEngineLoopInitComplete.Broadcast()
        FShaderPipelineCache::ResumeBatching()

    引擎的Shader是在游戏启动非常早的时候就会准备好,处理这块热更要注意

    Init函数中会创建Viewport(游戏最终画到的地方),然后调用GameInstance->StartGameInstance()函数来开始业务逻辑的执行

    游戏Tick循环

      GEngineLoop.Tick() // FEngineLoop::Tick()                                                                                    RenderThread(渲染线程)
        LLM(FLowLevelMemTracker::Get().UpdateStatsPerFrame())
    FThreadHeartBeat::Get().HeartBeat(true)
    FGameThreadHitchHeartBeat::Get().FrameStart()
    FPlatformMisc::TickHotfixables()
    如果不使用渲染线程,在这里会先执行TickRenderingTickables() IConsoleManager::Get().CallAllConsoleVariableSinks() //CVar有变化,这里广播通知 FCoreDelegates::OnBeginFrame.Broadcast()
    GLog->FlushThreadedLogs() GEngine->UpdateTimeAndHandleMaxTickRate()//更新CurrentTime和DeltaTime,如果设了最大帧率会在这里等待 SceneComponent在RegisterComponent时候,CreateRenderState就会创建Scenelnfo, 这里会真正更新到渲染线程 遍历WorldContext通知渲染线程UpdateScenePrimitives
    --------ENQUEUE_RENDER_COMMAND(UpdateScenePrimitives)--------Scene->UpdateAllPrimitiveSceneInfos(RHICmdList) ①处理RemovedLocalPrimitiveSceneInfos这里只是从数组中摘掉,收集到一起等最后再删 ②处理AddedLocalPrimitiveSceneInfos ③处理更新Transform的 FlushRuntimeVirtualTexture SetTransform AddPrimitiveToUpdateGPU,如果支持,会更新GPUScene UpdatedAttachmentRoots UpdatedCustomPrimitiveParams更新Custom数据 然后SetNeedsUniformBufferUpdate DistanceFieldSceneDataUpdates ④删掉前面收集的DeletedSceneInfos
    通知渲染线程BeginFrame  ------------------------ENQUEUE_RENDER_COMMAND(BeginFrame)---------------------------BeginFrameRenderThread(RHICmdList, CurrentFrameCounter) 遍历WorldContext,遁知渲染线程StartFrame
    ----------------ENQUEUE_RENDER_COMMAND(SceneStartFrame)---------------Scene->StartFrame(RHICmdList) VelocityData.PrimitiveSceneInfo->SetNeedsUniformBufferUpdate(true) 之前SceneComponenty移动调了Update Transform会加到VelocityData里 这里标记一下要刷UniformBuffer,紧接著10帧都会在这标记刷 GMalloc->UpdateStats()
    FStats::AdvanceFrame( false, FStats::FOnAdvanceRenderingThreadStats::CreateStatic( &AdvanceRenderingThreadStatsGT ) 
    CalculateFPSTimings() // 计算平均fps/ms 通知渲染线程ResetDeferredUpdates
    ----------------ENQUEUE_RENDER_COMMAND(ResetDeferredUpdates)------------------FDeferredUpdateResource::ResetNeedsUpdate() FlushPendingDeleteRHIResources_RenderThread() 这里会把渲染线程准备要删的RHIResource都删了, 如果drawcall太多或大量销毁资源这里会卡 FlushRHI drawcall太多这里就会卡着等刷完 BlockUntilGPUIdle delete... FPlatformApplicationMisc::PumpMessages(true)//处理windows消息循环 如果是IdleMode = ShouldUseIdleMode()会在这里Sleep 0.1秒 处理输入:FCoreDelegates::OnSamplingInput.Broadcast() SlateApp.PollGameDeviceState() SlateApp.FinishedInputThisFrame() MediaModule->TickPreEngine() GEngine->Tick(FApp::GetDeltaTime(), bIdleMode) StaticTick(DeltaSeconds, !!GAsyncLoadingUseFullTimeLimit, GAsyncLoadingTimeLimit / 1000.f) // 里面做等待资源加载的一些逻辑 遍历WorldContext调用 TickWorldTravel(Context, DeltaSeconds) //处理关卡加载的一些逻辑 Context.World()->Tick( LEVELTICK_All, DeltaSeconds ) BeginTickDrawEvent()通知渲染线程 --------------ENQUEUE_RENDER_COMMAND(BeginDrawEventCommand)-------------BeginDrawEvent WorldTick FWorldDelegates::OnWorldTickStart.Broadcast //Tick网络 BroadcastTickDispatch(DeltaSeconds) BroadcastPostTickDispatch() TickNetClient( DeltaSeconds ) 设了高优先级加载,并且在无缝切图中,执行ProcessAsyncLoading 设原点偏移SetNewWorldOrigin NavigationSystem->Tick(DeltaSeconds) //导航系统Tick,里面处理NavMesh FWorldDelegates::OnWorldPreActorTick.Broadcast MovieSceneSequenceTick.Broadcast 追历LevelCollections,收集需要Tick的 RunTickGroup(TG_PrePhysics) 这些是在物理线程前需要Tick的 Tick这个阶段的Actor和Component EnsureCollisionTreeIsBuilt()//这里会阻塞等物理 RunTickGroup(TG_StartPhysics) Tick这个阶段的Actor和Component RunTickGroup(TG_DuringPhysics, false) 物理线程执行的时候,这些组件Tick,不依赖物理的组件可以放这里 Tick这个阶段的Actor和Component RunTickGroup(TG_EndPhysics) Tick这个阶段的Actor和Component RunTickGroup(TG_PostPhysics) //物理做完后,需要Tick的放这里,一般这些组件依赖物理的结果 Tick这个阶段的Actor和Component if (LevelCollections[i].GetType() == ELevelCollectionType::DynamicSourceLevels) CurrentLatentActionManager.ProcessLatentActions GetTimerManager().Tick(DeltaSeconds)//业务的Timer会在这里触发 FTickableGameObject::TickObjects PlayerController->UpdateCameraManager//这里会更新相机 ProcessLevelStreamingVolumes() WorldComposition->UpdateStreamingState() RunTickGroup(TG_PostUpdateWork) Tick这个阶段的Actor和Component RunTickGroup(TG_LastDemotable) Tick这个阶段的Actor和Component FTickTaskManagerInterface::Get().EndFrame() FWorldDelegates::OnWorldPostActorTick.Broadcast FinishAsyncTrace()//异步的物理查询在这出结果 Flush网络 BroadcastTickFlush(RealDeltaSeconds) BroadcastPostTickFlush(RealDeltaSeconds) Scene->UpdateSpeedTreeWind(TimeSeconds) //这是个摇树的组件 FXSystem->Tick(DeltaSeconds)//特效或粒子系统 GEngine->ConditionalCollectGarbage()//尝试垃圾回收
    GEngine->AddOnScreenDebugMessage
    编辑器:UpdateCullDistanceVolumes() EndTickDrawEvent(TickDrawEvent)
    ---------------ENQUEUE_RENDER_COMMAND(EndDrawEventCommand)--------------EndDrawEvent 这样渲染线程就知道了卡在这之间的指令都是WorldTick的 如果非DS,关卡都加载好了,就更新 USkyLightComponent::UpdateSkyCaptureContents(Context.World()) UReflectionCaptureComponent::UpdateReflectionCaptureContents(Context.World()) UpdateTransitionType(Context.World()) BlockTillLevelStreamingCompleted(Context.World()) 服务器 Context.World()->UpdateLevelStreaming() ConditionalCommitMapChange(Context) FTickableGameObject::TickObjects(nullptr, LEVELTICK_All, false, DeltaSeconds) MediaModule->TickPostEngine() GameViewport->Tick(DeltaSeconds)//GameViewport类型为UGameViewportClient* RedrawViewports() GameViewport->Viewport->Draw(bShouldPresent)//这个FViewport继承的是RenderTarget 前面初始化时候,这里被设为了FSceneViewport EnqueueBeginRenderFrame(bShouldPresent) ----------ENQUEUE_RENDER_COMMAND(BeginDrawingCommand)-----------RHICmdList.BeginDrawingViewport(GetViewportRHI(), FTextureRHIRef()); UpdateRenderTargetSurfaceRHIToCurrentBackBuffer(); 更新RT到BackBuffer上
    Canvas.SetRenderTargetRect(FIntRect(0, 0, SizeX, SizeY)) ViewportClient->Draw(this, &Canvas)
    游戏实际调用UGameViewportClient::Draw BeginDrawDelegate.Broadcast CanvasObject->Canvas = SceneCanvas 设到UCanvas上 FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(InViewport,MyWorld->Scene,EngineShowFlags).SetRealtimeUpdate(true))//构造FSceneViewFamilyContext ViewFamily 遍历所有ULocalPlayer,拿到APlayerController因为相机在这里 如果是StereoRendering就有多个FSceneView,否则就1个,初始化一堆参数 给VR游戏用的左右眼,所以是多个View IStreamingManager::Get().AddViewInformation 把View信息告诉资源加载模块,看注释FOV较小时可以5倍速加载 FinalizeViews(&ViewFamily, PlayerViewMap) 根据所有View计算一个最大包围盒 GetRendererModule().BeginRenderingViewFamily 实际调用的是FRendererModule::BeginRenderingViewFamily
    World->SendAllEndOfFrameUpdates()
    BeginSendEndOfFrameUpdatesDrawEvent(Scene ? Scene->GetGPUSkinCache() : nullptr) -----ENQUEUE_RENDER_COMMAND(BeginDrawEventCommand)----SendAllEndOfFrameUpdates->GPUSkinCache->BeginBatchDispatch(RHICmdList)
    EndSendEndOfFrameUpdatesDrawEvent(SendAllEndOfFrameUpdates)----------------ENQUEUE_RENDER_COMMAND(EndDrawEventCommand)----------------SendAllEndOfFrameUpdates->GPUSkinCache->EndBatchDispatch(RHICmdList)
                                                                                              SendAllEndOfFrameUpdates->GPUSkinCache->TransitionAllToReadable(RHICmdList)
                                                                                              delete SendAllEndOfFrameUpdates
    FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(ViewFamily, Canvas->GetHitProxyConsumer())
    创建一个SceneRenderer, 里面会根据ShadingPath决定是Deferred渲染器还是Mobile渲染器 (Mobile里也可以DeferredShading) ENQUEUE_RENDER_COMMAND(FInitFXSystemCommand) USceneCaptureComponent::UpdateDeferredCaptures ENQUEUE_RENDER_COMMAND(FViewExtensionPreDrawCommand) 遍历场景中的PlanarReflections做SceneRenderer->Scene->UpdatePlanarReflectionContents SceneRenderer->ViewFamily.DisplayInternalsData.Setup(World) 提交FDrawSceneCommand这里开始让渲染线程画场景,SceneRenderer作为lambda传给了渲染线程----ENQUEUE_RENDER_COMMAND(FDrawSceneCommand)-----RenderViewFamily_RenderThread(RHICmdList, SceneRenderer)| SceneRenderer->Render FDeferredShadingSceneRenderer::Render FMobileSceneRenderer::Render 前面的2选1决定这里用哪个,这个函数就是UE4的整个渲染管线 后续填坑,这里不细说了 FlushPendingDeleteRHIResources_RenderThread()
    遍历所有PlayerController,画HUD,注意这里是ToneMapping后才画,不是在场景RT上画
    CanvasObject->Init
    CanvasObject->ApplySafeZoneTransform()
    PlayerController->MyHUD->SetCanvas(CanvasObject, DebugCanvasObject)
    PlayerController->MyHUD->PostRender() SceneCanvas->Flush_GameThread()
    DrawnDelegate.Broadcast()
    PostRender(DebugCanvasObject)
    DebugCanvasObject->Init
    DrawStatsHUD
    ViewportConsole->PostRender_Console(DebugCanvasObject) EndDrawDelegate.Broadcast() Canvas.Flush_GameThread() SetRequiresVsync(bLockToVsync) EnqueueEndRenderFrame(bLockToVsync, bShouldPresent)
    FViewport::EnqueueEndRenderFrame(bLockToVsync, bShouldPresent) -----------ENQUEUE_RENDER_COMMAND(EndDrawingCommand)------------ViewportEndDrawing(RHICmdList, Params)
    Parameters.Viewport->EndRenderFrame GetRendererModule().PostRenderAllViewports() IStreamingManager::Get().Tick 通知渲染线程TickRenderingTimer
    ----------------ENQUEUE_RENDER_COMMAND(TickRenderingTime)-----------------这里就是更新RT池 GRenderingRealtimeClock.Tick(DeltaSeconds)
    GRenderTargetPool.TickPoolElements() FRDGBuilder::TickPoolElements() ICustomResourcePool::TickPoolElements(RHICmdList) 编辑器:BroadcastPostEditorTick(DeltaSeconds); FAssetRegistryModule::TickAssetRegistry(DeltaSeconds)
    PreLoadScreenManager相关,播视频时候会阻塞在这里直到完成再向下执行 GShaderCompilingManager->ProcessAsyncResults
    GDistanceFieldAsyncQueue->ProcessAsyncTasks MediaModule->TickPreSlate() FSlateApplication::Get().Tick(ESlateTickType::PlatformAndInput) CurrentDemoNetDriver可能会建-个ConcurrentTask执行TickFlushAsyncEndOfFrame FSlateApplication::Get().Tick(ESlateTickType::TimeAndWidgets) TickTime()
    TickAndDrawWidgets(DeltaTime)
    PreTickEvent.Broadcast(DeltaTime)
    DrawWindows()
    PrivateDrawWindows()
    DrawPrepass( DrawOnlyThisWindow )
    DrawWindowAndChildren
    Renderer->DrawWindows( DrawWindowArgs.OutDrawBuffer )
    SlateBeginDrawingWindowsCommand -----------ENQUEUE_RENDER_COMMAND(SlateBeginDrawingWindowsCommand)------------Policy->BeginDrawingWindows()
    SlateDrawWindowsCommand ----------------ENQUEUE_RENDER_COMMAND(SlateDrawWindowsCommand)---------------Params.Renderer->DrawWindow_RenderThread
    SlateWindowRendered.Broadcast
    SlateEndDrawingWindowsCommand ------------ENQUEUE_RENDER_COMMAND(SlateEndDrawingWindowsCommand)-------------FSlateEndDrawingWindowsCommand::EndDrawingWindows
    if (DeferredUpdateContexts.Num() > 0)
    DrawWidgetRendererImmediate --------------ENQUEUE_RENDER_COMMAND(DrawWidgetRendererImmediate)-------------循环FRenderThreadUpdateContext,执行Context.Renderer->DrawWindowToTarget_RenderThread(RHICmdList, Context)
    PostTickEvent.Broadcast(DeltaTime)
    FTaskGraphInterface::Get().WaitUntilTaskCompletes(ConcurrentTask) 通知渲染线程WaitForOutstandingTasks
    ---ENQUEUE_RENDER_COMMAND(WaitForOutstandingTasksOnly_for_DelaySceneRenderCompletion)---FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly) RHITick( FApp::GetDeltaTime() ) GFrameCounter++; TotalTickTime += FApp::GetDeltaTime() FrameEndSync.Sync//等渲染线程的同步 delete PreviousPendingCleanupObjects DeleteLoaders() FTicker::GetCoreTicker().Tick(FApp::GetDeltaTime()) FThreadManager::Get().Tick() GEngine->TickDeferredCommands() MediaModule->TickPostRender() FCoreDelegates::OnEndFrame.Broadcast() 通知渲染线程EndFrame---------------------------ENQUEUE_RENDER_COMMAND(EndFrame)--------------------------EndFrameRenderThread(RHICmdList, CurrentFrameCounter) GEngine->SetGameLatencyMarkerEnd(CurrentFrameCounter)

    在Tick中会先执行游戏逻辑,调用World的Tick,然后Tick所有注册需要Tick的Actor和Component,这里会根据注册的阶段分别在不同时期Tick。

    结束之后会进入绘制视口,会先画场景,在画场景时才相当于是渲染线程这帧真正开始了,然后画UI。

    然后中间很多地方都穿插着多线程调度。最终我们看到引擎执行一帧大概如下图所示:

    注1:由于UI在场景之后绘制,假如UI遮挡住了大部分场景,被遮挡住的部分就白画了。

             所以如果能修改引擎代码的话,可以考虑在绘制开始阶段,先在场景的RT上UI对应的位置写上深度(需要额外处理半透明)或者建一些对应轮廓面片放在镜头近平面上挡住场景对应区域,这样就可以跳过这些像素的绘制。

    注2:如果游戏线程做的事情很少,基本上会阻塞在最后的FrameEndSync.Sync上。

             当你有一些很重的工作,但是又和渲染无关,比如网络游戏的解包或其他比较重的逻辑,就可以考虑在绘制这一阶段期间开启一个单独的线程,让子线程去做这些工作,而不是放在前面的Tick阶段。

    在Tick开始处,在Scene->UpdateAllPrimitiveSceneInfos(RHICmdList)中会先把场景数据的各种信息比如,Transform在渲染线程上刷一遍(因为很多东西是会动的)。

    然后引擎开始Tick World。这里比较重要的一点是,我们可以看到Tick的对象有很多阶段,平常用的比较多的是PrePhysics(Tick默认为该类型),DuringPhysics,PostPhysics这3个地方。

    为什么要区分这些阶段呢?这是因为UE4是个多线程的引擎,物理是一个很重的计算流程,物理的计算发生在一个单独的线程上,因此将Tick拆分成这些阶段,就可以让业务代码选择在什么时期执行。

    因为大部分的组件都是需要先准备好数据,交给物理线程来执行,所以UE4把Tick默认都放在了PrePhysics上,这样当所有组件Tick完,物理线程得到的数据就是最新的。

    但是考虑到假如你的组件或Actor和物理没任何关系,那么物理线程就会等待逻辑执行,在物理线程开始执行后,由于DurningPhysics基本没事情做,又反过来等待物理线程,这样游戏线程的总耗时就会被拉长。

    因此可以把一些不需要依赖物理的组件放在其他阶段,说不定能起到很好的优化效果。

    注:Actor和ActorComponent的Tick是分别注册且互相独立的,互相不存在依赖关系。所以当不需要Tick Component时,关掉Actor是不够的,Component也要单独关闭。

    参考

    UE4的执行流程和CPU优化

    剖析虚幻渲染体系(01)- 综述和基础(引擎启动流程) 

  • 相关阅读:
    [Swift通天遁地]一、超级工具-(7)创建一个图文并茂的笔记本程序
    [Swift通天遁地]一、超级工具-(6)通过JavaScript(脚本)代码调用设备的源生程序
    [Swift通天遁地]一、超级工具-(5)使用UIWebView(网页视图)加载本地页面并调用JavaScript(脚本)代码
    [Swift通天遁地]一、超级工具-(4)使用UIWebView(网页视图)加载HTML和Gif动画
    [Swift通天遁地]一、超级工具-(3)带切换图标的密码文本框
    [Swift通天遁地]一、超级工具-(2)制作美观大方的环形进度条
    将css 中的16进制颜色, 转化为 rgb格式
    Axure实现Tab选项卡切换功能
    UVA 10951
    No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=i386).错误解决方法
  • 原文地址:https://www.cnblogs.com/kekec/p/15187684.html
Copyright © 2011-2022 走看看