Input Polling - Low Latency API

Started by
15 comments, last by Endurion 8 years ago

I am reimplementing input systems in my home grown engine. I went to integrate steamworks and the steam controller with it and discovered I would need to use polling to accomplish the task. (Unless they actually do have a callback system somewhere, or a message pump - whatever though)

So I have a design that sort of works like this:
InputSystem <- InputMapper <- Contextual Bindings
InputMapper <- InputBindingDigital <- InputStateDigital -> InputStateDigital_SteamController
Or..
InputMapper <- InputBindingDigital <- InputStateDigital -> InputStateDigital_Keyboard

and also this..
InputMapper <- InputBindingAnalog <- InputStateAnalog -> InputStateAnalog_SteamController

So I'd be integrating any device through the InputState classes. I am not really here to discuss whether or not this is a good design, open to brief opinions/tips or any gotchya's there might be.


My question to you is what API would you all recommend?

I need to implement keyboard polling, I care most about the latency of API calls, but simplicity is also important. I suspect the simpler the API is the lower the latency of calls.

I did a bit of research to find API's that provide polling interfaces, but I couldn't find anything definitive about latency.

SDL_GetKeyboardState( ... ) - SDL
glfwGetKey( ... ) - glfw
GetKeyState( ... ) - windows.h

Advertisement
this is the single keyboard input routine i use in all my games:
// returns 1 if vkey is pressed, else returns 0
int Zkeypressed(int vkey)
{
SHORT a;
a=GetAsyncKeyState(vkey);
if (a & 0x8000) return(1);
return(0);
}
as i recall getasync is powered by hardware interrupts (IE it reads key state at a very low level from a data structure maintained by the OS and updated whenever a keyboard hardware interrupt occurs), so no latency.
i typically poll at 15 Hz minimum. its about as slow as you can go and still be sufficiently responsive.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

i typically poll at 15 Hz minimum. its about as slow as you can go and still be sufficiently responsive.

So that function must be defined with <Windows.h> right?

Where do you have your input polling?

Do you keep it separate from the game loop?

Perhaps different threads?

That is how I intend to do it, so I am just curious if I am doing it a common way.


i typically poll at 15 Hz minimum. its about as slow as you can go and still be sufficiently responsive.

If we assume that his game works at 60 FPS, that means that he polls the keyboard state once per frame.


i typically poll at 15 Hz minimum. its about as slow as you can go and still be sufficiently responsive.

If we assume that his game works at 60 FPS, that means that he polls the keyboard state once per frame.

Well not quite, that would be 60Hz @60FPS. 1Hz is equal to 1 cycle per second.

this is the single keyboard input routine i use in all my games:

GetAsyncKeyState(vkey);

typically poll at 15 Hz minimum

May I say that I consider this a terribly ill-advised strategy? Polling is, in my opinion, something one can do as a last resort, if nothing else helps. But never as "the one" default solution for all applications.

Polling has one very obvious problem (it needlessly burns CPU), but also a not-so-obvious problem: it is unreliable. And what's worst, it is unreliably unreliable -- you cannot even be sure that it doesn't work. Most of the time it "works fine", except when it doesn't. Which can make reproducing an issue a real pain.

This being unreliably unreliable comes from the fact that you do not record transitions between states, but you take discrete point samples. If your sampling frequency is high enough, this "works", but you have to go quite high to be on the safe side. Correctness is always subject to probability, and the transition from "works" to "fails" is smooth. You will probably never see a difference for turning the joystick/steering wheel left or right, but you might, and will, for other things.

For example, if a young hyperactive user with healthy fingers hits the "fire" button quickly, the "button down" state may very well live for only around 50-60ms. Well, that's great... 50% chance that you sample the key while it's down, and 50% chance that you're too early or too late.

Or you might encounter scheduling jitter, and instead of the intended 66.66ms (=15Hz) your process sleeps 80ms or 90ms. It's as if the user never pressed the button! Or maybe it works 99 out of 100 times and only fails in the most user-upsetting moment in the middle of a boss fight (quite possibly it even always works when you try it because your fingers are older and your computer is faster).

No such thing can happen if one processes events. Events are on/off, yes/no, black/white. Polling is... maybe.

as i recall getasync is powered by hardware interrupts

More like hardware interrupts generate key up / key down messages which go through the system's low level message queue, and then at some point set/clear a boolean flag per key in some global state table, and you test the flag in that table each time you call GetAsyncKeyState.

My question to you is what API would you all recommend?

