Swap chains are an integral part of how you get rendering data output to a screen. They usually consist of some group of output-ready buffers, each of which can be rendered to one at a time in rotation. In parallel with rendering to one of a swap chain’s buffers, some other buffer in the swap chain is generally read from for display output.
This post covers best practices when working with swap chains on NVIDIA GPUs. To get a high and consistent frame rate in your applications, see all Advanced API Performance tips.
It’s common to focus on the more frequently optimized parts of the rendering pipeline when looking to improve rendering performance. However, swap chains are often overlooked, leaving potential performance and latency on the table.
The following suggestions and considerations should provide more insight into the best ways to ensure optimum swap chain performance.
Recommended
- Use flip-mode swap chains. This is especially important for leveraging multiplane overlay support, which provides fullscreen-like performance and latency when running in windowed mode.
- Use
SetFullScreenState(TRUE)
, a (borderless) fullscreen window, and a non-windowed flip model swap chain to switch to true immediate independent flip mode.- This is the only mode that enables unlimited framerates with tearing for Direct 3D 12 when calling
Present(0,0)
. - For proper unlimited frame rate support for displays that support variable refresh rates, you must also use the
DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING
swap chain flag, along with theDXGI_PRESENT_ALLOW_TEARING
Present
flag
- This is the only mode that enables unlimited framerates with tearing for Direct 3D 12 when calling
- Use the
DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
flag consciously.- The flag is not necessary to achieve unlimited frame rates (see earlier note) if your window size matches the current screen resolution
- If this flag is set, trying to change the resolution using ResizeTarget before calling
SetFullScreenState(TRUE)
works fine and the framerate will be unlimited - If this flag is not set, trying to change resolution using
ResizeTarget
before callingSetFullScreenState(TRUE)
results in no change of display resolution. Your target is stretched to the current resolution and the frame rate is limited.
- If not in the fullscreen state (true immediate independent flip mode), control your latency and buffer count in your swap chain carefully for the desired frame rate and latency.
- Use
IDXGISwapChain2::SetMaximumFrameLatency(MaxLatency)
to set the desired latency, whereMaxLatency
is some number of frames (counted by queuedPresent
calls). - For this to work, you must create your swap chain with the
DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT
flag set. - DXGI starts to block in
Present
after you have theMaxLatency
number of present calls queued. - In this windowed state, a sync interval of 0 in a
Present
call ensures that the frame being presented is the most recent frame available for the next time desktop composition happens (combining the windowed rendered frame with the rest of the desktop), and all previously completed frames are discarded in favor of this latest one. No rendered frame is displayed until composition happens, which happens atVSYNC
time. This latest finished frame is what is displayed
- Use
- Use about 1-2 more swap chain buffers than the maximum number of frames that you intend to queue (in terms of command allocators, dynamic data, and the associated frame fences). Set the maximum frame latency to this number of swap chain buffers through
IDXGISwapChain2::SetMaximumFrameLatency(MaxLatency)
.- This ensures that you can limit queued frames and latency explicitly and optimally from within the application logic rather than relying on the OS to block or have it block at an unexpected time.
Not recommended
- Forgetting that, by default, there’s a per–swap chain limit of three queued frames before DXGI starts to block in
Present
. This means that it blocks on the fourth Present call if there are currently threePresent
calls queued.- Set the
DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT
flag on swap chain creation and useIDXGISwapChain2::SetMaximumFrameLatency
to modify this default value
- Set the
- Forgetting to call
ResizeBuffers
after you have switched to true immediate independent flip mode usingSetFullScreenState(TRUE)
.
Acknowledgments
Thanks to Cody Robson, Kumaresan Gnanasekaran, Adrian Muntianu, and Meenal Nachnani for their advice and assistance.