Swap chain

Now that we have a device, now we just need a method for the device to be able to communicate with the window in which we intend to render the outputted content. The IDXGISwapchain object is the interface used by both DirectX 11 and DirectX 12 for outputting image content to a screen.

The swapchain is the first object requiring a significant number of configuration properties in order to function. These will be described after the source code sample.

IDXGISwapChain1* swapChain;
result = renderer->dxgiFactory->lpVtbl->CreateSwapChainForHwnd(
    renderer->dxgiFactory,
    (IUnknown*)renderer->commandQueue,
    window,
    &(DXGI_SWAP_CHAIN_DESC1){
        .Width  = 0,
        .Height = 0,
        .Format = DXGI_FORMAT_R8G8B8A8_UNORM,
        .Stereo = FALSE,
        .SampleDesc = {
            .Count = 1,
        },
        .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
        .BufferCount = FRAME_BUFFER_COUNT,
        .Scaling     = DXGI_SCALING_STRETCH,
        .SwapEffect  = DXGI_SWAP_EFFECT_FLIP_DISCARD,
        .AlphaMode   = DXGI_ALPHA_MODE_UNSPECIFIED,
        .Flags       = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,
    },
    NULL,
    NULL,
    &swapChain
);

if ( !SUCCEEDED ( result ) )
    return -3;

result = swapChain->lpVtbl->QueryInterface (
    swapChain,
    &IID_IDXGISwapChain3,
    &renderer->dxgiSwapChain
);

if ( !SUCCEEDED ( result ) )
    return -4;

The function CreateSwapChainForHwnd requires a fair bunch of properties, a list of which now follows. The most important things to note about the function arguments itself are fairly clearly described in the documentation of this function over at MSDN.

The more complicated and interesting properties are part of the DXGI_SWAP_CHAIN_DESC1 structure. This structure describes how

It should lastly be noted that for legacy reasons CreateSwapChainForHwnd returns an IDXGISwapChain1 object, whereas we desire an IDXGISwapChain3 object. As per usual, we resolve this with a call to QueryInterface.

With the swapchain created, we now need to make sure we can use the buffers in rendering. For this, we first need to know the buffer images themselves, and then we insert the buffer images as Render Target Views into the Render Target View descriptor heap we created earlier:

//TODO(Rick): The function calling signature is broken in the C interface of Direct3D 12, so we cast the function to the proper function signature
D3D12_CPU_DESCRIPTOR_HANDLE cpuDesc;
((C_D3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart)renderer->stagingDescriptors.rtv->lpVtbl->GetCPUDescriptorHandleForHeapStart) ( renderer->stagingDescriptors.rtv, &cpuDesc );
UINT increment = renderer->device->lpVtbl->GetDescriptorHandleIncrementSize ( renderer->device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV );
for ( uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++ )
{
    result = renderer->dxgiSwapChain->lpVtbl->GetBuffer (
        renderer->dxgiSwapChain,
        i,
        &IID_ID3D12Resource,
        &renderer->backbuffer[i].res
    );
    if ( !SUCCEEDED ( result ) )
        return -1;

    renderer->backbuffer[i].desc = cpuDesc;
    renderer->device->lpVtbl->CreateRenderTargetView (
        renderer->device,
        renderer->backbuffer[i].res,
        NULL,
        cpuDesc
    );

    cpuDesc.ptr += increment;
}

In here, we are retrieving every single buffer out of the swap chain, and making a descriptor to the resource in the RTV descriptor heap. We initially take the first descriptor, and keep incrementing the descriptor by the size of each individual descriptor. Then each of these descriptors are stored along with their respective resource.