Jump to content


Windows / WMs and multiple threads


6 replies to this topic

#1 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 29 November 2007 - 10:04 PM

Hey there :)

This time round, I have a general design question, regarding Windows window messages:

Consider a multithreaded framework: There is a rendering thread, and a core logics thread.

Now, there came up a situation where a large amount of data (i.e. 10 - 60 seconds stall) needs to be loaded by the logics thread. The idea was quite simple: Since rendering is threaded anyways, create a third thread, which displays a small "loading" splash. and a little animation as well as some kind of progress report.

Everything works fine, but for a little detail:

Window messages _only_ can be recieved by the thread that initially created the window - which means my loading screen thread may not process window messages.

This bugs me a little bit, since the user can't move the window during loading, and may get the usual "This application is frozen" dialog box.

An obvious solution would be to carry out the loading in the extra thread, but I have design gripe with that: There are several different of these loading calls all over the place, while the actual loading screen remains the same (and thus ideally can be encapsulated in a class of its own)

Does anybody have any better ideas ? Ideally, I'd like to pass window ownership to the loading screen thread during the loading process.

Cheers,
- Wernaeh
Some call me mathematician, some just call me computer guy. Yet, I prefer the term professional weirdo :)

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 4782 posts
  • LocationBellevue, WA

Posted 30 November 2007 - 12:25 AM

From a brief survey of the relevant section of the Windows API, I didn't see any way to assign ownership of a window to a different thread.

I'd recommend just creating one thread that is dedicated to running the main interactive loop, including handling Windows messages. The rendering thread and logic thread would both be separate from this, though synchronized with it on each loop iteration. And you might even wish to create a fourth thread to do the data loading, rather than doing it in the main logic thread (to avoid complexity in the main loop / logic thread synchronization).
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 30 November 2007 - 01:41 AM

Thank you for looking it up, I feared that much ;)

I've thought about placing the window in a different thread once, as well, but the problem is that there are many dependencies between the window system's message pump, the input system, and the rendering system (drawing device context, key messages, and so on), so I would spend a lot of time synchronizing in between there. Furthermore, after the thread-locked window message queue, I wouldn't be surprised if I'd needed to be in the same thread to initialize DInput and OpenGL as well.

In regards to your suggestion for a fourth thread for loading (which I've read through several times, and maybe I didn't get it, I'm sorry :) ), I think this is what I'm currently doing already (minus the main interactivity thread, that is)

My setup:

Logics thread -> Run message pump, run game logics, load resources (requires rendering thread synchronization), run rendering (synched as well)

Rendering thread -> Process all rendering commands issued by logics thread

Loading screen thread (ideally) -> While logics thread is working on something bigger, run message pump, run rendering (i.e. take over the game loop)


Your suggestion essentially just flips logics and loading screen threads during a loading phase (i.e. do loading in the additional thread, while assuring the logics thread doesn't mess with anything the loading thread touches)

Is this what you intended ? If not so, please provide some more detail about your idea :)

Thank you again,

Cheers,
- Wernaeh
Some call me mathematician, some just call me computer guy. Yet, I prefer the term professional weirdo :)

#4 Reedbeta

    DevMaster Staff

  • Administrators
  • 4782 posts
  • LocationBellevue, WA

Posted 30 November 2007 - 05:42 AM

Hmm. What I was thinking of was something more like this:

---

Thread 1 (main thread) runs the main loop (which includes the message pump), and is responsible for keeping the GPU, render thread, and logic thread in sync. This thread owns the window(s), which may on certain events (e.g. input) dispatch messages to the logic thread.

Thread 2 (rendering thread) is responsible for doing all computation necessary for rendering, and sending the commands to the GPU. It stops and waits for a signal from the main thread after each frame. This thread owns the OpenGL rendering context (if using OpenGL). Note that the rendering context need not be owned by the same thread that owns the window (and can actually be shifted between threads if you like).

