Do we really need a fence event? One of the hardest things for me with the modern closer to the metal graphics APIs is understanding synchronization. I usually try to simplify things in order to understand the concept better.

In Directx12, we can use a set of "x" fences (x being the number of back buffers) to allow the CPU to work ahead of the GPU. This could be the code we use to wait for a previous fence (https://www.braynzarsoft.net):

if (fence[frameIndex]-&gt;GetCompletedValue() &lt; fenceValue[frameIndex])
{
    hr = fence[frameIndex]-&gt;SetEventOnCompletion(fenceValue[frameIndex], fenceEvent);
    if (FAILED(hr))
    {
        Running = false;
    }
    WaitForSingleObject(fenceEvent, INFINITE);
} 


Is there a reason why we can´t just:

while(fence[frameIndex]-&gt;GetCompletedValue() &lt; fenceValue[frameIndex])
{
}


Thanks!
In your first example, the call to WaitForSingleObject blocks the thread. This puts it into a waiting state, which takes it off the run queue and allows other threads to run on this CPU core. If no threads can run, the CPU will idle. When the fenceEvent completes, it wakes up this thread.

Your proposed replacement is called a spinlock. The thread keeps running, using 100% of a core until the fence's completed flag changes. No other code can run on this core, so there's no interleaving of CPU and GPU work. Worse yet, this code has to read from memory to check the flag every time around the loop, so it'll slow down any threads on other cores that are also accessing memory. Finally, because it's keeping the core and the memory bus active, it'll burn more power and generate more heat.

This is why fence events are needed: to enable the CPU to wait efficiently.