Sign in to follow this  
Reepal

ALT_TAB works at all resolution but NOT desktop resolution?!

Recommended Posts

Let me start off with a WTF? My application can ALT+TAB in and out of all resolutions except the current desktop resolution. It took me forever to figure out that this was the problem, but I tested by changing my desktop resolution etc, and this is certainly the problem. FYI, I can change resolution TO desktop resolution and it works fine as well! Basically this paragraph was to say that I don't think this problem has to do with my releasing/recreating everything. When I ALT+TAB while my app is at the current desktop resolution, it seems like it ALT+TABs out and back in really quick. Except its not really ALT+TAB'd back in. My mouse gets messed up skipping, and if I hit ALT+TAB again I see the ALT+TAB selection boxes moving by THROUGH my app... I used the WM_ACTIVATE case to see what's going on, and it is actually registering an ALT+TAB OUT AND BACK IN! SOMEONE HELP PLEASE! [Edited by - Reepal on September 4, 2004 5:35:06 AM]

Share this post


Link to post
Share on other sites
Nope, nothing interesting. But I have a feeling that it's either DirectInput (mouse and keyboard (am using unsigned btw)) or something with the Window Class settings.

FYI,
	
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
wc.lpszMenuName = NULL;
wc.lpszClassName = cAppName;

// Now we need to register this class with Windows.
RegisterClassEx(&wc);

// Create the window based on the previous class.
hWnd = CreateWindowEx(WS_EX_TOPMOST, // Advanced style settings.
cAppName, // The name of the class.
cAppName, // The window caption.
WS_POPUP | WS_SYSMENU | WS_VISIBLE, // The window style.
CW_USEDEFAULT, CW_USEDEFAULT, // The starting window position.
640, 480, // The initial window size.
NULL, // Handle to parent window.
NULL, // Handle to menu.
hInstance, // Handle to app's instance.
NULL); // Advanced.



Nothing unusual in there right? I tried removing all style settings as well, but still no go. I'm leaning towards DirectInput somehow getting mixed in, but I just tried destroying the DI objects and recreating them on ALT+TAB and still no go!

Share this post


Link to post
Share on other sites
For you WNDCLASSEX styles, try using CS_CLASSDC. And for your CreateWindow styles, try using WS_OVERLAPPEDWINDOW. These are the settings that I use (without any problems) and that the SDK tutorials use.

Share this post


Link to post
Share on other sites
Used OverlappedWindow and CS_CLASSDC, and still same results.

Also, when I ALT+TAB at the Desktop resolution, I get a small rectangle in the center of the screen for a split second that flashes away...

Another very interesting thing I noticed..my APP keeps running even as its ALT+TABBED out. Like, my Render function keeps calling since my ValidateDevice is NOT detecting failure in the TestCooperativeLevel. As I said before though, this doesn't happen at any other resolution except the same resolution as the desktop is in, so I don't think its actually a problem with the ValidateDevice function. Interesting to note though, when it does this, I can't use my DirectInput devices anymore, but Windows GetAsyncKeysTATE function still works!? Again, at any other resolution, on ALT+TAB my DirectInput devices still come back just fine. =\

Share this post


Link to post
Share on other sites
I think the device gets lost when I alt+tab OUT..but it automatically ALT+TAB's back in. (I'm not sure how or why it does that). But once it ALT+TAB's back in from Desktop Resolution (i obviously don't have control over this since it does it automatically), it knows it has the device back (because it keeps rendering, I have a sprite being rotated in the background which gets updated every frame).

Problem is that I can't ALT+TAB out and get it to STAY out at Desktop resolution.

And for whatever reason, when it automatically ALT+TAB's back in AT DESKTOP RESOLUTION ONLY, everything gets released/recreated, but my DirectInput doesn't work anymore even though it should have been destroyed and recreated as well. I also tested this withOUT DirectInput completely, and it does the same thing, so it's not DirectInput causing the problem.

Share this post


Link to post
Share on other sites
This sounds suspiciously similar to a problem we encountered on Deus Ex 2 and Thief 3.

We found that Alt-Tabbing away from full-screen mode, but only on Windows XP and only when game resolution was the same as the desktop resolution, had weird problems. Never happened on other OSes, never happened when the game's video mode differed from the desktop's, made no difference whether it was an ATI card or an NVIDIA card or one driver version versus another.

In our case, when you Alt-Tabbed away, one of two things would happen. Half the time the 3D hardware would continue rendering the end of the previous frame, even though GDI had taken over and redrawn the desktop. Typically you would see parts of the HUD (since they were drawn last) on top of Visual Studio or whatever else you had on the desktop. GDI and the 3D hardware were basically stepping on one another.

The other half of the time you would find yourself looking at the previous frame. GDI and User32 believed that they were in control of the primary buffer -- the cursor would change as you moved it around, you could click and everything, you just couldn't see anything because you were actually looking at the game's previous back buffer. And this is because GDI was drawing to the front buffer, which was not visible!

It's 50-50 because we had two buffers. Half the time you were looking at the front buffer and half the time you were looking at the game's back buffer.

This is not rational, understandable behavior, even for a buggy D3D app -- unless perhaps you're interfering with the runtime's view of the message queue, which can only happen by subclassing the focus window, which we were definitely not doing. And even then, why would it be OS- and resolution-specific?

But we were unsuccessful in pleading the case that this might, just might possibly be a runtime bug, not an app bug. And so you'll note that the release notes in these games say that Alt-Tabbing away from the game is not supported.

I'm not sure, but from the description I'm inclinded to think that you've encountered the same bug. And while I can't be 100% sure, I'm 99.9% convinced that this is not an app bug, it is a runtime or OS bug. And I absolutely DO NOT make that sort of judgment lightly. I spent hours and hours trying to narrow this down as our bug before I redirected my suspicion.

You can report this to directx@microsoft.com as a bug, but be prepared to go through the same rigamarole -- are you doing this, are you doing that, and eventually they might give up and stop responding. Perhaps if you include my post here, it will lend some credibility to the possibility -- however remote it may seem to them -- that this is a runtime/OS bug.

If you can send me the code, I'll be happy to take a look and see if it's the same kind of issue. It certainly sounds like it.

Share this post


Link to post
Share on other sites
Quote:
Another very interesting thing I noticed..my APP keeps running even as its ALT+TABBED out. Like, my Render function keeps calling since my ValidateDevice is NOT detecting failure in the TestCooperativeLevel.

Yes, this is even more consistent with my experience. Under these circumstances, the device would mysteriously not be lost.

There definitely seems to be some confusion here between D3D and GDI as to who owns the card. And that is NOT an app bug.

Share this post


Link to post
Share on other sites
Okay, after more testing, I agree. This is not an app bug.

I put in a generic pause call of 50ms in my ValidateDevice function. Although this severely slows down execution speed, when I ALT+TAB out, I can SEE my desktop for a brief second before it ALT+TAB's back in. According to the debugstrings I'm outputting while running, TestCooperativeLevel is returning true on ALT TAB OUT! (of course it doesn't do that when it's not the desktop resolution)

But there must be a way to get around this or fix this right? Unreal Tournament 2004 has no problem! =\

ANOTHER INTERESTING THING I FOUND! If Desktop is at 1280x1024x16, ALT+TAB at 1280x1024x32 works. If Desktop is at 1280x1024x32, ALT+TAB at 1280x1024x32 does NOT work! This shed some light as to what the problem might be?

I'm also wondering..can I CHANGE the desktop to be say 1280x1024x16 if my app is running at 1280x1024x32? That way the user won't really notice his desktop is changed =D and then I could change it back on exit of the app? Or is there a better work around?

Share this post


Link to post
Share on other sites
Quote:
Original post by Reepal
ANOTHER INTERESTING THING I FOUND! If Desktop is at 1280x1024x16, ALT+TAB at 1280x1024x32 works. If Desktop is at 1280x1024x32, ALT+TAB at 1280x1024x32 does NOT work! This shed some light as to what the problem might be?


That would make sense, because color depth is part of the diplay mode. The problem certainly lies in the fact that the driver doesn't need to change monitor resolution.

Share this post


Link to post
Share on other sites
Quote:
But there must be a way to get around this or fix this right? Unreal Tournament 2004 has no problem! =\

I don't know. There seems to be some other unknown factor that triggers this and I have not been able to figure out what it is. And I've tried! [smile]

Share this post


Link to post
Share on other sites
Quote:
If Desktop is at 1280x1024x16, ALT+TAB at 1280x1024x32 works. If Desktop is at 1280x1024x32, ALT+TAB at 1280x1024x32 does NOT work!

This is consistent with what I encountered. The desktop had to be in exactly the same video mode as the game, X times Y times Bit Depth. If a video mode switch was necessary on lost device, there was no problem. If not, you're screwed.

In the mode-switch cases, the runtime seems to regain control of the video hardware and properly restore it to GDI. In the other case it seems to fail to do so, such that the 3D hardware is still running when GDI thinks its been given back the device, and half the time the front buffer is not restored for GDI's purposes.

