오늘은 6단계부터 쭉~ 장치 초기화를 끝낼 것이다.
Descriptor Heap
이론부터 복습하고 가자.
Descriptor Heap 이론
Descriptor
리소스를 설명하기 위한 자료구조이다. 셰이더 혹은 GPU에 리소스를 연결할 경우, 이 리소스가 무엇인지 설명해주어야 한다. 앞서 만든 Command Queue나 SwapChain도 이 때문에 Descriptor를 설정해준 것이다.
실제로 GPU나 셰이더는 Descriptor를 받기 때문에, 여기에는 실제 리소스의 위치도 들어있고 접근 가능하다.
Descriptor Heap
DX12는 모든 Descriptor에 대해서 테이블을 만든다고 한다. 이를 서술자 힙, 즉 Descriptor Heap이라고 한다. 이 테이블 안에는 Descriptor들이 있다.
목적은 이렇다 하는데... 이렇게 보니 잘 모르겠어서 21세기의 공부 방식을 채택해 ChatGPT에게 쉬운 말로 풀어달라고 부탁했다.
데이터 전송의 최소화, 자원에 대한 액세스를 빠르게 수행해 렌더링 파이프라인의 성능을 향상시키는 용도가 있다고 한다. 아무리 AI가 똑똑하다고 해도, 선생님께 한 번 더 여쭤보기로 했다. 오늘은 주말이니까, 내일!
+)
RTV Descriptor Heap 생성
ComPtr<ID3D12DescriptorHeap> mRtvHeap;
DescriptorHeap 주소를 저장할 변수와 각 Descriptor를 가지고 있을 배열을 만들었다.
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = SwapChainBufferCount; // RenderTarget 개수만큼
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; // RTV Heap
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0; // 단일 GPU 어댑터이므로 0
md3dDevice->CreateDescriptorHeap
(
&rtvHeapDesc,
IID_PPV_ARGS(&mRtvHeap)
);
Descriptor의 값을 설정하고 Heap을 생성해주었다.
DSV Descriptor Heap 생성
ComPtr<ID3D12DescriptorHeap> mDsvHeap;
RTV Desc Heap과 동일하게 만들 곳을 선언해주었다.
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
md3dDevice->CreateDescriptorHeap
(
&dsvHeapDesc,
IID_PPV_ARGS(&mDsvHeap) // 시작 주소
);
타입과 개수 등등을 지정해주고 생성 완료~
RTV View
Render Target 이론
Render Target은 렌더링을 수행할 때 타겟이 되는 텍스쳐. 백 버퍼에 대한 Render Target View를 Output Merger 단계에 바인딩해야한다.
RTV View
이름 | D3D12_CPU_DESCRIPTOR_HANDLE |
역할 | GPU가 리소스(Render Target Texture)의 정보를 읽을 수 있게 하기 |
생성 | ID3D12Device->CreateRenderTargetView |
D3D12_CPU_DESCRIPTOR_HANDLE mRtvView[SwapChainBufferCount] = {};
리소스 뷰를 담을 공간을 만들어준다. 본래 리소스 뷰를 생성하고자 한다면, 리소스가 필요하지만, RTV같은 경우 이미 Swap Chain을 만들 때 2개의 버퍼(Back, Front)를 만들어주었기 때문에 따로 생성하지 않았다.
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
for (int i = 0; i < SwapChainBufferCount; i++)
{
mRtvView[i] = CD3DX12_CPU_DESCRIPTOR_HANDLE(rtvHeapHandle, i * mRtvDescriptorSize);
md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, mRtvView[i]);
}
헬퍼 클래스를 이용해 생성했다. 미리 생성해둔 RTVHeap의 시작 주소를 기반으로 RTVDescSize로 늘려가며 Descriptor의 주소를 가지고 RtvView에 넣어주었다.
DSV View, Depth Stencil Buffer
Depth Stencil Buffer 이론
시야 절두체(카메라) 안에서 얼마나 가까운지를 담고 있는 버퍼. 가까울 수록 0에 가깝고, 멀수록 1에 가깝다. 꼭 0과 1이 아니어도 된다고 하는데, 흔치 않은 경우이 듯하다.
이 Depth Stencil Buffer도 View로 표현되어 출력 병합기 단계에서 바인딩된다.
DSV View
이름 | D3D12_CPU_DESCRIPTOR_HANDLE |
역할 | GPU가 리소스(Depth Stencil Buffer)의 정보를 읽을 수 있게 하기 |
생성 | ID3D12Device->CreateRenderTargetView |
Depth Stencil Buffer 생성
// Depth Stencil Buffer
ComPtr<ID3D12Resource> mDepthStencilBuffer;
D3D12_CPU_DESCRIPTOR_HANDLE mDsvView = {};
리소스를 담을 공간, 뷰를 담을 공간을 같이 만들어주었다.
// -------------------- DESC -------------------------
D3D12_RESOURCE_DESC depthStencilDesc;
depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; // 2D Texture
depthStencilDesc.Alignment = 0;
depthStencilDesc.Width = mClientWidth; // Window Size
depthStencilDesc.Height = mClientHeight;
depthStencilDesc.DepthOrArraySize = 1;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; // Depth Stencil
// ---------------------------------------------------
// ----------------- CLEAR_VALUE --------------------
D3D12_CLEAR_VALUE optClear;
optClear.Format = mDepthStencilFormat; // DXGI_FORMAT_D24_UNORM_S8_UINT
optClear.DepthStencil.Depth = 1.0f;
optClear.DepthStencil.Stencil = 0.f;
// ---------------------------------------------------
//CommittedResource => CPU, GPU 공통 사용
md3dDevice->CreateCommittedResource
(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), // GPU만 사용 가능
D3D12_HEAP_FLAG_NONE,
&depthStencilDesc,
D3D12_RESOURCE_STATE_COMMON,
&optClear,
IID_PPV_ARGS(&mDepthStencilBuffer)
);
Descriptor와 Clear Value의 적절한 값을 넣어주고 커밋 리소스로 만들어주었다.
Depth Stencil View 생성
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesv;
dsvDesv.Flags = D3D12_DSV_FLAG_NONE;
dsvDesv.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
dsvDesv.Format = mDepthStencilFormat;
dsvDesv.Texture2D.MipSlice = 0;
mDsvView = mDsvHeap->GetCPUDescriptorHandleForHeapStart();
md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesv, mDsvView);
버퍼 코드에 비해서는 매우 짧다. RTV를 생성할 때처럼 올바른 값들을 넣어주고, 미리 만든 DescHeap의 시작 주소를 가져와서 View 또한 만들어주었다.
Viewport
Viewport는 렌더링할 영역이다. Command List가 리셋이 되면, 반드시 다시 Viewport를 세팅해주어야 한다. ScissorRect는 렌더링에서 제거하지 않을 영역을 설정하는 것이다. 특별한 이유가 없다면 Viewport와 똑같이 생성해주면 된다고 한다.
mScreenViewport.TopLeftX = 0;
mScreenViewport.TopLeftY = 0;
mScreenViewport.Width = static_cast<float>(mClientWidth);
mScreenViewport.Height = static_cast<float>(mClientHeight);
mScreenViewport.MinDepth = 0.0f;
mScreenViewport.MaxDepth = 1.0f;
mScissorRect = { 0, 0, mClientWidth, mClientHeight };
나중에 Draw 전 방금 설정한 Viewport와 ScissorRect를 설정해주는데, 아마 다음 편에서 하게될 것 같다.
이제 장치 초기화가 끝났다~ 렌더링 준비는 아직 많이 남았지만, 렌더링 파이프라인의 단계와 렌더링에 중요한 각 요소들을 알아볼 수 있어서 재미있었던 시간.
참고
'Direct X' 카테고리의 다른 글
[DX12] 렌더링 파이프라인에 대해 알아보자! (3) | 2023.02.27 |
---|---|
[DX12] 빈 화면을 렌더링해보자! (1) | 2023.02.26 |
[DX12] 장치를 초기화 해보자! (2) (Command Object, Swap Chain) (0) | 2023.02.13 |
[DX12] 장치를 초기화 해보자! (1) (Device, Fence, Desc Size, MSAA) (0) | 2023.02.13 |
[DX12] DX12 프로그래밍 전 알아야 하는 것들 (0) | 2023.02.09 |