UE5 스레드 아키텍처
Game Thread, Render Thread, RHI Thread의 역할과 동기화 이해
주요 스레드 개요
UE5의 핵심 스레드들
메인 게임 로직 실행
- 입력 처리
- 게임플레이 로직
- AI / Behavior Tree
- 물리 시뮬레이션
- 애니메이션 업데이트
씬 렌더링 준비
- 씬 가시성 계산
- 드로우 명령 생성
- 머티리얼 파라미터
- 라이팅 계산
- 셰도우 맵 준비
GPU 명령 번역
- 드로우 명령 번역
- GPU API 호출
- 리소스 바인딩
- 커맨드 버퍼 제출
병렬 작업 처리
- Task Graph 작업
- 에셋 비동기 로딩
- 물리 병렬 계산
- AI 경로 탐색
프레임 파이프라인
스레드 간 시간 관계
Render Thread는 Game Thread보다 1-2 프레임 뒤처집니다. 이로 인해 Game Thread에서 변경한 데이터가 즉시 화면에 반영되지 않습니다. 데이터 동기화에 주의해야 합니다.
스레드 확인 방법
현재 스레드 판별하기
// 현재 스레드 확인
if (IsInGameThread())
{
// Game Thread에서 실행 중
UE_LOG(LogTemp, Log, TEXT("Running on Game Thread"));
}
if (IsInRenderingThread())
{
// Render Thread에서 실행 중
}
if (IsInRHIThread())
{
// RHI Thread에서 실행 중
}
// Game Thread 강제 체크 (디버그용)
check(IsInGameThread());
// 어서션 - 조건 만족하지 않으면 크래시
checkf(IsInGameThread(),
TEXT("This function must be called from Game Thread!"));
스레드 간 통신
다른 스레드에 작업 전달
Render Thread로 명령 전송
// Game Thread에서 Render Thread로 명령 전송
ENQUEUE_RENDER_COMMAND(MyRenderCommand)(
[CapturedData = this->SomeData](FRHICommandListImmediate& RHICmdList)
{
// 여기는 Render Thread에서 실행됨
// 주의: 캡처는 반드시 값으로! (참조 캡처 금지)
ProcessOnRenderThread(CapturedData);
}
);
// 완료 대기 (동기화 필요 시)
FlushRenderingCommands(); // Game Thread 블로킹!
Render Command의 람다에서 참조 캡처([&])는 금지입니다. Render Thread가 실행될 때 원본 데이터가 이미 소멸되었을 수 있습니다. 항상 값 캡처([=] 또는 명시적 값 캡처)를 사용하세요.
Game Thread로 복귀
// 백그라운드 스레드에서 Game Thread로 작업 전달
AsyncTask(ENamedThreads::GameThread, [this]()
{
// Game Thread에서 실행됨
// UI 업데이트, UObject 접근 등 안전
OnWorkComplete();
});
스레드 안전성 규칙
멀티스레딩 기본 원칙
- UObject는 Game Thread에서만 접근 - 생성, 수정, 삭제 모두 Game Thread에서
- Render Thread 데이터 분리 - FRenderResource 등 별도 데이터 구조 사용
- 공유 데이터는 동기화 - FCriticalSection, FRWLock 사용
- 람다 캡처는 값으로 - 특히 다른 스레드로 전달 시
- 블로킹 최소화 - FlushRenderingCommands는 꼭 필요할 때만
// 잘못된 예 - Render Thread에서 UObject 접근
ENQUEUE_RENDER_COMMAND(BadCommand)(
[this](FRHICommandListImmediate& RHICmdList)
{
// 위험! UObject 접근
MyActor->GetActorLocation(); // 크래시 가능!
}
);
// 올바른 예 - 데이터 복사 후 전달
FVector CachedLocation = MyActor->GetActorLocation();
ENQUEUE_RENDER_COMMAND(GoodCommand)(
[CachedLocation](FRHICommandListImmediate& RHICmdList)
{
// 안전! 복사된 값 사용
ProcessLocation(CachedLocation);
}
);
핵심 요약
- Game Thread - 게임 로직, 입력, 물리, AI 처리
- Render Thread - 씬 가시성 계산, 드로우 명령 생성 (1-2 프레임 지연)
- RHI Thread - GPU API 호출, 커맨드 버퍼 제출
- ENQUEUE_RENDER_COMMAND - Render Thread로 명령 전송
- AsyncTask(GameThread) - Game Thread로 복귀
- UObject는 Game Thread 전용 - 다른 스레드에서 접근 금지
도전 과제
배운 내용을 직접 실습해보세요
stat Threading 명령으로 Game Thread, Render Thread, RHI Thread의 시간을 확인하세요. UE5의 스레드 모델(Game Thread, Render Thread, Worker Threads)을 도식화하세요.
FRunnable을 상속하여 백그라운드에서 RPG 월드 데이터를 처리하는 워커 스레드를 생성하세요. Init(), Run(), Stop()을 구현하고, FRunnableThread::Create()로 시작하세요.
Game Thread와 Worker Thread 간에 FCriticalSection(FScopeLock)으로 보호되는 공유 큐를 구현하세요. 패스파인딩 결과나 절차적 생성 데이터를 스레드 간에 안전하게 전달하세요.