Skip to main content
GameDev.net gamedev.net
🔒 Locked

Simple Multithreading Help (Graphics/Input)

Started by ddcruver Apr 1, 2007 at 5:54 PM 11 replies 1.8k views
Original Post
ddcruver
ddcruver
I am currenlty starting of with programming a game engine and I want to eventualy incorporate multithreading extensively but I have a quick question on a simple noob technique. If I were to say have a thread running my graphics renderer code and a thread that runs lets say everthing else or input. Will I gain any performance while I am waiting for a function call in the graphics code, say a operation that takes a while to return, ie. telling the the graphics card to render a scene. Will it if i have a second thread running will it gain more processor time because the other thread is waiting, or will it continue to use same amount of processing just for wasted cycles. Also say I use the Win32 function for Sleep() or other calls to sleep the thread, will the graphic thread get more processing time. In theory i want to sample the input only at a set rate and not between every render loop. If their render is rendering at a horrible 10 frames per second I still want to sample the input every say 100 milliseconds or if the render is rendereing at 500 fps then i do not want to sample the inputs every 2 milliseconds. Sorry this is a newbe approach, just want to ease in to the multithreading, and sorry if I left some details out that would help you answer my question better.
Kylotan
Kylotan
Typically, graphics has to go in your process's main thread. Just a warning.

Your approach will probably not gain you any performance on single core systems, and may even cost some performance. On multi-core systems however you may find a slight performance benefit as things are executed in parallel.

Using Sleep() will just pause the current thread. In theory your graphics will therefore then have more 'time' than if you hadn't called Sleep(), but that's not the way you should look at it, as you never gain extra time that way compared to a single-threaded approach which never even needs to call Sleep().

The main problem with multithreaded programming is synchronisation of data. You've made no mention of this so far, so I suggest you read up on mutual exclusion, deadlock, semaphores, and so on. Otherwise you will just run into random crashes or data corruption that are almost impossible to debug.
ddcruver
ddcruver
Let me restate this.
Main Thread -> Graphics
Secondary Thread -> Input

Graphics -> Constantly updating the screen as fast as can or a designated max fps.

Input -> GetInput then Sleep X amount of time then repeat.

Say this situation comes up
MainThread (M) -> Rendering .........................................
SecondThread (S) -> GetInput .... Sleep() ... GetInput() ... Sleep()

So on single thread it will jump back and forth between both threads correct.
But when it is jumping back in forth and say second thread is in a sleep call will it spend less time in that thread and go back to the rendering thread which
would be actually doing things.

Single Core
I was asking if this would happen.
Core -> |M |S|M |S|M |S|M
or this would happen.
Core -> |M |S |M |S

I am aware of the data sharing issue between the two threads I was ignoring that fact for now in my development because I want it to run first then optimize this is just a forsight.
JasonBlochowiak
JasonBlochowiak
Input really isn't that great of a candidate for a separate thread. Unless your rendering is soooo slow that the input sampling would otherwise be very low, in which case the game isn't going to feel responsive anyways.

Basically, for most games these days, physics (including ray-casting and other collision queries), rendering, and data decompression/decoding (music, video, textures, etc.) are really the only things worth the hassle of threading. Granted, they can really be worth the hassle, but aside from that, I wouldn't bother.
Xeile
Xeile
When a certain function is a blocking, I would place it in a separate thread. It can process while other functions get some execution-time too.

If I recall correctly, the Direct3DDevice::Present() method is a blocking function when the PresentParameter::PresentInterval is set to Default (One). You could of course set it to Immediate, but then you consume extra power, which laptop users don't be greatful for :P.

So, you could base your decision using single- or multithreading on whether or not a function is blocking.

Regards,

Xeile
oliii
oliii
If you wanna play around with multithreading and game stuff, give glfw a go. It's a open source OpenGL framework, that does graphics interface (OpenGL), multithreading, inputs and the OS interface.

It's my new favorite thing for OpenGL development.
Everything is better with Metal.
Kylotan
Kylotan
Quote:
Original post by ddcruver
Single Core
I was asking if this would happen.
Core -> |M |S|M |S|M |S|M
or this would happen.
Core -> |M |S |M |S

I really have no idea what you're trying to express there. As soon as you call Sleep(), that thread stops acting and one of the other threads takes over, until either the operating system pauses it to run another thread, or it calls Sleep(). There is no time wasted as a result of calling Sleep, as such. However it does take a small amount of time to switch between threads, which is why you shouldn't generally do it unless you would otherwise be waiting indefinitely (eg. on network or hard disk access).

Quote:
I am aware of the data sharing issue between the two threads I was ignoring that fact for now in my development because I want it to run first then optimize this is just a forsight.


Fixing critical bugs isn't an optimisation. If you don't synchronise your data properly, the code is hopelessly broken, and any measurement of speed or accuracy is largely pointless.
ddcruver
ddcruver
Quote:
Input really isn't that great of a candidate for a separate thread. Unless your rendering is soooo slow that the input sampling would otherwise be very low, in which case the game isn't going to feel responsive anyways.


I want to my rendering to be set to go at maximum rate while other systems will only go as much as needed. I can not think of a way to make it sample the keyboard every say 100 milliseconds. You could put this on a shared thread but that would in every loop iteration would have to check the elapsed time to see if it is time to get a new sample. I was thing that haveing its own thread that slept most of the time would relieve the fact of getting an elapsed time and checking to see if it should sample again. But then again im sure that the switching and sleeping of a thread every 100 milliseconds might be more expensive then checking doing an addition and comparison (checking elapsed time).

