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]->GetCompletedValue() < fenceValue[frameIndex])
    hr = fence[frameIndex]->SetEventOnCompletion(fenceValue[frameIndex], fenceEvent);
    if (FAILED(hr))
        Running = false;
    WaitForSingleObject(fenceEvent, INFINITE);

Is there a reason why we can´t just:

while(fence[frameIndex]->GetCompletedValue() < fenceValue[frameIndex])



1 Answer 1


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.

  • $\begingroup$ Ok, that makes sense, I guess that if you know the amount of time its going to be idling you could also do some work there :) $\endgroup$
    – Nacho
    Feb 5, 2018 at 10:26
  • $\begingroup$ @Nacho you can also put the fenceEvent in a list so you can use WaitForMulitpleObjects and also wait on (for example) Async File IO to complete on the same thread. $\endgroup$ Feb 5, 2018 at 10:30
  • $\begingroup$ @Nacho The idea is that you shouldn't block on the fence until you have to, because the next thing you're going to do on the CPU depends on prior GPU commands having finished. For example, you might have a thread that composites frames generated from another thread, and the producer thread sends a fence along with other per-frame data, to keep the data and the frames in sync. $\endgroup$
    – Dan Hulme
    Feb 5, 2018 at 19:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.