Advertisement

Low resolution in full screen display (DirectX12)

Started by March 02, 2025 09:20 PM
4 comments, last by shrink_tubing 1 day, 17 hours ago

Hi there,

I have had this problem for a while now and it's got me very confused. When I make a swap chain and resize its buffers to the same size as a window's new client area (after a change from a WM_SIZE message) everything is fine. 

However I would like to render at full screen size but at a resolution lower than full screen resolution. Just like when you change the resolution settings in a game but it doesn't shrink the window. I've read a lot of the MSDN and couldn't find anything specific to this problem, or if I did, I didn't understand it properly.

AFAIK I can make a swap chain's buffers a certain size, and then have them stretched over larger client area which would give me the effect I want. When I make the swap chain I use the following code:

DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = mClientWidth; // set to 1280 
sd.BufferDesc.Height = mClientHeight; // set to 720
sd.BufferDesc.RefreshRate.Numerator = mRefreshNumerator; // 1
sd.BufferDesc.RefreshRate.Denominator = mRefreshDenominator; // 60
sd.BufferDesc.Format = mBackBufferFormat;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_STRETCHED; // was originally DXGI_MODE_SCALING_UNSPECIFIED - 2/3/25 CH
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = SwapChainBufferCount;
sd.OutputWindow = mMainWnd;
sd.Windowed = true; // if in doubt use true
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

Note I make use of the DXGI_MODE_SCALING_STRETCHED flag for the MODE DESC scaling part. I naively hoped this would solve things but it hasn't. I believe you also have to use the DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH to override some of DXGI's default behaviour to make the effect I'm hoping for possible.

So when a WM_SIZE message occurs I don't resize the swap chain buffers, I just call this:

mSwapChain->SetFullscreenState(true, NULL);

And it mostly works, stretching 1280 x 720 over a 1920 x 1080 display. But it leaves the most terrible flickering near the top of the window:

Note the black line at the top just below the menu bar. In practice it flickers a great deal. It looks like two things are competing to paint that portion of the window.

I don't know if I've messed something up in DirectX or if its the Windows side where I'm not instructing Windows to paint the window properly.

I really haven't got a clue and I'd appreciate any advice.

Thanks 🙂

You can to use the IDXGIFactory::MakeWindowAssociation() function to change DXGI behaviour during mode changes.

You should avoid hardcoding the display mode (specifically the refresh rate), as that is often a source of problems during transitions to Fullscreen.

Only use the “Allow Mode Switch” flag when going fullscreen.

Is there anything in the debug output? If you are doing something wrong, you will get errors in the debug output (assuming you have enabled Direct3D debug mode).

Advertisement

Hi there!

Much thanks for replying to my question. I liked your post 🙂

I've removed the hard coding for the display mode and refresh rate and returned it to a users choice which is made based on what the factory finds in the hardware (it then displays it all in a little dialog box you choose settings from).

I checked the debug output and there wasn't anything there at all, although it certainly is active as I've used it before to debug stuff I've screwed up in DirectX.

What does seem to have fixed it though (at least for now) is removing the menu bar from the created window. Code below shows the way the window is now created:

WNDCLASSEXW wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 1024;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MESHDATA1));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
//wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MESHDATA1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassExW(&wcex);

Just making the lpszMenuName point to nothing removed the menu bar and after testing it with many different display types offered by the hardware (including non 16:9 ratios) none of them have caused a problem so far:

And that for now seems to be it. I suspect that potentially there has been a clash in the painting of the window between DirectX and Windows when it comes to the non-client area. 

I even tried adding this code at someone else's recommendation:

SetWindowLongPtr(hWnd, GWL_STYLE, 0);
SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST);

SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);

ShowWindow(hWnd, SW_SHOWMAXIMIZED);

And it displays full screen (regardless of resolution chosen) without problems:

If I choose a low resolution (such as 640 x 480) it just stretches the swap chain buffer across the screen and looks fine.

It seems to be fixed for now.

Thanks for the help! 🙂

It's nice you have fixed/mitigated the issue. The fact you solved the issue by removing the menu makes me wonder if you are using AdjustWindowRect() to size the window properly. You might want to check that, a menu shouldn't cause any problems for DirectX.

Good call. I'll go check that out now. Thanks 🙂

Advertisement