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

Started by
15 comments, last by Evil Steve 16 years ago
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.
Advertisement
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.
STOP THE PLANET!! I WANT TO GET OFF!!
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.
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.
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.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

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.
The only solution that I know of is the second thread. This is how I do all of my applications. You are correct.
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.
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.
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.

This topic is closed to new replies.

Advertisement