▲ 블루홀 테라본부 콘솔포팅 TFT 이해찬 프로그래머

지난 4월 3일 북미 PS4와 Xbox One으로 출시한 '테라'는 기술적인 면에서 눈여겨볼 수 있는 시도를 한 타이틀이 됐다. 이미 업데이트가 종료된 '언리얼엔진3'를 현세대 콘솔 기기에 이식한 것이기 때문이다. 콘솔 기기에서의 온라인 플레이를 고스란히 지원하는 것은 물론, 최적화 과정에 많은 시도와 개발 과정이 있었음은 능히 짐작할 수 있는 시도이기도 했다.

테라 콘솔 버전의 이식을 진행한 블루홀의 이해찬 프로그래머와 홍상혁 프로그래머는 NDC 2018 현장에서 테라를 PS4와 Xbox One으로 이식하며 고민하고 시도했던 과정을 공유하고자 단상에 섰다.

특히, 콘솔포팅에 대한 전략, 팀 빌딩이나 일정 수정 그리고 기술적 결정 사항들을 테라 콘솔에서 사용했던 사례를 들어 전달하고자 했다. 그리고 언리얼엔진3를 현세대 콘솔에 이식할 때 최적화 방법과 멀티 스레드 로직 최적화 과정을 청중에게 전달한다.

2016년 개발을 시작하여 2018년 4월 3일 PS4 와 Xbox One으로 런칭한 테라 콘솔 버전의 시작은 2015년 겨울로 거슬러 올라간다. 첫 제안은 테라의 북미 서비스를 담당하는 엔매스엔터테인먼트로부터 들어왔다. 엔매스는 당시 네버윈터라는 MMORPG가 Xbox One으로 출시되었다는 이야기를 블루홀에 전하며 테라의 콘솔버전을 제안했다.


초기에는 외주 업체를 통해 콘솔 포팅을 하는 방법을 고려했으나 결론적으로는 불발되었다. 지속적인 온라인 서비스를 진행해야 했기 때문에 이와 관련한 여러 문제가 있었다. 해결할 수는 있었지만, 서비스를 위한 런처, 페치 시스템, 운영, 유지보수 등은 블루홀이 담당해야 했기 때문이다.

또한, 핵심 기술을 외주로 해결하는 것은 블루홀이 가지고 있는 방향성과도 일치하지 않았다. 이러한 이유 때문에 테라 콘솔 포팅은 TF를 꾸려서 직접 진행하는 것으로 결정됐고 2016년 3월 4일 발족한 TF는 연구 프로젝트로 처음 시작했다.


하지만 언리얼엔진3로 제작된 테라를 이식하는 데에는 많은 고민의 과정이 필요했다. 언리얼엔진3는 PS3, Xbox 360용 게임 엔진이었기 때문이다. 결국 두 가지 중 하나의 선택을 해야만 했다. 기존 엔진을 개조할 것인지. 아니면 테라를 언리얼엔진4로 다시 만들 것인지 말이다.

개발 자원을 고려했을 때, 엔진이 바뀐다는 선택은 결국 아트 지원이 필요하고 개발 인력이 들어간다는 것을 의미한다. 하지만 콘솔 TFT는 소규모 인력의 R&D 팀이었고 테라본부의 지원도 기대하기 어려웠다. 따라서 테라 콘솔 포팅은 테크파워를 통해 언리얼엔진3를 개조하는 선택을 하게 된다.


하지만 막상 언리얼엔진3를 개조하여 포팅하려고 보니, 다른 문제가 발생했다. 언리얼엔진3의 업데이트는 2012년 종료되었지만, 에픽게임즈가 외주를 통해 현세대 콘솔 지원을 가능하게 해서 포팅에는 무리가 없었다. 그리고 테라를 포팅하려고 시도했을 때에는 구형 언리얼엔진3를 사용하고 있는 것을 발견했다.

