You are here: Home - Studies∞ - Mass Engine 설계의 기초(1)

0906062346

Mass Engine 설계의 기초(1)

Crytek의 CryEngine이나 EPIC의 Unreal엔진 등이 국내 유수의 게임회사들과의
계약을 체결하던 2002년 즈음, 많은 게임회사들이 자체 개발하던 3D엔진의 성능과,
엔진 구현에 들어간 비용을 유명 엔진과 비교하기 시작했다.

예전에는 3D엔진 하나로 코스닥에 상장이 가능하던 시절이 있었다.
이때에는 3D엔진이라는 것 자체가, 귀할 뿐 아니라, 상용으로 발매하기 보다는
회사의 기술력으로 사내에 보유되길 원했었기 때문이다.

하지만 게임브리오로 대변되는 염가 3D엔진 시장이 출현하면서, 3D엔진은 비싸다는
생각도 많이 개선되었을 뿐 아니라, 자본력이 풍부한 대기업들은 성능까지 출중한
해외의 유명한 엔진을 구입할 수 있게 되었기 때문에, 굳이 완벽하게 완성될지,
안될지도 모르는 위험부담이 큰 게임 엔진을 스스로 개발하기를 포기하기에 이르렀고
-실제로 게임 엔진 하나를 “완성”하는데는 4년 이상의 시간이 걸린다.-
현재 국내에는 아주 예전부터 여러 개의 엔진을 개발해봤던 베테랑 엔진 개발자를
보유한 회사에서나 혹은, 엔진을 구입할 만한 여력이 없는 일부 중소기업에서 캐쥬얼 게임을 만드는 정도로 사용할 목적 정도로만 엔진을 직접 개발하고 있다.

나는 KIST에서의 CAVE엔진, (주)사이오넥스의 스토리아엔진, (주)타프시스템의
가이아엔진, (주)엔틱스소프트의 be엔진 개발에 참여했고, 메인 엔진 디자인을
맡았던 경험을 가지고 있고, 현재 회사에서도 KKS엔진이라고 하는 쉐이더기반의
차세대 엔진을 5여년에 걸쳐 개발중이다.

이 컬럼에서는 Mass Engine을 디자인하는 업무에 종사중인 사람들이, 필수적으로
고민하게되는 이슈와, 망망대해와 같은 Mass Engine의 구현 범위에 대해 갈피를
잡지 못하는 엔진 디자이너들에게 일부나마 도움이 될만한 내용을 포함하려고 한다.

먼저 Mass Engine의 정의를 내리고 그 범위를 한정하는 일로부터 엔진 디자인을
시작한다. Mass Engine은 Renderer가 2D인지, 3D인지를 중심으로 엔진의 범위를
나누는 기존의 엔진 분류를 다시금 정렬하는 개념적 작업이 필요하다.

보통 Mass Engine을 개발하다보면, 그것이 2D기반의 엔진이든 3D기반의 엔진이든
어떤 공통의 모듈이 필요함을 발견하게 된다. 어쩌면 엔진을 거의 다 구현했을 때,
렌더러를 제외하면 추상화된 레이어에서는 두 엔진이 거의 동일한 형태로 디자인 되는
상황에 다다를 수도 있다.
이것은 엔진이라면 기본적으로 가져야 할 여러 동작들이 렌더링 하드웨어에는 상관없이
공통적으로 존재하기 때문이다. 이 컬럼에서도 렌더러를 제외한 엔진의 공통 분모를
찾아 이를 재 정리하는 과정을 보여주는 것을 목표로 하고 있다.

Mass Engine은 대부분 System call을 사용할 정도로 OS Layer에 깊숙히 관여하도록
구현하기 보다는 왠만하면 상위의 Standard Library들을 사용해서 구현하고,
표준라이브러리에 존재하지 않는 함수거나, 어떤 플랫폼에 특화된 기능을 사용하는
경우에는 특정한 기능을 Call 하는 함수를 한번 Wrapping해서 사용하게 된다.
이는 엔진의 Portabilility를 확보하는 장점이 있을 뿐 아니라, 디버깅 정보를 로깅할 때도
유용하게 사용된다. 여튼 Mass Engine의 이슈는 기능 함수 하나하나를 얼마나
얼마나 동작성을 좋게 할 것인지를 목표로 구현하는가가 아니라, 그 위에서 추상화된 기능을 얼마나 우아하게 모듈화하고, 사용이 편리하게 만드는가를 중심으로 구현해야한다.

