Skip to content

Releases: compdoge/gxr-root-ft

v1.4.0 - gaze conditioning and VD-freeze fix milestone

Choose a tag to compare

@compdoge compdoge released this 27 Oct 04:32

Galaxy XR face tracking relay v1.4.0 - gaze conditioning and VD-freeze fix milestone

The owner reports that v1.3.14 resolved the VD tracking freeze in testing.
v1.4.0 retains that Manager-cgroup escape unchanged. The new wink/squint work
below has passed offline checks but has NOT been validated on a live headset.

Gaze and wink changes

  • Fix a real gaze-filter time-unit bug: earlier versions supplied milliseconds
    to Hz-based One Euro equations, effectively passing noisy samples through.
    At 16 ms, the narrowed-lid filter accepted about 97.2% of each newest sample
    instead of its intended 3.4% (before the adaptive term).
  • Filter only new samples from each eye, with independent freshness, seconds
    for timing and previous-raw-sample velocity. One active eye no longer hides
    stale direction data from the other.
  • Do not seed gaze from an untrusted first sample. Once a good gaze exists,
    preserve it during distrust or direction expiry; reset velocity and bound
    reacquisition instead of snapping to centre or to the new raw direction.
  • Add wink entry/exit hysteresis and distrust the closing eye earlier, within
    the existing lid/pitch guards. Reset the fold after missing face data and
    remove the artificial half-wink first step after a gap.
  • Reject malformed eye indices and non-finite direction components, and correct
    the WebUI wink-threshold tuning hint.

True independent per-eye gaze, packet format, firmware probe offsets and the
PC-side module are unchanged. No vendor eye-state replica or new probe was added.

Validation and limitations

Offline module/relay suite: 142 passed, 0 failed, including a portable gaze test
with 58 deterministic checks. Host builds completed without warnings; the
AArch64/API 29 binary was rebuilt with Android NDK r29 and reports v1.4.0.
Archive members, CRCs, version metadata and packaged binary were verified.

The existing smoothing defaults were retained rather than retuned. Correcting
the math now introduces real smoothing latency, particularly at the 0.35 Hz
narrowed-lid setting. Wink/squint feel, the heuristic guards and reacquisition
still need headset validation. Direction-only loss can leave the last good gaze
held while the other expressions continue. Successful VD testing does not prove
that every interruption on every device has the same cause.

For Galaxy XR SM-I610 firmware I610UEU2AZF3 and its exact-build probes. Installation
alone does not establish that the updated relay is running; check the current
log for v1.4.0 armed. No headset installation or activation was performed as
part of preparing this release.

Logs are not anonymized: they can include LAN IPs, process/app identities and,
with verbose mode enabled, eye measurements. Obtain the wearer's consent and
review or redact exports before sharing them publicly. No technical capture
from community reporters is required.

v1.3.14 - candidate Manager cgroup freeze mitigation

Choose a tag to compare

@compdoge compdoge released this 25 Oct 05:27

Galaxy XR face tracking relay v1.3.14 - candidate fix for Action/WebUI freezes

Two separate v1.3.13 log exports showed long relay no-progress gaps after
Manager-origin starts. The Android app-cgroup freezer is the leading explanation,
but those logs did not record a freezer transition; a blocked kernel wait is
still possible. This build is a mitigation candidate, not a confirmed fix.

  • On a Manager Action/WebUI start, the supervisor checks for the expected
    per-app cgroup v2 leaf and mount. Before starting its sampler and relay, it
    attempts to move only itself to a dedicated non-app cgroup, checks that the
    destination is not frozen, and verifies its new membership. Its children then
    inherit the new cgroup. A boot start outside an app leaf is unchanged.
  • If the layout or write is unavailable, the supervisor logs a warning and uses
    the previous launch path; in that case this mitigation is not active. If a
    write leaves membership uncertain and rollback cannot be verified, it does
    not launch a relay from an unknown state.
  • The external sampler now logs whether it started or could not read the relay's
    initial process state, helping separate a missing sampler from one suspended
    alongside the relay on a future export.
  • No packet format, probe offsets, watchdog policy, PC-side module, or other
    cgroup controllers were changed. The separate one-eye-closed complaint is
    not addressed here.