엔진 업데이트도 2010년에 중지한 상태였으므로, 단순 이식도 어려운 상태였다. 플랫폼 레이어의 코드만 가져와서 해결될 것이 아니라, 다른 엔진 코어까지 가져와야 하는 상황이었다. 따라서 TF는 'UE3-Unsupported'에서 테라를 다시 만드는 방법으로 콘솔 버전 제작을 결정하게 된다


이후 출시까지 2년 동안 테라 콘솔 TFT는 개발에 돌입한다. 먼저 'UE3-Unsupported'를 PS4 데브킷엣더 먼저 돌려보는 과정을 거쳤다. 이후 최소한의 샘플게임을 PS4에서 구동하는 것에 성공했다. 프로토타입은 엘린이 PS4로 구현된 형태였으며, 이식의 가능성을 확인하는 단계였다.

가능성이 확인된 이후에는 새로운 프로그래머를 2명 더 영입하여 본격적인 이식에 들어갔다. 기존 UI GFX버전을 올리고 패드 입력계를 구현하는 것은 물론, 기존 아트 리소스 로딩, 엔진 버전이 달라지면서 필요해진 초기 작업 등을 진행했다. 이외에도 모든 테라의 콘텐츠 코드 이식 및 PS4에 대한 네트워크 작업도 진행했다.


작업 중 미국을 방문하여 'UE3-Unsupported'를 개발한 Hardsuit Labs로부터 컨설팅을 받기도 했다. 이는 외주가 아닌 컨설팅의 형태로 접근했으며, 전문가에게 빨리 배우자는 의미에서 진행했다. 결과적으로 팀은 두 팀으로 나뉘어 개발을 진행하게 됐다. 한국팀은 UI 개발을 담당했으며, 미국 팀은 PS4 개발 안정화를 담당했다.

이후 2016년 9월부터는 본격적인 프로덕션 단계를 준비하는 과정을 거쳤다. 팀의 규모를 늘리기 시작했으며, 콘솔용 배포 프로세스와 로컬라이징, 플랫폼 서버 연동 등을 준비했다. 이외에도 UI 개발 가속화, 전투경험 개선과 엔진 버그 수정까지 개발에 많은 노력을 들였다.


이후에는 소니와 MS의 출시 기준을 맞추는 TRC(Technical Requirements Checklist) / XR(Xbox Requirements) 단계를 거쳤다. 각 플랫폼마다 마스터 패키지에 대한 체크 리스트를 의미하며, 이는 모바일에서의 앱 심사와 유사한 것이다. 심사를 받는 과정에는 꽤 오랜 시간이 걸렸다. 테스트 필요 도구를 제공해야하고, 면제 요청이 가능은 하지만 절차가 많고 통과가 쉽지 않았다.

테라 콘솔은 이 과정에서 약 1년의 시간이 소요됐다. 일반적으로 4~5번은 떨어질 것을 고려해야 한다. 두 곳 모두 떨어지기는 했지만, 리포트에 구체적으로 고칠 부분을 명시해 주었기에 문제점을 수정할 수 있었다. 2017년 1월부터 5월까지는 중요도가 낮은 일부를 제외하고 모든 UI가 개발을 마쳤다. 로딩 최적화도 완료되었고 내부적으로는 여름 출시를 바라보기도 했다.


하지만 문제는 다른 곳에서 나왔다. 로딩 최적화는 마쳤지만, 프레임이 문제였다. 이후 9월까지 계속해서 최적화를 하려는 노력을 기울였으며, PAX에서의 시연을 마친 뒤에는 성공적인 반응을 얻기도 했다. 그리고 나서는 9월을 기점으로 폴리싱 작업에 돌입했다. 정말 중요한 것이 아니면 추가 기획을 받지 않았고, 버그수정과 최적화만 진행했고 출시를 위한 작업에 들어갔다.



