Are you developing a game for Windows, and are you working on input handling?
At first, it could reasonably be assumed that mouse and keyboard should be the simplest parts of this to deal with, but in reality, they are not – at least if we are talking about Windows. In fact, several extremely popular AAA games ship with severe mouse input issues when specific high-end mice are used, and some popular engines have issues that are still extant.
In this article we’ll explore a few reasons why that is the case, and end up with a solution that works but is still unsatisfactory. I assume that there is a whole other level of complexity involved in properly dealing with accessories like steering wheels, flight sticks, and so on in simulators, but so far I never had the pleasure of working on a game that required this, and this article will not cover those types of input devices.
↫ Peter ‘Durante’ Thoman
So, what is the problem? Basically, there are two ways to handle mouse input in Windows: if you use batched raw input processing, which is pretty much a requirement, you need to also choose whether or not to keep legacy input enabled. If you keep it enabled, the legacy input will add so much junk to your message queue it can negatively impact the performance of your game quite harshly. If you disable it, however, something really fun happens: you can no longer move the game window… Because the Windows UI uses legacy input.
Thoman has a solution that he and his company uses, and he considers it an ugly hack, but they just don’t know of a better way to solve this issue. Thoman keeps legacy input enabled, but just limits the number of message queue events per frame that are being processed (they limit it to 5). As far as they can tell, this doesn’t seem to have any negative side effects, but it’s clearly a bit of an ugly hack that shouldn’t be necessary.
I found this a rather interesting niche topic, and I wonder how many people have struggled with this before, and what kind of other solutions exist.
Didn’t microsoft solve this problem with the launch of DirectInput long ago? Oh that was deprecated in favour of using message loops instead by the developers. Microsoft never changes, at least not for the better.
I haven’t noticed a performance issue with standard win32 input events, but then again I don’t write windows games, I do more application development.
The author says this…
Yes. I consider NOT doing this a cardinal sin of GUI software. Ideally this would be planned for up front, because it can be hard to separate after the fact. When you do any sort of work that can block on the GUI thread, that’s a recipe for input problems down the line. This happens on windows and linux alike. I often edit files over network shares, and I’m frequently put off that the GUI locks up periodical when files are being opened/saved. The GUI can’t respond during blocking IO/CPU tasks because they’re done on the same thread….
An example some may be familiar with is blender failing to separate the work thread from the input thread. Of all types of applications, blender in particular desperately needs to separate GUI and processing, yet they did not do this. In a complex scene this causes input latency and complete lockups at the worst. A simple adjustment to a slider and the entire UI locks up while waiting to process the last input. A slider is interactive, so performance dropping below 1FPS makes it very painful to use if the scene is complex. The solution is to keep GUI and computation in separate threads. This is very effective and ideally everything that might block should never be performed in a GUI thread. I’d like to fix blender, but like the author indicated it’s much harder to fix as the code base grows.
It makes sense that game developers have largely stuck with sequential game loops though, Single threaded is easier program and multi-threaded code is notoriously hard to debug. Doubly so in unsafe languages.
A simple solution that might work for the author would be to keep the main thread extremely simple and don’t do anything beyond basic window management and recording input state in a global structure: mouse movement/input buttons/keypress bitmap/etc. A spinlock may be appropriate, but might not be needed if global writes can be done atomically. Now spin up a new thread for the main game loop. It need not concern itself with the win32 API messages at all, just poll the global structure every frame/compute cycle. This way, everything that can block is taken out of the main GUI thread, keeping it very responsive. Meanwhile the game loop is free to operate on input at it’s own pace.