[DirectX12] Descriptor Heap

윤태웅·2022년 8월 17일
0

DirectX12

목록 보기
1/11
post-thumbnail
post-custom-banner

개요

DirectX12의 GPU메모리 관리의 핵심인 Descriptor Heap 정리

Descriptor Heap

Descriptor Heap은 Descriptor들이 저장된 GPU힙 메모리를 의미한다.

Descriptor

Descriptor Heap을 이해하기 위해서는 먼저
Descriptor가 무엇인지 정확히 알아야 한다.

위 그림은 Descriptor를 이해하기 아주 좋은 예시이다.
Descriptor는 GPU메모리에 존재하는 Resource메모리에 대한 포인터, 해당 Resource의 Type,Format등을 저장하는 단위다. Descriptor의 종류로는 Shader Resource View, Constant Buffer View, Unordered Access Buffer View, Render Target View,Sampler 등이 있다.
View라는 이름은 Resource를 해석하는 데이터를 포함하기 때문에 붙은 이름인듯 하다.

Descriptor Heap

다시 Descriptor Heap으로 돌아와보자. Descriptor Heap은 Descriptor들이 저장된 GPU 힙 메모리 공간이다.

위 사진에는 Root Signature와 Descriptor Range같은 개념들도 섞여있는데, 이는 다음 포스팅에서 다룰 예정이고 일단, 우측에 있는 Descriptor Heap과 Resource Heap에 주목하자. Descriptor Heap의 요소인 CBV,SRV,UAV는 각각 Constant Buffer View, Shader Resource View, Unorderred Access View를 의미하고 Resource Heap의 요소들은 Descriptor들이 포인팅하는 실제 Resource들을 의미한다.

Descriptor Heap의 Pipeline바인딩

Descriptor Heap은 매 프레임 Command List마다

void SetDescriptorHeaps(
  UINT                 NumDescriptorHeaps,
  ID3D12DescriptorHeap * const *ppDescriptorHeaps
);

함수 호출로 바인딩해줄 수 있다. 이때, NumDescriptorHeaps라는 인자 때문에
많은 수의 Descriptor Heap을 동시에 바인딩할 수 있다고 착각할 수 있는데,
공식 문서에 따르면 동시 바인딩되는 것은 CBV_SRV_UAV type Descriptor Heap,Sampler Descriptor Heap 2개가 최대치라고 한다.

헷갈리는 개념, CPU Handle vs GPU Handle

Descriptor Heap을 이해하는데 가장 난해했던 부분이다.
코드로 예를 들어 보겠다.

HRESULT CBVSRVUAVDescriptorHeap::CreateUAV(
		_In_ const ComPtr<ID3D12Device>& pDevice,
		_In_ const ComPtr<ID3D12Resource>& pUAVResource,
		_In_ const D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc,
		_Out_ D3D12_GPU_DESCRIPTOR_HANDLE* gpuDescriptorHandle
	)
	{
		HRESULT hr = S_OK;
		CD3DX12_CPU_DESCRIPTOR_HANDLE uavCPUHandle{};
		hr = getEmptyDescriptorSpaceCPUHandle(&uavCPUHandle);
		if (FAILED(hr))
		{
			return hr;
		}
		pDevice->CreateUnorderedAccessView(pUAVResource.Get(), nullptr, &uavDesc, uavCPUHandle);
		CD3DX12_GPU_DESCRIPTOR_HANDLE gpuHandle{};
		getLastAllocatedDescriptorGPUHandle(&gpuHandle);
		*gpuDescriptorHandle = gpuHandle;
		m_numAllocated++;
		return hr;
	}

위 코드는 Unordered Access Resource에 대한 UAV를 GPU Descriptor heap메모리에 추가하는 코드이다.
CreateUnorderedAccessView의 네 번째 인자로 Descriptor Heap의 빈 공간에 대한 CPU Handle을 주고있음에 주목하자. 그리고 이렇게 생성한 Descriptor를 Command List에 바인딩하는 코드는 다음과 같다.

pCommandList->SetComputeRootDescriptorTable(
	static_cast<UINT>(EGlobalRootSignatureSlot::OutputViewSlot),
	m_raytracingOutputGPUHandle
);//아웃풋 UAV텍스쳐 바인딩

Command List에 GPU Handle을 주고 있다. 나는 여기서 헷갈리는 점이 2가지 생겼다.

  1. CPU Handle은 뭐고 GPU Handle은 무엇인가?
  2. 왜 만들때는 CPU Handle을 인자로 사용하고 Command List에 바인딩해줄 때는 GPU Handle을 인자로 사용하는가?

공부를 좀 더 해보니 의문이 풀렸다.
먼저 CPU Handle, GPU Handle의 개념은 다음과 같다.

CPU Handle: CPU Memory상에서의 GPU Descriptor Heap에 대한 참조
GPU Handle: GPU Memory상에서의 GPU Descriptor Heap에 대한 참조

일단 확실히 해야하는 개념은 Descriptor Heap자체는 GPU메모리에만 있다. 이에 대한 CPU참조를 가능하게 해주는 CPU Handle, GPU참조를 가능하게 해주는 GPU Handle이 존재하는 것이다. 이때 CPU참조는 그래픽 드라이버를 관리하는 API(D3D12)의 함수 호출로 이루어지며, GPU참조는 그래픽카드 하드웨어 내부(Shader)에서 일어난다. 이제 헷갈리는 부분이 해결된다.

Descriptor를 Create할 때는 CPU단에서 API함수 호출로 이루어지므로 CPU Handle을 사용하고 Descriptor를 바인딩 할때는 CommandList에 기록된 정보를 GPU가 직접 사용하게 되므로 GPU Handle을 사용한다.

post-custom-banner

0개의 댓글