Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force effect corrections part1 #105

Merged
merged 3 commits into from
Jun 20, 2024
Merged

Conversation

MmAaXx500
Copy link
Contributor

@MmAaXx500 MmAaXx500 commented Jun 16, 2024

I read #46 and wondered why they can feel it different with the driver installed inside the prefix, I thought maybe the driver applies some modification to the values that got sent out.

To test the things out I installed a Win10 in VirtualBox, installed the TM drivers, and wrote a POC program to send constant force effect with a range of force levels. I have done the same on Linux and Wine and compared the force levels that got sent out using Wireshark.

Some notable differences between the two:

  • On Windows, the available range is from -10000 to 10000 (DI_FFNOMINALMAX)
  • On Linux, the range is from-0x8000 to 0x7FFF

Things I did:

  • Wrote a POC for Windows using dinput to send a constant force effect with only the lMagnitude (level) set
  • Wrote a POC for Linux to send a constant force effect with only the level set
  • Used the same Windows program under Wine
  • I tried to send new effect uploads instead of updates (Wine doesn't like my idea)

Things I found:

  • Windows driver maps the [-10000;10000] range to [-16385;16381]
  • Windows driver handles <-10000 and >10000 values, and the sent force is -16385 and 16381 respectively
  • Wine doesn't like <-10000 and >10000 values, so I left them out
  • Wine really likes to send updates (even for unchanged parameters) instead of new effect uploads
  • This driver maps the [-0x8000;0x7FFF] range to [-0x8000;0x7FFF], so essentially from 50% in either direction the force is unchanged (I don't feel any difference in the force above 16381)
  • The TM drivers are not installing correctly under Wine, so made no difference for me. Maybe the Thrustmaster SDK (tm_api_lib_*.dll) is involved when a difference is made.

Based on these observations I gathered the requested and sent out values under Windows, Wine, and Linux and made a nice plot out of it. I also modified the driver to roughly match the force levels of the Windows driver by dividing the force level by 2.

Plot of the original and the modified force levels:
ffbplot

All the things I said are relevant only to the T300RS, I have no other wheels available.

Checklist:

  • Constant force
  • Periodic
  • Square wave
  • Triangle wave
  • Sine wave
  • Saw up wave
  • Saw down wave
  • Spring (in part2)
  • Friction (in part2)
  • Damper (in part2)
  • Rumble (in part2)
  • Inertia (in part2)
  • Gain (in part2)
  • Autocenter (in part2)
  • Ramp (in part3)
  • Envelope(s) (in part3)

If you think it's not a complete nonsense, and it's wroth to go through the checklist, let me know.

POCs
ffbtest_win.cpp.txt
ffbtest_linux.cpp.txt

Wireshark captures
const_-8000-7fff_win.pcapng.gz
const_-8000-7fff_linux_wheel.pcapng.gz
const_-10000-10000_wine_wheel.pcapng.gz
const_-8000-7fff_linux_corr2_wheel.pcapng.gz
const_-10000-10000_wine_corr2_wheel.pcapng.gz

Gathered data
ffb.ods

@Kimplul
Copy link
Owner

Kimplul commented Jun 16, 2024

That's really cool, nice work. I suppose since most games are run under Wine, it might make sense to try and match the Wine behaviour to Windows as closely as possible. I would be really interested in seeing how other effects square up, particularly the periodic effect as they seems to be the most popular ones behind the constant force. Sine wave probably the foremost among them.

Unless I'm mistaken, you wrote one program that directly calls DirectInput on Windows/Wine, but did you write the Linux program with direct ioctl calls or did you use a library like SDL? The approach to handling FFB differs between Windows and Linux and I'm not entirely sure how directly comparable they are, using SDL or something along those lines should (at least in theory) produce the same graphs for the same program regardless of operating system. I wrote a program that does something along those lines, see https://github.com/Kimplul/ffbsdl, but I didn't do anywhere near as neat a job collecting data as you have.

Did you notice any significant difference in games with this fix? I believe at least DiRT Rally only uses the constant effect, and I could image that if the wheel starts clipping forces it could make for a pretty jarring experience.

@MmAaXx500
Copy link
Contributor Author

I suppose since most games are run under Wine, it might make sense to try and match the Wine behaviour to Windows as closely as possible.

The good thing (at least in case of the constant effect) is matching the Linux driver corrects the behavior inside Wine too, so both native and Wine games should behave correctly.

Unless I'm mistaken, you wrote one program that directly calls DirectInput on Windows/Wine, but did you write the Linux program with direct ioctl calls or did you use a library like SDL?

The Windows program calls DirectInput the Linux one calls ioctl and write. I attached them as txt to the first post because cpp files cannot be attached for some reason.

The approach to handling FFB differs between Windows and Linux and I'm not entirely sure how directly comparable they are, using SDL or something along those lines should (at least in theory) produce the same graphs for the same program regardless of operating system.

Yeah, using some library that abstracts away the differences between Linux and Windows would make the graphs match visually, but then I have to deal with whatever the library may modify inside the effects. I tried to be close as possible to the hardware/OS and that was the native API. If I send some effect that way, I can tell what may the driver/OS modified inside them before sent to the wheel. If I have a library between me and the wheel, then I have to deal with that too.

The way I compared them is if something correspond to the maximum force in the native API (10000 on Windows, 0x7FFF on Linux) than the command that was sent to the wheel should contain the same force level too.

Did you notice any significant difference in games with this fix?

I haven't played any games with the wheel recently, so I don't have that "yeah, it feels better" comparison, but numerically it should be correct.
In BeamNG there is an FFB graph UI app that shows what the game wants from the wheel. Around 50% of the requested force, the wheel feels really strong, like 100% force. With this modification, there is no this type of unnecessary strong force feedback.

@Kimplul
Copy link
Owner

Kimplul commented Jun 16, 2024

The good thing (at least in case of the constant effect) is matching the Linux driver corrects the behavior inside Wine too, so both native and Wine games should behave correctly.

Good point.

The Windows program calls DirectInput the Linux one calls ioctl and write. I attached them as txt to the first post because cpp files cannot be attached for some reason.

Oh, completely glossed over those, sorry. Thought .txt meant they were data or something.

Yeah, using some library that abstracts away the differences between Linux and Windows would make the graphs match visually,

Might make them match, or at least that would be the intended behaviour. If there are any differences, we know we're breaking some internal assumption about the Linux FFB subsystem that SDL has, thought that would be a reasonably quick check.

If I have a library between me and the wheel, then I have to deal with that too.

Granted, that's also a good point.

I haven't played any games with the wheel recently, so I don't have that "yeah, it feels better" comparison, but numerically it should be correct.
In BeamNG there is an FFB graph UI app that shows what the game wants from the wheel. Around 50% of the requested force, the wheel feels really strong, like 100% force. With this modification, there is no this type of unnecessary strong force feedback.

Makes sense, just wanted to check there weren't any surprises in actual usage. I should probably try out some games I know to work and compare anyway.

Do you want to do more effects in this pull request or do like one effect per PR? I don't think there's any hurry in getting patches out to people but this also seems like something that could be 'rolled out' in steps.

@MmAaXx500
Copy link
Contributor Author

Do you want to do more effects in this pull request or do like one effect per PR?

If we split it up too much, then there will be a force scaling difference between the effects, so some forces maybe stronger than others. This probably worse than having equally strong forces.
If you know some more frequently used effects, then maybe a PR for the frequent ones and one (or more) for the less frequent ones.

@Kimplul
Copy link
Owner

Kimplul commented Jun 16, 2024

If you know some more frequently used effects, then maybe a PR for the frequent ones and one (or more) for the less frequent ones.

Purely anecdotally, the constant effect is the most common one, with the periodic sine a fairly distant second. Autocentering and gain are both common, but in a slightly different way since they generally act as a global setting. I've seen some games use friction and spring effects, but can't remember ever seeing ramps or other periodic effects than the sinusoidal one. Periodic effects are treated pretty much the same by the wheel so correcting one would likely apply to the others, though.

EDIT: Nor can I remember seeing any effects use envelopes.

@MmAaXx500
Copy link
Contributor Author

Part1

  • Constant

Part2 (maybe can be 1 too)

  • Periodic effects

Part3

  • Friction
  • Spring
  • Autocenter
  • Gain

Part4

  • Remaining ones

Just an idea, feel free to change anything you see fit.

@Kimplul
Copy link
Owner

Kimplul commented Jun 17, 2024

Sounds perfectly reasonable either way.

@MmAaXx500
Copy link
Contributor Author

I'm checking the periodic effects now, may take a day or two to complete it.

The constant force is done, the only unknown is the envelopes that the Windows driver places in them if no envelope is added to the effect. Probably irrelevant because they all have 0 length.

I have marked this PR as ready for review if you want to send this out into the wild before I finish the periodic ones.

@MmAaXx500 MmAaXx500 changed the title Force level correction Constant force level correction Jun 17, 2024
@MmAaXx500 MmAaXx500 marked this pull request as ready for review June 17, 2024 20:15
@MmAaXx500
Copy link
Contributor Author

Pushed the first part of the periodic effect fixes, the remaining part is the offset.

@MmAaXx500 MmAaXx500 changed the title Constant force level correction Force effect corrections part1 Jun 20, 2024
@peterlopen
Copy link

Amazing work

}

/* the interval [0; 32677[ is used by the wheel for the [0; 360[ degree phase shift */
periodic->phase = periodic->phase * 32677 / 0x10000;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

32677 is a bit surprising, I would've expected it to be 32768. I guess it could be that the driver is trying to avoid some unwanted behaviour with values at the extreme ranges?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it maps to some lookup table inside the wheel, although the value choice still odd, but it works out perfectly.
Wine has some issues using the correct values through the whole range, especially from 390 to 399 deg. I don't know why it happens, but it had it before too (see spreadsheet).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

32677 + 91 = 32768, I guess the Windows driver just doesn't use the last interval or something.

but it works out perfectly.

Does 32768 still work, or did you mean that the 32677 maximum is the highest working value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we assume the Windows driver is working correctly, then [0; 32677[ -> [0; 360[ and [0; 65354[ -> [0; 720[.
The wheel responds to the whole [0, 65535] interval.

@@ -948,6 +980,8 @@ static int t300rs_upload_constant(struct t300rs_device_entry *t300rs,
int ret;

level = (constant.level * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
/* the Windows driver uses the range [-16385;16381] */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous comment also applies to this, even more weird is the range being asymmetric.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an about -2 offset on the whole range in the constant force magnitude and in the periodic effect offset.
The Windows driver using -2 instead of 0 force/offset in these cases and never uses 0 (see spreadsheet).

I have not replicated this because I think it's a rounding error and saw no difference in behavior.

@Kimplul
Copy link
Owner

Kimplul commented Jun 20, 2024

Would you mind also writing up a small analysis of your findings and methodology? Doesn't have to be as detailed as the one for the constant force, but at a minimum I'd like to see the test programs you used.

@MmAaXx500
Copy link
Contributor Author

The method was the same: try everything and see what it does.

A comparison of what Windows and what this PR uses:

Windows This PR
Constant level [-16385; 16381] [-16383; 16383]
Periodic magnitude [0; 32764] [0; 32767]
Periodic offset [-32767; 32764] [-32767; 32767]
Periodic phase [0; 32677[ in steps of about 91 [0; 32677[ in steps of 1

Notable things

  • The about -2 offset of Constant level and Periodic offset are not replicated
  • This PR uses 0 for 0 input in case of Constant level and Periodic offset. Windows doesn't, it uses -2
  • Wine does not use correct phase values, especially from 390 to 399 degree phase shift (not because of this PR)

Updated spreadsheet: ffb.ods
Windows test program: ffbtest_win.cpp.txt
Linux test program: ffbtest_linux.cpp.txt

@Kimplul Kimplul merged commit 7237249 into Kimplul:master Jun 20, 2024
@Kimplul
Copy link
Owner

Kimplul commented Jun 20, 2024

Played some games (DiRT Rally 1 & 2, ETS2, RRRE) with the corrections, didn't feel any significant difference from before but the data is pretty convincing. I suppose the differences primarily become more noticeable with stronger effects like crashes, which are pretty chaotic by default. Thanks a lot, really interested in seeing what's wrong with the other effects!

@MmAaXx500 MmAaXx500 mentioned this pull request Jun 23, 2024
4 tasks
@MmAaXx500 MmAaXx500 mentioned this pull request Jul 19, 2024
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants