이전 편에서는 CommandAllocator와 CommandQueue 그리고 SwapChain을 구성하는 방법에 대해서 알아보았습니다.
Rust에서 DirectX12 개발하기(3) - DirectX 초기화하기(CommandQueue, CommandAllocator, SwapChain 초기화)
이전 편에서는 Rust에서 DirectX12를 사용하기 위해서, 디바이스를 초기화하는 방법을 살펴보았습니다. 2021.06.07 - [개발/Rust] - Rust에서 DirectX12 개발하기(2) - DirectX 초기화하기(디바이스 초기화) Rust..
honey-balm.tistory.com
이번 편에서는 RenderTargetView를 생성하는 방법에 대해서 알아보겠습니다.
RenderTargetView(RTV)란 간단하게 말해서 렌더링 파이프라인의 출력(한 프레임의 이미지 등)을 특정한 리소스와 바인딩할 때 사용됩니다. DirectX12에서는 어떠한 자원이 렌더링 파이프라인에 직접적으로 연결되지 않습니다. 연결된다는 것은 파이프라인이 사용할 자원에 대한 정보를 얻게 되는 과정으로 이해하시면 될 것 같습니다.
즉, 이전 단계에서 SwapChain을 생성했고, 그 때 buffer_index로 지정한 숫자만큼의 버퍼를 생성했습니다. 하지만 이 버퍼를 렌더링 파이프라인에 직접적으로 연결할 수 없다는 뜻입니다. 위에서도 설명했듯이, 렌더링 파이프라인에 연결하기 위해서는 RenderTargetView를 사용해야하는 것입니다.
사실 DirectX12에서는 RenderTargetView만이 아닌 다른 많은 리소스들이 파이프라인에 직접적으로 연결되지 않습니다. 그렇기 때문에 Descriptor라고 불리는 설명자를 생성해서, 이 Descriptor를 파이프라인에 연결해야합니다. RTV도 이 Descriptor의 일종입니다. 1
RTV를 생성하기 전에, RTV를 담고 있을 Descriptor Heap을 생성해야 합니다. Descriptor Heap이란 여러 Descriptor를 가지고 있는 일종의 Descriptor 배열입니다. 2
RTV를 생성하는 코드는 아래와 같습니다.
fn create_rtv_heap(
&self,
buffer_count: u32,
device: &ID3D12Device,
) -> Result<(ID3D12DescriptorHeap, u32)> {
let rtv_heap: ID3D12DescriptorHeap = unsafe {
device.CreateDescriptorHeap(&D3D12_DESCRIPTOR_HEAP_DESC {
NumDescriptors: buffer_count,
Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
..Default::default()
})
}?;
let rtv_desc_size =
unsafe { device.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV) };
Ok((rtv_heap, rtv_desc_size))
}
ID3D12Device::CreateDescriptorHeap 함수를 통해서 쉽게 DescriptorHeap을 생성할 수 있습니다. 매개변수로는 D3D12_DESCRIPTOR_HEAP_DESC 3가 전달됩니다. D3D12_DESCRIPTOR_HEAP_DESC의 구조체는 아래와 같습니다. 4
typedef struct D3D12_DESCRIPTOR_HEAP_DESC {
D3D12_DESCRIPTOR_HEAP_TYPE Type;
UINT NumDescriptors;
D3D12_DESCRIPTOR_HEAP_FLAGS Flags;
UINT NodeMask;
} D3D12_DESCRIPTOR_HEAP_DESC;
이 중에서 Type은 Descriptor의 타입을 설정하는 곳이고, NumDescripotr는 Descriptor의 수, Flags는 해당 힙이 GPU에서 사용될 수 있는지 지정합니다. NodeMask는 하나의 GPU만 사용할 경우에는 0으로 설정하면 됩니다.
위 코드에서는 RTV를 저장할 힙을 생성하므로, Type 필드를 D3D12_DESCRIPTOR_HEAP_TYPE_RTV로 설정했고, NumDescriptors 필드는 buffer_count로 설정합니다. 나머지 값은 기본 값으로 초기화하면 됩니다.
각 힙에 담길 Descriptor의 크기를 Device::GetDescriptorHandleIncrementSize 함수를 통해서 얻습니다. 이 크기는 힙 내부에서 Descriptor를 조회할 때 사용됩니다. 5
RTV를 담는 힙을 생성했으니, 이제는 실제 RTV를 생성하면 됩니다. 코드는 아래와 같습니다.
fn create_render_target(
&self,
buffer_count: u32,
rtv_desc_size: u32,
device: &ID3D12Device,
swap_chain: &IDXGISwapChain,
rtv_heap: &ID3D12DescriptorHeap,
) -> Result<Vec<ID3D12Resource>> {
let rtv_handle = unsafe { rtv_heap.GetCPUDescriptorHandleForHeapStart() };
let mut render_targets = Vec::new();
for i in 0..buffer_count {
let render_target: ID3D12Resource = unsafe { swap_chain.GetBuffer(i) }?;
unsafe {
device.CreateRenderTargetView(
&render_target,
std::ptr::null(),
&D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: rtv_handle.ptr + (rtv_desc_size * i) as usize,
},
)
}
render_targets.push(render_target);
}
Ok(render_targets)
}
위 코드에서는 D3D12_CPU_DESCRIPTOR_HANDLE이란 구조체를 ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart 함수를 통해서 얻습니다. 6
DirectX12에서 사용되는 리소스들은 종류에 따라서 가시성의 범위가 달라집니다. 기본적으로 모든 리소스는 CPU에서 사용될 수 있지만, GPU에서는 특정 리소스들만이 사용될 수 있습니다. RTV의 경우 CPU에서만 사용되는 리소스입니다. 그렇기 때문에 CPU Handle을 얻습니다.
그 다음 buffer_count만큼 RTV를 생성해주고, 생성한 RTV를 render_targets 벡터에 push 해주면 됩니다. RTV를 생성할 때는 ID3D12Device::CreateRenderTargetView라는 함수를 사용합니다. 7
SwapChain에서 버퍼를 얻어온 다음, 해당 버퍼를 CreateRendeTargetView의 pSource로 전달해줍니다. pDesc는 NullDescriptor를 전달해주고, 마지막으로 D3D12_CPU_DESCRIPTOR_HANDLE 구조체를 전달해줍니다.
D3D12_CPU_DESCRIPTOR_HANDLE의 유일한 필드는 ptr이며, 생성할 RTV의 주소를 지정해주면 됩니다. 이전에 얻은 rtv_handle.ptr을 통해 힙의 시작 지점 포인터를 얻은 후 create_rtv_heap 함수를 통해서 얻은 rtv_desc_size의 크기를 통해서 각 rtv의 주소값을 구해줍니다.
각 생성 함수를 호출하는 코드는 아래와 같습니다.
let (rtv_heap, rtv_desc_size) = ret.create_rtv_heap(2, &device)?;
let render_targets =
ret.create_render_target(2, rtv_desc_size, &device, &swap_chain, &rtv_heap);
이번 편에서는 DescriptorHeap을 생성하는 방법과 RenderTargetView를 생성하는 방법에 대해서 알아봤습니다.
샘플 프로젝트 코드는 github에 올렸습니다.
seonhwi07jp/rust-d3d12 (github.com)
seonhwi07jp/rust-d3d12
Contribute to seonhwi07jp/rust-d3d12 development by creating an account on GitHub.
github.com
해당 코드는 ver5 브랜치로 이동하면 볼 수 있습니다.
- Resource Binding Overview - Win32 apps | Microsoft Docs [본문으로]
- Descriptor Heaps Overview - Win32 apps | Microsoft Docs [본문으로]
- ID3D12Device::CreateDescriptorHeap (d3d12.h) - Win32 apps | Microsoft Docs [본문으로]
- D3D12_DESCRIPTOR_HEAP_DESC (d3d12.h) - Win32 apps | Microsoft Docs [본문으로]
- ID3D12Device::GetDescriptorHandleIncrementSize (d3d12.h) - Win32 apps | Microsoft Docs [본문으로]
- ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart (d3d12.h) - Win32 apps | Microsoft Docs [본문으로]
- ID3D12Device::CreateRenderTargetView (d3d12.h) - Win32 apps | Microsoft Docs [본문으로]
'개발 > Rust' 카테고리의 다른 글
Rust에서 DirectX12 개발하기(3) - DirectX 초기화하기(CommandQueue, CommandAllocator, SwapChain 초기화) (0) | 2021.06.09 |
---|---|
Rust에서 DirectX12 개발하기(2) - DirectX 초기화하기(디바이스 초기화) (0) | 2021.06.07 |
Rust에서 DirectX12 개발하기(1) - 윈도우 창 생성하기 (0) | 2021.06.07 |
Rust에서 DirectX12 개발하기(0) - 개발환경 준비하기 (0) | 2021.06.06 |