PART 11 · 강의 1/7

Fast Geometry Streaming

UE 5.6의 대규모 오픈월드 스트리밍 최적화 기술

01

Fast Geometry Streaming 개요

CD Projekt RED와 공동 개발한 오픈월드 스트리밍 솔루션

Epic Games + CD Projekt RED 공동 개발

Fast Geometry Streaming Plugin은 Unreal Engine 5.6에서 실험적 기능으로 도입된 대규모 오픈월드 스트리밍 솔루션입니다. 더 위처 4 개발 과정에서 CD Projekt RED와 Epic Games가 공동으로 개발했으며, 정적 지오메트리의 빠른 스트리밍을 통해 메모리 효율성을 극대화합니다.

35%
CPU 성능 향상
25%
GPU 성능 향상
60
목표 FPS
핵심 개념

Fast Geometry Streaming은 게임플레이에 영향을 주지 않는 정적 요소(건물, 표지판, 장식물 등)를 대상으로 합니다. World Partition과 병행하여 사용하며, 존 전환 시 끊김을 제거합니다.

02

플러그인 설정 및 활성화

프로젝트에서 Fast Geometry Streaming 사용하기

플러그인 활성화

DefaultEngine.ini
; Fast Geometry Streaming 플러그인 설정 [/Script/Engine.RendererSettings] r.FastGeometryStreaming=1 [FastGeometryStreaming] ; 스트리밍 거리 설정 (단위: Unreal Units) StreamingDistance=50000 ; 메모리 예산 (MB) MemoryBudgetMB=2048 ; 우선순위 모드 PriorityMode=Distance ; LOD 전환 속도 LODTransitionSpeed=2.0

C++ 런타임 설정

C++
// FastGeoStreamingManager.h #pragma once #include "CoreMinimal.h" #include "Subsystems/WorldSubsystem.h" #include "FastGeometryStreaming/FastGeoStreamingTypes.h" #include "FastGeoStreamingManager.generated.h" /** * Fast Geometry Streaming 관리 서브시스템 * 대규모 오픈월드의 정적 지오메트리 스트리밍을 관리 */ UCLASS() class MYGAME_API UFastGeoStreamingManager : public UWorldSubsystem { GENERATED_BODY() public: // 서브시스템 초기화 virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; // 스트리밍 설정 변경 UFUNCTION(BlueprintCallable, Category = "FastGeo") void SetStreamingDistance(float NewDistance); // 특정 영역 우선 로드 UFUNCTION(BlueprintCallable, Category = "FastGeo") void PrioritizeArea(const FBox& Area, EFastGeoPriority Priority); // 스트리밍 상태 조회 UFUNCTION(BlueprintPure, Category = "FastGeo") FFastGeoStreamingStats GetStreamingStats() const; // 메모리 예산 동적 조절 UFUNCTION(BlueprintCallable, Category = "FastGeo") void SetMemoryBudget(int32 BudgetMB); protected: // 현재 스트리밍 거리 UPROPERTY() float CurrentStreamingDistance; // 메모리 예산 UPROPERTY() int32 MemoryBudgetMB; // 활성 스트리밍 요청 TArray<FFastGeoStreamingRequest> ActiveRequests; };
C++
// FastGeoStreamingManager.cpp #include "FastGeoStreamingManager.h" #include "FastGeometryStreaming/FastGeoStreamingSubsystem.h" void UFastGeoStreamingManager::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); // 기본 설정 로드 CurrentStreamingDistance = 50000.0f; MemoryBudgetMB = 2048; // INI 파일에서 설정 읽기 GConfig->GetFloat( TEXT("FastGeometryStreaming"), TEXT("StreamingDistance"), CurrentStreamingDistance, GEngineIni ); UE_LOG(LogFastGeo, Log, TEXT("Fast Geometry Streaming 초기화: Distance=%f, Budget=%dMB"), CurrentStreamingDistance, MemoryBudgetMB); } void UFastGeoStreamingManager::SetStreamingDistance(float NewDistance) { CurrentStreamingDistance = FMath::Clamp(NewDistance, 10000.0f, 200000.0f); // 엔진 스트리밍 시스템에 전달 if (UFastGeoStreamingSubsystem* FastGeoSS = GetWorld()->GetSubsystem<UFastGeoStreamingSubsystem>()) { FastGeoSS->SetGlobalStreamingDistance(CurrentStreamingDistance); } } void UFastGeoStreamingManager::PrioritizeArea( const FBox& Area, EFastGeoPriority Priority) { FFastGeoStreamingRequest Request; Request.Bounds = Area; Request.Priority = Priority; Request.RequestTime = GetWorld()->GetTimeSeconds(); ActiveRequests.Add(Request); // 우선순위에 따라 즉시 로드 트리거 if (Priority == EFastGeoPriority::Immediate) { if (UFastGeoStreamingSubsystem* FastGeoSS = GetWorld()->GetSubsystem<UFastGeoStreamingSubsystem>()) { FastGeoSS->ForceLoadArea(Area); } } } FFastGeoStreamingStats UFastGeoStreamingManager::GetStreamingStats() const { FFastGeoStreamingStats Stats; if (UFastGeoStreamingSubsystem* FastGeoSS = GetWorld()->GetSubsystem<UFastGeoStreamingSubsystem>()) { Stats.LoadedMeshCount = FastGeoSS->GetLoadedMeshCount(); Stats.PendingLoadCount = FastGeoSS->GetPendingLoadCount(); Stats.MemoryUsedMB = FastGeoSS->GetMemoryUsageMB(); Stats.StreamingEfficiency = FastGeoSS->GetEfficiencyPercent(); } return Stats; }
03

