GRAPHICS PROGRAMMING

Actor Cluster Created 에 대하여...

jplee 2025. 1. 21. 01:53

언리얼 엔진의 Actor Clustering Enabled 에 대해서 살펴 보면...

주요 특징

  1. 액터 그룹화:
    • 서로 가깝거나 관련된 액터들을 하나의 클러스터로 묶습니다.
    • 묶인 클러스터는 스트리밍이나 렌더링 시 하나의 단위로 취급됩니다.
  2. 레벨 스트리밍 최적화:
    • 클러스터 단위로 액터를 로드하거나 언로드하여, 씬의 메모리 사용량과 성능을 최적화합니다.
    • 필요한 지역(클러스터)만 활성화하므로 대규모 월드에서 성능 병목을 줄이는 데 도움을 줍니다.
  3. 렌더링 효율성:
    • 묶인 액터들이 동일한 조건에서 평가되기 때문에, LOD(레벨 오브 디테일)Occlusion Culling(차폐 제거) 같은 최적화가 더 효과적으로 수행됩니다.
  4. 자동 클러스터링:
    • Unreal Engine은 클러스터링을 자동으로 수행하며, 위치와 관계를 기반으로 액터를 클러스터로 그룹화합니다.
    • 이 프로세스는 디자이너가 수동으로 관리하지 않아도 되도록 설계되었습니다.

사용 사례

  • 오픈 월드 게임:
    • 대규모 맵에서 플레이어가 특정 지역에 있을 때 해당 지역의 액터만 로드하거나 렌더링.
    • 예: 오픈 월드 RPG 게임에서 플레이어의 위치에 따라 도시, 숲, 건물 등의 클러스터가 개별적으로 활성화됨.
  • 레벨 파티셔닝:
    • 팀 기반의 개발 환경에서, 액터를 논리적으로 묶어 관리하기 쉽도록 함.
  • 메모리 관리:
    • 대규모 씬의 메모리 사용량을 줄이고, 씬의 복잡도에 비례한 성능 병목을 방지.

클러스터링 활성화

Actor Clustering Enabled는 레벨 스트리밍과 연관된 시스템 설정으로, 보통 다음 방법으로 활성화됩니다:

  1. World Partition 설정:
    • 레벨 설정에서 World Partition 기능을 사용하면 클러스터링이 기본적으로 활성화됩니다.
  2. 커스텀 클러스터링 제어:
    • 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 함수의 목적

  1. GC 성능 최적화:
    • 개별 액터를 추적하는 대신, 클러스터로 묶어서 관리하여 가비지 컬렉션 성능을 높임.
    • 클러스터 내 액터에 대한 참조 오버헤드를 줄여 효율성 향상.
  2. 메모리 관리 개선:
    • 액터를 그룹화하여 메모리 단편화(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);  // 클러스터링 불가능한 액터
    }
}

레벨에 포함된 모든 액터를 순회하며 두 가지로 분류:

  1. 클러스터링 가능한 액터:
    • CanBeInCluster() 함수가 true를 반환하는 액터.
    • ClusterActors 배열에 추가.
  2. 클러스터링 불가능한 액터:
    • ActorsForGC 배열에 추가되어 일반적인 가비지 컬렉션에 포함.
  3. 클러스터 생성
if (ClusterActors.Num())
{
    ActorCluster = NewObject<ULevelActorContainer>(this, TEXT("ActorCluster"), RF_Transient);
    ActorCluster->Actors = MoveTemp(ClusterActors); // 클러스터 액터 배열 이동
    ActorCluster->CreateCluster(); // 클러스터 초기화
}

클러스터링 가능한 액터가 있으면:

  • ULevelActorContainer 객체를 생성.
    • 이 객체는 클러스터 내 모든 액터를 참조합니다.
  • MoveTemp로 클러스터 액터 배열을 이동.
  • CreateCluster()를 호출해 클러스터를 초기화.

4. 상태 갱신

bActorClusterCreated = true;

클러스터 생성이 완료되었음을 표시하여 중복 생성 방지.

중요한 구성 요소

  1. 클러스터링 가능한 액터:
    • CanBeInCluster() 함수가 true를 반환하는 액터만 클러스터에 포함됩니다.
    • 클러스터링 불가능한 액터는 ActorsForGC 배열에 저장.
  2. ULevelActorContainer:
    • 클러스터링된 액터를 보관하는 컨테이너 역할.
    • 가비지 컬렉션 시, 이 컨테이너를 통해 클러스터링된 액터들을 효율적으로 처리.
  3. ActorsForGC:
    • 클러스터링 불가능한 액터들을 따로 관리하기 위한 배열.

CreateCluster의 장점

  1. 가비지 컬렉션 최적화:
    • 수많은 개별 액터를 관리하는 대신, 클러스터 단위로 참조를 처리하여 효율성을 높임.
  2. 메모리 단편화 감소:
    • 클러스터링으로 액터들의 메모리 사용을 더욱 일관되게 관리.
  3. 쿠킹된 빌드에서 효과적:
    • 쿠킹된 빌드는 런타임 성능이 중요하므로 클러스터링으로 오버헤드 감소.

제약 사항 및 고려 사항

  1. 클러스터링 조건:
    • 클러스터링 가능한 액터는 제한적입니다(CanBeInCluster()에 의존).
    • 투명하거나 특별한 처리가 필요한 액터는 제외될 수 있음.
  2. Z-Fighting:
    • 너무 많은 액터가 클러스터에 포함되면 깊이 충돌(Z-Fighting)이 발생할 가능성.
  3. 유동적인 액터 추가/삭제:
    • 런타임 중에 클러스터 상태가 변할 경우, 동기화 문제 발생 가능.

결론

ULevel::CreateCluster는 액터를 클러스터로 묶어 가비지 컬렉션 효율성을 극대화하는 함수입니다. 특히 쿠킹된 빌드에서 성능과 메모리 사용량을 최적화하는 데 효과적입니다. 클러스터링 가능한 액터와 불가능한 액터를 적절히 분리하여 관리하며, 이를 통해 불필요한 참조 오버헤드를 줄이고 실행 성능을 높일 수 있습니다.