Skip to content

Jaguar1 async RX ring: residual ~1-2% RX loss on MediaTek (PixelPilot#6 follow-up) — hypothesis + dead ends, needs device validation #330

Description

@josephnef

Summary

Follow-up to the now-closed OpenIPC/PixelPilot#6 (no video on MediaTek). The no-video regression was fixed and merged (#317, 16 KB RX URBs restored). But on-device validation surfaced a separate, residual RX-quality problem that is not fixed: video comes up but is "not perfect — artifacts when the screen changes." This issue documents the investigation so the next person doesn't repeat the dead ends.

Status: root cause is a hypothesis (async RX ring under transient load); NOT reproduced at realistic load on a bench rig; no validated fix. Needs validation on a real MediaTek device or a clean-RF setup.

Symptom

  • Reporter: RTL8812AU @ USB2 (480 Mbit), MediaTek Dimensity 810 (Redmi Note 11 5G).
  • floppyhammer's older fix-no-video build (pre-refactor devourer) = "perfect." Current master = "not perfect, artifacts when the screen changes."
  • Logcat from the imperfect run: wfb-ng PKT_LOST1–2% (steady drips of 1–3 pkts), FecChangeController: bumping FEC repeatedly, NALU pipeline shows ~2% of NALUs dropped after FEC couldn't recover. The MediaTek HW decoder itself reports Drop: 0 — so the loss is upstream, in RX delivery, not decode. "Screen changes" = video keyframes = a sudden burst of radio packets.

Hypothesis (unproven)

The RX path was rewritten from a synchronous single bulk-IN read (floppyhammer's era) to an async multi-URB ring (UsbTransport::rx_loop / devourer_rx_cb in src/UsbTransport.cpp). In the async ring the caller's consumer (on_dataFrameParser::recvbuf2recvframe → in PixelPilot, wfb-ng FEC+AES+UDP) runs inline inside devourer_rx_cb, on the single libusb event-pump thread, BEFORE the URB is resubmitted. Under a keyframe burst the inline cost can outrun URB reposting, the posted-URB depth collapses, and the chip RX-FIFO overflows → the 1–3-pkt drips. The synchronous model had no per-URB-resubmit dependency (the chip FIFO absorbed between-read gaps).

This is plausible but was not demonstrated to be the cause at realistic load (see below).

What was built to investigate

A bench harness (2 adapters, one as TX flood, one as the 8812AU RX on a USB2 hub):

  • A per-frame busy-spin injected at the stable recvbuf2recvframe entry (DEVOURER_RX_SINK_SPIN_US, scaled by the aggregated packet count) to model the inline consumer cost (wfb-ng ≈ 20–50 µs/frame).
  • Dual-era RX binaries: the synchronous build (floppyhammer era) vs current async build.
  • A keyframe-burst emulation added to txdemo (DEVOURER_TX_BURST_ON_MS / _OFF_MS: flood ON ms, idle OFF ms, repeat).
  • Delivery measured as received-frames / aired-frames.

(Scripts + the experimental fix branch are preserved; ping if you want them.)

What was found

  1. The mechanism is possible. At an extreme inline cost (spin = 100 000 µs/frame) the async ring's delivery craters (≈18 700 → 800 frames). So the ring can be starved by a slow inline consumer.
  2. But the rig does not reproduce it at realistic load. At ≤ 3000 µs/frame (≈60–150× the real wfb-ng cost) there is zero delivery loss. A fast x86 + xhci host has ~100× more USB-completion/CPU headroom than the phone.
  3. The rig is fundamentally unsuitable to see a 1–2% effect. Near-field (two adapters ~cm apart) forces a lossy link: MCS rates won't decode at any TX power (EVM saturation), so the only usable rate is 6M legacy at ~62% delivery. 38% baseline RF loss buries the reporter's 1–2% signal.

What did NOT help (dead ends — don't repeat)

  • Decoupling the consumer from the pump thread (bounded queue in devourer_rx_cb + a single worker thread running on_data, resubmitting the URB immediately). Implemented and A/B-tested. It regressed delivery in both steady-state and burst tests (e.g. under bursts: old inline held ~62% across spin 0/300/1000; decoupled = 59% / 62% / 25%). Root of the regression: the inline ring's slower reaping produces fat RX-agg buffers the chip FIFO absorbs, while the decoupled fast-reaping produces thin buffers + hard queue drops. Do not ship this fix on current evidence.
  • Deeper URB ring (DEVOURER_RX_URBS 8 → 32 → 128): null in steady state (all ~23 000). Depth can't help a consumer-limited steady state; it might help transients, but the rig couldn't show it.

Recommended next steps

  1. Validate on the actual MediaTek device (Dimensity/Helio/MT6765) — the only environment where the 1–2% transient effect is visible. Instrument per-commit or A/B the sync vs async RX on-device.
  2. Or reproduce on a clean-RF setup (real antenna separation + attenuators) so a high-delivery, high-MCS link is possible and a 1–2% effect is detectable — the near-field 2-adapter bench cannot do this.
  3. Do not change the RX architecture on bench evidence alone. The merged Jaguar USB RX: 16 KB URBs + matching agg caps — restore the MediaTek host fix #317 already fixed the real no-video regression; the residual quality gap is unproven and no rig-validated fix exists.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions