- Last Updated: UE 5.4
- World Partition, OFPA, Data Layer, Level Instance, HLOD의 해당 기능에 대한 기본적인 이해가 필요할 수 있습니다.
- 월드 빌딩 전반에 대한 공식 문서는 World Building Guide 참고
제목은 Deep Dive이지만 모든 내용을 다루기엔 방대해서 기본적인 에디터 환경에서의 소스 코드를 기반으로 스트리밍 생성/업데이트 흐름에 대한 이해를 목표로, 개인적인 정리 글이면서 대략 World Partition이 무엇인지는 알지만 실제 내부에서 액터가 어디에 저장되고, 스트리밍이 어떤 클래스로 어떻게 이뤄지는지 궁금하지만 어디서부터 봐야할지 진입의 엄두가 안 나신 분들을 위한 글입니다. 틀린 내용이 있을 수 있으므로 댓글로 알려주시면 수정하겠습니다. 😊
가장 먼저 World Partition에서 액터가 어떻게 관리되는지에 대해 다뤄보겠습니다.
1. Actor 관리
1.1. AActor

기본적으로 언리얼에서 월드에 배치된 오브젝트는 AActor 클래스를 상속 받습니다. 하지만 AActor는 동적으로 영역을 스트리밍하는 월드 파티션에서 그대로 사용하기엔 언로드된 상태를 나타낼 수 없고, 불필요한 데이터가 많은 점 등의 문제가 있습니다.
1.2. Actor Desc
FWorldPartitionActorDesc
따라서 월드 파티션에서는 FWorldPartitionActorDesc라는 액터의 메타 데이터를 가진 클래스(에디터 전용)를 통해 로드/언로드 및 액터 관리를 수행합니다. 해당 클래스를 살펴보면 월드 파티션에서 액터를 동적으로 관리할 수 있는 변수와 함수가 대부분임을 알 수 있습니다.

그럼 FWorldPartitionActorDesc는 언제 생성될까요? FWorldPartitionActorDesc::Init(...) 함수가 호출되는 부분을 보면 대표적으로 다음과 같습니다.
- FWorldPartitionActorDesc::Init(const AActor* InActor)
- Actor 월드에 배치 후 저장하는 경우
- UActorDescContainer::OnObjectPreSave(UObject* Object, FObjectPreSaveContext SaveContext)
- FWorldPartitionActorDescUtils::AppendAssetDataTagsFromActor(...)
- Actor를 월드에 배치 후 저장 안하고 PIE로 실행하는 경우
- FStreamingGenerationUnsavedDirtyActorDescInstance::Create(...) 참고
- Actor 월드에 배치 후 저장하는 경우
- FWorldPartitionActorDesc::Init(const FWorldPartitionActorDescInitData& DescData)
- 레벨 로드하는 경우
- FExternalPackageHelper::GetSortedAssets(...)로 FAssetData 목록 구해서 세팅
- 레벨 로드하는 경우
1. UWorld::InitWorld(...)
2. ULevel::OnLevelLoaded(...)
3. UWorldPartition::Initialize(UWorld* InWorld, const FTransform& InTransform)
4. UWorldPartition::RegisterActorDescContainerInstance(...)
5. UActorDescContainerInstance::Initialize(const FInitializeParams& InParams)
6. UActorDescContainerInstance::RegisterContainer(const FInitializeParams& InParams)
7. UActorDescContainerSubsystem::RegisterContainer(...)
8. UActorDescContainer::Initialize(const FInitializeParams& InitParams)
{
...
// 스캔한 에셋 순회하면서 ActorDesc 세팅
TUniquePtr<FWorldPartitionActorDesc> ActorDesc = FWorldPartitionActorDescUtils::GetActorDescriptorFromAssetData(Asset);
FWorldPartitionActorDescInstance
5.4 이전에는 FWorldPartitionActorDesc에서 모든 처리를 했으나, 5.4 이후부터 Transient/Context Data를 분리하여 FWorldPartitionActorDesc가 디스크에서 dirty한 상태가 되지 않도록 만들어진 클래스입니다.
아래에서도 ~Instance가 붙은 클래스가 여럿 나오는데 5.4부터는 월드 파티션에서 실제로 관리되는 클래스가 ~Instance 클래스로 변경되었습니다. 각 클래스는 Instance가 아닌 기존의 클래스를 포인터 멤버 변수로 갖습니다.
추후 액터 관리 방식 등의 차이에 대해 보충 할 수 있겠지만, 현재로서는 기존 클래스 = Instance 클래스로 이해하고 글을 읽으셔도 무방할 것으로 생각됩니다.

1.3. Actor Desc Container
다양한 방법을 통해 생성된 FWorldPartitionActorDesc를 관리해주는 컨테이너의 클래스는 다음과 같습니다.

TActorDescList
템플릿 클래스로 DescType에 대해 ActorDesc 추가, 삭제 및 기본적인 반복자 등을 제공합니다. 결과적으로 생성된 ActorDesc는 아래 변수에 담기게 됩니다. ActorDesc는 TChunkedArray와 TMap으로 관리됩니다. (TChunkedArray는 일반적인 게임 코드에선 거의 안 쓰이는데 메모리가 청크 단위로 분할되어 할당되므로 메모리 파편화가 줄어드는 장점이 있기에 렌더러나 코어한 부분에선 자주 보임)
// ActorDesc가 실제로 담기는 곳
FActorDescArray ActorDescList (= TChunkedArray<TUniquePtr<DescType>>)
FGuidActorDescMap ActorsByGuid (= TMap<FGuid, TUniquePtr<DescType>*, FDefaultSetAllocator, FActorGuidKeyFuncs>)
FActorDescList / FActorDescInstanceList
FActorDescList는 TActorDescList<FWorldPartitionActorDesc>를 상속 받은 클래스입니다. 이전 월드 파티션 구조에서는 대부분의 기능이 있었으나, 5.4에 이르러선 대부분의 기능이 TActorDescList로 옮겨지고 FWorldPartitionActorDesc로 특별화된 점 이외엔 크게 특별한 부분이 없는 클래스입니다.
UActorDescContainer
FActorDescList를 상속 받은 UObject 클래스입니다. FName으로 ActorDesc를 관리하는 Map을 갖고 있으며 Actor가 추가 삭제 될 때 함수나 델리게이트 등이 있습니다.
FNameActorDescMap ActorsByName = TMap<FName, TUniquePtr<FWorldPartitionActorDesc>*>
FActorDescAddedEvent OnActorDescAddedEvent
FActorDescRemovedEvent OnActorDescRemovedEvent
FActorDescUpdatingEvent OnActorDescUpdatingEvent
FActorDescUpdatedEvent OnActorDescUpdatedEvent
void RegisterActorDescriptor(FWorldPartitionActorDesc* ActorDesc)
void UnregisterActorDescriptor(FWorldPartitionActorDesc* ActorDesc)
void OnActorDescAdded(FWorldPartitionActorDesc* NewActorDesc)
void OnActorDescRemoved(FWorldPartitionActorDesc* ActorDesc)
void OnActorDescUpdating(FWorldPartitionActorDesc* ActorDesc)
void OnActorDescUpdated(FWorldPartitionActorDesc* ActorDesc)

UActorDescContainerInstance
5.4부터 FWorldPartitionActorDescInstance와 동일하게 Transient/Context Data를 분리하여 UActorDescContainer가 디스크에 Dirty 상태가 되지 않는 것을 목적으로 추가된 클래스입니다. 이 또한 기존의 UActorDescContainer를 TObjectPtr로 가집니다.

// 액터 추가/삭제 관련 함수
FWorldPartitionActorDescInstance* AddActor(FWorldPartitionActorDesc* InActorDesc)
void RemoveActor(const FGuid& InActorGuid)
FWorldPartitionActorDescInstance* AddActorDescInstance(FWorldPartitionActorDescInstance&& InActorDescInstance)
void RemoveActorDescInstance(TUniquePtr<FWorldPartitionActorDescInstance>* InActorDescInstance)
UActorContainerSubsystem
5.4에서 UActorDescContainer이 UActorDescContainerInstance로 분리된 후에 UActorDescContainer의 관리를 위해 추가된 에디터용 엔진 서브 시스템 클래스입니다. 실제 컨테이너의 등록 및 관리는 내부의 FContainerManager 클래스에서 이뤄지며 UActorContainerSubsystem는 FContainerManager 함수 호출 및 컨테이너 업데이트, 교체 델리게이트를 제공합니다.


1.4. Actor Desc Container Collection
FWorldPartitionActorDesc 목록을 가진 컨테이너는 여러 개가 존재할 수 있습니다. 따라서 컨테이너를 관리하는 컨테이너의 필요성이 있고, 컨테이너 컬렉션이 이에 해당합니다.
TActorDescContainerInstanceCollection
ActorDescContPtrType을 인자로 받는 컨테이너 컬렉션 템플릿 클래스로, 컨테이너는 TArray에서 관리됩니다. 컨테이너를 추가/삭제 한다던가, 각 컨테이너에서 함수를 호출한다던가 하는 함수들이 있습니다.
TArray<ActorDescContPtrType> ActorDescContainerInstanceCollection
5.4부터 UWorldPartition 클래스가 FActorDescContainerCollection 대신 인스턴스 클래스인 FActorDescContainerInstanceCollection (= TActorDescContainerInstanceCollection<TObjectPtr<UActorDescContainerInstance>>) 를 상속받도록 변경되었습니다.
UWorldPartition
월드 파티션의 메인 클래스이자 TActorDescContainerInstanceCollection<TObjectPtr<UActorDescContainerInstance>>를 상속 받은 클래스입니다. AActor부터 UWorldPartition에 이르기까지 클래스 간의 구조를 정리하면 다음과 같습니다.

지금까지의 내용을 통해 UWorldPartition 클래스가 Actor Desc를 가진 컨테이너를 지닌 컬렉션 클래스라는 것을 알 수 있었습니다. 간단한 상속 구조는 다음과 같습니다. 아무래도 월드 파티션의 메인 클래스이다보니 많은 함수와 변수를 갖고 있어서 대략적인 구조는 이쯤에서 마무리하고, 이어지는 2편에서 UWorldPartition부터 시작해 본격적인 스트리밍 생성과 업데이트에 대해 알아보겠습니다.

Etc.
참고
- UE 5.4 Source Code
'Column' 카테고리의 다른 글
[UE] World Partition Deep Dive 3 - Streaming Update (0) | 2025.02.09 |
---|---|
[UE] World Partition Deep Dive 2 - Streaming Generation (0) | 2024.08.29 |