For Galaxy XR SM-I610 firmware I610UEU2AZF3 and its firmware-specific probes.
Built with Android NDK r29. Offline module tests passed 124/124, the AArch64
binary was rebuilt with v1.3.14 embedded, and the ZIP contents were verified.
The cgroup migration has NOT been validated on a live headset: the mount layout,
KernelSU permission to migrate, and whether it prevents the reported freeze
remain unknown. Check the relay's process: and cgroup: lines in an export to
see whether the mitigation actually activated. Installation alone does not
prove an updated relay is running; check for v1.3.14 armed in the current log.

Logs are not anonymized and may contain LAN IPs, process/app identities and,
with verbose mode enabled, eye measurements. Get the wearer's consent and
review or redact an export before sharing it publicly. No technical test from
community reporters is required.

v1.3.13 - relay dropout diagnostics

Choose a tag to compare

@compdoge compdoge released this 24 Oct 18:29

Galaxy XR face tracking relay v1.3.13 - diagnostic logging update

This build adds evidence for intermittent tracking dropouts. It does not claim
to fix the underlying freeze. The reported v1.3.12 logs showed relay no-progress
gaps, but did not identify whether the process was frozen, blocked, or starved.

  • The heartbeat separates the five probe event counts and records event enable
    state, effective trace ring size, lost-event notices, read totals, scheduler
    CPU/run-queue time, maximum loop gap, and dilation state-write timing.
  • Startup logs bounded full cgroup context, more parent ancestry, and monotonic
    timestamps. Watchdog gap lines carry their own timestamps.
  • A low-rate supervisor sampler records a bounded process-state/wchan/freezer
    summary when CPU ticks stop advancing. This is a clue, not proof of a freeze.
  • Diagnostics do not change the packet format, probe offsets, ring-size request,
    re-arm policy, or watchdog restart policy.

For the Galaxy XR I610UEU2AZF3 firmware/probe offsets only. Built with Android
NDK r29 for AArch64. Offline tests passed 120/120; this build has not yet been
validated in a live headset session. The binary reports v1.3.13 in the armed
line. After installation, use the module's Action button when you are ready to
start the updated relay; installation alone does not switch a running process.

If tracking stops in ordinary use, note approximately when and export both the
current and previous module logs from the WebUI. Exports are NOT anonymized:
they can include a private LAN IP and app/cgroup identity. Verbose logs contain
gaze, eyelid, and pupil measurements. Obtain the wearer's consent and review or
redact logs before posting them publicly. No special technical test is required.

v1.3.12 - relay stall diagnostics and reliability

Choose a tag to compare

@compdoge compdoge released this 22 Oct 00:41

Galaxy XR face tracking relay v1.3.12

This is a reliability and diagnostic update for reports that face tracking stops
during a Virtual Desktop session and appears to return after opening KernelSU
Manager. The available logs showed a watchdog restart and a separate lock
collision; they did not establish a single root cause. This release is for field
testing, not a claim that every interruption is fixed.

  • The startup log records a bounded process/parent/cgroup summary. It helps
    distinguish a boot-started relay from one started in an app context without
    recording credentials or environment variables.
  • A loop gap over 15 seconds is logged as a possible period when the process
    was not scheduled or was blocked. The relay resumes instead of immediately
    exiting on return. Shorter watchdog stalls still trigger supervised restart.
  • The heartbeat reports a cached probe-health snapshot and its age rather than
    repeatedly reading tracefs outside the protected maintenance path.
  • Probe repair now requires repeated confirmed misses. Failed reads never
    trigger re-arming; automatic re-arms are paced and stop after repeated failed
    repairs rather than repeatedly clearing device-wide uprobes.
  • The supervisor briefly retries a single-instance lock collision and logs an
    explicit reason if another relay still holds the lock. A permanently stuck
    holder is not killed or automatically fixed by this change.

Install this zip through KernelSU Manager and reboot to run the updated sender.
After a reboot, unlock the headset before expecting ADB to work. The binary
reports v1.3.12 in its armed line. If tracking still stops, the module WebUI's
Export logs button preserves the current and previous logs for diagnosis.

v1.3.11 - a dilation range that sticks, eyes at their own rate

Choose a tag to compare

@compdoge compdoge released this 21 Oct 02:22

Galaxy XR face tracking relay v1.3.11

Eye and pupil rate, and a dilation range that stays put.

Faster eyes

  • Gaze and pupil used to be recomputed only when the headset delivered a face
    expression frame, so the whole frame advanced at the vendor's ~15 Hz even
    though eye direction arrives at ~57 Hz and pupil detections at ~42 Hz. The
    relay now recomputes on its transmit clock (16 ms at the default 60 Hz), so
    gaze latency drops from ~33 ms mean / ~67 ms worst to within one frame and
    the gaze filter finally sees its real input rate. Expression values still
    change at the vendor's ~15 Hz - that limit is upstream of this module.
  • Heartbeat now reports cmp= (frames computed), so this is visible on device:
    cmp should track tx (~60/s), not ev.

Dilation range that sticks

  • The window VRCFT normalizes pupil dilation against is now remembered across
    restarts (dilmin/dilmax), so a session does not start from a nearly empty
    window and slowly widen for the first minute.
  • The seed is the median of the first 8 clean frames instead of one frame.
  • A new extreme must still match the raw pupil reading when the dwell expires;
    one bad detection used to be committed through the smoothing tail and
    squeezed the range for the rest of the session.
  • A stale extreme is slowly healed back toward the recently observed range, so
    one stray cannot survive forever.
  • WebUI: new "Dilation range" card with Lock / Unlock / Reset, plus min/max number
    fields and Use these values for setting the range without calibrating (that
    writes both numbers and locks them). Lock freezes the learned window - the usual
    choice once it has settled, and the fix for one stray reading squeezing it;
    Unlock adapts again after a large change in light level; Reset forgets and starts
    over. The page now refreshes itself every 10 s and when you return to it, instead
    of reading the status once at load.
  • --dil-min / --dil-max set the window by hand (both together lock it).
  • New in the verbose pupil lines: lk=1 marks a locked range.

