Sign in to follow this  
DwarvesH

How to properly switch to fullscreen with DXGI?

Recommended Posts

DwarvesH    510

So i finally managed to create a stable Hello World for DirectX 11 (for some reason 10 and 11 never wanted to cooperate with me).

 

Yet most of the basic functionality is not working as expected.

 

For starters switching to fullscreen. All the documentation and other posts on the Internet say that DirectX 10/11 is so easy and low maintenance compared to 9 and just let DXGI take care of everything. Well, things are finicky to say the least.

 

So for starters, I would like to get DXGI resolution fullscreen switching to work as expected.

 

Here is the scenario:

1. I have a RenderForm class that creates a window. Specifying WS_POPUP is key for the behavior I am experiencing:

handle = CreateWindowEx(WS_EX_APPWINDOW, L"D3D10WindowClass", aTitle.c_str(), WS_CLIPSIBLINGS | WS_CLIPCHILDREN 
		| WS_POPUP /*WS_OVERLAPPEDWINDOW*/, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);

2. To better test everything, I am running in "fake" fullscreen mode, so the window has no frame and has the same dimensions as my desktop. I get a FPS of 670.

 

3. I switch to fullscreen mode with DXGI (Alt-Enter). I get 630 FPS. Normally one expects FPS to go up when switching to fullscreen. The swap chain when created has the flag for mode switching. Without ti, if I change from a less than native resolution to fullscreen, I get ugly artifacts I have never seen before.

swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

If I create the swap chain in fullscreen mode directly, I get 1370 FPS. The framerate is almost double compared to when I start in fullscreen mode. But that is not the extent of the problems. If I switch from this initial fullscreen mode to windowed, I get a white screen. If I switch back again I get the 630 fullscreen mode, the original high performance one being lost.

 

I do have code to handle window resize the swap chain, but because of WS_POPUP it is never called. If I create the window with WS_OVERLAPPEDWINDOW, things go to hell. The fullscreen window content is all squashed horizontally in the middle of the screen.

 

I could upload the code, but I think it is better to determine the correct way to handle going to fullscreen with DXGI before and try it out to see how that behaves. 

Share this post


Link to post
Share on other sites
DwarvesH    510

I found an easy way to reproduce the problem. Take any Rastertek tutorial as a sample, like this one:

http://www.rastertek.com/dx11tut04.html

 

Setting vsync off and changing the SystemClass::Initialize method to supply the correct maximum desktop resolution instead of 800x600 allow for easy reproduction.

 

All one needs to do is start the tutorial in windowed mode, switch to fullscreen and compare to it running in fullcreen mode directly. Every time I get only around 50% performance.

Share this post


Link to post
Share on other sites
DwarvesH    510

Your FPS don't matter as they're so high, and 670 to 630 means 95 microseconds which is nothing.

Display average frame-time in milliseconds or microseconds instead of the number of frames per second to get an accurate picture.

 

The GPU doesn't become faster in fullscreen. What it will do is use (usually) much better synchronization, to avoid artifacts from bad vsync etc.

It can often make sure that if one frame takes 10 ms, the next takes 13 ms, and then the third 10 ms again, they will all be displayed at 11 ms intervals if that matches the monitor refresh rate, instead of allowing such small random changes to cause stuttering.

 

Yes, the actual value is not important, but the general trend is. When switching to full-screen framerates is always higher. Not by much, but higher. I have 5 years of anecdotal evidence with this. Makes a lot of sense. In desktop mode even if you have the same bit depth, you color format might not match, while in fullscreen you change the color format too. Even if they are the same, in window mode Windows needs to blit you backbuffer to the window. This is a copy that takes a bit of time, plus the entire windowing system is involved. In exclusive fullscreen mode there should be no bliting under ideal circumstances.

 

But my trend is not up, but down. Something is terribly wrong. Even if I ignore this, the difference compounds. With vsync and 8x MSAA and an empty scene, my framerate drops from 60 to 38 when going fullscreen.

 

And even if I ignore this, if I start my application in fullscreen mode, and I hit Alt-Enter, I either get a white screen, or sometimes the rendering is frozen.

 

 

DXGI can either be instructed to simply go into fullscreen mode on the current desktop resolution (default), or actually try to change the display mode to match your  back-buffer resolution (mode-switch). I would recommend against mode switching.

 

Why wouldn't you recommend against mode switching? It is the logical and best thing to do. Without it you are guaranteed to have a performance detriment when switching to fullscreen. Even if I change modes Present complains that I did not use ResizeBuffers when going to fullscreen mode. I can't use it, because DXGI is in charge of going fullscreen. It does not notify me of this change. I guess I could try to detect in the render loop if fullscreen mode has changed and call ResizeBuffers redundantly.

 

 

Lastly, if the back-buffer size doesn't match the monitor size, it will be stretched. I guess this is where your artifacts come from. You need to handle for example WM_SIZE or in some other way detect when your window is resized, and when that happens Release your render target(s) and references to the back-buffer. Then call ResizeBuffers to resize the back-buffer to the new correct size and re-create your render target views and reset the view-port to the new size etc.

 

No, I know how stretching looks like. My artifacts look like if scan-lines would be randomly shortened. So should DXGI be in charge of going fullscreen or not? Because if not, I'll disable the mode switch and do it myself. I can't work any worse than it does today...

 

Does anybody have a link to a very well behaved DirectX 10/11 tutorial in C++? Nothing fancy, just render a cube or triangle at most, but it should be super stable when going in fullscreen mode an back. And not rastertek, does are not stable for me.

Share this post


Link to post
Share on other sites
Erik Rufelt    5901


No, I know how stretching looks like. My artifacts look like if scan-lines would be randomly shortened. So should DXGI be in charge of going fullscreen or not? Because if not, I'll disable the mode switch and do it myself. I can't work any worse than it does today...


But my trend is not up, but down. Something is terribly wrong. Even if I ignore this, the difference compounds. With vsync and 8x MSAA and an empty scene, my framerate drops from 60 to 38 when going fullscreen.

 

The scanlines sound like a broken GPU or driver bug. Performance could be fullscreen somehow fails and thinks it's occluded by another window or something I guess..

 

 


Does anybody have a link to a very well behaved DirectX 10/11 tutorial in C++?

 

Found this test that I did a couple of years ago.. still works well for me on Win7. Numpad + and - changes between available modes, and standard DXGI alt enter for fullscreen.

I know that they have changed some things for Win8 and store apps so if you're using that you probably want to read the docs on the new swap effects and format requirements...

#include <windows.h>
#include <D3D11.h>
#include <stdio.h>

#pragma comment (lib, "D3D11.lib")

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Main
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	// Register windowclass
	WNDCLASSEX wc;
	ZeroMemory(&wc, sizeof(wc));
	wc.cbSize = sizeof(wc);
	wc.lpszClassName = TEXT("MyClass");
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WndProc;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	RegisterClassEx(&wc);
	
	// Create window
	HWND hWnd = CreateWindow(
		wc.lpszClassName,
		TEXT("D3D11 Window"),
		WS_OVERLAPPEDWINDOW |
			WS_CLIPSIBLINGS |
			WS_CLIPCHILDREN,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		NULL,
		NULL,
		hInstance,
		NULL
	);
	
	// Create device and swapchain
	DXGI_SWAP_CHAIN_DESC scd;
	IDXGISwapChain *pSwapChain;
	ID3D11Device *pDevice;
	ID3D11DeviceContext *pDeviceContext;
	
	ZeroMemory(&scd, sizeof(scd));
	scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	scd.SampleDesc.Count = 1;
	scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	scd.BufferCount = 1;
	scd.OutputWindow = hWnd;
	scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
	scd.Windowed = TRUE;
	scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
	
	HRESULT hResult = D3D11CreateDeviceAndSwapChain(
		NULL,
		D3D_DRIVER_TYPE_HARDWARE,
		NULL,
		D3D11_CREATE_DEVICE_DEBUG,
		NULL,
		0,
		D3D11_SDK_VERSION,
		&scd,
		&pSwapChain,
		&pDevice,
		NULL,
		&pDeviceContext
	);
	if(FAILED(hResult)) {
		MessageBox(NULL, TEXT("D3D11CreateDeviceAndSwapChain"), TEXT("D3D11CreateDeviceAndSwapChain"), MB_OK);
		return 0;
	}
	
	// Render target
	ID3D11Texture2D *pBackBuffer;
	ID3D11RenderTargetView *pRTV;
	D3D11_TEXTURE2D_DESC backBufferDesc;
	
	pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer);
	pDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRTV);
	pBackBuffer->GetDesc(&backBufferDesc);
	pBackBuffer->Release();
	
	// Mode switching
	UINT currentMode = 0;
	bool modeChanged = false;
	BOOL currentFullscreen = FALSE;
	pSwapChain->GetFullscreenState(&currentFullscreen, NULL);
	
	// Main loop
	ShowWindow(hWnd, nCmdShow);
	
	bool loop = true;
	while(loop) {
		MSG msg;
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
			if(msg.message == WM_QUIT)
				loop = false;
			else {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else {
			// Check for mode change input
			bool changeMode = false;
			if(GetAsyncKeyState(VK_ADD) & 0x8000) {
				if(!modeChanged) {
					++currentMode;
					changeMode = true;
				}
			}
			else if(GetAsyncKeyState(VK_SUBTRACT) & 0x8000) {
				if(!modeChanged) {
					--currentMode;
					changeMode = true;
				}
			}
			else
				modeChanged = false;
			
			// Change mode
			bool changedThisFrame = false;
			
			if(changeMode) {
				IDXGIOutput *pOutput;
				pSwapChain->GetContainingOutput(&pOutput);
				
				UINT numModes = 1024;
				DXGI_MODE_DESC modes[1024];
				pOutput->GetDisplayModeList(scd.BufferDesc.Format, 0, &numModes, modes);
				
				if(currentMode < numModes) {
					DXGI_MODE_DESC mode = modes[currentMode];
					
					TCHAR str[255];
					wsprintf(str, TEXT("Switching to mode: %u / %u, %ux%u@%uHz (%u, %u, %u)\n"),
						currentMode+1,
						numModes,
						mode.Width,
						mode.Height,
						mode.RefreshRate.Numerator / mode.RefreshRate.Denominator,
						mode.Scaling,
						mode.ScanlineOrdering,
						mode.Format
					);
					OutputDebugString(str);
					
					pSwapChain->ResizeTarget(&(modes[currentMode]));
					
					changedThisFrame = true;
				}
				
				modeChanged = true;
			}
			
			// Check fullscreen state
			BOOL newFullscreen;
			pSwapChain->GetFullscreenState(&newFullscreen, NULL);
			
			// Resize if needed
			RECT rect;
			GetClientRect(hWnd, &rect);
			UINT width = static_cast<UINT>(rect.right);
			UINT height = static_cast<UINT>(rect.bottom);
			
			if(width != backBufferDesc.Width || height != backBufferDesc.Height || changedThisFrame || newFullscreen != currentFullscreen) {
				pDeviceContext->ClearState();
				pRTV->Release();
				
				pSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
				
				// Recreate render target
				pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer);
				pDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRTV);
				pBackBuffer->GetDesc(&backBufferDesc);
				pBackBuffer->Release();
			}
			
			// Remember fullscreen state
			currentFullscreen = newFullscreen;
			
			// Clear backbuffer
			float color[4] = {1.0f, 0.0f, 1.0f, 1.0f};
			pDeviceContext->ClearRenderTargetView(pRTV, color);
			
			// Present
			pSwapChain->Present(1, 0);
		}
	}
	
	// Release
	pSwapChain->SetFullscreenState(FALSE, NULL);
	
	pDeviceContext->ClearState();
	
	pRTV->Release();
	pDeviceContext->Release();
	
	ID3D11Debug *debugInterface = NULL;
	//hResult = pDevice->QueryInterface(__uuidof(ID3D11Debug), reinterpret_cast<void**>(&debugInterface));
	//if(FAILED(hResult)) {
	//}
	
	pDevice->Release();
	pSwapChain->Release();
	
	if(debugInterface != NULL) {
		debugInterface->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
		
		debugInterface->Release();
	}
	
	UnregisterClass(wc.lpszClassName, hInstance);
	
	return 0;
}

// Window procedure
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch(msg) {
		case WM_DESTROY:
			PostQuitMessage(0);
		return 0;
	}
	
	return DefWindowProc(hWnd, msg, wParam, lParam);
}

Share this post


Link to post
Share on other sites
DwarvesH    510

Thank you Erik!

 

That sample is indeed a lot more stable! I will try something like that detection of mode change on Monday. But I did test it on my home computers and things are looking good.

 

Except for starting up in fullscreen mode. If I put:

scd.Windowed = FALSE;

The application starts correctly, but first Alt-Enter leaves me with a nonfunctional window. Second crashes.

Share this post


Link to post
Share on other sites
Jason Z    6436

@Erik Rufelt: Can you throw that code into a GitHub / Codeplex repository?  It would be great to have a few samples to point at when questions like these come up (which honestly seems to be more frequent than expected...).

Share this post


Link to post
Share on other sites
Erik Rufelt    5901


The application starts correctly, but first Alt-Enter leaves me with a nonfunctional window. Second crashes.

It's better to start in window mode and then switch to fullscreen. It's discussed under Remarks here: http://msdn.microsoft.com/en-us/library/windows/desktop/bb174537%28v=vs.85%29.aspx

 

I updated the sample to allow starting to fullscreen and changed the "unknown" format for the back-buffer which is probably better if there are different formats (like 30-bit support on some modes). Check the link below for the new code.

 


@Erik Rufelt: Can you throw that code into a GitHub / Codeplex repository? It would be great to have a few samples to point at when questions like these come up (which honestly seems to be more frequent than expected...).

Sure!
https://github.com/rufelt/simpled3d11window

Share this post


Link to post
Share on other sites
DwarvesH    510

Holly shit! A stable DX11 app! Thank you! That was far harder than it needed to be. DirectX 9 is easier smile.png.

 

The only bit of instability I noticed is that if I start with MSAA and FRAPS and a debugger, I'll get a segfault on application exit when the swap chain is destroyed. I'll continue testing for a couple of days and also try to integrate it with my Application class and RenderForm class.

 

 

It's better to start in window mode and then switch to fullscreen. It's discussed under Remarks here: http://msdn.microsoft.com/en-us/library/windows/desktop/bb174537%28v=vs.85%29.aspx

 

GG Microsoft. I saw nothing that wouldn't lead me to believe that DXGI is a bit shit.

 

 

I updated the sample to allow starting to fullscreen and changed the "unknown" format for the back-buffer


What do you mean by "unknown" format?

Edited by DwarvesH

Share this post


Link to post
Share on other sites
DwarvesH    510

One strange and probably harmless behavior I have encountered with you code is that FRAPS framerate counter is rendered differently:

 

[attachment=25563:fraps.png]

 

The one on the left is how it is rendered in you application. The one on the right is how it is rendered in other applications. I'm investigating this a bit...

Share this post


Link to post
Share on other sites
Erik Rufelt    5901

FRAPS basically hacks into the app as far as I can tell and intercepts D3D the graphics.. not sure that's really something to take into account when designing an app.

About the "unknown" format, using DXGI_FORMAT_UNKNOWN for ResizeBuffers retains the old format. In the example only modes of that particular format are enumerated anyway so it won't matter, but if you enumerate modes of both R8G8B8A8 and R10G10B10A2 for example, switching from one to the other needs to reset the back-buffer format in ResizeBuffers.

 

You could do that for example like this:

