Directx 12 초기화
September 2025
DirectX 12 초기화
DirectX 12
는 저수준에 가깝게 API가 설계가 된 대신 성능최적화에 신경써서 초기화가 꽤 길어졌지만 한번만 수행하면 된다.
과정은 다음과 같은 단계들로 구성된다.
D3D12CreateDevice
함수를 이용해서ID3D12Device
를 생성ID3D12Fence
객체를 생성하고서술자
1들의 크기를 얻는다.- 사용하고자 하는 기능 지원 여부를 점검 ex) 4X MSAA2
명령 대기열
,명령 목록 할당자
, 주명령 목록
생성교환 사슬
을 서술하고 생성- 응용 프로그램에 필요한
서술자 힙
들을 생성 - 후면 버퍼의 크기를 설정하고, 후면 버퍼에 대한 렌더 대상 뷰3를 생성
- 깊이 ∙ 스텐실 버퍼를 생성하고, 그와 연관된 깊이 ∙ 스텐실 뷰를 생성
- 뷰포트 설정
- 가위 판정용 사각형 설정
장치 생성
장치는 디스플레이 어댑터를 나타나는 객체로 일반적으로 그래픽카드를 의미하지만, 소프트웨어 디스플레이 어댑터도 포함한다.
이러한 장치를 생성할 때는 다음과 같은 함수를 사용한다.
HRESULT WINAPI D3D12CreateDevice(
IUnknown* pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
REFIID riid, // ID3D12Device에 해당하는 ID를 넣어야 함
void** ppDevice
);
울타리 생성과 서술자 크기 얻기
장치를 생성한 다음에는 CPU와 GPU의 동기화를 위한 울타리 객체를 생성한다.
기능 지원 여부 점검
기능 지원은 다음과 같은 식으로 점검한다.
ThrowIfFailed(md3dDevice->CheckFeatureSupport(
D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
&msQualityLevels,
sizeof(msQualityLevels)
));
교환 사슬의 서술과 생성
이를 위해서는 우선 DXGI_SWAP_CHAIN_DESC
구조체 인스턴스의 멤버들을 생성하고자 하는 교환 사슬에 맞게 설저해야 한다.
typedef struct DXGI_SWAP_CHAIN_DESC
{
DXGI_MODE_DESC BufferDesc;
DXGI_SAMPLE_DESC SampleDesc;
DXGI_USAGE BufferUsage;
UINT BufferCount;
HWND OutputWindow;
BOOL Windowed;
DXGI_SWAP_EFFECT SwapEffect;
UINT Flags;
} DXGI_SWAP_CHAIN_DESC;
여기서 DXGI_MODE_DESC
는 또 다른 구조체로 다음과 같이 정의되어 있다.
typedef struct DXGI_MODE_DESC
{
UINT Width; // 버퍼 해상도 너비
UINT Height; // 버퍼 해상도 높이
DXGI_RATIONAL RefreshRate;
DXGI_FORMAT Format; // 버퍼 디스플레이 형식
DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;
DXGI_MODE_SCALING Scaling;
} DXGI_MODE_DESC;
교환 사슬을 서술하는 궂조체를 다 채웠으면 IDXGIFactory::CreateSwapChain
메서드를 사용해서 교환 사슬을 생성성한다.
HRESULT IDXGIFactory::CreateSwapChain(
IUnknown *pDevice, // ID3D12CommandQueue 포인터
DXGI_SWAP_CHAIN_DESC *pDesc, // 교환 사슬 서술 구조체 포인터
IDXGISwapChain **ppSwapChain // 생성된 교환 사슬 인터페이스를 돌려줌
);
서술자 힙 생성
서술자들을 담을 힙을 생성해야합니다.
메서드는 ID3D12Device::CreateDescriptorHeap
서술자 힙은 서술자 종류마다 따로 만드는 걸 주의 아래는 예시
ComPtr<ID3D12DescriptorHeap> mRtvHeap;
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));
렌더 대상 뷰(RTV) 생성
자원을 사용하려면 직접 파이프라인의 단계에 바인딩하는 게 아닌 자원에 대한 뷰(서술자)를 생성해서 그 뷰를 파이프라인 단계에 묶어야 한다.
특히, 후면 버퍼를 파이프라인에 바인딩하려면 후면 버퍼에 대한 렌더 대상 뷰를 생성해야 한다.
우선 교환 사슬에 저장되어 있는 버퍼 자원을 얻어야 하므로 다음 메서드를 사용한다.
HRESULT IDXGISwapChain::GetBuffer(
UINT Buffer,
REFIID riid,
void **ppSurface
)
HRESULT IDXGISwapChain::GetBuffer
를 호출하면 후면 버퍼의 COM
4 참조 횟수가 증가하므로 사용한 후에는 반드시 해제해야 한다. (ComPtr
을 사용하면 해제가 자동으로 처리)
렌더 대상 뷰를 생성할 때에는 ID3D12Device::CreateRenderTargetView
메서드를 사용한다.
다음은 교환 사슬의 두 버퍼에 대해 각각 RTV
를 생성하는 코드이다.
ComPtr<ID3D12Resource> mSwapChainBuffer[SwapChainBufferCount];
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
for(UINT i = 0; i < SwapChainBufferCount; i++)
{
// 교환 사슬의 i 번째 버퍼
ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i])));
// 버퍼에 대한 RTV를 생성
md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, rtvHeapHandle);
// 힙의 다음 항목으로
rtvHeapHandle.Offset(1, mRtvDescriptorSize);
}
깊이 ∙ 스텐실 버퍼와 뷰 생성
깊이 버퍼는 가장 가까운 물체들의 깊이 정보를(스텐실을 사용하는 경우 스텐실 정보도) 저장하는 2차원 텍스처이다.
텍스처는 GPU 자원의 하나이므로, 텍스처 자원을 서술하는 D3D12_RESOURCE_DESC
구조체를 채운 뒤
ID3D12Device::CreateCommittedResource
를 호출하면 버퍼를 생성할 수 있다.
GPU 자원들은 GPU의 힙에 존재한다. 본질적으로 GPU 힙은 GPU 메모리의 블록인데, 특정한 속성들을 가지고 있다.
ID3D12Device::CreateCommittedResource
메서드는 자원을 생성하고, 지정된 속성들에 부합하는 힙에 그 자원을 맡긴다.
그리고 렌더 대상 뷰를 생성하고 바인딩할 때 처럼 사용할려면 관련된 뷰를 생성하고 파이프라인에 바인딩해줘야한다.
뷰포트 설정
필요에 의해서 3차원 장면 즉 후면 버퍼의 일부를 그리고 싶다면 뷰포트(viewport)
를 서술하여 처리할 수 있다.
다음은 뷰포트를 서술하는 데 쓰이는 구조체이다.
typedef struct D3D12_VIEWPORT {
FLOAT TopLeftX;
FLOAT TopLeftY;
FLOAT Width;
FLOAT Height;
FLOAT MinDepth;
FLAOT MaxDepth;
} D3D12_VIEWPORT;
D3D12_VIEWPORT
구조체를 채운 후에는 ID3D12CommandList::RSSetViewports
메서드를 이용해서 뷰포트를 설정한다.
가위 직사각형 설정
가위 직사각형(scissor rectangle)
은 특정 픽셀들을 선별(Culling)하는 용도로 쓰인다.
일종의 최적화 기법이다. D3D12_RECT
라는 구조체로 서술한다.
typedef struct tagRect {
LONG left;
LONG top;
LONG right;
LONG bottom;
}
설정할 때에는 ID3D12CommandList::RSSetScissorRects
라는 메서드를 사용한다.