Search code examples
c++directxdirect3dvsyncdirectx-12

Direct3D 12 windowed mode forces vsync


I am writing a simple Direct3D 12 application to prepare for the release of Vulkan, and it works as expected in all regards but one: running in a bordered window restricts the framerate to 60fps, even with vsync disabled. What puzzles me: the same program in a fullscreen window runs at nearly 4000fps.

Using a dirty self-made profiler, I found that the hangup occurs in this section of my code, which waits until the last frame completes before starting to work on the next one.

if (m_fence->GetCompletedValue() < endFenceValue)
{
    result = m_fence->SetEventOnCompletion(endFenceValue, m_fenceEvent);
    if (result != S_OK) return false;
    WaitForSingleObject(m_fenceEvent, INFINITE); //Program stalls here
}
//m_fence is a pointer to an ID3D12Fence object
//endFenceValue is an unsigned long long
//m_fenceEvent is a HANDLE

The code used to present the rendered frame is ordinary:

if (m_vsync)
{
    result = m_swapChain->Present(1, 0);
    if (result != S_OK) return 0;
}
else
{
    result = m_swapChain->Present(0, 0);
    if (result != S_OK) return 0;
}

//Increase the fence value
result = m_commandQueue->Signal(m_fence, m_fenceValue);
if (result != S_OK) return 0;

return m_fenceValue++;
//m_swapChain is a pointer to an IDXGISwapChain3 object
//m_commandQueue is a pointer to an ID3D12CommandQueue object
//m_fenceValue is a HANDLE

Note: the first block of code uses the return value from the above function as endFenceValue.

The swap chain I am using is set up like so:

swapChainDesc.BufferCount = 2; //Double buffered
swapChainDesc.BufferDesc.Width = width; //Set width
swapChainDesc.BufferDesc.Height = height; //Set height
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; //32-bit back buffers
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; //Set the back buffers to be used as render targets
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; //Throw out old back buffer contents after getting a new frame
swapChainDesc.OutputWindow = window;
swapChainDesc.Windowed = !fullscreen;
//Auto-detect the refresh rate
swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 0;

//No multisampling for now
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
//Set the scan line ordering and scaling to unspecified
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
//Allow to switch between windowed and fullscreen modes
//Also changes the monitor resolution to match the width and height of the window in fullscreen mode
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

For those interested, I am using SDL to create the window, but writing my own WinMain did nothing to fix the problem. I have also tried inspecting my vsync settings in the nVidia Control Panel, exiting fl.ux (but not uninstalling it), and changing my performance settings in System Properties.

Can anyone provide an explanation or solution for this?


Solution

  • The refresh rate cap with windowed swap chain has been removed with windows 10 build version 10586.

    Update your windows and it should solve itself.