시간이 정말 많이 지나버렸지만... 4번부터 다시 복습해볼 예정이다.
Command~
그리는 명령들을 담고 있는 CPU의 Command List, 그 리스트를 제출할 GPU의 Command Queue를 만들어볼 것이다. 일단~ 만들기 전 다시 복습해보는 Command Queue와 Command List.
Command~ 이론 복습
Command Queue
이름 | ID3D12CommandQueue |
역할 | CmdList에서 받아온 명령을 GPU에 제출 |
생성 | ID3D12Device->CreateCommandQueue |
- 작업 완료 전까지 작업에서 참조하는 D3D리소스들이 해제되어서는 안 된다.
Command Allocator
이름 | ID3D12CommandAllocator |
역할 | 명령리스트에 추가된 명령들을 메모리에 저장하는 역할 |
생성 | ID3D12Device->CreateCommandAllocator |
Command List
이름 | ID3D12GraphicsCommandList |
역할 | Cmd Queue에 넣을 그래픽 렌더링 명령을 담는 리스트. |
생성 | ID3D12Device->CreateCommandList |
- ExecuteCommandList 함수를 실행해 GPU에 명령을 제출해도, 즉시 실행되지 않고 GPU가 적당한 때가 되면 실행한다.
- 생성시 Command Allocator (명령 할당자)가 필요하다.
- 현재 명령들을 추가하는 명령리스트 외에는 모두 닫혀있어야 한다. (한 리스트의 모든 명령이 할당자 안에 인접해서 저장되기 때문)
- 따라서 생성, 재설정시 열린 상태가 되는 것을 주의해야 한다.
=> 한 할당자로 여러 리스트를 생성하면 안 된다.
Command Queue 생성
ComPtr<ID3D12CommandQueue> mCommandQueue;
CommandQueue 변수를 선언해준다.
Command Queue는 GPU에 명령을 보내기 때문에, GPU가 Command Queue를 알아보기 위해서는 항상 Descriptor를 채워줄 필요가 있다.
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; // GPU가 직접 실행할 수 있는 명령 리스트
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; // 기본 명령 큐
우선순위, 노드마스크는 기본값으로 처리해주었다. 이 프로젝트에 명령 큐는 하나이기 때문에!
Command Allocator, List 생성
1할당자 1리스트이므로, 그 둘의 타입은 일치해야한다는 것을 기억하고 두 객체를 생성하면 된다.
// Cmd Alloc
md3dDevice->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&mDirectCmdListAlloc)
);
// Cmd List
md3dDevice->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
mDirectCmdListAlloc.Get(),
nullptr,
IID_PPV_ARGS(&mCommandList)
);
mCommandList->Close();
GUP가 직접 실행할 수 있는 타입(D3D12_COMMAND_LIST_TYPE_DIRECT)으로 통일해주었다. 만든 후, Command List는 클로즈해주었다. 나중에 그리기 전, Reset 함수를 호출해서 Open 상태로 만들어줄 것이다.
Swap Chain
백, 프론트 버퍼의 포인터만 바꾸어주는 Swap Chain~ 이 친구도 이론 복습을 한 번 하고 가는 게 좋겠다.
Swap Chain 이론
이름 | IDXGISwapChain |
역할 | 렌더링 화면 문제를 해결하기 위함 (더블 버퍼링) |
생성 | IDXGIFactory4->CreateSwapChain |
위처럼 화면 렌더 문제를 해결하기 위해 백 버퍼와 프론트 버퍼를 두는 더블 버퍼링을 사용한다. 스왑 체인은 더블 버퍼링과 비슷하지만, 프론트 버퍼가 백 버퍼를 복사하지 않고 렌더링하는 순간에 플리핑하며 둘의 위치, 즉 포인터를 바꾼다.
이로 인해 프론트 버퍼가 백 버퍼를 복사하는 과정을 생략해 효율적이다.
GPU가 읽어야하므로 Descriptor를 설정해주어야 한다.
Swap Chain 생성
// Swap Chain
ComPtr<IDXGISwapChain> mSwapChain;
static const int SwapChainBufferCount = 2; // buffer count (front, back => 2)
int mCurBackBuffer = 0; // cur back index
ComPtr<ID3D12Resource> mSwapChainBuffer[SwapChainBufferCount]; // buffers
구현을 위한 변수를 선언해주었다. 버퍼는 더 많아도 되지만, 프론트와 백. 딱 두 개만 사용했다.
mSwapChain.Reset();
DXGI_SWAP_CHAIN_DESC scDesc;
// ------------------ BUFFER DESC ---------------------
scDesc.BufferDesc.Width = mClientWidth; // 버퍼 가로 크기
scDesc.BufferDesc.Height = mClientHeight; // 버퍼 세로 크기
scDesc.BufferDesc.RefreshRate.Numerator = 60;
scDesc.BufferDesc.RefreshRate.Denominator = 1; // 1초에 60헤르츠
scDesc.BufferDesc.Format = mBackBufferFormat; // DXGI_FORMAT_R8G8B8A8_UNORM
scDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
scDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
// ----------------------------------------------------
// ------------------ SAMPLE DESC ---------------------
scDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
scDesc.SampleDesc.Quality = m4xMsaaState ? m4xMsaaQuality - 1 : 0;
// ----------------------------------------------------
scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 후면 버퍼 => 렌더 타겟용
scDesc.BufferCount = SwapChainBufferCount; // 버퍼 개수
scDesc.OutputWindow = mhMainWnd; // 핸들 (HWND)
scDesc.Windowed = true; // 창모드
scDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // Present 호출 후 백 버퍼 내용 버림
scDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // 디스플레이 모드 변경 가능
리셋 후 Descriptor에 엄청나게 많은 걸 설정해주었다...
// SWAP CHAIN 생성
mdxgiFactory->CreateSwapChain
(
mCommandQueue.Get(),
&scDesc,
&mSwapChain
);
그리고 Factory의 CreateSwapChain 함수를 통해서 Swap Chain을 만들어주었다. 왜 Command Queue가 필요한지 궁금해서 검색해봤는데, 찾을 수 없었다 ㅠ_ㅠ...
for (int i = 0; i < SwapChainBufferCount; ++i)
{
mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i]));
}
만든 Swap Chain의 버퍼들을 미리 만든 리소스 배열로 가져왔다.
참고
'Direct X' 카테고리의 다른 글
[DX12] 빈 화면을 렌더링해보자! (1) | 2023.02.26 |
---|---|
[DX12] 장치를 초기화 해보자! (3) (RTV, DSV, Viewport) (0) | 2023.02.26 |
[DX12] 장치를 초기화 해보자! (1) (Device, Fence, Desc Size, MSAA) (0) | 2023.02.13 |
[DX12] DX12 프로그래밍 전 알아야 하는 것들 (0) | 2023.02.09 |
[DX12] DX12를 들어가며 (0) | 2023.02.09 |