WinMain
int32 WINAPI WinMain(_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ char* pCmdLine, _In_ int32 nCmdShow)
{
int32 Result = LaunchWindowsStartup(hInInstance, hPrevInstance, pCmdLine, nCmdShow, nullptr);
LaunchWindowsShutdown();
return Result;
}
1. 처음 BP를 걸고 호출스택을 따라가보면 익숙한 WinMain에서 시작합니다.
LaunchWindowsStartup
LAUNCH_API int32 LaunchWindowsStartup( HINSTANCE hInInstance, HINSTANCE hPrevInstance, char*, int32 nCmdShow, const TCHAR* CmdLine )
{
TRACE_BOOKMARK(TEXT("WinMain.Enter"));
#if USING_ADDRESS_SANITISER
__asan_set_error_report_callback(ASanErrorCallback);
#endif
// Setup common Windows settings
SetupWindowsEnvironment();
int32 ErrorLevel = 0;
hInstance = hInInstance;
// Hm: 커맨드 라인에 들어온 인자들을 사용하기 편하게 가공하는 구간
if (!CmdLine)
{
CmdLine = ::GetCommandLineW();
// Attempt to process the command-line arguments using the standard Windows implementation
// (This ensures behavior parity with other platforms where argc and argv are used.)
if ( ProcessCommandLine() )
{
CmdLine = *GSavedCommandLine;
}
}
// When we're running embedded, assume that the outer application is going to be handling crash reporting
#if UE_BUILD_DEBUG
if (GUELibraryOverrideSettings.bIsEmbedded || !GAlwaysReportCrash)
#else
if (GUELibraryOverrideSettings.bIsEmbedded || bNoExceptionHandler || (bIsDebuggerPresent && !GAlwaysReportCrash))
#endif
{
// Don't use exception handling when a debugger is attached to exactly trap the crash. This does NOT check
// whether we are the first instance or not!
ErrorLevel = GuardedMain( CmdLine );
}
else
{
// Use structured exception handling to trap any crashes, walk the the stack and display a crash dialog box.
#if !PLATFORM_SEH_EXCEPTIONS_DISABLED
__try
#endif
{
GIsGuarded = 1;
// Run the guarded code.
ErrorLevel = GuardedMainWrapper( CmdLine );
GIsGuarded = 0;
}
#if !PLATFORM_SEH_EXCEPTIONS_DISABLED
__except( FPlatformMisc::GetCrashHandlingType() == ECrashHandlingType::Default
? ( GEnableInnerException ? EXCEPTION_EXECUTE_HANDLER : ReportCrash(GetExceptionInformation()) )
: EXCEPTION_CONTINUE_SEARCH )
{
// Crashed.
ErrorLevel = 1;
if(GError)
{
GError->HandleError();
}
LaunchStaticShutdownAfterError();
FPlatformMallocCrash::Get().PrintPoolsUsage();
FPlatformMisc::RequestExit( true, TEXT("LaunchWindowsStartup.ExceptionHandler"));
}
#endif
}
TRACE_BOOKMARK(TEXT("WinMain.Exit"));
return ErrorLevel;
}
- 커맨드 라인에 대한 유효성 검사를 진행하여 내용 저장한 후 본격적으로 GuardMain 함수에서 시작합니다.
GuardedMain
int32 GuardedMain( const TCHAR* CmdLine )
{
FTrackedActivity::GetEngineActivity().Update(TEXT("Starting"), FTrackedActivity::ELight::Yellow);
FTaskTagScope Scope(ETaskTag::EGameThread);
#if !(UE_BUILD_SHIPPING)
// If "-waitforattach" or "-WaitForDebugger" was specified, halt startup and wait for a debugger to attach before continuing
if (FParse::Param(CmdLine, TEXT("waitforattach")) || FParse::Param(CmdLine, TEXT("WaitForDebugger")))
{
while (!FPlatformMisc::IsDebuggerPresent())
{
FPlatformProcess::Sleep(0.1f);
}
UE_DEBUG_BREAK();
}
#endif
BootTimingPoint("DefaultMain");
// Super early init code. DO NOT MOVE THIS ANYWHERE ELSE!
FCoreDelegates::GetPreMainInitDelegate().Broadcast();
// make sure GEngineLoop::Exit() is always called.
// Hm : 객체가 Scope가 끝나고 파괴될 때 EngineExit()이 호출되도록 하는 구조
struct EngineLoopCleanupGuard
{
~EngineLoopCleanupGuard()
{
// Don't shut down the engine on scope exit when we are running embedded
// because the outer application will take care of that.
if (!GUELibraryOverrideSettings.bIsEmbedded)
{
EngineExit();
}
}
} CleanupGuard;
FTrackedActivity::GetEngineActivity().Update(TEXT("Initializing"));
// Hm : Unreal Editor 켜지기 전에 리소스 로딩하는 부분
int32 ErrorLevel = EnginePreInit( CmdLine );
// exit if PreInit failed.
if ( ErrorLevel != 0 || IsEngineExitRequested() )
{
return ErrorLevel;
}
{
FScopedSlowTask SlowTask(100, NSLOCTEXT("EngineInit", "EngineInit_Loading", "Loading..."));
// EnginePreInit leaves 20% unused in its slow task.
// Here we consume 80% immediately so that the percentage value on the splash screen doesn't change from one slow task to the next.
// (Note, we can't include the call to EnginePreInit in this ScopedSlowTask, because the engine isn't fully initialized at that point)
SlowTask.EnterProgressFrame(80);
SlowTask.EnterProgressFrame(20);
#if WITH_EDITOR
if (GIsEditor)
{
ErrorLevel = EditorInit(GEngineLoop);
}
else
#endif
{
ErrorLevel = EngineInit();
}
}
// Hm : 빈번하게 사용되는 로직, 현재 시간과 Start 시간을 구해서 소요 시간을 구한다.
double EngineInitializationTime = FPlatformTime::Seconds() - GStartTime;
UE_LOG(LogLoad, Log, TEXT("(Engine Initialization) Total time: %.2f seconds"), EngineInitializationTime);
#if WITH_EDITOR
UE_LOG(LogLoad, Log, TEXT("(Engine Initialization) Total Blueprint compile time: %.2f seconds"), BlueprintCompileAndLoadTimerData.GetTime());
#endif
ACCUM_LOADTIME(TEXT("EngineInitialization"), EngineInitializationTime);
BootTimingPoint("Tick loop starting");
DumpBootTiming();
FTrackedActivity::GetEngineActivity().Update(TEXT("Ticking loop"), FTrackedActivity::ELight::Green);
// Don't tick if we're running an embedded engine - we rely on the outer
// application ticking us instead.
// Hm : 드디어 대망의 틱이 도는 구문
// Hm : 엔진에서 종료 Request를 줄 때까지 계속 반복한다.
if (!GUELibraryOverrideSettings.bIsEmbedded)
{
while( !IsEngineExitRequested() )
{
EngineTick();
}
}
TRACE_BOOKMARK(TEXT("Tick loop end"));
#if WITH_EDITOR
if( GIsEditor )
{
EditorExit();
}
#endif
return ErrorLevel;
}
이후 로직은 EngineTick을 통해 진행되게 되며, while문 조건의 IsEngineExitRequested 함수는 창을 끄는 등 종료 요청이 들어오면 while문을 탈출하여 프로그램 종료 절차를 밟게 됩니다.
'Unreal Engine > [Inflearn_rookiss] UE5 엔진 분석' 카테고리의 다른 글
2. Unreal Engine 초기 세팅 (0) | 2024.10.07 |
---|---|
1. Unreal Engine Full Build (0) | 2024.10.05 |
댓글