The HelloWorld examples from Microsoft mostly use a single CommandAllocator and then wait until the previous frame is fully done. However they also say (in all caps) that it's not how it should be done.
So my idea is to create an Allocator per frame in the swapchain and keep which fence value to wait on in a circular buffer:
struct frame_resources{
ID3D12Resource* renderTarget;
ID3D12CommandAllocator* allocator;
uint64 fenceValue;
} resources[FRAME_COUNT];
uint frameIndex = swapChain->GetCurrentBackBufferIndex();
UINT64 lastFence;
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
if (fence->GetCompletedValue() < resources[frameIndex].fenceValue)
{
fence->SetEventOnCompletion(resources[frameIndex].fenceValue, fenceEvent);
DWORD result = MsgWaitForMultipleObjects(1, &fenceEvent, FALSE, INFINITE, QS_ALLEVENTS);
if(result == WAIT_OBJECT_0 + 1)
continue; //message in the queue
}
resources[frameIndex].allocator->Reset();
commandList->Reset(resources[frameIndex].allocator, 0);
//...
commandList->Close();
commandQueue->ExecuteCommandLists(1, &CommandList);
lastFence++;
resources[frameIndex].fenceValue = lastFence;
commandQueue->Signal(fence, lastFence);
swapChain->Present(0, DXGI_PRESENT_RESTART);
frameIndex = swapChain->GetCurrentBackBufferIndex();
}
Is this a sane approach? Or is there a better way?