Repository navigation
The ~1s IDR-wait freeze (#251): findings and what fixed it #286
mauricio-gg
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary. The "input lag" we chased for weeks was really repeated ~1 second video freezes. After a lost frame, the client threw away every following P-frame and held the last picture while the PS5 took 818-1499 ms to answer a keyframe (IDR) request. The fix, shipped as PR #257 ("submit-through-loss"), feeds those frames to the decoder instead of dropping them, so the picture keeps moving with brief smearing. It is behind a settings toggle that defaults to off. Status today: shipped, hardware-validated with the toggle on.
The symptom
The picture freezes for about a second at a time. On a bad stretch this happened roughly every 13 seconds. It felt like input lag, but the PS5's own monitor showed the game responding instantly. The console was receiving input and rendering fine; the delay was entirely in video delivery to the Vita.
The mechanism (measured)
succ = falseand discarded each one: 26 consecutive frames in the worst case measured.Eight recorded events in a two-minute session is several seconds of frozen video. Source logs: three hardware capture sessions; the richest was
92941169310_vitarps5-testing.log.The client-side bug found on the way
chiaki_bitstream_slice_set_reference_frame()returnsfalsefor H.264 (lib/src/bitstream.c:457-458; rewriting is implemented for H.265 only). The Vita is H.264-only (lib/src/session.c:111pins the codec; the decoder isSCE_VIDEODEC_TYPE_HW_AVCDEC). So the "use an older reference instead" recovery block inlib/src/videoreceiver.cnever succeeded: 0 successes in 112 attempts. Making it work was rejected, because all 112 observed slices carry noref_pic_list_modification, so redirecting a reference would mean inserting Exp-Golomb syntax and bit-shifting about 12 KB per frame on the receive thread.What was ruled out (do not re-investigate)
decode_avg_ms1.7 at window 8 and at window 112;display_fpstracksincoming; 4 frame overwrites in 2 min; 0 decode-queue drops;stuck_streak/cascade_streak0drain_max=1reported_precap > raw_lost; session loss 0.17%chiaki_takion_send_buffer_push; resend timeout 200 ms x 10 tries (lib/src/takionsendbuffer.c:13)Steady-state delivery is ordinary link smear of about +/-10 ms and is not the problem. The largest frame in the session was 30,792 bytes, about 91 ms on the wire, so frame size does not explain the console's ~1 s either.
The fix plan (implemented as PR #257)
Research conclusions behind it:
sceAvcdecDecodenever returned an error.gaps_in_frame_num_value_allowed_flag. If it is set, a conforming decoder invents phantom frames for the gap. If it is 0, a submitted frame lands in the benign "wrong reference, bounded drift" case. So the feature only activates when the parsed SPS proves the flag is 0 (chiaki_bitstream_h264_drift_safe(),lib/src/bitstream.c:463).CORRUPTFRAMEis a bare frame range with undocumented console semantics,IDRREQUESTcarries no payload, and the launch spec has no error-resilience knobs. Official Sony clients reportedly freeze ~1 s the same way, so the console's IDR latency is treated as irreducible.Plan steps: parse the SPS fields; add decoder-anomaly counters; add an IDR-arrival discriminator (to tell "console slow" from "keyframe died in the same loss burst"); add the toggle and flip the discard policy at the single discard gate; run a hardware A/B. Open follow-up ideas recorded in the plan: send
idr_periodin the server settings (defined intakion.proto, never sent by any client), and useHEADERREQUEST(VIDEO)as an SPS/PPS refresh.What main does today for the IDR wait
submit_on_missing_ref, default off:vita/src/config.c:57), a P-frame with a missing reference is requested-IDR and then submitted anyway:lib/src/videoreceiver.c:821-846(PIPE/DRIFT_SUBMIT, logged once per episode). With it off, the frame is dropped (videoreceiver.c:851-858) and the picture holds. The IDR request is sent immediately in both modes (videoreceiver.c:826), cooldown 100 ms, timeout 1000 ms (videoreceiver.c:49-50). After 3 consecutive failures the cascade skip kicks in (videoreceiver.c:51,:658).99798574648) measured drift-ON loss episodes freezing 16-33 ms against 300-1040 ms for the OFF path. PR fix(stream): resume resync + transport-death recovery — kill the silent freeze paths (#258, #261) #260 recommended running with the toggle ON; it is still off by default.104199521041showed no delay actually accumulates and the "lag with the toggle on" report was the client displaying the console's fast backlog drain. PR refactor: retire the abs= readout, the dead #262 stale-hold gate and PIPE/FPS stale= (#278) #280 later removed that whole gate (its cadence tracker,staleness_msplumbing and hold state machine), theabs=readout, and the stale counters. What remains invita/src/video.cis the older bounded freeze: a corrupt frame shows the last good picture for at mostFREEZE_MAX_STREAK= 8 consecutive frames (video.c:174-179,:1159).The latency documents, folded in
The two older documents (
docs/LATENCY_ANALYSIS.md,docs/LATENCY_QUICK_WINS.md) were a measurement plan and a list of "quick wins" written before the above was understood. The analysis document was a checklist of areas to instrument (video, audio, network, input) with a target of "<100 ms remote, <50 ms local" and estimated current figures of "150-200 ms remote". Those estimates were guesses, not measurements, and they are invalid per the warning above. The one concrete item it recorded was the GH #208 ENOBUFS fix (see the "Tried and reverted" thread and PRs #209/#244).Status of every item in the quick-wins list, checked against
main(0e9dd13):vita/src/video.c:1389; restored on stop,:1477)force_30fps, default off (vita/src/config.c:54, applied atvita/src/host.c:473-475)VITA_DECODE_THREAD_PRIORITY,vita/include/video.h:39)video.c:1329-1331); recv is USER_0 (lib/src/takion.c:1278), audio USER_2 (vita/src/audio.c:208)vita/src/host_input.c:270)vita/include/audio.h:5), but pinned to USER_2, not CPU 0vita/include/host_input.h:11); pinning dropped (affinity mask 0 = unrestricted,host_input.c:262)lib/src/takion.c:69)takion.c:51; see the socket-buffer thread)takion.c:80)enable_frame_pacerflag is gone. A display pacing gate exists,should_drop_frame_for_pacing()(vita/src/video.c:311-330), but it does nothing unlessforce_30fpsis on (default off)numOfStreams = 1andREF_FRAMES = 8(video.c:712-713). Decoder never errored across 22 logs, and decode averages 1.7 ms, so the gain is unprovenvita/src/config_values.c:18-29); lower is a user settingtakion.c:1332-1336)takion.c:429-433only logs a warninglib/src/audioreceiver.chas none)videoreceiver.c:15-17) and IDR is requested on the first missing reference (PR #187)Two experiments from the same document were abandoned and are worth remembering:
setsockopt(IP_DONTFRAG = 28)returned -1 (option not supported). The Vita's BSD stack does not implement it, so fragmentation cannot be disabled and there is nothing to gain here.Source: docs/ai/wip/251-handoff-prompt.md, docs/superpowers/plans/2026-08-14-idr-freeze-fix-251.md, docs/LATENCY_ANALYSIS.md and docs/LATENCY_QUICK_WINS.md (deleted from the repo in #281; recoverable from git history at commit 0e9dd13)
All reactions