In order to create a fence, the
ID3D12Device::CreateFence function is to be called. This function call includes an initial value and a flag field, which can specify whether or not the fence is to be shared between multiple GPU adapters.
In order to wait for the fence on the CPU, a Win32 event handle created by
CreateEvent is required, after which the
ID3D12Fence::SetEventOnCompletion function can be called to set the event when the fence reaches a specific value. After that,
WaitForMultipleObjects can be used to wait for fence completion.
In order to wait on the fence on the GPU, the
ID3D12CommandQueue::Wait method can be invoked to push a wait command to the command queue. The command queue will keep executing commands in order as they are submitted to it, and upon reaching the
Wait command will hold executing other commands until the fence reaches the value specified in the
The fence can be signalled (set to a specific value) by either the CPU or the GPU. On the CPU, use
ID3D12Fence::Signal to immediately set the fence value to the value specified. On the GPU, use
ID3D12CommandQueue::Signal to push a command to set the current fence value at that point in the command queue.
For the current example, the only thing we use the fence for is in order to queue multiple frames of commands and render targets at the same time, and making sure we are not using the same command buffer and render target while they are still in use by a previous submitted frame. In this scenario, the GPU signals, and the CPU waits. As such, we are creating both the fence and an event.
result = renderer->device->lpVtbl->CreateFence( renderer->device, 0xFFFFFFFFFFFFFFFF, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void**)&renderer->frameFence ); if ( !SUCCEEDED ( result ) ) RETURN_ERROR(-1, "CreateFence failed (0x%08X)", result ); renderer->eofEvent = CreateEventEx ( NULL, FALSE, FALSE, EVENT_ALL_ACCESS ); if ( renderer->eofEvent == NULL ) RETURN_ERROR(-1, "CreateEventEx failed (0x%08X)", GetLastError() );