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

Add tilt support to DFT processor #70

Merged
merged 5 commits into from
Aug 30, 2022
Merged

Conversation

quo
Copy link
Contributor

@quo quo commented Aug 20, 2022

See quo#3 (comment).

The basic idea is that there is a secondary transmitter about 6mm behind the primary transmitter in the pen. Tilt is calculated from the difference in positions of the signals from the two transmitters. I assume the distance between the transmitters is 6mm for all pens, but it's possible that the distance is encoded in the unknown binary signals somehow. Note that tilt data is only transmitted when the pen is touching the screen, not while hovering.

Additionally, if the distance between the tip of the pen and the primary transmitter is known, the tilt data can be used to calculate the actual tip position. I've guessed this distance to be 2mm based on behavior of my non-tilt pen, and added a correction for it, but the distance may be slightly different for each pen. Since tilt is not transmitted while hovering, with this correction the cursor will shift slightly when the pen starts or stops touching the screen, which may be undesirable.

Since I don't have a pen with tilt support myself, someone who does (@qzed?) should test these changes before they are merged. (I hope I got the azimuth/altitude calculations right, but it's very possible the azimuth will need to be mirrored or rotated further.)

For the tip position correction, please check if the 2mm value works, or if you need to adjust this up or down. Also, please compare the behavior with the tip distance set to 0, especially with things like slow diagonal movements. Is the additional noise from the tilt data (that is added to the position when tip distance is nonzero) noticeable?

yt *= ctx.config.height / (ctx.config.dft_tilt_distance * 10);

auto azm = std::max(0., std::fmod(std::atan2(-yt, xt) / M_PI + 2, 2)) * 18000;
auto alt = (.5 - std::acos(std::min(1., std::hypot(xt, yt))) / M_PI) * 18000;
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: I would prefer if these floats were fully typed out (0.5 instead of .5). Makes things a bit easier to read IMHO.

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 just realized I can change acos to asin, then the .5 isn't even needed. I'll change 0. and 1. to 0.0 and 1.0 though.

Choose a reason for hiding this comment

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

Maybe i'm wrong but if config.width/height is now in cm, then is no need to multiply by 10, and the correct formula is:
xt *= ctx.config.width / ctx.config.dft_tilt_distance;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

dft_tilt_distance is in mm. Should I change it to cm for consistency?

Choose a reason for hiding this comment

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

Sorry i thought that dft_tilt_distance is in cm. My bad.

Copy link
Member

Choose a reason for hiding this comment

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

dft_tilt_distance is in mm. Should I change it to cm for consistency?

I think that would be a good idea.

Copy link
Member

Choose a reason for hiding this comment

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

I'm somewhat wondering whether we shouldn't change things to mm everywhere... could avoid some floating-point inaccuracies if mm is the finest scale we're working with. On the other hand, I think everywhere the constants are applied we're already working with floating-point types, so I guess it doesn't make that much of a difference.

@StollD
Copy link
Member

StollD commented Aug 20, 2022

Thank you! The code looks good to me.

@denyskon
Copy link

denyskon commented Aug 20, 2022

I tried the current version without any modification. In the screencast: https://fileupload.ggc-project.de/download/e60b69cd7bf718e1/#djIuYh0HI0gQOa1YnZkFeQ , you can see the difference between iptsd with and without tilt support. Is there anything I could change to maybe reduce the noise or any data I could provide?

@quo
Copy link
Contributor Author

quo commented Aug 20, 2022

That's kind of what I was afraid of. The problem is that iptsd_dft_interpolate_position() is tuned for a transmitter near the screen, whereas the secondary transmitter is a few mm further away, and the distance varies depending on tilt angle. So you get this wavyness. I have some ideas on how to improve iptsd_dft_interpolate_position(), but it'll probably be a while. Probably best to set dft_tip_distance to 0 for now.

@freak007
Copy link

I tried this patch with dft_tip_distance = 2 and with dft_tip_distance = 0.
Best results are when the value is set to 0. Using with krita and openboard and i have not noticed problems. Palm rejection seems to wok better now and using pen with dialog buttons and interface is much more precise.

@StollD
Copy link
Member

StollD commented Aug 23, 2022

Just a heads up, I just pushed a large set of changes to the master branch. I didn't touch the DFT code, but two other changes impact this PR:

  • ctx.config.width and ctx.config.height are now ctx.display.width and ctx.display.height
  • Both are now stored in centimeters, instead of that weird millimeter * 10 stuff that I pulled from the HID descriptors.

@quo
Copy link
Contributor Author

quo commented Aug 24, 2022

@lxdb @freak007
Thanks for testing. Can you confirm if the tilt direction is detected correctly, and if the angle is roughly correct?

@StollD
On my SP7+ drm mmWidth and mmHeight are 260 and 170, whereas actual size is 259.78 * 173.19 mm. Close enough I guess, but not perfect.

I think it would be better to keep the width/height in config, and only use the drm values if the user has not set them explicitly. I was also planning to add support for the metadata feature report (see linux-surface/intel-precise-touch#4 (comment)), which contains the exact width/height, and also whether axes should be inverted. Adding support for this would also be easier if width/height are kept in the config class.

@StollD
Copy link
Member

StollD commented Aug 24, 2022

On my SP7+ drm mmWidth and mmHeight are 260 and 170, whereas actual size is 259.78 * 173.19 mm. Close enough I guess, but not perfect.

Yeah, it comes from the EDID, and there its rounded to centimeters

I think it would be better to keep the width/height in config, and only use the drm values if the user has not set them explicitly. I was also planning to add support for the metadata feature report (see linux-surface/intel-precise-touch#4 (comment)), which contains the exact width/height, and also whether axes should be inverted. Adding support for this would also be easier if width/height are kept in the config class.

That report looks really nice. Kinda annoys me that there is so much shiny HID stuff on the newer devices, and my SB2 needs all these hacks :P

Moving it back to the config seems like the better idea then. Althought I am not sure if we should keep the DRM values at all then. Having three possible sources seems like a bit of a mess, especially if we only need two (config and feature report) for surface devices.

@quo
Copy link
Contributor Author

quo commented Aug 24, 2022

I think it's fine to have a fallback to the EDID values for the older devices. You could even get rid of the libdrm dependency by just reading the EDID directly:

$ python3 -c 'f=open("/sys/class/drm/card0-eDP-1/edid","rb"); f.seek(21); print(*f.read(2))'
26 17

But since you still need the config files for older devices for InvertX/Y, maybe the fallback wouldn't be that useful.

@qzed
Copy link
Member

qzed commented Aug 27, 2022

FWIW I finally found some time to test this myself. Nice work! Math seems to be correct, tilt does work (at least with my quick test in krita), but as others mentioned it's a bit noisy. But that's a common theme that we'll have to work out in pretty much every other part of IPTSd as well.

@@ -63,6 +63,9 @@ iptsd_dft_interpolate_position(const Context &ctx, const struct ipts_pen_dft_win
// find critical point of fitted parabola
f64 d = (x[0] - x[2]) / (2 * (x[0] - 2 * x[1] + x[2]));

if (std::isnan(d))
Copy link
Member

@StollD StollD Aug 27, 2022

Choose a reason for hiding this comment

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

We are compiling iptsd with -ffast-math, which if I understand it correctly will make the compiler take some shortcuts, that can break NAN handling. So this might not actually be safe. Maybe its possible to catch the situation that would result in a NAN earlier?

(also I just noticed that I forgot to remove the NAN returns from iptsd_dft_interpolate_frequency, whoops)

Copy link
Member

Choose a reason for hiding this comment

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

You're correct: -ffast-math implies -ffinite-math-only, so inf and nan cannot be relied upon. Even if you explicitly set a = std::nan somewhere, I think the compiler can choose to optimize out a std::isnan(a) due to -ffinite-math-only assuming there are only ever valid finite numbers.

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've always thought that isnan should work even with -ffast-math/-ffinite-math-only, although I do know it is broken on older gccs.

Personally though, I think -ffast-math/-ffinite-math-only should never be used. The actual performance benefit tends to be minimal or non-existent. If there is a performance benefit, you can generally achieve it without -ffast-math by rearranging the offending expressions manually. NaNs and infinities are a core part of floating point math and pop up everywhere; if you really want to avoid them you will have to put validation everywhere (for essentially no real benefit).

Copy link
Member

Choose a reason for hiding this comment

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

Personally though, I think -ffast-math/-ffinite-math-only should never be used. The actual performance benefit tends to be minimal or non-existent.

True, the benefits may not exceed the drawbacks, but the alternative at the moment is hand-optimization and I'm not sure if we're at that stage already. Ideally we'd have a benchmark or performance comparison for this. On the other hand, I'm wondering whether the check above should be an abs() check on the divisor instead.

Copy link
Member

Choose a reason for hiding this comment

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

The most recent debate on -ffast-math/-ffinite-math-only and NaNs that I could find is from 2021 for clang/llvm, discussing whether isnan should be optimized out (which it seems it was at that point): https://lists.llvm.org/pipermail/llvm-dev/2021-September/152530.html. Not really sure what the conclusion of that was, but 2021 isn't that long ago so I think we should avoid isnan and the likes with -ffast-math.

Copy link
Member

@StollD StollD Aug 27, 2022

Choose a reason for hiding this comment

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

For what its worth, I would be fine with just removing -ffast-math. But idk how much impact it would have on perfomance.

Copy link
Member

@qzed qzed Aug 27, 2022

Choose a reason for hiding this comment

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

I'd guess for pen processing there shouldn't be much? The biggest impact will probably be in the touch processing. If there isn't any noticeable impact, we should probably disable that for now (or maybe only -ffinite-math-only?). I'd argue we're not really at the performance-optimization stage yet anyways.

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 did a very quick and dirty benchmark. Basically recorded ~1 minute of multitouch data, and ~1 min of pen data, and modified iptsd to parse it 100 times. I'm using gcc 11.3. Below is the user/sys time in seconds for various builds (I've listed sys because fast-math seems to have a small effect on it for some reason):

benchmark touch user touch sys pen user pen sys
debugoptimized with fast-math 33.646 15.626 13.604 25.386
release with fast-math 28.840 15.509 12.937 24.428
release w/o fast-math 30.395 16.124 13.068 24.250
release w/o fast-math, with LTO 21.133 16.112 12.224 24.283

So the optimization options don't really seem to affect the DFT processing. The touch processing becomes only about 5% slower without -ffast-math. And the userspace code becomes about 30% faster if you enable LTO (b_lto=true). So IMO it's fine to disable fast-math and probably a good idea to enable LTO.

Copy link
Member

Choose a reason for hiding this comment

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

Thank you! I also did some benchmarks, and they pretty much show the same results as yours, so I pushed the changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great, thanks. I think this can be merged now.

@StollD
Copy link
Member

StollD commented Aug 30, 2022

Was about to ask if you think this can be merged, only to see that you already answered that. Thank you!

@StollD StollD merged commit b941da4 into linux-surface:master Aug 30, 2022
@quo quo deleted the master branch August 31, 2022 19:35
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.

5 participants