TECHARTNOMAD TECHARTFLOW.IO

TECH.ART.FLOW.IO

[주석번역/PPT한글화]horizon forbidden west 공간 효율적 패키징

jplee 2023. 12. 21. 14:07

역자의 말.
언리얼엔진으로 프로젝트를 진행 하면서 머트리얼이나 셰이더에 대한 파인튠 업무를 제외하면 크게 매력적인 업무들이 별로 없기도 하고 해서... 최근엔 계속 CI/CD 나 콘텐츠 패키징 및 자동화 시스템 개발에 더 관심을 갖고 있습니다. 와중에 언제나 놀라움을 주는 개발사인 게릴라게임즈의 호라이즌 포비든 웨스트의 공간효율적 패키징 발표자료를 보고서 숙지하는 기분으로다가 변환?을 해 봤네요.  역시 뚜벅이 개발자님들은 지하철로 퇴근 하면서 슬슬 보시도록 준비했다능.... ㅎㅎ


PPT 한글화 버전 구글드라이브 링크.

Google Slides 로드 중

Google Slides에서 "Space Efficient Packaging for Horizon Forbidden West.pptx" 파일을 엽니다. 몇 분 정도 소요될 수 있습니다.

docs.google.com

호라이즌 제로 던을 개발하면서 얻은 교훈의 결과입니다,
저희는 스트리밍 시스템을 면밀히 살펴보고 파악한 문제를 완화하기 위해 노력하기로 결정했습니다.
자세히 살펴본 결과, 디스크 공간과 메모리 모두에서 공간 효율성을 극대화할 수 있는 패키징 알고리즘을 발견했습니다.
이 알고리즘 덕분에 150기가바이트의 게임이 될 뻔했던 Horizon Forbidden West100기가바이트의 디스크로 출시할 수 있었습니다.
오늘은 이 발견 과정을 살펴보고 패키징 알고리즘과 이를 통해 콘텐츠 전송 방식을 개선할 수 있는 방법에 대해 설명하겠습니다.

2014년부터 프로토타입 작업
오픈 월드 게임이 처음이라 워크플로의 모든 측면을 바꿔야 했습니다.
콘텐츠 제작, 처리, 패키징, 스트리밍

 시작 및 빠른 이동을 제외하고 (로딩 화면 없음)

역자 주 : Corridors 는 복도 또는 회랑을 뜻하는 단어입니다. 데이터 로딩 관점에서
loaded A zone -> corridor -> load B zone 과 같은 설계를 사용하지 않는다는 뜻입니다. 

콘텐츠 구조에 대해 설명하겠습니다.
게임 설정 방법 설명하기
패키징 알고리즘을 위한 단계 설정

활을 든 알로이의 모습입니다.

알로이의 활 중 하나입니다.

다양한 활 종류

그리고 각 활 유형에는 다양한 방식으로 다른 많은 변형이 있습니다.

활은 하나의 최상위 오브젝트로 시작됩니다.

그러나 그것은 그것의 일부인 다른 많은 개체와 연결됩니다. 일부는 같은 파일에 있고 일부는 다른 파일에 있습니다.

하나의 파일에 여러 개의 리본이 있습니다. 아티스트와 디자이너가 모든 활을 동시에 더 쉽게 작업할 수 있어 편리합니다.

각 활에는 매우 큰 전체 개체 네트워크가 여러 파일에 분산되어 있습니다.

이것은 플레이어 장비로, 알로이가 장착할 수 있는 장비를 정의합니다. 각 게임 유형(쉬움, 보통, 어려움, NG+ )에 맞는 로드아웃이 있습니다.
여기에는 이 로드아웃에서 유효한 각 오브젝트에 연결되는 InventoryItemResource 오브젝트가 포함되어 있습니다.

다음은 활 하나에 대한 레퍼런스입니다. 여기에는 다른 파일에 있는 최상위 활 오브젝트에 대한 링크가 있습니다. 물론 플레이어 로드아웃을 항상 로드하지만, 모든 인벤토리 리소스를 항상 로드하는 것이 아니라 현재 Aloy에 필요한 리소스만 로드하고 싶습니다.

