일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 절두체 컬링
- Frustum Culling
- Render Target
- 장치 초기화
- 큐브 매핑
- 게임 클래스
- 노멀 맵핑
- 네트워크 게임 프로그래밍
- 게임 디자인 패턴
- 게임 프로그래밍
- DirectX
- 동적 색인화
- DirectX12
- 입방체 매핑
- 네트워크
- 디퍼드 렌더링
- C++
- direct3d
- 조명 처리
- gitscm
- FrameResource
- 직교 투영
- Direct3D12
- light
- TCP/IP
- Deferred Rendering
- InputManager
- Dynamic Indexing
- effective C++
- gitlab
- Today
- Total
코승호딩의 메모장
[전체 화면 모드 전환] 본문
이번 포스팅은 DirectX12에서 전체 화면 모드로의 전환을 구현하는 방법을 기술합니다. 책의 내용에 전체 화면 관련 내용이 자세하게 나와 있지 않아 따로 내용을 추가적으로 기술하였습니다. 내용 중에 혹시라도 틀린 부분이나 추가해야할 부분이 있다면 피드백 해주시면 정말 감사드리겠습니다.
우선 전체 화면 모드는 시각적인 몰입도 뿐만 아니라 게임의 성능에도 영향을 끼친다. 전체 화면 모드에서는 DXGI는 플립(Flip)을 수행할 수 있다. 앞 포스팅에서 말했듯이 플립은 후면 버퍼와 전면 버퍼의 포인터만 하드웨어적으로 변경하기 때문에 후면 버퍼를 전면 버퍼에 모두 복사하는 블리트보다 성능이 좋다. 그러나 주의할 점은 DXGI가 플립을 수행하기 위해서는 후면 버퍼와 전면 버퍼의 크기와 형식이 일치해야 한다는 것이다.
윈도우의 클라이언트 영역의 크기가 변경되면 윈도우 운영체제는 WM_SIZE 메시지를 자동으로 발생시킨다. 그리고 DXGI는 변경되는 화면 모드에 따라 전면 버퍼의 크기를 자동적으로 변경한다. 따라서 Alt + Enter로 윈도우 모드에서 전체화면 모드로 변경하면 알아서 DXGI가 전면 버퍼의 크기를 자동적으로 변경하는 것이다.
그렇다면 그냥 Alt + Enter로 전체화면을 변경하거나 SetFullscreenState로 수동으로 전체화면 모드로 변경하면 되지 않을까?
> 답은 No다. 앞서 말했듯이 DXGI가 플립을 수행하기 위해서는 후면 버퍼와 전면 버퍼의 크기가 일치해야 한다. 즉 앞의 방식대로만 한다면 후면 버퍼의 크기는 그대로 일 것이다. 따라서 WM_SIZE 메시지에 따라 ResizeBuffer 함수를 사용해서 후면 버퍼의 크기를 바탕 화면 크기로 변경하면 될 것이다. 그렇다면 만약, 자신의 응용 프로그램의 후면 버퍼의 크기가 바탕 화면의 크기가 아니라면? 어떻게 해야 할까? 바로 스왑 체인을 생성할 때, 마지막 인자 FLAG에 DXGI_SWAP_CHAIN_ALLOW_MODE_SWITCH 플래그를 사용하고 ResizeTarget을 호출하면 모니터의 해상도가 응용 프로그램의 버퍼 크기와 일치하도록 변경될 것이다.
또한 주의할 점은, 스왑 체인이 생성될 때는 출력 타겟이 명시적으로 선택될 수 없다. 따라서 스왑 체인을 전체 화면 모드로 생성하지 않도록 권장한다. 출력 윈도우의 크기와 스왑 체인의 크기가 일치하지 않으면 프리젠테이션 성능에 문제가 발생하기 때문이다. 출력 윈도우의 크기와 스왑 체인의 크기를 일치시키는 방법은 다음과 같다.
- 스왑 체인을 윈도우 모드로 생성하고 SetFullscreenState 함수를 호출해 전체 화면 모드로 전환한다.
- 스왑 체인이 생성되면 스왑 체인에 대한 포인터를 저장한다. 응용 프로그램은 WM_SIZE 윈도우 메시지를 처리할 때 변경된 윈도우 클라이언트의 크기를 사용해 스왑 체인의 전면 버퍼와 후면 버퍼의 크기를 변경해야 한다. 이를 위해 ResizeTarget 함수와 ResizeBuffers 함수를 호출한다.
이제 다음으로 전체 화면 전환 코드를 알아보자.
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
...
#ifdef _WITH_SWAPCHAIN_FULLSCREEN_STATE
gGameFramework.ChangeSwapChainState();
#endif
return(TRUE);
}
우선 전처리기 지시어를 통하여 _WITH_SWAPCHAIN_FULLSCREEN_STATE이 정의되어 있다면 전체 화면 모드 변경한다.
case WM_KEYUP:
case VK_F9:
ChangeSwapChainState();
break;
case WM_SIZE:
{
_wndClientWidth = LOWORD(lParam);
_wndClientHeight = HIWORD(lParam);
break;
}
다음과 같이 F9키를 눌렀을 경우 ChangeSwapChainState 함수를 호출하고 이 함수에서는 SetFullScreenState를 통해 전체 화면에서 윈도우로 윈도우에서 전체 화면으로 변경한다. 그에 따라 클라이언트 영역의 크기가 변경 되었으므로 WM_SIZE 윈도우 메시지가 클라이언트 영역을 뜻하는 변수를 업데이트 하게 된다.
void CGameFramework::ChangeSwapChainState()
{
WaitSync();
BOOL bFullScreenState = FALSE;
_swapChain->GetFullscreenState(&bFullScreenState, NULL);
_swapChain->SetFullscreenState(!bFullScreenState, NULL);
DXGI_MODE_DESC targetParam;
targetParam.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
targetParam.Width = _wndClientWidth;
targetParam.Height = _wndClientHeight;
targetParam.RefreshRate.Numerator = 60;
targetParam.RefreshRate.Denominator = 1;
targetParam.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
targetParam.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
_swapChain->ResizeTarget(&targetParam);
for (int i = 0; i < SWAP_CHAIN_BUFFER_COUNT; ++i)
_rtvBuffer[i]->Release();
DXGI_SWAP_CHAIN_DESC sd;
_swapChain->GetDesc(&sd);
_swapChain->ResizeBuffers(SWAP_CHAIN_BUFFER_COUNT, _wndClientWidth, _wndClientHeight, sd.BufferDesc.Format, sd.Flags);
_swapChainBufferIndex = _swapChain->GetCurrentBackBufferIndex();
CreateRTV();
}
다음의 코드는 만약 현재 전체 화면이라면 윈도우 모드로, 윈도우 모드라면 전체 화면으로 전환하는 코드이다. 우선 GPU 대기열을 모두 비워준 상태에서 GetFullscreenState 함수와 SetFullscreenState 함수로 화면의 모드를 변경한다. 그 다음 출력 타겟의 크기와 스왑 체인의 후면 버퍼의 크기를 변경해줘야 한다. 때문에 ResizeTarget 함수를 사용하여 출력 타겟의 크기를 변경한다. 만약 스왑 체인이 윈도우 모드 상태일 때는 출력 윈도우의 크기를 변경할 것이고 스왑 체인이 전체 화면 모드 상태일 때는 디스플레이 모드를 변경할 것이다. 다음 현재 버퍼를 Release를 통해서 모든 참조를 해제한 후 새로운 클라이언트 영역을 가진 후면 버퍼를 ResizeBuffer 함수를 사용하여 변경한다. 마지막으로 새로 만든 버퍼에 대한 RTV를 생성한다.
참고로 필자는 _factory->MakeWindowAssociation(_hWnd, DXGI_MWA_NO_ALT_ENTER)를 통해서 Alt + Enter키를 이용해서 전체 화면으로 변경하는 것을 막아두었다. 만약 가능하게 하고 싶다면 이 명령어를 삭제하면 된다. 그러나 Alt + Enter를 눌러서 전체 화면으로 변경하였을 때 위 ChageSwapChainState 함수의 일련의 과정이 호출되지 않았기 때문에 반드시 ChageSwapChainState를 호출해야 한다.
잘 따라왔다면 다음과 같이 F9 키를 누르면 전체화면 -> 윈도우 모드 -> 전체화면 모드로 잘 전환이 될 것이다.
추가적으로 WM_SIZE 메시지는 응용 프로그램의 창 크기가 바뀔 때 발생한다. 창의 크기가 바뀌면 후면 버퍼와 깊이 스텐실 버퍼의 크기를 클라이언트 영역 직사각형에 맞게 갱신해야 한다. 후면 버퍼의 크기는 ResizeBuffers 메서드로 변경할 수 있으며 깊이 스텐실 버퍼를 파괴한 후 버퍼들뿐만 아니라 RTV와 DSV도 다시 생성해야 한다.
'DirectX12 > DirectX12 입문' 카테고리의 다른 글
[Frame Resource와 Render Item] (0) | 2023.09.19 |
---|---|
[Direct3D 렌더링] (0) | 2023.09.18 |
[시간 측정] (0) | 2023.09.14 |
[Direct3D 개요와 초기화] (0) | 2023.09.11 |