Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


We're also offering banner ads on our site from just $5! 1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Win32 Message Pump and OpenGL - rendering 'pauses' while dragging/resizing


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
16 replies to this topic

#1 shurcool   Members   -  Reputation: 439

Like
0Likes
Like

Posted 25 March 2008 - 05:06 AM

I've often seen many small OpenGL demos/apps that would sort of 'pause' whenever you drag the Win32 window around, or resize it, etc. The rendering basically comes to a halt during the time the mouse button is pressed down. There were a few rare examples that didn't exhibit that behaviour, and continued to update the frames even as its window was dragged/resized. I've never paid much attention to this because it was irrelevant at the time. However, I now have acquired a requirement to eliminate this behaviour for my networked game client in Windows. Searching around provided with me with surprisingly little info on the subject, but it seems the problem has to do with the Win32 message pump basically blocking the main window's thread when you start dragging/resizing your window, preventing the thread to execute normal rendering updates. From what I was able to gather, the only way to circumvent this issue is to create a 2nd separate thread solely for OpenGL rendering. Is that correct, or is there any other way (keeping rendering in main thread)? Currently, I have the window created and all my game logic/rendering in the main thread. The problem is, I don't want the user to be able to 'pause' the game logic and prevent it from running normally. Is my only solution to move it out of the main thread containing the win32 message pump? If so, should I take rendering along with it, or will rendering from a separate thread cause issues/problems and not worth the trouble? Or is it ok if I do it right? Another thing to mention is I'm currently using the GLFW library to handle the window creation for me, and since it's portable, it doesn't directly allow me to get the window handle, DC or rendering context (all that win32 stuff you deal with when creating a window yourself), and based on the only example I was able to find, you need to access the RC in order to render from another thread. A multithreaded OpenGL example It seems I would have to modify the library slightly to allow for that, but I don't have a problem with doing that for now if it's necessary. If anyone could point me to some articles or any info on this subject, I'd greatly appreciate it. Especially if anyone has done something like this using GLFW - that'd be just too perfect. Thanks, and sorry for a long post. P.S. I've tried my game on Linux, and it doesn't have that problem there. So I only need to get it fixed for Win32.

Sponsor:

#2 Structural   Members   -  Reputation: 328

Like
0Likes
Like

Posted 25 March 2008 - 11:24 PM

Quote:
Original post by shurcool
From what I was able to gather, the only way to circumvent this issue is to create a 2nd separate thread solely for OpenGL rendering.

Is that correct, or is there any other way (keeping rendering in main thread)?


I don't think that is correct. My project uses a separate thread for rendering and it halts when I drag the window.

Parhaps the trick is in the message handling when you get a window-move message. You could try to invalidate the window's client rect to force a redraw of that part of the framebuffer.
Other than this nothing else springs to mind.

#3 DanielPharos   Members   -  Reputation: 123

Like
0Likes
Like

Posted 27 March 2008 - 11:40 PM

Isn't it possible to keep a counter in the message loop, counting the amount of times you're processing a Windows message straight instead of going into the rendering part, and if that counter hits 10 (or something), ignore the Windows messages and jump to the rendering part of the code? That way, a big message queue won't block the rendering.

#4 Sc4Freak   Members   -  Reputation: 643

Like
0Likes
Like

Posted 28 March 2008 - 12:06 AM

Direct3D does this too. I don't really think it's a problem; it might be a feature (or limitation) of Windows itself. When moving the window of my D3D10 app, CPU usage drops to 0. I don't have any code to handle resizing events or anything (just DefWindowProc). That means that either Windows or Direct3D (probably D3D) is pausing program execution while the window is being dragged.

That means that there's not much you can do unless you find some sort of setting to change this functionality. Any code or hacks to invalidate the window or force a refresh wont work because any code that you write won't even be executed since something is blocking program execution.

NextWar: The Quest for Earth available now for Windows Phone 7.

#5 Endurion   Crossbones+   -  Reputation: 3645

Like
0Likes
Like

Posted 28 March 2008 - 12:52 AM

During the dragging and sizing Windows opens an own message pump. This obviously bypasses your message pump where you put the rendering/updating.

A possibility might be to react to that special cases by also calling Update during a WM_PAINT call with the elapsed time.

A call to render ought to be in WM_PAINT anyway.

