forked from unom/punktfunk
Field report: the native-vulkan HEVC stream goes grey with moving artifacts after a host context change (starting a game), and recovers by itself in 0.5-2 s. Of the three signals that lift the post-loss freeze, two are self-evident to the client and one is pure hearsay. An IDR predicts from nothing. A recovery mark is half a re-anchor and the gate says so by requiring two. But USER_FLAG_RECOVERY_ANCHOR is the HOST asserting a fact about the CLIENT's decoder -- "the picture I coded this P-frame against is one you still hold, intact" -- and the gate took it on faith, on the first occurrence, with no scrutiny at all. The host derives that claim from bookkeeping that tracks what the client RECEIVED, not what it managed to DECODE. Those diverge exactly when the client had to conceal, and then the anchor lifts the freeze onto a picture predicted from damage AND LEAVES IT LIFTED -- so the grey plate reaches the screen and keeps reaching it until some later signal re-arms and the 500 ms backstop extracts a real IDR. That is the observed 0.5-2 s, and it is the worst-shaped failure in the module: a re-anchor claim the client can refute is worse than no claim, because no claim merely holds. So corroborate it. pf-bitstream's planners now carry a per-picture clean bit -- damage propagates down the prediction chain, because the descendants of a concealed picture raise no warning of their own -- surfaced as PicturePlan::references_clean and carried to the consumer on DecodedVkFrame. The gate gains AnchorEvidence and on_decoded_corroborated, and refuses an anchor whose references the client can prove were damaged. Refusing can only ever hold LONGER: the freeze stays up, the backstop fires on its ORIGINAL deadline, and the client escalates to the IDR the anchor failed to be. Lanes with no local parser pass Unavailable and are bit-for-bit unchanged -- all 29 pre-existing reanchor tests pass untouched. Also brings the H.264 decoder to parity with its two siblings, found on the way. It was the only one of the three that failed OPEN: a DPB slot with no bound image was traced and decoded anyway (H.265 and AV1 return UnboundReferenceSlot), reference_count was computed after the held-slot loop so a dropped reference silently took an unrelated slot's picture in its place, and there was no RecoveryLatch, so a failure left the planner DPB, the slot map and the image bindings disagreeing forever. None of it raised a warning, so the frame was shipped, presented, and cleared the demotion streak on its way past. Both latches landed with the HEVC and AV1 decoders in August; H.264 predates them and was never retro-fitted. The damage classification moves onto the warning enums so the planner's ledger and the client's concealment test cannot drift apart -- still exactly one list, now in the crate that owns the enum, with pf_vkdecode::is_integrity_warning* delegating. Every copy stays an exhaustive match with no wildcard: a future variant must stop the compiler, never default to clean.