YukMingLaw / ArduinoJoystickWithFFBLibrary

An Arduino Joystick Library With Force Feedback Feature
GNU Lesser General Public License v3.0
208 stars 55 forks source link

When used, causes MASSIVE FPS drop on certain games. (but works otherwise) #37

Closed ThePixelArtist closed 1 year ago

ThePixelArtist commented 2 years ago

Hello, I have made a code for my wheel, which works just fine, but when i turn on games like DiRT rally, Assetto Corsa or Euro truck simulator 2, it causes a massive FPS drop. before i was getting 75 FPS in DiRT, but when i just plug in the arduino it instantly jumps to 8FPS. only game that i have registered no issue with FPS what so ever is BeamNG Drive.

The weird part to me is that when i disconnect my arduino the fps jump back up instantly, but if i connect it back again it immediately slows down again to 8FPS, also mind that the arduino isnt even doing anything when i connect it, the game seeing my arduino as a wheel with force feedback is enough for it to be slown down. Also mind the force feedback is working just fine in every game mentioned, but the FPS are not

I use arduino ProMicro is anyone was wondering

sgtnoodle commented 2 years ago

I've had no problems playing ETS2 and DiRT Rally with my wheel. (I built my wheel specifically for ETS2!) I forked this library so that I could add some minor features and fix some bugs, but I don't think I fixed anything that would have caused the problem you describe.

When you say your Arduino isn't doing anything, do you mean your loop function is literally empty? At the least, you probably need to call Joystick.sendState(); periodically. If you aren't, that's probably the problem. When the game requests the latest joystick positions, the HID joystick driver is probably trying to read the USB endpoint and NACK'ing over and over until it times out.

Here's what my loop function looks like. I rate limit it to run no faster than 1Khz, but otherwise just let it run as fast as it can.

void loop()
{
  static unsigned long last_loop_run = micros();
  const unsigned long now = micros();
  const float dt_sec = static_cast<long>(now - last_loop_run) * 1e-6f;
  if (dt_sec >= 0.001f)
  {
    last_loop_run = now;
    const long count = myLS7366.read_counter();
    const float pos = count * (-static_cast<float>(AXIS_HALF_RANGE) / COUNT_HALF_RANGE);
    pos_filter.update(pos, dt_sec);
    vel_filter.update(pos_filter.rate(), dt_sec);

    int16_t axis_pos;
    float hardstop_dc = 0.0f;
    if (pos_filter.output() > AXIS_HALF_RANGE)
    {
      axis_pos = AXIS_HALF_RANGE;
      hardstop_dc = max(-HARDSTOP_DC, (AXIS_HALF_RANGE - pos_filter.output()) * (HARDSTOP_DC / HARDSTOP_FADE));
    }
    else if (pos_filter.output() < -AXIS_HALF_RANGE)
    {
      axis_pos = -AXIS_HALF_RANGE;
      hardstop_dc = min(HARDSTOP_DC, (-AXIS_HALF_RANGE - pos_filter.output()) * (HARDSTOP_DC / HARDSTOP_FADE));
    }
    else
    {
      axis_pos = round(pos_filter.output());
    }

    float feedback_dc = 0.0f;
    myeffectparams[0].springPosition = axis_pos;
    myeffectparams[0].damperVelocity = bound(pos_filter.rate(), -MAX_RATE, MAX_RATE);
    myeffectparams[0].inertiaAcceleration = bound(vel_filter.rate() * 0.1f, -32767, 32767);
    myeffectparams[0].frictionPositionChange = myeffectparams[0].damperVelocity;
    Joystick.setEffectParams(myeffectparams);
    Joystick.getForce(forces);
    Joystick.setXAxis(axis_pos);
    Joystick.sendState();
    digitalWrite(LED_PIN, forces[0] >= 255 || forces[0] <= -255);
    feedback_dc = forces[0] * (FEEDBACK_DC / 255);

    const float feed_forward_duty_cycle = pos_filter.rate() * (ICR / MAX_RATE);
    const float duty_cycle = bound(feed_forward_duty_cycle + hardstop_dc + feedback_dc, -ICR, ICR);
    float brake_duty_cycle = 0.0f;
    if ((duty_cycle > 0 && duty_cycle < feed_forward_duty_cycle) ||
        (duty_cycle < 0 && duty_cycle > feed_forward_duty_cycle))
    {
       // We're regen braking. We need to turn on the brake resistor.
       brake_duty_cycle = bound(BRAKE_MULTIPLIER * sqrtf(feed_forward_duty_cycle * duty_cycle - duty_cycle * duty_cycle), 0, ICR);
    }    
    digitalWrite(DIR_PIN, duty_cycle < 0);
    analogWrite16(PWM_PIN, duty_cycle >= 0 ? duty_cycle : -duty_cycle);
    analogWrite16(BRAKE_PIN, brake_duty_cycle);
  }
}
s-zenichev commented 2 years ago

I was experimenting on this in Project Cars 2. With all FFB functions disabled in my code, the game doesn't even start. With the original Joystick (without FFB) library, my code works just fine. The problem must be in that direction somewhere.

YukMingLaw commented 2 years ago

I tested the lib in Assetto Corsa & Project-Cars 2, it works well. But in Horizon 4 it seems would slow down the fps in the game, the same probolem as you said.It needs some time to fix.😂

ThePixelArtist commented 2 years ago

Thanks for looking into this, i will be following the development of this issue with great interest

YukMingLaw commented 1 year ago

hello,bug seems be found!I set Y axis enable variable as False,and it work normal.I guess the Horizon 4/5 not support the second force feedback Axis to cause this problem. I will update an new API to set the force feedback Axis enable or not.

sgtnoodle commented 1 year ago

That's interesting. In my steering wheel project, I originally omitted the Y axis from the joystick. It worked in the games I tried, but none of the force feedback test applications worked with it. I enabled the Y axis but with a dummy position value, and the test applications started working.

I suppose you're talking about some other sort of axis enable, that isn't already part of the API?

On Wed, Jul 13, 2022, 6:02 PM York Law @.***> wrote:

hello,bug seems be found!I set Y axis enable variable as False,and it work normal.I guess the Horizon 4/5 not support the second force feedback Axis to cause this problem. I will update an new API to set the force feedback Axis enable or not.

— Reply to this email directly, view it on GitHub https://github.com/YukMingLaw/ArduinoJoystickWithFFBLibrary/issues/37#issuecomment-1183835432, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACNKBVIWA3U4U4MG67FHJNTVT5RLHANCNFSM5LPM7GZQ . You are receiving this because you commented.Message ID: @.***>

YukMingLaw commented 1 year ago

I found the final problem, which has nothing to do with the Y-axis enable_setting. Mainly the axis value sending frequency and the data frequency of obtaining force feedback do not conform to the game's internal force feedback calculation system, I guess that the force feedback calculation system used in some games is a synchronization mechanism, each axis data update will recalculate the force feedback value, and then send it to the device, and finally update the screen, which leads to a decline in FPS. which can fix the problem as follow:

void loop(){
//do something
  interval = interval  + 1;
  if(interval % 50 == 0){ // update the X Axis value at regular intervals
    Joystick.setXAxis(value); //update X Axis value
    interval = 0;
  }

  effectparams[0].springMaxPosition = ENCODER_MAX_VALUE;
  effectparams[0].springPosition = value;
  effectparams[1].springMaxPosition = ENCODER_MAX_VALUE;
  effectparams[1].springPosition = 0;
  Joystick.setEffectParams(effectparams);
  Joystick.getForce(forces);
//do something
}