GRAPHICS PROGRAMMING
Actor Cluster Created 에 대하여...
jplee
2025. 1. 21. 01:53
언리얼 엔진의 Actor Clustering Enabled 에 대해서 살펴 보면...
주요 특징
- 액터 그룹화:
- 서로 가깝거나 관련된 액터들을 하나의 클러스터로 묶습니다.
- 묶인 클러스터는 스트리밍이나 렌더링 시 하나의 단위로 취급됩니다.
- 레벨 스트리밍 최적화:
- 클러스터 단위로 액터를 로드하거나 언로드하여, 씬의 메모리 사용량과 성능을 최적화합니다.
- 필요한 지역(클러스터)만 활성화하므로 대규모 월드에서 성능 병목을 줄이는 데 도움을 줍니다.
- 렌더링 효율성:
- 묶인 액터들이 동일한 조건에서 평가되기 때문에, LOD(레벨 오브 디테일) 및 Occlusion Culling(차폐 제거) 같은 최적화가 더 효과적으로 수행됩니다.
- 자동 클러스터링:
- Unreal Engine은 클러스터링을 자동으로 수행하며, 위치와 관계를 기반으로 액터를 클러스터로 그룹화합니다.
- 이 프로세스는 디자이너가 수동으로 관리하지 않아도 되도록 설계되었습니다.
사용 사례
- 오픈 월드 게임:
- 대규모 맵에서 플레이어가 특정 지역에 있을 때 해당 지역의 액터만 로드하거나 렌더링.
- 예: 오픈 월드 RPG 게임에서 플레이어의 위치에 따라 도시, 숲, 건물 등의 클러스터가 개별적으로 활성화됨.
- 레벨 파티셔닝:
- 팀 기반의 개발 환경에서, 액터를 논리적으로 묶어 관리하기 쉽도록 함.
- 메모리 관리:
- 대규모 씬의 메모리 사용량을 줄이고, 씬의 복잡도에 비례한 성능 병목을 방지.
클러스터링 활성화
Actor Clustering Enabled는 레벨 스트리밍과 연관된 시스템 설정으로, 보통 다음 방법으로 활성화됩니다:
- World Partition 설정:
- 레벨 설정에서 World Partition 기능을 사용하면 클러스터링이 기본적으로 활성화됩니다.
- 커스텀 클러스터링 제어:
- C++ 또는 블루프린트를 통해 특정 액터 그룹의 클러스터링 동작을 커스터마이징할 수 있습니다.
성능 고려사항
- 장점:
- 큰 월드에서 성능 최적화가 용이.
- 메모리 사용량 감소 및 스트리밍 시간 단축.
- 단점:
- 자동 클러스터링 알고리즘이 항상 이상적이지 않을 수 있어, 특정 시나리오에서는 수동 조정이 필요할 수 있음.
- 클러스터링 자체로 인해 디버깅이 복잡해질 가능성.
void ULevel::CreateCluster()
{
// ULevels are not cluster roots themselves, instead they create a special actor container
// that holds a reference to all actors that are to be clustered. This is because only
// specific actor types can be clustered so the remaining actors that are not clustered
// need to be referenced through the level.
// Also, we don't want the level to reference the actors that are clusters because that would
// make things work even slower (references to clustered objects are expensive). That's why
// we keep a separate array for referencing unclustered actors (ActorsForGC).
if (FPlatformProperties::RequiresCookedData() && GCreateGCClusters && GActorClusteringEnabled && bGarbageCollectionClusteringEnabled && !bActorClusterCreated)
{
ActorsForGC.Reset();
TArray<AActor*> ClusterActors;
for (int32 ActorIndex = Actors.Num() - 1; ActorIndex >= 0; --ActorIndex)
{
AActor* Actor = Actors[ActorIndex];
if (Actor && Actor->CanBeInCluster())
{
ClusterActors.Add(Actor);
}
else
{
ActorsForGC.Add(Actor);
}
}
if (ClusterActors.Num())
{
ActorCluster = NewObject<ULevelActorContainer>(this, TEXT("ActorCluster"), RF_Transient);
ActorCluster->Actors = MoveTemp(ClusterActors);
ActorCluster->CreateCluster();
}
bActorClusterCreated = true;
}
}
이 부분을 보면...
ULevel::CreateCluster 함수는 언리얼 엔진(Unreal Engine)에서 특정 레벨에 있는 액터(Actor)들을 클러스터(Cluster) 로 묶어 Garbage Collection(가비지 컬렉션, GC) 을 최적화하는 역할을 합니다. 이 함수는 특히 쿠킹된 빌드(cooked builds)에서 메모리 관리를 개선하기 위해 설계되었습니다.
CreateCluster 함수의 목적
- GC 성능 최적화:
- 개별 액터를 추적하는 대신, 클러스터로 묶어서 관리하여 가비지 컬렉션 성능을 높임.
- 클러스터 내 액터에 대한 참조 오버헤드를 줄여 효율성 향상.
- 메모리 관리 개선:
- 액터를 그룹화하여 메모리 단편화(Fragmentation)를 줄이고 관리 효율성을 높임.
함수 코드의 주요 동작 원리
1. 클러스터 생성 조건
다음 조건이 모두 충족될 때 클러스터를 생성합니다:
if (FPlatformProperties::RequiresCookedData() && GCreateGCClusters && GActorClusteringEnabled && bGarbageCollectionClusteringEnabled && !bActorClusterCreated)
- FPlatformProperties::RequiresCookedData(): 쿠킹된 빌드에서만 클러스터 생성.
- GCreateGCClusters & GActorClusteringEnabled: 클러스터링 기능이 전역적으로 활성화되어야 함.
- bGarbageCollectionClusteringEnabled: 해당 레벨에서 클러스터링이 활성화되어야 함.
- !bActorClusterCreated: 이미 클러스터가 생성되지 않은 경우.
2. 클러스터링 대상과 비대상 액터 분리
for (int32 ActorIndex = Actors.Num() - 1; ActorIndex >= 0; --ActorIndex)
{
AActor* Actor = Actors[ActorIndex];
if (Actor && Actor->CanBeInCluster())
{
ClusterActors.Add(Actor); // 클러스터링 가능한 액터
}
else
{
ActorsForGC.Add(Actor); // 클러스터링 불가능한 액터
}
}
레벨에 포함된 모든 액터를 순회하며 두 가지로 분류:
- 클러스터링 가능한 액터:
- CanBeInCluster() 함수가 true를 반환하는 액터.
- ClusterActors 배열에 추가.
- 클러스터링 불가능한 액터:
- ActorsForGC 배열에 추가되어 일반적인 가비지 컬렉션에 포함.
- 클러스터 생성
if (ClusterActors.Num())
{
ActorCluster = NewObject<ULevelActorContainer>(this, TEXT("ActorCluster"), RF_Transient);
ActorCluster->Actors = MoveTemp(ClusterActors); // 클러스터 액터 배열 이동
ActorCluster->CreateCluster(); // 클러스터 초기화
}
클러스터링 가능한 액터가 있으면:
- ULevelActorContainer 객체를 생성.
- 이 객체는 클러스터 내 모든 액터를 참조합니다.
- MoveTemp로 클러스터 액터 배열을 이동.
- CreateCluster()를 호출해 클러스터를 초기화.
4. 상태 갱신
bActorClusterCreated = true;
클러스터 생성이 완료되었음을 표시하여 중복 생성 방지.
중요한 구성 요소
- 클러스터링 가능한 액터:
- CanBeInCluster() 함수가 true를 반환하는 액터만 클러스터에 포함됩니다.
- 클러스터링 불가능한 액터는 ActorsForGC 배열에 저장.
- ULevelActorContainer:
- 클러스터링된 액터를 보관하는 컨테이너 역할.
- 가비지 컬렉션 시, 이 컨테이너를 통해 클러스터링된 액터들을 효율적으로 처리.
- ActorsForGC:
- 클러스터링 불가능한 액터들을 따로 관리하기 위한 배열.
CreateCluster의 장점
- 가비지 컬렉션 최적화:
- 수많은 개별 액터를 관리하는 대신, 클러스터 단위로 참조를 처리하여 효율성을 높임.
- 메모리 단편화 감소:
- 클러스터링으로 액터들의 메모리 사용을 더욱 일관되게 관리.
- 쿠킹된 빌드에서 효과적:
- 쿠킹된 빌드는 런타임 성능이 중요하므로 클러스터링으로 오버헤드 감소.
제약 사항 및 고려 사항
- 클러스터링 조건:
- 클러스터링 가능한 액터는 제한적입니다(CanBeInCluster()에 의존).
- 투명하거나 특별한 처리가 필요한 액터는 제외될 수 있음.
- Z-Fighting:
- 너무 많은 액터가 클러스터에 포함되면 깊이 충돌(Z-Fighting)이 발생할 가능성.
- 유동적인 액터 추가/삭제:
- 런타임 중에 클러스터 상태가 변할 경우, 동기화 문제 발생 가능.
결론
ULevel::CreateCluster는 액터를 클러스터로 묶어 가비지 컬렉션 효율성을 극대화하는 함수입니다. 특히 쿠킹된 빌드에서 성능과 메모리 사용량을 최적화하는 데 효과적입니다. 클러스터링 가능한 액터와 불가능한 액터를 적절히 분리하여 관리하며, 이를 통해 불필요한 참조 오버헤드를 줄이고 실행 성능을 높일 수 있습니다.