테라 콘솔 버전의 개발에서 가장 문제가 되었던 것은 최적화 부분이었다. 콘솔버전에서의 최적화 문제가 없었다면 아마 더 빨리 출시할 수 있었을지도 모른다. 이는 언리얼엔진3로 제작한 MMORPG의 한계이기도 한다. 동적 데이터가 많은 MMORPG이기 때문에 간헐적으로 프레임 드랍이 유발된다. 또한, UI를 전부 다 메모리에 올려놓는 것이 불가능하다. 심지어 한 번에 하나의 파일만 로딩할 수 있으므로 다른 파일을 로딩 중에 UI를 오픈하면 순간 게임이 멈추는 문제가 발생하기도 했다.

PC 테라는 이러한 문제를 커스텀 로딩 API를 이용하여 활용하고 있었고, 로딩시간을 짧게 하는 것에 주안점을 두고 있었다. 문제는 이 커스텀 API를 콘솔로 포팅하려니 문제가 생긴다는 점. 시크 프리(Seek-Free)가 강제되었는데, 시크프리를 강제하는 것은 광학 디스크 실행을 가정한다는 점이다. 광학 디스크는 순차적으로 읽을 때 속도가 나오는 저장장치이며, 이는 오브젝트 단위의 로딩이 불가능해지고 항상 풀 패키지를 읽어야 한다는 것을 의미한다.


이러한 점은 기술적인 난제였다. 가장 좋은 방법은 하나의 파일이 용량이 크지 않도록 잘 관리하는 것이며, 아트 리소스를 고치는 게 정답이 된다. 하지만 PC 서비스와 일관성을 유지해야 했기에 수정은 불가능한 상태였다. 그래서 콘솔 포팅을 위해서는 시리얼 라이즈 로직을 다 손봐야만 했다. 다만, 전부 수정하는 것은 어려워, 테라 콘솔만의 방법으로 해결하려 했다.

테라 콘솔은 이 난제를 파티클 하나하나가 한 파일에 들어가도록 설계하여 수정했다. 쿠깅을 하기 전에 패키지를 쪼개고, 쪼개진 파일로 쿠킹을 시도했다. 다시 쿠킹된 파일은 약 10MB 단위의 파일로 합쳐, 여러 개를 차곡차곡 쌓아 컴포짓 파일을 구성한다. 그리고 매핑 테이블을 바꿔, 컴포짓 파일의 특정 메모리 주소를 리턴할 수 있도록 구성한다. 이러한 과정을 통해 결과적으로 작은 파일 하나를 로딩하는 것처럼 인식시켜 해결할 수 있었다.


다음으로 최적화에 고민한 것은 멀티 스레드 부분이었다. 현세대 콘솔은 1.6Ghz, 8코어로 구성되어 있다. 성능 자체로 본다면, 현세대 CPU의 반 정도의 수준이다. 게다가 언리얼엔진3는 오직 코어 2개만 사용하는 엔진이라는 한계도 가지고 있었다. 따라서 콘솔 버전을 포팅함에 있어서 멀티 스레드 최적화는 선택이 아닌 필수적인 요소로 자리 잡았다.

테라 콘솔팀은 멀티 스레드 병렬화를 통해 해결하고자 했다. 이 부분에서 약 반년이 넘는 시간이 소요됐고, 현재도 계속해서 작업 중에 있는 부분이다. 테라 콘솔은 지속적인 라이브 서비스를 예정하고 있으므로, PC 라이브의 서비스 코드를 지속적으로 가져와야 한다는 전제 조건이 있는 상태였다. 그래서 기존 코드 구조를 최대한 유지할 필요가 있었다. 마침 시간도 부족했기에 포크 앤드 조인(Fork and Join)으로 병렬화를 진행하는 것으로 결정했다.


포크 앤드 조인은 특정구간을 쪼개서 병렬화시키고 다 끝나야만 다음 스탭으로 넘어가는 방법을 의미한다. 최적화의 끝을 가고 싶을 때 선택하는 방법은 아니지만, 시간대비 효율을 가져가기에는 효과적인 선택지였다. 포팅에서의 실제 구현은 Lambda를 활용해서 진행했다. Lambda 연산을 어싱크 태스크 매니저에 쏘기만 했으며, 30개 정도가 모이면 포크하는 형태다.

