스마트폰 퍼포먼스 문제 해결
작성자: 이테이 둡데바니 (Itay Duvdevani) 작성일: 2012년 10월 3일
스마트폰 게임에 퍼포먼스 이슈가 있는가? 모미니스(MoMinis)의 프로그래머 이테이 둡데바니가 동료들과 함께 아주 단순한 소팅 문제를 창조적으로 해결한 방법을 설명하고, 그 과정에서 스마트폰 게임 엔진을 업그레이드한 기술적인 방법을 소개한다.
모미니스 플랫폼(MoMinis Platform)의 폰 관련 작업은 재미있는 일이다. 우리 콘텐츠 개발자들은 게임 메카닉, 버그, 퍼포먼스 튜닝 등을 시뮬레이터상에서 하는 경우가 대부분이어서, 새 타이틀을 실제 폰에서 테스트할 때가 되면 항상 약간 긴장하게 된다.
게임을 컴파일 하게 되면 게임의 로직을 깨뜨리는 버그를 발견할 수도 있고(시뮬레이터나 컴파일러에서), 시뮬레이터에서 제대로 시뮬레이팅되지 않은 퍼포먼스 문제를 찾아낼 수도 있다.
게임이 시뮬레이터에서는 작동했는데 폰에서는 작동하지 않을 때는 바로 우리 팀으로 넘어온다. 문제를 정확히 찾아낼 수 있는 이는 컴파일러 개발자인 우리들뿐이기 때문이다.
<젤리 점프>의 문제
우리 컨텐츠 개발자들은 새로운 타이틀 <젤리 점프(Jelly Jump)>를 만들고 있었다. 몇 주 동안 개발을 마치고 실제 폰에서 테스트를 시작할 수 있을 정도의 완성상태였다. 릴리즈까지는 일주일이 남아있었다. 고사양의 폰에서는 무척 재미있고 부드럽게 잘 실행되었다. 그런데 중간 사양이나 저사양의 디바이스에서는 화면이 불규칙적으로 깨져서( jitter) 게임을 플레이 할 수가 없었다. 우리 팀은 이 문제를 해결하기 위해 나섰다.
이 문제는 비교적 성능이 괜찮은 디바이스(800MHz 스냅드래곤, 아드레노 200GPU)에서 발견되었다. FPS가 낮아서 생기는 문제는 아니었지만, FPS가 몇 초에 한 번씩 흐트러졌고 무작위로 발생하는 것 같았다. 게임이 40-50FPS로 실행되다가 1초 정도 10-5FPS로 떨어졌다가 다시 계속되는 식이었다.
우리는 컨텐츠 개발자들로부터 테스트할 때 그래픽 에셋을 제거하면 게임이 더 잘 실행되더라는 귀띔을 받았다.
컴파일 과정 중 에셋-생성 단계가 게임 에셋을 텍스쳐 아틀라스(Texture Atlas)로 할당하게 된다. 이건 자동화된 단계이기 때문에 텍스쳐 스와핑을 최소화하기 위해서 어떤 오브젝트들을 같이 그릴 것인지 몇 가지 발견적 방법(heuristic)을 통해 예측한다. 이런 발견적 방법들은 지금은 아주 기초적인 것으로 통계 분석에만 의지한다. 퍼포먼스를 위한 최적화 기능은 아직까지 없다. 우리는 에셋이 비효율적인 방식으로 그루핑되어서 특정 오브젝트를 그릴 때마다 메모리 부족으로 텍스쳐 스래싱(Thrashing)이 발생하는 것이 아닌가 의심해보았다.
이것이 문제였다. 여기에 대해서는 ‘마법 같은 솔루션’도 없고 어떻게 피해갈 방법조차 없었다. 꼼수도 부릴 수가 없었다. 단순히 에셋 생성 파이프라인에 비해 게임 그래픽이 너무 많았다.
‘어떻게’ 이 문제를 해결할 것인지 며칠을 씨름했지만 실제적인 해결책이 없었다. 우리는 가정에 대해 의심하기 시작했고 텍스쳐에도 문제가 있었는지 테스트해 보았다. 에셋 생성 파이프라인 설정에서 그래픽을 모두 ¼ 사이즈로 줄인 다음, 에셋이 모두 한 텍스쳐에 들어가도록 했다. 놀랍게도 여전히 문제가 발생했다. (그리고 되게 보기 싫었다.)
게임의 ‘로직(logic)’이 병목을 만든다는 뜻이었다. 우리는 달빅(Dalvik) 프로파일러로 게임을 프로파일링 해보았다. 프로파일러는 FPS가 낮은 문제를 해결할 때는 매우 유용하지만 불행히도 FPS가 깨지는 문제를 해결할 때는 거의 쓸모가 없었다. 언제 깨질지를 미리 정확히 알고 있지 않는 한 말이다. 하긴 그걸 알고 있었으면 당연히 뭐가 문제인지도 알았겠지.
컨텐츠 개발자들과 같이 앉아 게임의 로직을 들은 다음, 직감에 따라 코드에 디버그 프린트를 몇 개 심어보았는데, 놀라운 결과가 나왔다. 한 로직을 반복하면서 대량의 오브젝트를 생성할 때 Z오더(Z-order) 코드가 생각과 다르게 작동하고 있었던 것이다!
Z Order를 반복하는 이유는?
우리 게임 엔진은 게임 로직을 컴파일 하는 일 말고도, 화면을 예쁘게 그리면서 터치 입력을 받아들이는 것을 책임지고 있다.
모미니스 플랫폼 같은 2D 엔진에서는 오브젝트를 Z 오더에 따라 정렬하고 가로질러(뒤에서 앞으로, 혹은 앞에서 뒤로 정렬) 터치 인풋을 받을 오브젝트와 먼저 그릴 오브젝트를 정한다.
터치 핸들러
터치 핸들러는 스크린 단에서의 터치 이벤트를 운영체제(OS)에서 받아 게임 단의 이벤트로 바꾸는 간단한 어댑터다. 따라서 내가 화면상의 특정 위치를 클릭하면, “사용자가 (x,y) 위치를 클릭했다” 대신에 그 위치에 있는 오브젝트가 논리적인 터치다운 이벤트를 받아들이게 되는 것이다.
우리는 게임 개발자들이 낮은 수준의 터치 이벤트를 핸들링해서 어떤 오브젝트가 응답하게 해야 할 것인지 관리하지 않아도 되도록 가상의 레이어를 제공한다. 우리의 개발 플랫폼을 초보 개발자들도 사용할 수 있게 하고 번거로움을 최소로 하기 위해서는 매우 중요한 부분이다.
게임 오브젝트가 여러 개 겹쳐 쌓여있고 터치 이벤트가 여러 오브젝트가 겹쳐있는 곳으로 들어오는 건 게임 개발자 수준에서 처리하기 어려운 일일 수 있다. 그래서 우리는 터치 이벤트를 가장 위에 있는 오브젝트, 즉 손가락이 닿는 곳에 보이는 오브젝트로 전달해준다. 그뿐이다.
이 때 가장 쉬운 방식은 전체 오브젝트를 앞에서부터 순서대로 스캔해서 이벤트를 처리할 수 있는 첫 번째 것을 찾는 것이다.
렌더 큐(The Render Queue)
모미니스 게임 엔진은 안드로이드와 아이폰 모두 OpenGL ES를 바탕으로 하고 있다. 호환성 문제 때문에 우리는 OpenGL ES 1.1 API만을 사용한다.
필레이트(fill rate)를 최대화 하기 위해서 우리는 오브젝트를 투명한 것과 불투명한 것의 두 그룹으로 나눈다. 그리고 먼저 불투명한 것들은 앞에서 뒤로 그리고, 안 보일 부분들은 나중에 그리는 대신 Z 버퍼에 놔두어서 전체 레스터라이제이션(rasterization) 파이프라인을 절약한다.
불투명한 오브젝트를 먼저 다 렌더링 한 다음에 투명한 것들을 그리는데, 투명한 그룹은 알파 블렌딩을 제대로 맞추기 위해 뒤쪽에서 앞으로 그려야 한다.
※ 자세한 내용은 첨부(PDF)화일을 참고하시기 바랍니다.
|