Share this post


Link to post
Share on other sites
Funny, that is not consistent with what I have seen. After reading this thread, I just tested it, setting my desktop to 1024x768x32 and running my game in fullscreen (it defaults to 1024x768x32). I was able to Alt+Tab out and back without a problem, both in identical resolutions and in different ones.

Try it for yourself and let me know the results. Unzip it preserving the subfolder names, then run "Quest.exe -fullscreen" to run it in fullscreen mode. It runs in 1024x768x32, though you can change this... for example, to run at 1280x1024x16, run "Quest.exe -w 1280 -h 1024 -bpp 16 -fullscreen".

...actually, after further testing, it seems that sometimes if I run it at a different resolution from my desktop, and Alt+Tab away, a black square the size of the game's resolution appears on the desktop. It blocks the view of the other windows onscreen, though it's drawn behind the mouse cursor and start menu. Clicking it re-focuses the game, and clicking any program on the task bar just brings me back into the game. Weird.

I'm running Windows XP but am using the 3rd-party Omega drivers for my Radeon 9600. Anyway try running my game, let me know what happens please.

Share this post


Link to post
Share on other sites
I've had similar problems with the desktop not redrawing itself in the past, although they were probably just bugs in my own code (I had to recreate direct draw when switching modes, otherwise I'd just get a black screen).
Wouldn't the easy workaround be to force the desktop to redraw itself after a second or so?


// this worked for us anyway..
void InvalidateDesktop()
{
HWND wnd;

wnd = GetDesktopWindow();
RedrawWindow(wnd, NULL, NULL, RDW_INVALIDATE | RDW_VALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
}



edit: Oops.. I think I misunderstood your problem, should've read the thread more closely.

Share this post


Link to post
Share on other sites
CGameProgrammer: I get the black box you described both at desktop resolution and not desktop resolution. Although, if you ALT+TAB a couple of times, it goes away =\. FYI, I'm running Windows XP with a GeForce 4 Ti 4200.

Share this post


Link to post
Share on other sites
Quote:
Original post by CGameProgrammer
Funny, that is not consistent with what I have seen. After reading this thread, I just tested it, setting my desktop to 1024x768x32 and running my game in fullscreen (it defaults to 1024x768x32). I was able to Alt+Tab out and back without a problem, both in identical resolutions and in different ones.

This is not surprising. It's not 100% reproducible across code bases. There is some other factor involved which I've not been able to isolate. In fact to this date Reepal's is the only other repro that I've heard of. Ion Storm's engine was far too complex to realistically whittle it down and figure out what that mysterious X factor is.

Share this post


Link to post
Share on other sites
Pretty disgusting..

In my render function I added in a little check:

if( g_hWndMain != GetForegroundWindow() )
{
OutputDebugString("Window is NOT in FOCUS!\n");
ValidateD3DDevice(fpRestoreGraphics);
return E_FAIL;
}


And sure enough, it says my Window is NOT in FOCUS! But the Debug output in my ValidateD3DDevice function from TestCooperativeLevel is pumping out D3D_OK!!!

So of course this leaves me at the point where, I've at least returned to the desktop, but the app still runs in the background! So I guess I can hack my way through this by putting the GetForegroundWindow check in my ValidateD3DDevice function and using a static bool to track its status (and know when to release/recreate everything) BUT I WANT TO DO IT LEGIT DAMN IT!

Windows knows its not in the foreground, why doesn't TestCooperativeLevel know?!

Share this post


Link to post
Share on other sites
well DUH.
if your window's not in focus and
TestCooperativeLevel is returning okay,
then your window's NOT in focus,
and your device is OKAY.
which guy assumed that the device is LOST
when you alt-tab in windowed mode?!

that is COMPLETELY UNTRUE.

when in windowed mode, the same device is used for
all of the windows. and also, you may NOT change
the color settings or resolution when you are in
windowed mode.

it is definitely NOT a run-time error.
the program is doing what you programmed it to do.

and dude your last post was so ironic.
you wanted to steal the device because your window is not
in focus and has control of the device?

the problem here is not your device. it's in the code.

Share this post


Link to post
Share on other sites
I tried alt-tab with my app, changing resolutions, minimizing, bit levels etc... anything i could think of and every time my app came back just as easily. I am fairly certain you are doing something wrong in the code. And mine is working just as well in fullscreen and windowed mode.

Are you sure you are releasing everything correctly. Calling the *on lost* for text or sprites (or whatever you may be using). Releasing resources before recreating etc...?

Share this post


Link to post
Share on other sites
THANKS FPROTO. MAJOR help in tracking this down.


This is my initialize Direct3D function:

HRESULT InitDirect3D(int iScreenWidth, int iScreenHeight, BOOL bWindowed, D3DFORMAT d3dFormat)
{
// Structure to hold information about the rendering method.
D3DPRESENT_PARAMETERS d3dpp;

// Structure to hold information about the current display mode.
D3DDISPLAYMODE d3ddm;

// Initialize our structures to zero.
ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));
ZeroMemory(&d3ddm, sizeof(D3DDISPLAYMODE));

