Skip to content

DescriptorHeap

Chuck Walbourn edited this page Apr 26, 2022 · 48 revisions
DirectXTK

In order to render with resources like textures, DirectX 12 uses a descriptor to provide the meta-data required. This helper provides a simple manager for the GPU memory heap of resource descriptors.

See also DescriptorPile

Related tutorial: Sprites and textures

Header

#include "DescriptorHeap.h"

Initialization

The client application maintains a list of identifiers for indices for each resource, such as:

enum Descriptors
{
    WindowsLogo,
    CourierFont,
    ControllerFont,
    GamerPic,
    Count
};

Then creates the heap to hold them:

resourceDescriptors = std::make_unique<DescriptorHeap>(device,
    D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
    D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
    Descriptors::Count);

Since this is the most common usage case (CBV/SRV/UAV heap with shader visibility), there's a simplified constructor version:

resourceDescriptors = std::make_unique<DescriptorHeap>(device, Descriptors::Count);

You can then use this to create shader resource view as a resource descriptor with the CPU handle:

D3D12_SHADER_RESOURCE_VIEW_DESC desc = {};

...

device->CreateShaderResourceView(tex, &desc,
    resourceDescriptors->GetCpuHandle(Descriptors::WindowsLogo));

Then to reference the SRV for rendering, you get the GPU handle using the same index identifier:

effect->SetTexture(resourceDescriptors->GetGpuHandle(Descriptors::WindowsLogo));

Strongly typed enums (a.k.a. enum class) are not recommended here as this requires adding a static_cast<int> when using the enum as a parameter to GetCpuHandle or GetGpuHandle.

Additional constructors

There are two alternative ways to construct a DescriptorHeap. The first takes ownership of an existing heap:

DescriptorHeap(ID3D12DescriptorHeap* pExistingHeap);

The second takes a D3D12_DESCRIPTOR_HEAP_DESC description:

DescriptorHeap(ID3D12Device* device, const D3D12_DESCRIPTOR_HEAP_DESC* pDesc);

Usage

There are four types of descriptor heaps in DirectX 12:

  • D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV - A heap of constant buffer views, shader resource reviews, and/or unordered-access views.
  • D3D12_DESCRIPTOR_HEAP_TYPE_RTV - A heap of render target views.
  • D3D12_DESCRIPTOR_HEAP_TYPE_DSV - A heap of depth/stencil views.
  • D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER - A heap of (non-static) samplers.

Shader Resource Views, Constant Buffer Views, and/or Unordered Access Views

To render, the descriptor heap must be made active on the current command list:

ID3D12DescriptorHeap* heaps[] = { resourceDescriptors->Heap() };
commandList->SetDescriptorHeaps(static_cast<UINT>(std::size(heaps)), heaps);

References to elements on the heap are made using the client identifiers:

// Returns a D3D12_GPU_DESCRIPTOR_HANDLE
resourceDescriptors->GetGpuHandle(Descriptors::WindowsLogo)

// Returns a D3D12_CPU_DESCRIPTOR_HANDLE
resourceDescriptors->GetCpuHandle(Descriptors::WindowsLogo)

To provide flexibility, setting the proper descriptor heaps to render with via SetDescriptorHeaps is left to the caller.

Render Target Views and Depth/Stencil Views

DescriptorHeap can also be used for creating Render Target View Descriptors or Depth/Stencil View Descriptors:

enum RTDescriptors
{
    SceneRT,
    Blur1RT,
    Blur2RT,
    RTCount
};

renderDescriptors = std::make_unique<DescriptorHeap>(device,
    D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
    D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
    RTDescriptors::RTCount);

device->CreateRenderTargetView(sceneTex.Get(),
    nullptr,
    renderDescriptors->GetCpuHandle(RTDescriptors::SceneRT));

depthDescriptors = std::make_unique<DescriptorHeap>(device,
    D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
    D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
    1);

device->CreateDepthStencilView(depthStencil.Get(),
    nullptr,
    depthDescriptors->GetCPUDescriptorHandleForHeapStart());

auto rtvDescriptor = renderDescriptors->GetCpuHandle(RTDescriptors::SceneRT);
auto dsvDescriptor = depthDescriptors->GetCPUDescriptorHandleForHeapStart();
commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor);

Samplers

Texture samplers can be statically defined within the DirectX 12 root signature, or provided on a descriptor heap. You can use DescriptorHeap to create your own sampler descriptor heap, but you can also make use of CommonStates which provides common combinations of sampler state. When used in conjunction with DescriptorHeap for the texture descriptors, the built-in effects expect drawing with the following heap settings:

ID3D12DescriptorHeap* heaps[] = { resourceDescriptors->Heap(), states->Heap() };
commandList->SetDescriptorHeaps(static_cast<UINT>(std::size(heaps)), heaps);

For a custom sampler heap:

samplerDescriptors = std::make_unique<DescriptorHeap>(device,
    D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
    D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
    32);

You can create as many heaps as you wish in your application, but remember that you can have only a single CBV/SRV/UAV descriptor heap and a single sampler descriptor heap active at a given time.

Copy descriptors

In addition to operations that utilize the CPU or GPU handle to a descriptor, the WriteDescriptors method can be used to write data directly into a descriptor or range of descriptors, typically copying from a existing set of descriptors.

D3D12_GPU_DESCRIPTOR_HANDLE WriteDescriptors(ID3D12Device* device,
    uint32_t offsetIntoHeap, uint32_t totalDescriptorCount,
    const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptorRangeStarts,
    const uint32_t* pDescriptorRangeSizes,
    uint32_t descriptorRangeCount);

D3D12_GPU_DESCRIPTOR_HANDLE WriteDescriptors(ID3D12Device* device,
    uint32_t offsetIntoHeap, const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptorRangeStarts,
    const uint32_t* pDescriptorRangeSizes, uint32_t descriptorRangeCount);

D3D12_GPU_DESCRIPTOR_HANDLE WriteDescriptors(ID3D12Device* device,
    uint32_t offsetIntoHeap,
    const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptors,
    uint32_t descriptorCount);

Properties

In addition to the methods above, a descriptor heap has the following properties:

  • GetFirstGpuHandle: Returns the first GPU handle in the heap
  • GetFirstCpuHandle: Returns the first CPU handle in the heap
  • GetGpuHandle: Returns a GPU handle given an index.
  • GetCpuHandle: Returns a CPU handle given an index.
  • Count: Returns the capacity of the heap.
  • Flags: Returns the flags used to create the heap.
  • Type: Returns the heap type.
  • Increment: Returns the increment needed for computing handles.
  • Heap: Returns a pointer to the underlying DirectX 12 heap object.

Threading model

Creation of resources is fully asynchronous, so you can create many heaps at the same time.

Further reading

Resource Binding in Direct3D 12
Descriptor Heaps

For Use

  • Universal Windows Platform apps
  • Windows desktop apps
  • Windows 11
  • Windows 10
  • Xbox One
  • Xbox Series X|S

Architecture

  • x86
  • x64
  • ARM64

For Development

  • Visual Studio 2022
  • Visual Studio 2019 (16.11)
  • clang/LLVM v12 - v18
  • MinGW 12.2, 13.2
  • CMake 3.20

Related Projects

DirectX Tool Kit for DirectX 11

DirectXMesh

DirectXTex

DirectXMath

Tools

Test Suite

Model Viewer

Content Exporter

DxCapsViewer

See also

DirectX Landing Page

Clone this wiki locally