다음은 두 가지 클래스에 대한 C++ RTTI 정의입니다. 텍스트 기반 콘텐츠 파일에서 사용할 수 있는 클래스와 속성을 정의합니다.
- 코드에서 포인터 유형을 정의합니다.
- 일반 링크, 포인터가 포함된 오브젝트와 함께 로드됨(Ref_LocalizedTextResource) 스트리밍 링크, 게임 로직에 의해서만 로드됨(StreamingRef_EntityResource)
일반 링크가 가리키는 오브젝트는 항상 링크가 포함된 오브젝트와 함께 로드됩니다(플레이어 로드아웃은 모든 플레이어 로드아웃 아이템과 함께 로드되어야 함). 스트리밍 링크가 가리키는 오브젝트는 상위 레벨 게임 시스템이 요청할 때만 로드됩니다(알로이가 인벤토리에서 활을 선택하면 활이 로드됨).

호라이즌 제로 던에서 얻은 교훈과 호라이즌 포비든 웨스트의 디자인 목표에 대해 말씀드리겠습니다.

중복이 (거의) 없음; 공간이 없습니다.

분석 중에 다음과 같은 패키징 알고리즘을 발견했습니다.
거래 대상
-   파일 간 콘텐츠 공유

-개체 그래프 간 겹침

을 사용하여 원본 파일 구조를 최적의 콘텐츠 레이아웃으로 대체합니다.
이 프레젠테이션에 사용할 콘텐츠 예시부터 시작하겠습니다.

다음은 Aloy의 활 네 개를 표현한 것입니다.
같은 색을 가진 개체는 같은 파일에 있습니다.
그래프가 훨씬 단순화되어 깊이가 3단계로만 표시되고 개체 수가 크게 줄었습니다.
그래도 패키징 알고리즘을 설명하는 데는 효과적입니다.

이렇게 하면 첫 번째 활에서 연결된 개체가 강조 표시됩니다.

이제 두 번째 활을 보면 몇 가지 새로운 개체가 나타나지만 첫 번째 활과 공유되는 개체도 볼 수 있습니다.
그러나 여기에는 고유한 개체도 있습니다.

세 번째 활은 고유한 오브젝트 외에 첫 번째 및 두 번째 활과 콘텐츠를 공유하며, 세 번째 활도 마찬가지입니다.

마지막으로 네 번째 활은 다른 세 개의 활과 겹칩니다.
각 활은 고유한 리소스 옆에 있는 다른 활과 서로 다른 객체 하위 집합을 공유한다는 점에 유의하세요.

패키징 알고리즘은 다음 세 단계로 구성됩니다.

 

방금 만든 그래프로 돌아가서 추상적인 표현을 만들어 보겠습니다.

다음은 추상 그래프입니다. 활을 나타내는 루트 객체에는 여전히 이름이 있고, 객체 노드에는 해당 파일을 나타내는 색상이 있습니다.
그러나 이러한 정보는 실제로 필요하지 않으므로 필요한 정보만 남기고 나머지는 줄이겠습니다:
객체와 그 관계

다음은 개체 그래프의 최종 표현입니다.
빨간색 노드는 루트 개체입니다.
파란색 노드는 일반 객체입니다.

이 단계에서는 그래프를 분할합니다.
첫 번째 루트부터 시작하겠습니다.

루트 객체에 간단한 문자로 레이블을 지정하는 것으로 시작하겠습니다.
내부적으로는 간단한 정수를 사용하지만, 이렇게 하면 더 쉽게 볼 수 있습니다.
이제 'A'에서 시작하여 그래프를 가로지르며 마주치는 각 객체를 'A'가 도달할 수 있는 것으로 표시하겠습니다.

이제 'A'에서 도달할 수 있는 모든 노드가 표시되어 있습니다.
물론 이 루트를 통해 노드의 하위 집합만 찾았습니다.
두 번째 루트로 계속 진행하겠습니다.

'A'가 고유하게 참조하는 일부 객체를 식별했습니다.
또한 'A''B'가 참조하는 여러 객체도 확인했습니다.
'C'에 대한 그래프를 계속 살펴봅시다.

이제 여러 가지 조합을 사용할 수 있습니다:
루트 'C'가 독점적으로 사용하는 노드의 경우 'C'
'A''C'가 공유하는 노드의 경우 'A C'
지금까지 살펴본 세 루트가 모두 공유하는 노드의 경우 'A B C'입니다.
마지막 루트를 해봅시다.

이렇게 하면 다시 한 번 'D'로만 도달할 수 있는 여러 개의 물체가 표시됩니다.
하지만 각 활에서 도달할 수 있는 오브젝트인 'C D''A B C D'도 표시됩니다.
모든 노드를 한 번에 살펴봅시다.

여기 라벨링 단계의 최종 결과가 있습니다.
전체적으로 4개의 활 사이에 사용된 전체 오브젝트 세트에 대해 9개의 서로 다른 레이블이 생겼습니다.
 

이제 그룹 그래프를 만들겠습니다.

중단한 부분으로 돌아가서 레이블별로 개체를 그룹화하는 것부터 시작하겠습니다.

다음은 9개의 개체 세트가 일렬로 깔끔하게 정리된 모습입니다.
링크가 루트별 개체에서 시작하여 오른쪽의 공유 개체를 향해 오른쪽으로만 이동하는 것을 볼 수 있습니다.
이름을 다시 추가하면 처음 찾은 개체를 그룹화된 상태로 볼 수 있습니다.

이제 이름을 볼 수 있으므로 무작위 오브젝트가 그룹화되었음을 알 수 있습니다.
폭발, 피해 수식어 및 피해 값을 *클릭*합니다.
true 와 UI 설명 리소스를 *클릭*합니다.
화살표 유형과 튜토리얼 메시지를 *클릭*하면 모든 활이 공유되는 것으로 보입니다.
디자이너와 아티스트에게 설명하는 것은 말할 것도 없고, 이러한 리소스를 그룹화해야 한다는 것을 수동으로 알아내지 못했을 것입니다.
이제 그룹 그래프를 만들어 보겠습니다.

이제 각 고유 레이블에 대한 그룹 개체를 만들었습니다.
그 안에 연결된 객체를 추가했습니다.
그리고 그룹 링크를 만들었습니다.
그룹 외부의 개체에 대한 링크가 있는 각 개체에 대해 해당 그룹 간에 링크를 만듭니다.

이제 각 그룹을 루트 레이블이 포함된 그룹의 조합으로 로드할 수 있습니다.
여기에서는 첫 번째 활과 그 객체를 볼 수 있습니다.

두 번째 활은 필수 그룹이 상당히 다른 활입니다.

물론 세 번째 활은 또 다른 그룹으로 로드됩니다.

네 번째 활도 마찬가지입니다.

물론 이 활을 함께 적재할 수도 있습니다. 여기에는 첫 번째 활과 두 번째 활이 있습니다.

또는 두 번째와 네 번째 활을 적재하여 또 다른 그룹을 만들 수 있습니다.

셰이더 컴파일이 작동하는 방식 때문에 동일한 코드가 생성될지 알 수 없는 순열이 많이 생성됩니다.

1,700만 개의 오브젝트가 있는 100기가바이트 게임에서 1분 이내에 실행됩니다.
그래프 분할 알고리즘은 O(#N * #M)으로 실행되며, 여기서 N은 루트 정점의 집합이고 M은 모든 정점의 집합입니다.

그룹 규모 분포를 살펴봅시다.
상당한 수의 소규모 그룹
4000개의 32바이트 그룹. 단일 팩트 값, 조인트 ID, UUID가 있는 기타 작은 개체 및 그 안에 몇 개의 작은 값이 있습니다.

당사는 수년에 걸쳐 이러한 기능에 투자해 왔으며, 그 중 일부는 처음에는 관련 없는 이유로 투자했습니다.
하지만 이를 통해 콘텐츠를 마음대로 이동할 수 있는 유연성을 확보할 수 있었습니다.
그리고 궁극적으로 이를 충분히 활용할 수 있는 기회를 제공했습니다.

아직 여러 가지 흥미로운 주제에 대해 이야기하지 못했습니다.
앞으로 이러한 주제에 대해 여러 차례 프레젠테이션을 진행할 계획입니다.
계속 지켜봐 주세요!

채용 중입니다!
많은 공석이 있지만 [클릭] 특히 도구, 워크플로, 빌드 시스템 등을 프로그래밍하거나 설계하는 데 관심이 있으신 분들에게 추천합니다. 저와 이야기를 나눠보세요!
할 일: 업데이트