虛幻4 引擎主迴圈
阿新 • • 發佈:2019-01-23
void FEngineLoop::Tick() { #if !UE_BUILD_SHIPPING && !UE_BUILD_TEST FScopedSampleMallocChurn ChurnTracker; #endif // Ensure we aren't starting a frame while loading or playing a loading movie ensure(GetMoviePlayer()->IsLoadingFinished() && !GetMoviePlayer()->IsMovieCurrentlyPlaying()); FExternalProfiler* ActiveProfiler = FActiveExternalProfilerBase::GetActiveProfiler(); if (ActiveProfiler) { ActiveProfiler->FrameSync(); } SCOPED_NAMED_EVENT(FEngineLoopTick, FColor::Red); // early in the Tick() to get the callbacks for cvar changes called { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_CallAllConsoleVariableSinks); IConsoleManager::Get().CallAllConsoleVariableSinks(); } { SCOPE_CYCLE_COUNTER( STAT_FrameTime ); ENQUEUE_UNIQUE_RENDER_COMMAND( BeginFrame, { GRHICommandList.LatchBypass(); GFrameNumberRenderThread++; RHICmdList.PushEvent(*FString::Printf(TEXT("Frame%d"),GFrameNumberRenderThread)); RHICmdList.BeginFrame(); }); { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_FlushThreadedLogs); // Flush debug output which has been buffered by other threads. GLog->FlushThreadedLogs(); } // Exit if frame limit is reached in benchmark mode. if( (FApp::IsBenchmarking() && MaxFrameCounter && (GFrameCounter > MaxFrameCounter)) // or time limit is reached if set. || (MaxTickTime && (TotalTickTime > MaxTickTime)) ) { FPlatformMisc::RequestExit(0); } { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_UpdateTimeAndHandleMaxTickRate); // Set FApp::CurrentTime, FApp::DeltaTime and potentially wait to enforce max tick rate. GEngine->UpdateTimeAndHandleMaxTickRate(); } { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_TickFPSChart); GEngine->TickFPSChart( FApp::GetDeltaTime() ); } QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Malloc_UpdateStats); // Update memory allocator stats. GMalloc->UpdateStats(); } FStats::AdvanceFrame( false, FStats::FOnAdvanceRenderingThreadStats::CreateStatic( &AdvanceRenderingThreadStatsGT ) ); { SCOPE_CYCLE_COUNTER( STAT_FrameTime ); // Calculates average FPS/MS (outside STATS on purpose) CalculateFPSTimings(); // Note the start of a new frame MALLOC_PROFILER(GMalloc->Exec(nullptr, *FString::Printf(TEXT("SNAPSHOTMEMORYFRAME")),*GLog)); // handle some per-frame tasks on the rendering thread ENQUEUE_UNIQUE_RENDER_COMMAND( ResetDeferredUpdates, { FDeferredUpdateResource::ResetNeedsUpdate(); FlushPendingDeleteRHIResources_RenderThread(); }); { SCOPE_CYCLE_COUNTER(STAT_PumpMessages); FPlatformMisc::PumpMessages(true); } bool bIdleMode; { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Idle); // Idle mode prevents ticking and rendering completely bIdleMode = ShouldUseIdleMode(); if (bIdleMode) { // Yield CPU time FPlatformProcess::Sleep(.1f); } } if (FSlateApplication::IsInitialized() && !bIdleMode) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_SlateInput); FSlateApplication& SlateApp = FSlateApplication::Get(); SlateApp.PollGameDeviceState(); // Gives widgets a chance to process any accumulated input SlateApp.FinishedInputThisFrame(); } GEngine->Tick( FApp::GetDeltaTime(), bIdleMode ); // If a movie that is blocking the game thread has been playing, // wait for it to finish before we continue to tick or tick again // We do this right after GEngine->Tick() because that is where user code would initiate a load / movie. { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_WaitForMovieToFinish); GetMoviePlayer()->WaitForMovieToFinish(); } if (GShaderCompilingManager) { // Process any asynchronous shader compile results that are ready, limit execution time QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_GShaderCompilingManager); GShaderCompilingManager->ProcessAsyncResults(true, false); } if (GDistanceFieldAsyncQueue) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_GDistanceFieldAsyncQueue); GDistanceFieldAsyncQueue->ProcessAsyncTasks(); } if (FSlateApplication::IsInitialized() && !bIdleMode) { { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_ProcessPlayerControllersSlateOperations); check(!IsRunningDedicatedServer()); // Process slate operations accumulated in the world ticks. ProcessLocalPlayerSlateOperations(); } // Tick Slate application FSlateApplication::Get().Tick(); } #if STATS // Clear any stat group notifications we have pending just incase they weren't claimed during FSlateApplication::Get().Tick extern CORE_API void ClearPendingStatGroups(); ClearPendingStatGroups(); #endif #if WITH_EDITOR { QUICK_SCOPE_CYCLE_COUNTER( STAT_FEngineLoop_Tick_AutomationController ); static FName AutomationController( "AutomationController" ); //Check if module loaded to support the change to allow this to be hot compilable. if (FModuleManager::Get().IsModuleLoaded( AutomationController )) { FModuleManager::GetModuleChecked<IAutomationControllerModule>( AutomationController ).Tick(); } } #endif #if WITH_ENGINE #if WITH_AUTOMATION_WORKER { QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_AutomationWorker); //Check if module loaded to support the change to allow this to be hot compilable. static const FName AutomationWorkerModuleName = TEXT("AutomationWorker"); if (FModuleManager::Get().IsModuleLoaded(AutomationWorkerModuleName)) { FModuleManager::GetModuleChecked<IAutomationWorkerModule>(AutomationWorkerModuleName).Tick(); } } #endif #endif //WITH_ENGINE { SCOPE_CYCLE_COUNTER(STAT_RHITickTime); RHITick( FApp::GetDeltaTime() ); // Update RHI. } // Increment global frame counter. Once for each engine tick. GFrameCounter++; // Disregard first few ticks for total tick time as it includes loading and such. if( GFrameCounter > 6 ) { TotalTickTime+=FApp::GetDeltaTime(); } // Find the objects which need to be cleaned up the next frame. FPendingCleanupObjects* PreviousPendingCleanupObjects = PendingCleanupObjects; PendingCleanupObjects = GetPendingCleanupObjects(); { SCOPE_CYCLE_COUNTER( STAT_FrameSyncTime ); // this could be perhaps moved down to get greater parallelizm // Sync game and render thread. Either total sync or allowing one frame lag. static FFrameEndSync FrameEndSync; static auto CVarAllowOneFrameThreadLag = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.OneFrameThreadLag")); FrameEndSync.Sync( CVarAllowOneFrameThreadLag->GetValueOnGameThread() != 0 ); } { SCOPE_CYCLE_COUNTER( STAT_DeferredTickTime ); // Delete the objects which were enqueued for deferred cleanup before the previous frame. delete PreviousPendingCleanupObjects; // Destroy all linkers pending delete #if WITH_COREUOBJECT DeleteLoaders(); #endif FTicker::GetCoreTicker().Tick(FApp::GetDeltaTime()); FSingleThreadManager::Get().Tick(); GEngine->TickDeferredCommands(); } ENQUEUE_UNIQUE_RENDER_COMMAND( EndFrame, { RHICmdList.EndFrame(); RHICmdList.PopEvent(); }); // Check for async platform hardware survey results GEngine->TickHardwareSurvey(); // Set CPU utilization stats. const FCPUTime CPUTime = FPlatformTime::GetCPUTime(); SET_FLOAT_STAT( STAT_CPUTimePct, CPUTime.CPUTimePct ); SET_FLOAT_STAT( STAT_CPUTimePctRelative, CPUTime.CPUTimePctRelative ); // Set the UObject count stat #if UE_GC_TRACK_OBJ_AVAILABLE SET_DWORD_STAT(STAT_Hash_NumObjects, GUObjectArray.GetObjectArrayNumMinusAvailable()); #endif } }
\Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp