프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 조진현

메모리 최적화에 대한 이슈는 게임 개발자에게 굉장히 민감한 주제 중에 하나 입니다.
특히나 게임 개발에서 메모리를 가장 많이 차지하는 것은 텍스쳐입니다.
지금부터 언급되는 텍스쳐는 DirectX9 에서 POOL_MANAGED 옵션으로 생성되는
Managed-Texture 형태의 텍스쳐로 국한합니다.
( 텍스쳐 관리 시스템에서 제어되는 텍스쳐는 POOL_MANAGED 옵션을 가진 텍스쳐 뿐입니다. )

게임개발자가 텍스쳐를 사용하는 동작을 제가 아래와 같이 단순화 시켜보았습니다.

먼저 우리가 원하는 텍스쳐를 메모리에 로드할 수 있습니다.
비디오-메모리에 직접적으로 로드할 수 있으며, 시스템-메모리에 로드할 수도 있습니다.
로드할 때, 우리는 메모리를 조금 더 최적화하기 위해서
DirectX 에게 여러가지 힌트 옵션을 설정하기도 합니다.
이런 힌트 옵션들은 DirectX의 여러 버전들마다 설정 방법이 조금씩 다르지만,
기본적으로 내용은 비슷합니다.( 읽기전용/쓰기 전용 등이죠... )

그리고 필요해 위해서 DirectX 의 상태를 변경해서 텍스쳐를 그래픽 파이프라인에 연결합니다.
SetTexture( ... ) 같은 API 들이 이런 역할을 합니다.

마지막으로 필요하지 않는 텍스쳐는 직접 제거를 해줍니다.

결국 개발자가 할 수 있는 것은 Load-->Bind-->Release 의 형태 뿐입니다.
게임을 하는 중에 비디오-메모리가 가득차 있는데,
새로운 텍스쳐가 로드되면 어떤 상황이 벌어질까요?
새로운 텍스쳐를 로드하기 전에 비디오-메모리의 사용 가능한 공간을 체크한 후에,
새로운 텍스쳐를 로드하면 간단히 해결할 수 있습니다.
하지만 지금 우리는 개발할 때, 그런 체크를 하지 않습니다.

이 상황으로 유츄해 보면, 누군가에 의해서 자동적으로 어떤 작업이 이루어지고 있습니다.
그 누군가가 바로 DirectX의 텍스쳐 관리 시스템입니다.
게임 개발자에게 주어진 권한은 텍스쳐의 메모리의 생성과 제거 정도에 지나지 않습니다.
그 이외에 나머지 처리는 모두 DirectX의 텍스쳐 관리 시스템에 의해서 제어됩니다.
이 텍스쳐 관리 시스템은 DirectX6 부터 도입되었습니다.
즉, 이전까지는 아마도 개발자들이 직접 메모리를 계산하면서 처리했을 겁니다.
( 아마도~~ㅎㅎ )

이런 텍스쳐 관리 시스템은 기본적으로 다음과 같은 동작을 합니다.
- 현재 사용 가능한 텍스쳐 메모리의 양을 체크하고 추적
- 현재 렌더링 작업을 위해서 필요한 텍스쳐와 그렇지 않은 텍스쳐를 결정
- 비디오-메모리에 있는 텍스쳐 중에 어떤 것이 제거되어야 하는지를 결정
- 시스템-메모리에서 다시 읽어들일 텍스쳐를 결정

사실 이것보다 더 많은 작업을 하겠지만, 제가 이 정도로만 간략화 시켜보았습니다.

텍스쳐 관리 시스템은 여러 결정권을 가지고 있습니다.
특히나 비디오-메모리에서 제거 권한은 우리 개발자에게 정말 두려운 권한입니다.
힘들게(?) 비디오-메모리에 올려두었는데,
시스템에서 자기 마음대로(?) 그냥 내려버리면 얼마나 가슴 아프시겠습니까?

그래서 이런 가슴 아픈 일을 방지하는 차원에서
텍스쳐들에게 우선 순위를 부여할 수 있습니다.
텍스쳐 관리 시스템은 일정한 규칙을 가지고 비디오-메모리에서 텍스쳐를 제거합니다.
그 규칙이라는 것은 기본적으로 LRU( least-recently-used ) 에 기반을 두고 있습니다.
이는 비디오-메모리가 가득차 있는 상태라면,
이들 중에 비디오-메모리에서 제거가 되는 것은
가장 오랫동안 사용되지 않은 텍스쳐라는 것을 의미합니다.

개발자들은 비디오-메모리의 부족에 고통을 호소합니다.( 저만 그런가요? ㅎㅎ )
비디오-메모리가 가득찬 상태에서 새로운 하나의 텍스쳐가
비디오-메모리로 로드된다고 가정하겠습니다.
그러면, 텍스쳐 관리 시스템은 LRU로 체크를 해서
하나의 텍스쳐를 비디오-메모리에서 제거할 것입니다.
그리고 새로운 텍스쳐를 비디오-메모리로 로드할 것입니다.
실제로 텍스쳐 관리 시스템은 이것보다 훨씬 복잡한 방법으로 동작할 것입니다.
( 예를 들기 위해서 텍스쳐 크기 같은 문제를 제외하고 예를 드는 것 뿐입니다. )

이런 상황에서 10개의 새로운 텍스쳐가 비디오-메모리로 로드된다고 생각해 보십시오.
시스템-메모리에서 비디오-메모리의 데이터 전송이 최소 10번이 이루어져야 합니다.
또한 최소 10번 이상의 비디오-메모리에서 제거할 텍스쳐를 결정하는 작업이 이루어져야 합니다. 
이것이 많아지면, 병목 현상을 초래할 수 있습니다.
이것은 텍스쳐 스레싱( texture thrashing ) 이라고들 합니다.

실제 게임 플레이 중인 상황에서 갑자기 새로운 배경이나 오브젝트들이 등장할 때
프레임이 느려지는 현상이 발생하게 되는데, 이런 작업도 거기에 영향을 미치는 요소입니다.
게임이 실행 중인 경우에는 이미 많은 비디오-메모리를 사용 중이니,
사실 빈번하게 발생할 수도 있는 일입니다.
무책임한 말일 수 있으나, 이것은 어쩔 수 없는 일입니다.
( 적절히(?) 개발자들이 노력해야 할 부분이죠? ㅎㅎㅎ )

그런데 비록 오래 사용되지는 않았지만,
무척이나 중요한 텍스쳐가 있으면 조금 문제가 되지 않을까요?
반드시 비디오-메모리에 있어야 할 텍스쳐라는 개념이 아닙니다.
( 만약 반드시 비디오-메모리에 있어야 한다면 POOL_DEFAULT 로 직접 관리해 주시면 됩니다.)

텍스쳐 관리 시스템에서 비디오-메모리에서 제거할 목록들을 구성할 때 
일종의 특혜(?) 가 있는 텍스쳐들은 제거 목록에서 제외시켜달라는 의미로 볼 수 있습니다.
이것은 비디오-메모리에 계속해서 남아있을 필요는 없지만,
되도록이면 조금 더 오래 남겨달라는 의미로 해석하실 수 있습니다.
이렇게 특혜(?)를 부여하는 것이 바로 우선 순위를 설정하는 작업입니다.
비디오-메모리에서 제거될 후보군들 중에서 LRU 값이 같다면,
우선 순위가 낮은 텍스쳐가 제거되는 것입니다.

IDirect3DResource9 라는 인터페이스를 상속받아서
Direct3D의 텍스쳐 인터페이스가 만들어졌습니다.

IDirect3DResource9 인터페이스는 이 우선 순위를 설정할 수 있는 멤버함수를 가지고 있습니다.
IDirect3DResource9::SetPriority( ... ) 가 바로 그것입니다.
Managed-Texture 의 경우 기본적으로 우선 순위가 0 입니다.
0보다 높은 숫자를 입력하면, 높은 우선 순위가 됩니다.
그런데 무턱되고 높은 숫자를 입력하면, 문제의 소지가 있을 수 있습니다.
그래서 이를 상수로써 정해진 몇몇 값들이 있습니다.
그 값들은 아래와 같습니다.

D3D9_RESOURCE_PRIORITY_MINIMUM 0x28000000
D3D9_RESOURCE_PRIORITY_LOW 0x50000000
D3D9_RESOURCE_PRIORITY_NORMAL 0x78000000
D3D9_RESOURCE_PRIORITY_HIGH 0xa0000000
D3D9_RESOURCE_PRIORITY_MAXIMUM 0xc8000000


이것은 DirectX가 스케쥴링 목적으로 정의한 상수들입니다.
이 우선 순위를 그대로 텍스쳐에 적용하시면 안됩니다.
일반적으로 텍스쳐의 경우 D3D9_RESOURCE_PRIORITY_NORMAL 우선 순위를 사용해야 합니다.
그리고 아래와 같이 설정하는 것이 좋습니다.

ManagedTex->SetPriority( D3D_RESOURCE_PRIORITY_NORMA + 1 ); 
ManagedTex->SetPriority( D3D_RESOURCE_PRIORITY_NORMA + 2 );
ManagedTex->SetPriority( D3D_RESOURCE_PRIORITY_NORMA + 3 );


Managed-Texture는 실제로 렌더링 작업이 이루어지기 전까지
비디오-메모리에 데이터가 존재하지 않습니다.
렌더링 작업이 한번이라도 이루어져야 할 때,
시스템-메모리의 텍스쳐 메모리가 비디오-메모리로 전송이 됩니다.
이것도 바로 텍스쳐 관리 시스템이 하는 작업 중에 하나입니다.
그런데 텍스쳐를 로딩하고, 이를 비디오-메모리에 바로 올리고 싶은 경우가 있을 수 있습니다.
이미 텍스쳐를 로딩한다는 것 자체가,
바로 렌더링 작업에 사용할 것이라는 확신이 있는 경우일 것입니다.
그 때는 IDirect3DResource9::PreLoad() 를 사용하시면 됩니다.
그러면 약간의 성능 향상을 보일 수도 있습니다.
( 이것은 저도 아주 약간의 효과를 경험했었습니다.^^ )

게임을 플레이 중에 씬 전체가 새로운 텍스쳐들로 가득 채워져야 하는 경우는 없으셨나요?
그런 경우 텍스쳐 스레싱 현상이 아주 크게 느껴질 수도 있습니다.
그래서 한꺼번에 비디오-메모리를 모두 비워버리는 API가 있습니다.
IDirect3DDevice9::EvictManagedResource() 가 바로 그것인데,
이 API는 보시다시피 Device 인터페이스의 API 입니다.
즉, 버퍼와 텍스쳐를 포함한 Managed 리소스까지 모두 비디오-메모리를 비워버립니다..
그래서 씬이 완전히 새롭게 구성될 때, 꽤 유용할 수 있습니다.
( 저는 이 API 아주 좋아합니다..ㅎㅎㅎ )

DirectX9 의 텍스쳐 관리 시스템은 사실상 이 세가지 API를 통해서 제어를 합니다.
나머지는 우리 개발자의 손을 떠난 문제입니다.
위의 문제를 고민하기 전까지 저는 게임에 사용되는 메모리를 계산해 본 적이 없었습니다.
예를 들면 상점이나 특정 지역에서 사용되는 메모리 계산이겠죠.
게임 프로그래머가 단순히 주어진 리소스를 렌더링만 한다는 개념에서
벗어나게 해주었던 작업들이였습니다.
 
TAG

댓글을 달아 주세요

  1. Favicon of http://www.kallru.com kallru 2011.12.13 10:05  댓글주소  수정/삭제  댓글쓰기

    오 새로운걸 알고 갑니다. ^^

  2. Silverchime 2011.12.13 10:06  댓글주소  수정/삭제  댓글쓰기

    존로딩이라든지 새로운 인스턴스존 입장 이럴때 비디오램을 다 비워주면 성능향상이 있을까요...? 우리팀 프로그래머에게 전달해줘야겠네요 ^^ 흥미롭게 잘 보았습니다. 감사합니다.

    • Favicon of https://gamedevforever.com 조진현 2011.12.13 10:30 신고  댓글주소  수정/삭제

      존 로딩과 같은 경우에는 프로그래머들이 아마도 관련 리소스를 제거할 것입니다. 그래서 시스템-메모리와 비디오-메모리가 비워질 것입니다.
      비디오-메모리만 다 비우는 경우는 현재 존이 다음에 반드시 복귀해야 할 경우라던지, 잠깐 다른 씬이 보여질 수 있는 경우가 유용할 것입니다.
      반드시 '빠르다'라고 하기보다는 프로그래머들이 게임에 맞게 여러 성능 테스트를 유추하고 실행해 봐야 합니다..^^ 눈에 보일만큼 큰 성능 향상은 없었습니다.ㅎㅎ

    • Favicon of https://gamedevforever.com Silverchime 2011.12.13 10:57 신고  댓글주소  수정/삭제

      앗 친절한 답변.... 감사합니다 ^^

  3. Favicon of http://ozlael.egloos.com ozlael 2011.12.13 17:50  댓글주소  수정/삭제  댓글쓰기

    오홍 우선순위를 설정 할 수 있는지는 처음 알았네요
    좋은 글 감삼다

  4. Favicon of http://3dstudy.net 김용준 2011.12.21 13:26  댓글주소  수정/삭제  댓글쓰기

    예전 spirit3d.net이 되살아 난것 같아서 너무 기쁩니다 엉엉 ㅠ.ㅠ

  5. Favicon of http://www.ahappydeal.com/cell-phones-mobiles-c-59.html china mobile 2012.07.31 14:08  댓글주소  수정/삭제  댓글쓰기

    그것은 충분히 흥미있는 주제에 대한 유용한 정보와 양질의 기사를 읽는 기회를 가지고 훌륭합니다