UINT numModes = 1024;
DXGI_MODE_DESC modes[1024];
pOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &numModes, modes);
UINT numModes2 = 1024 - numModes;
pOutput->GetDisplayModeList(DXGI_FORMAT_R10G10B10A2_UNORM, 0, &numModes2, modes + numModes);
numModes += numModes2;

Share this post


Link to post
Share on other sites
DwarvesH    510

I did some further testing and the actual mode change seems to be very stable. I tested also on our dedicated computer with a broken GPU that kinda sorta sometimes maybe works well but generally there are tons of problems and the code showed standard behavior for that system.

 

Then I spent over 2 hours making the border style of the window changeable. The actual change was easy, but Windows API seems to be a bit buggy here. After you change the border to none and back, things like GetWindowRect do not return the appropriate value. I fixed it eventually.

 

The problems didn't end here. Windows API again insists to clip window sizes to desktop if they are bigger and they have a frame, so changing to certain larger modes would cause the window to become smaller than the mode. This wasn't a problem since ResizeTarget/ResizeBuffers used this smaller size, but I found no way to make it behave well.

 

So I did a hackish fix, where if the window becomes arbitrarily large compared to the desktop, I remove the window border and if needed set the left or top to 0 so I can maximize the visible area. I also override the user setting to "no frame" in order for it to seem less weird why the user asked one thing and got something else.

 

I couldn't find a better solution and I won't bother with it any more.

 

Finally, I will need to add support for life MSAA mode change and I think I'm done with window management. I'll try to figure that out tomorrow...

Share this post


Link to post
Share on other sites
Erik Rufelt    5901

If you want DXGI to handle your fullscreen switches for you, don't touch the window. If you want to change window parameters, disable DXGI handling using MakeWindowAssociation and handle it yourself. When DXGI monitors your window it will automatically handle changes to the window as well as change the window when needed. Two separate programs (yours and DXGI) trying to simultaneously handle the same window is asking for trouble and sync issues.

 

Not sure what you mean by switching to larger modes, if you let DXGI handle your window and change to an actual enumerated mode of an output it should automatically handle the window so it covers that output. If you make the window cover more area than the monitor and want the offscreen parts drawn to (or areas on another monitor), then you don't want fullscreen mode (and can't really have it, as it is defined as exactly the area of one monitor).

 

As far as I can tell the window itself doesn't really matter in true fullscreen.. and you can get pretty interesting issues by resizing the window to a smaller size than the screen while still drawing in fullscreen. What happens is the screen will still be filled without clipping to the window, and any background windows will look funny at best afterwards.

Share this post


Link to post
Share on other sites
mhagain    13430

If you want DXGI to handle your fullscreen switches for you, don't touch the window. If you want to change window parameters, disable DXGI handling using MakeWindowAssociation and handle it yourself. When DXGI monitors your window it will automatically handle changes to the window as well as change the window when needed.

 

Important to add to this because it might not be immediately obvious.  There is no middle ground here.  Either DXGI does everything and you do nothing, or else you have to do everything yourself (i.e just like back in the old D3D9 days).

Share this post


Link to post
Share on other sites
DwarvesH    510

If you want DXGI to handle your fullscreen switches for you, don't touch the window. If you want to change window parameters, disable DXGI handling using MakeWindowAssociation and handle it yourself. When DXGI monitors your window it will automatically handle changes to the window as well as change the window when needed. Two separate programs (yours and DXGI) trying to simultaneously handle the same window is asking for trouble and sync issues.

 

 

 

If you want DXGI to handle your fullscreen switches for you, don't touch the window. If you want to change window parameters, disable DXGI handling using MakeWindowAssociation and handle it yourself. When DXGI monitors your window it will automatically handle changes to the window as well as change the window when needed.

 

Important to add to this because it might not be immediately obvious.  There is no middle ground here.  Either DXGI does everything and you do nothing, or else you have to do everything yourself (i.e just like back in the old D3D9 days).

 

 

Well, DXGI does virtually nothing except for switching to fullscreen. Thanks to Erik I figured out that it is not even capable of going fullscreen on its own because without a manual call to ResizeBuffers performance is really bad.

 