앞 뒤로만 Lambda 연산을 넣고 기존 코드를 보존할 수 있는 형태로 구현할 수 있었기에보다 빠르고 사용하기 용이한 형태로 구현할 수 있었다. 이후 월드 틱(World Tick)에서는 싱크하는 함수를 넣기만 하면 된다. 싱크 내붸서는 태스크를 분배한 것들이 끝나기를 기다리며, 시작이 안 된다면 끌어와서 처리하는 형태로 설계했다. 이렇게 모든 태스크가 끝이 나면, 모아둔 콜백을 처리한다.

이와같이 테라 콘솔 팀은 다양한 최적화 과정을 거쳐서 게임을 출시했다. GFX tick을 엔진 Tick과 분리하고, 포크 앤 조인 방식으로 최대한 기존 코드를 유지하며 빠르게 병렬화를 할 수 있었다. 크리티컬 섹션은 락과 콜백 등으로 처리하려 했으며, 때에 따라서 로직 자체를 수정하는방식으로 최적화를 위해 노력했다.


최적화 과정을 통해 1Tick 동안 CPU 사용상황은 크게 개선됐다. 테라 로직과 언리얼엔진3 로직을 두 코어가 사용하던 과거의 환경에서 테라 게임 로직, GFX(UI) 로직, 언리얼엔진3 로직과 랜더링 로직까지 모든 코어에서 사용하는 형태로 최적화를 할 수 있었다.

강연자는 자신들의 최적화 과정을 정리하며 '언리얼엔진3 게임을 현세대 콘솔에 이식하는 것은 불가능한 일이 아니다'라고 표현한다. 'UE3-Unsupported' 활용과 전문가 컨설팅으로 빠르게 기반을 만들 수 있고, TRC / XR은 인내심으로 대응할 수 있었다. 그리고 최적화 문제가 있었음에도 어떻게든 해결할 수 있었다. 또한, 현재 100만 장이 판매될 정도로 어느 정도의 시장성도 확보한 상태라고 설명했다.



▲ 블루홀 테라본부 홍상혁 프로그래머

다음으로 자리한 블루홀 테라본부의 홍상혁 프로그래머는 콘솔 TFT를 겸직하며 진행했던 멀티 스레드 랜더링의 필요성과 구조의 구체적인 내용을 청중에게 전했다. 앞서 있었던 강연에서 콘솔 최적화 부분을 조금 더 세밀하게 들어간 형태다.

언리얼엔진3로 개발한 게임을 현세대 콘솔에 이식하는 과정에서는 멀티 스레드 렌더링이 반드시 요구된다. 이는 현세대 콘솔 기기의 하드웨어적 한계, 언리얼엔진3 렌더링 로직의 한계가 존재하기 때문이다. 현세대 콘솔들은 PC성능과 단순 비교를 했을 때, 1/2 정도의 성능을 가진다. 다만, PC 대비 물리적인 코어의 개수가 많다. 따라서 콘솔 최적화는 CPU 코어 수를 활용한 접근과 CPU 모듈 공유 캐쉬를 고려한 접근이 요구된다.


언리얼엔진3는 로직에서 부하가 걸리는 한계점이 있다. 메인 스레드와 렌더링 스레드로 분리되어있는 구조에서 렌더링 스레드에서 RenderCommand 처리 부하가 발생한다.렌더링 로직의 불균형은 렌더링 패스 단위의 멀티 스레드화가 불가함을 의미한다. 그러므로 콘솔 최적화 과정에서는 로직 멀티 스레드화를 고려한 접근 방식이 필요하다.

