Skip to main content
GameDev.net gamedev.net
🔒 Locked

Fullscreen Problem

Started by mdl731 Jun 7, 2011 at 8:19 PM 9 replies 2.5k views
Original Post
mdl731
mdl731
Hello people!

I've been working on a 2D platform game with my DirectX and C++ engine, and It's coming along nice (considering that I just learned DirectX 5 weeks ago). Anyway, now I'm trying to implement the toggle fullscreen at runtime functionality on the engine but I'm having some problems...

This is the code I'm using:



void Engine::ToggleFullScreen(bool value)
{
DWORD dwStyle, dwExStyle;

D3DDISPLAYMODE dm;
this->mD3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dm);

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = !value;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.hDeviceWindow = mWindowHandle;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferFormat = dm.Format;

if(value)
{
d3dpp.BackBufferWidth = dm.Width;
d3dpp.BackBufferHeight = dm.Height;

dwStyle = WS_POPUP;
dwExStyle = WS_EX_APPWINDOW;

ShowCursor(false);
}
else
{
d3dpp.BackBufferWidth = gEngine->GetScreenWidth();
d3dpp.BackBufferHeight = gEngine->GetScreenHeight();

dwStyle = WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX;
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;

ShowCursor(true);
}

mSpriteHandler->OnLostDevice();

HRESULT hRes = mDevice->Reset(&d3dpp);

mSpriteHandler->OnResetDevice();

SetWindowLong(mWindowHandle, GWL_STYLE, dwStyle);
SetWindowLong(mWindowHandle, GWL_EXSTYLE, dwExStyle);
ShowWindow(mWindowHandle, SW_SHOW);

}



When i press alt+enter the window style is the only thing that changes, the game keeps running and accepting input but the game screen is frozen.

Anyone knows what am I doing wrong????
katsh
katsh
your have to re-load textures ,mesh, to a gpu , when they gone from its video memory cause of alt-tab,alt-enter which over write its video memory.
21st Century Moose
21st Century Moose
You should be allocating most resources in D3DPOOL_MANAGED so releasing and recreating them shouldn't be needed. I'm willing to bet that your SetWindowLong and ShowWindow calls are causing the device to become lost, so maybe move them to before your reset call. Also, if you're not checking the return HRESULT from your Present call for D3DERR_DEVICELOST, you should do so. There are plenty of threads on here about the best way to handle that situation (and if you're going fullscreen you'll definitely need to at some time). I'll see if I can dig one up later on if you want.
Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls. 
mdl731
mdl731

your have to re-load textures ,mesh, to a gpu , when they gone from its video memory cause of alt-tab,alt-enter which over write its video memory.


All the textures for the sprites I'm using are D3DPOOL_MANAGED, am I supposed to release and recreate them like the D3DPOOL_DEFAULT textures?
mdl731
mdl731

You should be allocating most resources in D3DPOOL_MANAGED so releasing and recreating them shouldn't be needed. I'm willing to bet that your SetWindowLong and ShowWindow calls are causing the device to become lost, so maybe move them to before your reset call. Also, if you're not checking the return HRESULT from your Present call for D3DERR_DEVICELOST, you should do so. There are plenty of threads on here about the best way to handle that situation (and if you're going fullscreen you'll definitely need to at some time). I'll see if I can dig one up later on if you want.


I'm checking now the HRESULT codes of my RenderStop function:



int Engine::RenderStop()
{
if(this->mDevice == 0)
return 0;
if(this->mDevice->EndScene() != D3D_OK)
{
Message("Device EndScene Failed");
return 0;
}

HRESULT hRes = this->mDevice->Present(0,0,0,0);
if(hRes == D3DERR_DEVICELOST)
{
Message("Device Lost");
return 0;
}

return 1;
}



And the problem seems to be in the EndScene() call, i have no idea why though.....

Edit: I just checked again and the problem is at the Reset() call. What can cause Reset() to fail??
TomKQT
TomKQT

Edit: I just checked again and the problem is at the Reset() call. What can cause Reset() to fail??


You'll most probably get a direct answer to that question by using the Debug D3D runtimes. (DirectX Control Panel -> Use debug version of Direct3D).
mdl731
mdl731

[quote name='mdl731' timestamp='1307481162' post='4820693']
Edit: I just checked again and the problem is at the Reset() call. What can cause Reset() to fail??


You'll most probably get a direct answer to that question by using the Debug D3D runtimes. (DirectX Control Panel -> Use debug version of Direct3D).
[/quote]

Thanks for the reply, that was helpful. I just checked the debug and i get this error when i try to switch to full screen:

Direct3D9: (ERROR) :All user created D3DPOOL_DEFAULT surfaces must be freed before ResetEx can succeed. ResetEx Fails.
Direct3D9: (ERROR) :ResetEx failed and ResetEx/TestCooperativeLevel/Release are the only legal APIs to be called subsequently.

But now I'm confused, because I'm not using any texture or surface on the D3DPOOL_DEFAULT.....

