Simultaneous multiple key press release delay

Started by
13 comments, last by mark ds 10 years, 1 month ago

Your GetTime() method looks suspicious to me. You could be throwing away lots of precision because of the integer divide.

The simplest way to solve that is to make it return a double, and make the program start time come out as zero. Note that float is no good for this - after the program has been running for a while the accuracy will be too low.


// Get current time in seconds
double GetTime()
{
        static LARGE_INTEGER frequency = GetFrequency();
 
        LARGE_INTEGER counter;
         QueryPerformanceCounter(&counter);

        // We want time zero to be when the program starts. I've hacked that in with a static variable here.
        static LARGE_INTEGER programStartTime = counter;

        return (double)(counter.QuadPart - programStartTime.QuadPart) / (double)frequency.QuadPart;
}
Advertisement

Yeah sorry, I overlooked that if. Yet the other points still stand, and that you only accept 1 message and ignore the rest will make it still look wrong, for example, if you do push-up, release-up, push-up, push-right, you have added to up-velocity twice and right-velocity once and that will have the effect of not looking diagonal until you press down once.

And you are using an angle there that is not correctly representing the movement you apply:


                x += xvel;
                y += yvel;
 
                if ((xvel != 0) || (yvel != 0))
                {
                        if ((xvel != 0) && (yvel != 0))
                                degrees = 45;
                        else
                                degrees = 0;
                }
 
                glClear(GL_COLOR_BUFFER_BIT);
 
                glLoadIdentity();
 
                glTranslatef(x + quadWidth / 2.f, y + quadHeight / 2.f, 0);
               
                glRotatef(degrees, 0, 0, 1);

And that velocity depends too much on old keypresses so if press the keys a few times and dont count and press the opposite keys exactly the same number of times it will pretty much always show 45 degrees.

Your GetTime() method looks suspicious to me.

GetTime() multiplies counter x 1 000 000 before division and that gives perfect microsecond accuracy ( frequency = 1 second = 1 000 000 microseconds). Anyway please also take a look at second source code which actually makes drawing without any time involved. Its not time releated problem at all.

for example, if you do push-up, release-up, push-up, push-right, you have added to up-velocity twice and right-velocity once and that will have the effect of not looking diagonal until you press down once.

Not true. Press-Up velocityY -=4, Release-Up velocityY +=4 (resulting velocityY == 0), Press-Up velocityY -=4, Press-Right velocityX += 4 (resulting velocityY == -4, velocityX == 4) etc. Please notice that on key release I call ProcessInput with negative velocity value (on key press positive value accordingly).
It would be great if you compile (it has no dependencies) and take a look it works perfectly fine except multiple key "simultaneous" press / release not always detected correctly becouse of delay between message (they are detected one by one, first one key and after slight delay second key which leads to 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.

That has nothing to do with 2 keys being pressed at once, which should be consistently as far apart in microseconds as you are actually pressing them.


The problem is your method of timing. You should be timing only the point at which WM_INPUT is caught, not all the other code that is run inside WM_INPUT.
In other words, good:


                case WM_INPUT:
                {

                        unsigned long long MsgTime = GetTime();
                        // Do stuff and log MsgTime.

Bad:


                case WM_INPUT:
                {

                        // Do a bunch of stuff, allocate some memory (this is likely why your timings vary so much).
                        …
                        …
                                                Records[Count].Time = GetTime();

You’re aren’t currently timing when the key was pressed, you are timing when the key was pressed, plus some logic plus a call to new.

V-sync isn’t helping either. It will lag your input thread unless you are rendering from another thread. This can easily add 16 milliseconds to your timings, but your first test case does not have this problem.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid



You’re aren’t currently timing when the key was pressed, you are timing when the key was pressed, plus some logic plus a call to new.

Good point. Although WM_KEYDOWN / WM_KEYUP approach (commented out) has no allocations and same results. Anyway in real game I need to determine which event happend (press or release) and then determine key, so I need to do this allocations.

Especially for you reupload and fixed - pastebin

Last test up to 65 ms delays, Vsync is only 16 ms.

Unlikely to be of help, but i note anyway:

You are using QueryPerformanceCounter - what is the frequency you are given for it (btw, there is no need to re-query the frequency as it is strictly not allowed to change at runtime)?

That 65 sounds quite close to the 55 resolution limit (8253/8254 timer?). I have never seen GPC using that timer, but it is possible if your hardware can not give anything better (for example, my CPU has dynamic clock frequency and hence can not be used - instead some unknown much-much lower frequency timer is used [~3.5 mil, but high resolution]). I highly doubt you are suffering from bad GPC timer, but for sanity check you could do QPC in a loop and output the times (without frequency division of course) - to see what the resolution for your given frequency actually is.

edit: On second though - ignore that. Too bloody unlikely.

there is no need to re-query the frequency as it is strictly not allowed to change at runtime

He only calls it once; it is a static.


Last test up to 65 ms delays, Vsync is only 16 ms.

V-sync and other factors are why input actual in games are not handled in the way shown in your test application, so I disregard those results entirely and would only accept your data for your basic non-OpenGL test.

You are still timing the call to new; it just spans across multiple calls to WindowProc(). That is, you store the current time after WM_INPUT, then do stuff, but all that stuff you do is delaying the next WM_INPUT which could already be in the buffer and waiting.

Eliminate the call to new entirely.
Make a static buffer of an array of 3 RAWINPUT structures and if the first call to GetRawInputData() returns a size greater than (sizeof( RAWINPUT ) * 3) then print an error, dump the message, and increase the size of the static buffer if you want.
While this may not be what you would do in an actual game, the important point now is to find the bottleneck.

If it improves the timings then you know at least one of the main culprits. If not, it won’t be a problem in a real game to do it properly but you need to keep searching for the answer before you add back the allocations (which are leaking, just so you know).


You should also be prepared to accept that your timings are accurate. Maybe you aren’t hitting 2 keys as closely together as you think.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Using the <time> variable from the MSG structure gives similar results - 15,16,31 or 32 ms between calls. As L. Spiro said, it's quite possible that no one can actually hit two keys at precisely the same time.

Incidentally, I'm testing on a Win8.1 machine. Maybe an XP or Win7 system will give different results. Unlikely, but...

Edit - here are some actual numbers. The first is the time according to QueryPerformanceCounter, the second as reported by the time variable in the MSG structure. Both are in milliseconds.

40.37 - 47
19.97 - 15
19.98 - 16
21.76 - 32
19.87 - 31
19.92 - 16
20.03 - 31
20.74 - 16
18.82 - 15
19.99 - 16
20.21 - 16

I've altered your program quite considerably, moving the timing into the GetMessage loop.

This topic is closed to new replies.

Advertisement