Simultaneous multiple key press release delay

Started by
13 comments, last by mark ds 10 years, 1 month ago
I am developing 2D game and faced problem with simultaneous multiple key press / release detection needed, for instance, for character diagonal movement.
Problem is when I "simultaneous" (it has nothing to do with my reaction I press / release very quickly) press / release multiple (two) keys delay between messages often is much more than single frame (16ms with VSync), for instance, it can reach 50-100 millisecond, so character starts moving single direction and only after slight delay diagonal (as intended).
Is it normal for WinAPI messages or is it hardware limit? Is it possible to detect input faster or how games deal with it?
Only solution that kinda helps is process inputs in game logics with periodic delay (for instance every 100 ms), but this sacrifices control responsiveness very much.
I am dispatching WinAPI WM_KEYDOWN / WM_KEYUP messages in while loop.
I also tried to dispatch input in seperate from render thread with GetMessage and also tried RawInput approach.
Delay measurement test project : pastebin
Actual OpenGL diagonal movement test project : pastebin
Advertisement

There shouldn't be much of a delay from the key being pressed to the message being sent to your program. I don't have any experience with Windows programming, but I have used SDL and SFML on Linux and Mac OS X, and this has never been a problem.

Looking at your code a bit, it looks like you simply register a callback function to process events, so you don't have a whole lot of control of when they get processed. Perhaps you could write your game loop more explicitly, and then you can make sure you process all the events in the queue before you proceed to rendering? [EDIT: Never mind. I see you are dispatching all the events in a loop. I missed that in my first reading.]

I think nothing is inherently wrong with your code (even though the behavior that you get is obviously undesired, I think it is somehow nevertheless exactly doing what you're asking for).

What you are doing is peek and drain the message queue until it is empty, then you draw some stuff and flip buffers, which presumably blocks for 16ms.

16ms is a very long time for a computer, and everything else that you do happens more or less "instantly" in comparison to that. Therefore most of the time, this will just work fine, as you spend 99% of your time inside SwapBuffers, and so your different key presses all arrive while your application is blocked. When you next drain the message queue, you get all events that are in the queue, and it's correct.

However, sometimes, it may just happen that one key event arrives in the message queue while you're draining it. After that, there are none left, so your loop continues (of course, what else!). The next event arrives, but that is now irrelevant since you're already somewhere in your OpenGL calls, and after that you block for 16ms. So you get a huge delay between two keypresses that actually happen simultaneously (or nearly so).

Now, why you get figures of 100+ milliseconds, I can't answer. That's truly odd.

(I once upon a time, years ago, tried stamping messages with values obtained from timeGetTime by the way, and Windows message queues push events through much faster than the minimum resolution (which is 0.5ms here), so I doubt it's a Windows message problem as such)

Actually cause of problem is that there no such thing as "simultaneous" in programming. For instance, I press and hold two keys (down & right) and move diagonally, then I "simultaneous" (as quickly as possible) release these both keys, but window message loop (pretty same with raw input) receives WM_KEYUP Down and then only after slightly delay (it can reach 50-100 ms according to tests!) it receives WM_KEYUP Up. Because game logic and rendering processes much faster (60 fps, vsync, 16 ms) it assumes Up key is still being pressed (changes direction and animation frame to Up instead of keeping Diagonal). You can compile given code and test yourself, it has no dependencies and fully working.

Question is - is such behaviour normal? Is there way to process input faster? Could it be hardware (keyboard) limit? Or how to workaround this to make simultaneous multiple key press / release more "smooth" for player (diagonal movement, combos etc) ?
More I delay input process in game logics more "smooth" it becomes for player (input messages get in time) but also less responsiveness it gets.

You got that right, there is no such thing as "simultaneous", since only one message can enter the message queue at the same time (even if the keys are pressed at exactly the same time).

That's what I mean... usually this will work just fine, and the simultaneous events will be quasi-simultaneous in your queue (following one after the other). But of course it may happen that you process one, see that the queue is empty, and move on. And then, the event that is being posted immediately after ("simultaneously") will have to wait for a full frame.

Except 100ms is more like 8 frames... which I don't understand. That smells like you're lost in a call to Sleep somewhere, but I don't see one anywhere in that code.

Log from first program - pastebin

It is rarely 100 milliseconds but often within 25-50 milliseconds, which is 1 or 2 frames (using VSync 60 fps, ~ 16 ms per frame) which already leads to unwanted result (game logics changes direction, animation frame (angle) etc).

So only solution I see so far is using raw input (which is subjectively a little bit faster) instead of wm_keydown / up and process input in game with periodic delay (once per 2-5 frames instead of every frame) to avoid unwanted results.

According to stackoverflow delays of the order of 50 ms or so are common in processing key presses through the normal Windows message queue.

Thanks for effort anyway. I am still looking for any better solutions.

(stupid me, replied to the wrong topic)

I tried your code, and nearly always get ~20ms delay between simultaneous key presses. I wasn't aware that the delay was so significant. Incidentally, WM_KEYxxx vs WM_INPUT was the same - I saw no improvement whatsoever.

However, my wireless keyboard receiver is attached to my monitor USB connector, which is cabled to the PC, which can't help. I wonder if a wired keyboard fares any better?

I saw that you directly add 1 to the velocity each time you get a message, without any checks, and then directly add that to the position. I would not do that, because its dependent on how many messages you get, which can be dependent on hardware or windows.

You also never reset those values, so you have to first accumulate many messages before you have enough to compensate after changing direction.

I would instead have acceleration values dependent on keys pressed on last frame and clamp those to some maximum at each change.

Then calculate the velocity from that and the frame time and clamp it to some maximum and possibly dampen it so that it goes back to zero slowly. Only then calculate the position.

Reset the acceleration to zero before next frame.

I saw that you directly add 1 to the velocity each time you get a message, without any checks, and then directly add that to the position.

Sorry, you are very wrong. I add velocity only once on key press and this has nothing to do with my problem.


if ((lParam & (1 << 30)) == 0)
  ProcessInput(wParam, 4);

if (!Keys[raw->data.keyboard.MakeCode])
{
  Keys[raw->data.keyboard.MakeCode] = true;


  ProcessInput(MapVirtualKey(raw->data.keyboard.MakeCode, MAPVK_VSC_TO_VK), 4);
}

This topic is closed to new replies.

Advertisement