Variable refresh rate displays

Variable refresh rate displays require tearing to be enabled, this is also known as "vsync-off" support.

Variable refresh rate displays/Vsync off

Support for variable refresh rate displays is achieved by setting certain flags when creating and presenting the swap chain.

To use this feature, app users need to be on Windows 10 systems with either KB3156421 or the Anniversary Update installed. The feature works on all versions of Direct3D 11 and 12 using DXGI_SWAP_EFFECT_FLIP_* swap effects.

To add vsync-off support to your apps, you can refer to a complete running sample for Direct3D 12, D3D12Fullscreen (refer to Working Samples). There are also a few points not explicitly called out in the sample code but you need to pay attention to.

  • ResizeBuffers (or ResizeBuffers1) must have the same swap chain creation flag (DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) passed to it as Present (or Present1).
  • DXGI_PRESENT_ALLOW_TEARING can only be used with sync interval 0. It is recommended to always pass this tearing flag when using sync interval 0 if CheckFeatureSupport reports that tearing is supported and the app is in a windowed mode - including border-less fullscreen mode. Refer to the DXGI_PRESENT constants for more details.
  • Disabling vsync does not necessarily uncap your frame rate: developers also need to make sure Present calls are not throttled by other timing events (such as the CompositionTarget::Rendering event in an XAML-based app).

The code below recaps a few key pieces you need to add to your apps.

//--------------------------------------------------------------------------------------------------------
// Check Tearing Support
//--------------------------------------------------------------------------------------------------------

// Determines whether tearing support is available for fullscreen borderless windows.

void DXSample::CheckTearingSupport()
{
// Rather than create the 1.5 factory interface directly, we create the 1.4
// interface and query for the 1.5 interface. This will enable the graphics
// debugging tools which might not support the 1.5 factory interface.

    ComPtr<IDXGIFactory4> factory4;
    HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory4));
    BOOL allowTearing = FALSE;
    if (SUCCEEDED(hr))
    { 
        ComPtr<IDXGIFactory5> factory5;
        hr = factory4.As(&factory5);
        if (SUCCEEDED(hr))
        {
            hr = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing));
        }
    }
    m_tearingSupport = SUCCEEDED(hr) && allowTearing;
}

//--------------------------------------------------------------------------------------------------------
// Set up swapchain properly
//--------------------------------------------------------------------------------------------------------

// It is recommended to always use the tearing flag when it is supported.
swapChainDesc.Flags = m_tearingSupport ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;

//--------------------------------------------------------------------------------------------------------
// Present
//--------------------------------------------------------------------------------------------------------

UINT presentFlags = (m_tearingSupport && m_windowedMode) ? DXGI_PRESENT_ALLOW_TEARING : 0;

// Present the frame.
ThrowIfFailed(m_swapChain->Present(0, presentFlags));

DXGI 1.5 Improvements

Programming Guide for DXGI