Optimizing input, Listeners, jInput, Keyboard-- questions and contemplations.

Started by spinsane, November 25, 2010, 21:32:22

Previous topic - Next topic

spinsane

I am getting really dumb about wanting to optimize user input handling as much as possible.


I could use a listener and set integers-as-strings for the action command, and then just parse the integer and put it into a switch block for some pretty good efficiency (what about a listener per key approach?). Since the listener reacts to key pressing, it isn't arbitrarily checking to see if a key is down during every game loop. Instead it sits peacefully sleeping in its own thread until prompted to awake (right?).

But, we don't want game-updates to happen outside of game-loop anyways, so the listener would use booleans to communicate with the game-loop in a series of if-blocks (unless there is a way to sync a listener to a logic loop without losing accuracy). In addition, in a game where computational cost of input handling matters, there's probably going to be a LOT of input- so, it isn't like this listener thread is ever going to be sleeping, so it looks like jInput is going to be faster. jME is configured to promote the use of Listeners, so I'm really curious to know if that's actually a good thing.

Is this all true? Does anyone know how the two methodologies differ in computational costs? Does calling 'isKeyDown' every update cost more than an actionListener firing?

-------------------------------------------------------
Assuming jInput is going to be faster,
On the conceptual side-- is using isKeyDown less accurate than handling Keyboard events?

Say I did something like this in my logic loop.
while(Keyboard.next())
switch(Keyboard.getEventKey(()) //for each key whose state changed
{
case Keyboard.KEY_A: left = !left; break; //change the state of that key's effect on logic
case Keyboard.KEY_B: right = !right; break;
case Keyboard.KEY_C: forward = !forward; break; //down is implicitly true
}

if(left) left(); //If the key is down, perform whatever action.
if(right) right();
if(forward)forward();


Is it more accurate than doing something like this?
if(Keyboard.isKeyDown(Keyboard.KEY_A)) left();
if(Keyboard.isKeyDown(Keyboard.KEY_B)) right();
if(Keyboard.isKeyDown(Keyboard.KEY_C)) forward();


What would the differences in runtime be? If a user taps the key really really really fast or the update loop lags, I imagine the second method would lose precision.

When I think about runtime complexity (my knowledge on the topic is minimal)- I get the following impressions:
The first method has C+M comparisons, where C is the number of keys pressed (a switch is O(1), so C*1 = C) and M is the number of possible bindings.

The second method has M comparisons, but also M calls to isKeyDown. isKeyDown checks the state of the keyboard after a poll, which presumably happens when Display updates (right?). Assuming Keyboard is really efficient, poll stores its data in a jump table that isKeyDown checks using a switch internally, meaning that one call = one comparison. So, isKeyDown methodology requires 2M comparisons, one for checking the key and another for evaluating that result. M will always be greater than C, so the first method should be more efficient.

So- it seems like the first method would be both me efficient and more accurate. What do you think?

Now, I assume Keyboard.poll()/create()/destroy() all also take place in Display.update()/create()/destroy() (because I've been using Keyboard.isKeyDown without doing any of those other things). This seems like a bad thing, because Keyboard.poll() is locked to the same rate as display updates, which if we want logic to update faster than the render side, the keyboard isn't being polled properly- I assume we can just poll the keyboard directly in our logic loop, but then a polling also takes place arbitrarily in the display.update, which is an unnecessary call- unless poll doesn't actually poll and just calls the most recent polling done by the OS internally- but there's still a number of assignments taking place that is equal to the number of keys. IDK how the hell polling works... If a call to Keyboard.poll isn't optimized and Display can't be configured to NOT poll, then Listeners suddenly seem more attractive, but how much more attractive? I have no idea.

Obviously this is theoretical. I don't care "if it doesn't matter that much," I want to know which is best. If I'm doing something that requires a LOT of physics, then being particular of every little thing matters.



So, to sum it up (by fast, I mean require fewer computations)
1. Are optimized listeners fast?
2. Is it faster to have multiple listeners to instead of unnecessary comparisons in one listener?
3. Is jInput faster than a Listener (and by derivative faster than jME input)?
4. Between the two jInput techniques shown, which seems better?
5. How does polling actually work? Is there any way to sync it to a fixed logic step in LWJGL?

Thanks.

Matthias


spinsane

So, do listeners suck? And does Display.update() call Keyboard.poll()?


spinsane

Quote from: Matthias on November 25, 2010, 22:17:51
when you use Display.update() you can handle input like this

I actually want Display to NOT poll the keyboard when it updates. I was just wondering if Display.update() calls Keyboard.poll() internally.

kappa

Quote from: spinsane on November 25, 2010, 22:09:56
So, do listeners suck? And does Display.update() call Keyboard.poll()?
yes Display.update() calls both Keyboard.poll() and Mouse.poll()

Quote from: spinsane on November 25, 2010, 22:23:02
I actually want Display to NOT poll the keyboard when it updates. I was just wondering if Display.update() calls Keyboard.poll() internally.
if you don't want it to call both Mouse.poll() and Keyboard.poll() then use Display.update(false);

spinsane

Quote from: kappa on November 25, 2010, 22:28:07
Quote from: spinsane on November 25, 2010, 22:09:56
So, do listeners suck? And does Display.update() call Keyboard.poll()?
yes Display.update() calls both Keyboard.poll() and Mouse.poll()

Quote from: spinsane on November 25, 2010, 22:23:02
I actually want Display to NOT poll the keyboard when it updates. I was just wondering if Display.update() calls Keyboard.poll() internally.
if you don't want it to call both Mouse.poll() and Keyboard.poll() then use Display.update(false);

Awesome!

One last question-
Keyboard events are interpreted by Display.processMessages(). So, Display.update() calls Keyboard.poll() AND Display.processMessages()? Does Display.update(false) also call Display.processMessages()?

I want to divorce the Keyboard from the Display as much as possible so that I can have all my input being handled in the logic side of my game-loop without Display doing any frivolous activity. Display inherently combines rendering and input whether you use polling or events- so does Display.update(false) process events for Keyboard?

Are my assumptions correct?



spinsane

Oh---

In what cases would you ever want to use the poll and isKeyDown method?

jediTofu

Quote from: spinsane on November 26, 2010, 22:17:49
Oh---

In what cases would you ever want to use the poll and isKeyDown method?

I personally use Keyboard.isKeyDown in my main loop because:

(1) Thread issues.  Outside of the main loop/thread, just a partial part may (in rare instances) be called before going back to the main loop/thread.  That means KEY_A could have been recorded but not KEY_B, even though they were pressed at the same time.  Also, this means during logic in the main loop it could kick out and then process KEY_B.  So in the first half of the code, KEY_B wasn't down, and in the second half of the code, it is now down changing logic/etc.  I like to know that all of my key checking is before the logic or after, not during it.

(2) Synchronous with the frames.  If key events are not in sync with the frames, you could potentially have KEY_RIGHT for instance calling "x += xSpeed" a dozen times.  Then on the next frame, you've jumped 100 units into a spike.


Both points are avoided with well written code, but I haven't seen any trouble in using isKeyDown in my own code.
cool story, bro

spinsane

Err, I think I was being ambiguous on that last question. Listeners are clearly bad for the reasons you stated. Any technique used to get listeners to function properly with logic is wasteful bloat and unnecessary extra work.

However, between the two methods that LWJGL empowers us to use, I'm having trouble seeing why you would want to use Keyboard.isKeyDown() over a loop with Keyboard.next() and a Keyboard.getKeyEvent() switch. The latter technique, from what I can tell, is superior in every way. Comparisons are only made for every key pressed, instead of for every possible key that could have been pressed- otherwise there doesn't seem to be a difference as far as I can tell.

ryanm

If you're only interested in in specific keys it's simpler to just check them with isKeyDown() every frame instead of checking every event.

jediTofu

Sorry for re-bumping this, but...

Is there any cause for concern for this:
"Please note that the key code returned is NOT valid against the current keyboard layout. To get the actual character pressed call getEventCharacter"
http://www.lwjgl.org/javadoc/org/lwjgl/input/Keyboard.html#getEventKey()

It might just be me, but I find this a bit confusing.  Does this mean that values returned won't match the constants in the Keyboard class?  Or just that we shouldn't expect values returned to be 'A'?  I was under the impression that "getEventKey() == Keyboard.KEY_ESCAPE" would be tantamount to "isKeyDown(Keyboard.KEY_ESCAPE)"
cool story, bro

Fool Running

Quote from: jediTofu on December 02, 2010, 06:06:48
It might just be me, but I find this a bit confusing.  Does this mean that values returned won't match the constants in the Keyboard class?  Or just that we shouldn't expect values returned to be 'A'?  I was under the impression that "getEventKey() == Keyboard.KEY_ESCAPE" would be tantamount to "isKeyDown(Keyboard.KEY_ESCAPE)"
What it means, is that in some keyboard layouts (in this case the French keyboard) the letter 'A' on an English QWERTY keyboard will result in the letter 'Q'. When checking to see if a key is down, you'll need to check for KEY_A. However, since the keyboard layout is not English, if you allow the user to type letters into a text field, you need to call getEventCharacter() so that you correctly get the letter 'Q' instead of assuming that KEY_A will result in a letter 'A'.

EDIT: Made the description clearer.
Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D