This is how I load and image to a texture for my sprites:


bool Texture::Load(std::string filename, D3DCOLOR transcolor)
{
HRESULT hResult;
D3DXIMAGE_INFO info;

hResult = D3DXGetImageInfoFromFile(filename.c_str(), &info);
if(hResult != D3D_OK)
{
mTexture = 0;
return false;
}

hResult = D3DXCreateTextureFromFileEx(gEngine->GetDevice(),
filename.c_str(), info.Width, info.Height,
1, 0, D3DFMT_UNKNOWN,
D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT,
transcolor, &info, 0, &mTexture);

if(hResult != D3D_OK)
{
mTexture = 0;
return false;
}

mTexture->GetLevelDesc(0, &mDesc);

return true;
}
21st Century Moose
21st Century Moose
There are some other calls that could result in D3D holding on to a default pool surface. One in particular to watch out for is IDirect3DDevice9::GetBackBuffer - the surface it returns will need to be Released. Basically double-check any Get call you might have that gives you a D3D interface as it's commonly your responsibility to Release it when done.
Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls. 
mdl731
mdl731

There are some other calls that could result in D3D holding on to a default pool surface. One in particular to watch out for is IDirect3DDevice9::GetBackBuffer - the surface it returns will need to be Released. Basically double-check any Get call you might have that gives you a D3D interface as it's commonly your responsibility to Release it when done.


Thanks man!, that seemed to be the problem. I was using the IDirect3DDevice9::GetBackBuffer for my TileScroller class and I didn't Release() the surface it returned. Now fullscreen toggle works like a charm. The only problem I have now when I try to atl+tab the program the device gets lost. Any ideas how can I solve that?
21st Century Moose
21st Century Moose
Believe it or not, you've already got over 90% of the code you need for this. You check for a D3DERR_DEVICELOST from your Present call, and if you get one, you handle it something like this (this is just one way I've found that works well for me, there will be others):
  1. Start an infinite loop.
  2. Sleep for a few milliseconds because you don't want to be hogging the CPU while you're not the active app. You might also want to run a Peek/Translate/Dispatch message loop here.
  3. Call IDirect3DDevice9::TestCooperativeLevel; what you do next depends on the HRESULT you get from this call.
  4. If you get D3DERR_DEVICELOST the device is still lost, so let your infinite loop go to it's next iteration.
  5. If you get D3DERR_DEVICENOTRESET the device is ready to be reset. Release your resources and call Reset. Don't recreate anything just yet, we'll be doing that shortly. Just let the loop spin again.
  6. If you get D3D_OK the device reset has completed so recreate your resources and exit the infinite loop.
  7. If you get D3DERR_DRIVERINTERNALERROR or anything else, something bad happened and the only really safe/reliable course of action is to exit the program (with an appropriate error message if you wish).
A common mistake you see is that people try to handle this from WM_ACTIVATE in their window procedure. This is not a good idea as - while it will handle the Alt-Tab case - it won't handle other events that may cause a device to become lost. Microsoft delibrately don't document the full range of events because they quite honestly can't. Anything from power saving, screen savers, another app stealing the focus, the user crashing out of the app or yanking out their gfx card, and a myriad other circumstances could be the cause. The only really safe way is to check the HRESULT of your Present call.
Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls. 
mdl731
mdl731

Believe it or not, you've already got over 90% of the code you need for this. You check for a D3DERR_DEVICELOST from your Present call, and if you get one, you handle it something like this (this is just one way I've found that works well for me, there will be others):
  1. Start an infinite loop.
  2. Sleep for a few milliseconds because you don't want to be hogging the CPU while you're not the active app. You might also want to run a Peek/Translate/Dispatch message loop here.
  3. Call IDirect3DDevice9::TestCooperativeLevel; what you do next depends on the HRESULT you get from this call.
  4. If you get D3DERR_DEVICELOST the device is still lost, so let your infinite loop go to it's next iteration.
  5. If you get D3DERR_DEVICENOTRESET the device is ready to be reset. Release your resources and call Reset. Don't recreate anything just yet, we'll be doing that shortly. Just let the loop spin again.
  6. If you get D3D_OK the device reset has completed so recreate your resources and exit the infinite loop.
  7. If you get D3DERR_DRIVERINTERNALERROR or anything else, something bad happened and the only really safe/reliable course of action is to exit the program (with an appropriate error message if you wish).
A common mistake you see is that people try to handle this from WM_ACTIVATE in their window procedure. This is not a good idea as - while it will handle the Alt-Tab case - it won't handle other events that may cause a device to become lost. Microsoft delibrately don't document the full range of events because they quite honestly can't. Anything from power saving, screen savers, another app stealing the focus, the user crashing out of the app or yanking out their gfx card, and a myriad other circumstances could be the cause. The only really safe way is to check the HRESULT of your Present call.



Thank you so much for the help! Now my game supports atl+tabbing and fullscreen/windowed switch!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.