3
\$\begingroup\$

After adding multisampling to a DirectX 11 project, I noticed that the screen was no longer updating when calling IDXGISwapChain ::Present. Further testing showed that it was in fact updating the screen, but it was always 1 frame behind. If I added a line to the scene and presented it, I wouldn’t see that line until I do the next present call. This behavior is specific to the use of multisampling. If I turn multisampling off, the behavior goes away. This behavior is also specific to my Intel display adapter. If I use my Nvida display adapter, the behavior goes away.

The multisampling settings are determined by using ID3D11Device::CheckMultisampleQualityLevels, and the creation of the back buffer and depth stencil work, so I don’t think the settings themselves are incorrect in any way. Also the multisampling does work; the end result is properly multisampled. I have played with various multisampling settings within the available range, and all settings produce the same behavior. Just in case I’m doing something wrong, I tried using the debug layer, but got no warnings or errors.

Through hours of testing, I found two work-arounds:

1 - Call Present twice:

HRESULT result = _swapChain->UnmanagedPointer->Present(0, 0);
if (result != S_OK)
    ExceptionThrower::Throw(result, "Failed to present the swap chain buffer.");

result = _swapChain->UnmanagedPointer->Present(0, 0);
if (result != S_OK)
    ExceptionThrower::Throw(result, "Failed to present the swap chain buffer.");

When I do this, the second Present call puts the correct information on screen.

2 - Call ID3D11DeviceContext::Flush after Present

HRESULT result = _swapChain->UnmanagedPointer->Present(0, 0);
if (result != S_OK)
    ExceptionThrower::Throw(result, "Failed to present the swap chain buffer.");

_d3dDeviceContext->Flush();

After the Flush call, the correct information is on screen.

As I understand it, both of these work arounds incur a significant penalty, so I would rather find a better solution (if on exists). I especially dislike this work around because it penalizes everyone. There is no way for me to know when this behavior is happening. I have a feeling that I may be dealing with a driver bug since this is only an issue with the Intel adapter. I hate to force all scenarios to add this overhead when for most, it won’t be necessary. Oh and in case someone asks, I have updated the Intel drivers to the most recent, but it’s an old adapter, so these drivers date back to 2012.

Has anyone seen this type of behavior before? Is there a better work around, or perhaps a solution I’m not aware of? If forced to use one of the two work arounds, which of the two would be the best? I’m leaning towards the second (Flush call), but not sure.

In case helpful, here is my swap chain creation code

DXGI_SWAP_CHAIN_DESC desc;
ZeroMemory(desc, sizeof(DXGI_SWAP_CHAIN_DESC));

desc.BufferDesc.Width = width;
desc.BufferDesc.Height = height;
desc.BufferDesc.RefreshRate.Numerator = 60;
desc.BufferDesc.RefreshRate.Denominator = 1;
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.OutputWindow = outputWindow;
desc.Windowed = true;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
desc.Flags = 0;

if (useMultiSampling)
{
    desc.SampleDesc.Count = _multiSampleCount;
    desc.SampleDesc.Quality =  _multiSampleQuality - 1;
}
\$\endgroup\$
1
\$\begingroup\$

To present an MSAA back-buffer, it must be resolved (i.e. the multiple samples per pixel need to be reduced to a single pixel) before it can be composited and shown to the user. For older presentation modes with DXGI (DXGI_SWAP_EFFECT_DISCARD or DXGI_SWAP_EFFECT_SEQUENTIAL) and with Direct3D 9, this is 'magic' done behind the scenes on your behalf which likely introduces the additional frame of latency.

For both the Universal Windows Platform (UWP) apps and Direct3D 12 generally, this old model is not supported: i.e. trying to create a backbuffer with a SampleDesc.Count > 1 will result in a failed call--basically when you use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL or DXGI_SWAP_EFFECT_FLIP_DISCARD.

You instead implement MSAA resolves directly. You create your render target as MSAA and then explicitly call ResolveSubresource to get it from your MSAA render target to the backbuffer for presentation.

Note that one additional complication here is if your render target is an SRGB format. Resolve won't handle the gamma-correction, so you typically have to use ResolveSubresource from your SRGB MSAA Render Target to an SRGB Render Target, then render as a full-screen quad the SRGB Render Target onto the final backbuffer which is not SRGB.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your response. I understand what you're saying, and I do have a solution like what you speak of for a WPF application we have. This of course is due to the fact that in WPF, you must render to surfaces, there is no swap chain in use. And it's true, in this solution, I do not have the frame latency issue. I don't really understand how this would apply to the swap chain, however. Do I need to use a different render target, and then copy it to the swap chain's buffer with ResolveSubresource? It seems like that would introduce a lot of overhead. \$\endgroup\$ – Jason Tyler Aug 6 '16 at 21:50
  • 1
    \$\begingroup\$ The overhead is already there... it's just done 'magically' by DXGI. In order to present an MSAA render target, it must be resolved either explicitly or implicitly, and that requires a destination non-MSAA buffer. For UWP and Direct3D 12, you always have to do it explicitly. \$\endgroup\$ – Chuck Walbourn Aug 6 '16 at 22:07
  • \$\begingroup\$ I see. So, would you recommend rendering to a MSAA surface, and then copying that to the swap chain's buffer by using ResolveSubresource? And then, when the copy is complete, call Present? \$\endgroup\$ – Jason Tyler Aug 6 '16 at 22:17
  • \$\begingroup\$ I'm suggesting that might be the source of the 'mystery' latency, so something you could try. \$\endgroup\$ – Chuck Walbourn Aug 7 '16 at 16:36
  • \$\begingroup\$ It is a good advice to perform msaa resolve on your own of course, but you may also be victim of IDXGIDevice1::SetMaximumFrameLatency, the driver may queue your frame to improve performance and so run with a frame behind : msdn.microsoft.com/en-us/library/windows/desktop/… \$\endgroup\$ – galop1n Aug 12 '16 at 5:10
0
\$\begingroup\$

Additionally, you haven’t specified the refresh rate correctly. As a result, transitions to full screen mode would result in DXGI using blit as opposed to flip. The correct value is 60000 for the numerator and 1001 for the denominator. Even better, enumerate the display modes from the adapter and use the values contained therein.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.