World Partition 통합

기존 스트리밍 시스템과의 협력적 운영

Fast Geometry Streaming은 World Partition과 병행하여 작동합니다. World Partition이 게임플레이 관련 액터를 처리하는 동안, Fast Geometry Streaming은 시각적 요소에 집중합니다.

C++
// WorldPartitionFastGeoIntegration.h #pragma once #include "CoreMinimal.h" #include "WorldPartition/WorldPartitionStreamingSource.h" #include "WorldPartitionFastGeoIntegration.generated.h" /** * World Partition과 Fast Geometry Streaming 통합 컴포넌트 * 플레이어 카메라 기반으로 스트리밍 소스를 제공 */ UCLASS(ClassGroup=(Streaming), meta=(BlueprintSpawnableComponent)) class MYGAME_API UFastGeoStreamingSourceComponent : public UWorldPartitionStreamingSourceComponent { GENERATED_BODY() public: UFastGeoStreamingSourceComponent(); // 스트리밍 소스 설정 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FastGeo") float FastGeoStreamingRadius = 30000.0f; // 카메라 전방 우선 로드 거리 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FastGeo") float ForwardPriorityDistance = 50000.0f; // LOD 바이어스 설정 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FastGeo") float LODBias = 0.0f; protected: virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; // 카메라 방향 기반 우선순위 계산 void UpdateStreamingPriorities(); private: // 마지막 업데이트 위치 FVector LastUpdateLocation; FRotator LastUpdateRotation; };
C++
// WorldPartitionFastGeoIntegration.cpp #include "WorldPartitionFastGeoIntegration.h" #include "FastGeometryStreaming/FastGeoStreamingSubsystem.h" #include "Camera/CameraComponent.h" UFastGeoStreamingSourceComponent::UFastGeoStreamingSourceComponent() { PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.TickInterval = 0.1f; // 10Hz 업데이트 } void UFastGeoStreamingSourceComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // 위치 변화 감지 (최적화) FVector CurrentLocation = GetOwner()->GetActorLocation(); FRotator CurrentRotation = GetOwner()->GetActorRotation(); float LocationDelta = FVector::Dist(CurrentLocation, LastUpdateLocation); float RotationDelta = FMath::Abs(CurrentRotation.Yaw - LastUpdateRotation.Yaw); // 의미 있는 변화가 있을 때만 업데이트 if (LocationDelta > 500.0f || RotationDelta > 15.0f) { UpdateStreamingPriorities(); LastUpdateLocation = CurrentLocation; LastUpdateRotation = CurrentRotation; } } void UFastGeoStreamingSourceComponent::UpdateStreamingPriorities() { UFastGeoStreamingSubsystem* FastGeoSS = GetWorld()->GetSubsystem<UFastGeoStreamingSubsystem>(); if (!FastGeoSS) return; FVector Location = GetOwner()->GetActorLocation(); FVector ForwardVector = GetOwner()->GetActorForwardVector(); // 카메라 전방 영역 우선 로드 FBox ForwardPriorityBox = FBox::BuildAABB( Location + ForwardVector * (ForwardPriorityDistance * 0.5f), FVector(ForwardPriorityDistance, FastGeoStreamingRadius, 5000.0f) ); // 스트리밍 힌트 전달 FFastGeoStreamingHint Hint; Hint.PriorityBounds = ForwardPriorityBox; Hint.LODBias = LODBias; Hint.bForceHighestLOD = false; FastGeoSS->SetStreamingHint(Hint); }
World Partition vs Fast Geometry Streaming
  • World Partition: 게임플레이 액터, NPC, 인터랙티브 오브젝트
  • Fast Geometry Streaming: 건물, 지형 장식, 정적 배경 요소
  • 두 시스템은 독립적으로 작동하며 메모리를 공유하지 않음
04

메모리 최적화 전략

메모리 단편화 감소 및 효율적인 리소스 관리

C++
// FastGeoMemoryManager.h #pragma once #include "CoreMinimal.h" #include "FastGeoMemoryManager.generated.h" /** * Fast Geometry 메모리 관리자 * 메모리 풀링과 단편화 방지를 담당 */ UCLASS() class MYGAME_API UFastGeoMemoryManager : public UObject { GENERATED_BODY() public: // 메모리 풀 초기화 void InitializeMemoryPools(int32 TotalBudgetMB); // 메시 데이터 할당 요청 void* AllocateMeshData(SIZE_T Size, EFastGeoMeshType MeshType); // 메시 데이터 해제 void FreeMeshData(void* Data, SIZE_T Size); // 메모리 압박 시 정리 void TrimMemory(int32 TargetFreeMB); // 현재 메모리 상태 UFUNCTION(BlueprintPure, Category = "Memory") FFastGeoMemoryStats GetMemoryStats() const; private: // 메모리 풀 (크기별) TArray<FFastGeoMemoryPool> MemoryPools; // LRU 캐시 관리 TMap<FName, FFastGeoCacheEntry> MeshCache; // 메모리 예산 int32 TotalBudgetMB; int32 UsedMemoryMB; }; // 구현 void UFastGeoMemoryManager::InitializeMemoryPools(int32 InTotalBudgetMB) { TotalBudgetMB = InTotalBudgetMB; UsedMemoryMB = 0; // 크기별 메모리 풀 생성 // 작은 메시 (1MB 이하): 전체의 20% MemoryPools.Add(FFastGeoMemoryPool(1 * 1024 * 1024, TotalBudgetMB * 0.2f)); // 중간 메시 (1-10MB): 전체의 50% MemoryPools.Add(FFastGeoMemoryPool(10 * 1024 * 1024, TotalBudgetMB * 0.5f)); // 큰 메시 (10MB 이상): 전체의 30% MemoryPools.Add(FFastGeoMemoryPool(100 * 1024 * 1024, TotalBudgetMB * 0.3f)); UE_LOG(LogFastGeo, Log, TEXT("메모리 풀 초기화: 총 %dMB 예산"), TotalBudgetMB); } void UFastGeoMemoryManager::TrimMemory(int32 TargetFreeMB) { // LRU 기반으로 오래된 캐시 제거 int32 FreedMB = 0; // 접근 시간 기준 정렬 MeshCache.ValueSort([](const FFastGeoCacheEntry& A, const FFastGeoCacheEntry& B) { return A.LastAccessTime < B.LastAccessTime; }); // 가장 오래된 것부터 제거 TArray<FName> KeysToRemove; for (auto& Pair : MeshCache) { if (FreedMB >= TargetFreeMB) break; KeysToRemove.Add(Pair.Key); FreedMB += Pair.Value.SizeMB; } for (const FName& Key : KeysToRemove) { FFastGeoCacheEntry* Entry = MeshCache.Find(Key); if (Entry) { FreeMeshData(Entry->Data, Entry->Size); MeshCache.Remove(Key); } } UE_LOG(LogFastGeo, Log, TEXT("메모리 정리 완료: %dMB 해제됨"), FreedMB); }
메모리 최적화 핵심 전략
  • 메모리 풀링 - 크기별 풀로 단편화 방지
  • LRU 캐시 - 최근 사용되지 않은 메시 우선 해제
  • 동적 예산 조절 - 시스템 상태에 따른 자동 조정
  • 비동기 로딩 - 메인 스레드 블로킹 방지
SUMMARY

핵심 요약

  • Fast Geometry Streaming은 UE 5.6의 실험적 기능으로, CD Projekt RED와 공동 개발
  • 정적 지오메트리 전용으로 게임플레이 액터는 World Partition이 처리
  • 메모리 단편화 감소와 존 전환 시 끊김 제거가 주요 목표
  • CPU 최대 35%, GPU 최대 25% 성능 향상 달성
  • 메모리 풀링LRU 캐시를 통한 효율적 리소스 관리
주의사항

Fast Geometry Streaming은 UE 5.6에서 실험적(Experimental) 기능입니다. 프로덕션 사용 전 충분한 테스트가 필요하며, 향후 API 변경 가능성이 있습니다.

PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Fast Geometry Streaming 설정

Project Settings에서 Fast Geometry Streaming을 활성화하고, 오픈월드 RPG에서 원거리 지오메트리의 스트리밍 속도 개선을 확인하세요. 기존 Nanite 스트리밍과 비교하세요.

실습 2: 스트리밍 풀 크기 조정

Fast Geometry Streaming의 풀 크기와 우선순위를 조정하여, 빠른 카메라 이동(말 타기, 패스트 트래블) 시에도 팝인이 발생하지 않도록 최적화하세요.

심화 과제: 프로파일링 기반 스트리밍 최적화

stat Streaming과 Unreal Insights를 사용하여 Fast Geometry Streaming의 디스크 I/O, 메모리 사용량, GPU 전송 시간을 프로파일링하세요. SSD vs NVMe 환경에서의 성능 차이를 비교하세요.