Two Interesting Performance Characteristics in #Unity
Recently I was working on a rebindable input system for my upcoming game Disturbancy. I've discovered many interesting properties that emerge when working with inputs and controls, but I will share one.
Unity doesn't support gamepad vibration out of the box. The joysticks are not standardized, so the only way to control vibration is to either buy or develop a special, native plugin OR to use XinputDotNetPure library. This library is free and works on Windows with XInput gamepads (up to 4 connected) to control vibration. Using the provided code examples the setup was easy, however, there is a problem. Unity gamepads enumeration takes into account both XInput and non-XInput gamepads, so the index of the currently used gamepad from Unity’s GetAxis/GetButton methods doesn’t correspond to XInput’s index, which only includes XInput-based gamepads. This creates an initial problem: if users have multiple gamepads, which gamepad to send the vibration to? We would need to check if any button is pressed on the gamepad within XInput and set the last used one, that’s the one we can send vibration to. This is done by checking for gamepad state every frame. The problem is,
XInputDotNetPure.GamePad.GetState(int playerIndex) takes more than a millisecond! For orientation, in order to hit 60FPS we have 16ms to draw everything, and this time will include rendering time from the engine, Physix and other internal components. The executing time of player scripts will be comparatively small. So, out of 16ms spending one just on gamepad detection is not wise. I have opted to handle this in a semi-automatic way: the gamepad will be auto-detected at startup and is selectable in options. This covers most common cases while avoiding the performance penalty.
The other tip is about Dictionary performance. I used a dictionary to store bindings for rebindable controls, and keyed them by readonly string. For every control I stored primary and secondary inputs. The problem is, Dictionary lookup is slow. For example, 19 controls (x2 variations) took 0.19ms just for lookups! Using TryGetValue or indexer didn’t make any difference. Out of those,
GenericEqualityComparer`1.Equals() takes 0.10ms, and
GenericEqualityComparer`1.GetHashCode() 0.02ms. These are pretty big numbers just for a couple of lookups. So, I refactored my Input class and was able to shave off 0.33ms off of the frame time. The solution was to use arrays. Arrays and lists have similar performance characteristics, but arrays have a slight advantage as Lists have to divert the indexing to a backing store.
If you're finding this article helpful, consider our asset Dialogical on the Unity Asset store for your game dialogues.