Descriptor heaps

In DirectX 12, most runtime resources are accompanied by descriptors. The resources denote the memory regions in use and some rough data about the data contained within these memory regions, whereas a descriptor contains data describing how the data is to be interpreted.

We will need to create descriptor views for every type of object we intend to use. The descriptor heap types are:

Name Non shader visible Shader visible
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER
D3D12_DESCRIPTOR_HEAP_TYPE_RTV
D3D12_DESCRIPTOR_HEAP_TYPE_DSV

Aside from the descriptor heap type, a descriptor heap can also be shader visible or not. Both states will allow a CPU to edit and read its contents, but only a shader-visible descriptor heap will allow a shaders to index the descriptors. Non shader visible heaps can be used in order to stage descriptors for use in the shader visible descriptor heap, allowing for views to be made while a shader-visible descriptor heap is still in use by a command queue, after which they can be copied over using ID3D12Device::CopyDescriptors or ID3D12Device::CopyDescriptorsSimple.

In order to allow for safe creation and deletion of descriptors, it is recommended to have a single descriptor heap of each type marked as non shader visible, and have a shader visible descriptor heap of each type per frame buffer. This allows you to mark a resource for deletion in your engine, then replace the descriptors on the shader visible descriptor heap to a NULL descriptor one-by-one, and finally delete the resource when all references are gone, leaving space in the spot where the resource once was if another resource is added in the meantime.

Note that only a single shader visible descriptor heap of each type can be bound at the same time.

// CPU (Staging)
for ( uint32_t i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i++ )
{
    result = renderer->device->lpVtbl->CreateDescriptorHeap (
        renderer->device,
        &(D3D12_DESCRIPTOR_HEAP_DESC){
            .Type           = i,
            .NumDescriptors = __TEMP_DESCRIPTOR_COUNT,	// TODO(Rick): Make this (more) dynamic!
            .Flags          = 0,
        },
        &IID_ID3D12DescriptorHeap,
        &renderer->stagingDescriptors.heaps[i]
    );
    if ( !SUCCEEDED(result) )
        RETURN_ERROR(-1, "CreateDescriptorHeap failed (0x%08X)", result );
}

// GPU (only CBV_SRV_UAV & SAMPLER)
for ( uint32_t frameBufferIdx = 0; frameBufferIdx < FRAME_BUFFER_COUNT; frameBufferIdx++ )
{
    for ( uint32_t i = 0; i <= D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; i++ )
    {
        result = renderer->device->lpVtbl->CreateDescriptorHeap (
            renderer->device,
            &(D3D12_DESCRIPTOR_HEAP_DESC){
                .Type           = i,
                .NumDescriptors = __TEMP_DESCRIPTOR_COUNT,	// TODO(Rick): Make this (more) dynamic!
                .Flags          = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
            },
            &IID_ID3D12DescriptorHeap,
            &renderer->frame[frameBufferIdx].gpuDescriptors.heaps[i]
        );
        if ( !SUCCEEDED(result) )
            RETURN_ERROR(-1, "CreateDescriptorHeap failed (0x%08X)", result );
    }
}

//TODO(Rick): Finish this chapter