Multithreading for time sensitive input... or not

Started by
12 comments, last by Hawkblood 7 years ago

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?

Advertisement

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.

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.
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;
}
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. : (
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.

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.

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.

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.

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.

This topic is closed to new replies.

Advertisement