home..

Directx 12 초기화

DirectX 12 초기화

DirectX 12는 저수준에 가깝게 API가 설계가 된 대신 성능최적화에 신경써서 초기화가 꽤 길어졌지만 한번만 수행하면 된다.

과정은 다음과 같은 단계들로 구성된다.

  1. D3D12CreateDevice 함수를 이용해서 ID3D12Device를 생성
  2. ID3D12Fence 객체를 생성하고 서술자1들의 크기를 얻는다.
  3. 사용하고자 하는 기능 지원 여부를 점검 ex) 4X MSAA2
  4. 명령 대기열, 명령 목록 할당자, 주 명령 목록 생성
  5. 교환 사슬을 서술하고 생성
  6. 응용 프로그램에 필요한 서술자 힙들을 생성
  7. 후면 버퍼의 크기를 설정하고, 후면 버퍼에 대한 렌더 대상 뷰3를 생성
  8. 깊이 ∙ 스텐실 버퍼를 생성하고, 그와 연관된 깊이 ∙ 스텐실 뷰를 생성
  9. 뷰포트 설정
  10. 가위 판정용 사각형 설정

장치 생성

장치는 디스플레이 어댑터를 나타나는 객체로 일반적으로 그래픽카드를 의미하지만, 소프트웨어 디스플레이 어댑터도 포함한다.

이러한 장치를 생성할 때는 다음과 같은 함수를 사용한다.

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를 호출하면 후면 버퍼의 COM4 참조 횟수가 증가하므로 사용한 후에는 반드시 해제해야 한다. (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라는 메서드를 사용한다.

각주

  1. 서술자는 응용프로그램에서 사용할 자원들을 어떻게 사용할지를 정의한 객체이다. 

  2. multisample anti-aliasing, 다중표본화를 의미 

  3. 서술자와 동의어 

  4. Component Object Model, DirectX의 프로그래밍 언어 독립성과 하위 호환성을 가능하게 하는 기술로 COM의 세부사항은 숨겨져 있지만 알아두어야 하는 건 COM 인터페이스를 가르키는 포인터를 특별한 함수 또는 또 다른 COM 인터페이스의 메서드를 이용해서 얻는 방법이다. 

© 2025 HookSSi   •  Powered by Soopr   •  Theme  Moonwalk