Alt tabbing in and out of a DX windows game

Started by
16 comments, last by Tertsi 19 years, 6 months ago
When I alt + tab out of my fullscreen DirectX 9c, D3D windows game and try to put the focus back to the game it won't automatically switch to a full screen window and the following code crashes. So how to handle this problem?

// Inside graphics class is the line: D3DPRESENT_PARAMETERS	d3dpp;

// Inside the updategame function which is executed constantly.
	HRESULT hr;
	hr = MGEgfxref->lpdevice->TestCooperativeLevel();
	if(hr != D3D_OK)
	{
		if(hr == D3DERR_DEVICENOTRESET) MGEgfxref->lpdevice->Reset(&MGEgfxref->d3dpp);
		else return;
	}



[Edited by - Tertsi on October 26, 2004 5:33:19 PM]
Advertisement
It's nt Windows. It's your graphics API, what are you using?
For example, assuming you are using DirectX::DirectDraw7
lets say your in 1024x768x32 mode and you game
changes the display mode to 640x480x24 when initialized.
Then you decide to alt+tab out of the window to go do
whatever. When you leave the window your display settings are changed back to 1024x768x32 and all of your DirectDraw surfaces are lost and must be restored and anything drawn to them
must be redrawn because of the resolution change. To restore them simply call IDirectDrawSurface7::Restore on all the surfaces you have created and then redraw your sprites, etc..
Hopes this helps.

[Edited by - Shadow Wolf on October 24, 2004 6:19:35 AM]
Alright it should now work, i'll test it later and come back if I have any problems.
EDIT: Now I have a new problem.

[Edited by - Tertsi on October 24, 2004 7:33:29 AM]
Bump.
Quote:Original post by Tertsi
When I alt + tab out of my fullscreen DirectX 9c, D3D windows game and try to put the focus back to the game it won't automatically switch to a full screen window and the following code crashes. So how to handle this problem?

*** Source Snippet Removed ***


It is not that code that causes the crash. The crash comes from you not having released everything that is not managed or in video memory. For instance, vertex buffers, textures, etc...

Putting things as managed when loading or creating etc... saves you from many of these problems. There are other things that also needs attention, such as use of the d3dx writing capabilities. They need to be handled properly (see the documentation in the SDK)

Furthermore you cannot simply reset, you need to handle the the cases properly before it can be reset (i.e: D3DERR_DEVICELOST & D3DERR_DEVICENOTRESET) First you handle D3DERR_DEVICELOST while it cannot reset (basically because you are not in the app at the moment) when it is ready to be reset (D3DERR_DEVICENOTRESET) then you reset. Make sure that you have a valid creation struct in case you need to recreate the setup from the beginning (it happens) should reset fail. Then you must release everything and basically recreate the whole thing. If you are ingame you can store current data and return once everything is restored.

You may have to reload textures, create buffers etc... again depending on what you do.
No no no no! :)
I strongly suggest you take a shot at it though ;)
Because sooner or later it will be a source of annoyance.
Just try and put everything you can as managed (D3DPOOL_MANAGED)
it will take care of these things for you. Then you only need
to think about restoring renderstates, handle the occasional
DrawText function(if you use it, etc...) much less trouble in the long run.
No no no no! :)
Okay it just happens that my textures are already in the managed pool. So basically I'll have to recreate all surfaces and reset the renderstates after the Reset() call. But how can I obtain the textures without reloading them, or are they still there and don't need reloading if I'm using the managed pool? Do I also have to recreate the device, or just the render states and d3dpp? What exactly must I do with the sounds which are loaded in RAM? Must I release and reload them all? If you could post your own code which handles the issue that would be awesome.

Managed textures will restore themselves. Sound is not affected (or at least they shoudn't be. I never had a problem with that.) Just remember to stop playing sounds or it can be kind of annoying ;)
When switching screenmodes (which is likely to happen when you are using ALT-TAB) the renderstates might also be changed by windows(or whatever is taking focus) thus you might have to set them back when you come back into the application. A few redundant renderstates is not such a big deal.

A small note: If you are using a fullscreen window (i.e not a dedicated directx fullscreen) then alt-tab is not so much of a problem since it handles like a window. The drawback is that directx in windowed mode is slower(quite significantly on some cards).

Back on track.
Basically what you might have to restore is. D3DXFONT(handle the OnLostDevice, OnResetDevice & Release situations), recreate rendertargets and any other buffers that reside on the card and not in memory. Remember that everything which is stored in systemmem or is managed do not need to be recreated since they are backed by a copy in normal RAM. Costly in memory terms yes, but well worth the benefits. VertexBuffers are easily forgotten about but should be recreated or they most likely will cause trouble.

I basically do this:

checkfor the TestCooperativeLevel, if it is not OK then I will not enter the drawing part of the program:

(pseudocode. note that I included a test for the dxdevice because
when you quit the application it might take a second run before it comes through. And what happens if the device is released ? well, you get the idea... ;))

if(dxdevice && Test... == OK)
{
...doing my thing..
}
else
{
...let us handle the problems..
(D3DERR_DEVICELOST etc...)
}

That is basically it.
No no no no! :)
Thank you, that wasn't so hard after all and I got it working. Great!

I encountered a few problems though. One is that now when you press a key after coming back to the game it will play the annoying windows error sound, the DInput works though. Also when you try to quit the game it crashes weirdly.

Here's the code:

	HRESULT hr;	hr = MGEgfxref->lpdevice->TestCooperativeLevel();	if(hr != D3D_OK)	{		if(hr == D3DERR_DEVICENOTRESET)		{			for(int i = 0; i < 12; ++i) if(MGEgfxref->font) delete MGEgfxref->font;			MGEgfxref->lpdevice->Reset(&MGEgfxref->d3dpp);			MGEgfxref->d3dxsprite->OnResetDevice();			MGEgfxref->d3dxline->OnResetDevice();			MGEgfxref->d3dpp.BackBufferCount = 1;			MGEgfxref->d3dpp.BackBufferHeight = windowheight;			MGEgfxref->d3dpp.BackBufferWidth = windowwidth;			MGEgfxref->d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;			//	Set render states.			MGEgfxref->lpdevice->SetRenderState(D3DRS_LIGHTING, false);			MGEgfxref->lpdevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);			MGEgfxref->lpdevice->SetRenderState(D3DRS_ZENABLE, false);			MGEgfxref->lpdevice->SetRenderState(D3DRS_ZWRITEENABLE, false);			MGEgfxref->lpdevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);			MGEgfxref->lpdevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);			MGEgfxref->lpdevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);			//	Setup texture stages.				MGEgfxref->lpdevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);			MGEgfxref->lpdevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);			MGEgfxref->lpdevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);			MGEgfxref->lpdevice->SetVertexShader(NULL);			// Setup fonts. (the normal init code is here)                        // LOAD AND SET THE CUSTOM CURSOR the normal init code is here)				}		else if(hr == D3DERR_DEVICELOST)		{			MGEgfxref->d3dxsprite->OnLostDevice();			MGEgfxref->d3dxline->OnLostDevice();			MGEsoundref->stop(0);			MGEsoundref->stop(1);			return;		}

This topic is closed to new replies.

Advertisement