최근 게임 엔진이 발전하면서 '최적화'도 화두에 올랐다. 이전에 일일이 개발자들이 고심하면서 한땀한땀 빚어냈던 것들을 엔진 내에서도 편리하게 적용할 수 있도록 기술이 발전했지만, 엔진 자체가 무거워지거나 혹은 그 기술들을 이리저리 짜깁기하면서 비효율적인 처리가 발생하는 사례도 그만큼 많아졌기 때문이다.

유니티에서는 이를 좀 더 빠르게 캐치하고 개발자들이 최적화를 더욱 쉽게 할 수 있도록 프로파일러 구축에 힘써왔다. 유니티6에서도 이러한 기조는 유지, 메모리 프로파일러와 타임라인과 프로필 애널라이저를 한층 개선해 개발자들이 좀 더 쉽게 메모리 점유율의 변동을 시각적으로 체크하고 적절한 조치를 취할 수 있게끔 할 예정이다. 피터 홀 시니어 매니저는 유나이트 2024 현장에서 타임라인과 프로필 애널라이저 등 유니티6에서 선보일 기술이 이전 대비 얼마나 더 효율 좋게 최적화할 수 있는지 설명해나갔다.

▲ 기껏 개발했는데 뭔가가 삐걱이는 순간, 이 현상을 분석해줄 프로파일러가 필요하다

강연에서 그는 가상의 게임 개발사를 테마로 최적화 과정을 설명했다. 그의 분신인 '피트'는 새 게임 스튜디오에서 근무하게 된 개발자로, 여러 동료들과 함께 신작을 개발했다. 어느 정도 완성이 된 상태에서 테스트를 위해 게임을 실행했지만, 생각보다 느리게 구동되고 있었다. 이에 피트는 엔지니어 샘과 함께 작업을 하게 됐다.

최적화를 위해서 가장 먼저 게임이 CPU를 많이 활용하는지, GPU를 많이 쓰는지 알아야 했다. 통상 유니티에서는 CPU가 엔진 코드와 개발자가 작성한 코드, 렌더링 코드를 실행하고 있으며 여기서 GPU가 렌더링 코드를 할당 받아서 처리하는 구조다. 그 중 만약 CPU가 처리에 40ms를 소모하고 GPU가 20ms를 쓴다면, CPU가 처리하는 동안 GPU가 대기하는 양상이 된다. 물론 이와 반대 양상이 생기기도 한다. 이를 쉽게 체크하기 위해 유니티에서는 프로파일러를 활용하고 있다. 여기에 '하이라이트' 모듈을 추가, CPU나 GPU 어디에서 문제가 발생했나 더 쉽게 찾아볼 수 있다.

▲ 어떤 부분에 심각한 문제가 생겼나 좀 더 보기 쉽게 하이라이트 표시가 되어있다

그렇게 해서 GPU에서 병목 현상이 발생했다는 걸 알아챈 두 사람은 이제 GPU 최적화를 위해 유니티의 여러 기능을 활용하기로 했다. 우선 프레임 디버거를 통해 각 프레임이 어떻게 렌더링 되고 있나 체크하기로 하고, 혹은 렌더독이나 여러 플랫폼 특화 툴로 렌더링 코드를 체크해보기도 한다. 통상 GPU에서 병목 현상이 발생하는 이유로는 과중한 셰이더나 과한 밀도의 지형지물, 용량이 과하게 큰 텍스처나 고해상도 옵션 등을 꼽을 수 있고, 이러한 문제는 프로파일러를 분석하면서 처리해나가게 된다.

한편, GPU 과부하가 아닌 렌더 쓰레드에서 문제가 발생했을 여지도 있었다. 유니티에서 그래픽 렌더링은 렌더링 코드 - 렌더 쓰레드 - GPU를 거쳐서 진행되는데, 렌더 쓰레드의 처리 시간이 길어지면 GPU에서 대기 시간이 발생해버리는 일이 있기 때문이다. 이런 상황에서 GPU에서 처리하는 비중을 높인 것이 GPU 상주 드로어고, 이 기능이 더해지면서 CPU 및 렌더 쓰레드의 부하가 발생하는 비율이 줄어들었다.

