From 5aeb8d255269b39c9cd3d11bf717ed3719527c39 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 21:36:16 +0200 Subject: [PATCH] fix(client): a failed AV1 decode left the surface's facts saying it holds the last picture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `damaged` path has cleared `Session::held[setup_slot]` since M7, for a reason that now applies to the failure path too: the slot map says the surface holds THIS picture while the surface still carries whatever the previous occupant decoded, so a later `show_existing_frame` naming it blits the old picture's pixels with the old picture's geometry and colour. The failure path never reached that far before — `decode_into`'s error returned straight out of `frame_av1` — and the previous commit made it continue so the slot releases could run. --- crates/pf-client-core/src/video_d3d11_native.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 10582a8d..78759d3f 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -607,6 +607,20 @@ impl NativeD3d11Decoder { // with the frames after the failure, which is the pump's question and not this // function's, and the failure already ends in a keyframe request. let shown = self.decode_and_present_av1(au, &sub, damaged); + if shown.is_err() { + // The surface's `held` entry, on the path that now CONTINUES rather than + // returning early. The slot map says this surface holds THIS picture while + // the surface still carries whatever the previous occupant decoded, so a + // later `show_existing_frame` naming it would blit the old picture's pixels + // with the old picture's geometry and colour. The `damaged` path has + // cleared it for that reason since M7; the failure path never reached this + // far before. + if let Some(session) = self.session.as_mut() { + if let Some(held) = session.held.get_mut(usize::from(sub.setup_slot)) { + *held = None; + } + } + } // The surfaces this frame's own refresh displaced while its submission still // NAMED them (fn docs). Released here for the same reason the block below