前言:
刚接触UE时候,想必很多人很难置信UE整个平台框架是从c++Win32框架搭建的,很多人想必对于其实现的基本流程很迷茫,在此,记录自己学习研究步骤,有问题请指出
在Vs中点击在文件中查找,如果是rider或者其他编辑器平台,应该有属于自己的查询方式
在查找框中搜索int main
,并点击查找全部
点击LaunchLumin.cpp中的查找项
这时候我们看到了此处main
函数中的代码代码块:
int main(int argc, char *argv[])
{
FPlatformMisc::SetGracefulTerminationHandler();
int ErrorLevel = 0;
// read the command line file
InitCommandLine();
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Final commandline: %s\n"), FCommandLine::Get());
#if !UE_BUILD_SHIPPING
GAlwaysReportCrash = true; // set by default and reverse the behavior
if (FParse::Param(FCommandLine::Get(), TEXT("nocrashreports")) || FParse::Param(FCommandLine::Get(), TEXT("no-crashreports")))
{
GAlwaysReportCrash = false;
}
#endif
if (!IncreasePerProcessLimits())
{
fprintf(stderr, "Could not set desired per-process limits, consider changing system limits.\n");
ErrorLevel = 1;
return ErrorLevel;
}
// if (!FPlatformMisc::IsDebuggerPresent() || GAlwaysReportCrash)
// {
// // Use default crash handler, but we still need to set this up to register signal handlers
// FPlatformMisc::SetCrashHandler(nullptr);
// }
// ErrorLevel = GuardedMain(FCommandLine::Get());
// initialize the engine
GEngineLoop.PreInit(0, NULL, FCommandLine::Get());
// initialize HMDs
// @todo Lumin: I guess we don't need this?
// InitHMDs();
UE_LOG(LogAndroid, Display, TEXT("Passed PreInit()"));
GLog->SetCurrentThreadAsMasterThread();
GEngineLoop.Init();
UE_LOG(LogAndroid, Log, TEXT("Passed GEngineLoop.Init()"));
#if !UE_BUILD_SHIPPING
if (FParse::Param(FCommandLine::Get(), TEXT("Messaging")))
{
// initialize messaging subsystem
FModuleManager::LoadModuleChecked<IMessagingModule>("Messaging");
TSharedPtr<ISessionService> SessionService = FModuleManager::LoadModuleChecked<ISessionServicesModule>("SessionServices").GetSessionService();
SessionService->Start();
// Initialize functional testing
FModuleManager::Get().LoadModule("FunctionalTesting");
}
#endif
// tick until done
while (!IsEngineExitRequested())
{
// FAppEventManager::GetInstance()->Tick();
// if(!FAppEventManager::GetInstance()->IsGamePaused())
{
GEngineLoop.Tick();
}
// else
// {
// // use less CPU when paused
// FPlatformProcess::Sleep(0.10f);
// }
#if !UE_BUILD_SHIPPING
// show console window on next game tick
// if (GShowConsoleWindowNextTick)
// {
// GShowConsoleWindowNextTick = false;
// AndroidThunkCpp_ShowConsoleWindow();
// }
#endif
}
// FAppEventManager::GetInstance()->TriggerEmptyQueue();
UE_LOG(LogAndroid, Log, TEXT("Exiting"));
// exit out!
GEngineLoop.Exit();
FEngineLoop::AppExit();
FPlatformMisc::LowLevelOutputDebugString(TEXT("Exiting is over"));
if (ErrorLevel)
{
printf("Exiting abnormally (error code: %d)\n", ErrorLevel);
}
return ErrorLevel;
}
接着转入引擎的循环函数Tick()
这便是引擎的Tick函数
void FEngineLoop::Tick()
{
// make sure to catch any FMemStack uses outside of UWorld::Tick
FMemMark MemStackMark(FMemStack::Get());
#if !UE_BUILD_SHIPPING && !UE_BUILD_TEST && MALLOC_GT_HOOKS
FScopedSampleMallocChurn ChurnTracker;
#endif
// let the low level mem tracker pump once a frame to update states
LLM(FLowLevelMemTracker::Get().UpdateStatsPerFrame());
LLM_SCOPE(ELLMTag::EngineMisc);
// Send a heartbeat for the diagnostics thread
FThreadHeartBeat::Get().HeartBeat(true);
FGameThreadHitchHeartBeat::Get().FrameStart();
FPlatformMisc::TickHotfixables();
// Make sure something is ticking the rendering tickables in -onethread mode to avoid leaks/bugs.
if (!GUseThreadedRendering && !GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed))
{
TickRenderingTickables();
}
// Ensure we aren't starting a frame while loading or playing a loading movie
ensure(GetMoviePlayer()->IsLoadingFinished() && !GetMoviePlayer()->IsMovieCurrentlyPlaying());
#if UE_EXTERNAL_PROFILING_ENABLED
FExternalProfiler* ActiveProfiler = FActiveExternalProfilerBase::GetActiveProfiler();
if (ActiveProfiler)
{
ActiveProfiler->FrameSync();
}
#endif // UE_EXTERNAL_PROFILING_ENABLED
FPlatformMisc::BeginNamedEventFrame();
uint64 CurrentFrameCounter = GFrameCounter;
#if ENABLE_NAMED_EVENTS
TCHAR IndexedFrameString[32] = { 0 };
const TCHAR* FrameString = nullptr;
if (UE_TRACE_CHANNELEXPR_IS_ENABLED(CpuChannel))
{
FrameString = TEXT("FEngineLoop");
}
else
{
#if PLATFORM_LIMIT_PROFILER_UNIQUE_NAMED_EVENTS
FrameString = TEXT("FEngineLoop");
#else
FCString::Snprintf(IndexedFrameString, 32, TEXT("Frame %d"), CurrentFrameCounter);
FrameString = IndexedFrameString;
#endif
}
SCOPED_NAMED_EVENT_TCHAR(FrameString, FColor::Red);
#endif
// execute callbacks for cvar changes
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_CallAllConsoleVariableSinks);
IConsoleManager::Get().CallAllConsoleVariableSinks();
}
{
TRACE_BEGIN_FRAME(TraceFrameType_Game);
SCOPE_CYCLE_COUNTER(STAT_FrameTime);
#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
FCoreDelegates::OnBeginFrame.Broadcast();
// flush debug output which has been buffered by other threads
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_FlushThreadedLogs);
GLog->FlushThreadedLogs();
}
// exit if frame limit is reached in benchmark mode, or if time limit is reached
if ((FApp::IsBenchmarking() && MaxFrameCounter && (GFrameCounter > MaxFrameCounter)) ||
(MaxTickTime && (TotalTickTime > MaxTickTime)))
{
FPlatformMisc::RequestExit(0);
}
// set FApp::CurrentTime, FApp::DeltaTime and potentially wait to enforce max tick rate
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_UpdateTimeAndHandleMaxTickRate);
GEngine->UpdateTimeAndHandleMaxTickRate();
GEngine->SetGameLatencyMarkerStart(CurrentFrameCounter);
}
for (const FWorldContext& Context : GEngine->GetWorldContexts())
{
UWorld* CurrentWorld = Context.World();
if (CurrentWorld)
{
FSceneInterface* Scene = CurrentWorld->Scene;
ENQUEUE_RENDER_COMMAND(UpdateScenePrimitives)(
[Scene](FRHICommandListImmediate& RHICmdList)
{
Scene->UpdateAllPrimitiveSceneInfos(RHICmdList);
});
}
}
// beginning of RHI frame
ENQUEUE_RENDER_COMMAND(BeginFrame)([CurrentFrameCounter](FRHICommandListImmediate& RHICmdList)
{
BeginFrameRenderThread(RHICmdList, CurrentFrameCounter);
});
for (const FWorldContext& Context : GEngine->GetWorldContexts())
{
UWorld* CurrentWorld = Context.World();
if (CurrentWorld)
{
FSceneInterface* Scene = CurrentWorld->Scene;
ENQUEUE_RENDER_COMMAND(SceneStartFrame)([Scene](FRHICommandListImmediate& RHICmdList)
{
Scene->StartFrame();
});
}
}
#if !UE_SERVER && WITH_ENGINE
if (!GIsEditor && GEngine->GameViewport && GEngine->GameViewport->GetWorld() && GEngine->GameViewport->GetWorld()->IsCameraMoveable())
{
// When not in editor, we emit dynamic resolution's begin frame right after RHI's.
GEngine->EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::BeginFrame);
}
#endif
// tick performance monitoring
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_TickFPSChart);
GEngine->TickPerformanceMonitoring( FApp::GetDeltaTime() );
extern COREUOBJECT_API void ResetAsyncLoadingStats();
ResetAsyncLoadingStats();
}
#if UPDATE_MALLOC_STATS
// update memory allocator stats
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Malloc_UpdateStats);
GMalloc->UpdateStats();
}
#endif
}
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_RENDER_COMMAND(ResetDeferredUpdates)(
[](FRHICommandList& RHICmdList)
{
FDeferredUpdateResource::ResetNeedsUpdate();
FlushPendingDeleteRHIResources_RenderThread();
});
{
SCOPE_CYCLE_COUNTER(STAT_PumpMessages);
FPlatformApplicationMisc::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);
}
}
// @todo vreditor urgent: Temporary hack to allow world-to-meters to be set before
// input is polled for motion controller devices each frame.
extern ENGINE_API float GNewWorldToMetersScale;
if( GNewWorldToMetersScale != 0.0f )
{
#if WITH_ENGINE
UWorld* WorldToScale = GWorld;
#if WITH_EDITOR
if( GIsEditor && GEditor->PlayWorld != nullptr && GEditor->bIsSimulatingInEditor )
{
WorldToScale = GEditor->PlayWorld;
}
#endif //WITH_EDITOR
if( WorldToScale != nullptr )
{
if( GNewWorldToMetersScale != WorldToScale->GetWorldSettings()->WorldToMeters )
{
WorldToScale->GetWorldSettings()->WorldToMeters = GNewWorldToMetersScale;
}
}
GNewWorldToMetersScale = 0.0f;
}
#endif //WITH_ENGINE
// tick active platform files
FPlatformFileManager::Get().TickActivePlatformFile();
// Roughly track the time when the input was sampled
FCoreDelegates::OnSamplingInput.Broadcast();
// process accumulated Slate input
if (FSlateApplication::IsInitialized() && !bIdleMode)
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(Input);
SCOPE_TIME_GUARD(TEXT("SlateInput"));
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_SlateInput);
LLM_SCOPE(ELLMTag::UI);
FSlateApplication& SlateApp = FSlateApplication::Get();
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_PollGameDeviceState);
SlateApp.PollGameDeviceState();
}
// Gives widgets a chance to process any accumulated input
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_FinishedInputThisFrame);
SlateApp.FinishedInputThisFrame();
}
}
#if !UE_SERVER
// tick media framework
static const FName MediaModuleName(TEXT("Media"));
IMediaModule* MediaModule = FModuleManager::LoadModulePtr<IMediaModule>(MediaModuleName);
if (MediaModule != nullptr)
{
MediaModule->TickPreEngine();
}
#endif
// main game engine tick (world, game objects, etc.)
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.
{
if (FPreLoadScreenManager::Get())
{
if (FPreLoadScreenManager::Get()->HasRegisteredPreLoadScreenType(EPreLoadScreenTypes::EngineLoadingScreen))
{
//Wait for any Engine Loading Screen to stop
if (FPreLoadScreenManager::Get()->HasActivePreLoadScreenType(EPreLoadScreenTypes::EngineLoadingScreen))
{
FPreLoadScreenManager::Get()->WaitForEngineLoadingScreenToFinish();
}
//Switch Game Window Back
UGameEngine* GameEngine = Cast<UGameEngine>(GEngine);
if (GameEngine)
{
GameEngine->SwitchGameWindowToUseGameViewport();
}
}
//Destroy / Clean Up PreLoadScreenManager as we are now done
FPreLoadScreenManager::Destroy();
}
else
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_WaitForMovieToFinish);
GetMoviePlayer()->WaitForMovieToFinish(true);
}
}
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 !UE_SERVER
// tick media framework
if (MediaModule != nullptr)
{
MediaModule->TickPreSlate();
}
#endif
// Tick the platform and input portion of Slate application, we need to do this before we run things
// concurrent with networking.
if (FSlateApplication::IsInitialized() && !bIdleMode)
{
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_ProcessPlayerControllersSlateOperations);
check(!IsRunningDedicatedServer());
// Process slate operations accumulated in the world ticks.
ProcessLocalPlayerSlateOperations();
}
FSlateApplication::Get().Tick(ESlateTickType::PlatformAndInput);
}
#if WITH_ENGINE
// process concurrent Slate tasks
FGraphEventRef ConcurrentTask;
const bool bDoConcurrentSlateTick = GEngine->ShouldDoAsyncEndOfFrameTasks();
const UGameViewportClient* const GameViewport = GEngine->GameViewport;
const UWorld* const GameViewportWorld = GameViewport ? GameViewport->GetWorld() : nullptr;
UDemoNetDriver* const CurrentDemoNetDriver = GameViewportWorld ? GameViewportWorld->GetDemoNetDriver() : nullptr;
// Optionally validate that Slate has not modified any replicated properties for client replay recording.
FDemoSavedPropertyState PreSlateObjectStates;
const bool bValidateReplicatedProperties = CurrentDemoNetDriver && CVarDoAsyncEndOfFrameTasksValidateReplicatedProperties.GetValueOnGameThread() != 0;
if (bValidateReplicatedProperties)
{
PreSlateObjectStates = CurrentDemoNetDriver->SavePropertyState();
}
if (bDoConcurrentSlateTick)
{
const float DeltaSeconds = FApp::GetDeltaTime();
if (CurrentDemoNetDriver && CurrentDemoNetDriver->ShouldTickFlushAsyncEndOfFrame())
{
ConcurrentTask = TGraphTask<FExecuteConcurrentWithSlateTickTask>::CreateTask(nullptr, ENamedThreads::GameThread).ConstructAndDispatchWhenReady(
[CurrentDemoNetDriver, DeltaSeconds]()
{
if (CVarDoAsyncEndOfFrameTasksRandomize.GetValueOnAnyThread(true) > 0)
{
FPlatformProcess::Sleep(FMath::RandRange(0.0f, .003f)); // this shakes up the threading to find race conditions
}
if (CurrentDemoNetDriver != nullptr)
{
CurrentDemoNetDriver->TickFlushAsyncEndOfFrame(DeltaSeconds);
}
});
}
}
#endif
// Tick(Advance) Time for the application and then tick and paint slate application widgets.
// We split separate this action from the one above to permit running network replication concurrent with slate widget ticking and painting.
if (FSlateApplication::IsInitialized() && !bIdleMode)
{
FSlateApplication::Get().Tick(ESlateTickType::TimeAndWidgets);
}
#if WITH_ENGINE
if (bValidateReplicatedProperties)
{
const bool bReplicatedPropertiesDifferent = CurrentDemoNetDriver->ComparePropertyState(PreSlateObjectStates);
if (bReplicatedPropertiesDifferent)
{
UE_LOG(LogInit, Log, TEXT("Replicated properties changed during Slate tick!"));
}
}
if (ConcurrentTask.GetReference())
{
CSV_SCOPED_SET_WAIT_STAT(Slate);
QUICK_SCOPE_CYCLE_COUNTER(STAT_ConcurrentWithSlateTickTasks_Wait);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(ConcurrentTask);
ConcurrentTask = nullptr;
}
{
ENQUEUE_RENDER_COMMAND(WaitForOutstandingTasksOnly_for_DelaySceneRenderCompletion)(
[](FRHICommandList& RHICmdList)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_DelaySceneRenderCompletion_TaskWait);
FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly);
});
}
#endif
#if STATS
// Clear any stat group notifications we have pending just in case they weren't claimed during FSlateApplication::Get().Tick
extern CORE_API void ClearPendingStatGroups();
ClearPendingStatGroups();
#endif
#if WITH_EDITOR && !UE_BUILD_SHIPPING
// tick automation controller (Editor only)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_AutomationController);
static FName AutomationController("AutomationController");
if (FModuleManager::Get().IsModuleLoaded(AutomationController))
{
FModuleManager::GetModuleChecked<IAutomationControllerModule>(AutomationController).Tick();
}
}
#endif
#if WITH_ENGINE && WITH_AUTOMATION_WORKER
// tick automation worker
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_AutomationWorker);
static const FName AutomationWorkerModuleName = TEXT("AutomationWorker");
if (FModuleManager::Get().IsModuleLoaded(AutomationWorkerModuleName))
{
FModuleManager::GetModuleChecked<IAutomationWorkerModule>(AutomationWorkerModuleName).Tick();
}
}
#endif
// tick render hardware interface
{
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 parallelism
// 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 );
}
// tick core ticker, threads & deferred commands
{
SCOPE_CYCLE_COUNTER(STAT_DeferredTickTime);
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(DeferredTickTime);
// Delete the objects which were enqueued for deferred cleanup before the previous frame.
delete PreviousPendingCleanupObjects;
#if WITH_COREUOBJECT
DeleteLoaders(); // destroy all linkers pending delete
#endif
FTicker::GetCoreTicker().Tick(FApp::GetDeltaTime());
FThreadManager::Get().Tick();
GEngine->TickDeferredCommands();
}
#if !UE_SERVER
// tick media framework
if (MediaModule != nullptr)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_MediaTickPostRender);
MediaModule->TickPostRender();
}
#endif
FCoreDelegates::OnEndFrame.Broadcast();
#if !UE_SERVER && WITH_ENGINE
{
// We emit dynamic resolution's end frame right before RHI's. GEngine is going to ignore it if no BeginFrame was done.
GEngine->EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::EndFrame);
}
#endif
// end of RHI frame
ENQUEUE_RENDER_COMMAND(EndFrame)(
[CurrentFrameCounter](FRHICommandListImmediate& RHICmdList)
{
EndFrameRenderThread(RHICmdList, CurrentFrameCounter);
});
GEngine->SetGameLatencyMarkerEnd(CurrentFrameCounter);
// 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
TRACE_END_FRAME(TraceFrameType_Game);
}
#if BUILD_EMBEDDED_APP
static double LastSleepTime = FPlatformTime::Seconds();
double TimeNow = FPlatformTime::Seconds();
if (LastSleepTime > 0 && TimeNow - LastSleepTime >= CVarSecondsBeforeEmbeddedAppSleeps.GetValueOnAnyThread())
{
LastSleepTime = 0;
FEmbeddedCommunication::AllowSleep(TEXT("FirstTicks"));
}
#endif
}
在这个Tick
函数中我们可以找到我们常见的帧记录:
这里,看到了来自UE的线程管理
这里也可以看到在UE非服务器上,都会去执行属于自己的media
核心框架
在帧末尾,绑定了最后一帧应当做的事,与发射动态解决方法的事件
文章描述得很简单,到最后,只是告诉大家一个查询源码的思路,UE代码块很多,根据自己的感兴趣的方向看就好,去查找就好