Hey guys,
I'm trying to implement a double buffered multithreading in my renderer but have run into an issue with the synchronization.
Here's the function on the [main thread] that generates the command list:
void FERenderer::Render(ISwapChain *swapChain)
{
// Set current swapChain to render to
this->currentChain = swapChain;
this->renderer->SetCurrentSwapChain(swapChain);
// Create [RenderThread] here (will wait until [MainThread] is done generating command list)
#ifdef MULTITHREADED_RENDERER_ENABLED
if (!this->renderThreadCreated && !this->contentManager->IsCompilingShader())
{
this->renderThread = std::thread(&IBackEndRenderer::ExecuteCommandList, this->renderer,
this->commandList, std::ref(renderThreadID));
this->renderThreadCreated = true;
this->renderThread.detach();
}
this->CreateCommandList();
#else
this->CreateCommandList();
if(!this->contentManager->IsCompilingShader())
{
renderThreadID = 1; // workaround
this->renderer->ExecuteCommandList(this->commandList,
std::ref(renderThreadID));
renderThreadID = 0;
}
#endif
this->profiler->FrameComplete();
#ifdef MULTITHREADED_RENDERER_ENABLED
this->renderer->RenderDoneCondition().Wait();
this->renderer->RenderCommandsReadyCondition().Set();
this->renderer->BufferSwappedCondition().Wait(); // <- If removed, no deadlock !
#endif
}
And this is the function on the [render thread] that executes the command list:
void LipsRenderD3D11::ExecuteCommandList(std::vector<unsigned char> *cmdList, bool &renderThreadID)
{
#ifdef MULTITHREADED_RENDERER_ENABLED
while (true)
#endif
{
#ifdef MULTITHREADED_RENDERER_ENABLED
// Wait for [MAINTHREAD] to provide us with command list data
bufferSwappedCondition.Reset();
renderDoneCondition.Set();
renderCommandsReadyCondition.Wait();
renderDoneCondition.Reset();
renderCommandsReadyCondition.Reset();
// Swap buffer IDs
renderThreadID = !renderThreadID;
// Notify [MAINTHREAD] that the buffer was swapped, so we can generate new commands in parallel
bufferSwappedCondition.Set();
#endif
// Early out if forcing shutdown of renderThread
if (this->forceShutdown)
return;
const size_t dataSize = cmdList[!renderThreadID].size();
unsigned char* commandList = cmdList[!renderThreadID].data();
unsigned int n = 0;
SwapChainD3D11 *swapChain = (SwapChainD3D11*)this->currentChain;
while (n < dataSize)
{
eRC e_RC = (eRC)ReadCommand<unsigned int>(commandList, n);
switch (e_RC)
{
//...
}
}
}
}
I tested this synchronization design in a console application and logically it should really work, however putting this in my renderer I'm getting a deadlock as soon as I run my engine.This deadlock doesn't happen when I remove the
BufferSwappedCondition().Wait() but that makes it crash. It runs okay as far as I can tell if I make renderThreadID atomic and then have it do the swap (which creates a form of synchronization) but I'm not sure if that's a viable workaround. Do you guys have any idea why this leads to a deadlock ?