#6 Evil Steve   Members   -  Reputation: 1983

Like
0Likes
Like

Posted 28 March 2008 - 01:01 AM

I can't remember how I handle this, I think I check for WM_ENTERSIZEMOVE and start a timer, then handle WM_TIMER messages and just render once.
Alternatively, you could handle WM_MOVING and WM_SIZING, but I think they're only called when the window changes position or resizes, so if the user just clicks and holds on the title bar it'll pause.

#7 taby   Members   -  Reputation: 336

Like
0Likes
Like

Posted 28 March 2008 - 01:34 AM

The only solution that I know of is the second thread. This is how I do all of my applications. You are correct.

#8 shurcool   Members   -  Reputation: 439

Like
1Likes
Like

Posted 28 March 2008 - 02:08 AM

Ok, time to post an update, as I've done some research and feel like I now know just about everything there is to know about this topic... And there are no good news.

I did some debugging myself, by adding a printf("%d", uMsg); to the WindowProc callback function that receives all the messages (from DispatchMessage() ). And I've made a few interesting discoveries.

For one, it turns out the Windows Message Pump is NOT non-blocking! What happens when the user clicks his mouse on the title bar (or anywhere else on the window border), first there's a WM_NCLBUTTONDOWN notification, followed by a WM_SYSCOMMAND with wParam == SC_MOVE. What happens then, is, you have to pass the message to DefWindowProc(), the default windows message handler.

And guess what, it doesn't return from DefWindowProc() until you let go of your mouse! Isn't that great?

Another great point is that DefWindowProc() is completely undocumented, as far as the details go, you have no idea what it really does. So it turns out that half the time your program, no matter how much effort you put into it, can exhibit strange/completely unexpected behaviour, because you have to rely on a closed-source win32 function to do most of the default message processing for you. How can that function know all the details of your program's specific requirements and needs? Oh well.

----------

I have learned there are ways around this without succumbing to multithreading. DefWindowProc() basically starts its own message pump and sends a few types of messages while the user has his mouse held down. Namely things like WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE, WM_MOVING and WM_SIZING, and a few others (but not WM_PAINT!).

What people tend to do, is start a timer on WM_ENTERSIZEMOVE, update the screen on WM_TIMER (which occurs while the main event loop is still stuck in DefWindowProc() handling the WM_SYSCOMMAND/SC_MOVE message), and stop the timer when its no longer needed (on WM_EXITSIZEMOVE).

The only 2 useful discussions on the topic on the whole internet that I found:
http://www.gamedev.net/community/forums/topic.asp?topic_id=440341 - good info on what happens when user drags window, a way to bypass it, and some problems with it.
http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.ui/2006-02/msg00153.html - and its replies. The guy solves the pause when the Context Menu is activated.

There are still problems with that approach. It's described in the upper link above. One of them is that if the user simply clicks on the title bar, but doesn't move the mouse yet, there's a 500 millisecond delay before there are any messages.

Of course, this is not that big a deal, but in any case, it seems any kind of effort to fight Win32's blocking default message handler and try to get your own rendering/logic to be done in between is just a dirty hack, and might not always work/in all situations. I'm not too happy with it.

I'm not sure if creating another thread and rendering from there is a much better solution, as it needlessly complicates some things. But at this point I don't know what to do.

My hopes for a Clean and Nice Win32 solution have fallen in peril. Now I know why so many people hate it. >_< How can you make the main event loop default-message-handler-function blocking!? Argh.

#9 MJP   Moderators   -  Reputation: 11568

Like
0Likes
Like

Posted 28 March 2008 - 02:26 AM

Quote:
Original post by shurcool

How can that function know all the details of your program's specific requirements and needs?


It doesn't know, it's not psychic. It simply implements default behavior. You shouldn't need the Windows source code to figure that out. If you want non-default behavior, don't send the message to DefWindowProc. Just handle the WM_SYSCOMMAND and subsequent WM_MOUSEMOVE messages, and move the window yourself.

#10 shurcool   Members   -  Reputation: 439

Like
0Likes
Like

Posted 28 March 2008 - 04:25 AM

Quote:
Original post by MJP
Quote:
Original post by shurcool

How can that function know all the details of your program's specific requirements and needs?