Thread 3 (logic thread) is responsible for updating the game state each frame. It may also have a message pump of some kind (could be a Windows message pump or a custom-built message sending system) to receive messages sent from the main thread. Like the render thread, it stops and waits for a signal from the main thread after each frame.

Threads 4...n are worker threads spawned by the logic thread when it needs to do some computation that will take more than one frame (maybe running AI scripts, maybe loading things from disk...). They sit idle until the logic thread tells them to do something, and send a signal to the logic thread when they are done. The logic thread keeps on running and updating the state while the worker threads are doing their thing, i.e. the logic thread does not wait for the worker threads to finish.

---

Now, I have to make the disclaimer that I've never implemented a multithreaded rendering engine, so there may be some flaw in this plan that I haven't thought through. Also, while this seems like a "logical" separation of concerns to me, you could probably combine threads 1 and 3 into a single thread that both manages the GUI and does the updates, cutting down on the two layers of dispatch involved in the message handling. In either case the point is that the thread with the GUI never ends up waiting for the long processing tasks in the worker threads to finish.
reedbeta.com - developer blog, OpenGL demos, and other projects

#5 Nico

    New Member

  • Members
  • Pip
  • 4 posts

Posted 30 November 2007 - 10:42 AM

Wernaeh said:

Window messages _only_ can be recieved by the thread that initially created the window - which means my loading screen thread may not process window messages.
You can give any of your threads access to the mainthreads message queue and input state by calling AttachThreadInput. Just make sure both threads have already created a message queue (can be enforced by a dummy call to PeekMessage flagged with NOREMOVE). After attaching the input the new thread can get into a TranslateMessage/DispatchMessage loop.

regards

#6 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 30 November 2007 - 12:19 PM

Thank you for the more detailed description, Reed, I get it now :)

It is indeed similar to my system, but it considers that there may be more than one parallel loading thread.

This makes placing the actual loading in an extra thread much more logical than placing the windows message handling and rendering in a loading screen thread. I think in this context, leaving gui and interactivity in the main thread while doing loading logics in an external thread seems much more intuitive. I guess I'll give it a try.

Quote

You can give any of your threads access to the mainthreads message queue and input state by calling AttachThreadInput.

Ah okay, thank you for your input :) While doing my original research, I stumbled upon this call as well - but as far as I know, it only duplicates input messages (keys, etc) into the message queue of another thread, and doesn't handle any other messages (size, timers, ...) Did I miss something here ?

Cheers,
- Wernaeh
Some call me mathematician, some just call me computer guy. Yet, I prefer the term professional weirdo :)

#7 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 02 December 2007 - 12:53 PM

Just for those interested,
after a lot of trying and code restructuring and even more tinkering, I finally came up with at least a half way clean solution.

The problem with seperating the loading code into a seperate thread and keeping the main thread as the interactive one is more of a cosmetical one:

There are several different loading calls all over code, most of them are realized as member functions. Splitting of a static thread entry point for each loading call is a little bit ugly. Ideally, I'd still have the normal code flow, but a LoadScreen.Start() before and a LoadScreenStop() after each loading process.

Since loading in this context mainly refers to entity and logics objects, there also won't be more than a single loading thread at a time (after all, I just serially read out a single file in most cases, which is hard to parallelize). File loading on a lower level may still be multithreaded, though, as may be any other form of large-data resource loading. Also, threading off the loading process requires me to consider this in near to every interface the loading thread might use - which are many compared to the ones used by an interactive loading screen thread.

So what's the solution I finally decided on ? - Rather than an interactive thread, as discussed previously, I just moved the entire window message handling, including destruction and creation, to its own (non-main) thread. This thread now nicely is contained within my window manager, and I never ever need to worry about it again. Message handling need not even be called anymore - input is done via DirectInput, which works from an arbitrary thread, as do audio and rendering. Finally, I have a nice, animated loading screen that even features audio output and input processing, and can easily be shown and stopped again. *imagine someone happily jumping through his office here*

Thanks again for all the input :)

Cheers,
- Wernaeh
Some call me mathematician, some just call me computer guy. Yet, I prefer the term professional weirdo :)





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users