Descriptor
는 D3D12에서 바인딩의 기본 단위이며 오브젝트를 GPU에 완전히 설명해주는 작은 데이터 블록이다.
Direct3D12 이전에는 View
라는 이름으로 불렸으며 Direct3D12에서도 여전히 View로 불리고 사용되는 녀석들도 여전히 남아있다.
렌더링 파이프라인에 어떤 데이터를 묶기 위해선 Descriptor의 형태로 데이터를 저장해서 해당 Descriptor를 PipelineState에 넣어야 한다.
예를 들어 하나의 텍스쳐를 그려내고 싶다면 Shader Resource View라는 Descriptor를 만들어서 해당 텍스쳐의 가상 메모리 주소와 타입 등 필요한 정보를 Descriptor에 저장하고 해당 Descriptor를 PipelineState에 적용해 그리기 요청을 하는 방식이다.
이 Descriptor는 Constant Buffer View(CBV)
Shader Resource Views(SRV)
, Unodered Access Views(UAV)
등 다양한 타입의 Descriptor가 있으며 각 사이즈는 하드웨어, 드라이버 그리고 오브젝트의 타입에 따라 다르며 현재 알려진 하드웨어에 한해서는 64Byte이하의 사이즈를 갖고 있으며 ID3D12Device::GetDescriptorHandleIncrementSize
메소드를 통해서 사이즈를 얻어올 수 있다.
Descriptor 객체는 Free나 Release
가 필요 없지만 Descriptor는 실제 데이터의 가상주소를 포함하고 있어 nullptr에 대한 접근의 위험이 있다.
예를들어 SRV Descriptor같은 경우에는 Resource(버퍼)의 가상의 GPU 주소를 포함하고 있어야 한다고 했는데 만약 Resource가 파괴되거나 액세스 할 수 없는경우에는 문제가 발생할 수 있기 때문에 해당 Descriptor를 사용하지 않도록 해야하는 것은 개발자의 책임이다.
이런 Descriptor를 사용하는 한 가지 방법은 Descriptor를 위한 백업 메모리인 Descriptor Heap
을 사용하는 것이다. 이 경우에는 Descriptor Table
을 정의하여 사용하는것이 좋으며 이 Descriptor Table은 파이프라인의 범위를 식별하기 때문에 Draw/Dipatch할 때 사용할 Descriptor를 찾을 수 있게 만들어 준다.
이렇게 만들어진 Descriptor Table은 Root Signature
의 인자로 사용된다.
여기서 RootSignature는 작은 수의 Descriptor는 별도로 관리하지 않고 직접 소유
할 수 있다.
위 사진은 RootSignature내부의 예로 CBV를 소유하고 있다.
Descriptor Table
은 간단히 말하면 Descriptor의 배열
이며 각 하나 이상의 타입(CBV, SRV, UAV)을 가진 Descriptor를 저장한다.
그래픽 및 파이프라인은 인덱스
를 통해서 Descriptor Table을 참조해서 리소스에 대해 접근한다.
그래픽 파이프라인이 항상 전체 힙을 관리하도록 하는게 아니라 Descriptor Tables을 사용하도록 하는 주된 이유는 다음과 같다.
쉐이더를 전환하는데 가장 저렴한 방법
이며 셰이더는 큰 힙 공간에서 리소스를 어떻게 찾아야하는지 알 필요가 없다.Descriptor Heap
의 주요 기능은 렌더링 중 쉐이더가 참조하는 Type의 Descriptor를 저장하는 메모리 공간이다.
Descriptor Heap은 얼마든지 크게 만들 수 있으며
하드웨어의 사양에 따라 제한될 수 있다.
Shader가 참조할 수 있는 Descriptor Heap은 다음과 같다.
하나는 D3D12_SRV_UAV_CBV_DESCRIPTOR_HEAP
이며 말 그대로 Shader Resource View, Unordered Access View, Constant Buffer View를 모두 보유할 수 있다.
다른 하나는 D3D12_SAMPLER_DESCRIPTOR_HEAP
이며 오직 Sampler만 저장이 가능하다.
이 타입은 힙이 생성될 때 셰이더가 보이거나 보이지 않도록 요청할 수 있다. 만약 보이도록 요청한다면 힙 할당에 대한 하드웨어 크기 제한이 있을 수 있다.
그리고 애플리케이션은 여러개의 힙을 만들 수 있는데 하나의 큰 힙 안에 여러개의 하위 힙들을 만들 수 있다. 이렇게 가능하게 한 이유는 어떤 하드웨어에서는 힙을 전환하기 위해선 GPU가 유휴상태에 들어가야 하기 때문이며 (이전 힙에대해 GPU가 참조를 모두 완료해야한다고 보장해야 함) 따라서 애플리케이션은 GPU의 유휴상태에 대한 가능성을 열어둬야한다.
참고