Mass Engine의 모듈들을 구현하다 보면, 각 구현이 서로 동일한 레이어 상에 존재하는 것이 아니라, 각 구현된 모듈도 서로 상 하위의 계층구조를 가질 수 밖에 없음을 깨닫게
되는데, 그러므로 모듈을 설계할 때, 어떤 모듈이 가장 하위 구조에 속할 것인가를 먼저
생각해야 한다. 엔진의 범위를 설명할 때에도 이 계층 순서를 기준으로 설명을 하면
보다 이해가 쉬울 것이다.

Mass Engine이 반드시 가져야 할 첫번째 모듈은 기본자료구조(DS)이다. 세상에는 생각보다 많은 플랫폼과 하드웨어가 존재하며, 진정한 OSMU를 위해서는 겉보기등급의
이식 우월성이 아니라, 거의 원클릭으로 각 플랫폼에 필요한 바이너리 패키지들이
빌드 될 수 있어야 한다. 이렇게 하기 위해서는 STL 등의 표준 자료 구조 패키지를
너무 믿어서는 안된다. 일례로 한국에서 아주 유명한 WIPI-C라는 무선통신표준화플랫폼에서는 STL을 사용할 수 없다. (뿐만 아니라 통신망사업자에 따라, sprintf와 같은 표준 C함수 조차 지원하지 않는다.) 그러므로, 엔진을 디자인할 때 이 엔진을 구현하면서 어떤 자료 구조형을 중점적으로 사용하면서 디자인할 것인지,-예를 들어, vector인지, list인지…- 먼저 결정하고(이때에는 HW의 퍼포먼스 등이 영향을 끼친다.)
그 이후에 각각의 자료구조형들을 먼저 디자인한다. (세상에 존재하는 모든 자료구조형을 구현해야 하는 것이 아님에 유의, 욕심을 버릴 것) 이 자료구조형은 아주 밑바닥에
해당하는 레이어이므로, C++과 같은 객체지향 형태의 언어로 작성하기 보다는
C와 같은 Procedural한 언어로 작성한 후 그 위를 C++과 같은 OOP언어로 wrapping
하는 것이, 엔진 레이어의 랜덤한 위치에서 자유로이 이 자료구조형을 사용하기에 유리하다.

Mass Engine의 기본자료형이 완성되었다면, 다음은 메모리관리 모듈을 만들 차례이다.
모든 디바이스에서 같은 비트의 메모리 얼로케이션이 일어나지 않을 뿐 아니라,
메모리를 Static으로 잡아놓고, 혹은 런타임에 Dynamic Allocation을 하지 않는다면
성능이 떨어지는 많은 OS에서 메모리 단편화 현상을 직접 제어할 수 있다. 또,
점유된 메모리 Segment에 디버깅을 위한 특정 정보를 포함하게 하여 디버깅이 용이
해질 수도 있다. 메모리 메니져를 만들 때에는 대부분 그렇듯 First-fit, Best-fit 아니면
Worst-fit을 사용하겠지만, 어떤 알고리즘이 됐든 메모리정보를 가지는 노드의 크기가
너무 커지지 않게 주의 해야 한다는 점과, 정작 Dynamic Allcation을 통제하기 위해서
메모리 관리자를 만들면서 메모리 관리자 내부에서는 Dynamic Allocation이 일어나는
일이 생겨서는 안된다는 점이다. 일반적으로는 Node Pool을 만들어 메모리관리자
내부에서도 동적할당이 일어나지 않도록 만든다. 그리고 마지막으로 주의해야 할 점은
현재 점유한 메모리의 할당 해제 혹은 새로운 메모리 블럭의 할당을 위해서는
노드 검색이 필요한데, 이 노드 검색에 너무 많은 부하가 걸리도록 알고리즘을
구성해서는 안된다는 것이다.
메모리 얼로케이션은 엔진 내부 및 전체 어플리케이션에 걸쳐 생각보다 많은 빈도로
발생하기 때문에 한번의 검색이 빠르게 이루어지지만, 할당된 구조를 유지하는데
많은 부하가 걸리게 작성하기 보다는, 한번의 검색은 빠르지 않더라도 할당된 구조를
단순하게 관리하도록 하는 편이 유리하다. 예를 들어, 할당된 메모리 블럭들의 정보를
유지하기 위해서 트리구조 등을 사용하는 것 보다는, 싱글 링크드 리스트를 이용하는
쪽이 좋다.

- 작성중 -

by HKP

Leave a Reply

Back to top