Nothing changed in the wink path (v1.3.9's wink fix stands) and the transport,
dormancy and watchdog behaviour are unchanged.

Install: KernelSU Manager -> Modules -> Install from storage, then reboot. After
the reboot, enter your PIN on the headset before adb will answer again.

gxr-root-ft v1.3.9 (wink fix, no more I/O stalls)

Choose a tag to compare

@compdoge compdoge released this 17 Oct 00:24

gxr-alvr v1.3.9 - winks close properly, and the relay no longer parks on I/O

Headset module only. Install the zip in the KernelSU Manager and reboot. The PC
side (VRCFT-ALVR-UE) is unchanged.

Winks now close all the way

A one-eye wink used to leave the avatar's eyelid part-open, and the reason was
measurable: the raw eyes_closed value the headset reports for a wink tops out
around 0.6 (openness ~0.4), while a full blink in the same capture reaches exactly
1.0. The device folds the lid tightener into that value in a step that runs after
the point this module reads.

The sender now restores that, and only that: when one eye is clearly closing while
the other stays open, the eyelid is driven shut over 200 ms. Both eyes closing (a
blink) or both narrowing (a squint) show no left/right difference, so neither is
touched - which is what makes this different from the older behaviour where a
squint made the lid snap shut.

Two knobs, both in the WebUI's Developer options:

  • --wink-gap (default 0.35): how big the left/right difference has to be to count
    as a wink. Raise it if a wink is missed, lower it if a partial one-eye closure
    snaps shut. 0 disables the fold.
  • --wink-ms (default 200): how long a detected wink takes to close fully.

The relay no longer parks on I/O

Found while reading a diagnostic capture: two relay restarts at about 85 seconds of
uptime with verbose logging on, each after 40-56 seconds in which the main loop
could not service its own watchdog. Three fixes:

  • the UDP socket is non-blocking, so a full send buffer (Wi-Fi hiccup, roaming
    access point, busy driver) drops a datagram instead of parking the transmit tick;
  • a completed log write now counts as loop progress, so a filesystem stall is not
    mistaken for a wedged relay - restarting only rewrote the same log and made the
    tracking gap longer;
  • verbose logging is batch-written and turns itself off if single writes take more
    than a second, saying so in the log, rather than stalling the sensor loop.

If you have seen face tracking "freeze for a bit and then come back", this is very
likely what that was.

Logs are readable again

The log is created 0666 after each rotation. A restart driven from the WebUI runs
as root with a restrictive umask, which was leaving a root-only file that
adb pull could not read - exactly why one diagnostic capture could not be
retrieved. Export logs also chmods what it writes to /sdcard/Download.

Not in this release

The separate report that pupils contract while squinting is not fixed, because
it does not reproduce: on the device measured here a squint costs the emitted pupil
about 12% (4.22 mm -> 3.72 mm), occlusion is flagged in 95% of deep narrowing, and
dilation never drops below 0.1. The pupil and dilation code is unchanged. That
report needs a capture from the person seeing it - and then it gets fixed on
evidence rather than on a guess.

gxr-root-ft v1.3.8 (idle dormancy, 6 MB ring buffer, Pause/Export)

Choose a tag to compare

@compdoge compdoge released this 15 Oct 23:35

gxr-alvr v1.3.8 - cooler when idle, and a Pause button

Headset module only. Install the zip in the KernelSU Manager and reboot. The PC
side (VRCFT-ALVR-UE) is unchanged.

Lighter when it is not doing anything

  • It idles now. With no face events for a second, the relay drops from the
    full transmit rate to a 10 Hz keepalive -- still sending a relaxed neutral
    frame, so the PC side sees no difference -- and returns to the full rate the
    instant real events arrive. Before this it polled and transmitted at the full
    rate around the clock, tracking or not.
  • The trace ring buffer went from 16 MB per CPU to 1 MB per CPU: about
    96 MB -> 6 MB of kernel memory, unreclaimable, held for as long as the
    module runs. The old size only existed to survive a bug fixed in v1.3.7.
  • If the system kills the relay, it now stays stopped. rc=137 (SIGKILL --
    usually the low-memory killer, or a policy shedding background work) is treated
    like "another copy is already running": the supervisor logs it and stops. It
    used to rebuild every uprobe and ask for the ring buffer again, which is exactly
    the load the system was trying to shed. Start it again from the Manager's
    Action button or the WebUI.

Pause, Resume and log export (WebUI)

  • Pause stops the supervisor and the relay, and the relay disables its probes
    as it exits, so a paused module really is not tracing anything.
  • Resume starts it again. A pause lasts until Resume or a reboot.
  • Export logs copies the current log, the previous run and the install log to
    /sdcard/Download.

Why Pause matters: the Galaxy XR is thermally marginal and VR streaming
already runs it near its limit, so "is the face-tracking module costing me heat or
frames?" deserves a measurement, not a guess. Run the same game, in the same
place, with the same stream settings, once paused and once running, and compare
temperature and FPS. No reboot, no zip swap.

Also

  • The 3 s "no probe events" self-heal no longer clears and rebuilds every uprobe
    on the device whenever a session ends. It reports a quiet episode and lets the
    health check decide, because it can tell "our probe set was changed under us"
    from "nothing is tracking". Heartbeat field stalls= is now quiet=.
  • Watchdog: arming or clearing uprobes can block in the kernel's RCU
    synchronisation for longer than one tick, so deliberate tracefs maintenance no
    longer counts as a stalled loop.
  • --tx-hz 5 now means 5 Hz awake and 5 Hz idle; idle never speeds up past your
    own rate.

What the module costs the headset

Measured from a real 4.3 h Virtual Desktop session: the five probes together fire
about 330 times a second (roughly 4.6 per frame at 72 Hz), and the relay sends
50-60 small datagrams/s. The module only reads -- no patching, no injection, no
camera imagery -- and it hooks functions the headset already calls for its own
tracking.

gxr-root-ft v1.3.7 (freeze fix, self-healing link, bounded log)

Choose a tag to compare

@compdoge compdoge released this 14 Oct 23:40

A reliability release for the link itself. The face/eye signal path is unchanged.

The "frozen face" is fixed

Under Virtual Desktop the tracking could go quiet and the avatar's face would hold its last pose until an app switch produced a new event. The relay read the kernel's trace_pipe with a blocking read. That file is a consuming read, so a poll can say "readable" while finding nothing for the relay -- and the read then parked until the NEXT event arrived. With nothing else generating events, that could be minutes, or forever.

The read is now non-blocking, so a quiet pipe can never park the relay again. It also means the link keeps transmitting at its steady rate while the headset is silent, relaxing the face to neutral instead of freezing it.

The link now says what it is doing

The sender used to write to the log only when the device fed it, so "nothing is tracking", "the reader wedged" and "the process died" all looked identical -- which is what made this hard to pin down. It now writes a heartbeat every 15 seconds from its main loop, so it keeps reporting even when nothing is arriving:

gxr_alvr: hb t=<ms> ev=<events> tx=<datagrams> idle=<ms since last event> probes=<n>/5 on=<1|0> stalls=<n> verbose=<1|0>

If you ever do need to report a problem, that line is the whole story: idle climbing while ev stands still means the headset pipeline is not feeding the relay; ev climbing while tx is frozen would mean the transmit side is wedged; no new heartbeat at all means the process is gone. ev counts only this relay's own events, so other tracefs users cannot make the link look alive.

Self-healing

  • If no events arrive for 3 seconds (after some have been seen), the relay re-arms all of its probes by itself and logs that it did.
  • A watchdog exits the relay if its main loop stalls for 5 seconds, and the module now runs it under a small supervisor that starts it again. A crash or a stall recovers on its own instead of needing the Action button or a reboot.
  • A relay that cannot start at all (for example after a firmware update moves the probed symbols) is now retried with a growing delay -- 2 s up to 60 s -- instead of every 2 seconds forever.

Smaller log, less noise

  • The log starts fresh on every start, with the previous run kept as gxr_alvr.log.1.
  • Past 4 MB in a single run, the verbose tuning trace switches itself off and the heartbeat slows to every 5 minutes. No more 36 MB logs.
  • The module's own README documents the heartbeat and the supervisor.

Upgrading

Install the zip from the KernelSU Manager and reboot -- the reboot is what starts the new version. (Installing on its own does not restart the relay.) Your PC IP, port, TongueRoll and Verbose settings are carried over automatically.

gxr-root-ft v1.3.6 (broadcast hygiene, clearer install)

Choose a tag to compare

@compdoge compdoge released this 14 Oct 21:22

Docs and broadcast hygiene — no change to the face/eye signal path.

Fewer broadcast packets

Every datagram used to go to both the LAN broadcast address and 255.255.255.255 — the same thing twice on a normal network, at twice the airtime, for a stream Wi-Fi already sends at its slowest basic rate. Two community reports (ping spikes in Steam Link, wireless trackers losing lock) lined up with it, and setting a fixed PC IP fixed both. 255.255.255.255 is now a fallback only, used when no directed broadcast can be resolved.

New: --tx-hz

A datagram-rate knob (default 60, clamped 5–240) in the module's Developer options. If this stream disturbs Wi-Fi trackers or a stream sharing your air, lower it — no need to pin an IP. Pinning the PC IP still gives you unicast at the full rate.

Clearer docs

  • The README now says up front that ALVR is not required. You need this headset module plus the VRCFaceTracking module — nothing else. "ALVR" is only the packet format we reuse.
  • Install is the first section, after a What you need checklist (rooted headset + KernelSU Manager, VRCFaceTracking, same Wi-Fi).
  • The PC-side step is written out properly: where ALVRModule.dll goes (%APPDATA%\VRCFaceTracking\CustomLibs) and that it appears as ALVR Module.
  • One troubleshooting section: nothing arrives / flaky auto-connect / Wi-Fi trackers and streams stuttering.
  • The module's own README is now reference-only, so each instruction exists in exactly one place.

Also

  • The module is displayed as Galaxy XR face tracking relay. The module id is unchanged (gxr-alvr), so existing installs and updates are unaffected.
  • This repo is now gxr-root-ft — old links redirect.

Upgrading

Nothing to do. Only the broadcast behaviour changed; if you had already set a PC IP you were never affected by it.

gxr-alvr v1.3.5 (no look-down freeze, narrowed-lid smoothing)

Choose a tag to compare

@compdoge compdoge released this 13 Oct 23:58

Headset-side face/eye relay for VRCFaceTracking, feeding the VRCFT-ALVR-UE module. Reads the Galaxy XR perception host through read-only ftrace uprobes and streams the full face/eye set as ALVR UniExpr records on UDP 41463.

Install

  1. Copy gxr-alvr-v1.3.5.zip to the headset and install it in the KernelSU Manager, then reboot.
  2. That's it — no PC-side setup, no IP to type. If auto-connect is flaky, open Manager → Modules → this module → WebUI and set the PC's IP.

Gaze no longer freezes when you look down

The device's face model reports "eyes closed" as a function of gaze pitch — looking down only ~11–23° already reads as fully shut — so the old eyelid-only gate froze clean gaze on 48–84% of look-down frames.

Gaze is now trusted per eye from the eyelid and the pupil, where the pupil test is relative to that eye's own recent size (so a bright scene, which legitimately constricts the pupil, cannot trip it), plus a pitch guard so looking down stays responsive:

  • lid shut and not looking down → a real blink → held
  • narrowed lid and occluded pupil → held
  • narrowed lid and normal pupil → smoothed harder (the DNN direction is noisy there but cannot be proven wrong)
  • otherwise → untouched

Measured on real captures: look-down false positives 48–84% → 0–9%, bright scene 0%, blink recall 100% at 0 frames latency. The residual squint-band shake is cut about 2.5× by the smoothing tier.

Also in v1.3.5

  • The verbose gaze log gained pu= (pupil size relative to that eye's baseline, -1 = unavailable) so you can see why an eye was held.
  • If Steam Link (or any stream) stutters or pings spike: the relay broadcasts ~120 packets/s across the whole LAN in its default auto mode. Setting the PC IP in the WebUI switches it to unicast and the broadcasts stop. See the README.

Since v1.2.9 (everything in between)

  • Steady 60 Hz transmit with a 2 s hold, so face tracking no longer "stops after every test".
  • Raw openness (1 − EyesClosed) with the squint fold removed — the lid no longer snaps shut over the last ~20%.
  • Per-eye gaze conditioning (One Euro filter) — the device itself applies no gaze filter and has no lid→gaze gate.
  • Per-eye pupil with a drift-based fallback, a robust dilation envelope, and per-eye wink handling.
  • WebUI Developer options and config that survives module updates.

Full detail is in the commit history and headset/ALVR-SENDER.md.