Without really knowing anything about the steam controller (but having looked at the description online), I would still recommend a solution that uses messages (so... virtual keyboard or capturing a "joystick" with old multimedia functions).

If I understand what's online correctly, the Steam controller can simulate either a virtual keyboard or a Gamepad (which is basically... a more or less "ordinary" joystick with a few more buttons and FF). Which means you should be able to use the old multimedia functions that place events in your event queue (in gamepad mode), or just handle the generated keyboard events without even knowing that they actually come from a special controller. Even if the old multimedia functions poll internally (I don't know if they do, but might as well), processing messages is still the "correct" approach. It is as correct and as good as you can get on your end.

But of course, there's also XInput... which, ironically requires you to poll... :rolleyes:

this is the single keyboard input routine i use in all my games:
// returns 1 if vkey is pressed, else returns 0
int Zkeypressed(int vkey)
{
SHORT a;
a=GetAsyncKeyState(vkey);
if (a & 0x8000) return(1);
return(0);
}
as i recall getasync is powered by hardware interrupts (IE it reads key state at a very low level from a data structure maintained by the OS and updated whenever a keyboard hardware interrupt occurs), so no latency.
i typically poll at 15 Hz minimum. its about as slow as you can go and still be sufficiently responsive.

This might be enough for slow games, but if you need to catch double-clicks or fast input with precise timing in general (eg for an FPS or an RTS), then you're looking at sub-10 millisecond intervals, which you can't trap with low-frequency polling. 60Hz translates to a 16.6 ms per tick, which will cause your game to lack responsiveness. This is doubly confusing if you need to respond to key up events.

Respond to WM_CHAR/WN_KEYDOWN/WM_LBUTTONDOW and their up versions instead.

That being said, unless you need it, synchronization between the input and logic threads can be bothersome, which GetAsyncKeyState() avoids.


So that function must be defined with right?

its part of the win API, so windows.h is most likely the correct header file to include.


Where do you have your input polling?

in process_input. as in while ! quit_game, render_all, process_input, update_all. IE the main input routine.


Do you keep it separate from the game loop?

Nope.


Perhaps different threads?

the sad fact is that precious little in games is truly parallel, so i don't even mess with multi-thread.


That is how I intend to do it, so I am just curious if I am doing it a common way.

actually - no. most folks use the windows message queue i believe. and most seem to think polling is evil for some reason. if they did do polling, odds are they'd look to the generic windows input device API.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


But of course, there's also XInput... which, ironically requires you to poll...
Well, for standard windows input messages you also have to poll the message pump :wink: :lol:

As for GetAsyncKeyState being unreliable -- yes, it will miss fast double-(or more)-taps -- but IIRC it actually is stateful such that it won't ever miss a momentary keypress. When a key is pressed, a flag is set internally to true, which isn't cleared until after the next time GetAsyncKeyState is called, meaning quick taps can't be lost.

It's still a terrible plan though for being partially unreliable - the message pump doesn't miss double-taps :)

I'm pretty sure xinput does the same thing, so that very short/momentary presses can't get lost in between two polls.


If we assume that his game works at 60 FPS, that means that he polls the keyboard state once per frame

guess again. the whole game runs at 15fps.

back when FPS first became a "thing". there was a period where all devs were trying to decide what was the "right" fps for games. TV and movies ran at 24 fps. games ran at anything from under 10 to over 30 fps, depending on game and hardware. folks would struggle to squeeze 30+ fps out of DOOM v1.0. gotta remember, everything was software rasterizers back then, no HW accel. so you basically had two kinds of games, you light arcade stuff like DOOM, and your hard core stuff like Falcon 4,0 gold flight sim. the light stuff was all graphics and little simulation. these types of games went for smooth animation, and thus favored FPS over simulation depth. hard core titles with serious simulation going on under the hood could never achieve the frame rates of a game that just tracks location and hit points. so hard core games settled for slower framerates (typically 20+ was considered acceptable). Rockland Software Productions has always been about "hard core games, by hard core gamers, for hard core gamers." so when i had to choose a target FPS for my company, it became a question of how slow can you go and still be sufficiently responsive, to leave more time for render and update? turns out, a slow FPS is not really the problem. an unsteady FPS is what really nukes playability. if you play a game at 60, then 30, then 15 fps, at first you'll notice a bit of a difference in responsiveness of input and smoothness of animation, but after a few seconds you no longer notice it. so it tuns out you can go as slow as 15 FPS and still be adequately responsive. this gives you frame times of 66ms. polling at that rate is still more responsive than input in skyrim for example.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement