Synchronization

One of the biggest changes of Direct3D 12 when compared to Direct3D 11 is the explicit management of syncrhonization. Direct3D 11 attempted to hide this as much as possible; Resources could be updated at any time, and the runtime would make sure the proper data was accessible by the hardware at the intended moment in time.

This is no longer the case in Direct3D 12. The user is now responsible for updating resources in such a way that no data that is still required is overwritten, and no resources still in use are deleted. Data from the previous CPU frame may still be in use by the GPU, and the user should be mindful not to overwrite that data, as unexpected issues may occur in so doing.

A solution to this is to create a buffer or heap that is large enough to contain N frames worth of data. When filling the data, rather than overwriting the same location, the position would change every write. This was normally already done dynamically by the Direct3D 11 runtime, but as part of improving performance and explicit management possibilities, this responsibility was relegated to the user for Direct3D 12.

In addition to synchronizing the resource updates, synchronization also needs to be done between command queues and between a command queue and the CPU. For this purpose, the concept of a fence was added. A fence is an object with a counter that can be waited upon on both the CPU and the GPU. This allows the CPU to wait for the GPU, the GPU for the CPU or the GPU for another GPU queue.

CPU-bound case; Synchronization not technically necessary

GPU-bound case; Failure to wait for command buffer completion can cause serious issues

The case above demonstrates a sample situation where synchronization is necessary: The CPU submits command buffers 1 − 3 to the GPU, and the GPU executes them directly upon receiving them. But the GPU takes longer to execute a command buffer than it takes for the CPU to submit a command buffer. After a number of submissions, the CPU has no command buffers to submit to, and has to wait. Resetting command buffer #3 in this scenario will trigger the debug layer to issue an error in debug scenario's. Without the debug layers present, this can lead to undefined results.