Skip to content

SDL_DINPUT_HapticUpdateEffect sends unnecessary flags to IDirectInputEffect_SetParameters, which harms FFB quality in certain devices/usecases #12511

@badfontkeming

Description

@badfontkeming

Context

This appears to affect both SDL2 and SDL3 (based on my reading of the code). This was discovered in the PCSX2 project, where a slice of Windows users have experienced a regression in force feedback (FFB) quality since the switch from DInput to SDL, while other users with different FFB wheels haven't noticed a thing.

Most racing titles with FFB, past or present, implement FFB primarily by updating the magnitude parameter of a single, always-running constant force. In a DirectInput implementation, that would look something like this:

// C++ psuedocode

// effect is some existing, already-running constant force effect
auto constForce = (DICONSTANTFORCE*)effect.lpvTypeSpecificParams;
constForce->lMagnitude = new_magnitude;
ref->SetParameters(effect, DIEP_TYPESPECIFICPARAMS); // Note that DIEP_TYPESPECIFICPARAMS is the only flag.

I did a lot of low-level digging into USB PID (Physical Interface Device, HID except for haptics) and DirectInput. I'll spare you the details, so here's all you need to know:

  • There are two PID reports associated with a force: The "basic" parameters, and the "type-specific" parameters (for a constant force, it's just magnitude.)
  • When DIEP_TYPESPECIFICPARAMS is the only flag set, only the "type-specific" report is sent by DirectInput drivers
  • Setting any other flags causes the "basic" report to be sent as well.
    • This appears to aggravate specific wheels, causing what users describe as a "loss of detail" during rapid updates
  • This was tested with an experimental test build that added a toggle to bypass SDL and call DirectInput's SetParameters directly with the DIEP_TYPESPECIFICPARAMS flag. Affected users reported that enabling this setting provided an immediate, noticeable improvement in FFB quality.
    • I validated with Wireshark that this bypass prevented the redundant report from being sent.

Relevant Code

Inside of SDL_DINPUT_HapticUpdateEffect, all of these flags are set when sending an update to DirectInput:

// src/haptic/windows/SDL_dinputhaptic.c

    /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
     *  only change those parameters. */
    flags = DIEP_DIRECTION |
            DIEP_DURATION |
            DIEP_ENVELOPE |
            DIEP_STARTDELAY |
            DIEP_TRIGGERBUTTON |
            DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;

The comment here already acknowledges that this is known to an extent, but I assume it hadn't yet been known to cause problems.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions