Search code examples
c++windowsdirectxdirect3ddxgi

Properly Handling Alt-Enter / Alt-Tab Fullscreen Resolution


The MSDN page on DXGI gives instructions on how to handle fullscreen resolutions different from the desktop resolution. It says to call IDXGISwapChain::ResizeTargets() before calling IDXGISwapChain::SetFullscreenState() to prevent flickering, among other things.

It does not say how to handle Alt-Enter, which calls IDXGISwapChain::SetFullscreenState() before the program is given a chance to make its own call to IDXGISwapChain::ResizeTargets(). If the latter method is called upon a WM_SIZE message, another WM_SIZE message will be sent, possibly causing an infinite loop. How can I ensure that the latter will be called before the former when alt-enter or alt-tab are pressed, and that mode switching occurs painlessly in general?


Solution

  • This is going to be really tricky ... the right way how this is supposed to be handled is IDXGIFactory::MakeWindowAssociation, which, as far as I know, nobody has managed to use successfully. You may want to try it anyway.

    The "right" answer is to manually handle Alt+Enter. So, disable Alt+Enter using MakeWindowAssociation and get your hands dirty. First, there is no need to capture WM_SIZE. Instead, listen on WM_ENTERSIZEMOVE, WM_CAPTURECHANGED, WM_WINDOWPOSCHANGED and WM_EXITSIZEMOVE. This will prevent you from having to deal with WM_SIZE and still get all relevant window resizing events. (When doing this, read this question as well: WM_ENTERSIZEMOVE / WM_EXITSIZEMOVE - when using menu, not always paired)

    Ok, so assuming everything went fine, for Alt+Enter, you have to do the following: You set your swap chain to full screen using IDXGISwapChain::SetFullscreenState and then resize your swap chain (IDXGISwapChain::ResizeBuffers). By default, you'll get a swap chain which is as close as possible to the current resolution of your window before resizing. The way you do this properly is to enumerate the full screen resolutions first, and upon going fullscreen, forcing the resolution you want to have. This sounds ugly, but it seems to be the most robust way to solve the problem.

    In general, real, exclusive fullscreen mode is not worth the trouble, as you will always get flickering when someone goes Alt+Tab (you can't avoid it if a mode switch happens, as the screen itself will have to readjust.) A much better solution is to use a fullscreen borderless window. You simply create a window class without any decoration, make it full-screen, place it such that it covers the whole screen and be done with it. Then you don't have to worry about Alt+Enter and Alt+Tab at all. It also allows people to continue working on a second screen without flickering. Performance wise, this is pretty ok'ish (most new games support this as "borderless fullscreen".)

    There might be a silver bullet which solves all of this correctly, but I haven't seen it yet. If there is a cleaner/nicer solution, I'd be really curious to hear it. "Borderless fullscreen" seems to be the current standard though, IIRC, Unity 5 will only allow "borderless fullscreen" for Direct3D 11.