[DirectX12] #Part 2 - Direct 3D 기초
"DirectX12를 이용한 3D 게임 프로그래밍 입문" 서적의 Part 2, "Direct3D의 기초"에 해당하는 내용입니다.
현재, 게임 개발에 입문하여 기초지식을 습득하여 기록하기 위함이며, 개념 이해와 예제코드 작성을 진행.
명령 대기열(Command Queue)과 명령 목록(Command List)
GPU에는 명령 대기열이 있습니다. CPU는 그리기 명령어들이 담긴 명령 목록을 Direct3D API를 통해 그 대기열에 제출합니다. 하지만, CPU가 명령들을 명령 대기열에 제출했다고 해도, GPU는 그 명령들을 즉시 실행하지 않습니다! 여기서 우리가 알아야 할 점은 게임 같은 고성능 응용 프로그램의 목표는 가용 하드웨어 자원을 최대한 활용 할 수 있도록 CPU와 GPU의 상호작용을 원할하게 하는 것입니다.
ID3D12CommandQueue 인터페이스
#define IID_PPV_ARGS(ppType) __uuidof(**(pType)), IID_PPV_ARGS_Helper(ppType)
Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue; //CommandQueue를 가리키는 포인터
D3D12_COMMAND_QUEUE_DESC queueDesc = {}; //Desc 순서로 담을 배열
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAGE_NONE;
ThrowIfFailed(md3Device->CreateCommandQueue(
&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
먼저 메크로를 살펴보면, uuidof(**(ppType))은 (**(ppType))의 COM 인터페이스 ID로 평가됩니다. "ID3D12CommandQueue" 인터페이스의 주요 메서드는 명령들을 대기열에 추가하는 "ExecuteCommandList' 입니다.
ExecuteCommandList
void ID3D12CommandQueue::ExecuteCommandList (
//배열에 있는 명령 목록의 개수
UINT Count,
//명령 목록들의 배열을 가리키는 포인터
ID3D12CommnadList *const *ppCommandLists;
);
ID3D12CommandList(명령목록 인터페이스)
HRESULT ID3D12Device::CreateCommandList(
UINT nodeMask, //GPU가 하나일 경우 0으로 설정
D3D12_COMMAND_LIST_TYPE type, //명령 목록의 종류 아래에서 추가 설명
ID3D12CommandAllocator *pCommandAllocator, // 명령 목록 할당자 아래에서 설명
ID3D12PipelineState *pInitialState, // 명령 목록의 파이프라인 초기 상태
REFIID riid, // 인터페이스 COM id
void **ppCommandList //명령 목록을 가리키는 포인터
)
mCommandList->RSSetViewports(1, &mScreenViewport);
mCommnadList->ClearRenderTargetView(mBackBufferView,
Colors::LightSteelBlue, 0, nullptr);
mCommnadList->DrawIndexedInstance(36, 1, 0, 0, 0);
...
...
mCommandList->Close();
ID3D12CommnadList를 가리키는 포인터 mCommandList를 사용하여 명령들을 명령 목록에 추가하는 예제 코드입니다. 뷰포를 설정하고(RSSetViewports), 렌더 대상 뷰를 지우고(ClearRenderTargetView), 그리기 호출을(DrawIndexedInstance) 실행합니다. 이 메서드들은 단지 명령들을 명령 목록에 추가만 할뿐, 명령들을 실행 시키는건 "ExecuteCommandLists"입니다. 명령 목록을 제출하기 전에, 먼저 "ID3D12CommandAllocator" 형식의 메모리 할당자 하나를 명령 목록과 연관시켜야 합니다. 아래에서 살펴 보겠습니다.
ID3D12Device::CreateCommandAllocator (명령 목록 메모리 할당자)
HRESULT ID3D12Device::CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE type,
REFIID riid,
void **ppCommandAllocator,
);
여기서 type은 할당자와 연관 시킬 수 있는 명령 목록의 종류입니다. 흔히 두 종류가 있습니다.
1. D3D12_COMMNAD_LIST_TYPE_DIRECT: GPU가 직접 실행하는 명령 목록
2. D3D12_COMMNAD_LIST_TYPE_BUNDLE: 일련의 명령들을 소위 '묶음' 단위로 기록할 수 있는 최적화 수단
riid는 다시 ID3D12CommandAllocator 인터페이스의 COM ID입니다. 마지막은 당연히 메모리 할당자 객체를 가리키는 포인터겠죠. 추가적으로, 한 할당자를 여러 명령 목록에 연관시키는것은 가능하지만, 명령들을 여러 명령 목록에 동시에 기록할 수 없습니다. 명령목록들간의 명령들을 추가하는 작업은 "동기화" 작업이 필요하겠죠.
ID3D12Device::CommandList::Reset (명령 목록 해제 혹은 초기화)
HRESULT ID3D12CommnadList::Reset(
ID3D12CommandAllocator *pAllocator,
ID3D12PipelineState *pInitialState,
)
이 메서드는 주어진 Command List를 처음 생성했을때와 같은 상태로 만듭니다. 이 메서드를 이용하면 명령 목록을 해제하고, 명령 목록 내부의 메모리를 재사용 할 수 있게됩니다. 이러한 재설정 작업은 명령 대기열에 있는 명령들에는 영향이 미치지 않음을 주의해야 합니다. 명령 할당자의 경우, 하나의 프레임을 완성하는 데 필요한 렌더링 명령들을 명령 목록에 추가하고 할당자를 연관시킨 후에 GPU의 명령 대기열에 제출한 후에는, 명령 할당자의 메모리를 다음 프레임을 위해 재사용해야 합니다. 이때 ID3D12CommandAllocator::Reset을 사용합니다.
CPU는 명령들을 명령 목록에 추가하여 명령 할당자를 연관시키고, GPU의 명령 대기열에 추가합니다.
'게임개발 > DirectX12' 카테고리의 다른 글
[DirectX12]#Part2 - Direct3D의 기초_상주성(Residency) (0) | 2021.12.28 |
---|---|
[DirectX12]#Part2 - Direct3D의 기초_기능 지원 점검 (0) | 2021.12.28 |
[DirectX12]#Part2 - Direct3D의 기초_DXGI (0) | 2021.12.28 |
[DirectX12]#Part2 - Direct3D의 기초_엘리어싱과 다중표본화 (0) | 2021.12.27 |
[DirectX12]#Part2 - Direct3D의 기초_자원과 서술자 (0) | 2021.12.27 |