1

I'm building a DirectX Windows Desktop application to present stereoscopic images via two monitors. Currently, I have one swap chain per window (monitor). When updating the windows, I use a simple loop and call Present on the swap chains sequentially, which obviously results in them not updating simultaneously.

However, when I tried creating 2 separate threads and executing Present on both of the swap chains concurrently, the majority of the time a deadlock occurs, which I suspect is because of 2 threads being created and joined each frame, which may be too fast to do so.

Is there any alternative means of updating the contents of the swapchains at once?

This is my rendering code, which gets called once per frame:

void Game::Render(DrawFunction func)
{
    for (int i = 0; i < NUMBER_OF_WINDOWS; i++)
    {
        Clear(i);

        m_deviceResources->PIXBeginEvent(L"Render");
        auto context = m_deviceResources->GetD3DDeviceContext();

        m_spriteBatch->Begin();

        func(i, m_spriteBatch.get());

        m_spriteBatch->End();

        m_deviceResources->PIXEndEvent();

        auto renderTarget = m_deviceResources->GetRenderTargetView(i);
        context->OMSetRenderTargets(1, &renderTarget, nullptr);

        m_toneMap[i]->Process(context);

        ID3D11ShaderResourceView* nullsrv[] = { nullptr };
        context->PSSetShaderResources(0, 1, nullsrv);
    }

    m_deviceResources->ThreadPresent();

    for (int i = 0; i < NUMBER_OF_WINDOWS; i++)
    {
        m_deviceResources->CleanFrame(i);
    }
}

void DX::DeviceResources::ThreadPresent()
{
    std::thread one([&]() { m_swapChain[0]->Present(1, 0); });
    std::thread two([&]() { m_swapChain[1]->Present(1, 0); });

    one.join();
    two.join();
}
Richard Robinson
  • 867
  • 1
  • 11
  • 38
  • 1
    Creating and joining threads are expensive operations. I doubt that doing this for every frame is a good way to improve performance. Synchronizing output on two monitors might need more effort when just synchronizing two threads. The graphic drivers and H/W may (or may not) play a role also. (I roughly remember that SGI provided a special genlock port to sync. graphics boards for things like multi-display stereo output - many, many years ago...) ;-) NVidias GL VSync extension is something else which comes in mind (although I don't know how it could help you). – Scheff's Cat Aug 07 '19 at 06:42
  • Out of curiosity, I googled a bit and found e.g. [SO: Fast multi-window rendering](https://stackoverflow.com/q/13213648/7478597). (I must admit that I know a bit about OpenGL but rather nothing about DirectX. Concerning OpenGL, multi-threading is often a dead-end as things are serialized in the graphics driver or below.) You may try your luck yourself with [google "directx sync two windows"](https://www.google.com/search?q=directx+sync+two+windows&oq=directx+sync+two+windows). – Scheff's Cat Aug 07 '19 at 06:49
  • 1
    Your issue isn't your call order but maybe determined by the driver. You could flush your drawing Pipeline but this has a performance hit. But have a look at this also -> http://msdn.microsoft.com/en-us/library/windows/desktop/ee417025.aspx#Multithreading_and_DXGI as this discusses the topology. I can't recall where, but I had believed that calling present from anywhere but your parent thread is an issue. – ErnieDingo Aug 08 '19 at 01:06
  • Thanks @ErnieDingo! Do you know of any alternative methods to do what I'm trying to? Surely it's possible to sync 2 swap chains? – Richard Robinson Aug 08 '19 at 02:50
  • 1
    If both pipelines are clear, the I would assume that the Presents on both should be almost instantaneous. As mentioned, see if flushing the pipeline helps. As Present has to actually wait for the current rendering to be completed prior to swapchain presentation. – ErnieDingo Aug 08 '19 at 03:08
  • @ErnieDingo thank you again! Ya, the `Present`s are very quick, but the difference is still noticeable when I record the monitors at 240 fps. How do I flush the pipeline? I'm fairly new to DirectX still (I'm using DirectXTK if that matters) – Richard Robinson Aug 08 '19 at 03:36
  • I assume you are using DirectX 11, as such on your device context you can call flush(). Read here for more details -> https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-flush . This does have an overhead though, but it may mean you wont have a noticable difference on the Present() – ErnieDingo Aug 08 '19 at 23:41
  • You may want to look at what people do for head-mounted stereoscopic displays. Very low latency constraints. There game conference talks, for example. – Pablo H Jan 04 '23 at 15:11

0 Answers0