1. 程式人生 > >虛幻4 引擎主迴圈

虛幻4 引擎主迴圈

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