▲ 렌더링 과정에서 병목 현상은 렌더 쓰레드에서 발생하기도 하는데, 이를 완화해주는 것이 GPU 상주 드로어다

그렇다면 CPU에서 병목 현상이 발생했다면, 어떻게 처리를 해야 할까? 우선 프로파일 분석기를 프로젝트에 추가, 프로파일러에 나온 자료들을 분석해야 한다. 예를 들어 bool 데이터가 있는 스크립트에서 가비지 콜렉터나 가비지 얼로케이션이 주기적으로 발동한다면, 계속 참과 거짓을 체크하는 작업을 반복하고 있는 것으로 의심해볼 수 있다. 이런 식으로 CPU의 할당을 많이 잡아먹고 있는 부분을 수정했다고 해도, 종종 플레이할 때마다 CPU 점유율이 들쭉날쭉해지는 경우가 있다. 그때는 스파이크가 난 부분을 마커 요약을 통해 확인. 조치를 취하게 된다.

이런 식으로 최적화를 진행해도 종종 문제가 나는 일이 있는데, 특히 저사양의 모바일 환경에서는 최적화 문제가 자주 나고는 한다. 모바일은 CPU 메모리를 50% 이상을 한 번에 쓰는 일이 드물고, 그런 환경에서는 과하게 발열이 날 수 있어 쓰로틀링이 걸리는 일도 있다. 즉 처음부터 빌드를 타겟 사양에 맞춰서 설계할 필요가 있었던 것이다.

▲ 가비지 콜렉터와 얼로케이션이 쭉 번갈아서 실행되고 있는 게 확연히 보인다

유니티의 메모리 프로파일러에서는 자신이 타겟으로 하는 디바이스에서 메모리 점유율이 얼마나 차지하는지는 물론, 메모리 할당량 등 정보를 확인할 수 있다. 모노비헤이비어에 엮인 오브젝트가 어떤 것이 있나 확인도 가능하며, 여기에 하나의 씬만이 아니라 두 개의 씬을 분석, 각 씬에서 최적화 이슈가 어떻게 발생하는지 서로 비교분석할 수 있다. 또한 렌더링된 텍스처에서 어떤 부분이 용량을 가장 크게 잡아먹고 있는지도 면밀히 파악할 수 있도록 지원한다.

이처럼 최적화 기능을 충분히 활용하더라도 100% 완벽하게 최적화가 안 되는 일이 왕왕 발생하고는 한다. 실제로 유저들의 환경은 제각각 다르기 때문이다. 특히나 라이브 서비스 게임은 업데이트 과정에서 일부 문제가 발생하는 경우가 자주 있고, 그때마다 개발자들이 골치가 아픈 상황이다.

그런 개발자들을 위해 홀 시니어 매니저는 가장 기본적인, 그렇지만 잊기 쉬운 조언을 몇 가지 제시했다. 가장 낮은 사양의 타겟 기기를 대상으로 프로파일링하고, 프레임 레이트를 어느 정도에 맞출 것인지 확인할 것, 그리고 너무 자주하지는 말라는 점이다. 프로파일 자체도 부하가 가는 작업이기 때문이다. 그렇지만 최적화는 중요한 작업인 만큼, 프로파일링은 처음 프로젝트 시작부터 종종 하고 시간을 할애해서 테스트 레벨, 실제 게임 플레이 레벨에서도 진행, 최대한 완벽하게 준비하는 것이 필요하다고 강조했다.

▲ 어떤 부분에서 임팩트가 큰지 세밀하게 확인할 수 있는 것은 물론

▲ 씬 단위로 비교 분석, 더 면밀하게 프로젝트를 프로파일링할 수 있다