On Polling and Input Consistency
This is a rather technical document about USB polling, input latency, and the issues that arise with input consistency. If you only care about the NaxGCC's solutions to this problem, and not the background knowledge, you can skip ahead to the Input Consistency Modes section.
"Input consistency" and "input integrity" are used interchangeably in this document, and refer to the predictability with regards to the frame on which your input will come out.
General Info
The vast majority of human interface devices (HIDs) transmit their states over USB using a technique known as "polling". Essentially, these devices (clients) advertise themselves as supporting a certain polling interval/frequency to whatever device they're attached to (hosts). The host then polls the client at the desired frequency, or at a lower one (= not as often, = more slowly) if the host doesn't support the client's desired frequency.
The Nintendo Switch supports polling USB devices at up to , or once every . A game running at takes to draw a frame, meaning with a polling rate of , a USB device would be able to update its state up to 3 times per frame. Notice how I said "up to", because this is where things start to get wonky.
The reason it's "up to 3 times" and not "3 times, period" is because the USB polling and the game's frame draws are not multiples/divisors of each other, which causes them to continuously shift their offset to one another. Even a polling interval of would be perfectly sufficient, provided the host polls the adapter right before the frame is supposed to be drawn. Due to technical reasons, this is not possible however, so we are stuck with two asynchronous intervals: Polling & frame draws.
even if the host didn't poll the adapter right before the frame is drawn, it would still be fine, because as long as the polling interval is a divisor of the frame draw time, at least input integrity would be guaranteed, even if latency could still be variable depending on when your device was plugged in
Input Integrity
Now, what does this mean for your input integrity? Essentially, in a system where polling intervals are not multiples/divisors of each other, the time window in which your inputs are guaranteed to come out on the frame you'd expect them to is equal to where is the time it takes to draw a frame () and is the polling interval (). So, on the Nintendo Switch, where USB polling is locked and frame time of Smash Ultimate is , our window of time during which inputs are guaranteed to come out on the expected frame is
This then means that during the first of a "frame capture window", your input is guaranteed to come out on the frame you'd expect (the one whose frame capture window is currently open). For any input, the probability that your input will arrive at the expected frame, for any that is the time elapsed (in ms) since the start of the frame capture window, is . As you can see, for , and for .
In plain English, with a game running at and USB polling at , you have an (slightly more than half a frame) window where your inputs are guaranteed to be consistent, everything outside that window is RNG to varying degrees, whether your input will be delayed by a frame or not.
Now, using this we can calculate the overally probability that any input may be delayed by a frame, assuming a uniform distribution of inputs within the frame capture window. As we discussed before, for any , and for any . Since the probability that your input is delayed increases linearly the closer you get to the end of the frame capture window, we can establish that the average of all where is , and we can calculate the overall probability that your input is coming out on the expected frame like so: . This means that, in turn, every input you make has a chance of being delayed by a frame.
However, that's not the end of it...
Joybus
See, the math above assumes that the GCC adapter is the device providing the inputs to the console. However, it is only a middleware, and the true source of your inputs is your controller. The GameCube controller interacts with the GCC adapter pretty much the same as with the OG GameCube, using the joybus protocol, which is the same protocol that N64 controllers use. And its age shows, it doesn't have differential signalling, checksumming, or any of the other goodies other, more modern protocols (like USB) have.
But worst of all, it comes with yet another polling rate. Originally, the joybus protocol is polled at intervals, which would be catastrophic in this case. Luckily, the original GCC adapter made by Nintendo appears to poll at instead. Still not great, but better. Now, you might be thinking to yourself "but Naxdy, is less than , so surely this is a good thing?" well yes, but no. It would be a good thing, if the GCC could be connected to the Nintendo Switch directly, but it cannot, it has to go through the adapter, which has its own polling rate of 8ms.
So, you end up with a system with three independent polling rates: The Switch polls the adapter at intervals, and the adapter polls the controller at intervals, and then the controller has whatever scan rate it has (Phobs have a scan interval FYI). Now, remember how the time frame in which your input is guaranteed to come out on the expected frame was , but what about a system with multiple polling rates? Well, in this case it's where is the number of individual polling rates and are the individual polling rates (in ms).
Again in plain English, if you have multiple polling rates, the frame window in which your inputs are guaranteed to come out on the frame you'd expect them to, is the total time of the frame window minus the sum of all polling rates. So, let's do some addition and subtraction for our use case here: . That's less than half a frame!
This is how it is when you're playing with a PhobGCC (the best GCC currently available) on a first party GCC adapter from Nintendo. Note that third party adapters may very well be much worse than this, because they could poll the GCC at an even lower frequency (= more slowly).
BONUS QUESTION: What if the sum of all polling intervals is larger than the frame time window, i.e. ? That's right, in this case your inputs are always RNG! (good thing that's not the case here though)
The Solution
Since the NaxGCC connects directly to the console and eliminates the joybus protocol entirely, there is no second polling rate. The scan rate of the NaxGCC's sticks is , and the buttons are scanned as quickly as the MCU allows (I've measured as the average ceiling of the time, with the worst outliers being ). While not quite reaching the window length, the sticks have a window of guaranteed input integrity, and the buttons are getting fairly close to at worst (more than half a frame).
This isn't the end of it though. NaxGCC connecting directly to the console brings another advantage with it, namely we can "trick" the console into sampling the controller at a different interval altogether, one that works to our advantage. We can actually pretend to be a "laggy" USB device, by artificially introducing a variable delay in order to ensure the controller actually sends its button state to the console every instead of every . Why ? Because is a divisor of (the game's frame draw time), meaning the controller will be polled exactly twice as often as the game updates, and eliminating the chance of the polling intervals and the game's frame draw cycles offsetting to your disadvantage. This ensures that if you press and hold a button for (exactly 6 frames), it will always translate to 6 frames in-game (with polling at , there is a chance of it registering as 5 or 7 frames instead, as we established above).
unfortunately, due to real world limitations like timing jitter, this is not always the case, but it is the case of the time, see below for more info
The Experiment
So, what real-world impact does this have? I created a test in which I have the NaxGCC press and release a button, both in intervals. Meaning the game should register 6 frames held, 6 frames released, 6 frames held, and so on. I used the training mode modpack to measure the exact number of frames the game actually recognizes the button as held / released. I let this test run for 10 minutes and recorded it to a video, then used a python script to go through every 6th frame and record what the game actually registered using optical character recognition.
When in "OG controller" mode, the inputs were about accurate meaning of the time, the game registered 5 or 7 frames held/released when it should have been 6. This is in line with our math from above. In "input consistency" mode, the accuracy was at ! So, with a NaxGCC, not only do you get the lowest latency possible, since you're eliminating any sort of middleware in form of an adapter, but you also get the highest input integrity possible.
The original full-length videos of the experiment are saved and uploaded to my YouTube channel, in case you'd like to cross-check the results:
Repetition of this experiment is important, because in theory, it is possible to get "lucky" by plugging in the controller during the first of a frame capture window, and then never unplugging it, which would result in a theoretical accuracy. However, this is not a realistic scenario, and the experiment has been repeated multiple times to ensure that the results are consistent.
Input Consistency Modes
The NaxGCC features three separate input consistency modes, which can be cycled through during controller configuration.
Regular Mode
In regular mode, the NaxGCC will not attempt to modify the polling rate of the controller, and will instead send the controller's state to the console as soon as it is requested. This is the same behavior as the vast majority of other USB controllers and adapters when connected to the Nintendo Switch. This mode is not recommended for competitive play, as it carries a chance of your inputs being misinterpreted (see Input Integrity for details).
PC Mode
Same as Regular mode, except it asks to be polled at 1000Hz instead of 125Hz. This is useful if you're planning to use your controller on pretty much any device that isn't the Nintendo Switch. This mode carries the lowest latency, so if the device you're connecting to supports it, it is recommended to use this mode.
This is not recommended for use on the Switch, because it carries no benefit over Regular mode, and may actually lead to incompatibilities with other connected devices.
Consistency Mode
In consistency mode, the NaxGCC introduces a variable delay in order to wait responding to a poll request from the console until exactly (half a frame) have elapsed since the last status update. This ensures that the controller is polled exactly twice as often as the game updates and (nearly) eliminates the chance of your inputs being misinterpreted. This is the recommended mode for competitive play, and is the default mode for the NaxGCC.
While this is not exactly in-spec behavior for a USB device, it is still within the realm of what is generally considered to be "acceptable" with USB, and has been extensively tested to ensure that it does not cause any issues with the console.
"Super Hack" Mode
In "super hack" mode, the NaxGCC abstains from responding to the console's poll requests until the controller state has changed, and at least have elapsed since the last poll request. This ensures that, for inputs made more than apart, the latency is minimized, since the state is immediately dispatched to, and interpreted by, the console, at a slight cost to input integrity for inputs made less than apart.