I thought this was a race too, but from my testing I'm pretty sure the documentation is just not well specified.
Specifies an event that should be fired when the fence reaches a certain value.
from: https://msdn.microsoft.com/en-us/library/windows/desktop/dn899190(v=vs.85).aspx
The word reaches there actually means the fence value is greater than or equal to the "certain value".
And it seems the event is set immediately in the case where the fence value >= certain value before SetEventOnCompletion is called.
This is relatively easy to test out by modifying D3D12HelloTriangle.cpp.
Modify the CreateFence call to set the initial value to 100.
Then run some tests by hacking up WaitForPreviousFrame.
void D3D12HelloTriangle::WaitForPreviousFrame()
{
ThrowIfFailed(m_fence->SetEventOnCompletion(75, m_fenceEvent));
WaitForSingleObject(m_fenceEvent, INFINITE);
/*** ALWAYS REACHES HERE ***/
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}
void D3D12HelloTriangle::WaitForPreviousFrame()
{
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), 50));
ThrowIfFailed(m_fence->SetEventOnCompletion(75, m_fenceEvent));
WaitForSingleObject(m_fenceEvent, INFINITE);
/*** NEVER REACHES HERE, although presumably could if you fill up the queue enough ***/
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}
void D3D12HelloTriangle::WaitForPreviousFrame()
{
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), 75));
ThrowIfFailed(m_fence->SetEventOnCompletion(50, m_fenceEvent));
WaitForSingleObject(m_fenceEvent, INFINITE);
/*** ALWAYS REACHES HERE ***/
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}
void D3D12HelloTriangle::WaitForPreviousFrame()
{
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), 200));
ThrowIfFailed(m_fence->SetEventOnCompletion(150, m_fenceEvent));
WaitForSingleObject(m_fenceEvent, INFINITE);
/*** ALWAYS REACHES HERE ***/
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}
void D3D12HelloTriangle::WaitForPreviousFrame()
{
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), 99));
ThrowIfFailed(m_fence->SetEventOnCompletion(100, m_fenceEvent));
WaitForSingleObject(m_fenceEvent, INFINITE);
/*** NEVER REACHES HERE, although presumably could if you fill up the queue enough ***/
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}
Since the sample only ever increments the fence the greater than or equal behavior protects against a race.