Quote:
I really have no idea what you're trying to express there. As soon as you call Sleep(), that thread stops acting and one of the other threads takes over, until either the operating system pauses it to run another thread, or it calls Sleep(). There is no time wasted as a result of calling Sleep, as such. However it does take a small amount of time to switch between threads, which is why you shouldn't generally do it unless you would otherwise be waiting indefinitely (eg. on network or hard disk access).


Sorry the formating got screwed up on submiting it I am new to posting suff on a form and am not use to there being no toolbar for formating like other boards. But I not really aware of what sleep actually does. If it is blocking or not and what blocking is.

Just to clairify I am thankful that all of you are telling me about the data corruption, and i am awair of that i just did not say anything about it because it is not what my questions about.

My main question was ment to be is that do certain functions actually block. Like will sleep tell windows to suspend the thread for that given time or to do a wait that still use time. Also if i have two threads running and a blocking function is called on one. Will it automatically give the other thread more time. What should i look for to find out if a function is blocking.

Also I know I have reading to do in these subjects but what calls in OpenGL and DirectX are block or take awhile on the graphics card side via it would give another thread running in that proccess more time.
Kylotan
Kylotan
To make your rendering go at the maximum rate while having everything else take only as long as needed, it's easiest to put everything in one thread! You don't need to sample the keyboard; the operating system will do that for you, and you only have to process keyboard events when it presents them to you.

A 'blocking' operation is an operation that takes significant time to complete because it is waiting on an external data source. Sleep() is a blocking operation from the point of view of that thread, because the thread stops working until the external data source (the OS scheduler) tells it to wake up. However in multithreaded terms, Sleep() explicitly allows other threads to run, whereas a typical blocking call will not (although the OS routinely and regularly switches between threads to ensure they all get some action). Calling a blocking operation will not automatically transfer control to another thread. You won't typically find documentation on what is a blocking function and what is not; just assume that anything that waits on external data, such as disk access, network access, that sort of thing, may cause a delay. Typically these operations have non-blocking equivalents which can wake up your sleeping threads when they have data available, but they are also typically quite complex to use.
ddcruver
ddcruver
Quote:
To make your rendering go at the maximum rate while having everything else take only as long as needed, it's easiest to put everything in one thread! You don't need to sample the keyboard; the operating system will do that for you, and you only have to process keyboard events when it presents them to you.


Ok what if you dont want to use the windows message loop method? I have heard that it is a really slow way of dealing with keyboard input because of its defaulted low priotity compared to other things. If I sample the keybord via GetAsyncKeyState() i believe that is its Win32 name, I must do it explicitly and besides that I would like to create a call back for indivdual keys or combinations and how would I tell this to the os.

Is there a message pool in linux or the mac os platform how is there standard way of getting key information.
FluffysWhole
FluffysWhole
The Sleep() method may or may not give other threads in your app time to work depending on the time value given and available system cpu time. You cannot call #any# function and guarentee that it does/or does not cause a thread switch.

You cannot guarentee which thread will get execution time next.

Your application, even if full screen, is not the only process or thread running.
Thread execution depends on process class mixed with thread priority mixed with last excuted time and current thread state. Raising these can cause all sorts of lockup fun :)

Calling GetAsyncKeyState() lots will likely be slower then waiting for the messages through the WinProc callback. If you want these more timely increase the thread priority that deals with the message pump for the window or give DXInput a go.

If you want a window on MS Windows, you need a message pump somewhere.

You can do game graphics not in the main app thread but you cannot use DX from multiple threads due to its COM and ThreadAlloc nature. Only one thread may create and use a DX interface including any child interfaces that interface may create.

In my opinion the best things to have in threads are:

window/messaging/input <- primary process thread
audio
networking
physics tick
graphics
disk access
AI.

With rough priorities in that order, first being highest priority.

Care must be taken that none of the threads share data but instead post updates to each other via a dirty/current data flip mechanism. As soon as you go down the road of locking stuff you begin to lose much in the way of benefits unless you are #very# careful in design about where youre locks are and the probabilities of waiting. Let alone double lock issues :(

#edit forgot AI

[Edited by - FluffysWhole on April 4, 2007 6:59:57 PM]
ddcruver
ddcruver
Thank you FluffysWhole and eveyone else.
jeremymm
jeremymm
Quote:
Original post by JasonBlochowiak
Input really isn't that great of a candidate for a separate thread. Unless your rendering is soooo slow that the input sampling would otherwise be very low, in which case the game isn't going to feel responsive anyways.


Threading input may not give you much in terms of extra performance but you can make a 30 FPS game playable with input running at a nice 100 Hz. This is true when sending input data in a lock step/cheat detection network scenario or when doing force feed back calculations. Running at a higher "constant" frame rate also helps most types of filtering/interpolation you are doing on the input date.

IMHO, after network code, the next logical thing to "thread" is input I/O do to the assumption that the devices are "read only" (exception of course to force feed back). Then once you have the experience of threading network and input I/O, look for data parallelization opportunities in your code. I.e. If you are doing 16 heavy calculations on independent data sets and you can detect four cores are your disposal then you should make four threads and have then each work on 4 of the 14 calculations. It really does not matter if data parallelization is in graphics, physics, collision, etc..., it can give you a decent performance increase and will scale well from single core to many cores if properly implementated.

As a side note. I NEVER use Sleep() in my code. Blocking/timing out on some type of mutex/semaphore/event/whatever will provide a much more consitent behavoir.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.