스레드와 비동기 처리
GameThread, WorkerThread, TaskGraph 기반 애니메이션 병렬 평가 메커니즘을 심층 분석합니다.
애니메이션 스레딩 모델
게임 스레드와 워커 스레드 간 애니메이션 작업 분배
병렬 애니메이션 평가는 bUseParallelAnimEvaluation이 true일 때 활성화됩니다. 다음 조건을 모두 만족해야 합니다: AnimInstance가 NeedsImmediateUpdate에서 false 반환, 게임 스레드 의존성 없음, a.ParallelAnimEvaluation CVar 활성화.
TaskGraph와 병렬 평가
UE5 TaskGraph 시스템을 활용한 애니메이션 태스크 스케줄링
애니메이션 병렬 평가는 TaskGraph 시스템을 통해 스케줄링됩니다. 각 SkeletalMeshComponent의 AnimGraph 평가가 독립 태스크로 생성되어 워커 스레드 풀에서 병렬 실행됩니다.
// USkeletalMeshComponent::TickAnimation 내부
void USkeletalMeshComponent::TickAnimation(float DeltaTime)
{
if (ShouldUseParallelEvaluation())
{
// 게임 스레드: 데이터 준비
AnimInstance->PreUpdateAnimation(DeltaTime);
// 워커 스레드로 평가 태스크 디스패치
ParallelAnimationEvaluationTask = TGraphTask<FParallelAnimEvaluationTask>::
CreateTask(nullptr, ENamedThreads::GameThread)
.ConstructAndDispatch<>(this);
// 이 시점에서 게임 스레드는 다른 작업 수행 가능
// 다음 컴포넌트의 TickAnimation 호출 등
}
else
{
// 게임 스레드에서 직접 평가 (폴백)
AnimInstance->UpdateAnimation(DeltaTime);
AnimInstance->EvaluateAnimation();
}
}
// 프레임 후반에 완료 대기
void USkeletalMeshComponent::CompleteParallelAnimationEvaluation()
{
// 워커 스레드 태스크 완료 대기
ParallelAnimationEvaluationTask->Wait();
// 결과 적용 (게임 스레드)
AnimInstance->PostEvaluateAnimation();
UpdateBoneTransforms();
SyncPhysicsBodies();
}
| CVar | 설명 | 기본값 |
|---|---|---|
a.ParallelAnimEvaluation |
병렬 애니메이션 평가 전역 활성화 | 1 (활성) |
a.ParallelAnimUpdate |
병렬 애니메이션 업데이트 활성화 | 1 (활성) |
a.ParallelAnimInterpolation |
병렬 본 보간 활성화 | 1 (활성) |
스레드 안전 프로그래밍
커스텀 AnimNode에서 스레드 안전하게 데이터를 접근하는 패턴
금지: 게임 스레드 데이터 직접 접근
AnimNode의 Update/Evaluate에서 UAnimInstance, AActor, UWorld 등 게임 스레드 객체에 직접 접근하면 경합(Race Condition)이 발생합니다.
권장: FAnimInstanceProxy 사용
PreUpdate에서 필요한 데이터를 Proxy에 복사하고, Update/Evaluate에서는 Proxy의 캐시 데이터만 사용합니다.
권장: Property Access
AnimGraph의 Property Access 바인딩은 스레드 안전하게 구현되어 있어, 직접 프로퍼티 복사 없이 안전하게 데이터에 접근 가능합니다.
주의: Notify 발동 타이밍
Queued Notify는 게임 스레드에서 발동되지만, Branching Point는 평가 스레드에서 트리거될 수 있습니다. Notify 콜백에서의 스레드 안전성을 확인하세요.
// 올바른 패턴: Proxy를 통한 데이터 접근
struct FMyAnimProxy : public FAnimInstanceProxy
{
// 게임 스레드에서 복사 (PreUpdate에서)
float CachedSpeed = 0.f;
bool bCachedIsInAir = false;
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override
{
Super::PreUpdate(InAnimInstance, DeltaSeconds);
UMyAnimInstance* MyAnim = Cast<UMyAnimInstance>(InAnimInstance);
// 게임 스레드 데이터를 안전하게 복사
CachedSpeed = MyAnim->Speed;
bCachedIsInAir = MyAnim->bIsInAir;
}
};
// AnimNode에서 Proxy 데이터 사용
void FAnimNode_Custom::Update_AnyThread(const FAnimationUpdateContext& Context)
{
FMyAnimProxy& Proxy = Context.AnimInstanceProxy->GetProxyOnAnyThread<FMyAnimProxy>();
// 안전: Proxy의 캐시 데이터 사용
float Speed = Proxy.CachedSpeed;
// 위험: UAnimInstance 직접 접근 금지!
// float Speed = AnimInstance->Speed; // RACE CONDITION!
}
비동기 애니메이션 로딩
애니메이션 애셋의 비동기 스트리밍과 로딩 전략
// 비동기 애니메이션 로딩
void AMyCharacter::AsyncLoadAnimation(TSoftObjectPtr<UAnimMontage> MontageRef)
{
FStreamableManager& StreamableManager =
UAssetManager::GetStreamableManager();
StreamableManager.RequestAsyncLoad(
MontageRef.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(
this, &AMyCharacter::OnAnimationLoaded, MontageRef)
);
}
void AMyCharacter::OnAnimationLoaded(
TSoftObjectPtr<UAnimMontage> MontageRef)
{
UAnimMontage* Montage = MontageRef.Get();
if (Montage)
{
// 애니메이션 로딩 완료, 사용 가능
GetMesh()->GetAnimInstance()->Montage_Play(Montage);
}
}
// PoseSearchDatabase의 비동기 빌드
// DB의 SearchIndex는 에디터에서 저장 시 빌드됨
// 런타임에는 직렬화된 인덱스를 로드하여 즉시 사용
UE5에서 AnimSequence의 Compression 데이터 스트리밍이 지원됩니다. 전체 애니메이션을 메모리에 로드하지 않고, 필요한 프레임 범위만 스트리밍하여 메모리 사용량을 줄입니다. 프로젝트 설정의 bEnablePerformanceLog로 스트리밍 히트/미스를 모니터링하세요.
핵심 요약
- UE5 애니메이션은 게임 스레드(데이터 준비/후처리)와 워커 스레드(AnimGraph 평가)로 분리 실행된다.
- TaskGraph로 각 SkeletalMeshComponent의 평가가 독립 태스크로 병렬 실행된다.
- AnimNode에서는 FAnimInstanceProxy를 통해서만 데이터에 접근하여 스레드 안전성을 보장한다.
- Property Access는 스레드 안전한 데이터 바인딩 시스템으로, Proxy 수동 복사를 대체할 수 있다.
- 비동기 애니메이션 로딩과 Animation Streaming으로 메모리와 로딩 시간을 최적화한다.
도전 과제
배운 내용을 직접 실습해보세요
프로젝트 설정에서 Allow Multi Threaded Animation Update가 활성화되어 있는지 확인하세요. Unreal Insights로 애니메이션 평가가 Worker Thread에서 수행되는지 타이밍을 확인하고, Game Thread 대비 병렬 처리의 효과를 측정하세요.
AnimBP에서 Property Access 노드를 사용하여 Character Movement의 Velocity를 AnimGraph에서 직접 읽는 패턴을 구현하세요. 기존 Event Graph 캐싱 방식과 비교하여 스레드 안전성과 성능 차이를 확인하세요.
C++에서 FAnimInstanceProxy를 오버라이드하여 워커 스레드에서 안전하게 실행되는 커스텀 애니메이션 로직을 구현하세요. PreUpdate, Update, PostUpdate의 실행 스레드를 이해하고, 게임 스레드 데이터를 Proxy로 복사하는 패턴을 적용하세요.