7b99b41ede
Much of design/ described work that has since shipped. Trim each doc to
its durable rationale + still-open items (the code is the source of truth
for shipped detail; git history holds the full originals).
- Shipped plans -> status stubs: stats-capture, gamestream-host-plan,
apple-stage2-presenter, windows-service.
- Trimmed completed-out / open-kept: implementation-plan, hdr-pipeline,
host-latency, gpu-contention (fixed stale status table), game-library,
linux-setup (fixed m0->spike + stale zero-copy claim),
session-aware-host-followups, windows-client-bootstrap,
windows-dualsense-{scoping,game-detection}, windows-virtual-display,
security-review (per-finding status table; #12 still open),
apollo-comparison (shipped backlog collapsed to one-liners).
- Windows-host cluster consolidated: windows-host.md -> redirect into
windows-host-rewrite.md (whose stale scorecard is corrected -- goal1 is
merged, M4 done); windows-secure-desktop.md archived (now a fallback
behind IDD-push primary).
- Kept evergreen: ci.md, gamescope-multiuser.md, windows-build-and-packaging.md.
- New design/README.md: per-doc status table + consolidated open-items
roll-up so nothing is tracked in only one buried doc.
- Repoint 5 code comments to the archived secure-desktop doc path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
98 lines
5.9 KiB
Markdown
98 lines
5.9 KiB
Markdown
---
|
||
title: "DualSense Haptics"
|
||
description: "Feasibility and scoping for audio-driven DualSense haptics."
|
||
---
|
||
|
||
> **Status:** Audio-driven advanced (voice-coil) haptics — **NO-GO, DEFERRED.** The reachable
|
||
> HID work it scoped instead — **adaptive triggers + two-motor rumble** — **SHIPPED** (commit
|
||
> `59edeed`; see CLAUDE.md gamepad section, `inject/dualsense.rs`). This doc is trimmed to the
|
||
> deferral rationale (the three walls) + the conditions that would trigger a revisit.
|
||
|
||
Advanced voice-coil haptics on the DualSense are driven by the controller's **USB audio interface**
|
||
(4-channel surround, the back two channels carry the haptic waveform), *not* by HID reports.
|
||
Emulating that on a Linux host and faithfully replaying it on the Apple client both hit hard walls,
|
||
and the supply of software that actually *emits* these haptics on a Linux host is essentially zero.
|
||
We defer the audio-haptics feature.
|
||
|
||
(Grounded in a 4-agent feasibility read — host USB-gadget viability, DualSense audio descriptors,
|
||
Linux game demand, Apple client render path — 2026-06-10.)
|
||
|
||
## The one distinction that decides everything
|
||
|
||
| Feature | How it's driven | Reachable for us? |
|
||
|---|---|---|
|
||
| Basic rumble (2 motors) | HID output report `0x02`, bytes 3–4 | **Yes** — already parsed; client already has `nextRumble()` |
|
||
| **Adaptive triggers** (L2/R2 resistance) | HID output report `0x02`, bytes 11–22 / 22–33 | **Yes** — already parsed in `dualsense.rs`; just needs the `0xCD` back-channel + client render |
|
||
| **Advanced haptics** (voice-coil actuators) | **USB *audio* interface** — 4-ch, back 2 channels = haptic PCM | **No (for now)** — see the three walls below |
|
||
|
||
The UHID DualSense we already built is **HID-only**. It cannot present the DualSense's *audio*
|
||
interface, so it structurally cannot carry advanced haptics. That's not a bug in our implementation —
|
||
it's the wrong transport for this signal.
|
||
|
||
## The three walls (any one is fatal on its own)
|
||
|
||
### Wall 1 — Host capture needs a kernel rebuild
|
||
To *capture* haptic audio a game emits, the host must present a virtual device that owns the
|
||
DualSense audio interface. The standard way is a composite USB gadget (`configfs` + `f_hid` +
|
||
`f_uac2`) bound to a software UDC (`dummy_hcd`).
|
||
|
||
- ✅ Present & enabled on this box: `CONFIG_USB_CONFIGFS`, `CONFIG_USB_CONFIGFS_F_HID`,
|
||
`CONFIG_USB_CONFIGFS_F_UAC2`, plus `libcomposite`/`usb_f_hid`/`usb_f_uac2`/`u_audio` modules.
|
||
- ❌ **Blocker:** `# CONFIG_USB_DUMMY_HCD is not set` in `/boot/config-7.0.0-22-generic`. No
|
||
`dummy_hcd.ko`, no `/sys/class/udc/`. **No UDC → nothing to bind the gadget to.** Requires a
|
||
custom kernel build to enable `CONFIG_USB_DUMMY_HCD=m`, plus root for module-load/configfs.
|
||
|
||
A lighter alternative exists — a **virtual PipeWire/ALSA sink renamed as the DualSense** (this is how
|
||
the working Linux setups capture the back-2-channels today, via WirePlumber rules). It skips the
|
||
kernel rebuild, but is gated by the same Wall 2 below, and games' audio-device detection is
|
||
hardcoded per-title so it's fragile.
|
||
|
||
### Wall 2 — Almost nothing on a Linux host emits these haptics
|
||
This is the decisive one. The *supply* that would feed our capture barely exists:
|
||
|
||
- **Steam Input (Linux):** no official advanced-haptics support (open feature request as of 2026).
|
||
- **Sony's `hid-playstation` kernel driver:** explicitly does **not** expose VCM haptics or adaptive
|
||
triggers — basic rumble only.
|
||
- **RPCS3:** treats the DualSense as a generic pad; no advanced haptics.
|
||
- **Native Linux games:** effectively **zero** with advanced haptics.
|
||
- **The only working path** is a handful of Proton titles (FF7 Remake, Ghostwire, Deathloop, Animal
|
||
Well, Stellar Blade) via ClearlyClaire's *custom Wine patches*, **USB-only**, Steam Input
|
||
disabled, forced into a 4.0-surround profile, device renamed to match Windows. ~5–10 games total.
|
||
- Bluetooth can't carry it on Linux *or* Windows (Sony's proprietary A2DP repurposing isn't exposed).
|
||
|
||
A host-side capture feature is only as useful as the software willing to drive it. On Linux that set
|
||
is a niche-of-a-niche.
|
||
|
||
### Wall 3 — The Apple client can't faithfully replay it
|
||
Even with a captured waveform, the primary client (macOS/iOS) can't render it well:
|
||
|
||
- macOS GameController exposes the DualSense as a **basic gamepad** — no voice-coil / adaptive-trigger
|
||
access. Those are PS5-only in Apple's stack.
|
||
- CoreHaptics is **discrete, pattern-based** (`CHHapticPattern` events, ≤30 s), **not** a PCM
|
||
streaming sink. Converting a streamed haptic waveform to patterns is lossy — it throws away exactly
|
||
the fidelity that makes voice-coil haptics worth having.
|
||
- There is **no public macOS API** to route CoreAudio to the DualSense's channels 3–4. Doing it
|
||
anyway means private/reverse-engineered APIs that break across OS updates.
|
||
|
||
## Conditions for a future GO on audio haptics
|
||
|
||
Revisit if **all three** change:
|
||
|
||
- A real DualSense is available on the dev box to capture an authoritative `lsusb -v` + the exact
|
||
UAC channel/sample-rate/format layout (today: undocumented, would need reverse-engineering).
|
||
- The host target gains a UDC (custom kernel with `dummy_hcd`, or real hardware OTG) **or** we accept
|
||
the PipeWire-renamed-sink path *and* the title set that emits haptics on Linux grows beyond the
|
||
Proton-patch niche.
|
||
- The client target shifts to one that can render PCM haptics (a Linux/Windows client with direct
|
||
CoreAudio-style channel access, or a future Apple API) — or we accept lossy pattern conversion.
|
||
|
||
Until then the cost/benefit is upside-down: three hard subsystems (kernel, USB gadget, audio
|
||
routing) to serve ~5–10 Proton titles, rendered lossily on the one client we ship.
|
||
|
||
## Open items
|
||
|
||
- **Audio-driven advanced (voice-coil) haptics — DEFERRED.** Explicitly blocked until **all three**
|
||
"Conditions for a future GO" above are met (a real DualSense to capture the UAC layout, a host UDC
|
||
or grown Linux haptic-title supply, and a client that can render PCM haptics). This doc is the down
|
||
payment to revisit then.
|