View Full Version : DirectDraw: obscured window crashes computer
Ronkes
06-29-2004, 04:16 AM
I'm writing a 2D engine and I have run into a peculair problem which I can't seem to solve. I use DirectDraw to render my game to a window. It runs fine for the most part, but every time I completely obscure the window's client area (e.g. with another window), my computer freezes. Sometimes the video card reverts to an ultra-low resolution (what seems like 320 x 200 with 16 colours) and I get a message I need to restart. Most of the times, though, I need to reset my computer the hardware-way. Once, after restarting, I got a message that the problem was caused by the video driver. I haven't seen the message a second time, though. I run Windows XP.
I have tested this on another computer. It doesn't crash, but it runs painfully slow when the window is obscured. The computer has a different video card and runs a different OS (Windows 2000). Obviously, slowing down is preferable to crashing, but both computers show a similar problem, so it must be a bug in my code.
What I do in my code comes down to this. I create a primary surface and an offscreen surface. I attach a clipper to the primary surface with the following code (error checking omitted for brevity).
_directDrawObject->CreateClipper(0, &_primaryClipper, 0);
_primarySurface->SetClipper(_primaryClipper);
_primaryClipper->SetHWnd(0, _windowHandle);
_primaryClipper->Release();
Everything gets rendered to the offscreen surface and then I copy the entire offscreen surface to the primary surface. The offscreen surface is not necessarily of the same size as the primary surface; it gets scaled during the blit, but when I disable scaling the error still occurs.
RECT source = {0, 0, GetWorkAreaWidth(), GetWorkAreaHeight()};
POINT screen = {0, 0};
ClientToScreen(_windowHandle, &screen);
RECT destination = { screen.x, screen.y, GetWidth() + screen.x, GetHeight() + screen.y };
HRESULT result = _primarySurface->Blt(&destination, _backBuffer, &source, DDBLT_WAIT, 0);
The problem only occurs when the client area is fully obscured. A partly obscured client area works just fine. Anybody any idea how to solve this problem?
Wayward
06-29-2004, 04:38 AM
I setup the clipper same as you. My blit is slightly different:
RECT windowRect;
GetClientRect ( hWnd, &windowRect );
ClientToScreen( hWnd, ( LPPOINT )&windowRect );
ClientToScreen( hWnd, ( LPPOINT )&windowRect + 1 );
hr = mPrimarySurface->Blt( &windowRect, mBackSurface, NULL, DDBLT_WAIT, NULL );
gilzu
06-29-2004, 06:59 AM
use this instead:
_directDrawObject->CreateClipper(0, &_primaryClipper, 0);
_primaryClipper->SetHWnd(0, _windowHandle);
_primarySurface->SetClipper(_primaryClipper);
first mistake: clipper should be initialized before setting as the primary clipper. nothing major, but still
second mistake: releasing the clipper, and letting Direct Draw use it. When direct draw uses the pointer you supplied to the clipper, it reaches a released memory which cause Access Violation Error (or if you use VC++, that annoying memory cannot be "write"/"read")
third: i don't know if you did it to shorten the code, but you must redraw the window after each time its being resized/moved/covered by other windows. use the WM_PAINT or a timer interval of <1sec
Hope it helped, when you'll find out the cause, make sure to write what it was for future reference (other people who get stuck with the same problem).
Good luck!
Originally posted by Wayward
ClientToScreen( hWnd, ( LPPOINT )&windowRect + 1 );
Wayward, i am bit sleepy today so maybe i can't see the obvious.. but what's that supposed to do? surely the "+1" is a typo?
Wayward
06-29-2004, 07:26 AM
Haze, the +1 in pointer arithmetic means +1 POINT, i.e. get the 2nd point. It's treating a RECT structure as two POINT structures.
Gilzu, attaching the clipper to the primary surface (SetClipper) increases its reference count, so the release doesn't actually release it. This 'trick' means that the clipper will be automatically released when the primary surface is released.
Ronkes
06-29-2004, 07:58 AM
Thanks for the input guys, but the problem remains.
Gil, the documentation seems to suggest that the calling order of SetHWnd and SetClipper is not important as long as you blit after the two calls, but the docs are a bit fuzzy on this one. Wayward already explained the call to release the clipper. Also, clipping works just fine as long as the entire window isn't obscured. Of course this did not stop me from trying your suggestions. I switched the two calls around and removed the release, but to no avail. O, and yes, I redraw the window regularly. I just left that part out for brevity, but thanks for the suggestion.
Wayward, your blitting code does about the same as mine minus the stretching. I pasted it into my program and tried it, but no dice. It did make me think, though: you set up the clipper the same way and you blit practically the same way and (I assume) your code works just fine. So the problem lies somewhere else.
After some testing I discovered the following. If I don't render anything to the back buffer and I don't perform the flip, all is well. No surprise here. If I don't render to the back buffer and I do perform the flip, all is still well. This suggest that there is something wrong with rendering to the back buffer when the primary surface gets fully clipped. Now, if I do render to the back buffer, but I don't perform the flip, the problem actually gets worse. My computer slows down/crashes whether the window is clipped or not. That I find surprising.
I also noticed that running the game loop - without actually performing game logic or drawing anything - takes about 50% of CPU time, regardless of the CPU speed it seems. Is this normal or should it be much less?
I will second the comment about waiting until the end of the program to release the clipper object (reguardless of how it was created): the clipper is still in use.
Are you sure 'GetWorkAreaWidth/Height' are always returning valid values?
Do you check the return value of EACH and EVERY routine (Win32 and DirectDraw APIs) used in the creation, usage and destruction of your main window and directdraw objects?
Lastly, could you make a very simple program that exibits the error and post the source code, so we can see excatly how you're setting things up? There are so many variables involved, that without seeing the code you're trying to run, offering possible solutions is just shooting in the dark.
David
Ronkes
06-30-2004, 12:32 AM
Are you sure 'GetWorkAreaWidth/Height' are always returning valid values?
Yes, I am. But as I pointed out, the problem still exists if I blit without stretching. Since the problem only occurs when the window is fully obscured, it doesn't seem likely to me that I use illegal rectangles; that would cause problems in other situations, too.
Do you check the return value of EACH and EVERY routine (Win32 and DirectDraw APIs) used in the creation, usage and destruction of your main window and directdraw objects?
Yes, I do. Even the simplest Win32 API call gets checked for returned error codes. Since I'm building a framework that I intend to reuse, I am putting a lot of effort in things like error handling and memory management. I have to admit though, that at the moment I consider it a possibility that my bug is caused by leaking video memory. Anyone know if there is a simple way to check available video memory on the fly?
Lastly, could you make a very simple program that exibits the error and post the source code, so we can see excatly how you're setting things up?
I'll try to come up with something manageble today. I should isolate the code anyway if I ever want to find the solution; I've been putting it off for far too long now. The code might get rather long, in which case I'll just post it on my website.
Ronkes
06-30-2004, 03:00 AM
I stripped down my code to try and isolate the problem. You can find the result here http://www.ronkes.nl/bug.zip (6 KB). Evidently, everything outside the Graphics class is just to keep things running. Your help is much appreciated.
When compiling and running your code on my PC, there where no crashes, but I experienced the slowness or sluggish-ness you mentioned when the window was fully obsured and also the high CPU usage.
I modified the code slightly to remedy this.
Changed your message pump from this:MSG message;
if (PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}to this:MSG message;
while( PeekMessage( &message, NULL, 0, 0, PM_REMOVE ) )
{
if( message.message == WM_QUIT ) break;
TranslateMessage( &message );
DispatchMessage( &message );
}
Sleep( 1 );The while processes all messages that have occured since the message pump was last called, not just one, as is currently being done.
This may seem unnessasary when your game is running at a very high frame rate, but if your frame rate should drop, your message queue could become flooded with pending messages and your window become unresponsive.
The call to Sleep helps keep the rest of the system responsive by preventing your app from hogging all the CPU time. Even though your not doing anything in the main loop, the looping itself is demading the CPUs almost constant attention.
Putting the Sleep in your main loop will limit your updates-per-second to a max of 1000, another option would be (and you may already be doing this) is to detect when your app has been switched away from (i.e. loses focus) and then conditionally add the Sleep.if( !_has_focus ) Sleep( 1 );Making these two changes caused your code to run fine: No slowdowns or unresponsive-ness and low (~0%) CPU usage.
HTH,
David
Wayward
06-30-2004, 11:36 PM
I get the same results as DGuy.
It was very sluggish (regardless of whether the window was obscured) until I pasted in DGuy's message pump. I tried obscuring the window with both versions but it wouldn't crash. Does it crash instantly, or do you have to let the unprocessed windows messages build up? I couldn't spot anything unusual in the rest of the code either.
Donavon Keithley
07-01-2004, 11:20 AM
This sounds like the same issue from this thread:
http://discuss.microsoft.com/SCRIPTS/WA-MSD.EXE?A2=ind0306D&L=DIRECTXDEV&P=R8246&I=-3
The problem was an Nvidia driver bug, fixed in 44.03. So you might just try new drivers. If you're concerned about this happening to customers who are running on old drivers, the thread describes a workaround. Basically you use GetRandomRgn to determine if your window is completely obscured and if so, Sleep through it.
Also, I would recommend Sleep(0) to play nice with other apps. If no other threads are scheduled for a timeslice, you get to keep the CPU and there's no context switch.
Ronkes
07-02-2004, 03:32 AM
David, you are my hero. :) I just tried your changes and it works like a charm. Also a big thank you to everyone who took the trouble to help me out. I can now move on to adding new features to my framework once again.
The crash only happens on one of my PCs. Apparently that PC just has a piece of hardware or a driver that couldn't quite handle the situation properly.