DX8, 9 Frame Work에 대해서

SleepyHead - [펌]
seeper0 - [펌 
DX8, 9 Frame Work의 추가 내용

<SleepyHead - [펌]>: http://blog.naver.com/jinofstar.do?Redirect=Log&logNo=120007395974

P.105
INT WINAPI WinMain()
{
     CMyD3DApplication d3dApp; // 클래스 생성
     ………
     If(FAILED(d3dApp.Create(hInst)) // 초기화
           Return 0;

    Return d3dApp.Run();  // 프로그램 시작
}

애플리케이션 마법사로 Directx 프로그램을 할 때 생성된 클래스
CD3DApplication을 상속받았다. 이 클래스를 수정해서 프로그램을 만든다.

 

초기화 과정

D3dApp.Create() -> ConfirmDevice() -> OneTimeSceneInit() -> InitDeviceObjects() -> RestoreDeviceObjects()

D3dApp.Create()

애플리케이션 초기화, HAL을 사용할 수 있는지 여부 등, 현재 시스템에서 실행할 수 있는 디바이스(LPDIRECT3DDEVICE9)를 조사한 뒤 사용 가능한 디바이스로 애플리케이션을 시작

 ConfirmDevice()

생성한 디바이스를 조사할 때 호출되는 함수, 직접 코딩
예를 들어 그래픽 칩이 지원하는 셰이더의 버전이 있는데, 주어진 프로그램에서 사용 하고 있는 셰이더의 버전과 맞지 않으면 에러를 리턴 시켜서 실행할 수 없다고 표현,
모두 실패하게 되면 프로그램은 종료. 한 개라도 통과하면 실행

OneTimeSceneInit()

한번만 호출, 최초에 한 번만 읽으면, 나중에는 변경되지 않을 데이터를 읽어드리는데 사용

 InitDeviceObjects()

디바이스가 확정되고 나서 호출되는 초기화 함수
디바이스가 생성된 후의 초기화, HAL을 REF로 전환햇을때도 호출
디바이스가 바뀌었을때, 재설정이 필요나 오브젝트 처리를 담당
예를 들어 3D 모델의 등록엔 LPDIRECT3DDEVICE9 디바이스가 필요하므로 이러한 초기화는 모두 여기서 함.
여기서 확보한 메모리는 DeleteDeviceObjects()에서 해제

RestoreDeviceObjects()

화면의 크기가 바뀐 후에 호출되는 초기화 함수,
잡다한 인자를 읽어들이는 렌더링 스테이트, 텍스처 스테이지 스테이트 설정 등도 함
여기서 확보한 메모리는 InvalidateDeviceObjects()에서 해제

 

렌더링 과정

d3dApp.Run()이 호출되면서 렌더링 시작

d3dApp.Run()

윈도우 메시지를 감시, idle 타임에 렌더링 루틴을 호출

화면을 한 프레임 갱신할 때마다 FrameMove()와 Render()가 교대로 호출
그러나 이것은 보기 좋게 기능을 분리해 놓은 것 뿐 Render에 Framework가 해야할 일을 해도 무방

 FrameMove()

오브젝트나 카메라를 작동시키는 작업
애니메이션 처리 등 담당 
UpdateInput() 호출되어
키 정보 구조체를 갱신
키보드 조작을 추가하려면 UserInput 구조체를 추가하거나 UpdateInput()에 키를 추가

Render()

실제 렌더링 코드를 넣어서 렌더링 한다.
RenderText()를 호출, FPS 값이나 사용중인 그래픽 칩 정보를 표시

해제, 재설정 과정

재설정은 3가지 경우에 필요
- 화면 크기 변경
- 디비이스 변경
- 종료시

InvalidateDeviceObjects()

RestoreDeviceObjects()에서 확보한 메모리나 설정을 해제

DeleteDeviceObjects()

InitDeviceObjects()에서 확보한 리소스 해제

FinallCleanUp()

 OneTimeSceneInit에서 확보한 메모리 해제

화면 크기 변경

프레임버퍼의 크기가 변경되므로 프레임 버퍼의 재설정 필요
InvalidateDeviceObjects() -> RestoreDeviceObjects() 다시 설정

디바이스 변경

InvalidateDeviceObjects() -> DeleteDeviceObjects() -> InitDeviceObjects() -> RestoreDeviceObjects()

종료시

InvalidateDeviceObjects() -> DeleteDeviceObjects() -> FinalCleanUp() -> 소멸자 -> 종료

 

<seeper0 - [펌]> : http://blog.naver.com/seeper0.do?Redirect=Log&logNo=120001420552

DirectX 8과 9의 Frame Work에 대해서 써볼까 한다.
이것이 무엇인고 하니... DX8,9 프로그래밍할때 똑같은 부분이 존재한다.
예를 들자면 windows 프로그래밍 부분도 있고 dx 초기화 부분도 그렇고..
심지어는 윈도우 리사이징까지 다 포함하는 코딩이 있다.
이건 다시치기 상당히 귀찮은 부분이다. (다 아는 사람에 한해서는..)
그래서 나같은 경우는 기본 FrameWork를 사용한다.
(물론 몇가지 마음에 안드는 점은 고쳐서 사용한다.)

VC++과 DX SDK를 잘 인스톨 하면 다음과 같은 화면을 볼수 있다.

[그림 생략 ................  위 링크에만 있음]

위의 화면은 DX8을 깐 모습이다. DX9는 9라고 써나온다. 둘다 깔은 경우는 두개가 다 나온다.
만약 진행한다면 몇가지 화면들이 나온다. (위저드화면) 몇번 하다보면 알 수 있을 것이다.
초기화면을 삼각형을 고를수도 있고 없는 화면을 고를수도 있다. 그건 마음대로 해도 된다.
이렇게 생성된 소스에서 다른건 손댈 필요 없고 CMyD3DApplication 만 손대면 된다.
물론 CD3DApplication 를 손 댈수도 있다. (하지만 잘 모를때 건드리는건 비추!)
어쨌건 실제 게임코드는 CMyD3DApplication에 들어가야한다. (물론 보통은 여러 클래스로 나눈다.)
간단 한 게임을 만든다고 생각할때 CMyD3DApplication로만 해보는것도 좋을것 같다.
어쨌건 CMyD3DApplication에서 다음 함수는 알아야 한다. (이것들은 짝을 이룬다.)

1. 무조건 한번씩 호출되는 함수 (이때 m_pd3dDevice는 초기화 되어있지 않음)
OneTimeSceneInit()
FinalCleanup()

2. m_pd3dDevice가 초기화 될때 호출되는 함수 (보통은 게임에 한번만 호출)
InitDeviceObjects()
DeleteDeviceObjects()

3. surface lost 현상이 일어나면 사라지는 데이터를 로딩할때 호출하는 함수
(alt-tab이나 resize 등을 할때 호출)
RestoreDeviceObjects()
InvalidateDeviceObjects()

1번은 무조건 짝을 이룰 필요는 없다. 허나 상반된 개념이다.
2번은 보통 시작과 종료시 호출된다. (3D 디바이스를 바꾸지 않는다면)
3번은 surface lost는 화면전환등으로 인해 비디오 램에 손실이 가는 현상을 말한다. (다시설명)

그래서 결국 실행순서는 (프로그램을 짜야하는 부분만을 따짐)

1) OneTimeSceneInit() (프로그램 초기화시 호출)
2) InitDeviceObjects() (d3d 장치 초기화시 호출)
3) RestoreDeviceObjects() (처음시작하거나 리사이즈, alt-tab 사용시 복귀할때 호출)
4) FrameMove(), Render() (번갈아 가며 막 호출 -> 그림을 그리는데 있어서 중요한 부분)
5) InvalidateDeviceObjects() (리사이즈, alt-tab, 종료시 호출 )
6) DeleteDeviceObjects() (d3d 장치 종료시 호출)
7) FinalCleanup() (프로그램 종료시 호출)

 화면전환을 4번한다면 보통 아래와 같이 된다.
1)-2)-3)-4)-5)-3)-4)-5)-3)-4)-5)-3)-4)-5)-3)-4)-5)-6)-7)
여기서 보면 5)-3) 으로 이어지는 구간은  전환을 시도하면 서피스 로스트 현상이 일어나
5)가 호출이 되는거고 복구가 되면서 3)이 호출되는 것임.
(alt-tab이건 resize건 간에 포함.. 5)-3) 구간이 4개 있음을 알 수 있다.)
게임이 종료되면 5)가 호출되고 6)이 호출되고 7)이 호출되면서 프로세스가 종료된다.

그러나 텍스쳐의 경우 D3DPOOL_MANAGED로 할 경우 굳이 처리해줄 필요는 없다.
하지만 D3DPOOL_DEFAULT를 이용하면 비디오램만을 이용할것이다.
기본 예제에서는 월드 매트릭스를 이용해 삼각형이 회전하는 것을 보여준다.

DX8, 9 Frame Work의 추가 내용

엮인글을 보면 생각해보니 실제 게임에 대해서 빠진 부분이 있다.
바로 텍스쳐나 버텍스 버퍼의 초기화를 어디서 해야할까?
이것이 고민일것이다.
M$의 예제를 보면 보통 RestoreDeviceObjects() 에서 이들의 초기화를 하고 있다.
한장면을 연출하기 위할때는 여기도 괜찮지만 약간 볼륨이 커지면 이곳은 전혀 좋은 곳이 아니다.

