I am writing a D3D12 application where I have two command queues. One for rendering and another for data copying.As I can’t post here all the code here is the exec flow with some of the actual code in the important areas:
The app consists of two stages: 1) Render to backbuffer which has two RTVs (double buffering). 2) Copy the last presented buffer to another D3D texture resource.
I must use separate queues. That’s mandatory.
cmdQueue1 – rendering queue created as D3D12_COMMAND_LIST_TYPE_DIRECT
cmdQueue2 – copy queue created as D3D12_COMMAND_LIST_TYPE_COPY
.
-
Submit cmdQueue1 for rendering.
-
Present and sync:
<code>ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);HRESULT res = m_swapChain->Present(1, 0);assert(SUCCEEDED(res));//make sure rendering has finished before flipping:const UINT64 currentFenceValue = m_fenceValues[m_frameIndex];res = (m_commandQueue->Signal(m_fence.Get(), currentFenceValue));assert(SUCCEEDED(res));m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();if (m_fence->GetCompletedValue() < m_fenceValues[m_frameIndex]){res = m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent);assert(SUCCEEDED(res));WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);}m_fenceValues[m_frameIndex] = currentFenceValue + 1;</code><code>ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() }; m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); HRESULT res = m_swapChain->Present(1, 0); assert(SUCCEEDED(res)); //make sure rendering has finished before flipping: const UINT64 currentFenceValue = m_fenceValues[m_frameIndex]; res = (m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); assert(SUCCEEDED(res)); m_frameIndex = m_swapChain->GetCurrentBackBufferIndex(); if (m_fence->GetCompletedValue() < m_fenceValues[m_frameIndex]) { res = m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent); assert(SUCCEEDED(res)); WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE); } m_fenceValues[m_frameIndex] = currentFenceValue + 1; </code>ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() }; m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); HRESULT res = m_swapChain->Present(1, 0); assert(SUCCEEDED(res)); //make sure rendering has finished before flipping: const UINT64 currentFenceValue = m_fenceValues[m_frameIndex]; res = (m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); assert(SUCCEEDED(res)); m_frameIndex = m_swapChain->GetCurrentBackBufferIndex(); if (m_fence->GetCompletedValue() < m_fenceValues[m_frameIndex]) { res = m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent); assert(SUCCEEDED(res)); WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE); } m_fenceValues[m_frameIndex] = currentFenceValue + 1;
-
Now comes the copy part which uses cmdQueue2:
<code>ID3D12CommandList* ppCommandLists[] = { m_dataCopyCommandList };m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);m_commandQueue->Signal(m_pCopyFence, m_nCopyFenceVal);if (pFence->GetCompletedValue() < nFenceValue){pFence->SetEventOnCompletion(nFenceValue, m_event);WaitForSingleObject(m_event, INFINITE);}m_nCopyFenceVal++;</code><code>ID3D12CommandList* ppCommandLists[] = { m_dataCopyCommandList }; m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); m_commandQueue->Signal(m_pCopyFence, m_nCopyFenceVal); if (pFence->GetCompletedValue() < nFenceValue) { pFence->SetEventOnCompletion(nFenceValue, m_event); WaitForSingleObject(m_event, INFINITE); } m_nCopyFenceVal++; </code>ID3D12CommandList* ppCommandLists[] = { m_dataCopyCommandList }; m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); m_commandQueue->Signal(m_pCopyFence, m_nCopyFenceVal); if (pFence->GetCompletedValue() < nFenceValue) { pFence->SetEventOnCompletion(nFenceValue, m_event); WaitForSingleObject(m_event, INFINITE); } m_nCopyFenceVal++;
Each queue is synchronized with its own event object and fence.
The application works fine. Also the copy of the backbuffer to an auxiliary texture resource seems to be ok.But the output window spits from time to time the following error:
D3D12 ERROR: ID3D12CommandQueue::ExecuteCommandLists:
Non-simultaneous-access Texture Resource (0x00000212AC79B090:’Unnamed
Object’) is still referenced by write|transition_barrier|assert_read
GPU operations in-flight on another Command Queue
(0x00000212A6381C90:’Unnamed ID3D12CommandQueue Object’). It is not
safe to start read|transition_barrier GPU operations now on this
Command Queue (0x00000212A609AC20:’Unnamed ID3D12CommandQueue
Object’). This can result in race conditions and application
instability. [ EXECUTION ERROR #1047:
OBJECT_ACCESSED_WHILE_STILL_IN_USE]
I am not sure I understand why there can be an issue as I make sure the first queue is done executing before moving to the next one. Also, all this logic currently runs on the same thread. Is it safe to ignore the above warning or I am missing something in my code?