It doesn't know, it's not psychic. It simply implements default behavior. You shouldn't need the Windows source code to figure that out. If you want non-default behavior, don't send the message to DefWindowProc. Just handle the WM_SYSCOMMAND and subsequent WM_MOUSEMOVE messages, and move the window yourself.

I'm sorry, but that's a bad argument imo.

The only thing you can (realistically) do is *add* to the default behaviour, you can't replace it.

Unless you want to re-implement every single detail of window movement that there is, like moving it with your mouse, checking whether the 'Show windows contents while dragging' visual setting is on, drawing a temporary dashed-line window-outline if not, using the keyboard to move the window, being interrupted while dragging a window by any kind of unknown event (user pressing the Windows start-button on the keyboard, for example, etc. etc.) and so on. Yeah, unless you wanna do all that, the only *feasable* choice you have is to rely on the unspecified default behaviour of DefWindowProc().

Even if you do manage to re-create all of that window dragging behaviour yourself, guess what, there are other messages that get blocked in DefMessageProc(), like opening a context menu for example. You wanna re-write that code to handle it yourself too?

All that work, just to have some extra flexibility in how your app behaves - not a very smart design at all. No matter how hard you try, you wouldn't be able to simulate what Window's DefWindowProc does identically, creating for a less consistent OS where each window operates differently when being dragged.

Besides, when a new version/update of Windows comes out, are you gonna rewrite all that stuff again?

It's no argument that rewriting a *part of the OS* in your app just to have some more flexibilty in some of your app's behaviour is downright idiotic, if not infeasable.

#11 MJP   Moderators   -  Reputation: 11568

Like
0Likes
Like

Posted 28 March 2008 - 05:02 AM

It wasn't an argument and I'm not sure why you're turning this into one. I simply told you the standard method for overriding the default behavior of the windowing system. If you don't like the consequences of doing it in this particular situation, don't do it.

#12 shurcool   Members   -  Reputation: 439

Like
0Likes
Like

Posted 28 March 2008 - 05:30 AM

Sorry, I'm still a little on the edge after finding out all that stuff about Win32 code, and I jumped the gun when your post somewhat seemed to defend it like "there's nothing wrong with Win32 msg pump design, just do this and everything'll work the way you want it to."

Anyway, Windows is a reality we live in so I'll just have to face it. >.< I just kinda had better expectations for it before (I was never one of those guys who always dissed it).

#13 Bob Janova   Members   -  Reputation: 769

Like
0Likes
Like

Posted 28 March 2008 - 12:50 PM

In my opinion, your game engine loop shouldn't be tied to your rendering loop anyway. So sure, rendering stops when the window is being resized, but your game logic shouldn't. That is done by having the game logic in a separate thread.

#14 shurcool   Members   -  Reputation: 439

Like
0Likes
Like

Posted 28 March 2008 - 02:41 PM

Yeah, that's kinda what I'm considering at this point, however it's still not an ideal solution, as the input polling has to stay in the main thread. I'll have to think more about this.

#15 Josh Petrie   Moderators   -  Reputation: 3175

Like
0Likes
Like

Posted 28 March 2008 - 04:41 PM

FWIW, some of the details of what DefWindowProc will do under the hood are spelled out in the documentation for the messages themselves versus that function specifically. Additionally, DefWindowProc is (like some other aspects of the API) not spelled out in exact detail for a reason: you aren't supposed to care, so that the development team is free to make tweaks without breaking assumptions you may have made on implementation details.

After all, they have enough of those hacks in the OS to cover the asses of bad developers already.

Josh Petrie | Game Developer, Undead Labs


#16 MJP   Moderators   -  Reputation: 11568

Like
0Likes
Like

Posted 29 March 2008 - 06:24 AM

Quote:
Original post by jpetrie
After all, they have enough of those hacks in the OS to cover the asses of bad developers already.


Do you read Raymond Chen's blog as well?



#17 Evil Steve   Members   -  Reputation: 1983

Like
0Likes
Like

Posted 29 March 2008 - 06:28 AM

Quote:
Original post by shurcool
Yeah, that's kinda what I'm considering at this point, however it's still not an ideal solution, as the input polling has to stay in the main thread. I'll have to think more about this.
What sort of input polling? If you just record mouse movement, mouse button presses and keyboard presses, you can just record them in an array of volatile bools and ints. Access to them should be atomic, so there shouldn't be any problems accessing them from two threads.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS