4

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?

ratchet freak
  • 47,288
  • 5
  • 68
  • 106
  • it is a sane approach, and i would say the recommended way, but you need to include the command list too, as you cannot call Reset until the command list is fully consumed. I create usually backbufferCount + N with N in[1..4] allocators and at least one main command list per allocator. The N is to allow the driver queuing frames if want too to absorb gpu bubbles. – galop1n Jan 25 '16 at 23:52
  • @galop1n that's a misconception actually: [Unlike ID3D12CommandAllocator::Reset, you can call \[ID3D12GraphicsCommandList::\]Reset while the command list is still being executed.](https://msdn.microsoft.com/en-us/library/windows/desktop/dn903895%28v=vs.85%29.aspx) – ratchet freak Jan 25 '16 at 23:58

1 Answers1

3

You've probably already found the answer by now, but I'll answer just in case. You cannot reset a command allocator while the GPU may be executing a command list stored in the memory associated with the command allocator, so your approach is correct, you will need to have a separate command allocator per frame buffer.

Command lists on the other hand can be reset immediately after calling ExecuteCommandLists(). This means that while you must have a command allocator per frame, you only need one command list per "thread".

Command lists can only be filled by one thread at a time, which means you need to have one command list per thread which fills out a command list.

So, you need to have numFrames*numThreads command allocators, and numThreads command lists

iedoc
  • 2,266
  • 3
  • 30
  • 53