if(g_pD3D)
{
g_pD3D->Release();
g_pD3D=0;
}

// Acquire the pointer to the Direct3D interface.
g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
if(!g_pD3D)
{
// We couldn't acquire a pointer to the interface.
OutputDebugString("Could not acquire IDirect3D8 pointer.\n");
return E_FAIL;
}

if(g_pDevice)
{
g_pDevice->Release();
g_pDevice=0;
}

// Get the settings for the current display mode.
if (FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm)))
{
// We couldn't get the display adapter information.
OutputDebugString("Could not get display adapter information.\n");
return E_FAIL;
}
/*ZeroMemory(&d3ddm, sizeof(D3DDISPLAYMODE));
// Get the settings for the current display mode.
if (FAILED(g_pDevice->GetDisplayMode(&d3ddm)))
{
// We couldn't get the display adapter information.
OutputDebugString("Could not get display information.\n");
return E_FAIL;
}*/


// Now we have to fill in our presentation parameters.
// Width of the backbuffer.
d3dpp.BackBufferWidth = iScreenWidth;
// Height of the backbuffer.
d3dpp.BackBufferHeight = iScreenHeight;
// The format of the backbuffer.
d3dpp.BackBufferFormat = bWindowed ? d3dFormat : d3ddm.Format;

// The number of backbuffers.
d3dpp.BackBufferCount = 1;
// The type of multisampling.
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
// The swap effect.
d3dpp.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC;

// The handle to the window that we want to render to.
d3dpp.hDeviceWindow = g_hWndMain;
// Windowed for full screen.
d3dpp.Windowed = bWindowed;

// Let Direct3D manage the depth buffer.
d3dpp.EnableAutoDepthStencil = TRUE;
// Set the depth buffer format to 16 bits.
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

// Use the default refresh rate available.
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

// Present the information as fast as possible.
d3dpp.FullScreen_PresentationInterval = bWindowed ? 0 : D3DPRESENT_INTERVAL_ONE;
// Allow the backbuffer to be accessed for 2d work.
d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;

// Acquire a pointer to the Direct3D device.
if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWndMain,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pDevice)))
{
// We couldn't create the render device.
OutputDebugString("Could not create the render device.\n");
return E_FAIL;
}

// Save our device dimensions for validation.
g_iDeviceHeight = iScreenHeight;
g_iDeviceWidth = iScreenWidth;

// Save our presentation parameters for validation.
g_d3dPresParams = d3dpp;

// Clear the back buffer.
//g_pDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(100, 100, 100),
// 1.0f, 0);

// Get a pointer to our backbuffer.
if (FAILED(g_pDevice->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &g_pBackSurface)))
{
// We just can't get our back buffer.
OutputDebugString("Couldn't initialize our backbuffer.\n");
return E_FAIL;
}

g_pDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

g_pDevice->SetVertexShader(RIPFVF);

return S_OK;
}


Here's the interesting changes:

ORIGINALLY I HAD:

d3dpp.BackBufferFormat = bWindowed ? d3ddm.Format : d3dFormat;



CHANGED IT TO:

d3dpp.BackBufferFormat = bWindowed ? d3dFormat : d3ddm.Format;



Now when I ALT+TAB at all resolutions it works. (Kinda screwy results if the desktop started at 16bit color though since its using the Desktop DisplayMode Format to base off fullscreen). If I change resolution though (basically call this function again after shutting down Direct3D) to another resolution and change back to desktop resolution again I have the same problem as before. So I think saving the desktop's format and restoring THAT on EVERY InitDirect3D call will work fine.

But is there a better way to go about doing this?
Obviously my calls are weird now, but I would have liked to use something along the lines of

// For Fullscreen
r=InitDirect3D(g_iDeviceWidth, g_iDeviceHeight, FALSE, D3DFMT_A8R8G8B8);
//FMT will be ignored now and use adapter format instead

// For Windowed
r=InitDirect3D(g_iDeviceWidth, g_iDeviceHeight, TRUE, D3DFMT_UNKNOWN);
//FMT must be specified now

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