[idd] driver: fix deadlock caused by command queue completion callback

The callback runs in a random thread, we can't call directx methods
safely from it, so move reset so it's called automatically when a free
copy list is obtained.
This commit is contained in:
Geoffrey McRae 2025-03-30 16:52:58 +00:00
parent 3b883bf9fe
commit cadcfe4b39
5 changed files with 69 additions and 43 deletions

View File

@ -1,7 +1,7 @@
#include "CD3D12CommandQueue.h"
#include "CDebug.h"
bool CD3D12CommandQueue::Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE type, const WCHAR* name)
bool CD3D12CommandQueue::Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE type, const WCHAR* name, CallbackMode callbackMode)
{
HRESULT hr;
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
@ -54,6 +54,11 @@ bool CD3D12CommandQueue::Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE ty
return false;
}
if (callbackMode != DISABLED)
{
ULONG flags = (callbackMode == FAST) ?
WT_EXECUTEINWAITTHREAD : WT_EXECUTEINPERSISTENTTHREAD;
RegisterWaitForSingleObject(
&m_waitHandle,
m_event.Get(),
@ -63,7 +68,8 @@ bool CD3D12CommandQueue::Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE ty
},
this,
INFINITE,
WT_EXECUTEINPERSISTENTTHREAD);
flags);
}
m_name = name;
m_fenceValue = 0;
@ -92,15 +98,18 @@ bool CD3D12CommandQueue::Execute()
ID3D12CommandList * lists[] = { m_cmdList.Get() };
m_queue->ExecuteCommandLists(1, lists);
m_queue->Signal(m_fence.Get(), ++m_fenceValue);
m_pending = true;
m_needsReset = true;
++m_fenceValue;
m_fence->SetEventOnCompletion(m_fenceValue, m_event.Get());
m_queue->Signal(m_fence.Get(), m_fenceValue);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to set the fence signal (%ls)", m_name);
return false;
}
m_fence->SetEventOnCompletion(m_fenceValue, m_event.Get());
m_pending = true;
return true;
}
@ -121,6 +130,9 @@ void CD3D12CommandQueue::Wait()
bool CD3D12CommandQueue::Reset()
{
if (!m_needsReset)
return true;
HRESULT hr;
hr = m_allocator->Reset();
@ -137,6 +149,7 @@ bool CD3D12CommandQueue::Reset()
return false;
}
m_needsReset = false;
return true;
}

View File

@ -4,7 +4,6 @@
#include <wdf.h>
#include <wrl.h>
#include <d3d12.h>
#include <functional>
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
@ -25,27 +24,35 @@ class CD3D12CommandQueue
HandleT<HANDLENullTraits> m_event;
HANDLE m_waitHandle = INVALID_HANDLE_VALUE;
UINT64 m_fenceValue = 0;
bool m_needsReset = false;
typedef std::function<void(CD3D12CommandQueue * queue, void * param1, void * param2)> CompletionFunction;
typedef void (*CompletionFunction)(CD3D12CommandQueue * queue, void * param1, void * param2);
CompletionFunction m_completionCallback = nullptr;
void * m_completionParams[2];
bool Reset();
void OnCompletion()
{
Reset();
m_pending = false;
if (m_completionCallback)
m_completionCallback(
this,
m_completionParams[0],
m_completionParams[1]);
m_pending = false;
}
public:
~CD3D12CommandQueue() { DeInit(); }
bool Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE type, const WCHAR * name);
enum CallbackMode
{
DISABLED, // no callbacks
FAST, // callback is expected to return almost immediately
NORMAL // normal callback
};
bool Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE type, const WCHAR * name,
CallbackMode callbackMode = DISABLED);
void DeInit();
void SetCompletionCallback(CompletionFunction fn, void * param1, void * param2)
@ -55,6 +62,7 @@ class CD3D12CommandQueue
m_completionParams[1] = param2;
}
bool Reset();
bool Execute();
//void Wait();

View File

@ -108,7 +108,8 @@ CD3D12Device::InitResult CD3D12Device::Init(CIVSHMEM &ivshmem, UINT64 &alignSize
}
for(int i = 0; i < ARRAYSIZE(m_copyQueue); ++i)
if (!m_copyQueue[i].Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COPY, L"Copy"))
if (!m_copyQueue[i].Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COPY, L"Copy",
m_indirectCopy ? CD3D12CommandQueue::NORMAL : CD3D12CommandQueue::FAST))
return InitResult::FAILURE;
//if (!m_computeQueue.Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COMPUTE, L"Compute"))
@ -176,7 +177,10 @@ CD3D12CommandQueue * CD3D12Device::GetCopyQueue()
for (int i = 0; i < ARRAYSIZE(m_copyQueue); ++i)
{
auto& queue = m_copyQueue[i];
if (queue.IsReady())
if (!queue.IsReady())
continue;
queue.Reset();
return &queue;
}
Sleep(1);

View File

@ -145,6 +145,21 @@ void CSwapChainProcessor::SwapChainThreadCore()
}
}
void CSwapChainProcessor::CompletionFunction(CD3D12CommandQueue * queue, void * param1, void * param2)
{
UNREFERENCED_PARAMETER(queue);
auto sc = (CSwapChainProcessor *)param1;
auto fbRes = (CFrameBufferResource*)param2;
if (sc->m_dx12Device->IsIndirectCopy())
sc->m_devContext->WriteFrameBuffer(
fbRes->GetFrameIndex(),
fbRes->GetMap(), 0, fbRes->GetFrameSize(), true);
else
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
}
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer)
{
ComPtr<ID3D11Texture2D> texture;
@ -212,23 +227,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
return false;
}
copyQueue->SetCompletionCallback(
[](CD3D12CommandQueue * copyQueue, void * param1, void * param2)
{
UNREFERENCED_PARAMETER(copyQueue);
auto sc = (CSwapChainProcessor *)param1;
auto fbRes = (CFrameBufferResource *)param2;
if (sc->m_dx12Device->IsIndirectCopy())
sc->m_devContext->WriteFrameBuffer(
fbRes->GetFrameIndex(),
fbRes->GetMap(), 0, fbRes->GetFrameSize(), true);
else
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
},
this,
fbRes);
copyQueue->SetCompletionCallback(&CompletionFunction, this, fbRes);
srcRes->Sync(*copyQueue);
copyQueue->GetGfxList()->CopyTextureRegion(

View File

@ -54,6 +54,8 @@ private:
void SwapChainThread();
void SwapChainThreadCore();
static void CompletionFunction(CD3D12CommandQueue * queue, void * param1, void * param2);
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer);
public: