AMF (user-LTR bitfield), QSV (mfxExtRefListCtrl) and Vulkan Video (the
app-owned DPB slot table) each hand-implemented the same loss-recovery
decision: distrust every reference encoded at-or-after the loss start,
anchor on the newest one strictly older. Three copies had already
diverged once — the fecbec2d taint sweep reached AMF/QSV a commit before
the Vulkan backend was carved out, and Vulkan shipped without it. The
decision now lives once in enc/rfi.rs, pure and unit-tested (this path
had zero coverage and is the loss-recovery path); every mechanism —
how a force is applied, how distrust is persisted — stays in its
backend.
Behavior-preservation notes, each critic-verified against the shipped
code:
- Callers feed only currently-trusted references; taint (>= loss) and
anchor (< loss) predicates are disjoint, so pre-sweep-view pick ==
post-sweep-table pick on every input. Tie-break preserved (first
entry wins == the strict '>' all three used, ascending slot order).
- plan_slot_recovery delegates its pick half to pick_anchor, which
keeps both items live on every leg (pick_anchor's only external
caller is Linux+vulkan-encode; dead_code is an item lint) and makes
'anchor chosen from the taint snapshot' structural.
- The decline arms are deliberately NOT uniform and stay put: AMF/QSV
clear an un-consumed pending_force; Vulkan leaves pending_loss armed
(a stale arm re-resolves at frame-build into the healing IDR) — now
pinned in comments on both sides.
- Vulkan's frame-build site is a pick-only re-run by design (the arm
carries the loss start, not the slot); pick_recovery_slot is deleted
and its tests migrated 1:1 into the shared module.
Test-count deltas are expected: the rfi tests now run on every Windows
combo (amf.rs is featureless there) and on the Linux feature leg.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>