Question
Differences between Microsoft and Nvidia examples of DirectX12 CPU/GPU synchronization
I'm trying to understand the CPU/GPU synchronization in DirectX 12, but there are some things that confuse me. Here is the sample code from Microsoft's HelloFrameBuffering
example:
// Prepare to render the next frame.
void D3D12HelloFrameBuffering::MoveToNextFrame()
{
// Schedule a Signal command in the queue.
const UINT64 currentFenceValue = m_fenceValues[m_frameIndex];
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue));
// Update the frame index.
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
// If the next frame is not ready to be rendered yet, wait until it is ready.
if (m_fence->GetCompletedValue() < m_fenceValues[m_frameIndex])
{
ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent));
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
}
// Set the fence value for the next frame.
m_fenceValues[m_frameIndex] = currentFenceValue + 1;
}
My question is, why do we update the m_frameIndex
before checking if the fence has reached the expected fence value? This means we use the fence value of a different framebuffer, which is not the same value we used in the Signal()
call. This seems a bit strange to me.
I also check out Nvidia's sample code:
struct FrameContext
{
ComPtr<ID3D12CommandAllocator> m_allocator;
ComPtr<ID3D12CommandAllocator> m_computeAllocator;
ComPtr<ID3D12Fence> m_fence;
uint64_t m_fenceValue = 0;
};
void DeviceResources::MoveToNextFrame()
{
FrameContext* ctx = &m_frameContext[m_frameIndex];
DX::ThrowIfFailed(m_commandQueue->Signal(ctx->m_fence.Get(), ctx->m_fenceValue));
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
if (ctx->m_fence->GetCompletedValue() < ctx->m_fenceValue)
{
DX::ThrowIfFailed(ctx->m_fence->SetEventOnCompletion(ctx->m_fenceValue, m_fenceEvent.Get()));
WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, false);
}
ctx->m_fenceValue++;
}
As we can see, they use the same fence value for the Signal()
call and for comparision with GetCompletedValue()
. Could someone help me understand the pros and cons of these two approaches?