Thread: [GD-Windows] message pump problem
Brought to you by:
vexxed72
From: Marin K. <m.k...@in...> - 2003-11-24 09:18:34
|
[sorry, this may be posted twice, I posted it from the wrong account and it got stuck for moderator approval] Hi, I was wondering exactly what code you guys use for your message pumps? The thing is that after recent bug hunting I changed the pump to something like while(1) { if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { GetMessage(&msg, NULL, 0, 0); TranslateMessage(&msg); DispatchMessage(&msg); } else { //do game frame here } } , and I've had a support mail from a guy to whom the game stops at the loading screen. After inspecting his logs, it turns out that he's getting an endless stream of WM_PAINT messages, and the game loop stuff never gets executed. I've reread the GetMessage/PeekMessage stuff in MSDN and there's some mumbo-jumbo about WM_PAINT messages not being removed from the queue by GetMessage(), and system issueing WM_PAINT messages when the queue is empty but, frankly, I didn't manage to understand any of it. So, what exactly if the correct way to handle this? I've created a workaround and while it works, it's ugly as hell and I'd rather it's not there at all. Thanks, Marin Kovacic Lemonade Productions http://www.lemonade-p.com |
From: Slava K. <sla...@gs...> - 2003-11-24 09:27:27
|
Hello Marin. INT CD3DApplication::Run ( ){ // Now we're ready to recieve and process Windows messages. BOOL bGotMsg; MSG msg; PeekMessage ( &msg, NULL, 0U, 0U, PM_NOREMOVE ); BOOL bIsIconic =3D FALSE; while( msg.message!=3DWM_QUIT && !m_bStop ){ // Use PeekMessage() if the app is active, so we can use idle time to // render the scene. Else, use GetMessage() to avoid eating CPU time. if( m_bActive ) bGotMsg =3D PeekMessage ( &msg, NULL, 0U, 0U, PM_REMOVE ); else bGotMsg =3D GetMessage ( &msg, NULL, 0U, 0U ); // =E5=F1=EB=E8 =F1=EE=EE=E1=F9=E5=ED=E8=E5 =EF=EE=EB=F3=F7=E5=ED=EE, = =EE=E1=F0=E0=E1=E0=F2=FB=E2=E0=E5=EC =E5=E3=EE if( bGotMsg ){ TranslateMessage ( &msg ); DispatchMessage ( &msg ); }else if( m_bActive && m_bReady ){ // =E8=ED=E0=F7=E5 =F0=E5=ED= =E4=E5=F0=E8=EC =EA=E0=E4=F0 (=E5=F1=EB=E8 =EF=F0=E8=EB=EE=E6=E5=ED=E8=E5 =E0=EA=F2=E8=E2=ED=EE) if( bIsIconic ){ bIsIconic =3D IsIconic(g_hwndMain); Sleep ( 100 ); continue; } bIsIconic =3D IsIconic(g_hwndMain); if( !g_bRenderInactive ){ HWND hActiveWnd =3D GetActiveWindow(); if( hActiveWnd!=3Dg_hwndMain ){ Sleep ( 100 ); continue; } } UpdateEnvironment ( ); if( FAILED(Render3DEnvironment()) ){ SendMessage ( m_hWnd, WM_CLOSE, 0, 0 ); } } } // =E3=EE=F2=EE=E2=EE return (INT) msg.wParam; } ----- Original Message ----- From: "Marin Kovacic" <m.k...@in...> To: <gam...@li...> Sent: Monday, November 24, 2003 11:17 AM Subject: [GD-Windows] message pump problem > [sorry, this may be posted twice, I posted it from the wrong > account and it got stuck for moderator approval] > Hi, > > I was wondering exactly what code you guys use for your > message pumps? The thing is that after recent bug hunting > I changed the pump to something like > > while(1) > { > if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) > { > GetMessage(&msg, NULL, 0, 0); > TranslateMessage(&msg); > DispatchMessage(&msg); > } > else > { > //do game frame here > } > } > > , and I've had a support mail from a guy to whom the game > stops at the loading screen. After inspecting his logs, it turns out > that he's getting an endless stream of WM_PAINT messages, > and the game loop stuff never gets executed. > > I've reread the GetMessage/PeekMessage stuff in MSDN and > there's some mumbo-jumbo about WM_PAINT messages not > being removed from the queue by GetMessage(), and system > issueing WM_PAINT messages when the queue is empty but, > frankly, I didn't manage to understand any of it. > > So, what exactly if the correct way to handle this? I've created > a workaround and while it works, it's ugly as hell and I'd rather > it's not there at all. > > Thanks, > Marin Kovacic > Lemonade Productions > http://www.lemonade-p.com > > > > > ------------------------------------------------------- > This SF.net email is sponsored by: SF.net Giveback Program. > Does SourceForge.net help you be more productive? Does it > help you create better code? SHARE THE LOVE, and help us help > YOU! Click Here: http://sourceforge.net/donate/ > _______________________________________________ > Gamedevlists-windows mailing list > Gam...@li... > https://lists.sourceforge.net/lists/listinfo/gamedevlists-windows > Archives: > http://sourceforge.net/mailarchive/forum.php?forum_id=3D555 |
From: Jon W. <hp...@mi...> - 2003-11-24 17:04:35
|
The problem with this is that Windows expects your message proc to deal with a number of messages in a burst and gets finicky when intervening "other thing" happen. I would re-structure that loop as: while( 1 ) { while( PeekMessage( PM_REMOVE ) ) { if( !IsDialogMessage() ) { TranslateMessage(); DispatchMessage(); } } Draw(); } Further, I'd set up the window proc to also call Draw() from WM_TIMER messages, and possibly from WM_PAINT. That's necessary if you want to keep updating while the user is moving the window. When you get WM_PAINT, you have to BeginUpdate() and EndUpdate() even if you don't do anything else. If you don't begin/end updates, then Windows will not realize that the window has been updated, the dirty region will be non-empty, and you'll get an endless stream of WM_PAINT messages. (Sound familiar? :-) Cheers, / h+ -----Original Message----- From: gam...@li... [mailto:gam...@li...]On Behalf Of Marin Kovacic Sent: Monday, November 24, 2003 1:17 AM To: gam...@li... Subject: [GD-Windows] message pump problem [sorry, this may be posted twice, I posted it from the wrong account and it got stuck for moderator approval] Hi, I was wondering exactly what code you guys use for your message pumps? The thing is that after recent bug hunting I changed the pump to something like while(1) { if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { GetMessage(&msg, NULL, 0, 0); TranslateMessage(&msg); DispatchMessage(&msg); } else { //do game frame here } } , and I've had a support mail from a guy to whom the game stops at the loading screen. After inspecting his logs, it turns out that he's getting an endless stream of WM_PAINT messages, and the game loop stuff never gets executed. I've reread the GetMessage/PeekMessage stuff in MSDN and there's some mumbo-jumbo about WM_PAINT messages not being removed from the queue by GetMessage(), and system issueing WM_PAINT messages when the queue is empty but, frankly, I didn't manage to understand any of it. So, what exactly if the correct way to handle this? I've created a workaround and while it works, it's ugly as hell and I'd rather it's not there at all. Thanks, Marin Kovacic Lemonade Productions http://www.lemonade-p.com ------------------------------------------------------- This SF.net email is sponsored by: SF.net Giveback Program. Does SourceForge.net help you be more productive? Does it help you create better code? SHARE THE LOVE, and help us help YOU! Click Here: http://sourceforge.net/donate/ _______________________________________________ Gamedevlists-windows mailing list Gam...@li... https://lists.sourceforge.net/lists/listinfo/gamedevlists-windows Archives: http://sourceforge.net/mailarchive/forum.php?forum_id=555 |
From: Marin K. <m.k...@in...> - 2003-11-25 06:32:16
|
> The problem with this is that Windows expects your message proc to deal with > a number of messages in a burst and gets finicky when intervening "other > thing" happen. I would re-structure that loop as: > while( 1 ) { > while( PeekMessage( PM_REMOVE ) ) { > if( !IsDialogMessage() ) { > TranslateMessage(); > DispatchMessage(); > } > } > Draw(); > } But it's essentially the same thing. I'm not executing any of the game stuff while there's still messages in the queue (if I did I wouln't get the stall). Note the if-else: while(1) { if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { GetMessage(&msg, NULL, 0, 0); TranslateMessage(&msg); DispatchMessage(&msg); } else { //do game frame here } } > Further, I'd set up the window proc to also call Draw() from WM_TIMER > messages, and possibly from WM_PAINT. That's necessary if you want to keep > updating while the user is moving the window. I'm refreshing the frontbuffer from the WM_PAINT (not executing the game logic). I'll give WM_TIMER a bit of thought. > When you get WM_PAINT, you have to BeginUpdate() and EndUpdate() even if you > don't do anything else. If you don't begin/end updates, then Windows will > not realize that the window has been updated, the dirty region will be > non-empty, and you'll get an endless stream of WM_PAINT messages. (Sound > familiar? :-) Duh. Found a bug. It must have slipped in during the previous bug-hunting session. :) Check it out, it's ridiculous: case (WM_PAINT): { BeginPaint(g_focusWindow, NULL); if (g_pBlitko) { g_pBlitko->PresentFrame(); return (0); } EndPaint(g_focusWindow, NULL); return (0); }break; Thanks for the help, Marin Kovacic Lemonade Productions http://www.lemonade-p.com |
From: Donavon K. <kei...@ea...> - 2003-11-30 18:57:17
|
Here's a little trivia plus a trick: In addition to what Jon said, the explanation for this behavior is that, unlike nearly all other messages, WM_PAINT is not queued up. Rather the way it works is that, when GetMessage & PeekMessage detect that all queued messages have been handled, they go through the list of windows associated with the thread and if one is found with an invalidated region, they return a synthesized WM_PAINT message for it. This way there's no problem with paint messages stacking up and painting doesn't get in the way of higher priority things. EndPaint validates the window and that's why you're supposed to call it. Make sense? Now, I find this can be usefully exploited. Whenever I need a quick-and-dirty D3D app or whatever (and because I'm not a fan of the D3D app wizard code), I go through the Win32 wizard and then just remove BeginPaint & EndPaint from the WM_PAINT handler and instead call my "do frame" code there. Voila, no need to screw with the message pump or recall what PeekMessage's parameters are. The behavior is nearly identical to the PeekMessage loop Jon posted. There is a minor caveat: WM_TIMER is also synthesized in the same way as WM_PAINT but only after all windows are validated, so you'll never see a WM_TIMER message. And if you try to do this with multiple windows on the same thread, clearly only one of them will end up getting paint messages. Oh, and this behavior is actually documented. Of course it's documented that you're supposed to call BeginPaint/EndPaint, so that's breaking the rules. If that bothers you, you could instead call InvalidateRect after EndPaint and get the same effect. Donavon Keithley > -----Original Message----- > From: gam...@li... > [mailto:gam...@li...] On Behalf Of > Marin Kovacic > Sent: Tuesday, November 25, 2003 12:32 AM > To: gam...@li... > Subject: Re: [GD-Windows] message pump problem > > > The problem with this is that Windows expects your message proc to deal > with > > a number of messages in a burst and gets finicky when intervening "other > > thing" happen. I would re-structure that loop as: > > while( 1 ) { > > while( PeekMessage( PM_REMOVE ) ) { > > if( !IsDialogMessage() ) { > > TranslateMessage(); > > DispatchMessage(); > > } > > } > > Draw(); > > } > > But it's essentially the same thing. I'm not executing any of the game > stuff > while there's still messages in the queue (if I did I wouln't get the > stall). > Note the if-else: > > while(1) > { > if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) > { > GetMessage(&msg, NULL, 0, 0); > TranslateMessage(&msg); > DispatchMessage(&msg); > } > else > { > //do game frame here > } > } > > > Further, I'd set up the window proc to also call Draw() from WM_TIMER > > messages, and possibly from WM_PAINT. That's necessary if you want to > keep > > updating while the user is moving the window. > > I'm refreshing the frontbuffer from the WM_PAINT (not executing the game > logic). > I'll give WM_TIMER a bit of thought. > > > When you get WM_PAINT, you have to BeginUpdate() and EndUpdate() even if > you > > don't do anything else. If you don't begin/end updates, then Windows > will > > not realize that the window has been updated, the dirty region will be > > non-empty, and you'll get an endless stream of WM_PAINT messages. (Sound > > familiar? :-) > > Duh. Found a bug. It must have slipped in during the previous bug-hunting > session. :) > Check it out, it's ridiculous: > > case (WM_PAINT): > { > BeginPaint(g_focusWindow, NULL); > if (g_pBlitko) > { > g_pBlitko->PresentFrame(); > return (0); > } > EndPaint(g_focusWindow, NULL); > return (0); > }break; > > Thanks for the help, > Marin Kovacic > Lemonade Productions > http://www.lemonade-p.com > > > > ------------------------------------------------------- > This SF.net email is sponsored by: SF.net Giveback Program. > Does SourceForge.net help you be more productive? Does it > help you create better code? SHARE THE LOVE, and help us help > YOU! Click Here: http://sourceforge.net/donate/ > _______________________________________________ > Gamedevlists-windows mailing list > Gam...@li... > https://lists.sourceforge.net/lists/listinfo/gamedevlists-windows > Archives: > http://sourceforge.net/mailarchive/forum.php?forum_id=555 |
From: Marin K. <m.k...@in...> - 2003-12-02 17:25:18
|
> Here's a little trivia plus a trick: > In addition to what Jon said, the explanation for this behavior is that, > unlike nearly all other messages, WM_PAINT is not queued up. Rather the > way it works is that, when GetMessage & PeekMessage detect that all > queued messages have been handled, they go through the list of windows > associated with the thread and if one is found with an invalidated > region, they return a synthesized WM_PAINT message for it. This way > there's no problem with paint messages stacking up and painting doesn't > get in the way of higher priority things. EndPaint validates the window > and that's why you're supposed to call it. > Make sense? Yeah, it makes perfect sense now that I've accustomed to the idea. It was all jibberish while I was reading about it in the MSDN. :-) -- Marin Kovacic Lemonade Productions http://www.lemonade-p.com |
From: Donavon K. <kei...@ea...> - 2003-11-30 19:07:28
|
Oh, and WRT to this: > But it's essentially the same thing. I'm not executing any of the game > stuff > while there's still messages in the queue (if I did I wouln't get the > stall). > Note the if-else: > > while(1) > { > if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) > { > GetMessage(&msg, NULL, 0, 0); > TranslateMessage(&msg); > DispatchMessage(&msg); > } > else > { > //do game frame here > } > } What Jon was saying is that you're processing at most one message per frame. Not a good idea. Nothing guarantees that your app will receive fewer messages per second than your frame rate and if you're counting on this, you're asking for trouble. You want to pump the queue dry, then do your frame. Donavon Keithley |