Advertisement

DXGI_SWAP_EFFECT_DISCARD to DXGI_SWAP_EFFECT_FLIP_DISCARD

Started by April 09, 2018 05:35 PM
11 comments, last by ROGRat 6 years, 3 months ago

After reading some articles about the DXGI flip model:

I decided to switch my DXGI_SWAP_EFFECT_DISCARD to DXGI_SWAP_EFFECT_FLIP_DISCARD.

I do not use multiple swap chains per HWND and neither do I mix GDI/D3D (only D3D writes to the swap chain). Based on the articles, it seems that I just need to change my swap chain descriptor from


DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width       = m_display_configuration.GetDisplayWidth();
swap_chain_desc.Height      = m_display_configuration.GetDisplayHeight();
swap_chain_desc.Format      = m_display_configuration.GetDisplayFormat(); // DXGI_FORMAT_R8G8B8A8_UNORM
swap_chain_desc.SampleDesc.Count = 1u; // thus no MSAA
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.BufferCount = 1u;
//swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swap_chain_desc.Flags       = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

to


DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width       = m_display_configuration.GetDisplayWidth();
swap_chain_desc.Height      = m_display_configuration.GetDisplayHeight();
swap_chain_desc.Format      = m_display_configuration.GetDisplayFormat(); // DXGI_FORMAT_R8G8B8A8_UNORM
swap_chain_desc.SampleDesc.Count = 1u; // thus no MSAA
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.BufferCount = 2u;
swap_chain_desc.SwapEffect  = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swap_chain_desc.Flags       = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

Since my use case seems pretty basic, only two changes seem/are required (i.e. buffer count and swap effect). I wonder if I am missing something (though, my program compiles and runs fine)? (I still use CreateSwapChainForHwnd.)

 

According to the last article, I can even use a R10G10B10A2_UNORM which won't get clamped depending on the desktop?

🧙

The biggest advantage is the use of the waitable object. You can now do something meaningful while waiting for the vsync ( because now, the wait is on you ). And it is even better, because the wait is not on vsync, but on the driver in need of a new frame, with a fine grain control over how many frames can be queued, putting the wait upfront reduce latency because you prepare a frame closer to the moment it will display !

 

 

Advertisement
13 minutes ago, galop1n said:

The biggest advantage is the use of the waitable object. You can now do something meaningful while waiting for the vsync ( because now, the wait is on you ). And it is even better, because the wait is not on vsync, but on the driver in need of a new frame, with a fine grain control over how many frames can be queued, putting the wait upfront reduce latency because you prepare a frame closer to the moment it will display !

So instead of waiting for the back buffer to be drawn, you keep rendering and removing out-dated presents till drawining is allowed?

Quote

If not in fullscreen state (true immediate independent flip mode) do control your latency and buffer count in your swap-chain carefully for the desired FPS and latency
Use IDXGISwapChain2::SetMaximumFrameLatency(MaxLatency) to set the desired latency
For this to work you need to create your swap-chain with the DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT flag set.
li>A sync interval of 0 indicates that "the buffer I am presenting now is the newest buffer available next time composition happens" and discards all previous presents. However, the present does not go through until composition happens, which currently is only at VSync.
DXGI will start to block in Present() after you have presented MaxLatency-1 times
At the default latency of 3 this means that you FPS can’t go higher than 2 * RefershRate. So for a 60Hz monitor the FPS can’t go above 120 FPS.
Try using about 1-2 more swap-chain buffers than you are intending to queue frames (in terms of command allocators and dynamic data and the associated frame fences) and set the "max frame latency" to this number of swap-chain buffers.

https://developer.nvidia.com/dx12-dos-and-donts

 

And if vsync is turned off, everything stays the same?

🧙

Pretty sure those recommendations are from before the ALLOW_TEARING flag existed. As of the Windows 10 November Update (1511), the default behavior for a windowed swapchain with sync interval 0 is to run as fast as possible, instead of that weird N*refresh behavior. However, if you end up in direct/independent flip, you still end up back with that behavior, unless you use the ALLOW_TEARING flag.

My suggestion for simplicity is to just use the ALLOW_TEARING flag for sync interval 0, unless you really want to get fancy and try to implement something smarter.

16 hours ago, SoldierOfLight said:

Pretty sure those recommendations are from before the ALLOW_TEARING flag existed. As of the Windows 10 November Update (1511), the default behavior for a windowed swapchain with sync interval 0 is to run as fast as possible, instead of that weird N*refresh behavior. However, if you end up in direct/independent flip, you still end up back with that behavior, unless you use the ALLOW_TEARING flag.

My suggestion for simplicity is to just use the ALLOW_TEARING flag for sync interval 0, unless you really want to get fancy and try to implement something smarter.

So DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT always, and ALLOW_TEARING additionally in case of no vsync?

🧙

Up to you if you want the latency waitable object, it's usually nothing but a positive but not strictly required. But for VSync off, yes you should use ALLOW_TEARING unless you want to try doing something very smart.

Advertisement
2 hours ago, SoldierOfLight said:

Up to you if you want the latency waitable object, it's usually nothing but a positive but not strictly required.

My frame rate decreased a lot without vsync?


void Manager::Render() {
	m_swap_chain->Wait();
	m_swap_chain->Clear();
	m_renderer->Render(GetWorld());
	m_swap_chain->Present();
}

void SwapChain::Wait() const noexcept {
	WaitForSingleObjectEx(m_frame_latency_waitable_object, INFINITE, true);
}

 

🧙

Did you bump the swapchain's frame latency to 2 or did you leave it at 1? I meant that it's a positive generally if you're comparing apples-to-apples. The default frame latency for a non-waitable swapchain is 3, where the default for the waitable swapchain is 1. Try 2 or 3. A lower latency prevents some CPU-GPU parallelism.

1 hour ago, SoldierOfLight said:

The default frame latency for a non-waitable swapchain is 3, where the default for the waitable swapchain is 1.

Thought, it was always 3:

Quote

The maximum number of back buffer frames that will be queued for the swap chain. This value is 3 by default.

https://msdn.microsoft.com/en-us/library/windows/desktop/dn268313(v=vs.85).aspx

 

A value of 2 is much better than the default 1. 3 is slightly better than 2 (in my test scene). Though, the difference with and without the waitable object is pretty small.

 

I also noticed a new problem: I cannot switch anymore between fullscreen and windowed mode via m_swap_chain->SetFullscreenState(FALSE/TRUE, nullptr); ?

🧙

This topic is closed to new replies.

Advertisement