mirror of https://github.com/PCSX2/pcsx2.git
USB: add direct-to-directinput hack for windows for testing
This commit is contained in:
parent
2d5faa627f
commit
6d9b9d44a1
|
@ -60,17 +60,17 @@ namespace usb_pad
|
|||
void SDLFFDevice::CreateEffects(const std::string_view device)
|
||||
{
|
||||
// Most games appear to assume that requested forces will be applied indefinitely.
|
||||
// Gran Turismo 4 uses a single indefinite spring(?) force to center the wheel in menus,
|
||||
// and both GT4 and the NFS games have been observed using only a single constant force
|
||||
// Gran Turismo 4 uses a single indefinite spring(?) force to center the wheel in menus,
|
||||
// and both GT4 and the NFS games have been observed using only a single constant force
|
||||
// command over long, consistent turns on smooth roads.
|
||||
//
|
||||
//
|
||||
// An infinite force is necessary as the normal mechanism for looping FFB effects,
|
||||
// the iteration count, isn't implemented by a large number of new wheels. This deficiency
|
||||
// exists at a firmware level and can only be dealt with by manually restarting forces.
|
||||
//
|
||||
//
|
||||
// Manually restarting forces causes problems on some wheels, however, so infinite forces
|
||||
// are preferred for the vast majority of wheels which do correctly handle them.
|
||||
//
|
||||
//
|
||||
// Known "Problem" wheels which don't implement effect iterations
|
||||
// - Moza series: DOES implement infinite durations
|
||||
// - Accuforce v2: DOES implement infinite durations (deduced from anecdote, not confirmed manually)
|
||||
|
@ -195,18 +195,57 @@ namespace usb_pad
|
|||
if (m_constant_effect_id < 0)
|
||||
return;
|
||||
|
||||
const s16 new_level = static_cast<s16>(std::clamp(level, -32768, 32767));
|
||||
s16 new_level = static_cast<s16>(std::clamp(level, -32768, 32767));
|
||||
if (m_constant_effect.constant.level != new_level)
|
||||
{
|
||||
#ifdef __WIN32__
|
||||
if (bypass_sdl_when_updating)
|
||||
{
|
||||
new_level = new_level * 10000 / 32767;
|
||||
// TODO: Other force types probably need the same type of attention, but constant forces are
|
||||
// the most sensitive to this issue as they're generally going to be updated the most frequently.
|
||||
|
||||
// DANGER! Reading this code may give you radiation poisoning.
|
||||
// It's ugly, but it works. Ideally SDL would be patched to make this unnecessary,
|
||||
// but if patching SDL proves to be too unwieldy, a cleaned-up version of this
|
||||
// might be appropriate.
|
||||
|
||||
// It's not easy to identify by hand if you're not highly experienced, so I'd recommend
|
||||
// average joes use USBPcap via Wireshark in order to verify whether your approach works.
|
||||
// Try this filter:
|
||||
// usb.src=="host" && usb.data_len!=0 && usbhid.data
|
||||
|
||||
// Steal the raw DirectInput references from SDL and update them directly.
|
||||
// Allows us to set our own flags for SetParameters. This is important because
|
||||
// SDL sends unnecessary flags with its updates, which causes unnecessary HID reports,
|
||||
// which may be causing a loss in detail due to wheels unnecessarily reinitializing
|
||||
// the force.
|
||||
|
||||
// Yeah there's raw C casts, I couldn't figure out how to appease the C++ compiler
|
||||
// when using stl casts before I had to stop working on this
|
||||
_SDL_Haptic* real = (_SDL_Haptic*)(m_haptic);
|
||||
auto ref = real->effects[m_constant_effect_id].hweffect->ref;
|
||||
auto k = (DICONSTANTFORCE*)real->effects[m_constant_effect_id].hweffect->effect.lpvTypeSpecificParams;
|
||||
k->lMagnitude = new_level;
|
||||
ref->SetParameters(&real->effects[m_constant_effect_id].hweffect->effect, DIEP_TYPESPECIFICPARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_constant_effect.constant.level = new_level;
|
||||
if (SDL_HapticUpdateEffect(m_haptic, m_constant_effect_id, &m_constant_effect) != 0)
|
||||
Console.Warning("SDL_HapticUpdateEffect() for constant failed: %s", SDL_GetError());
|
||||
}
|
||||
#else
|
||||
m_constant_effect.constant.level = new_level;
|
||||
if (SDL_HapticUpdateEffect(m_haptic, m_constant_effect_id, &m_constant_effect) != 0)
|
||||
Console.Warning("SDL_HapticUpdateEffect() for constant failed: %s", SDL_GetError());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Avoid re-running already-running effects by default. Re-running a running effect
|
||||
// causes a variety of issues on different wheels, ranging from quality/detail loss,
|
||||
// to abrupt judders of the wheel's FFB rapidly cutting out and back in.
|
||||
//
|
||||
//
|
||||
// Known problem wheels:
|
||||
// Most common (Moza, Simagic, likely others): Loss of definition or quality
|
||||
// Accuforce v2: Split-second FFB drop with each update
|
||||
|
|
|
@ -5,6 +5,51 @@
|
|||
|
||||
#include "USB/usb-pad/usb-pad.h"
|
||||
#include "Input/SDLInputSource.h"
|
||||
#ifdef __WIN32__
|
||||
#include <sdl_haptic.h>
|
||||
#include <dinput.h>
|
||||
#endif
|
||||
|
||||
#ifdef __WIN32__
|
||||
// Copied some internal structure definitions from SDL's source
|
||||
// in order to scoop out their inner bits that are excluded from
|
||||
// public-facing headers.
|
||||
|
||||
// It's ugly, but it works for the purposes of this hack/PoC
|
||||
|
||||
struct haptic_hweffect
|
||||
{
|
||||
DIEFFECT effect;
|
||||
LPDIRECTINPUTEFFECT ref;
|
||||
};
|
||||
|
||||
struct haptic_effect
|
||||
{
|
||||
SDL_HapticEffect effect; // The current event
|
||||
struct haptic_hweffect* hweffect; // The hardware behind the event
|
||||
};
|
||||
|
||||
struct _SDL_Haptic
|
||||
{
|
||||
Uint8 index; /* Stores index it is attached to */
|
||||
|
||||
struct haptic_effect* effects; /* Allocated effects */
|
||||
int neffects; /* Maximum amount of effects */
|
||||
int nplaying; /* Maximum amount of effects to play at the same time */
|
||||
unsigned int supported; /* Supported effects */
|
||||
int naxes; /* Number of axes on the device. */
|
||||
|
||||
struct haptic_hwdata* hwdata; /* Driver dependent */
|
||||
int ref_count; /* Count for multiple opens */
|
||||
|
||||
int rumble_id; /* ID of rumble effect for simple rumble API. */
|
||||
SDL_HapticEffect rumble_effect; /* Rumble effect. */
|
||||
struct _SDL_Haptic* next; /* pointer to next haptic we have allocated */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace usb_pad
|
||||
{
|
||||
|
|
|
@ -166,6 +166,9 @@ namespace usb_pad
|
|||
"Off", nullptr, nullptr, nullptr, nullptr, SteeringCurveExponentOptions},
|
||||
{SettingInfo::Type::Boolean, "FfbDropoutWorkaround", TRANSLATE_NOOP("USB", "Workaround for Intermittent FFB Loss"),
|
||||
TRANSLATE_NOOP("USB", "Works around bugs in some wheels' firmware that result in brief interruptions in force. Leave this disabled unless you need it, as it has negative side effects on many wheels."),
|
||||
"false"},
|
||||
{SettingInfo::Type::Boolean, "FfbDirectInputHack", TRANSLATE_NOOP("USB", "HACK: Bypass SDL for FFB updates on Windows"),
|
||||
TRANSLATE_NOOP("USB", "Forgive me, SDL. It's not you, it's me. (It's you). Has no effect on non-Windows platforms."),
|
||||
"false"}
|
||||
};
|
||||
|
||||
|
@ -234,6 +237,11 @@ namespace usb_pad
|
|||
const bool use_ffb_dropout_workaround = USB::GetConfigBool(si, port, devname, "FfbDropoutWorkaround", false);
|
||||
mFFdev->use_ffb_dropout_workaround = use_ffb_dropout_workaround;
|
||||
}
|
||||
if (mFFdev != NULL)
|
||||
{
|
||||
const bool bypass_sdl_when_updating = USB::GetConfigBool(si, port, devname, "FfbDirectInputHack", false);
|
||||
mFFdev->bypass_sdl_when_updating = bypass_sdl_when_updating;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -289,6 +289,7 @@ namespace usb_pad
|
|||
virtual void DisableForce(EffectID force) = 0;
|
||||
|
||||
bool use_ffb_dropout_workaround = false;
|
||||
bool bypass_sdl_when_updating = false;
|
||||
};
|
||||
|
||||
struct PadState
|
||||
|
|
Loading…
Reference in New Issue