그래픽스/DirectX12

[Directx12][4장][2]Direct3D 기본 구조

우향우@ 2023. 3. 21. 21:31

기본 Direct3D의 구성

1.초기화 단계★

DIrectX12의 초기화는 다음의 단계를 따른다.

0.winapi를 통해 mainWindow생성 후 그 핸들 확보

1.IDXGIFactory4 생성

2.ID3D12Device 생성

3.fence생성 및 4X MSAA 품질 수준 지원 여부 점검 (지원/비지원, 최대 레벨)

4. 명령 대기열, 명령 할당자, 명령 목록 생성

5. 교환사슬 생성 (윈도우 핸들 필요)

6. 서술자 힙 생성

7. 교환사슬의 후면버퍼와 RTV서술자 힙에 대한 RTV생성

8. 깊이 스텐실 버퍼 생성 및 그 자원과 DSV 서술자 힙에 대한 DSV생성

9. 뷰포트 및 가위 직사각형 구조체 초기화

보통 함수 구조는 다음과 같이 된다.

init함수()
{
    메인 윈도우 생성();
    factory생성();
    device생성(); // 기본 어댑터로 생성 실패시 factory통해서 다른 어댑터 가져옴
    fence생성 및 mxaa 품질 수준 check(); // device 사용
    명령 삼형제 생성(); //device사용
    교환사슬 생성(); //device, 명령 대기열, 메인 윈도우 핸들 사용
    서술자 힙 생성(); //device 사용

    onResize(); /7,8,9포함
}

onResize() //7,8,9는 출력창 크기 변경시마다 다시 호출해야하기에 따로 빼둠
{
    명령 대기열 flush 및 명령 목록 reset(); //이 함수에서 gpu명령을 사용하기에 비우고 시작
    교환사슬의 후면버퍼들 및 기존 dsv자원 reset(); //초기화에는 의미없지만 resize시 필요
    교환사슬에 새로운 크기의 새 후면버퍼생성(); // 교환사슬 함수로 제공됨
    교환사슬의 후면버퍼와 RTV힙의 핸들에 대한 RTV 생성();
    깊이 스텐실 버퍼 생성();
    깊이 스텐실 버퍼와 DSV힙의 핸들에 대한 DSV 생성();
    명령 목록에 깊이 스텐실 버퍼의 상태 전이 명령어 삽입및 명령 대기열 제출();
    명령 대기열 flush();
    뷰포트 및 가위 직사각형 구조체 크기에 맞춰 초기화();
}

이러한 init함수가 처리되고 나면 9번까지의 초기화가 마무리된다.

이렇게 초기화를 진행하며 얻은 여러 객체나 데이터들은 전역 이나 멤버 변수들에 저장하여 이후에 사용 할 수 있도록한다.

init함수가 처리된 후에는 Run함수의 프레임 루프를 바로 실행 할 수 있게 된다.

2. 프레임 루프★

init함수가 수행된 후에는 Run함수를 수행하여 프레임 루프로 진입한다.

함수 구조는 다음과 같다.

Run()
{
    윈도우 메세지 구조체 msg 선언;
    타이머 초기화 // 타이머는 이 다음에 설명

    while(msg != QUIT)
    {
        if(PeekMessage(&msg.....)) //메시지가 있으면 처리, 없으면 프레임 처리
        {
            translate메시지(msg)
            dispatch메시지(msg)
        }
        else
        {
            타이머.Tick(); // 타이머 갱신으로 deltaTime등 계산
            if(앱 일시정지가 아니라면)
            {
                fps계산 및 타이틀에 출력;
                Update(타이머) // 기하구조 같은 추상정보 계산
                Draw(타이머) // 계산된 추상정보들로 실제 렌더링
            }
            else
            {
                sleep(100); // 앱 일시정지시 100단위로 대기
            }
        }        
	}
}

3. 타이머★

실시간 렌더링에서는 매 프레임의 변화를 계산해야한다. 이때 시간의 흐름을 위해 타이머 객체를 만들어 사용한다.

타이머 객체는 매 프레임마다 Tick메소드가 호출되며 그때마다 프로그램의 클럭타임을 불러와 시간의 흐름을 계산하고 기록한다.

타이머 객체의 주 역할은 매 프레임에서 한 프레임의 시간 변화량인 deltaTIme을 제공하는 것과 일시정지 시간을 제외한 특정 시점부터의 지금까지 흐른 총시간을 제공하는 것이다.

4. 메시지 처리

메시지 처리는 mainWindow를 생성할때 메시지 프로시저로 사용할 전역함수의 포인터를 desc구조체에 기록한뒤 mainWindow를 생성하면 해당 윈도우에서 발생한 메세지는 그 전역함수로 넘어가 처리되게 된다.

만약 객체의 메소드를 메시지 프로시저로 사용하고 싶다면 해당 전역함수에서 그 객체에 접근할 수 있도록 static 클래스 변수나 전역변수등에 저장한 뒤 전역변수에서 해당 객체의 메소드를 호출 해주면 된다.

참고서적

DirectX 12를 이용한 3D 게임 프로그래밍 입문