Sign in to follow this  
AntiGuy

Multithreading for time sensitive input... or not

Recommended Posts

I suppose this is more of design issue than anything else, but maybe someone can help me out.

I'm making a game that runs at a fixed time step and uses the gamepad in a very time sensitive manner (i.e. how long a button is held etc...). Simple enough right, the problem is if the game stutters or runs a bit slower than it should, the input timings will be off.

I was thinking of using multithreading as a solution but that a reputation for being a headache. Any alternatives or is this one of those cases where multithreading is necessary?

Share this post


Link to post
Share on other sites

In UI design, it's best practice to have your inputs be responsive, independent of performance. This normally means running the event listeners on another thread, since the worst case is for an event listener to be caught in a performance drop and an input being lost.

Share this post


Link to post
Share on other sites
It can mean being on a dedicated thread.

It can also mean making sure that your code don't block, meaning nothing stalls the system or makes the program wait around.

Writing non-blocking code is often gentler than introducing threading with all the attendant concurrency issues.

Share this post


Link to post
Share on other sites
Simple enough right, the problem is if the game stutters or runs a bit slower than it should, the input timings will be off.

Does that actually happen? Supposing it does, I'm not sure multithreading would solve your problem. Multithreading is nice for gathering inputs in multiplayer games, though. Anyway, IF that scenario actually happens, I would gather all input in the beginning of the main loop, place everything inside an array, also with the time that it happened. In the middle of the main loop (the update function), you check inputs in the array using something like `getInput(...)`, the function will only return TRUE if the input exist AND isn't from too long ago (that's why the timing). Something like this:

bool getInput(int key)
{
    for (int i = 0; i < INPUT_COUNT; i++) {
        if (inputs[i].key == key && currentTime() - input[i].time < 100) {
            /* It is the key I'm querying and it happened less than 100 ms ago */
            return TRUE;
        }
    }
    return FALSE;
}
Edited by felipefsdev

Share this post


Link to post
Share on other sites
The idea of non-blocking code is a bit new to me. I just always assumed anything that needed to be done while something else was occuring required multithreading.
 

Does that actually happen? Supposing it does, I'm not sure multithreading would solve your problem.
 
I'm positive it will happen. The button presses are sensitive to how long their held,  if a frame or so for whatever reason takes takes longer than it should, at time of release, an action could exectue for a shorter tap (using simulation time) or a longer tap (using real time) due to the delta time going over the window.
This can be solved by checking the input concurrently and getting better precision of the time a button was released, but I have no experience with that so I'm stuck. : (

Share this post


Link to post
Share on other sites
What kind of input API are you using? I think I've used some input APIs in the past which included timestamps in their input buffers, which were accurate to when the input was detected, letting you handle them later on while still having accurate timings.

Whether you can use threads or not may also depend on the engine/APIs you have available. Some engines don't like it when you access certain things on a background thread (Unity in particular is fairly strict about this, but I don't know if their Input system cares or not - I haven't tried it on a thread yet).

If you can make calls to your input API from a background thread, it should be pretty trivial to make a queue to hold your input. Give each input element a timestamp and that should let you do whatever you need. Edited by Nypyren

Share this post


Link to post
Share on other sites

People aren't really THAT precise with their inputs. No one is even going to notice if a button hold is occasionally detected as lasting a frame too long or too short unless your target frame rate is too low to begin with.

And if frames consistently take longer than expected, you have bigger problems to worry about than precise input timing.

Share this post


Link to post
Share on other sites

I'm using XInput, and that sounds like a great solution. By background thread do you mean simply spawning a thread at the start of the application and using that to make API calls while the rest of the application runs? That would still be multithreading though right, The things I've been hearing horror stories about?

For consoles I wouldn't  see this as much of an issue, but PCs are largely unpredictable and performance dips happen for a multitude of reasons. I'll concede that the game running at 50hz instead of 60hz (probably still in a playable range IMO)  for example probably won't hurt too much, but unless you can say that one random hiccup can't launch an input read over a few frames late and trigger the incorrect command, I can't see this as an nonissue.

Share this post


Link to post
Share on other sites

Multithreading is another tool, no reason to be afraid of it. Threads are really useful in games. Issues with synchronization are a pain to debug, but rarely an issue if you have a solid plan for your program's execution (and are using a threading solution that helps you minimize error). Especially in the case of input management, where your event will likely only have to affect a small amount of data.

Share this post


Link to post
Share on other sites

My (limited) understanding of IO is that it works because some of the legwork is done in hardware, so the operating system can poll the start of the hardware through driver interfaces.  This sort of asynchrony doesn't require a thread.  I know at the very least that overlapped network IO in Windows works that way, and what limited experience I have using low-level IO in academic settings has looked much the same.

Moving input handling to another thread because you're afraid of slow frames sounds like misapplied effort.  There may be legitimate reasons to throw input handling on another thread, but there are even more legitimate reasons to stabilize your framerate.

Edited by SeraphLance

Share this post


Link to post
Share on other sites

By background thread do you mean simply spawning a thread at the start of the application and using that to make API calls while the rest of the application runs? That would still be multithreading though right, The things I've been hearing horror stories about?


Yeah. Don't worry about it in this case though. It won't be too hard for this purpose.

As long as you carefully manage how you access data on each thread, it's unlikely that anything bad would happen. There are only two things you're dealing with in this case: a queue of input, and a flag to exit the background thread when the main thread wants it to shut down.

A simple pseudocode implementation would be:
 
// start this function as a thread during app startup
void InputReadingThreadFunction()
{
  while (main thread has not requested that the input thread terminate) // probably OK with a normal bool with no special synchronization
  {
    if input is available in XInput
    {
      put that input into a synchronized queue
    }
  }
}

void MainThreadInputPollingFunction()
{
  // Do this instead of reading from XInput on your main thread
  while (input is available in synchronized queue)
  {
    Process that input
  }
}
The type of queue, variables, etc depend on the language and APIs you have available. Edited by Nypyren

Share this post


Link to post
Share on other sites

Alright, I'm feeling a lot better about this now! Doesn't seem like attempting anything too crazy.

Thanks everybody, you've been a real help!

Share this post


Link to post
Share on other sites

But also bear in mind that collecting input immediately on a background thread and then handing it to the main thread later is not going to get you the input any quicker than collecting it on the main thread. The only benefit is that you might have been able to get a more accurate timestamp when you received the input; but some input APIs will have already collected that data for you anyway, meaning a single-threaded approach is sufficient.

Share this post


Link to post
Share on other sites

I didn't see anyone asking what the input is for..... Why do you need it so "precise"?

If you are running at 60fps consistently and it drops to 30fps at some random time for a few seconds, that's still only 16.67ms difference. If the player can detect an input difference in 16.67ms, then the player is a freakin robot.

 

You *can* multithread the input, but you would want to declare any variables used to store the state of such input as "volatile". This will allow your main thread to view the data without releasing it from the detached thread. I'm in agreement with Kylotan. No matter if you poll the input in a detached thread or in the main thread, you will still be handling it at the frame rate of the main thread.....

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this