테라 콘솔의 최적화는 포크 앤드 조인(Fork and Join) 방식으로 진행했다. 앞의 강연에서 언급했던 것과 마찬가지로, 코드의 흐름을 해치지 않는 선에서 병렬화가 가능한 장점이 있었기 때문이었다. 하지만 반대로 한계점은 있었다. 조인의 시간이 길어지면 병렬화 효과가 줄어들었고, 병렬화에 딸려오는 크리티컬 패스를(Critical path)를 줄이기 위해서 잡을 작은 단위로 분할했을 때, 소프트웨어적인 컨텍스트 스위치(Context Switch, CPU (중앙 처리 장치)를 하나의 프로세스 또는 스레드에서 다른 스레드로 전환하는 것)에서 비용이 발생했다. 이는 하나의 잡을 처리하는데 준비가 더 길어지는 것을 의미한다.


콘솔 포팅을 진행하며 개발팀은 렌더링 로직을 병렬화할 필요성을 찾았다. 언리얼엔진3 렌더링 로직에 한계로 던전에서 5 FPS도 나오지 않는 결과가 나왔다. 이는 멀티 스레드 렌더링이 고려되어있지 않기 때문이었고, 일반적인 렌더 패스 단위의 멀티 스레드 렌더링으로는 원하는 성능을 달성할 수 없었다.

멀티 스레드 렌더링은 다음과 같은 과정을 통해서 이루어진다. 크게는 CPU 렌더링과 GPU 렌더링이다. CPU 렌더링에서는 GDI API를 이용한 드로우 콜 백 패킷 생성, API 내부에서의 버퍼 기록이 이루어진다. GPU 렌더링에서는 기록된 드로우 커맨드 버퍼를 GPU에서 처리하는 과정을 거치게 된다.


하드웨어에 따라서 다를 수 있지만, 멀티 스레드 렌더링에서는 외부 API가 제공된다. DX 11의 커맨드 리스트와 커맨드 올로케이터 같은 것들이 예다. 또한, GPU가 실행된 시점을 제공하는 내부 API 또한 제공된다. 일견 보기에는 더 많은 일을 할 수 있는 것처럼 보이지만, 이는 하드웨어 최적화를 프로그래머에게 맡기는 구조이기도 하다. 따라서 멀티 스레드 렌더링은 잘못 사용할 경우, 싱글 스레드보다 낮은 퍼포먼스가 나올 수 있다.

테라 콘솔의 경우는 렌더링 최적화를 위하여 몇 가지 개선책을 도입했다. 멀티 스레드의 구조를 파악하고 렌더링 스테이트를 메인 스레드에 반영할 수 있는 FDrawListTask Class, 태스크 객체 관리를 위한 드로우리스트 태스크 매니저(DrawListTaskManager)를 도입했다. 그리고 이를 통해 렌더링에 따른 프레임 드랍을 개선할 수 있었다.


부가적인 렌더 패스 단위는 태스크화를 거쳐, UI 랜더링을 위한 스케일폼 UI 렌더(Scale form UI Render), 포스트 프로세스(Post Process), 다이렉트 라이트의 그림자 부하를 줄이기 위한 섀도우 렌더링(Shadow Rendering), 성능 확보를 위해서 반드시 적용해야 했던 이닛뷰즈(Init Views) 등을 태스크화 하여 프레임을 확보할 수 있었다.


테라 콘솔에서 CPU와 GPU 최적화를 위해 적용한 기술들을 설명한 강연자는, 마지막 정리를 통해 '콘솔 개발에는 멀티 스레드 지식이 반드시 요구된다'고 설명했다. 이는 하드웨어와 소프트웨어가 발전하며 경시해왔던 부분이기도 하다. 하드웨어 구조의 지식이나, 어셈블리 디버깅, 메모리, 파일 IO 등 보다 극한을 추구하는 요소들이 개발에 요구되는 것이다.

이는 개발자에게 있어서는 '추구할 만한 고통'이 된다. 마주 해야 하는 어려움은 많이 존재하기는 하지만, 반대로 개발자를 자극할 수 있는 도전인 셈이다. 강연자는 이러한 콘솔 개발의 과정을 통해서 '기술의 극한을 따라가고 있다는 느낌과 보람'을 동시에 얻을 수 있다고 설명했다. 그리고 콘솔 개발은 프로그래머라면 즐길 수 있는 고통이 될 수 있다며 강연을 마쳤다.