USB: Use infinite duration for FFB forces, make the recently added FFB workaround optional
Note
This PR changed a lot over the course of its development as a lot of research was done to ensure this was the correct approach; I ultimately pivoted back to this after digging through everything I could find. As a result, the comment history may be confusing/misleading if read out of context.
Description of Changes
- By default, remove the workaround that bypassed the "effect already running" check for constant forces
- Change the length of all FFB effects to SDL_HAPTIC_INFINITY.
- Add a config option to restore the old workaround for wheels that still need it
Rationale behind Changes
Getting here has been a bit of a journey, and I'm not sure whether this counts as a victory or a surrender. Some wheels are just... outright bugged. Issues we've been seeing with the Simagic wheel appear to be encountered by some other games such as BeamNG, which have some workarounds in their config options that specifically specify that "buggy wheels" might need them.
For the wheels that are bugged-but-less-bugged, adding an infinite duration to the constant force will prevent FFB dropouts. The majority of wheels that don't respect the iteration count still respect infinite durations... except Simagic (at least the Alpha Mini).
The old workaround's approach of restarting the force constantly results in a loss of subjective quality/definition in the wheel, as independently noticed by several testers. As a result, we want that behavior configurable and off-by-default, since most wheels will no longer need it.
This leaves room for followup PRs that improve the workaround by restarting the force every 10-ish seconds, rather than every time the force is updated.
Suggested Testing Steps
- The workaround toggle can be validated via Wireshark with USBPcap. Try this filter:
usb.src=="host" && usb.data_len!=0 && usbhid.data[0]==0x0a
Known problem wheels:
- Moza series: Does not honor DI effect iteration counts, DOES honor infinite durations, experiences detail loss with old workaround.
- Simagic Alpha Mini: Does not honor DI effect iteration counts, does NOT honor infinite durations. Experiences detail loss with old workaround, but still needs it to avoid dropouts (A followup PR can mitigate this)
- Accuforce V2: Does not honor DI effect iteration counts, DOES (appear to) honor infinite durations, and experiences notable micro-drops with the old workaround due to the wheel temporarily pausing when a force is restarted.
Known good wheels for regression testing:
- Thrustmaster T150: Implements DI effect iteration count, implements infinite force durations, gummy gear drive so you'll never notice a loss of detail if one manifests 😅. Ironically, despite being a low-end wheel, it's by far the most compliant with the HID force-feedback standard.
Wheelbase: Simagic Alpha Mini Car used: Toyota Castrol Tom's Supra '00 Tracks driven/lap count/FFB drop occured: - Laguna Seca, 7 laps, no FFB drops - Fuji Speedway '80s, 10 laps, one FFB drop - Test Course, 4 laps, forgot to count but definitely got drops at the end of the banked turns
So starting with the good news. FFB worked just as well as it did with the changes made in #11906 . No drops (mostly) and no cobblestone effect (btw this is not the same as oscillation!). The drops I experienced were very fringe cases that we expected, but maybe these weren't expected with #11915 ? Not sure. In the previous PR, badfont stated that "long-living constant forces will still eventually timeout" and that's consistent with the drops that occured in my testing tonight.
FYI, for the rest of this comment I'm going abbreviate long-living constant forces as LLCF.
On Fuji Speedway '80s, the one and only drop happened somewhere in the middle of the last corner leading to the main straight. I don't remember which lap it occured, but I wasn't able to reproduce it. I guess depending on the line you take you could end up with a LLCF since the last section of this track is essentially an extremely long right turn. FFB quickly recovered as soon as I exited the turn onto the main straight, which again is consistent with what badfont described in the previous PR.
I wish I had thought to do this over the weekend, but Test Course is probably the best... test course lol... for this issue. The long banked turns are prime candidates for LLCFs. I stayed towards the upper section of the banking, driving centered on the outer most yellow line or outside of it. My experience was that staying on the outside created enough FFB "events" to prevent the timeout. The top of the banking is quite bumpy. Whereas staying on the outer yellow line led to timeouts towards the end of the banked turns. And just like with Fuji Speedway '80s, FFB recovered after recentering the wheel on the long straights.
Hearing that the FFB drops are happening again I'm sure isn't what we want to hear, but these were such specific cases that most are likely never to encounter them. With Fuji 80s you have to take a very specific line to recreate the drop. With Test Course, you can easily recreate the drop but I would argue that 99% of GT4 players are not spending their time racing on an oval anyway. The rest of the tracks are "technical" enough that you should basically never encounter an LLCF timeout with this workaround. In my opinion, this is an acceptable flaw until more time and effort can be spent on a more "robust" solution. For what it's worth, I spent about three hours last night playing Nightly Release v2.1.202 and had zero drops in that session.
It'll be later in the week, but I definitely want to do these same tests using Nightly Release v2.1.202. Particularly interested in trying to recreate the LLCF timeout on Test Course with the previous changes.
Also, going forward I think it would be good to have a standardized test that people can follow if they desire. Any car works really, but a higher powered one would get you through the tests laps faster. Fuji '80s and Test Course should definitely be on the track list because of the potential for LLCF timeout. Laguna is one of my favorite tracks, so I'm biased, but in PCSX2 stable you can get a FFB timeout like clockwork going into the Andretti Hairpin. Makes sense because the main straight is more of a long left hander (LLCF!!!).
Sorry for the essay of a response. Hope this feedback is useful!
That's very useful feedback, thank you for the thorough response! This leaves me with a lot of lingering questions, but the first thing I'd want to do is eliminate the most possible confounding factor, which is whether this could possibly happen on the old set of changes too.
At least on paper, dropouts should be able to happen on the old changes too based on how you described, but with the caveat that a single FFB update would've been enough to restore the feedback in that scenario.
There's actually a really good standard place ingame to test some of my assumptions, which would help me know what I'm looking at on your end. If you're able, I'd appreciate if you could do this on the "old" and "new" fix:
- Go into GT4's menu
- In PCSX2's controller settings, "reconnect" the wheel by changing "Wheel Device" to "Not Connected", then back to "Wheel Device"
- You should notice the wheel has some gentle auto-centering provided by the game. Start a stopwatch, hold the wheel at about 90 degrees, and time exactly how long it takes before this auto-centering effect drops out.
Some other thoughts:
You noted that the force didn't return until the effect was stopped by centering the wheel and car, but you also noted that keeping the car in a "bumpier" part prevented the dropout. This is surprising to me, and leads to a few possibilities:
- The Alpha Mini might actually have a "safety timeout" when a force isn't regularly updated, and the force may need to be completely restarted if it times out.
- The Alpha Mini might have a hardcoded cap on constant force lengths, and the bumpy surface may have incidentally issued "stop" commands which naturally required restarting the force.
I wonder if I can get a hold of the wheel's developers via email. Sim racing equipment is a small world, they might actually be able to get my question to an engineer over there.
I think the next step here is probably for me to scrap together a temporary version with some improved FFB logging included. There's already some FFB logs included in PCSX2's debug builds, but if I can get something more human-readable into your hands that'd significantly improve the information I have to work with.
On stream, Jimmy Broadbent was reporting a feeling of heaviness and loss of definition on his Moza R21 with the previously merged workaround, implying the work in this PR may have a positive effect on the FFB feel on Moza wheels.
I'm not an experienced sim racer so it's harder for me to intuitively spot this, but I'm going to see if I can find any places ingame where the difference is sufficiently "obvious" between this approach and the previously merged one, just for the sake of reference.
The approach in this PR should result in more faithful FFB output for devices that suffered quality loss in the previous update. Some testing todos:
- Find out more about the Simagic Alpha Mini's dropouts. Are they caused by a timeout from not receiving updates, or does it sincerely need the effect to be regularly re-run?
- ~~Right now, SetConstantForce has a check that skips calling SDL_HapticUpdateEffect if the force is unchanged. Could this inadvertently result in timeouts if it suppresses duplicate updates? This also depends on how often the game sends updates.~~ (Update: Nah, the game rarely ever sends duplicate requests; it appears to filter them out on its end.)
- Improved logging will help with this, and I'm going to add some when I'm home from work.
After testing the latest pr i can confirm coblestoning is completely fixed on the acuforce v2 but after spending some time last night it seems the ffb is inverted. The steering axis is correct but the ffb seems to be completely opposite what it should be. As I turn In you normally see counter force due to the resistance of understeer but the ffb actually force the steering in towards the corner. Normally if a curb is hit the ffb forces the wheel away from the impact but it's currently turning it inward toward to curb. Would it be possible to add a invert ffb option as a toggle perhaps? If not I may manage a work around by creating a vjoy device and inverting the steering axis and then mapping it correctly in pcsx2. An inverted steering axis toggle could also fix it i think but would require you to initially map the axis inverted and then enable the toggle of that makes sense?
I would also like to confirm the menu spring works correctly and does keep the wheel centered and no longer cuts ffb.
@SOVL22 shouldn't you be able to invert FFB within the driver software? I've never seen the Accuforce software but I'd imagine it has a feature like that. I know Simagic does. Odd issue if this is actually happening though.
@badfontkeming I'll report back some time this week. Fortunately, I have all three versions of PCSX2 installed (stable, previous merge, and this PR) so it'll be easy for me to test all three. I didn't notice the loss in detail that Jimmy is reporting, but I also didn't think too much of it because I was so focused on checking for the drops.
I guess it's also worth noting that Jimmy has a way more powerful wheelbase compared to mine (21Nm vs 10Nm), so the level of detail he can feel will likely be different than what I do. Also, he's an actual race car driver where I'm just playing pretend in my bedroom, lol. Regardless, I'm sure I'll be able to feel the difference once I'm actually looking for it. Anyway, feel free to ping me here or on discord if there's anything you need to chat about. I'm fully on-board with helping you test out future changes.
Not able to invert ffb in the driver no it has many options but no inversion for ffb can't invert the steering axis either without using a third party app like V-joy to create a virtual steering axis invert it map it inverted then un invert it.
I'm 100% certain the ffb is inverted for me though in game.
On Tue, Oct 15, 2024, 5:45 p.m. limeaway510 @.***> wrote:
@SOVL22 https://github.com/SOVL22 shouldn't you be able to invert FFB within the driver software? I've never seen the Accuforce software but I'd imagine it has a feature like that. I know Simagic does. Odd issue if this is actually happening though.
@badfontkeming https://github.com/badfontkeming I'll report back some time this week. Fortunately, I have all three versions of PCSX2 installed (stable, previous merge, and this PR) so it'll be easy for me to test all three. I didn't notice the loss in detail that Jimmy is reporting, but I also didn't think too much of it because I was so focused on checking for the drops.
I guess it's also worth noting that Jimmy has a way more powerful wheelbase compared to mine (21Nm vs 10Nm), so the level of detail he can feel will likely be different than what I do. Also, he's an actual race car driver where I'm just playing pretend in my bedroom, lol. Regardless, I'm sure I'll be able to feel the difference once I'm actually looking for it. Anyway, feel free to ping me here or on discord if there's anything you need to chat about. I'm fully on-board with helping you test out future changes.
— Reply to this email directly, view it on GitHub https://github.com/PCSX2/pcsx2/pull/11915#issuecomment-2415220986, or unsubscribe https://github.com/notifications/unsubscribe-auth/BMCU63MJIKL5MZ7SFD6UEZDZ3WEFZAVCNFSM6AAAAABP5SOO6WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJVGIZDAOJYGY . You are receiving this because you were mentioned.Message ID: @.***>
That's very odd. Anyway, I just remembered you can manually edit the .ini files for your input profiles. It's a common issue for pedals to be inverted. I experienced this with my VNM pedals.
If you go to your PCSX2 root directory, go to the inputprofiles folder and open up which ever input profile you're using for GT4. You can open the .ini files using notepad. Once it's open, scroll down until you find the [USB1] or [USB2] header and underneath that you'll find all of your peripheral bindings. Somewhere in that list will be your steering axis and FFB device.
At the end of these three lines of code, add a ~ to the end if it doesn't already exist. If it does, then delete. Save the file. Relaunch PCSX2 and reassign your input profile to GT4. This worked for my pedals so I'd imagine it will be the same for any of the other inputs. Let me know if it works or if you need further instructions.
Converting the PR to a draft in preparation for some temporary logging additions to trace down any remaining dropout causes.
Some very basic logs have been added that simply print whenever the force has been started, stopped, or updated.
Next time you test dropouts, pull the log up alongside the game and watch it update as you play, so you can see whether the wheel is being updated, as well as whether it's stopped/started, when it drops out on you.
After cranking my wheel to its maximum and testing it out, I think I'm able to reproduce the "feel" issues caused by the last PR. It seems like re-running the effect with each update somehow overrepresents weaker opposing forces caused by things like simulated bumps in the road. I can confirm that this PR doesn't have that issue.
Other research notes:
After digging through the code of the scarce few open source FFB implementations I could find including some other emulators (Model 3's Supermodel, as well as Dolphin), the only things I noted were:
- Both emus seem to use DirectInput directly, rather than going through SDL. After reading SDL's source, however, I don't see any obvious deficiencies or differences in behavior that would explain these issues. Notably, SDL's API closely matches some of DirectInput's semantics.
- Supermodel sets DirectInput's "play immediately" flag when updating the FFB effect, but Dolphin doesn't, so this might be a red herring.
- Both emus send an iteration count of 1 instead of infinite, it's not clear whether this could do anything, but given that many of these problematic wheels don't respect any iteration count, it's possible that non-one iteration counts evoke weird behavior in some wheels?
- Some notes online, someone mentioned that a Fanatec developer suggested that they send constant forces with an infinite duration (which is what this PR does)
- Most modern software is going to very frequently send FFB updates, potentially hundreds of times a second, where GT4 sends them far less frequently than that, sometimes waiting upwards of 150-300ms between updates. Some wheels may misbehave when updated this infrequently, but that feels like a stretch to assume.
- It seems like, for the Driving Force Pro, GT4 uses a spring force or something of the like to center the wheel in menus, not constant forces. I might've gotten it confused with how it handles the non-pro wheel, or I might've just been 110% wrong.
I'm almost tempted to either reverse engineer the game or acquire hardware to sniff the USB line to confirm that the updates are this infrequent, since I know a few PCSX2 developers have shaky faith in the USB subsystems. The latter would be a huge commitment (especially in terms of having even more crap lying around my apartment lmao), so if I do anything it'll probably be reverse engineering.
I mean, supposedly you could even just check out the old wx builds that worked. But stupid question: if those wheels aren't respecting SDL's expectations, why isn't this getting fixed there?
You could just check out the old wx builds that worked.
Yeah should've done that lmao; I just kinda assumed they had similar faults. If I have time after work this is a logical next step. Thanks for suggesting it, I get tunnelvision when working on stuff like this.
But stupid question: if those wheels aren't respecting SDL's expectations, why isn't this getting fixed there?
Not a stupid question at all! Thing is, the iteration count that isn't being respected has a 1:1 correspondence with the semantics of DirectInput, which is what SDL ultimately uses under the hood on Windows.
~~As a result, using DirectInput directly won't solve this problem, and there's nothing to fix on SDL's end. This is an issue of device drivers not respecting the standard because of how rare it is for FFB effects to be looped given that most modern sims send up to hundreds of constant force updates a second.~~
Egg on my face, there is actually something we can accomplish by using DirectInput directly, as SDL isn't passing along the DIEP_START flag into DirectInput's SetParameter function. See below comments for more details
Would it help to periodically update the effect? We could just update it with the same value if the game doesn't send them frequently enough.
That's one of the things I'm looking to determine with those temp logging statements I added, as it should help testers spot whether dropouts are happening in the middle of a "drought" of updates. I'd rather have that info first, just to be confident that it achieved something if I added it.
Beyond that, FFB as a whole is so nonstandard that I don't know whether sending redundant updates will affect the feeling of the FFB, similar to how re-running the effect does.
I looked at the old dinput implementation, and it seems like it sets two flags: DIEP_TYPESPECIFICPARAMS | DIEP_START
Meanwhile, SDL sets several more flags that communicate to DInput that various parts of the effect data are present and valid, such as direction, duration, envelope, etc. It also does not include the DIEP_START flag. This makes me wonder two things:
- Could excess flags for infrequently used features (envelopes, trigger buttons) be evoking driver/firmware bugs? Does including the duration flag inadvertently cut the duration short somehow, etc?
- Could
DIEP_STARTbe the secret here? Does it "magically" reset the FFB timeout? If so, it might be technically possible to scoop out SDL's DInput handle for the effect and meddle with it for testing.
Did a few laps on Test Course during my lunch break.
On the banked turns, I'm pretty much able to keep the wheel at a steady angle. While doing so, the log prints out "FFB Constant Force: Updated: some value". Similar to my previous tests, FFB drops right around the the end of the banked turns which interestingly doesn't change anything in the logs. It keeps printing the "Updated: some value" line.
Once the wheel was centered, sometimes FFB recovered immediately and sometimes it took a few seconds. When it did recover, log printed the following:
FFB Constant Force: STOPPED FFB Constant Force: Updated: "some value" FFB Constant Force STARTED
I noticed on the long straights I'll see the same sequence printed pretty frequently. I'm assuming that happens because of the oscillations that occur with wheel on center at higher speeds. There were a few instances where a small steering input on the banked turns would lead to a STOPPED-VALUE-STARTED sequence. If this happened late enough in the sector then I wouldn't get a FFB drop.
I also tried the hold at 90 deg test in the menus. Did it three times while holding for 1 minutes, but no change in the wheelbase or logs.
Attached file contains some snippets of the log.
I just pushed a super-ugly test commit that digs into the guts of SDL in order to send the DIEP_START flag with each constant force update. Ignore the failing builds, this is a bit more quick-and-dirty for the sake of gathering information so I made no attempt to "correctly" integrate DirectInput into the project.
On my Moza R9, setting the flag when updating a constant force refreshes the duration. Additionally, if the duration elapses, this flag appears to re-start the effect on its own. My gut instinct from rifling through various samples and tutorials is that setting this flag is common practice; I guess this is why. As mentioned before, PCSX2's old DirectInput implementation sends this flag (along with an infinite duration), so I wouldn't be surprised if it's de-facto necessary.
As for whether or not this flag causes a similar feel issue to the previous approach of re-running the effect: I doubt it. From my reading this flag seems like the "normal" way to rapidly update an ongoing constant force for racing sims/etc, so devices are probably equipped to deal with this. I'll still test on my own wheel later to confirm that I can't tell a difference.
If this hack works to resolve dropouts for affected users, we probably want to weigh our options, including potentially modifying SDL (if practical) to add the ability to set this flag when calling SDL_HapticUpdateEffect. I doubt any maintainers would want this hack in the codebase as-is.
It might be worth asking the SDL maintainers about this. If they have a reason to not use that flag it would be good to now it.
Turns out there's a long-closed issue in SDL's tracker that mentions these issues specifically with PCSX2. I'm going to wait to gather a few reports about whether DIEP_START fixes their dropouts, and if it does, I'll approach the SDL maintainers and try to reopen that issue with the new information.
As a result, using DirectInput directly won't solve this problem, and there's nothing to fix on SDL's end.
Be it as it may, you know SDL has a device quirks database right?
This is an issue of device drivers not respecting the standard
At this point I could probably just tell you to try to see if you can find anything meaningful with ghidra... but I think you'll have such a better time asking to @JacKeTUs which has been banging their head on this for months.
As a result, using DirectInput directly won't solve this problem, and there's nothing to fix on SDL's end.
Be it as it may, you know SDL has a device quirks database right?
I should probably go back and edit some previous comments, as I've had to correct myself on exactly that--my most recent build does in fact go directly to DirectInput specifically because SDL isn't passing a flag to DirectInput that we need. Sorry for the confusion, many of my older comments aren't up-to-date with my current knowledge.
If this hack works to resolve dropouts for affected users, we probably want to weigh our options, including potentially modifying SDL (if practical) to add the ability to set this flag when calling
SDL_HapticUpdateEffect.
We build SDL with a batch file, and adding a step to apply a patch would be possible (like we do with shaderc, or used to with qt).
Ideally getting it fixed upstream would be best, but it's still an option
We build SDL with a batch file, and adding a step to apply a patch would be possible (like we do with shaderc, or used to with qt).
Ideally getting it fixed upstream would be best, but it's still an option
Good to know, thanks. In the short term, I'm thinking it makes sense to patch it on our end and confirm the results before sending it upstream; the SDL team has a lot more variables to account for than we do when it comes to using this flag, since they have to account for all software consuming SDL. For a purpose-built PCSX2-specific patch, we should only need a single line to add the flag--due to the simplicity of PCSX2's usage of SDL haptics, I don't even think we need any conditional logic.
BTW, I went ahead and updated the OP since the information has changed significantly since the original writeup. Should be good to catch anyone up if they're not sure where things are.
I tested the "hack", nightly 2.1.202, and stable.
For the hack version, the logs didn't show anything interesting and I didn't get any drops during my usual test on the oval. However, something about this version just feels very off but it's hard to describe other than saying that it feels very vague. There's definitely resistance when I make a steering input, but a lot of the road imperfections that would normally jolt your wheel were very weak. The on-center oscillations that normally occur were also very weak unless I took my hands completely off the wheel. This is not the case with all the other versions.
I think I feel the difference that Jimmy Broadbent was describing between 2.1.202 and stable, but it's hard for me to say for sure because the drops are just so frequent with stable.
I tested the "hack", nightly 2.1.202, and stable.
For the hack version, the logs didn't show anything interesting and I didn't get any drops during my usual test on the oval. However, something about this version just feels very off but it's hard to describe other than saying that it feels very vague. There's definitely resistance when I make a steering input, but a lot of the road imperfections that would normally jolt your wheel were very weak. The on-center oscillations that normally occur were also very weak unless I took my hands completely off the wheel. This is not the case with all the other versions.
I think I feel the difference that Jimmy Broadbent was describing between 2.1.202 and stable, but it's hard for me to say for sure because the drops are just so frequent with stable.
Hmm. That confused me a bit, so I disassembled BeamNG in order to peek at what it does here, since I figured their developers would mind the least out of any. It appears to pass the DIEP_START and DIEP_TYPESPECIFICPARAMS flags with a very similar convention to this test build. I say "appears" because there's a chance I'm wrong, as Ghidra struggled a bit due to how the engine swaps between native and Lua code.
That said, it sets DIEP_START conditionally, and I'm not sure under which conditions. Might be based on a quirks list, might be based on the state of the wheel. Not sure yet.
~~The digging will continue until morale improves. I need to do some live debugging later when I have the time to confirm that I'm not missing something, but it seems like some other titles are actually just using the HID API to send HID commands straight to the wheel, bypassing DirectInput entirely.~~
~~If DirectInput turns out to be uncommon amongst contemporary sims, it'd make sense why their DirectInput drivers would be lackluster.~~
I'm treating this like a red herring for now, this may have actually been a manufacturer-specific SDK. Either way, I finally found a "canon" interaction between a modern PC game and the problem Simagic wheel (see below), which gives me a clearer path forward.
I had the idea to look up any relationship between Simagic wheels and BeamNG, and discovered that Beam's condition for setting the DIEP_START flag is a dropdown in the settings, which they recommend leaving off for performance and ffb quality reasons.
The setting specifically advertises itself as a workaround for "buggy wheels". It's a relief to see other devs acknowledging that this is a bug. Some guides for setting up Simagic wheels specifically instruct the user to enable this flag, so I at least have some closure now. I checked in Wireshark+USBPcap to confirm that it does exactly this.
Following that, I think it probably makes sense to follow their footsteps and just submit to the reality of needing a config setting for this. The wheel's probably just bugged.