8일에 수업 받고 13일에 쓰는 DX12 정리. 정말 여전히 머리가 빙빙 돌지만... 2학년 2학기, 처음 배웠을 때는 아무 생각 없이 코드를 따라 쳤다면, 이제는 대충 이게 뭔지는 이해하고 코드를 치는 것 같다.
근데 이게 코드를 이해했다기보다는, DX12의 전체적인 흐름을 이해했다고 보는 게 빠르겠다. 코드는 아직도... 이해가 안 된다.
수업에서 장치 초기화는 이러한 순서로 이루어졌다. 오늘 복습해볼 내용들이다.
Device 생성
이름 | ID3D12Device |
의미 | 그래픽 카드를 추상화한 객체 |
사용 | Cmd List, Cmd Queue, Committed Resource, Swap Chain 등을 만들 때 사용된다. |
생성 | D3D12CreateDevice 함수 |
왜 굳이 D3D와 DXGI를 구분한 건지 필요성을 느끼지 못했는데, 찾아보니 DXGI에는 실제로 3D를 구현하는 데에 필요하지 않은 저수준의 그래픽 작업들을 관리하는 라이브러리라고 한다. IDXGIFactory 또한 3D 구현에 직접적으로 직결되지 않으니, DXGI의 라이브러리인 것 같다.
ComPtr<IDXGIFactory4> mdxgiFactory;
ComPtr<ID3D12Device> md3dDevice;
Factory, Device의 각 객체를 선언해주었다.
CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory));
CreateDXGIFactory1 함수로 Factory를 생성해준다. 매크로를 통해 인터페이스 ID와 주소를 전달했다.
HRESULT hardwareResult = D3D12CreateDevice
(
nullptr /* Default Adapter (Base GPU) */,
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&md3dDevice)
);
CreateDeviece 함수로 md3dDevice에 Device를 생성해주었다. 디폴트 어댑터로, 기대 수준은 DX11이 돌아가는 장치로 설정해주었다.
if (FAILED(hardwareResult)) // 레벨을 지원하지 않으면
{
ComPtr<IDXGIAdapter> pWarpAdapter;
// Adapter를 하나씩 가지고옴
mdxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter));
D3D12CreateDevice
(
pWarpAdapter.Get(), // 객체
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&md3dDevice)
);
}
만약 DX11 레벨을 지원하는 하드웨어 디바이스를 생성할 수 없다면, 다른 방법이 필요하다. 소프트웨어 어댑터인 WARP를 받는 장치를 생성하는 것이다. 이를 받으려면 DXGI 라이브러리의 IDXGIFactory라는 컴 객체가 필요하다.
IDXGIFactory는 어댑터, 출력장치, 디바이스를 포함하고 있는 객체라고 한다. 모니터가 몇 개 연결되어있는지, 그래픽 카드가 몇 개 장착되어있는지에 대한 정보를 얻을 수 있다. 후에 Swap Chain도 Factory를 사용해서 생성한다.
EnumWarpAdapter로 디바이스에 새로 제공할 소프트웨어 어댑터를 새로 받아오고 다시 장치를 만들어주었다.
Fence 만들기
CPU와 GPU의 동기화, 속도를 맞추기 위해 필요한 펜스! Device를 통해 만들어주었다.
// Fence
ComPtr<ID3D12Fence> mFence;
UINT64 mCurrentFence = 0; // 현재 펜스
ID3D12Fence 변수를 하나 선언해주었다. 나는 아직도 D3D와 DXGI를 가르는 명확한 기준을 잘 모르겠다... DXGI에 있을 거라고 생각한 친구인데 말이다.
후에는 mFence->GetCompletedValue()보다 mCurrentFence값이 크면 작업을 기다리도록 WaitForSingleObject 함수를 통해 작업을 기다려주게 설정할 것이다.
md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence));
방금 만든 장치를 사용해서 Fence를 만들어주었다.
Descriptor 크기 얻기
Descriptor 크기는 GPU마다 다를 수 있으므로 장치를 초기화할 때 크기를 얻었다.
// render target view
mRtvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// depth stencil
mDsvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
// cbv, srv, uav
mCbvSrvUavDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
내 GPU는 각각 32, 8, 32였지만... 언제 쓰는지는 잘 모르겠다. 후에 가면 알게 되겠지?
+) Swap Chain을 구현할 때 Descriptor Handle을 만들 때 썼다!
4X 멀티 샘플링 지원 여부 확인
DX11 이상 지원한다면, 멀티 샘플링 지원 여부는 따로 확인할 필요가 없지만, 공부차 점검한다고 하셨다.
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
msQualityLevels.Format = mBackBufferFormat; // R8G8B8A8_UNORM
msQualityLevels.SampleCount = 4; // 4x MSAA
msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
msQualityLevels.NumQualityLevels = 0;
// 후면 버퍼 4x MSAA 지원 여부 검사
md3dDevice->CheckFeatureSupport
(
D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
&msQualityLevels,
sizeof(msQualityLevels)
);
m4xMsaaQuality = msQualityLevels.NumQualityLevels;
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS라는 이름이 긴 구조체로 지원 여부를 검사했다.
내 컴퓨터는 구조체의 NumQualityLevels 값이 1이 나왔다. 으레 알고 있었다만, 4X MSAA가 지원되는 것이다.
또, 8X MSAA는 지원됐지만, 16X MSAA는 지원되지 않았다. 너무 큰가보다.
참고
'Direct X' 카테고리의 다른 글
[DX12] 빈 화면을 렌더링해보자! (1) | 2023.02.26 |
---|---|
[DX12] 장치를 초기화 해보자! (3) (RTV, DSV, Viewport) (0) | 2023.02.26 |
[DX12] 장치를 초기화 해보자! (2) (Command Object, Swap Chain) (0) | 2023.02.13 |
[DX12] DX12 프로그래밍 전 알아야 하는 것들 (0) | 2023.02.09 |
[DX12] DX12를 들어가며 (0) | 2023.02.09 |