왜냐면 RestoreDeviceObjects()는 어디까지나 화면복구시 호출되는 루틴이기 때문이다.
보통 처음에 실행되므로 여기다 초기화루틴을 넣었지만 만약 윈도우 size나 full화면전환이
일어나는 게임이라면 계속 데이터는 초기화 될수 있다.

그렇다고 OneTimeSceneInit()  에서는 아직 디바이스가 생기지 않았으므로
텍스쳐나 버텍스 버퍼등을 할당할 수 없다.

즉 이런 것을 해야하는 루틴은 FrameMove에 들어가야 한다.
하지만 FrameMove는 계속 호출되는것이 아닌가?

그래서 (그래서 였던가? ^^) 로딩장면을 만들고 그에 따라 한번만 호출되기 FrameMove에서
호출하면 되는것이다.
오브젝트가 하나가 움직이는것은 RestoreDeviceObjects()에서 구현해도 상관없으나
(이러면 보통은 윈도우 사이즈가 조절되면 리스타트 되는 경우가 많다..)
여러 오브젝트가 움직인다면 FrameMove()에 넣을 것을 권장하는 바이다.
또한 여기다 넣으면 Loading 화면을 구현하기도 쉬워진다. ^^
RestoreDeviceObjects()에서는 이미 사용하고 있는 오브젝트들이 문제가 있을 경우
다시 할당해 주면 된다.

FrameWork에서 빠진 주요 함수

한가지 더 중요한 함수가 있다.
그것은 MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) 이다.
이것은 콜백함수에서 호출하게 되어있다.
결국은 윈도우 메시지가 생길때 마다 MsgProc()가 호출되는것이다.
윈도우의 메시지를 잡아서 처리하고 싶을때 이 함수를 사용하면 된다.
MsgProc의 사용법을 모른다면 Windows 프로그래밍을 더 공부해라..
많은 윈도우즈 프로그래밍 책들이 이것을 잘 설명하고 있다.

언급은 했으나 설명이 부족한 함수는 FrameMove()Render() 이다.
소스를 보면 FrameMove() 한번 Update()한번 호출한다. FrameMove는 보통 Render()될
복잡한 CPU연산을 해준다.  Render()는 그냥 순수 Render() 함수만 해준다.
이 이유는 바로 Render()에서 Begin() End() 사이는 가급적 순수 렌더링만해주는것이
효율에 좋기 때문이다. 그때 CPU는 락이 걸리거나 기타 등등의 상황이 되기 때문이다.
이 구간은 가급적 다른 루틴을 넣는것은 좋지 않다. 또한 보기 좋게 하기 위해 FrameMove()에
넣을것을 권장한다. 하지만 설계가 잘 되어있지 않다면 FrameMove()에 넣기는 힘들것이다. ^^;

버텍스 버퍼에서 LOCK 할 때 무슨일이 일어나는가?
Lock을 실행할때는 대단히 주의 해야한다.
cpu가 이 작업에 할당되고 다른 일을 못하게 된다