berarma / new-lg4ff

Experimental Logitech force feedback module for Linux
GNU General Public License v2.0
315 stars 20 forks source link

Plan #1

Closed berarma closed 4 years ago

berarma commented 4 years ago

The goal for this project is getting a more complete FF module into the mainstream Linux kernel. I hope @mungewell, @edwin-v, @Eliasvan and any others involved in previous similar efforts can contribute ideas and oppinions helping make this possible.

The current module in the Linux kernel only supports constant force and that's a big constraint for some developers since the same wheel in Windows supports all effects. The hardware is capable of doing more but not as much as would be needed.

My first effort (current code in this repository) is an experimental module that tries to provide all the hardware features. For now I'm focusing on the Logitech Driving Force G29 since it's what I have. It doesn't support envelopes and I guess there will be some bugs but it's functional and it showcases what can be done.

It only supports 4 force slots. It's based on ff-core instead of ff-memless since we need to do things differently.

Some hardware quirks I've found with the the new module:

But this approach is constrained too since it only offers 4 force slots and doesn't support some common effects. After some time working with the module I've concluded that the solution should be implemented half by hardware and half by software so that we can have a full feature set with minimal effort and processing. Emulating forces in user space wouldn't allow us fully take advantage of the hardware so it must be implemented in the driver module.

The best way to take advantage of the hardware would be using 3 slots for conditional forces since these can be easily aggregated and they're the hardest to emulate (reading the wheel position is required). The remaining slot would be used to emulate all the other force types (non-conditional) using constant force (maybe ramp force to smooth it).

ff-memless is alreading emulating envelopes and asynchronous start and stop of the forces. This would take it a step further. All Logitech wheels with FF would have a similar set of features as they have in Windows, right now the G920 is the only wheel that has the same features.

Edit: I've just realized it wouldn't be so easy to aggregate spring effects when the center and/or deadband is different. This is a corner case, we could approximate the result or just emulate the spring effect when needed. Anyway, it's the easiest conditional effect to emulate since it only depends on position while the others depend on velocity.

berarma commented 4 years ago

Now that I've acquired some knowledge on the field I've revisited linux-ff-memless-next and I see we've reached the same conclussions. In fact, I think most of the work is already done. The difference is that I didn't mean to write a new kind of ff-memless driver, but this approach is more generic and more useful I guess. I'll try to take Michal's work and make it into an out-of-tree module.

There were issues about getting this into the kernel. I plan to keep it as an out-of-tree kernel so that anyone can use it and test it while we do what we can do get it accepted by the kernel developers.

https://gitorious.org/linux-ff-memless-next/linux-ff-memless-next https://github.com/edwin-v/linux-hid-lg4ff-next

edwin-v commented 4 years ago

The work was indeed already done. Here is the problem that needs to be solved

The ff-memless-next version of lg4ff uses the same approach. However, since it supports all effects, games that use it will use more than one slot. More than one slot means more than one USB packets sent to the wheel. Since lg4ff uses a blocking approach, this leads to horrible delays.

This problem was solved in KLGD which uses a threaded/queued approach. However, this still has minor stability issues.

Note that the G920 driver is also asynchronous and had to be because it actually needs to wait for responses.

Last problem. Emulation of effects is not really a kernel thing. Before you decide on an approach, you may want to check whether it would not actually be denied for inclusion.

Also note that FF_FRICTION and FF_INERTIA are not supported by any hardware. On Windows they are played as FF_DAMPER.

berarma commented 4 years ago

After taking a second look at ff-memless-next I'm impressed by the amount of work that went into it. I'm even more impressed by the fact that it didn't go anywhere which is sad.

As far as I know, it didn't succeed because the linux-input maintainers didn't want to have two implementations for FF and they were also reluctant to replace the existing one because of the possible regressions in all the hardware that depends on it. This is probably because they're understaffed and the target audience isn't big enough.

I recall reading also about performance problems due to device latencies.

What would I've done differently in ff-memless-next:

What would I've done differently in the corresponding hid-lg4ff-next module:

I think that maybe implementing everything in hid-lg4ff as if it were an improved driver would be possibly less scary for maintainers than reimplementing ff-memless. I don't like this solution more than ff-memless-next but it's about trying to get it in the kernel in whatever form.

berarma commented 4 years ago

Hi Edwin. Thanks for commenting, much appreciated.

The work was indeed already done. Here is the problem that needs to be solved

The ff-memless-next version of lg4ff uses the same approach. However, since it supports all effects, games that use it will use more than one slot. More than one slot means more than one USB packets sent to the wheel. Since lg4ff uses a blocking approach, this leads to horrible delays.

Conditional effects are rarely updated. And using the refresh command takes half the time than start/stop commands. I've measured approximately 0.6ms in my system vs 1.2ms. It also depends on the update rate, it doesn't need to be too high. I think the current ff-memless uses a 50ms rate for updating envelopes. It shouldn't be worse than the current implementation if we don't lower the rate below 25ms. Right now, applications are doing it themselves and we don't know at which rate.

This problem was solved in KLGD which uses a threaded/queued approach. However, this still has minor stability issues.

This is a much more complex approach that would be a lot harder to get into the kernel without a lot of discussion, involvement and interest on many parts.

Note that the G920 driver is also asynchronous and had to be because it actually needs to wait for responses.

Last problem. Emulation of effects is not really a kernel thing. Before you decide on an approach, you may want to check whether it would not actually be denied for inclusion.

This was my first thought but when taking it outside of the kernel we can't take advantage of the hardware and everything would be a lot worse. Also, we couldn't target the API kernel anymore since it would provide a very limited feature set in most cases. It would be more complicated to standarize.

Windows is ahead of us by miles by not being so picky on what can be in a driver and what not. This way of thinking can scare manufacturers to support their devices on Linux since adding support would be complex and it would work worst than it does on Windows giving a bad image.

Besides, I bet there's a lot of drivers emulating things in the kernel (as ff-memless already does with delay/length, envelopes and the rumble effect). We can't require all devices to implement the kernel API by firmware.

I plan on making an out-of-tree module ala Windows. Time will tell if we can change some minds. This or we can keep complaining about the bad Linux support compared to Windows. Developers already do complain.

I'm not criticizing the current module, I'm very grateful for it since it's the reason I even bought a wheel and the reason I'm learning about Linux driver programming. The problem is the conservative attitude probably explained by the lack of human resources.

Also note that FF_FRICTION and FF_INERTIA are not supported by any hardware. On Windows they are played as FF_DAMPER.

Yes, I've noticed, thanks. The device itself seems to replace them internally.

berarma commented 4 years ago

One more idea. The lg4ff module can already be non-blocking if we use kernel timers for sending commands to the device. And there's no need for a queue, commands can be dinamically generated at the time the device is ready. I don't really understand the need for KLGD.

edwin-v commented 4 years ago

What would I've done differently in ff-memless-next:

* Conditional effects of the same type can be combined too, at least damper, friction and inertia.

This is not actually possible. Features like saturation, center and deadband make these effects impossible to combine.

Which also means that this is impossible:

* Reserve a fixed slot for each conditional effect.
* Leave the slots playing continuosly with a null force and use the refresh command. The refresh command has half the latency than the start/stop commands in my testing.

* Leave inertia and friction out instead of mapping them to the damper effect.

This has been a matter of debate among us as well. Technically I agree with you, but since games expect the same response from Linux at as Windows, this would create an incompatibility and the few things that actually use conditionals are all ported from Windows. Technically you could consider to emulate them over constant force, but this is harder than it sounds.

Conditional effects are rarely updated. And using the refresh command takes half the time than start/stop commands. I've measured approximately 0.6ms in my system vs 1.2ms.

Trust me on this. Everything that uses them update them all once per frame. You should assume about 2 ms delay for a G27, so updating 4 effects would give you 6 ms. Which on a frame time of 16 ms would be a disaster. I found this out the hard way when implementing conditional effects in lg4ff crushed my ETS2 fps.

One more idea. The lg4ff module can already be non-blocking if we use kernel timers for sending commands to the device. And there's no need for a queue, commands can be dinamically generated at the time the device is ready. I don't really understand the need for KLGD.

I think you might be underestimating things a bit here. :) The fact that you want to be non-blocking with regard to user space already means that there must be a queue somewhere because. Things get way more interesting when you realise that software misbehaves. Take ETS2/ATS for instance. When vsync was off and you dropped into the menu, your framerate would easily go up to something like 1000 fps. Which lead to 4000 commands per second being submitted to the kernel. This is way more than you can ever send to the wheel and leads to a very rapidly increasing queue. This problem is still present in the current in-kernel G920 driver where ETS2 managed to queue up 15 minutes (!) of commands while I was changing a few settings. You might say that a game shouldn't be doing this, bit this is still a kernel driver and this shouldn't really be allowed. There were two fixes. One is to start ignoring commands when the queue isn't empty. However, this makes the wheel response highly unpredictable. The other (which I have implemented in a beta version of the G920 driver) is to start throwing away unsent commands that have become irrelevant.

This whole process is also what KLGD was designed to do. Provide an asynchronous submission scheme with overload protection.

berarma commented 4 years ago

This is not actually possible. Features like saturation, center and deadband make these effects impossible to combine.

Damper, inertia and friction don't have a center nor deadband. Their coefficients and saturation levels can be added up if the effect type is the same. The calculated values might differ from the real values because of rounding but I think it's possible. The spring effect couldn't be combined for the reasons you give.

These are corners cases though, I guess not many games will use several spring, damper, inertia or friction effects.

This has been a matter of debate among us as well. Technically I agree with you, but since games expect the same response from Linux at as Windows, this would create an incompatibility and the few things that actually use conditionals are all ported from Windows. Technically you could consider to emulate them over constant force, but this is harder than it sounds.

This is no problem, they could be combined with the damper effect.

Trust me on this. Everything that uses them update them all once per frame. You should assume about 2 ms delay for a G27, so updating 4 effects would give you 6 ms. Which on a frame time of 16 ms would be a disaster. I found this out the hard way when implementing conditional effects in lg4ff crushed my ETS2 fps.

I trust you and I'm really grateful you share this information with us. The fact is, it can be done, Windows does it.

I think you might be underestimating things a bit here. :) The fact that you want to be non-blocking with regard to user space already means that there must be a queue somewhere because. Things get way more interesting when you realise that software misbehaves. Take ETS2/ATS for instance. When vsync was off and you dropped into the menu, your framerate would easily go up to something like 1000 fps. Which lead to 4000 commands per second being submitted to the kernel. This is way more than you can ever send to the wheel and leads to a very rapidly increasing queue. This problem is still present in the current in-kernel G920 driver where ETS2 managed to queue up 15 minutes (!) of commands while I was changing a few settings. You might say that a game shouldn't be doing this, bit this is still a kernel driver and this shouldn't really be allowed. There were two fixes. One is to start ignoring commands when the queue isn't empty. However, this makes the wheel response highly unpredictable. The other (which I have implemented in a beta version of the G920 driver) is to start throwing away unsent commands that have become irrelevant.

I'm sure I'm underestimating a lot but don't underestimate my perserverance. :)

I think there must be a simpler way to do it. Insane amounts of commands sent to the driver should be filtered out. I don't see the point in having a queue. This is not data that we want to arrive in one piece. We can drop packets that exceed a given rate. The user wouldn't notice their effect anyway.

We can't just ignore commands when a queue isn't empty. We should filter commands to still get a realtime response. Say we get 10 updates in a 25ms period to update just one effect. Only the last update would take effect, the other nine would get discarded. You could argue this wouldn't give the real response the game intends, but the real response the game intends is impossible with the given hardware. We just can do what the Windows drivers probably does, most probably it's approximating the results by software.

Supose a game generates 4000 updates per second, maybe updating 7 effects (I think ET2 uses spring, damper, friction, 3 sines and 1 square ). This would correspond to updating just 3 slots. If we update the slots every 25ms we just need to send 3 commands every 25ms. So 100 comands (4000/40) every 25ms (or any amount) would be converted to 3 commands by the driver by combining the effects and filtering updates that are too close in time. Sending more commands than the kernel can process (without sending them to the hardware) would throttle the application, there's nothing we can do about it. The same would happen in Windows.

This might sound complicated but I think it could be done without resorting to complicated structures with queues. In this case queues are bad because they add lots of latency.

We could also interpolate 10 commands into 1. It would smooth the output out. This could be actually good in some cases since high frequency and high level updates can result in very noisy effects and more hardware wear without improving the user experience. It would be more computationally expensive though.

I'm taking your posts very seriously and I thank you for warning me about the problems that will arise. I'll try different approaches to make the problem solvable in a satisfactory (but never perfect) way. I think that a good design that handles incoming commands in an efficient way coupled with optimization could be what we need.

edwin-v commented 4 years ago

These are corners cases though, I guess not many games will use several spring, damper, inertia or friction effects.

But you can't make the choice for someone else at driver level.

This might sound complicated but I think it could be done without resorting to complicated structures with queues. In this case queues are bad because they add lots of latency.

Queues are pretty light in the kernel, so I wouldn't dismiss it. In fact, the queues in the G920 driver are so low latency that FF plugin for ETS2 can't deal with it and start generating feedback vibration.

I admire your enthusiasm and perseverance is good. But everything that you want has already been made. It's just not in the kernel. Having out of kernel modules doesn't really work either as I have distributed those for years now and they're not really commonly picked up. Personally I sort of agree that these things don't belong in kernel space.

At the end of the day, what needs to happen before anything new is made, is to figure out what we'll actually be allowed to do in kernel space and what needs to be moved to user space. Then figure out what is needed to hook into the evdev system. Technically most of it is already possible. You can already create new evdev devices from existing ones.

This was always my plan for going forward since my beta for the new G920 driver also contained more code than I feared kernel maintainers would be happy with. Real life kinda took over though.

berarma commented 4 years ago

Your comment is disheartening.

But you can't make the choice for someone else at driver level.

But we can have a cut driver that makes you want to go back to Windows.

Queues are pretty light in the kernel, so I wouldn't dismiss it. In fact, the queues in the G920 driver are so low latency that FF plugin for ETS2 can't deal with it and start generating feedback vibration.

The problem is not the queue implementation but the action of queueing realtime events.

I don't really mind if everyone else is happy with the current driver. I really want to make my wheel work like it should. If others, including kernel maintainers, don't want it, ok.

berarma commented 4 years ago

There's already a working module using the ideas here. It's been tested on a number of games, native and using Wine/Proton. I haven't had any latency problems, I don't know if it's because it doesn't queue effects or because the G29 processes commands faster than its predecessors. It needs to be tested on more devices.

Please, open new issues to discuss bugs and improvements.