UDN
Search public documentation:

ContentStreamingKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > 엔진 프로그래밍 > 콘텐츠 스트리밍

콘텐츠 스트리밍


개요


차세대 콘솔 플랫폼은 그래픽과 계산 능력의 비약적인 향상을 약속합니다. 그러나 이 플랫폼은 큰 메모리 용량을 필요로 합니다. 최대한의 디테일을 성취하려면 차세대 게임은 스트리밍의 사용과 원활한 월드를 지원하여 게임 콘텐츠를 필요에 따라 로드할 수 있어야 합니다.

스트리밍


여기서는 스트리밍 을 다음과 같은 의미로 사용합니다. 시각 예측 방식에 따라 주문형의 처리되지 않은 커다란 콘텐츠 덩어리(일반적으로 수 메가 바이트)를 데이터를 급하게 전환할 필요가 없는 플랫폼의 원시 포맷으로 메모리에 직접 로드하는, 최적화된 로딩입니다.

스트리밍 시스템은 UObject 파생 클래스와 같은 복합 오브젝트 지향 데이터를 지원하는 것을 목표로 하지 않습니다. 레벨의 UTexture 오브젝트(텍스처를 기술하고 있는 200 바이트 크기의 UObject 파생 클래스)는 텍스처와 연관된 밉맵 (mipmap) 이 스트림되는 동안 항상 레벨이 로드된 메모리에 머뭅니다. 즉 스트리밍 지원은 복합 데이터 구조가 아니라 대량 콘텐츠만을 대상으로 합니다.

여기서는 이 방법으로 텍스처 mips 을 스트림하고 스트리밍이 필요한 다른 콘텐츠에 대한 패키지 스트리밍 사용만을 대상으로 하고 있습니다. 오디오를 스트림할 계획은 없습니다.

심리스 월드


Unreal Engine 3 는 심리스(Seamless, 이음새 없는, 즉 로딩없이 매끄럽게 이어지는 거대한) 월드를 지원하고 심리스 월드 시스템은 레벨과 관련된 복합 오브젝트 지향 데이터의 동적 배경 로딩 및 언로딩을 목표로 합니다. 이 시스템을 스트리밍과 함께 사용하면 월드와 관련된 모든 데이터를 동적으로 로드시킬 수 있습니다.

Unreal Engine 3 에서의 월드 (UWorld 오브젝트) 는 다수의 레벨로 (ULevel 오브젝트) 구성되어 있고 일반적으로 게임은 수백의 개별 레벨을 포함할 수 있습니다. 이 레벨은 근접 거리를 기준으로, 보다 분명히 말하자면 로드/언로드 트리거 및 기타 다른 기준에 따라 동적으로 로드 및 언로드될 수 있습니다.

레벨은 개별적으로 로드 및 언로드될 수 있는 액터 및 기타 복합 오브젝트 지향 데이터의 세트를 정의합니다. 개념적으로 어느 레벨에 의해 참조되는 모든 오브젝트가 로드되고 C++ 및 스트립트 코드에서 (예. FindObject?) 에서 참조할 수 있는 경우와 참조할 수 없는 경우로 나뉩니다. 원자성 (atomicity) 은 레벨에 의해 참조되는 오브젝트가 다른 오브젝트에 대한 포인터를 포함할 수 있게 하고 이것은 액터, 구성 요소 (components), 머티리얼, 게임플레이 스크립트의 복합 계층에서 아주 빈번하게 일어나는 패턴입니다. 따라서 C++ 및 스크립트 코드는 아직 로드되지 않은 오브젝트에 대한 참조를 처리할 필요가 없습니다.

ULevel 추상적 개념은 로딩 및 기타 파일 작업별 원자를 지원하기 위해 존재하지만 일반적으로 게임플레이 코드에서는 숨겨져 있습니다. 현재 로드된 정적 및 동적 액터를 단일 목록으로 통합하는 UWorld 추상적 개념을 통해 액터 및 게임플레이 기능은 표시됩니다. 따라서 일반적인 게임플레이 코드는 레벨 "경계" 에 대해 고려할 필요가 없으며 액터는 월드내에서 이동하면서 "레벨 변화" 를 하지 않습니다.

비동기 패키지 로딩

심리스 월드 로딩 코드는 배경에서 패키지를 완전히 로드할 수 있는 능력에 기반하고 이 코드는 다른 데이터의 임의적인 조합을 (사전) 로드 및 다시 쓰레기 수집에 의해 제거되는데 사용될 수 있습니다.

상세한 설명

패키지 로딩

Unreal 의 로딩 코드의 핵심은 DLL 을 모델로 한 종속성을 지닌 패키지에 있습니다. 패키지에는 다음 내용이 순서대로 항상 포함됩니다.

  • 패키지 파일 요약
  • 이름 테이블
  • 임포트 테이블
  • 익스포트 테이블

패키지 파일 요약은 기본 정뵈 및 패키지에 저장된 다양한 테이블에 관한 오프셋/크기가 포함됩니다. 이름 테이블에는 모든 직렬화된 이름이 포함됩니다. 익스포트 테이블은 패키지로 직렬화된 모든 오브젝트를 포함하는 반면에 임포트 테이블은 패키지로 직렬화된 도든 직접적 종석성을 포함됩니다.

익스포트 테이블에 있는 각 익스포트 파일은 데이터의 위치가 나타나는 오프셋이 포함됩니다. 익스포트는 일차적 직선으로 생성되고 로드될 수 있는 순서로 패키지에 저장됩니다. 이는 즉 오브젝트의 클래스 또는 아우터(outer) 는 항상 오브젝트 자체보다 전에 정렬됩니다.

패키지를 직접 다루는 C++ 클래스는 ULinker 하위 클래스인 ULinkerLoad 와 ULinkerSave 입니다. ULinkerLoad 는 UObject 을 UObject 이 로드된 패키지에 연결합니다. UObject 과 연관된 링커를 결코 갖지 않는 몇 가지 유형의 UObject 이 있습니다. 예. intrinsic (전용) 클래스와 최고 레벨 패키지. 그 경우 GetLinker() 는 NULL 을 반환합니다. 그러나 이것은 오브젝트가 링커로부터 분리되는 유일한 경우가 아닙니다. 동일한 파일 이름으로 패키지를 저장하면 링커에서 해당 파일이 분리되게 되어 코드 저장시 파일을 안전하게 덮어쓰게 되고 동일한 방법으로 이름을 변경할 수 있습니다. 로더를 재설정하는 또 다른 경우는 패키지가 다른 아우터로 로드되는 경우입니다. 콘솔 플랫폼에서 링커는 메모리를 절약하기 위해 유지되지 않습니다.

일반적으로 관련된 링커를 사용하여 오브젝트를 로드하면 로드 작업이 이미 메모리에 있는 오브젝트를 반환하도록 유발하고, 반면에 관련 링커가 없는 오브젝트를 로드하면 디스크에서의 오브젝트 교체를 유발합니다. 패키는 저장된 후 간접 참조되므로 패키지와 연관된 링커가 없는 경우 변경 사항을 취소하려면 편집기가 패키지의 수동 재로딩을 지원할 필요가 있기 때문에 실제로 코드는 더 복잡해집니다. 스크립트 컴파일도 또한 복합성을 가중시키는 역할을 한다. 콘솔 플랫폼에서 엔진은 항상 먼저 메모리에서 오브젝트를 검색하려 하고 StaticLoadObject 을 통한 단일 오브젝트의 로딩의 경우 DVD 에서 로드되는 작업을 방해하는 것이 실용적이지 않기에 디스크에서 로드되지 않습니다.

단일 오브젝트를 로딩하는 것은 패키지를 통해 오브젝트를 로딩하는 것과 비교해서 약간 다른 코드 경로를 따르고 본 문서에서는 다뤄지지 않습니다. 다음 목록은 엔진이 전체 패키지를 로드할때 일어나는 일을 설명하는 목록입니다.

  • UObject::LoadPackage 가 BeginLoad 를 호출함.
  • UObject::GetPackageLinker 가 호출되어
    • 패키지에 대한 기존 링커의 존재 여부를 확인함.
    • 완전하게 자격없는 파일 이름이 있으면 해결함.
    • "진정한" 파일 이름을 사용하여 ULinkerLoad 오브젝트 생성함.
  • 반환된 ULinkerLoad 는 순차적으로 "모든 오브젝트 로드"/ "모든 익스포트 만들기"에 사용됨.
  • EndLoad 를 호출하여
    • 루프에서 실제 직렬화로 연결되고 PostLoad 가 호출하는
    • 대롱거리는 포인터를 피하려 캐시된 임포트 오브젝트를 임포트 테이블에서 분리함.
    • 대롱거리는 포인터를 피하려 강제로된 익스포트를 익스포트 테이블에서 분리함.

ULinkerLoad 오브젝트를 만들때 연관된 작업을 좀 더 추가하려면:

  • 패키지 파일 요약을 읽습니다.
  • 이름 테이블을 읽습니다.
  • 임포트 테이블을 읽습니다.
  • 익스포트 테이블을 읽습니다.
  • 기존 오브젝트가 편집기의 익스포트 테이블에 잠재적으로 통합됩니다.
  • 모든 임포트 가 확인됩니다(해당 소스 링커에 지명된 익스포트와 일치).

모든 임포트 를 확인하는 것은 링커 오브젝트 만들기를 가속하려 LOAD_NoVerify 를 지정함으로 연기될 수 있지만 이것은 또한 오류 처리가 생각대로 진행되지 않을 것을 의미합니다.

비동기 패키지 로딩

엔진은 비동기적 패키지 로딩을 지원합니다. 더 정확하게 말하자면 엔진은 비동기적으로 링커에 전달된 모든 오브젝트의 로딩을 지원합니다. 이 기능은 UObject::PreloadLinkerAsync 에서 찾을 수 있고 다음을 수행합니다.

  • 적절한 링커에 있는 ULinkerLoad::CreateExport 로 맵되는 ULinkerLoad::CreateImport 를 통해 모든 임포트 를 생성합니다.
  • 모든 익스포트 오브젝트를 만들고 그 데이터를 직렬화합니다 (한번에 1 개씩).
  • PreLoad (실제 직렬화)의 경로가 모든 익스포트로 지정된지를 확인합니다.
  • PostLoad 를 오브젝트로 경로 지정합니다.

이 작업은 시간 제한이 있는 여러 프레임에 걸쳐 진행됩니다. 다음 단계는 이전 단계는 완료되면 실행됩니다.

PostLoad 가 새로운 오브젝트가 생성/로드되는 것을 유발하기에 이 절차는 루프에서 실행됩니다. 더 이상의 작업이 없으면 RF_AsyncLoading 이 비동기적으로 로드/생성된 모든 오브젝트에서 삭제됩니다. 그리고 나서 임포트 오브젝트와 강제된 익스포트는 분리되고 (EndLoad 에서 처럼) 완료 콜백이 호출됩니다.

비동기 로딩 중 생성된 오브젝트는 로딩이 완료될때까지 다른 엔진으로부터 숨겨져야 하므로 RF_AsyncLoading 으로 표시됩니다. 비동기 로딩 코드는 기존 오브젝트를 동일한 위치로 재로드하는데 사용될 수 없고 이런 방식으로 사용되는지 확인합니다.

시크-프리 측면

시크-프리(디스크 등의 탐색시간이 없는 순차적인) 로딩시 모든 임포트가 이미 로드되었다는 것이 보장되므로 임포트 생성 단계는 어떠한 디스크 활동도 초래하지 않습니다. 패키지를 로드하는데 필요한 모든 오브젝트는 패키지의 일부가 아니더라도 익스포트 테이블에서 찾을 수 있습니다. 이것은 저장시 오브젝트를 강제로 이루어지게 하는 RF_ForceTagExp 라 불리는 오브젝트 플래그에 의해 이루어지는데 그렇지 않으면 오브젝트는 패키지로 직렬화되는 임포트 테이블에 머물고 그 다음엔 익스포트 테이블에 속하게 됩니다. 이런한 오브젝트는 특별히 플래그되어 익스포트가 생성될때 기존 패키지에서 로드된 것처럼 만들어지고 이는 즉 로드된 패키지가 아니라 "outer most (가장 외부)" 를 갖게됨을 의미합니다.

"강제로 익스포트" 에 대한 익스포트를 만들기 전에 엔진은 오브젝트가 이미 메모리에 존재하는지 확인하고 그것을 조정 (reconcile) 합니다. 이것은 다중 레벨 경우의 여러 맵이 단 한번 로드될 필요가 있는 동일한 콘텐츠를 공유하게 만듭니다.

비동기 로딩에 대해 참조해야 할 것은 비동기 배경 로딩이 이루어지는 동안 일반 로딩은 이루어질 수 없다는 점입니다. 이 말은 즉 만약 비동기 로드가 아닌 경우 배경 활동은 비동기가 완료될때까지 차단된다는 것을 의미합니다. 쓰레기 수집의 경우도 마찬가지입니다. 쓰레기 수집은 비동기 로딩 동안에는 이루어질 수 없고 로딩이 완료될때까지 쓰레지 수집 작업이 차단됩니다. 자동 시한 쓰레지 수집 코드는 로드시 쓰레기 수집이 차단되는 것을 방지하기 위해 처리되지 않은 요청이 있는 동안 쓰레기 수집의 호출을 피하게 합니다.

시크-프리 로딩은 종속성 해결시(임포트 만들기로도 알려짐) 탐색이 발생하는 것을 방지하기 위해 로드될 패키지에 이미 로드되었다는 것을 보장하지 않는 종속성을 복제하는 방식에 의존합니다. 대부분의 탐색은 payload (페이로드)가 없고 쉽게 복제할 수 있는 UMaterialExpression 과 같은 작은 도우미 오브젝트에 의해 발생합니다. 반면 정적 메쉬 및 사운드 데이터는 크기의 관점에서 보았을때 복제하는데 가장 데이터의 사용량이 많을 것입니다.

보통 텍스처는 복제할 해당 페이로드/대량 데이터를 가지지 않지만 시크-프리 패키지에 넣어질 UObject 데이터와 최소 밉레벨 (miplevels) 을 갖고 나머지를 텍스처 스트리밍 코드를 통해 스트리밍 시킵니다.

공유된 콘텐츠를 가진 패키지를 만들고 먼저 로드시켜 시크-프리 로딩 부분을 거의 희생하지 않고 복제를 줄일 수 있습니다. 이것은 대부분 수동 프로세스에 의해 이루어지고 멀티 플레이어 맵용 오디오를 제외하고 Gears of War 에 대해 별로 활용하지 않았습니다.

네트워킹

네트워킹 코드는 인덱스를 통해 오브젝트 참조와 통신하는 패키지 맵이라 불리는 구조에 의존합니다. 패키지 맵 코드는 오브젝트를 인덱스로 또는 그 반대로의 변환을 하기 위해 오브젝트 링커 인덱스와 최상 레벨 패키지 이름을 사용합니다. 시크-프리 로딩 코드를 통해 로드된 오브젝트는 연관된 링커를 가지지 않아 코드는 시크-프리 패키지의 오리지널 링커 인덱스에 저장될 필요가 있고 그것을 사용하여 패키지 맵을 빌드합니다.

초기 로딩

초기 로딩은 좀 더 알아두어야 할 점을 가지고 있습니다. cooking 절차 중 다음을 수행합니다.

  • Native 클래스를 포함한 스크립트 패키지는 완전히 로드됨.
  • 패키지에 의해 참조되는 모든 콘텐츠는 강제 익스포트를 통해 패키지로 들어감.

이 방식으로 코드가 실행하여 혼합 스크립트/콘텐츠 패키지에 대한 모든 일반 익스포트 후 강제 익스포트가 정렬됩니다. 이것은 일반 익스포트에 대한 모든 데이터를 단일 메모리 블록에 로드하여 해결할 수 없는 교차 참조 관련 문제를 다룰 필요 없게 만듭니다.

CreateExport 는 클래스를 만들고 직렬화하지만 콘텐츠를 만들고 직렬화는 하지 않는 모든 스크립트 패키지 익스포트에서 호출됩니다. 일단 모든 클래스가 만들어지면 스크립트 블록에 생성된 메모리는 해제되고 콘텐츠는 익스포트를 만들고 보통 시크-프리 패키지처럼 슬라이딩 윈도우를 사용하여 만들어진 익스포트를 직렬화할 수 있어 데이터의 비동기 프리패칭 (prefetching) 을 할 수 있습니다.

위의 모든 필수 코드는 모든 native 클래스가 로드된는 것을 확인하기전에 완료됩니다. 이것은 native 클래스에 의해 참조되는 콘텐츠가 시크-프리 방식으로 로드되어 이 단계 중 디스크 액세스가 이루어지지 않음을 확실하게 하기 위함입니다. 여기서의 중요한 점은 모든 native 클래스는 항상 엔진 시작시에 로드되고 이는 즉 기본 속성 또는 직접 코드 참조를 통한 콘텐츠 참조 또한 로드된다는 것을 의미합니다. Native 클래스는 쓰레기 수집되지 않기에 native 클래스에 의해 참조되는 콘텐츠는 항상 존재합니다. 따라서 native 가 아니면 항상 로드될 필요가 없는 native 클래스에서의 콘텐츠 참조를 갖지 않는 것이 매우 중요합니다. 이 경우 좋은 해결 방법은 해당 native 클래스를 하위 클래스로 지정하고 다양한 스크립트 패키지에 있는 하위 클래스의 콘텐츠의 내용을 참조하는 것입니다.

초기 로드시 native 클래스를 포함한 모든 스크립트 패키지를 완전히 로드합니다. Native 클래스와 참조된 내용을 포함하지 않은 패키지에 있는 클래스만을 스크립트하여 맵 파일에 넣습니다. 주어진 월드에 있는 모든 맵에 대해 콘텐츠의 로드가 보장되는 경우 복제를 줄일 수 있는 여러 방법이 있습니다. 예를 들면 콘텐츠를 월드의 지속 레벨로 이동하거나 항상 로드된 패키지를 갖게 하는 방법이 있습니다.

콘솔 쿠킹

콘솔에 대한 cooking 데이터에 관해서는 대량 데이터를 강제 익스포트에서 분리하는 것은 필요로 하는데 그 이유는 일반 패키지가 그 패키지가 의존하는 맵전에 쿡되어져 엔진이 그 패키지의 대량 데이터에 대한 오프셋, 크기, 기타 다양한 정보를 추적하여 시크-프리 레벨 패키지에 다량 데이터를 올바르게 직렬화되게 만들 수 있기 때문입니다. 이것은 또한 패키지의 변경 사항 후 패키지에 의존하는 모든 맵들이 다시 쿡되어지는 것을 필요로 합니다. 쿡커는 맵 이름 목록이 명령라인에서 지정될 수 있게 하고 쿡이 되어지는데 필요한 모든 작업을 처리하고 모든 종속성을 자동으로 처리하는 것을 보장합니다.

좀 더 자세한 정보는 Content Cooking 페이지를 참조해 주십시오.

기타 참고

로드 플래그의 개념은 프로퍼게이션 (propagation) 과 관련된 몇 가지 문제점을 가지고 있지만 깨끗이 해결될 예정입니다. 가장 큰 문제는 패키지내에서 각 오브젝트를 로드하기 위해 사용된 로드 플래그는 원래 해당 패키지에 대한 참조를 가진 패키지를 직렬화하면 절대적으로 발생하는 링커를 만들때 사용된 것이라는 점입니다.

초기 로딩 및 프레임당 작업량은 UObject::Serialize 및 파생 버전과 다양한 << 연산의 성능을 개선함으로 줄일 수 있습니다. 엔진은 구조체의 모든 멤버가 메모리에서 선언된 순서대로 직렬화되고 데이터가 직렬화되어 직접 메모리의 레이아웃에 매핑되어 정렬로 인한 전위 차 또한 직렬화하고 바이트의 순서가 다른 것은 문제가 되지 않는 대량 직렬화의 개념을 지원합니다. 이것은 모든 잠재적인 바이트 순서 변환을 수행하고 또한 플랫폼에 따라 구조체 멤버 정렬이 다를 수 있는 사실을 다루기 위해 코드를 절약하는 것에 의존합니다. 전반적으로 대량 직렬화는 매우 위태로운 구조체이지만 색상 또는 벡터 에레이와 같은 간단한 데이터 유형에는 잘 작동합니다. 현재 엔진은 약 2 곳에서 그것을 사용하고 있고 이상적으로 TTypeInfo 의 개념을 확장하여 기본적으로 직렬화된 어레이가 자동으로 간단한 경우 대량 직렬화를 사용할 수 있게 할 예정입니다.

또 하나의 효과적인 최적화는 스크립트 직렬화를 사용하는 FMatrix 와 같이 SerializedTaggedProperties 를 통해 현재 직렬화하는 일반 데이터 형식을 처리하는 특별한 경우입니다. 'immutablewhencooked' 속성 플래그는 이 과정을 데이터 타입에 대한 커스텀 직렬화 코드가 필요 없이 자동화하는 많은 경우에 사용됩니다.

텍스처 스트리밍

정적 오브젝트의 텍스처 스트리밍 코드는 적용될 오브젝트의 경계 상자의 화면 공간 크기를 확인하고 UV 스케일 팩터를 고려하여 콘솔의 경우 디스크에 압축되어 있는 텍스터 데이터를 비디오 메모리로 스트리밍합니다.

동적 오브젝트는 시각성에 기반아여 텍스처를 스트리밍하고 텍스처 튀어 보이는 것을 피하기 위해 오브젝트의 밉레벨 (mipllevels) 에서 스트리밍을 강제로 하기 위한 다양한 오버라이드가 있습니다. 독특한 텍스처 처리된 캐릭터를 많이 가진 Unreal Tournament 2007 과 같은 빠른 게임에 골격 메쉬가 잘 적응하게 텍스처 스트리밍을 코드를 확장할 계획입니다.

오디오 스트리밍

오디오 콘텐츠를 스트리밍할 계획은 현재 없습니다. 그러나 여러분의 요구 사항을 만족시킬 수 있게 대화 상자 다양성을 크게 증강시키기 위한 cooking 솔류션이 구현됩니다.