KoreanFoodie's Study
4-2. Direct3D 기초 : CPU와 GPU의 상호작용 , 명령 대기열, CPU/GPU 동기화 본문
4-2. Direct3D 기초 : CPU와 GPU의 상호작용 , 명령 대기열, CPU/GPU 동기화
GoldGiver 2021. 11. 17. 09:41
'DirectX 12를 이용한 3D 게임 프로그래밍 입문'을 읽으며 내용을 정리하고 중요한 부분을 기록하는 글입니다.
4-2. Direct3D 기초 : CPU와 GPU의 상호작용 , 명령 대기열, CPU/GPU 동기화
알아 두어야 할 개념들 :
명령 대기열과 명령 목록
CPU는 GPU와 상호작용하며 그래픽을 그려준다. 우리는 GPU와 CPU가 각각 쉬지 않고 열심히 일하기를 원한다. CPU/GPU 사이에는 동기화가 필요하지만, 동기화는 병렬성을 망칠 수 있다.
즉, 명령 대기열이 꽉 차 CPU가 놀거나 명령 대기열이 비어 있어 GPU가 노는 상황은 방지해야 한다.
Direct3D 12에서 명령 대기열을 대표하는 인터페이스는 ID3D12CommandQueue이다. 이 인터페이스를 생성하려면 대기열을 서술하는 D3D12_COMMAND_QUEUE_DESC 구조체를 채운 후 ID3D12Device::CreateCommandQueue를 호출해야 한다.
Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
ThrowIfFailed(md3dDevice->CreateCommandQueue(
&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
보조 매크로 IID_PPV_ARGS의 정의는 다음과 같다.
#define IID_PPV_ARGS(ppType) __uuidof(**(ppType)), IID_PPV_ARGS_Helper(ppType)
주요 메서드 중 하나는 명령 목록에 있는 명령들을 대기열에 추가하는 ExecuteCommandLists 메서드이다.
void ID3D12CommandQueue::ExecuteCommandLists(
// Number of commands lists in the array
UINT Count,
// Pointer to the first element in an array of command lists
ID3D12CommandList *const *ppCommandLists);
실제 그래픽 작업을 위한 명령 목록은 이 인터페이스를 상속하는 ID3D12GraphicsCommandList라는 인터페이스는 명령들을 명력 목록에 추가하는 여러 메서드가 있다. 다음 코드는 뷰포트를 설정하고, 렌더 대상 뷰를 지우고, 그리기 호출을 실행하는 명령드을 추가한다.
// mCommandList pointer to ID3D12CommandList
mCommandList->RSSetViewports(1, &mScreenViewport);
mCommandList->ClearRenderTargetView(mBackBufferView,
Colors::LightSteelBlue, 0, nullptr);
mCommandList->DrawIndexedInstanced(36, 1, 0, 0, 0);
이 코드들은 명령들을 목록에 추가만하고, 나중에 ExecuteCommandLists를 호출해야 명령들이 명령 대기열에 추가되어, 이후 GPU가 명령들을 뽑아 실행한다.
명령들을 명령 목록에 다 추가한 후에는 ID3D12GraphicsCommandList::Close 메서드를 호출해서 명령들의 기록이 끝났음을 Direct3D에 알려 주어야 한다.
mCommandList->Close();
명령 목록에는 ID3D12CommandAllocator 형식의 메모리 할당자가 하나 연관된다. 명령 목록에 추가된 명령들은 이 할당자의 메모리에 저장된다. 할당자는 ID3D12Device의 다음의 메서드를 이용해서 생성한다.
HRESULT ID3D12Device::CreateCommandAllocator(
// 명령 목록의 종류.
// DIRECT는 GPU가 직접 실행.
// BUNDLE은 명령을 묶음 단위로 기록하는 최적화 수단을 제공. 예제에서는 사용 X
D3D12_COMMAND_LIST_TYPE type,
// 생성하고자 하는 ID3D12CommandAllocator 인터페이스의 COM ID
REFIID riid,
// 생성된 명령 할당자를 가리키는 포인터 (출력 매개변수)
void **ppCommandAllocator);
명령 목록 역시 ID3D12Device로 생성한다.
HRESULT ID3D12Device::CreateCommandList(
// GPU가 하나인 시스템은 0. 여러 개일 때는 명령 목록과 연결시킬 물리적 GPU 어댑터 노드를 지정
UINT nodeMask,
// 명령 목록의 종류. DIRECT 또는 BUNDLE
D3D12_COMMAND_LIST_TYPE type,
// 생성된 명령 목록 할당자
ID3D12CommandAllocator *pCommandAllocator,
// 명령 목록의 초기 파이프라인 상태 지정
ID3D12PipelineState *pInitialState,
// ID3D12CommandList 인터페이스의 COM ID
REFIID riid,
// 생성된 명령 목록을 가리키는 포인터 (출력 매개변수)
void **ppCommandList);
한 할당자를 여러 명령 목록에 연관시켜도 되지만, 명령들을 여러 명령 목록에 동시에 기록할 수는 없다. 현재 명령들을 추가하는 명령 목록을 제외한 모든 명령 목록은 닫혀 있어야 한다. 따라서 같은 할당자로 두 명령 목록을 연달아 생성하면 다음과 같은 오류가 발생한다.
D3D12 ERROR: ID3D12CommandList::{Create,Reset}CommandList:
The command allocator is currently in-use by another command list.
ID3D12CommandQueue::ExecuteCommandList(C)를 호출한 후 ID3D12CommandList::Reset 메서드를 호출하면 C의 내부 메모리를 새로운 명령들을 기록하는 데 재사용할 수 있게 된다. Reset 메서드의 매개변수들의 의미는 ID3D12Device::CreateCommandList의 해당 매개변수들의 의미와 같다.
HRESULT ID3D12CommandList::Reset(
ID3D12CommandAllocator *pAllocator,
ID3D12PipelineState *pInitialState);
명령 목록을 재설정해도 명령 대기열이 참조하는 명령들은 연관된 명령 할당자의 메모리에 여전히 남아 있으므로 상관이 없다.
하나의 프레임을 완성하는 데 필요한 렌더링 명령들을 모두 GPU에 제출한 후, 명령 할당자의 메모리를 다음 프레임을 위해 재사용하기 위해 ID3D12CommandAllocator::Reset 메서드를 사용한다.
HRESULT ID3D12CommandAllocator::Reset(void);
이 개념은 std::vector::clear을 호출하는 것과 비슷하다. 단, 명령 대기열이 할당자 안의 자료를 참조하고 있을 수도 있으므로, GPU가 명령 할당자에 담긴 모든 명령이 실행했음이 확실해지기전 까지는 명령 할당자를 재설정하지 말아야 한다.
CPU/GPU 동기화
'Game Dev > DirectX' 카테고리의 다른 글
4-4. 시간 측정과 애니메이션 (타이머, GameTimer 클래스) (0) | 2021.11.18 |
---|---|
4-3. Direct3D 초기화 (ID3D12Device, Fence, 4X MSAA 점검 등) (0) | 2021.11.17 |
4-1. Direct3D 기초 : COM, 텍스쳐 형식, 교환사슬과 페이지 전환, 깊이 버퍼링, 다중 표본화, DXGI, 상주성 (0) | 2021.11.16 |
3. 선형변환, 아핀변환, 좌표 변환, DirectXMath 변환 함수 (0) | 2021.11.15 |
2. DirectXMath 라이브러리의 행렬 다루기 (0) | 2021.11.15 |