Besides going to fullscreen there is a ton of window management that I think is needed for a well behaved rendering window. DXGI attempts none of it.

 

I am trying a middle-ground. It seems to work almost perfectly. What is needed is pending 3 days on about 50 lines of Win API/DXGI babysitting code and fine tune it until it works almost the way it is ideal. Ideal of course being subjective.

 

I am almost sure that things are good and I did test on 5 computers, but testing would be a lot easier if I had a text output on my window showing me the exact sizes.

 

But I can't have text output because DirectX 11. So I need to write my own class for text output. Which would be a lot easier if I had sprites. But I can't have sprites because of DirectX 11. So I am writing my own SpriteBatch class. Which is easy, except for orthographic cameras. I have never ever before used an orthographic camera. For the past two hours I've been trying to render a single simple quad with an orthographic camera and no luck yet. with projection it works flawlessly, but if i use my orthographic matrix nothing renders. I need to figure out that orthographic camera business and the figure out how to draw pixel perfect sprites in a MSAA render target. After that my SpriteBatch class will work but will barely have any features.

 

Porting from DX9 is not easy... sad.png

 

And yes, I know about DXTK or how it is called, but that has an unpleasant Windows 8 smell around it and it also locks you into DX11 I think. I found some other font engines but they are pretty big for my needs and I doubt will satisfy me because I'm a huge Unicode nazi.

 

Not sure what you mean by switching to larger modes, if you let DXGI handle your window and change to an actual enumerated mode of an output it should automatically handle the window so it covers that output. If you make the window cover more area than the monitor and want the offscreen parts drawn to (or areas on another monitor), then you don't want fullscreen mode (and can't really have it, as it is defined as exactly the area of one monitor).

 

As far as I can tell the window itself doesn't really matter in true fullscreen.. and you can get pretty interesting issues by resizing the window to a smaller size than the screen while still drawing in fullscreen. What happens is the screen will still be filled without clipping to the window, and any background windows will look funny at best afterwards.

 

I am talking about windowed mode switches. Let's say you want 720p  and have a 768p monitor. No problem. But now if you switch to 768p, because of you window border and probably non-zero window location, the bottom right corner of the window is off-screen. This wouldn't be a problem except for Windows API: once the sizable window goes out of bounds, Win API will start to clip it. Asking for a 900p window for example will clip it randomly to a value that is neither 768p nor 900p. Even asking for 768p will clip it to a bit bellow.This only happens if the border is sizable and I found no way around it. For frame-less windows it behaves as expected.

Share this post


Link to post
Share on other sites
Erik Rufelt    5901

I am talking about windowed mode switches. Let's say you want 720p and have a 768p monitor. No problem. But now if you switch to 768p, because of you window border and probably non-zero window location, the bottom right corner of the window is off-screen. This wouldn't be a problem except for Windows API: once the sizable window goes out of bounds, Win API will start to clip it. Asking for a 900p window for example will clip it randomly to a value that is neither 768p nor 900p. Even asking for 768p will clip it to a bit bellow.This only happens if the border is sizable and I found no way around it. For frame-less windows it behaves as expected.

 

You are really doing things you shouldn't be doing.

If you want a 720p front buffer on a 768p monitor, then you're not in fullscreen in the first place. In that case yes, cover the area you want with a window is a reasonable option, but then most certainly disable DXGI fullscreen. And again, the swap-chain is not and should not be in fullscreen then.

One simple option in that case is to simply set the view-port to a 720p area on a normal 768p fullscreen back-buffer, to avoid the problem.

 

You seem to be doing some pretty weird things that makes matters much more complicated than they need to be.

Edited by Erik Rufelt

Share this post


Link to post
Share on other sites
DwarvesH    510

You are really doing things you shouldn't be doing.

If you want a 720p front buffer on a 768p monitor, then you're not in fullscreen in the first place. In that case yes, cover the area you want with a window is a reasonable option, but then most certainly disable DXGI fullscreen. And again, the swap-chain is not and should not be in fullscreen then.

One simple option in that case is to simply set the view-port to a 720p area on a normal 768p fullscreen back-buffer, to avoid the problem.

 

You seem to be doing some pretty weird things that makes matters much more complicated than they need to be.

 

I'm not doing anything super complicated, just what I consider the bare minimum rules for a well behaved application:

1. It should handle windowed applications with a sizable border, a non- sizable border or no border while respecting windows conventions (non-sizable border should not have maximize box, etc.). In windowed mode any buffer size should be supported.

2. It should handle fullscreen mode. In fullscreen mode only one of the display modes of the adapter can be used.

3. It should handle transition form windowed to fullscreen in a consistent manner, i.e. restoring window border and position. If it can't it will do its best to maintain a stable application but give a beep.

4. All parameters must be changeable at run time as long as they don't contradict other rules.

5. Stretching is never an option. The back-buffer must always have the same size as the front buffer. Centered smaller viewport than the resolution is forbidden.

 

These are all a bit challenging with DXGI but not impossible. There are just a ton of special cases. Like how the back buffer in windowed mode can't have odd dimensions. Making in even in a simplistic manner is easy. Yet a more complicated method is used because there is no guarantee what theme the user has. He may even have something like Windows blinds installed. I found this to be a good general code to handle odd sizes:

case WM_SIZING: {
			RECT w, c;
			GetWindowRect(hWnd, &w);
			GetClientRect(hWnd, &c);
			RECT* r = (RECT*)lParam;

			int bx = abs((w.right - w.left) - (c.right - c.left));
			int by = abs((w.bottom - w.top) - (c.bottom - c.top));

			if ((r->right - r ->left - bx) % 2)
				 r->right--;
			if ((r->bottom - r->top - by)% 2)
				 r->bottom--;

			return TRUE;
		}

Anyway, this only has to be done once. Unfortunately, the solutions form DirectX 9 do not work with DXGI, so new solutions must be found. I did solve 95% of them. Sometimes when in windowed mode on a random resolution DXGI switches to fullscreen mode that makes no sense. I might fix that too, but it is not a priority task.

 

I am far more concerned with the rest. I wrote my SpriteBatch class which finally works. Doesn't do that much batching though, but at least I can do 2D stuff like GUI. The good part is that I will port it to DirectX 9 too and no longer use the one that is supplied thus making my GUI code more portable and able to run on all 3 versions.

 

Now to write the Font whatever class...

Share this post


Link to post
Share on other sites
Erik Rufelt    5901


Centered smaller viewport than the resolution is forbidden.

This means that any aspect ratio not matching the monitor is always window mode. Perhaps that's obvious and I misunderstood your post that you wanted fullscreen mode for a different aspect ratio.

 


Like how the back buffer in windowed mode can't have odd dimensions

 

Do you have any reference for that?

Never had that problem, can use windows with client area of prime x prime without issue...

Share this post


Link to post
Share on other sites
DwarvesH    510

Like how the back buffer in windowed mode can't have odd dimensions

 

Do you have any reference for that?

Never had that problem, can use windows with client area of prime x prime without issue...

 

 

The backbuffer can have odd dimensions and 3D rendering works great with it. It is 2D rendering that does not work well for GUI and stuff. I am using an orthographic camera and a SpriteBatch (written by myself) type class to render the GUI. I am also using filtering to render the GUI so that stretched controls look nice and smooth. Without filtering odd buffers work. With filtering, they don't. It is probably my fault, not a real issue with DX, since I did not pay attention to texel centers and whatnot that is needed for dealing with bilinear filtering.

 

I may investigate this further and if it is an issue with SpriteBatch, I may disable the code that forces the window to be even. But right now I need to get the port form C#/DX9 to C++/agnostic between DX10 and DX11 done ASAP! I will be at least one week late :).

 

 

Do you or do you not use MakeWindowAssociation with DXGI_MWA_NO_WINDOW_CHANGES?

 

No, I do not. I tried to do as little as possible and let DXGI do as much as possible. Probably would have been much easier to handle everything myself.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this