Skip to main content

rusty_h264_decoder/
mb16.rs

1//! I_16x16 macroblock decoding — the mirror of the encoder's `mb16`.
2//!
3//! Parses each macroblock's residuals and reconstructs it with the exact same
4//! prediction + inverse-transform helpers the encoder uses, so decoder output
5//! matches encoder reconstruction bit-for-bit.
6#![allow(clippy::needless_range_loop)]
7
8#[allow(unused_imports)]
9use alloc::borrow::ToOwned;
10#[allow(unused_imports)] // MutexPf is std-only (the MV dump); LockPf is used in both
11use crate::sync::{LockPf, MutexPf};
12#[allow(unused_imports)]
13use alloc::boxed::Box;
14#[allow(unused_imports)]
15use alloc::format;
16#[allow(unused_imports)]
17use alloc::string::{String, ToString};
18#[allow(unused_imports)]
19use alloc::vec;
20#[allow(unused_imports)]
21use alloc::vec::Vec;
22use rusty_h264_common::bit_reader::OutOfData;
23use rusty_h264_common::cavlc::{decode_residual_block_into, read_cbp_inter, read_cbp_intra};
24use rusty_h264_common::inter::{
25    inter_partitions, mc_chroma_padded, mc_luma_padded, predict_mv, predict_partition_mv,
26    MvNeighbor,
27};
28use rusty_h264_common::predict::{
29    add_residual_8x8, chroma8x8_pred, chroma_qp, intra4x4_pred, intra8x8_pred, luma16x16_pred,
30    nnz_raster_from_z, reconstruct_4x4_dc_into, reconstruct_4x4_scan_into, I16Mode,
31    CHROMA_4X4_SCAN_XY, LUMA_4X4_SCAN_XY,
32};
33use rusty_h264_common::transform::{
34    inverse_quant_8x8_dq, inverse_quant_chroma_dc, inverse_quant_chroma_dc_weighted,
35    inverse_quant_luma_dc_scan, Dequant8Qp, DequantQp, DQ8_FLAT, DQ_FLAT,
36};
37use rusty_h264_common::{BitReader, YuvFrame};
38
39/// One frame's motion field, in 4x4-block raster (`mb_w*4` wide).
40///
41/// Captured from any conformant stream this decoder parses — including x264's —
42/// so a harness can compare motion fields between encoders without depending on
43/// external MV-export tooling.
44pub struct MvField {
45    pub mb_w: usize,
46    pub mb_h: usize,
47    pub mv: Vec<(i32, i32)>,
48    pub ref_idx: Vec<i32>,
49    pub inter: Vec<bool>,
50}
51
52/// Frames captured in decode order when `RFF_MV_DUMP=1`. Diagnostic only.
53#[cfg(feature = "std")]
54pub static MV_DUMP: crate::sync::Mutex<Vec<MvField>> = crate::sync::Mutex::new(Vec::new());
55
56/// `RH264_DUMP_MB` -- the per-frame/per-slice conformance-bisection dump.
57///
58/// ROUTED AT BUILD TIME, like the rest of the knob inventory. It was a bare
59/// `knob("RH264_DUMP_MB").is_some()` at four call sites, which is
60/// `std::env::var` PLUS a String allocation on EVERY slice and every DPB add --
61/// and it kept each dump body, with its `eprintln!` formatting machinery and
62/// String building, compiled into the shipping decoder. `deblock` alone carried
63/// four `Stderr::write_fmt`, four `panic_fmt` and thirteen alloc/free calls that
64/// exist only for a dump nobody enables.
65/// `RS_H264_ROUTE_DUMP` -- the per-picture content-route dump. Build-time routed
66/// for the same reason as `dump_mb_on`.
67pub(crate) fn route_dump_on() -> bool {
68    #[cfg(not(feature = "knobs"))]
69    {
70        false
71    }
72    #[cfg(feature = "knobs")]
73    {
74        static ON: rusty_h264_common::once::OnceLock<bool> =
75            rusty_h264_common::once::OnceLock::new();
76        *ON.get_or_init(|| rusty_h264_common::knob("RS_H264_ROUTE_DUMP").is_some())
77    }
78}
79
80pub(crate) fn dump_mb_on() -> bool {
81    #[cfg(not(feature = "knobs"))]
82    {
83        false
84    }
85    #[cfg(feature = "knobs")]
86    {
87        static ON: rusty_h264_common::once::OnceLock<bool> =
88            rusty_h264_common::once::OnceLock::new();
89        *ON.get_or_init(|| rusty_h264_common::knob("RH264_DUMP_MB").is_some())
90    }
91}
92
93pub fn mv_dump_on() -> bool {
94    #[cfg(not(feature = "knobs"))]
95    {
96        false
97    }
98    #[cfg(feature = "knobs")]
99    {
100        static ON: rusty_h264_common::once::OnceLock<bool> =
101            rusty_h264_common::once::OnceLock::new();
102        *ON.get_or_init(|| rusty_h264_common::knob("RFF_MV_DUMP").map_or(false, |v| v != "0"))
103    }
104}
105
106/// Copy filtered MB rows `[prev..mb_rows)` from coded-size planes into a
107/// Frame-MT progress slot's live padded planes, then bump `ready_rows`.
108/// Coarser publish: only advance the watermark every 2 MB rows (and always
109/// on the last row) to cut lock/notify traffic.
110fn publish_filtered_rows_to_slot(
111    slot: &crate::RefFrame,
112    rec_y: &[u8],
113    rec_u: &[u8],
114    rec_v: &[u8],
115    cw: usize,
116    ccw: usize,
117    ch: usize,
118    mb_rows: usize,
119) {
120    if mb_rows == 0 || slot.frozen.get().is_some() {
121        return;
122    }
123    if !crate::row_publish_on() {
124        return;
125    }
126    let mb_h = ch.div_ceil(16);
127    // Batch: defer watermark until even MB rows (or picture end).
128    if mb_rows < mb_h && (mb_rows & 1) != 0 {
129        return;
130    }
131    let Some(live) = &slot.live else {
132        slot.publish_ready_rows(mb_rows * 16);
133        return;
134    };
135    let cch = ch / 2;
136    let prev = slot.ready_rows.load(core::sync::atomic::Ordering::Acquire) / 16;
137    if mb_rows <= prev {
138        return;
139    }
140    let mut py = live.py.write_pf();
141    let mut pu = live.pu.write_pf();
142    let mut pv = live.pv.write_pf();
143    let ls = cw + 2 * crate::LPAD;
144    let cs = ccw + 2 * crate::CPAD;
145    for mr in prev..mb_rows {
146        for dy in 0..16 {
147            let y = mr * 16 + dy;
148            if y >= ch {
149                break;
150            }
151            let src = &rec_y[y * cw..(y + 1) * cw];
152            let dst_y = y + crate::LPAD;
153            // ONE row segment covering pad+picture+pad, then `split_at_mut` and
154            // two fills — the edge reads and the 2*LPAD writes were separate
155            // whole-plane indexes. Same shape as `expand_plane`.
156            let seg = &mut py[dst_y * ls..][..2 * crate::LPAD + cw];
157            let (lpad, rest) = seg.split_at_mut(crate::LPAD);
158            let (mid, rpad) = rest.split_at_mut(cw);
159            mid.copy_from_slice(src);
160            let (Some(&left), Some(&right)) = (mid.first(), mid.last()) else {
161                continue;
162            };
163            lpad.fill(left);
164            rpad[..crate::LPAD].fill(right);
165        }
166        for dy in 0..8 {
167            let cy = mr * 8 + dy;
168            if cy >= cch {
169                break;
170            }
171            for (rec, plane) in [(rec_u, &mut *pu), (rec_v, &mut *pv)] {
172                let src = &rec[cy * ccw..(cy + 1) * ccw];
173                let dst_y = cy + crate::CPAD;
174                let seg = &mut plane[dst_y * cs..][..2 * crate::CPAD + ccw];
175                let (lpad, rest) = seg.split_at_mut(crate::CPAD);
176                let (mid, rpad) = rest.split_at_mut(ccw);
177                mid.copy_from_slice(src);
178                let (Some(&left), Some(&right)) = (mid.first(), mid.last()) else {
179                    continue;
180                };
181                lpad.fill(left);
182                rpad[..crate::CPAD].fill(right);
183            }
184        }
185    }
186    if prev == 0 {
187        // Copy the whole first picture ROW upward instead of walking columns:
188        // `split_at_mut` at the pad boundary proves both sides at once.
189        let (pad, rest) = py.split_at_mut(crate::LPAD * ls);
190        if let Some(first) = rest.get(..ls) {
191            for row in pad.chunks_mut(ls) {
192                row.copy_from_slice(first);
193            }
194        }
195        for plane in [&mut *pu, &mut *pv] {
196            let (pad, rest) = plane.split_at_mut(crate::CPAD * cs);
197            if let Some(first) = rest.get(..cs) {
198                for row in pad.chunks_mut(cs) {
199                    row.copy_from_slice(first);
200                }
201            }
202        }
203    }
204    slot.publish_ready_rows(mb_rows * 16);
205}
206
207/// Reconstructed coded-size planes plus CAVLC `nnz` context grids.
208pub struct FrameDecoder {
209    mb_w: usize,
210    mb_h: usize,
211    /// Slice QP (`SliceQPy`) — the deblock filter's frame-level QP.
212    qp: u8,
213    /// Running luma QP (`QPy`), carried across macroblocks and stepped by each
214    /// `mb_qp_delta` (spec §7.4.5). Equals `qp` on constant-QP streams.
215    cur_qp: u8,
216    /// `chroma_qp_index_offset` from the active PPS (§8.5.8).
217    chroma_qp_offset: i32,
218    cw: usize,
219    ch: usize,
220    ccw: usize,
221    cch: usize,
222    rec_y: Vec<u8>,
223    rec_u: Vec<u8>,
224    rec_v: Vec<u8>,
225    /// Per-macroblock luma QP (`QPy`), for per-edge deblock strength.
226    mb_qp: Vec<u8>,
227    /// First macroblock address of the slice currently being decoded. Neighbors
228    /// with a lower address belong to an earlier slice and are "not available"
229    /// for prediction (spec §8.3/§8.4). Slices are contiguous raster ranges (we
230    /// reject FMO/slice-groups), so address ≥ this ⇔ same slice.
231    slice_first_mb: usize,
232    nnz_y: Vec<u8>,
233    nnz_c: [Vec<u8>; 2],
234    modes_y: Vec<u8>,
235    coded_y: Vec<bool>,
236    /// Per-4×4-block List-0 motion (mv + ref index, `-1` = no L0). For P slices
237    /// this is the only motion; B slices add the List-1 grids below.
238    /// NARROWED (i32,i32) -> (i16,i16). A motion vector is quarter-pel and
239    /// level-bounded (+/-2048 qpel at 5.1), so i16 has 16x headroom -- and
240    /// `pack_mb` ALREADY narrowed these to i16 unconditionally for the deblock
241    /// path, so the store-side assumption predates this change rather than being
242    /// introduced by it.
243    ///
244    /// Rust has no implicit numeric coercion, so the COMPILER is the proof that
245    /// no arithmetic silently happens at i16: every read site had to be given an
246    /// explicit widen, and anything left implicit would not compile.
247    mv_y: Vec<(i16, i16)>,
248    inter_y: Vec<bool>,
249    /// NARROWED i32 -> i8 (2026-09-07). A `ref_idx` is -1..31 by the spec
250    /// (7.4.5.1: num_ref_idx_active is at most 32), so three of every four
251    /// bytes were padding -- and this grid is re-armed for EVERY picture,
252    /// which the copy census priced at 69 MB over a 60-frame 720p decode.
253    ref_idx_y: Vec<i8>,
254    /// Per-4×4-block List-1 motion for B slices (`ref_idx1 = -1` = no L1).
255    mv1: Vec<(i16, i16)>,
256    ref_idx1: Vec<i8>,
257    /// `RefPicList1` and B-slice flags (unused outside B slices).
258    refs1: Vec<crate::Ref>,
259    num_ref_active1: usize,
260    is_b: bool,
261    /// True if the stream's profile permits B-slices (`profile_idc != 66`). When
262    /// false (Baseline / Constrained Baseline), `as_reference_pooled` skips the per-block
263    /// motion (mv/ref_idx/ref_poc) that only B temporal/spatial direct ever reads.
264    b_possible: bool,
265    direct_spatial: bool,
266    nnz_l_cache: [u8; 25],
267    nnz_c_cache: [[u8; 9]; 2],
268    /// Decoded-picture buffer (most-recent first); empty in I-slices. `ref_idx`
269    /// indexes into this list.
270    refs: Vec<crate::Ref>,
271    /// `num_ref_idx_l0_active` for the current slice — drives whether `ref_idx`
272    /// is coded (active > 1) and its te(v)/ue(v) form, independently of how many
273    /// reference pictures actually exist (spec §7.4.5.1, §9.1).
274    num_ref_active: usize,
275    /// `constrained_intra_pred_flag`: when set, intra prediction may only use
276    /// samples from intra-coded neighbors (inter neighbors are "not available").
277    constrained_intra: bool,
278    /// High-profile 4×4 scaling matrices in **raster** order, indexed by
279    /// `[Y-intra, Cb-intra, Cr-intra, Y-inter, Cb-inter, Cr-inter]`. `None` = flat.
280    scaling: Option<[[i32; 16]; 6]>,
281    /// High-profile 8×8 luma scaling matrices in raster order `[Y-intra, Y-inter]`
282    /// (4:2:0 has only these two). `None` = flat.
283    scaling8: Option<[[i32; 64]; 2]>,
284    /// `transform_8x8_mode_flag` from the PPS: enables `transform_size_8x8_flag`.
285    transform_8x8_mode: bool,
286    /// SPS `qpprime_y_zero_transform_bypass_flag` — refusal gate in `step_qp`.
287    transform_bypass: bool,
288    /// GATE 1 router counters (big-oppy-decoder §2): skip MBs and
289    /// residual-coded MBs this picture. Two integer adds per MB, no atomics.
290    route_skip_mbs: u32,
291    route_coded_mbs: u32,
292    /// Per-slice `(first_mb, idc == 2)` in decode order — drives the
293    /// `disable_deblocking_filter_idc == 2` cross-slice-edge suppression.
294    slice_bounds: Vec<(usize, bool)>,
295    /// The CURRENT slice's `disable_deblocking_filter_idc == 2`, latched by
296    /// `set_deblock_params` and recorded per slice by the decode loops.
297    cur_idc2: bool,
298    /// Per-macroblock `transform_size_8x8_flag` (for deblocking: internal 4×4
299    /// luma edges of 8×8-transform MBs are not filtered).
300    mb_t8x8: Vec<bool>,
301    /// Bit per macroblock: the DECODER knows this macroblock's sixteen blocks
302    /// share one motion set in both lists, so the deblock derivation need not run
303    /// the `mb_uniform` kernel to rediscover it.
304    ///
305    /// Deliberately NOT folded into `mb_kind`: that enum's `InterUniform` carries
306    /// a single-list contract the blind derivation arms rely on, and a
307    /// bi-predicted direct macroblock would violate it. This says one thing only.
308    mb_umot: Vec<bool>,
309    // ---- Row-interleaved deblocking state (docs/row-interleave-plan.md) ----
310    /// Per-MB boundary strengths, filled row-by-row as decode completes rows.
311    bs_frame: Vec<rusty_h264_common::deblock::MbBs>,
312    /// Rows whose bS is derived (watermark).
313    bs_rows: usize,
314    /// Rows already deblock-FILTERED (watermark; R3).
315    flt_rows: usize,
316    /// Two-row rolling window of packed records (prev = row r-1, cur = row r).
317    pk_prev: Vec<rusty_h264_common::deblock::MbPack>,
318    pk_cur: Vec<rusty_h264_common::deblock::MbPack>,
319    /// Transform-block coded mask (nnz with the 8x8 OR applied), filled per row.
320    nnz_dbr: Vec<u8>,
321    /// Unfiltered bottom rows of the last-filtered MB row (intra reads these).
322    bak_y: Vec<u8>,
323    bak_u: Vec<u8>,
324    bak_v: Vec<u8>,
325    /// Entropy-decouple: deferred pixel jobs + the per-slice activation flag
326    /// (CABAC slices only — the CAVLC loop has no flush hooks).
327    edc_jobs: Vec<EdcJob>,
328    /// Recycled job boxes (see `take_pinter_job`): a coded inter macroblock
329    /// used to malloc, fill, memcpy and free a 2.8 KB box every time.
330    edc_job_pool: Vec<Box<PInterJob>>,
331    edc_nores_pool: Vec<Box<PInterNoResJob>>,
332    /// Coefficient planes for the B and intra arms (which reconstruct inline,
333    /// so no job carries them): taken for a macroblock, zeroed per CODED block
334    /// by the parse, put back after recon. `None` only while in use.
335    scratch_luma: Option<Box<[[i32; 16]; 16]>>,
336    scratch_luma8: Option<Box<[[i32; 64]; 4]>>,
337    scratch_cac: Option<Box<[[[i32; 16]; 4]; 2]>>,
338    edc_active: bool,
339    // ---- E2: the worker-thread plumbing (all None outside a threaded slice) ----
340    edc_tx: Option<crate::sync::mpsc::SyncSender<EdcMsg>>,
341    edc_ctx_rx: Option<crate::sync::mpsc::Receiver<PixelCtx>>,
342    edc_back_tx: Option<crate::sync::mpsc::Sender<PixelCtx>>,
343    /// FIRST internal fault seen while decoding this picture, if any.
344    ///
345    /// The EDC worker and the parse thread talk over channels whose `send`
346    /// fails only when the peer is gone. The old code `.expect("worker
347    /// alive")`ed, which converts a dead helper thread into a panic in a
348    /// thread that did nothing wrong -- and a decoder that panics on
349    /// attacker-controlled input hands out a denial of service.
350    ///
351    /// A dropped job is NOT recoverable (the row simply would not be filtered,
352    /// so the picture would be silently wrong), so this is not fail-soft: the
353    /// fault is recorded here and `decode_slice` turns it into
354    /// `DecodeError::Internal` at picture finalize. Wrong pixels are never
355    /// returned as if they were right.
356    fault: Option<&'static str>,
357    /// While the parse thread holds the pixel context for an intra macroblock
358    /// (planes moved into `self`), the rest of the context parks here.
359    edc_parked: Option<PixelCtx>,
360    /// E3: while parsing a B macroblock in threaded mode, its MC regions
361    /// accumulate here instead of executing (the pixel side is the worker's).
362    edc_regions: Option<Vec<BRegion>>,
363    /// Measurement only: previous full-MB single-rect direct (x, y, refi0,
364    /// refi1, m0, m1) for run-adjacency counting. No decode behavior.
365    bsk_last: Option<(usize, usize, i32, i32, (i32, i32), (i32, i32))>,
366    /// D10: jobs accumulated for the current row, sent as ONE message.
367    edc_batch: Vec<EdcJob>,
368    /// D12: bits/MB carried in from previous slices (0 = not yet known).
369    bits_per_mb: f64,
370    /// Current slice's deblock parameters (set per slice by the caller).
371    db_ena: bool,
372    db_oa: i32,
373    db_ob: i32,
374    /// Per-macroblock deblock derivation CLASS (`MB_KIND_*`), so the loop filter
375    /// can skip the 24-block neighbourhood gather on macroblocks whose strengths
376    /// are determined by syntax alone. Starts UNSET; anything left UNSET simply
377    /// takes the blind path, so a missed producer site costs speed, not
378    /// correctness. Only classes that are uniform BY SYNTAX are written — notably
379    /// NOT `B_Skip`/`B_Direct`, whose direct-derived motion varies per 4×4.
380    mb_kind: Vec<u8>,
381    /// Explicit weighted-prediction tables, when active for this slice.
382    weights: Option<WeightTable>,
383    /// `frame_mt::row_progress_on()` cached at construction (see
384    /// wait_refs_for_mb).
385    row_wait: bool,
386    /// Colocated-picture fast-probe cache (set_b_context): true when refs1[0]
387    /// is frozen (1T), short-term, with motion grids — col_zero then skips
388    /// the Option + live + long_term + w4 branch chain per probe.
389    col_ok: bool,
390    col_w4: usize,
391    /// Measurement knobs cached at construction — these were OnceLock derefs
392    /// on EVERY skip macroblock (no_bskipfast per B skip, no_runmv per P
393    /// skip); one field test now.
394    /// rowdb_on()/rowhook_eager() cached at construction — these were atomic
395    /// loads inside row_hook_at on EVERY macroblock.
396    /// MEASUREMENT KNOB, cached. `kind_loads()` is a `OnceLock` deref and was
397    /// evaluated as a MATCH GUARD on every Skip/InterUniform macroblock in
398    /// `derive_bs_row` — the dominant class. The census (DBSDERIVE kindarm=0 on
399    /// 6/6 corpus streams) shows the arm it guards never fires by default, so
400    /// every one of those derefs was pure tax. Same treatment `k_rowdb` /
401    /// `k_roweager` already had.
402    /// True once ANY macroblock in this picture used the 8x8 transform. The
403    /// `nnz_dbr` row copy + the per-row t8 scan in `derive_bs_row` exist ONLY
404    /// to apply the 8x8 coded-status OR; with no t8 macroblock `nnz_dbr` is a
405    /// byte-for-byte copy of `nnz_y`, so both are skipped and the derivation
406    /// reads `nnz_y` directly. Census: t8mb=0 on every cavlc/main stream.
407    any_t8: bool,
408    /// True once any slice in this picture carries
409    /// `disable_deblocking_filter_idc == 2`. Replaces a per-ROW
410    /// `slice_bounds.iter().any(..)` scan (census: idc2row=0 on the corpus).
411    any_idc2: bool,
412    /// Raster address whose P_Skip MV is FORCED (0,0): the previous
413    /// decode_p_skip was at addr-1 and committed (0,0), so this MB's left
414    /// neighbor either fires §8.4.1.1's zero-MV rule (in-slice: ref 0,
415    /// (0,0)) or is unavailable (out-of-slice — same rule). Interior MBs of
416    /// a skip run skip the 3-neighbor gather entirely.
417    skip_zero_next: usize,
418    /// Pending B_Skip grid+recon span: (mb row, x0, n, kind) of CONSECUTIVE
419    /// fast B_Skips with IDENTICAL committed values, deferred and range-
420    /// filled/band-reconned at flush. Flushed before ANY grid or pixel
421    /// reader runs — see span_flush callers.
422    bzspan: Option<(usize, usize, usize, BzKind)>,
423    /// Pending P_Skip (0,0) grid-commit span — the P mirror of `bzspan`
424    /// (values: ref 0 list-0, mv (0,0), inter, coded, DC mode, kind SKIP).
425    /// The bool records whether the RECON is deferred too (1T, identity/no
426    /// weights) — decided at push time, not re-derived at flush (begin_slice
427    /// may have changed the fields in between).
428    pzspan: Option<(usize, usize, usize, bool)>,
429    /// Per-picture bitmap: MB decoded as a ref0/(0,0)-both-lists zero-bi fast
430    /// B_Skip. Never cleared mid-picture — false only ever means "derive
431    /// normally" — so no MB path carries a clearing duty.
432    bzero: Vec<bool>,
433    /// Pooled CABAC slice scratch — refilled (not reallocated) at slice entry;
434    /// carried across pictures via `GridPool`. Taken out of `self` at
435    /// `decode_slice_cabac_inner` entry and put back at its normal exit (an
436    /// error exit simply forfeits the pooled allocation).
437    sc_cat: Vec<u8>,
438    sc_cbp: Vec<u8>,
439    // Chroma pred mode is 0..=3 (7.4.5: intra_chroma_pred_mode), with -1 as the
440    // "no neighbour" sentinel and the only read being `(1..=3).contains(..)`.
441    // i32 was four bytes carrying two bits.
442    sc_cmode: Vec<i8>,
443    sc_nzc: Vec<[u8; 24]>,
444    sc_cbfdc: Vec<u16>,
445    sc_skip: Vec<bool>,
446    sc_ref: Vec<[i8; 16]>,
447    /// SATURATED-ABSOLUTE, `u8` (2026-09-07). This was `[[i16; 2]; 16]` -- the
448    /// signed mvd per 4x4 block -- but its ONLY consumer is the CABAC mvd
449    /// ctxInc, which computes `|nb| + |na|` and compares it against 3 and 32.
450    /// Neither the sign nor any magnitude above 33 can change that answer:
451    /// if either neighbour is >= 33 the sum already exceeds 32 both ways, and
452    /// if both are < 33 saturating is a no-op. So a byte carries exactly the
453    /// information an i16 did, at half the width -- on a grid refilled per
454    /// SLICE, which the copy census priced at 230 KB a time.
455    sc_mvd: Vec<[[u8; 2]; 16]>,
456    sc_ref1: Vec<[i8; 16]>,
457    sc_mvd1: Vec<[[u8; 2]; 16]>,
458    sc_direct: Vec<bool>,
459    /// Lazily cached `implicit_weights(0, 0)` — slice-constant (POCs of
460    /// refs[0]/refs1[0] and cur don't change within a slice). Reset in
461    /// set_b_context.
462    iw00: Option<Option<(i32, i32)>>,
463    /// Cached `weights.list_identity(0)` — all list-0 refs identity; gates the
464    /// whole weight_partition pass for P inter (one choke point, all sites).
465    weights_l0id: bool,
466    /// Cached `weights.ref0_identity()` — identity tables (the x264 weightp=2
467    /// common case) make every ref-0 weighting pass an exact no-op, and the
468    /// skip fast paths check this per MB, so it is computed once per slice.
469    weights_id0: bool,
470    /// Current picture's `PicOrderCnt` (for temporal direct + implicit weighting).
471    cur_poc: i32,
472    /// `weighted_bipred_idc` (0 = none/average, 1 = explicit, 2 = implicit).
473    weighted_bipred_idc: u8,
474    /// `direct_8x8_inference_flag` (B direct co-located sub-block selection).
475    direct_8x8_inference: bool,
476    /// Frame-MT Phase B: shared progress Arc for the picture being decoded.
477    progress: Option<crate::Ref>,
478    /// Slice-stable ref→POC tables for bS (≤16 entries). `derive_bs_row` used
479    /// to `collect()` these every row — same values for the whole slice.
480    ref_poc0: Vec<i32>,
481    ref_poc1: Vec<i32>,
482}
483
484/// Explicit weighted-prediction tables (spec §7.4.3.2 / §8.4.2.3.2). Per
485/// reference list, per ref index: a luma `(weight, offset)` and two chroma
486/// `(weight, offset)` (Cb, Cr). `log2` denominators are shared.
487#[derive(Clone, Default)]
488pub struct WeightTable {
489    pub luma_log2_denom: i32,
490    pub chroma_log2_denom: i32,
491    /// `[list][ref_idx] = (weight, offset)`.
492    pub luma: [Vec<(i32, i32)>; 2],
493    /// `[list][ref_idx][cb=0/cr=1] = (weight, offset)`.
494    pub chroma: [Vec<[(i32, i32); 2]>; 2],
495}
496
497impl WeightTable {
498    /// True when list-0 ref-0 carries identity weights (w == 1<<denom, offset 0)
499    /// for luma and both chroma planes. x264's weightp=2 puts a pred_weight_table
500    /// in EVERY P slice, but outside fades the entries are identity — and the
501    /// identity weight is an EXACT no-op ((s*2^d + 2^(d-1))>>d + 0 == s), so a
502    /// P_Skip recon may bypass the weighting pass byte-identically.
503    fn ref0_identity(&self) -> bool {
504        self.luma[0]
505            .first()
506            .is_some_and(|&(w, o)| w == 1 << self.luma_log2_denom && o == 0)
507            && self.chroma[0].first().is_some_and(|c| {
508                c.iter()
509                    .all(|&(w, o)| w == 1 << self.chroma_log2_denom && o == 0)
510            })
511    }
512
513    /// True when EVERY entry of `list` (all refs, luma + both chroma) is the
514    /// identity weight — the x264 weightp=2 shape outside fades. Identity is
515    /// an exact per-pixel no-op (see ref0_identity), so the whole per-ref
516    /// weighting pass may be skipped for this list.
517    fn list_identity(&self, list: usize) -> bool {
518        self.luma[list]
519            .iter()
520            .all(|&(w, o)| w == 1 << self.luma_log2_denom && o == 0)
521            && self.chroma[list].iter().all(|c| {
522                c.iter()
523                    .all(|&(w, o)| w == 1 << self.chroma_log2_denom && o == 0)
524            })
525    }
526
527    /// Applies a single-list (uni-prediction) luma weight (spec §8.4.2.3.2).
528    /// Resolves a list/ref slot's (weight, offset) ONCE. `list` is 0..1 and the
529    /// fallback is the IDENTITY weight, which is what an absent entry means.
530    #[inline]
531    fn luma_wo(&self, list: usize, refi: usize) -> (i32, i32) {
532        self.luma[list & 1]
533            .get(refi)
534            .copied()
535            .unwrap_or((1 << self.luma_log2_denom, 0))
536    }
537
538    /// Ditto for chroma component `cc`.
539    #[inline]
540    fn chroma_wo(&self, list: usize, refi: usize, cc: usize) -> (i32, i32) {
541        self.chroma[list & 1]
542            .get(refi)
543            .map(|c| c[cc & 1])
544            .unwrap_or((1 << self.chroma_log2_denom, 0))
545    }
546
547    fn apply_luma(&self, sample: u8, list: usize, refi: usize) -> u8 {
548        let (w, o) = self.luma_wo(list, refi);
549        let lwd = self.luma_log2_denom;
550        let v = if lwd >= 1 {
551            ((sample as i32 * w + (1 << (lwd - 1))) >> lwd) + o
552        } else {
553            sample as i32 * w + o
554        };
555        v.clamp(0, 255) as u8
556    }
557
558    /// Applies a single-list (uni-prediction) chroma weight for component `cc`.
559    fn apply_chroma(&self, sample: u8, list: usize, refi: usize, cc: usize) -> u8 {
560        let (w, o) = self.chroma_wo(list, refi, cc);
561        let cwd = self.chroma_log2_denom;
562        let v = if cwd >= 1 {
563            ((sample as i32 * w + (1 << (cwd - 1))) >> cwd) + o
564        } else {
565            sample as i32 * w + o
566        };
567        v.clamp(0, 255) as u8
568    }
569}
570
571/// Why a macroblock could not be decoded.
572#[derive(Debug, Clone, PartialEq, Eq)]
573pub enum MbError {
574    Truncated,
575    Unsupported(&'static str),
576}
577
578impl From<OutOfData> for MbError {
579    fn from(_: OutOfData) -> Self {
580        MbError::Truncated
581    }
582}
583
584/// Recycled per-picture scratch grids.
585///
586/// `FrameDecoder::new` used to allocate ~1.65 MB of frame-wide grids for EVERY
587/// coded picture and drop them when the picture finished. The sampled profiler
588/// prices that (stage `dec-setup`) at 6.7% of decode — larger than dequant,
589/// reconstruct and intra prediction combined, and none of it is codec work.
590///
591/// Two costs are being paid, and the allocation is the bigger one. A ~460 KB
592/// `Vec` goes straight to the OS, so every page is a fresh zero page and the
593/// decoder takes a soft page fault on FIRST TOUCH of each 4 KB — a cost charged
594/// to whatever per-macroblock stage happens to touch it first, not to the
595/// allocation. Handing the same buffers back keeps the pages mapped and warm.
596///
597/// The initialising fill is NOT skipped: these grids are read as neighbour
598/// context (`modes_y` must read 2/DC, `ref_idx_y` must read -1) before every
599/// block that writes them, so a stale value from the previous picture is a
600/// correctness bug, not a performance trade. `clear()` + `resize()` keeps the
601/// fill and drops only the allocation.
602///
603/// The reconstruction planes are deliberately NOT pooled: `into_frame` MOVES
604/// them out as the caller's output frame, so there is nothing to hand back.
605#[derive(Default)]
606pub struct GridPool {
607    /// Deferred-job boxes carried across pictures (see `take_pinter_job`).
608    job_pool: Vec<Box<PInterJob>>,
609    nores_pool: Vec<Box<PInterNoResJob>>,
610    /// D12: running bits-per-macroblock of decoded slices, the E2 dispatch's
611    /// density signal. Lives here because `GridPool` is the only state that
612    /// survives a picture (`FrameDecoder` is rebuilt per picture).
613    bits_per_mb: f64,
614    mb_qp: Vec<u8>,
615    bs_frame: Vec<rusty_h264_common::deblock::MbBs>,
616    pk_prev: Vec<rusty_h264_common::deblock::MbPack>,
617    pk_cur: Vec<rusty_h264_common::deblock::MbPack>,
618    nnz_dbr: Vec<u8>,
619    bak_y: Vec<u8>,
620    bak_u: Vec<u8>,
621    bak_v: Vec<u8>,
622    nnz_y: Vec<u8>,
623    nnz_c0: Vec<u8>,
624    nnz_c1: Vec<u8>,
625    modes_y: Vec<u8>,
626    coded_y: Vec<bool>,
627    /// NARROWED (i32,i32) -> (i16,i16). A motion vector is quarter-pel and
628    /// level-bounded (+/-2048 qpel at 5.1), so i16 has 16x headroom -- and
629    /// `pack_mb` ALREADY narrowed these to i16 unconditionally for the deblock
630    /// path, so the store-side assumption predates this change rather than being
631    /// introduced by it.
632    ///
633    /// Rust has no implicit numeric coercion, so the COMPILER is the proof that
634    /// no arithmetic silently happens at i16: every read site had to be given an
635    /// explicit widen, and anything left implicit would not compile.
636    mv_y: Vec<(i16, i16)>,
637    inter_y: Vec<bool>,
638    /// NARROWED i32 -> i8 (2026-09-07). A `ref_idx` is -1..31 by the spec
639    /// (7.4.5.1: num_ref_idx_active is at most 32), so three of every four
640    /// bytes were padding -- and this grid is re-armed for EVERY picture,
641    /// which the copy census priced at 69 MB over a 60-frame 720p decode.
642    ref_idx_y: Vec<i8>,
643    mv1: Vec<(i16, i16)>,
644    ref_idx1: Vec<i8>,
645    mb_t8x8: Vec<bool>,
646    /// Bit per macroblock: the DECODER knows this macroblock's sixteen blocks
647    /// share one motion set in both lists, so the deblock derivation need not run
648    /// the `mb_uniform` kernel to rediscover it.
649    ///
650    /// Deliberately NOT folded into `mb_kind`: that enum's `InterUniform` carries
651    /// a single-list contract the blind derivation arms rely on, and a
652    /// bi-predicted direct macroblock would violate it. This says one thing only.
653    mb_umot: Vec<bool>,
654    mb_kind: Vec<u8>,
655    bzero: Vec<bool>,
656    // CABAC slice scratch (D13 follow-through): these were fresh `vec![..]`
657    // allocations at EVERY slice entry — ~407 KB per P slice, ~700 KB per B
658    // slice at 720p, the same fresh-page class GridPool exists to kill.
659    sc_cat: Vec<u8>,
660    sc_cbp: Vec<u8>,
661    // Chroma pred mode is 0..=3 (7.4.5: intra_chroma_pred_mode), with -1 as the
662    // "no neighbour" sentinel and the only read being `(1..=3).contains(..)`.
663    // i32 was four bytes carrying two bits.
664    sc_cmode: Vec<i8>,
665    sc_nzc: Vec<[u8; 24]>,
666    sc_cbfdc: Vec<u16>,
667    sc_skip: Vec<bool>,
668    sc_ref: Vec<[i8; 16]>,
669    /// SATURATED-ABSOLUTE, `u8` (2026-09-07). This was `[[i16; 2]; 16]` -- the
670    /// signed mvd per 4x4 block -- but its ONLY consumer is the CABAC mvd
671    /// ctxInc, which computes `|nb| + |na|` and compares it against 3 and 32.
672    /// Neither the sign nor any magnitude above 33 can change that answer:
673    /// if either neighbour is >= 33 the sum already exceeds 32 both ways, and
674    /// if both are < 33 saturating is a no-op. So a byte carries exactly the
675    /// information an i16 did, at half the width -- on a grid refilled per
676    /// SLICE, which the copy census priced at 230 KB a time.
677    sc_mvd: Vec<[[u8; 2]; 16]>,
678    sc_ref1: Vec<[i8; 16]>,
679    sc_mvd1: Vec<[[u8; 2]; 16]>,
680    sc_direct: Vec<bool>,
681    // D24 (inline-execution.md 11.10): the ref-POC mirrors were the only
682    // per-picture Vecs NOT recycled - a fresh alloc + collect per picture each.
683    ref_poc0: Vec<i32>,
684    ref_poc1: Vec<i32>,
685}
686
687/// Reuse `v`'s allocation for `n` copies of `val`. Identical OBSERVABLE result to
688/// `vec![val; n]`; differs only in that it reuses the existing allocation when the
689/// capacity already suffices.
690// Saturate to the only thing the mvd ctxInc can distinguish: |mvd| capped at
691// 33. Above that the neighbour sum already exceeds 32, so the context is
692// identical and the extra magnitude is unreachable information.
693fn sat_mvd(m: [[i16; 2]; 16]) -> [[u8; 2]; 16] {
694    core::array::from_fn(|i| {
695        core::array::from_fn(|c| m[i][c].unsigned_abs().min(33) as u8)
696    })
697}
698
699#[inline]
700#[cfg_attr(feature = "profile", track_caller)]
701fn refill<T: Clone>(mut v: Vec<T>, n: usize, val: T) -> Vec<T> {
702    // The single funnel for every per-picture grid clear, so instrumenting it
703    // here prices the whole pool re-arm in one place. Pooling made the ALLOCATION
704    // free; what is left is the clear, and that is the thing to measure.
705    cpystat::note(&cpystat::POOL_CLEAR, n * core::mem::size_of::<T>());
706    #[cfg(feature = "profile")]
707    cpystat::note_line(
708        core::panic::Location::caller().line(),
709        n * core::mem::size_of::<T>(),
710    );
711    v.clear();
712    v.resize(n, val);
713    v
714}
715
716impl FrameDecoder {
717    /// Non-pooled ctor — tests only; production always enters via `with_pool`
718    /// (`GridPool` recycling, see lib.rs).
719    #[cfg(test)]
720    pub fn new(
721        mb_w: usize,
722        mb_h: usize,
723        qp: u8,
724        chroma_qp_offset: i32,
725        refs: Vec<crate::Ref>,
726        num_ref_active: usize,
727        constrained_intra: bool,
728        transform_8x8_mode: bool,
729        b_possible: bool,
730    ) -> Self {
731        Self::with_pool(
732            mb_w,
733            mb_h,
734            qp,
735            chroma_qp_offset,
736            refs,
737            num_ref_active,
738            constrained_intra,
739            transform_8x8_mode,
740            b_possible,
741            GridPool::default(),
742        )
743    }
744
745    /// As `new`, but reusing a previous picture's grid allocations. See `GridPool`.
746    pub fn with_pool(
747        mb_w: usize,
748        mb_h: usize,
749        qp: u8,
750        chroma_qp_offset: i32,
751        refs: Vec<crate::Ref>,
752        num_ref_active: usize,
753        constrained_intra: bool,
754        transform_8x8_mode: bool,
755        b_possible: bool,
756        pool: GridPool,
757    ) -> Self {
758        let (cw, ch) = (mb_w * 16, mb_h * 16);
759        let (ccw, cch) = (cw / 2, ch / 2);
760        let bits_per_mb = pool.bits_per_mb;
761        let ref_poc0 = {
762            let mut v = pool.ref_poc0;
763            v.clear();
764            v.extend(refs.iter().map(|f| f.pic_poc()));
765            v
766        };
767        Self {
768            mb_w,
769            mb_h,
770            qp,
771            cur_qp: qp,
772            chroma_qp_offset,
773            cw,
774            ch,
775            ccw,
776            cch,
777            rec_y: vec![0; cw * ch],
778            rec_u: vec![0; ccw * cch],
779            rec_v: vec![0; ccw * cch],
780            mb_qp: refill(pool.mb_qp, mb_w * mb_h, qp),
781            slice_first_mb: 0,
782            nnz_y: refill(pool.nnz_y, (mb_w * 4) * (mb_h * 4), 0),
783            nnz_c: [
784                refill(pool.nnz_c0, (mb_w * 2) * (mb_h * 2), 0),
785                refill(pool.nnz_c1, (mb_w * 2) * (mb_h * 2), 0),
786            ],
787            modes_y: refill(pool.modes_y, (mb_w * 4) * (mb_h * 4), 2),
788            coded_y: refill(pool.coded_y, (mb_w * 4) * (mb_h * 4), false),
789            mv_y: refill(pool.mv_y, (mb_w * 4) * (mb_h * 4), (0, 0)),
790            inter_y: refill(pool.inter_y, (mb_w * 4) * (mb_h * 4), false),
791            ref_idx_y: refill(pool.ref_idx_y, (mb_w * 4) * (mb_h * 4), -1i8),
792            // GATED ON b_possible: the List-1 grids are written only by B-slice
793            // paths and read only through `has1`/`ref_id1.is_empty()`, both of
794            // which are false without B slices. `as_reference_pooled` already
795            // gates its clones this way -- the pool refill did not, so a
796            // Baseline stream cleared 478 KB per picture it could never read.
797            mv1: if b_possible {
798                refill(pool.mv1, (mb_w * 4) * (mb_h * 4), (0, 0))
799            } else {
800                let mut v = pool.mv1;
801                v.clear();
802                v
803            },
804            ref_idx1: if b_possible {
805                refill(pool.ref_idx1, (mb_w * 4) * (mb_h * 4), -1i8)
806            } else {
807                let mut v = pool.ref_idx1;
808                v.clear();
809                v
810            },
811            refs1: Vec::new(),
812            num_ref_active1: 0,
813            is_b: false,
814            b_possible,
815            direct_spatial: true,
816            nnz_l_cache: [0x80; 25],
817            nnz_c_cache: [[0x80; 9]; 2],
818            refs,
819            num_ref_active,
820            constrained_intra,
821            scaling: None,
822            scaling8: None,
823            transform_8x8_mode,
824            transform_bypass: false,
825            route_skip_mbs: 0,
826            route_coded_mbs: 0,
827            slice_bounds: Vec::new(),
828            cur_idc2: false,
829            mb_t8x8: refill(pool.mb_t8x8, mb_w * mb_h, false),
830            mb_umot: refill(pool.mb_umot, mb_w * mb_h, false),
831            bs_frame: refill(pool.bs_frame, mb_w * mb_h, Default::default()),
832            bs_rows: 0,
833            flt_rows: 0,
834            pk_prev: {
835                let mut v = pool.pk_prev;
836                v.clear();
837                v
838            },
839            pk_cur: {
840                let mut v = pool.pk_cur;
841                v.clear();
842                v
843            },
844            // GATED ON THE PPS FLAG, not just on `any_t8`. The four reads of
845            // this grid all sit inside `if self.any_t8`, and `any_t8` is only
846            // ever set from `transform_size_8x8_flag`, which the PPS's
847            // `transform_8x8_mode_flag` gates. So with 8x8 off the grid is never
848            // touched -- and it was still being cleared for every picture, which
849            // the copy census priced at 17.3 MB over a 60-frame decode. Main and
850            // Baseline profiles cannot carry 8x8 at all, so this is the common
851            // case rather than an edge one.
852            nnz_dbr: if transform_8x8_mode {
853                refill(pool.nnz_dbr, (mb_w * 4) * (mb_h * 4), 0)
854            } else {
855                let mut v = pool.nnz_dbr;
856                v.clear();
857                v
858            },
859            bak_y: refill(pool.bak_y, cw, 0),
860            bak_u: refill(pool.bak_u, ccw, 0),
861            bak_v: refill(pool.bak_v, ccw, 0),
862            edc_jobs: Vec::new(),
863            edc_job_pool: pool.job_pool,
864            edc_nores_pool: pool.nores_pool,
865            scratch_luma: None,
866            scratch_luma8: None,
867            scratch_cac: None,
868            edc_active: false,
869            edc_tx: None,
870            edc_ctx_rx: None,
871            edc_back_tx: None,
872            fault: None,
873            edc_parked: None,
874            edc_regions: None,
875            bsk_last: None,
876            edc_batch: Vec::new(),
877            bits_per_mb,
878            db_ena: false,
879            db_oa: 0,
880            db_ob: 0,
881            mb_kind: refill(
882                pool.mb_kind,
883                mb_w * mb_h,
884                rusty_h264_common::deblock::MB_KIND_UNSET,
885            ),
886            weights: None,
887            row_wait: crate::row_progress_on(),
888            col_ok: false,
889            col_w4: 0,
890            any_t8: false,
891            any_idc2: false,
892            skip_zero_next: usize::MAX,
893            bzero: refill(pool.bzero, mb_w * mb_h, false),
894            sc_cat: pool.sc_cat,
895            sc_cbp: pool.sc_cbp,
896            sc_cmode: pool.sc_cmode,
897            sc_nzc: pool.sc_nzc,
898            sc_cbfdc: pool.sc_cbfdc,
899            sc_skip: pool.sc_skip,
900            sc_ref: pool.sc_ref,
901            sc_mvd: pool.sc_mvd,
902            sc_ref1: pool.sc_ref1,
903            sc_mvd1: pool.sc_mvd1,
904            sc_direct: pool.sc_direct,
905            bzspan: None,
906            pzspan: None,
907            iw00: None,
908            weights_id0: false,
909            weights_l0id: false,
910            cur_poc: 0,
911            weighted_bipred_idc: 0,
912            direct_8x8_inference: false,
913            progress: None,
914            ref_poc0,
915            ref_poc1: {
916                let mut v = pool.ref_poc1;
917                v.clear();
918                v
919            },
920        }
921    }
922
923    fn refresh_ref_pocs(&mut self) {
924        self.ref_poc0.clear();
925        self.ref_poc0.extend(self.refs.iter().map(|f| f.pic_poc()));
926        self.ref_poc1.clear();
927        self.ref_poc1.extend(self.refs1.iter().map(|f| f.pic_poc()));
928    }
929
930    /// Frame-MT Phase B: attach the shared progress Arc for row watermarks.
931    pub fn set_progress_slot(&mut self, slot: crate::Ref) {
932        self.progress = Some(slot);
933    }
934
935    /// Wait until every L0/L1 ref has enough ready luma rows for MB row `mb_y`
936    /// (pad conservatively for MV overshoot).
937    #[inline(always)]
938    fn wait_refs_for_mb(&self, mb_y: usize) {
939        // Cached at construction: in 1T (row-progress off, the default) this
940        // is one field test instead of a fn call + OnceLock read per MB.
941        if !self.row_wait {
942            return;
943        }
944        let need = crate::RefFrame::rows_needed_for_mb(mb_y, self.ch);
945        crate::RefFrame::set_mc_row_need(mb_y, self.ch);
946        for r in self.refs.iter().chain(self.refs1.iter()) {
947            // Only pay the wait when the ref may still be in-flight (Phase B).
948            if r.live.is_some() && r.frozen.get().is_none() {
949                r.wait_ready_rows(need);
950            }
951        }
952    }
953
954    fn publish_progress(&mut self) {
955        let Some(slot) = &self.progress else {
956            return;
957        };
958        // Under EDC the worker publishes (PixelCtx::publish_progress_rows).
959        if self.edc_tx.is_some() {
960            return;
961        }
962        // Cached field, not the knob function: this runs per ROW.
963        if !rowdb_on() || !self.db_ena || self.flt_rows == 0 {
964            return;
965        }
966        publish_filtered_rows_to_slot(
967            slot,
968            &self.rec_y,
969            &self.rec_u,
970            &self.rec_v,
971            self.cw,
972            self.ccw,
973            self.ch,
974            self.flt_rows,
975        );
976    }
977
978    /// Sets the explicit weighted-prediction tables for this slice.
979    pub fn set_weights(&mut self, weights: WeightTable) {
980        self.weights_id0 = weights.ref0_identity();
981        self.weights_l0id = weights.list_identity(0);
982        self.weights = Some(weights);
983    }
984
985    /// Applies explicit uni-prediction weighting to a motion-compensated partition
986    /// (luma `pred_y` region + the two chroma planes), if weighting is active.
987    /// `list` is the reference list and `refi` the partition's reference index.
988    fn weight_partition(
989        &self,
990        pred_y: &mut [u8; 256],
991        c_pred: &mut [[u8; 64]; 2],
992        list: usize,
993        refi: usize,
994        rx: usize,
995        ry: usize,
996        rw: usize,
997        rh: usize,
998    ) {
999        let Some(wt) = &self.weights else { return };
1000        // Identity list-0 table (x264 weightp=2 outside fades): the whole pass
1001        // is an exact per-pixel no-op — skip it. RS_H264_NO_SKIPFP restores it
1002        // for paired A/B (same knob family as the P_Skip weight skip).
1003        if list == 0 && self.weights_l0id && !no_skipfp() {
1004            edcstat::bump(&edcstat::WP_SKIPPED, 1);
1005            return;
1006        }
1007        // REFUTED, reverted: row-slicing these loops measured +28% instructions
1008        // on `weight_partition` (the extents are runtime values, so the slice
1009        // bounds cost more than the per-sample checks they replaced). The real
1010        // win for this function was hoisting the identity test to its CALLERS,
1011        // which stands.
1012        // RESOLVE ONCE, APPLY MANY. `apply_luma` re-read `self.luma[list][refi]`
1013        // — an array index, a Vec deref and an element index, two of them bounds
1014        // checked — on EVERY sample, up to 256 luma plus 128 chroma per call.
1015        // The weight is a property of the partition, not of the pixel. (The loop
1016        // SHAPE is untouched: row-slicing it is refuted above.)
1017        // MASKED, not row-sliced. `pred_y` is a fixed `[u8; 256]` and `c_pred` a
1018        // `[[u8; 64]; 2]`, so `& 255` / `& 63` are no-ops that prove the index
1019        // outright — where SLICING these loops cost +28% (the extents are runtime
1020        // values, so the slice bounds outweigh the checks). Same lesson as
1021        // `luma_centre`: the refutation was of one SHAPE, not of the goal.
1022        let (lw, lo) = wt.luma_wo(list, refi);
1023        weight_block(pred_y, 16, rx, ry, rw, rh, lw, lo, wt.luma_log2_denom);
1024        let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
1025        for cc in 0..2 {
1026            let (cw, co) = wt.chroma_wo(list, refi, cc);
1027            weight_block(
1028                &mut c_pred[cc & 1],
1029                8,
1030                crx,
1031                cry,
1032                crw,
1033                crh,
1034                cw,
1035                co,
1036                wt.chroma_log2_denom,
1037            );
1038        }
1039    }
1040
1041    /// Sets the High-profile scaling matrices (raster order: six 4×4 lists, two
1042    /// 8×8 luma lists). The caller un-zig-zags the SPS lists. Flat is the default.
1043    /// GATE 1 router counters: (skip MBs, residual-coded MBs) this picture.
1044    pub fn route_counters(&self) -> (u32, u32) {
1045        (self.route_skip_mbs, self.route_coded_mbs)
1046    }
1047
1048    /// SPS `qpprime_y_zero_transform_bypass_flag` — see [`Self::step_qp`].
1049    pub fn set_transform_bypass(&mut self, on: bool) {
1050        self.transform_bypass = on;
1051    }
1052
1053    pub fn set_scaling(&mut self, scaling: [[i32; 16]; 6], scaling8: [[i32; 64]; 2]) {
1054        self.scaling = Some(scaling);
1055        self.scaling8 = Some(scaling8);
1056    }
1057
1058    /// Dequantizes a 4×4 AC block with scaling list `list` (flat if none active).
1059    /// Per-(qp, list) dequant constants for the fused scan-order kernel: the flat
1060    /// table at zero cost, or the scaling-list build once per macroblock.
1061    #[inline]
1062    fn dq_const(&self, qp: u8, list: usize) -> DequantQp {
1063        match &self.scaling {
1064            Some(s) => DequantQp::weighted(qp, &s[list]),
1065            None => DQ_FLAT[(qp as usize).min(51)],
1066        }
1067    }
1068
1069    /// Single-coefficient twin of `dequant` for position 0 (DC-only fast path).
1070    fn dequant_dc4(&self, level: i32, qp: u8, list: usize) -> i32 {
1071        rusty_h264_common::transform::dequantize_dc4(
1072            level,
1073            qp,
1074            self.scaling.as_ref().map(|s| s[list][0]),
1075        )
1076    }
1077
1078    /// Inverse-quantizes a chroma DC block with scaling list `list`'s DC weight.
1079    fn dequant_chroma_dc(&self, levels: &[i32; 4], qp: u8, list: usize) -> [i32; 4] {
1080        match &self.scaling {
1081            Some(s) => inverse_quant_chroma_dc_weighted(levels, qp, s[list][0]),
1082            None => inverse_quant_chroma_dc(levels, qp),
1083        }
1084    }
1085
1086    /// Sets the B-slice context for the slice about to be decoded: `RefPicList1`,
1087    /// its active count, and the direct-mode flag.
1088    pub fn set_b_context(
1089        &mut self,
1090        refs1: Vec<crate::Ref>,
1091        num_ref_active1: usize,
1092        direct_spatial: bool,
1093        cur_poc: i32,
1094        weighted_bipred_idc: u8,
1095        direct_8x8_inference: bool,
1096    ) {
1097        self.is_b = true;
1098        self.refs1 = refs1;
1099        self.num_ref_active1 = num_ref_active1;
1100        self.direct_spatial = direct_spatial;
1101        self.cur_poc = cur_poc;
1102        self.weighted_bipred_idc = weighted_bipred_idc;
1103        self.direct_8x8_inference = direct_8x8_inference;
1104        self.iw00 = None; // slice-scoped cache: lists/POC may have changed
1105                          // Colocated fast-probe cache (see col_zero / col_zero_fast).
1106        self.col_ok = self
1107            .refs1
1108            .first()
1109            .is_some_and(|c| c.live.is_none() && !c.long_term && c.w4 != 0);
1110        self.col_w4 = self.refs1.first().map_or(0, |c| c.w4);
1111        self.refresh_ref_pocs();
1112    }
1113
1114    /// Cached `implicit_weights(0, 0)` — constant within a slice.
1115    fn iw00(&mut self) -> Option<(i32, i32)> {
1116        // Read the discriminant ONCE. The `is_none()` + `unwrap()` pair tested it
1117        // twice and carried an `unwrap_failed` path for a value this very function
1118        // had just written. `get_or_insert_with` cannot be used here: the closure
1119        // would need `&mut self` while the `Option` is already borrowed.
1120        match self.iw00 {
1121            Some(v) => v,
1122            None => {
1123                let v = if self.refs.is_empty() || self.refs1.is_empty() {
1124                    None
1125                } else {
1126                    self.implicit_weights(0, 0)
1127                };
1128                self.iw00 = Some(v);
1129                v
1130            }
1131        }
1132    }
1133
1134    /// Steps the running luma QP by a `mb_qp_delta` (spec §7.4.5, 8-bit depth):
1135    /// `QPy = (QPy_prev + delta + 52) % 52`.
1136    /// Steps QPy by `mb_qp_delta` (spec §7.4.5 modulo). Called exactly for
1137    /// residual-coded macroblocks (mb_qp_delta presence ⇔ cbp != 0 or I_16x16),
1138    /// which makes it the one chokepoint for the transform-bypass refusal:
1139    /// with `qpprime_y_zero_transform_bypass_flag` set and QP'Y == 0 the
1140    /// residual is LOSSLESS-bypassed (no transform, no quant — and DPCM intra
1141    /// forms), which this decoder does not implement. Refusing here is loud
1142    /// and exact: all-PCM lossless streams (no mb_qp_delta) still decode.
1143    fn step_qp(&mut self, delta: i32) -> Result<(), MbError> {
1144        // Router counter: step_qp fires exactly once per residual-coded MB.
1145        self.route_coded_mbs += 1;
1146        self.cur_qp = (self.cur_qp as i32 + delta + 52).rem_euclid(52) as u8;
1147        if self.transform_bypass && self.cur_qp == 0 {
1148            return Err(MbError::Unsupported(
1149                "transform-bypass (lossless) macroblock",
1150            ));
1151        }
1152        Ok(())
1153    }
1154
1155    /// Maps a luma QP to its chroma QP, applying `chroma_qp_index_offset`
1156    /// (spec §8.5.8): `QPc = qpc_table(Clip3(0, 51, QPy + offset))`.
1157    fn chroma_qp_for(&self, qp_y: u8) -> u8 {
1158        let qpi = (qp_y as i32 + self.chroma_qp_offset).clamp(0, 51) as u8;
1159        chroma_qp(qpi)
1160    }
1161
1162    /// Resets per-slice state before decoding a continuation slice of the same
1163    /// picture: the running QP (each slice carries its own `slice_qp`) and the
1164    /// reference list (each slice may reorder it).
1165    pub fn begin_slice(&mut self, slice_qp: u8, refs: Vec<crate::Ref>, num_ref_active: usize) {
1166        self.cur_qp = slice_qp;
1167        self.qp = slice_qp;
1168        self.refs = refs;
1169        self.num_ref_active = num_ref_active;
1170        self.weights = None; // re-set per slice if a pred_weight_table is present
1171        self.weights_id0 = false;
1172        self.weights_l0id = false;
1173        self.refresh_ref_pocs();
1174    }
1175
1176    /// Whether the neighbor macroblock at `(nbx, nby)` is in the slice currently
1177    /// being decoded (address ≥ the slice's first MB). For single-slice pictures
1178    /// `slice_first_mb == 0`, so this is always true and prediction is unchanged.
1179    #[inline]
1180    fn nbr_in_slice(&self, nbx: usize, nby: usize) -> bool {
1181        nby * self.mb_w + nbx >= self.slice_first_mb
1182    }
1183
1184    /// Whether the neighbor 4×4 block at `(nbx, nby)` may contribute to intra
1185    /// prediction. With `constrained_intra_pred`, an inter-coded neighbor is
1186    /// treated as unavailable (spec §8.3.1.2.{1,2}); otherwise always usable.
1187    #[inline]
1188    fn intra_nbr_ok(&self, nbx: usize, nby: usize) -> bool {
1189        // Fallible read: this is inlined at eleven sites across the intra paths,
1190        // and `constrained_intra` short-circuits it on nearly every stream, so
1191        // the check was pure tax on a value that is usually never loaded.
1192        // `unwrap_or(true)` is the conservative direction — an out-of-range
1193        // neighbour reads as inter, i.e. UNAVAILABLE, which is what the
1194        // constrained-intra rule means by a neighbour it cannot use.
1195        !self.constrained_intra
1196            || !self
1197                .inter_y
1198                .get(nby * (self.mb_w * 4) + nbx)
1199                .copied()
1200                .unwrap_or(true)
1201    }
1202
1203    fn mv_neighbors(&self, mb_x: usize, mb_y: usize) -> [MvNeighbor; 3] {
1204        let w4 = self.mb_w * 4;
1205        let get = |avail: bool, bx: isize, by: isize| {
1206            if avail {
1207                let idx = by as usize * w4 + bx as usize;
1208                match (self.mv_y.get(idx), self.ref_idx_y.get(idx)) {
1209                    (Some(&m), Some(&r)) => MvNeighbor {
1210                        available: true,
1211                        mv: (i32::from(m.0), i32::from(m.1)),
1212                        ref_idx: i32::from(r),
1213                    },
1214                    _ => MvNeighbor::NONE,
1215                }
1216            } else {
1217                MvNeighbor::NONE
1218            }
1219        };
1220        let (bx, by) = (mb_x as isize * 4, mb_y as isize * 4);
1221        let a = get(mb_x > 0 && self.nbr_in_slice(mb_x - 1, mb_y), bx - 1, by);
1222        let b = get(mb_y > 0 && self.nbr_in_slice(mb_x, mb_y - 1), bx, by - 1);
1223        let c = if mb_y > 0 && mb_x + 1 < self.mb_w && self.nbr_in_slice(mb_x + 1, mb_y - 1) {
1224            get(true, bx + 4, by - 1)
1225        } else {
1226            get(
1227                mb_x > 0 && mb_y > 0 && self.nbr_in_slice(mb_x - 1, mb_y - 1),
1228                bx - 1,
1229                by - 1,
1230            )
1231        };
1232        [a, b, c]
1233    }
1234
1235    fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
1236        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
1237        self.mv_neighbors_block_grid(pbx, pby, pwb, 0)
1238    }
1239
1240    fn skip_mv(&self, mb_x: usize, mb_y: usize) -> (i32, i32) {
1241        let [a, b, c] = self.mv_neighbors(mb_x, mb_y);
1242        if !a.available
1243            || !b.available
1244            || (a.ref_idx == 0 && a.mv == (0, 0))
1245            || (b.ref_idx == 0 && b.mv == (0, 0))
1246        {
1247            (0, 0)
1248        } else {
1249            predict_mv(a, b, c, 0)
1250        }
1251    }
1252
1253    fn set_mb_mv(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32), inter: bool, refi: i32) {
1254        // ROW FILLS. This wrote all sixteen cells of three grids one indexed
1255        // store at a time - FORTY-EIGHT separate bounds checks per call - and
1256        // re-evaluated `if inter` inside the inner loop. Each macroblock row is
1257        // four CONTIGUOUS cells, so twelve `fill`s cover it.
1258        let w4 = self.mb_w * 4;
1259        let r = if inter { refi } else { -1 };
1260        for dy in 0..4 {
1261            let a = (mb_y * 4 + dy) * w4 + mb_x * 4;
1262            self.mv_y[a..a + 4].fill((mv.0 as i16, mv.1 as i16));
1263            self.inter_y[a..a + 4].fill(inter);
1264            self.ref_idx_y[a..a + 4].fill(r as i8);
1265        }
1266    }
1267
1268    /// Commit one inter partition's motion into the 4×4 grid (ref 0, 1-ref P).
1269    /// `(rx,ry,rw,rh)` are MB-relative luma pixels; committing before the next
1270    /// partition's prediction is what lets a later partition predict from it.
1271    fn commit_inter_grid(
1272        &mut self,
1273        mb_x: usize,
1274        mb_y: usize,
1275        rx: usize,
1276        ry: usize,
1277        rw: usize,
1278        rh: usize,
1279        mv: (i32, i32),
1280        refi: i8,
1281    ) {
1282        // Row-range fills instead of the per-4x4 scatter: partitions are
1283        // rectangles, so each grid row is one contiguous run (same values).
1284        let w4 = self.mb_w * 4;
1285        let (bx0, bw) = (mb_x * 4 + rx / 4, rw / 4);
1286        for by in ry / 4..ry / 4 + rh / 4 {
1287            let a = (mb_y * 4 + by) * w4 + bx0;
1288            self.mv_y[a..a + bw].fill((mv.0 as i16, mv.1 as i16));
1289            self.inter_y[a..a + bw].fill(true);
1290            self.ref_idx_y[a..a + bw].fill(refi);
1291            self.coded_y[a..a + bw].fill(true);
1292        }
1293    }
1294
1295    /// Per-slice deblock parameters, needed DURING decode by the row-interleave
1296    /// path. `ena` is already resolved against `RFF_ABL_DEBLOCK` by the caller.
1297    pub fn set_deblock_params(&mut self, ena: bool, oa: i32, ob: i32, idc2: bool) {
1298        // Latch: the FIRST disabling slice turns row filtering off for the rest
1299        // of the picture (see `row_hook`); rows already filtered stay counted
1300        // in `flt_rows` and the picture-end tail handles the remainder.
1301        self.db_ena = ena && (self.flt_rows == 0 || self.db_ena);
1302        self.db_oa = oa;
1303        self.db_ob = ob;
1304        self.cur_idc2 = idc2;
1305    }
1306
1307    /// Slice index owning `addr` (slices are raster-contiguous, bounds ascending).
1308    fn slice_of(&self, addr: usize) -> usize {
1309        match self.slice_bounds.binary_search_by(|&(f, _)| f.cmp(&addr)) {
1310            Ok(i) => i,
1311            Err(i) => i - 1,
1312        }
1313    }
1314
1315    /// Derives bS for macroblock row `r` from the just-decoded (hot) grids into
1316    /// `bs_frame`, maintaining the two-row rolling record window (R2 of
1317    /// docs/row-interleave-plan.md).
1318    fn derive_bs_row(&mut self, r: usize) {
1319        // bS derivation reads the motion/nnz grids — flush deferred spans.
1320        self.span_flush();
1321        // Same stage label the in-filter derivation used, so profiles keep
1322        // pricing bS derivation wherever it lives.
1323        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DebDerive);
1324        use rusty_h264_common::deblock::{
1325            derive_mb_kind, BlockInfo, MbBs, MbKind,
1326        };
1327        let (mb_w, w4) = (self.mb_w, self.mb_w * 4);
1328        edcstat::bump(&edcstat::DBS_ROWS, 1);
1329        // Transform-block coded mask for this row: raw nnz, then the 8x8 OR for
1330        // t8 macroblocks (spec §8.7: the 8x8 transform's coded status is per 8x8).
1331        //
1332        // T8 GATE. `nnz_dbr` exists ONLY to carry that OR. With no 8x8
1333        // macroblock anywhere in the picture it is a byte-for-byte copy of
1334        // `nnz_y`, so both the 4-row copy and the per-row scan below are pure
1335        // work: the derivation reads `nnz_y` directly instead. Census
1336        // (DBSDERIVE) measured t8mb=0 on EVERY cavlc and main stream while
1337        // nnz_rowcopy ran 10800-16320 sub-rows per decode.
1338        if self.any_t8 {
1339            for br in r * 4..r * 4 + 4 {
1340                let a = br * w4;
1341                self.nnz_dbr[a..a + w4].copy_from_slice(&self.nnz_y[a..a + w4]);
1342            }
1343            edcstat::bump(&edcstat::DBS_NNZ_ROWCOPY, 4);
1344            // WHOLE-MACROBLOCK ROW SLICES. The 2x2-of-2x2 nest did sixteen
1345            // bounds-checked reads and sixteen bounds-checked writes per t8
1346            // macroblock; the same cells are four contiguous runs of four, so
1347            // four slice reads and four slice writes cover them. The OR is over
1348            // bytes, so `a | b | c | d > 0` is exactly the old per-cell `> 0`.
1349            let t8_row_pre = &self.mb_t8x8[r * mb_w..][..mb_w];
1350            for mb_x in 0..mb_w {
1351                if !t8_row_pre[mb_x] {
1352                    continue;
1353                }
1354                edcstat::bump(&edcstat::DBS_T8MB, 1);
1355                let base = r * 4 * w4 + mb_x * 4;
1356                let mut src = [[0u8; 4]; 4];
1357                for (k, row) in src.iter_mut().enumerate() {
1358                    row.copy_from_slice(&self.nnz_y[base + k * w4..][..4]);
1359                }
1360                let mut out = [[0u8; 4]; 4];
1361                for b8 in 0..4usize {
1362                    let (cx, cy) = ((b8 % 2) * 2, (b8 / 2) * 2);
1363                    let any =
1364                        (src[cy][cx] | src[cy][cx + 1] | src[cy + 1][cx] | src[cy + 1][cx + 1]) > 0;
1365                    for sy in 0..2 {
1366                        for sx in 0..2 {
1367                            out[cy + sy][cx + sx] = any as u8;
1368                        }
1369                    }
1370                }
1371                for (k, row) in out.iter().enumerate() {
1372                    self.nnz_dbr[base + k * w4..][..4].copy_from_slice(row);
1373                }
1374            }
1375        }
1376        let info = BlockInfo {
1377            inter: &self.inter_y,
1378            nnz: if self.any_t8 {
1379                &self.nnz_dbr
1380            } else {
1381                &self.nnz_y
1382            },
1383            mv: &self.mv_y,
1384            ref_id: &self.ref_idx_y,
1385            mv1: &self.mv1,
1386            ref_id1: if self.ref_poc1.is_empty() {
1387                &[]
1388            } else {
1389                &self.ref_idx1
1390            },
1391            w4,
1392            t8x8: &self.mb_t8x8,
1393            bs: &[],
1394            poc0: &self.ref_poc0,
1395            poc1: &self.ref_poc1,
1396            kind: &self.mb_kind,
1397        };
1398        let has1 = !info.ref_id1.is_empty();
1399        core::mem::swap(&mut self.pk_prev, &mut self.pk_cur);
1400        // ROW-LENGTH ROWS, WRITTEN IN PLACE. These were cleared here and `push`ed
1401        // once per macroblock, so every 288-byte record was built in a stack
1402        // temporary and memcpy'd into the vector, behind a capacity test and a
1403        // `grow_one` edge. Holding both rows at `mb_w` makes the slot addressable
1404        // before the record is built. Reads are unchanged: the loop only looks at
1405        // `mb_x` and `mb_x - 1`, both already written this row.
1406        if self.pk_cur.len() != mb_w {
1407            self.pk_cur.clear();
1408            self.pk_cur
1409                .resize(mb_w, rusty_h264_common::deblock::MbPack::default());
1410        }
1411        // WIN: read the knob at the USE SITE, not through the struct field.
1412        //  is  under cfg(not(knobs)), so LLVM folds
1413        // this to a constant and the kind-loads arm below becomes dead -- which
1414        // is what finally lets  (1543 instrs of Skip/InterUniform/
1415        // Inter gathering the decoder never runs) leave the decode binary.
1416        // Storing a build-time-constant knob in a FIELD erases the constant:
1417        // the field is a runtime value and every arm it guards stays live.
1418        let kl = kind_loads();
1419        // Census gate resolved ONCE per row: `edcstat::on()` is a relaxed atomic
1420        // load and LLVM will not CSE atomic loads, so a per-MB bump costs a load
1421        // each. Same rule as hoisting an A/B arm selector out of the loop under
1422        // test (codec-measurement 15).
1423        let stats = edcstat::on();
1424        // ROW SLICES for the three per-macroblock grids. Each was indexed
1425        // `r * mb_w + mb_x` against a whole-FRAME Vec, which the compiler
1426        // cannot prove in range; sliced to THIS row, `[mb_x]` is provably
1427        // `< mb_w`. These are disjoint FIELDS of `self`, so the `&mut` on
1428        // `bs_frame` coexists with the `&` borrows `info` holds.
1429        let row0 = r * mb_w;
1430        let bs_row = &mut self.bs_frame[row0..][..mb_w];
1431        let t8_row = &self.mb_t8x8[row0..][..mb_w];
1432        let umot_row = &self.mb_umot[row0..][..mb_w];
1433        // SLICED LIKE ITS SIBLINGS, not through an emptiness test. `mb_kind` is
1434        // built by the same `refill(pool, mb_w * mb_h, ..)` that builds `bs_frame`,
1435        // `mb_t8x8` and `mb_umot` -- the three lines directly above already slice
1436        // on exactly this invariant and would panic the same way if it broke. The
1437        // empty arm made `kind_row`'s length a union of 0 and `mb_w`, which is not
1438        // provable, so every `kind_row[mb_x]` in the per-macroblock loop below
1439        // carried a bounds check that its siblings did not.
1440        let kind_row = &self.mb_kind[row0..][..mb_w];
1441        for mb_x in 0..mb_w {
1442            // Always pack: UNSET / Inter neighbours in this row and the next
1443            // read left/top MbPack. Kind stores MbBs directly (no i32 hop).
1444            // Decided BEFORE the record is built, because `pack_mb` uses it too:
1445            // a macroblock known uniform reads block 0 once and splats it rather
1446            // than gathering the same values sixteen times.
1447            let kind_here = MbKind::from_u8(kind_row[mb_x]);
1448            let known_uniform = matches!(
1449                kind_here,
1450                Some(MbKind::Skip | MbKind::InterUniform)
1451            ) || umot_row.get(mb_x).copied().unwrap_or(false);
1452            let Some(slot) = self.pk_cur.get_mut(mb_x) else {
1453                continue;
1454            };
1455            rusty_h264_common::deblock::pack_mb_into(
1456                &info,
1457                has1,
1458                mb_x,
1459                r,
1460                known_uniform,
1461                slot,
1462            );
1463            // UNIFORMITY, DECIDED ONCE, WHERE THE RECORD IS BUILT.
1464            //
1465            // Two kinds state the answer the `mb_uniform` kernel would spend six
1466            // motion planes computing: `Skip` is P_Skip (one reference, one
1467            // vector) and `InterUniform` is P_L0_16x16, a SINGLE partition. Both
1468            // are set only on P paths, where the List-1 planes sit at their
1469            // uniform defaults. On the tt corpus that covers ~20-23% of the
1470            // macroblocks reaching the derivation.
1471            //
1472            // Recording it on the RECORD rather than inside the derivation is
1473            // what makes a macroblock's NEIGHBOURS uniformity visible, which
1474            // collapses each macroblock edge's four motion tests to one.
1475            // SAME `slot`, not a second lookup -- and not `last_mut()`. While the
1476            // rows were `push`ed, the record just built WAS the last one; once
1477            // they are held at row length that is the row's final slot instead,
1478            // which silently wrote every macroblock's uniformity onto index
1479            // mb_w - 1 and left the rest at their default `false`. Byte-identity
1480            // could not see it, because `uniform` is a hint whose false direction
1481            // takes the long path to the same strengths -- it only turned the
1482            // edge collapse off. Bound once, used twice.
1483            slot.uniform = match kind_here {
1484                // Intra records are never read for motion: a neighbour whose
1485                // `inter` is false takes the constant strength-4 arm.
1486                Some(MbKind::Intra) => false,
1487                _ if known_uniform => {
1488                    rusty_h264_common::deblock::verify_uniform_hint_check(slot);
1489                    true
1490                }
1491                // Only what neither the kind nor the B paths answered asks the
1492                // kernel.
1493                _ => rusty_h264_common::deblock::mb_uniform(slot),
1494            };
1495            if stats {
1496                edcstat::bump(&edcstat::DBS_MB, 1);
1497            }
1498            // KIND ECONOMICS ON THE PACKED PATH: derive_mb_kind(Skip) does 9
1499            // fresh strided Blk::loads per MB, but pack_mb already ran for this
1500            // MB (the line above) — the packed `_` arm derives from those
1501            // records with no further gathers. A B_Skip kind experiment
1502            // measured kind-arm 1.8% SLOWER (z=-2.69) with +1.07M loads, so
1503            // Skip/InterUniform route to the packed arm; Intra keeps the kind
1504            // arm (pure constants, no loads). `RS_H264_KIND_LOADS=1` restores
1505            // the old routing for paired A/B.
1506            match kind_row.get(mb_x).copied().and_then(MbKind::from_u8) {
1507                Some(MbKind::Intra) => {
1508                    if stats {
1509                        edcstat::bump(&edcstat::DBS_INTRA, 1);
1510                    }
1511                    // WIN: intra strengths are a constant table keyed on availability;
1512                    // asking derive_mb_kind for them dragged its whole gathering body
1513                    // (Skip/InterUniform/Inter, 1654 instrs) into the decode binary,
1514                    // even though the only other arm that calls it is guarded by a
1515                    // build-time false knob.
1516                    bs_row[mb_x] =
1517                        rusty_h264_common::deblock::intra_mb_bs(mb_x, r, t8_row[mb_x]);
1518                }
1519                Some(k @ (MbKind::Skip | MbKind::InterUniform)) if kl => {
1520                    if stats {
1521                        edcstat::bump(&edcstat::DBS_KINDARM, 1);
1522                    }
1523                    let mut m = derive_mb_kind(&info, mb_x, r, k);
1524                    // This arm is behind a build-time-false knob, but it is a
1525                    // PRODUCER of stored strengths and the filter no longer
1526                    // re-tests t8 on the precomputed path -- so it has to encode
1527                    // the same thing every other producer does.
1528                    if t8_row[mb_x] {
1529                        m.v[1] = [0; 4];
1530                        m.v[3] = [0; 4];
1531                        m.h[1] = [0; 4];
1532                        m.h[3] = [0; 4];
1533                    }
1534                    bs_row[mb_x] = m;
1535                }
1536                _ => {
1537                    let Some(cur) = self.pk_cur.get(mb_x) else {
1538                        continue;
1539                    };
1540                    let left = if mb_x > 0 {
1541                        self.pk_cur.get(mb_x - 1)
1542                    } else {
1543                        None
1544                    };
1545                    let top = if r > 0 { self.pk_prev.get(mb_x) } else { None };
1546                    let mb_t8 = t8_row[mb_x];
1547                    // WIN: derive DIRECTLY into the 32-byte record this loop
1548                    // stores. The pair of `[[i32; 4]; 4]` scratch arrays that
1549                    // used to sit here were 128 bytes zeroed per macroblock, and
1550                    // every value written into them was a bS in 0..=4 that a
1551                    // narrowing pass below then copied, one lane at a time, into
1552                    // exactly this `MbBs`. The core is generic over the width now
1553                    // (`BsElem`), so the u8 form is the same derivation, not a
1554                    // second copy of it, and the i32 monomorphization stays out
1555                    // of the decode binary entirely.
1556                    // AND STRAIGHT INTO THE GRID SLOT, no 32-byte temporary.
1557                    // `bs_row` is this row of `bs_frame`, a disjoint field from
1558                    // the record rows `cur`/`left`/`top` come from, so the
1559                    // derivation can write where the value is kept. Same removal
1560                    // as the packed record's, one size down.
1561                    let m = &mut bs_row[mb_x];
1562                    *m = MbBs::default();
1563                    rusty_h264_common::deblock::census_note_packed();
1564                    let flat =
1565                        rusty_h264_common::deblock::derive_mb_records_bs(cur, left, top, m, mb_t8);
1566                    if stats {
1567                        edcstat::bump(&edcstat::DBS_PACKED, 1);
1568                    }
1569                    // MEASUREMENT ONLY: sizes the `kind_loads()` match guard that
1570                    // used to be a OnceLock deref here (now `k_kindloads`).
1571                    if stats
1572                        && matches!(
1573                            // WIN: kind_row is already this row of mb_kind, proven in range
1574                            // above, so [mb_x] needs no whole-frame bounds check. Identical
1575                            // when mb_kind is empty: kind_row is &[] and both answer None.
1576                            kind_row.get(mb_x).copied().and_then(MbKind::from_u8),
1577                            Some(MbKind::Skip | MbKind::InterUniform)
1578                        )
1579                    {
1580                        edcstat::bump(&edcstat::DBS_KINDGUARD, 1);
1581                    }
1582                    // FLAT-AWARE NARROWING. `derive_mb_records` returns early on a
1583                    // flat inter macroblock having written ONLY edge 0 of each
1584                    // orientation; edges 1..4 are still the caller's zero-init, so
1585                    // widening all 32 entries copies 24 known zeros onto 24 known
1586                    // zeros - after a `MbBs::default()` that zeroed them a third
1587                    // time. Census DBSDERIVE flat: 96.8% screen_text, 90.2%
1588                    // FourPeople, 82.1% akiyo - the dominant class.
1589                    // FLAT-AWARE NARROWING IS GONE, not merely cheaper: with the
1590                    // derivation writing `u8` in place there is nothing to narrow.
1591                    // A flat macroblock (census DBSDERIVE: 96.8% screen_text,
1592                    // 90.2% FourPeople, 82.1% akiyo -- the dominant class) leaves
1593                    // edges 1..4 at the zero `MbBs::default()` already wrote,
1594                    // which is what the widen-then-narrow pair used to spend 24
1595                    // copies of a known zero arriving at.
1596                    if flat && stats {
1597                        edcstat::bump(&edcstat::DBS_FLAT, 1);
1598                    }
1599                    debug_assert!(!flat || (m.v[1..] == [[0u8; 4]; 3] && m.h[1..] == [[0u8; 4]; 3]));
1600                    // MEASUREMENT ONLY: `on()` first, so the 32-byte compare
1601                    // never runs in a shipped decode (it is not otherwise needed
1602                    // here — the consumer in filter_frame_rows makes it).
1603                    if stats && m.v == [[0u8; 4]; 4] && m.h == [[0u8; 4]; 4] {
1604                        edcstat::bump(&edcstat::DBS_ALLZERO, 1);
1605                    }
1606                }
1607            }
1608        }
1609        // disable_deblocking_filter_idc == 2 (spec §7.4.3): a slice may forbid
1610        // filtering ITS macroblocks' edges against OTHER slices. bS = 0 on the
1611        // crossing MB edges kills exactly those filters; interior edges keep
1612        // their derived strengths. Guarded so single-slice / idc 0-1 pictures
1613        // pay one branch per row.
1614        if self.any_idc2 && self.slice_bounds.len() > 1 {
1615            edcstat::bump(&edcstat::DBS_IDC2ROW, 1);
1616            for mb_x in 0..mb_w {
1617                let slot = r * mb_w + mb_x;
1618                let si = self.slice_of(slot);
1619                if !self.slice_bounds.get(si).is_some_and(|b| b.1) {
1620                    continue;
1621                }
1622                if mb_x > 0 && self.slice_of(slot - 1) != si {
1623                    if let Some(b) = self.bs_frame.get_mut(slot) {
1624                        b.v[0] = [0; 4];
1625                    }
1626                }
1627                if r > 0 && self.slice_of(slot - mb_w) != si {
1628                    if let Some(b) = self.bs_frame.get_mut(slot) {
1629                        b.h[0] = [0; 4];
1630                    }
1631                }
1632            }
1633        }
1634    }
1635
1636    /// Decode-loop hook: called at each MB-loop head with the NEXT address to be
1637    /// decoded; derives AND FILTERS (R3) every fully-decoded row. Filtering a
1638    /// row here preserves the spec's raster per-MB filter order exactly (every
1639    /// MB the row's edges touch is already decoded; bottom-adjacent edges
1640    /// belong to the NEXT row's MBs, which filter later).
1641    ///
1642    /// Fast path: mid-row heads (`done <= bs_rows`) do no row work — return
1643    /// before the profiler scope (this hook is entered once per MB; scoping
1644    /// every call was measuring the timer, not the filter).
1645    /// row_hook with the caller's carried row — avoids the per-MB division.
1646    #[inline(always)]
1647    fn row_hook_at(&mut self, addr: usize, mby: usize) {
1648        if rowdb_on() {
1649            // ~44/45 of calls are mid-row: one compare, no atomic loads.
1650            if !rowhook_eager() && mby <= self.bs_rows {
1651                return;
1652            }
1653        }
1654        self.row_hook(addr, mby);
1655    }
1656
1657    /// `mby` is the row `addr` sits in. It is a PARAMETER rather than
1658    /// `addr / self.mb_w` because both callers already carry it: the slice loops
1659    /// track `(mbx, mby)` with a compare-and-wrap precisely so the per-macroblock
1660    /// div+mod never runs, and this hook -- entered PER MACROBLOCK on the
1661    /// non-rowdb arm -- was reconstructing it with an integer divide anyway.
1662    fn row_hook(&mut self, addr: usize, mby: usize) {
1663        debug_assert_eq!(mby, addr / self.mb_w, "row_hook: mby must be addr's row");
1664        // `RS_H264_ROWHOOK_EAGER=1` restores per-MB profiler scoping (A/B oracle).
1665        // READ THE KNOB FUNCTIONS, NOT THE CACHED FIELDS. The old note here said
1666        // the functions cost a OnceLock deref plus a relaxed atomic load per
1667        // entry; that was true BEFORE the routing round, and is not true now --
1668        // they are `return false` under cfg(not(knobs)), i.e. a constant. Going
1669        // through a struct FIELD erases that constant, because the field is a
1670        // runtime value, and every arm the knob guards then stays live in the
1671        // binary. That is what kept `derive_mb_kind` (1543 instrs the decoder
1672        // never runs) linked in until the same change was made for kind_loads.
1673        let eager = rowhook_eager();
1674        if !rowdb_on() {
1675            edcstat::bump(&edcstat::MBS, 1);
1676            let _rh = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecRowHook);
1677            self.edc_flush();
1678            let done = mby;
1679            if done > self.bs_rows {
1680                self.bs_rows = done;
1681                self.publish_progress();
1682            }
1683            return;
1684        }
1685        let done = mby;
1686        // ~44/45 of calls are mid-row: no derive/filter/handoff yet.
1687        if !eager && done <= self.bs_rows {
1688            return;
1689        }
1690        edcstat::bump(&edcstat::MBS, 1);
1691        let _rh = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecRowHook);
1692        if done <= self.bs_rows {
1693            return;
1694        }
1695        if self.edc_tx.is_some() {
1696            // E2: derivation stays here (it reads the syntax grids); filtering
1697            // is the worker's, fed the row's bs/qp/t8 snapshot. `flt_rows`
1698            // advances on the worker and comes home with the context.
1699            self.edc_giveback();
1700            while self.bs_rows < done {
1701                let r = self.bs_rows;
1702                self.derive_bs_row(r);
1703                self.bs_rows += 1;
1704                let base = r * self.mb_w;
1705                // ORDER: this row's pixel jobs must reach the worker BEFORE the
1706                // filter message for the same row.
1707                self.edc_flush_batch();
1708                edcstat::bump(&edcstat::ROWS, 1);
1709                edcstat::bump(
1710                    &edcstat::ROWBYTES,
1711                    (self.mb_w * (core::mem::size_of::<rusty_h264_common::deblock::MbBs>() + 2))
1712                        as u64,
1713                );
1714                let msg = EdcMsg::Row {
1715                    r,
1716                    bs: self.bs_frame[base..base + self.mb_w].to_vec(),
1717                    qp: self.mb_qp[base..base + self.mb_w].to_vec(),
1718                    t8: self.mb_t8x8[base..base + self.mb_w].to_vec(),
1719                };
1720                self.edc_post(msg);
1721            }
1722            return;
1723        }
1724        self.edc_flush();
1725        while self.bs_rows < done {
1726            let r = self.bs_rows;
1727            self.derive_bs_row(r);
1728            self.bs_rows += 1;
1729            // Row filtering requires deblock enabled on EVERY slice so far
1730            // (`db_ena` latches false once any slice disables it): a mixed
1731            // picture falls back to the picture-end tail so "latest slice
1732            // wins" semantics are preserved.
1733            if self.db_ena {
1734                self.save_bak(r);
1735                self.filter_row(r);
1736                self.flt_rows = r + 1;
1737            }
1738            self.publish_progress();
1739        }
1740    }
1741
1742    /// Saves the UNFILTERED bottom pixel rows of MB row `r` before filtering
1743    /// modifies them: the next row's intra prediction must read pre-deblock
1744    /// samples (spec §8.3), and filtering touches the bottom three rows while
1745    /// intra reads exactly the bottom ONE (+ the corner) — so one backup row
1746    /// per plane suffices, overwritten per row.
1747    fn save_bak(&mut self, r: usize) {
1748        let y0 = (r * 16 + 15) * self.cw;
1749        self.bak_y.copy_from_slice(&self.rec_y[y0..y0 + self.cw]);
1750        let c0 = (r * 8 + 7) * self.ccw;
1751        self.bak_u.copy_from_slice(&self.rec_u[c0..c0 + self.ccw]);
1752        self.bak_v.copy_from_slice(&self.rec_v[c0..c0 + self.ccw]);
1753    }
1754
1755    /// Filters one MB row against the stored strengths, using the CURRENT
1756    /// slice's alpha/beta offsets (single-offset streams — the whole corpus —
1757    /// are bit-identical to the picture-end call; the plan's risk register
1758    /// documents the multi-offset divergence).
1759    fn filter_row(&mut self, r: usize) {
1760        let info = rusty_h264_common::deblock::BlockInfo {
1761            inter: &self.inter_y,
1762            // Same source `derive_bs_row` used (see the T8 GATE there). This
1763            // path passes a non-empty `bs`, so it takes the PRECOMPUTED arm and
1764            // never reads `nnz` at all; kept in step so the two cannot diverge.
1765            nnz: if self.any_t8 {
1766                &self.nnz_dbr
1767            } else {
1768                &self.nnz_y
1769            },
1770            mv: &self.mv_y,
1771            ref_id: &self.ref_idx_y,
1772            mv1: &self.mv1,
1773            ref_id1: &self.ref_idx1,
1774            w4: self.mb_w * 4,
1775            t8x8: &self.mb_t8x8,
1776            bs: &self.bs_frame,
1777            poc0: &[],
1778            poc1: &[],
1779            kind: &self.mb_kind,
1780        };
1781        rusty_h264_common::deblock::filter_frame_rows_pre(
1782            &mut self.rec_y,
1783            &mut self.rec_u,
1784            &mut self.rec_v,
1785            self.mb_w,
1786            self.mb_h,
1787            r..r + 1,
1788            &self.mb_qp,
1789            self.chroma_qp_offset,
1790            self.db_oa,
1791            self.db_ob,
1792            &info,
1793        );
1794    }
1795
1796    /// Top-neighbour LUMA pixel for intra prediction: reads the unfiltered
1797    /// backup row when the row above has already been deblock-filtered by the
1798    /// row-interleave (flt_rows gates it; 0 when the interleave is off, so
1799    /// this compiles to the plain read on the fallback path).
1800    #[inline]
1801    fn top_y_px(&self, py: usize, x: usize) -> u8 {
1802        // 128 is the spec's value for an unavailable sample (1 << (BitDepth - 1),
1803        // §8.3.1.2), so the fallback is the one the prediction rules already use
1804        // — and unreachable anyway, since callers gate on availability first.
1805        if py % 16 == 0 && self.flt_rows * 16 >= py {
1806            self.bak_y.get(x).copied().unwrap_or(128)
1807        } else {
1808            self.rec_y
1809                .get((py - 1) * self.cw + x)
1810                .copied()
1811                .unwrap_or(128)
1812        }
1813    }
1814
1815    /// Slice form of [`Self::top_y_px`] for the contiguous 16-wide I16 gather.
1816    #[inline]
1817    fn top_y_row(&self, py: usize, x: usize, n: usize) -> &[u8] {
1818        if py % 16 == 0 && self.flt_rows * 16 >= py {
1819            &self.bak_y[x..x + n]
1820        } else {
1821            &self.rec_y[(py - 1) * self.cw + x..][..n]
1822        }
1823    }
1824
1825    /// Top-neighbour CHROMA pixel (plane `c`: 0 = U, 1 = V).
1826    #[inline]
1827    fn top_c_px(&self, c: usize, cy: usize, x: usize) -> u8 {
1828        // 128 = the spec's unavailable sample, as in `top_y_px`.
1829        if cy % 8 == 0 && self.flt_rows * 8 >= cy {
1830            let bak = if c == 0 { &self.bak_u } else { &self.bak_v };
1831            bak.get(x).copied().unwrap_or(128)
1832        } else {
1833            let rec = if c == 0 { &self.rec_u } else { &self.rec_v };
1834            rec.get((cy - 1) * self.ccw + x).copied().unwrap_or(128)
1835        }
1836    }
1837
1838    /// Slice form of [`Self::top_c_px`] for the 8-wide chroma gather.
1839    #[inline]
1840    fn top_c_row(&self, c: usize, cy: usize, x: usize, n: usize) -> &[u8] {
1841        if cy % 8 == 0 && self.flt_rows * 8 >= cy {
1842            if c == 0 {
1843                &self.bak_u[x..x + n]
1844            } else {
1845                &self.bak_v[x..x + n]
1846            }
1847        } else {
1848            let rec = if c == 0 { &self.rec_u } else { &self.rec_v };
1849            &rec[(cy - 1) * self.ccw + x..][..n]
1850        }
1851    }
1852
1853    /// Snapshots the (deblocked) reconstruction as a reference picture, drawing
1854    /// its padded-plane allocations from `pool` (recycled
1855    /// planes of evicted DPB frames — see `Decoder::reclaim_retired`). ~1.9 MB of
1856    /// fresh allocation per reference picture otherwise (`dpb-clone` stage, 3-4%
1857    /// of decode, mostly first-touch page faults).
1858    pub fn as_reference_pooled(
1859        &self,
1860        pool: &mut Vec<Vec<u8>>,
1861        mv_pool: &mut Vec<Vec<(i16, i16)>>,
1862        ref_pool: &mut Vec<Vec<i8>>,
1863    ) -> crate::RefFrame {
1864        // MV CAPTURE (`RFF_MV_DUMP=1`) — lets a harness read the motion field any
1865        // conformant H.264 stream carries, including x264's, using this decoder as
1866        // the parser. Diagnostic only; inert unless the env var is set.
1867        #[cfg(feature = "std")]
1868        if mv_dump_on() {
1869            MV_DUMP.lock_pf().push(MvField {
1870                mb_w: self.mb_w,
1871                mb_h: self.mb_h,
1872                // MvField is a public diagnostic type a harness reads, so it
1873                // keeps its i32 shape; widened here, under RFF_MV_DUMP only.
1874                mv: self.mv_y.iter().map(|&(x, y)| (i32::from(x), i32::from(y))).collect(),
1875                // Widened rather than narrowing MvField: it is a PUBLIC diagnostic
1876                // type a harness reads, and this runs only under RFF_MV_DUMP.
1877                ref_idx: self.ref_idx_y.iter().copied().map(i32::from).collect(),
1878                inter: self.inter_y.clone(),
1879            });
1880        }
1881
1882        // The per-block motion (mv/ref_idx/ref_poc) is read ONLY by B temporal/spatial
1883        // direct (`col.mv/ref_idx/ref_poc`, guarded on `w4 != 0` + `idx < len`). On
1884        // Baseline/Constrained-Baseline streams (no B) it's pure waste — skip the two
1885        // grid clones + the per-block ref_poc resolve/alloc. `w4 = 0` makes the B
1886        // readers no-op even on malformed input.
1887        // PRICED: the four grid clones plus the ref_poc expansion, per REFERENCE
1888        // picture. `refill` (the pool-clear counter) never saw any of it -- this
1889        // is frame-scale copying that happens outside the per-picture re-arm.
1890        cpystat::note(
1891            &cpystat::REF_BUILD,
1892            if self.b_possible {
1893                // `size_of_val` on the SLICE, never `len() * size_of::<T>()`
1894                // with the width written out: this counter shipped hardcoding
1895                // `(i32, i32)` and would have reported double the moment the
1896                // grids narrowed to `(i16, i16)`. The compiler now supplies the
1897                // width, so the instrument cannot drift from the code it prices.
1898                core::mem::size_of_val(&self.mv_y[..])
1899                    + core::mem::size_of_val(&self.ref_idx_y[..])
1900                    + core::mem::size_of_val(&self.mv1[..])
1901                    + core::mem::size_of_val(&self.ref_idx1[..])
1902            } else {
1903                0
1904            },
1905        );
1906        // RECYCLE, DON'T CLONE. The snapshot has to be a copy -- the live grid
1907        // is refilled next picture -- but `clone()` also ALLOCATES, and the
1908        // previous reference's grids come back through `reclaim_retired`. Same
1909        // mechanism as the padded planes, one field over.
1910        let mut take_mv = |src: &[(i16, i16)]| -> Vec<(i16, i16)> {
1911            let mut v = mv_pool.pop().unwrap_or_default();
1912            v.clear();
1913            v.extend_from_slice(src);
1914            v
1915        };
1916        let mut take_ref = |src: &[i8]| -> Vec<i8> {
1917            let mut v = ref_pool.pop().unwrap_or_default();
1918            v.clear();
1919            v.extend_from_slice(src);
1920            v
1921        };
1922        let (mv, ref_idx, mv1, ref_idx1, poc_lut, w4) = if self.b_possible {
1923            (
1924                take_mv(&self.mv_y),
1925                take_ref(&self.ref_idx_y),
1926                take_mv(&self.mv1),
1927                take_ref(&self.ref_idx1),
1928                // CARRY THE LUT, NOT ITS EXPANSION. This used to build the table
1929                // and then immediately spread it over all 57,600 blocks with a
1930                // `.collect()` -- a 230 KB Vec, per reference picture, cloned
1931                // again with the frame, every element of which was
1932                // `poc_lut[ref_idx[i]]`. `ref_idx` travels in the same struct,
1933                // and the one consumer (temporal direct's MapColToList0) reads a
1934                // single entry, so it does the lookup itself now. 230 KB -> 128 B.
1935                {
1936                    let mut poc_lut = [i32::MIN; 32];
1937                    for (i, f) in self.refs.iter().take(32).enumerate() {
1938                        poc_lut[i & 31] = f.pic_poc();
1939                    }
1940                    poc_lut
1941                },
1942                self.mb_w * 4,
1943            )
1944        } else {
1945            (
1946                Vec::new(),
1947                Vec::new(),
1948                Vec::new(),
1949                Vec::new(),
1950                [i32::MIN; 32],
1951                0,
1952            )
1953        };
1954        // Pop an exact-size recycled buffer per plane; a miss falls back to a
1955        // fresh allocation inside `pad_plane_into`.
1956        let mut take = |len: usize| -> Vec<u8> {
1957            // THE BIGGEST COPY IN THE DECODER AND IT WAS NOT ON THE BOOKS. A
1958            // padded 720p luma plane is 1,053,696 B and the two chroma planes
1959            // 263,424 B each -- per REFERENCE picture, i.e. a larger unit again
1960            // than the per-picture re-arm `refill` prices. `REF_BUILD`'s own doc
1961            // said it excluded these; nothing counted them.
1962            cpystat::note(&cpystat::PLANE_PAD, len);
1963            match pool.iter().position(|v| v.len() == len) {
1964                Some(i) => pool.swap_remove(i),
1965                None => {
1966                    // A miss is not merely an allocation. `pad_plane_into`
1967                    // resizes the empty Vec, so the whole plane is ZEROED, and
1968                    // its loop then writes every byte of every row -- the zero
1969                    // fill is pure waste, one padded plane's worth of it.
1970                    cpystat::note(&cpystat::PLANE_MISS, len);
1971                    Vec::new()
1972                }
1973            }
1974        };
1975        let (lpw, lph) = (self.cw + 2 * crate::LPAD, self.ch + 2 * crate::LPAD);
1976        let (cpw, cph) = (self.ccw + 2 * crate::CPAD, self.ch / 2 + 2 * crate::CPAD);
1977        crate::RefFrame {
1978            // Pad once here (ExpandPicture) instead of extracting a clamped tile
1979            // on every MC call — same copy class as the old plane clone.
1980            py: rusty_h264_common::inter::pad_plane_into(
1981                take(lpw * lph),
1982                &self.rec_y,
1983                self.cw,
1984                self.ch,
1985                crate::LPAD,
1986            ),
1987            pu: rusty_h264_common::inter::pad_plane_into(
1988                take(cpw * cph),
1989                &self.rec_u,
1990                self.ccw,
1991                self.ch / 2,
1992                crate::CPAD,
1993            ),
1994            pv: rusty_h264_common::inter::pad_plane_into(
1995                take(cpw * cph),
1996                &self.rec_v,
1997                self.ccw,
1998                self.ch / 2,
1999                crate::CPAD,
2000            ),
2001            cw: self.cw,
2002            ch: self.ch,
2003            ready_rows: core::sync::atomic::AtomicUsize::new(0),
2004            live: None,
2005            frozen: crate::sync::Once::new(),
2006            frame_num: 0, // set by the caller (decode_slice knows frame_num)
2007            poc: 0,       // set by the caller
2008            mv,
2009            ref_idx,
2010            mv1,
2011            ref_idx1,
2012            poc_lut,
2013            w4,
2014            long_term: false,
2015            long_term_idx: 0,
2016        }
2017    }
2018
2019    fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
2020        let w4 = self.mb_w * 4;
2021        let top_unavail = mb_y == 0 || !self.nbr_in_slice(mb_x, mb_y - 1);
2022        let left_unavail = mb_x == 0 || !self.nbr_in_slice(mb_x - 1, mb_y);
2023        // The four TOP neighbours are one contiguous run of the nnz grid; only
2024        // the left column is strided. Hoisting the uniform `top_unavail` test out
2025        // of the loop turns four checked grid loads into one slice copy.
2026        if top_unavail {
2027            self.nnz_l_cache[1..5].fill(0x80);
2028        } else {
2029            let src = &self.nnz_y[(mb_y * 4 - 1) * w4 + mb_x * 4..][..4];
2030            self.nnz_l_cache[1..5].copy_from_slice(src);
2031        }
2032        for lby in 0..4 {
2033            self.nnz_l_cache[(lby + 1) * 5] = if left_unavail {
2034                0x80
2035            } else {
2036                self.nnz_y
2037                    .get((mb_y * 4 + lby) * w4 + (mb_x * 4 - 1))
2038                    .copied()
2039                    .unwrap_or(0)
2040            };
2041        }
2042    }
2043    #[inline]
2044    fn nc_pred(&self, lbx: usize, lby: usize) -> i32 {
2045        // MASKED, and it pays twice over: this helper and `nnz_cache_set` are
2046        // inlined into the intra path, the inter path AND both slice loops, so
2047        // the two unprovable indexes were replicated at every call site. The
2048        // cache is a 5x5 grid in a `[u8; 25]` and every caller passes a 4x4 block
2049        // coordinate (`LUMA_4X4_SCAN_XY`, `b8*2 + s`, or a literal), so `& 3` is
2050        // a no-op that puts the worst case at 4 * 5 + 4 = 24.
2051        let (lbx, lby) = (lbx & 3, lby & 3);
2052        let left = self.nnz_l_cache[(lby + 1) * 5 + lbx] as i32;
2053        let top = self.nnz_l_cache[lby * 5 + (lbx + 1)] as i32;
2054        let r = left + top;
2055        if r < 0x80 {
2056            (r + 1) >> 1
2057        } else {
2058            r & 0x7f
2059        }
2060    }
2061    #[inline]
2062    fn nnz_cache_set(&mut self, lbx: usize, lby: usize, total: u8) {
2063        self.nnz_l_cache[((lby & 3) + 1) * 5 + ((lbx & 3) + 1)] = total;
2064    }
2065    fn chroma_cache_load(&mut self, mb_x: usize, mb_y: usize) {
2066        let w2 = self.mb_w * 2;
2067        let top_unavail = mb_y == 0 || !self.nbr_in_slice(mb_x, mb_y - 1);
2068        let left_unavail = mb_x == 0 || !self.nbr_in_slice(mb_x - 1, mb_y);
2069        for c in 0..2 {
2070            if top_unavail {
2071                self.nnz_c_cache[c][1..3].fill(0x80);
2072            } else {
2073                let src = &self.nnz_c[c][(mb_y * 2 - 1) * w2 + mb_x * 2..][..2];
2074                self.nnz_c_cache[c][1..3].copy_from_slice(src);
2075            }
2076            for by in 0..2 {
2077                self.nnz_c_cache[c][(by + 1) * 3] = if left_unavail {
2078                    0x80
2079                } else {
2080                    self.nnz_c[c & 1]
2081                        .get((mb_y * 2 + by) * w2 + (mb_x * 2 - 1))
2082                        .copied()
2083                        .unwrap_or(0)
2084                };
2085            }
2086        }
2087    }
2088    #[inline]
2089    fn chroma_nc_pred(&self, c: usize, bx: usize, by: usize) -> i32 {
2090        // Same shape as `nc_pred`, one size down: a 3x3 grid in `[u8; 9]`, two
2091        // planes, and every caller passes 0..1 for all three coordinates.
2092        let (c, bx, by) = (c & 1, bx & 1, by & 1);
2093        let left = self.nnz_c_cache[c][(by + 1) * 3 + bx] as i32;
2094        let top = self.nnz_c_cache[c][by * 3 + (bx + 1)] as i32;
2095        let r = left + top;
2096        if r < 0x80 {
2097            (r + 1) >> 1
2098        } else {
2099            r & 0x7f
2100        }
2101    }
2102    #[inline]
2103    fn chroma_nnz_cache_set(&mut self, c: usize, bx: usize, by: usize, total: u8) {
2104        self.nnz_c_cache[c & 1][((by & 1) + 1) * 3 + ((bx & 1) + 1)] = total;
2105    }
2106
2107    /// Decodes one slice's macroblocks (raster order) starting at `first_mb`,
2108    /// until `more_rbsp_data()` is exhausted or the picture is full. Returns the
2109    /// next macroblock address (= total when the picture is complete). In a
2110    /// P-slice each macroblock is preceded by `mb_skip_run`.
2111    /// CABAC slice-data decode (docs/cabac-decode-plan.md), brought up brick by brick
2112    /// against the instrumented openh264 oracle. Phase 1: verify engine init; the
2113    /// syntax layer (Phase 2+) is WIP.
2114    pub fn decode_slice_data_cabac(
2115        &mut self,
2116        rbsp: &[u8],
2117        start_byte: usize,
2118        slice_qp: u8,
2119        cabac_init_idc: u32,
2120        is_i: bool,
2121        is_p: bool,
2122        first_mb: usize,
2123    ) -> Result<usize, MbError> {
2124        // E2: overlap parse (this thread) with pixel reconstruction (a scoped
2125        // worker owning the planes) for P slices. I slices and B slices keep
2126        // the inline path (their pixel coupling is per-MB); the ownership
2127        // ping-pong around intra-in-P macroblocks is `edc_intra_sync`.
2128        let eligible = edc_on() && rowdb_on() && !is_i && (is_p || self.is_b);
2129        let threaded = eligible && edc_spawn_worker(self.mb_w, self.mb_h, self.bits_per_mb, true);
2130        edcstat::bump(&edcstat::DISPATCH_ON, threaded as u64);
2131        edcstat::bump(&edcstat::DISPATCH_SEEN, eligible as u64);
2132        if !threaded {
2133            let r = self.decode_slice_cabac_inner(
2134                rbsp,
2135                start_byte,
2136                slice_qp,
2137                cabac_init_idc,
2138                is_i,
2139                is_p,
2140                first_mb,
2141            );
2142            self.note_slice_density(rbsp.len().saturating_sub(start_byte), first_mb, &r);
2143            return r;
2144        }
2145        #[cfg(feature = "std")]
2146        {
2147            let ctx = self.edc_take_ctx();
2148            // D7 PROBE: is the CPU overhead PAYLOAD (alloc/copy per job) or
2149            // SYNCHRONISATION (blocking on a full queue, park/unpark)? The bound
2150            // separates them: raising it removes send-blocking without changing a
2151            // single byte copied. `RS_H264_EDC_BOUND` sweeps it.
2152            let (tx, rx) = crate::sync::mpsc::sync_channel::<EdcMsg>(edc_bound());
2153            let (ctx_tx, ctx_rx) = crate::sync::mpsc::channel::<PixelCtx>();
2154            let (back_tx, back_rx) = crate::sync::mpsc::channel::<PixelCtx>();
2155            let (res, ctx, panicked) = std::thread::scope(|sc| {
2156                let h = sc.spawn(move || edc_worker(ctx, rx, ctx_tx, back_rx));
2157                self.edc_tx = Some(tx);
2158                self.edc_ctx_rx = Some(ctx_rx);
2159                self.edc_back_tx = Some(back_tx);
2160                // UNWIND SAFETY (found by the fuzzer as a HANG, not a failure): a
2161                // panic inside the parse loop would skip the cleanup below — but
2162                // the sender lives in `self`, which outlives the unwind, so the
2163                // channel would never close, the worker would never exit, and the
2164                // scope's join would block forever, converting a diagnosable panic
2165                // into a silent deadlock under `catch_unwind` harnesses. Catch,
2166                // clean up, join, restore the planes, THEN resume the panic.
2167                let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2168                    self.decode_slice_cabac_inner(
2169                        rbsp,
2170                        start_byte,
2171                        slice_qp,
2172                        cabac_init_idc,
2173                        is_i,
2174                        is_p,
2175                        first_mb,
2176                    )
2177                }));
2178                self.edc_flush_batch(); // ORDER: no job may outlive the channel
2179                self.edc_giveback(); // if an intra macroblock left us holding
2180                self.edc_tx = None; // closes the channel -> worker drains + returns
2181                self.edc_ctx_rx = None;
2182                self.edc_back_tx = None;
2183                match (r, h.join()) {
2184                    // The worker yields `Option<PixelCtx>`: `None` means the
2185                    // parse side died holding the context, so there is nothing
2186                    // to restore.
2187                    (Ok(res), Ok(ctx)) => (res, ctx, None),
2188                    (Err(p), Ok(ctx)) => (Err(MbError::Truncated), ctx, Some(p)),
2189                    (Ok(_), Err(p)) | (Err(_), Err(p)) => (Err(MbError::Truncated), None, Some(p)),
2190                }
2191            });
2192            if let Some(ctx) = ctx {
2193                self.edc_restore_ctx(ctx);
2194            }
2195            if let Some(p) = panicked {
2196                std::panic::resume_unwind(p);
2197            }
2198            self.note_slice_density(rbsp.len().saturating_sub(start_byte), first_mb, &res);
2199            res
2200        }
2201        #[cfg(not(feature = "std"))]
2202        {
2203            unreachable!("the EDC worker needs std; edc_spawn_worker is false without it")
2204        }
2205    }
2206
2207    /// Feed the D12 dispatch its density signal from a slice just decoded.
2208    /// Exponentially smoothed so one atypical slice cannot flip the arm, and
2209    /// only ever read on the NEXT slice — the current one is already committed.
2210    fn note_slice_density(&mut self, bytes: usize, first_mb: usize, r: &Result<usize, MbError>) {
2211        let Ok(end) = r else { return };
2212        let mbs = end.saturating_sub(first_mb);
2213        if mbs == 0 {
2214            return;
2215        }
2216        let bpm = (bytes * 8) as f64 / mbs as f64;
2217        self.bits_per_mb = if self.bits_per_mb == 0.0 {
2218            bpm
2219        } else {
2220            0.75 * self.bits_per_mb + 0.25 * bpm
2221        };
2222    }
2223
2224    fn decode_slice_cabac_inner(
2225        &mut self,
2226        rbsp: &[u8],
2227        start_byte: usize,
2228        slice_qp: u8,
2229        cabac_init_idc: u32,
2230        is_i: bool,
2231        is_p: bool,
2232        first_mb: usize,
2233    ) -> Result<usize, MbError> {
2234        self.edc_active = edc_on();
2235        let mut cab =
2236            crate::cabac::Cabac::new(rbsp, start_byte, slice_qp as i32, cabac_init_idc, is_i);
2237        let (range, _offset) = cab.dbg_state();
2238        // The crate already HAS a `cabac-trace` feature for this; the bare knob()
2239        // was env::var plus a String allocation per SLICE, inside the hottest
2240        // function in the decoder, and it kept every trace arm compiled in.
2241        #[cfg(not(feature = "cabac-trace"))]
2242        let trace = false;
2243        #[cfg(feature = "cabac-trace")]
2244        let trace = rusty_h264_common::knob("RH_CABAC_TRACE").is_some();
2245        debug_assert_eq!(range, 510, "CABAC init range must be 510");
2246
2247        let mbw = self.mb_w;
2248        let total = self.mb_w * self.mb_h;
2249        // Per-MB neighbour state (single-slice assumption: avail == in-bounds).
2250        // SCOPED: zero-initialised allocations sized by MB count, once per slice.
2251        // D13: B-only grids (List-1 ref/mvd + direct flags) are ~292 KB at 720p and
2252        // are ONLY written on the B branch below — allocating them on every P/I
2253        // slice was the same fresh-page class GridPool fixed for the frame grids.
2254        // `RS_H264_FAT_SLICE=1` restores the always-alloc path for A/B.
2255        let _alloc_g =
2256            rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecSliceAlloc);
2257        // POOLED (was fresh `vec![..]` per slice): refill reuses the pooled
2258        // allocation with contents identical to a fresh vec, so the body below
2259        // is untouched. Taken out of `self` here; put back at the normal exit.
2260        let mut cat = refill(core::mem::take(&mut self.sc_cat), total, 255u8); // 0=I4x4, 2=I16, 255=unavailable
2261        let mut mb_cbp = refill(core::mem::take(&mut self.sc_cbp), total, 0u8);
2262        let mut cmode = refill(core::mem::take(&mut self.sc_cmode), total, -1i8); // chroma pred mode
2263        let mut mb_nzc = refill(core::mem::take(&mut self.sc_nzc), total, [0u8; 24]); // 16 luma raster + 8 chroma
2264        let mut cbf_dc = refill(core::mem::take(&mut self.sc_cbfdc), total, 0u16);
2265        let mut mb_skip = refill(core::mem::take(&mut self.sc_skip), total, false);
2266        let mut mb_ref = refill(core::mem::take(&mut self.sc_ref), total, [-1i8; 16]); // per-4×4-block List-0 ref (-1 = intra)
2267        let mut mb_mvd = refill(core::mem::take(&mut self.sc_mvd), total, [[0u8; 2]; 16]); // per-block mvd (for mvd ctxInc)
2268                                                                                            // D13: B-only grids (~292 KB @720p) only on B slices (or FAT_SLICE A/B).
2269        let want_b_grids = self.is_b || fat_slice_on();
2270        let mut mb_ref1 = if want_b_grids {
2271            refill(core::mem::take(&mut self.sc_ref1), total, [-1i8; 16])
2272        } else {
2273            Vec::new()
2274        };
2275        let mut mb_mvd1 = if want_b_grids {
2276            refill(core::mem::take(&mut self.sc_mvd1), total, [[0u8; 2]; 16])
2277        } else {
2278            Vec::new()
2279        };
2280        let mut mb_direct = if want_b_grids {
2281            refill(core::mem::take(&mut self.sc_direct), total, false)
2282        } else {
2283            Vec::new()
2284        };
2285        drop(_alloc_g);
2286        // Multi-slice availability (spec §6.4.x): a macroblock before this
2287        // slice's first_mb is NOT available — for pixel-domain prediction
2288        // (`nbr_in_slice`, like the CAVLC twin sets) AND for every CABAC ctx
2289        // neighbour below. The per-slice ctx arrays' defaults are not the
2290        // "unavailable" value (cat 255 reads as available-I16, mb_cbp 0 as
2291        // all-zero-cbp, mb_t8x8 is a frame grid…), so the `left`/`top`
2292        // options themselves are gated on slice membership — one gate that
2293        // every downstream ctxIdxInc read inherits. Confirmed against ffmpeg
2294        // on an x264 `--slices 4` stream, which desynced without this.
2295        self.slice_first_mb = first_mb;
2296        self.slice_bounds.push((first_mb, self.cur_idc2));
2297        self.any_idc2 |= self.cur_idc2;
2298        let mut last_delta_qp = 0i32;
2299        let mut addr = first_mb;
2300
2301        let _mbloop_g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbLoop);
2302        // A new slice's first MBs may read grids a previous slice's deferred
2303        // spans still owe (P-after-B in one picture included).
2304        self.span_flush();
2305        // `mbw` is `pic_width_in_mbs_minus1 + 1` from the SPS. It cannot be 0 in
2306        // a conformant stream, but it is a runtime value, so this div+rem pair
2307        // carried a divide-by-zero panic reachable from a MALFORMED header --
2308        // on the untrusted-input path, at slice entry. `.max(1)` retires both
2309        // checks and cannot change a conformant decode.
2310        let mbw_nz = mbw.max(1);
2311        let (mut mbx, mut mby) = (addr % mbw_nz, addr / mbw_nz);
2312        // Slice-invariant B properties, formerly re-derived per macroblock.
2313        if self.is_b {
2314            self.edc_flush(); // drain anything a preceding P slice queued
2315        }
2316        let b_records = self.edc_tx.is_some();
2317        let b_refs_ok = !self.refs.is_empty() && !self.refs1.is_empty();
2318        // Loop-invariant: the lowest `addr` whose top neighbour is in this slice.
2319        let top_lim = first_mb + mbw;
2320        // Reused across macroblocks: MC overwrites every byte it reads back (all
2321        // B layouts cover the full macroblock), so the per-MB 384-byte zero-init
2322        // these used to pay was dead. Byte-identity is the guard.
2323        let mut pred_y = [0u8; 256];
2324        let mut c_pred = [[0u8; 64]; 2];
2325        // The loop wraps mbx at entry; bias so the first iteration is exact.
2326        // BOUND the entropy-coded loop (fuzzer-surfaced): a mutated stream can
2327        // never produce decode_terminate and the engine zero-fills forever. The
2328        // bound needs checking ONCE at entry — every in-loop path that advances
2329        // `addr` already breaks on `addr >= total` before continuing.
2330        if addr >= total {
2331            return Err(MbError::Truncated);
2332        }
2333        // BIND THE LENGTH FOR THE LOOP. Every grid below is `refill(.., total, ..)`
2334        // so its length is EXACTLY `total`, and the loop is entered only with
2335        // `addr < total` (checked above, re-asserted each turn) — but nothing
2336        // related the two, so every `[addr]` carried a check. Twenty-eight of
2337        // those became `get_mut` in the previous pass and cost ~2% on CABAC
2338        // content (crowd_run-main 1.021x, z=+2.11): a branch per write on the
2339        // decoder's hottest loop. Reborrowing at the literal `total` makes
2340        // `addr < total` and `addr < len` the SAME fact — no check, no branch.
2341        // The borrows end with this block, before the grids go back to the pool.
2342        // `mb_direct`/`mb_ref1`/`mb_mvd1` are deliberately NOT bound here: they
2343        // are `Vec::new()` on non-B slices, so slicing them to `total` would be
2344        // the very panic this campaign is removing.
2345        {
2346            let cat = &mut cat[..total];
2347            let mb_cbp = &mut mb_cbp[..total];
2348            let cmode = &mut cmode[..total];
2349            let mb_nzc = &mut mb_nzc[..total];
2350            let cbf_dc = &mut cbf_dc[..total];
2351            let mb_skip = &mut mb_skip[..total];
2352            let mb_ref = &mut mb_ref[..total];
2353            let mb_mvd = &mut mb_mvd[..total];
2354            loop {
2355                // A REAL check, not `debug_assert` — which compiles OUT in release,
2356                // so nothing carried `addr < total` across the loop's back-edge and
2357                // the reborrows above folded nothing on their own. This cannot fire
2358                // (every path that advances `addr` already breaks on `addr >= total`,
2359                // and entry is guarded above), but stating it once per macroblock
2360                // replaces FIFTEEN per-write branches with one that never taken.
2361                if addr >= total {
2362                    break;
2363                }
2364                // Carried coordinates: one compare-and-wrap replaces the per-MB
2365                // div+mod pair (and row_hook's own division).
2366                if mbx == mbw {
2367                    mbx = 0;
2368                    mby += 1;
2369                }
2370                self.row_hook_at(addr, mby);
2371                self.wait_refs_for_mb(mby);
2372                let left = (mbx > 0 && addr > first_mb).then(|| addr - 1);
2373                let top = (mby > 0 && addr >= top_lim).then(|| addr - mbw);
2374
2375                // Brick 3.1/3.2: P-slice mb_skip_flag, then mb_type (P mb_type is neighbour-
2376                // independent; intra sub-types map to the I dispatch below).
2377                
2378                let mb_type = if is_p {
2379                    // Direct bool arithmetic — no Option chain on the hot path.
2380                    let sctx = 11
2381                        + (left.is_some() && !mb_skip.get(addr - 1).copied().unwrap_or(true))
2382                            as usize
2383                        + (top.is_some()
2384                            && !mb_skip.get(addr.wrapping_sub(mbw)).copied().unwrap_or(true))
2385                            as usize;
2386                    // ONE engine view carries mb_skip_flag, the terminate bin of a skipped
2387                    // macroblock, and mb_type of a coded one (was 2-3 view/commit round
2388                    // trips of 8 memory ops each per macroblock).
2389                    let (data, mut e, ctx) = cab.view();
2390                    if e.decode_decision(data, ctx, sctx) != 0 {
2391                        if let Some(p) = mb_skip.get_mut(addr) {
2392                            *p = true;
2393                        }
2394                        last_delta_qp = 0; // skip codes no mb_qp_delta → delta ctxInc resets
2395                                           // P_Skip recon reuses the entropy-free CAVLC primitive verbatim: it
2396                                           // takes no bit-reader (skip has no coded syntax past the flag), just
2397                                           // predicts the skip MV, motion-compensates, and commits the grid.
2398                        self.decode_p_skip(mbx, mby)?;
2399                        if let Some(p) = self.mb_qp.get_mut(addr) {
2400                            *p = self.cur_qp;
2401                        } // skip inherits QPy
2402                        let eos = e.decode_terminate(data);
2403                        cab.commit(e);
2404                        addr += 1;
2405                        mbx += 1;
2406                        if eos || addr >= total {
2407                            break;
2408                        }
2409                        continue;
2410                    }
2411                    let mbt = mb_type_p_eng(&mut e, data, ctx);
2412                    cab.commit(e);
2413                    // NON-SKIP P MB: the inter arms below predict MVs from the
2414                    // grids (mv_neighbors_block) and the intra arm gathers — flush
2415                    // the deferred P span (B span cannot be pending in a P slice,
2416                    // but span_flush is one Option check each).
2417                    self.span_flush();
2418                    if mbt <= 3 {
2419                        let _gb =
2420                            rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbP);
2421                        // noSubMbPartSizeLessThan8x8Flag (spec 7.3.5): P_8x8 permits the
2422                        // 8x8 transform only when every sub-partition is itself 8x8.
2423                        let mut allow8 = true;
2424                        // Inter MB (Bricks 3.3/3.4/3.5). 1-ref stream → ref_idx not coded (ref=0).
2425                        // Build the 30-entry mvd/ref neighbour cache (openh264 WelsFillCacheInterCabac).
2426                        let mut mvdc = [[0i16; 2]; 30];
2427                        let mut refc = [-1i8; 30];
2428                        // Index the neighbour's RECORD once, then read within it.
2429                        // `mb_ref[l][bi]` is two bounds checks - one on the Vec, one
2430                        // on the array - repeated for each of the four entries and
2431                        // again for `mb_mvd`, i.e. sixteen checks per macroblock for
2432                        // four neighbours. Binding the record hoists the Vec check
2433                        // out; the inner index is a literal and folds.
2434                        if let Some(l) = left {
2435                            let (Some(lr), Some(lm)) = (mb_ref.get(l), mb_mvd.get(l)) else {
2436                                return Err(MbError::Truncated);
2437                            };
2438                            for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
2439                                refc[ci] = lr[bi];
2440                                mvdc[ci] = [lm[bi][0] as i16, lm[bi][1] as i16];
2441                            }
2442                        }
2443                        if let Some(t) = top {
2444                            let (Some(tr), Some(tm)) = (mb_ref.get(t), mb_mvd.get(t)) else {
2445                                return Err(MbError::Truncated);
2446                            };
2447                            for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
2448                                refc[ci] = tr[bi];
2449                                mvdc[ci] = [tm[bi][0] as i16, tm[bi][1] as i16];
2450                            }
2451                        }
2452                        if mbx > 0 && mby > 0 {
2453                            let a = addr - mbw - 1;
2454                            if let (Some(r), Some(m)) = (mb_ref.get(a), mb_mvd.get(a)) {
2455                                (refc[0], mvdc[0]) =
2456                                    (r[15], [m[15][0] as i16, m[15][1] as i16]);
2457                            }
2458                        }
2459                        if mby > 0 && mbx + 1 < mbw {
2460                            let a = addr - mbw + 1;
2461                            if let (Some(r), Some(m)) = (mb_ref.get(a), mb_mvd.get(a)) {
2462                                (refc[5], mvdc[5]) =
2463                                    (r[12], [m[12][0] as i16, m[12][1] as i16]);
2464                            }
2465                        }
2466                        let mut mmvd = [[0i16; 2]; 16];
2467                        let mut mref = [0i8; 16];
2468                        // mb_pred (spec 7.3.5.1): all ref_idx_l0 FIRST (only when >1 active
2469                        // ref), then all mvd + ref-aware predict + commit. `refidx!` parses one
2470                        // partition's ref_idx (ctxIdxOffset 54, ctx from neighbour refc) and
2471                        // seeds refc so a later partition's ref/mvd context sees it — mirror
2472                        // of the encoder's two-phase emit_mb_cabac_p_inter.
2473                        macro_rules! refidx {
2474                            ($pi:expr, $zb:expr) => {{
2475                                if self.num_ref_active > 1 {
2476                                    let s = CACHE30[$pi & 15].clamp(6, 29);
2477                                    let c0 =
2478                                        (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
2479                                    let r = parse_ref_idx_cabac(&mut cab, c0);
2480                                    for &zb in $zb.iter() {
2481                                        refc[CACHE30[zb & 15]] = r;
2482                                    }
2483                                    r
2484                                } else {
2485                                    0i8
2486                                }
2487                            }};
2488                        }
2489                        macro_rules! part {
2490                            ($pi:expr, $zb:expr, $pred:expr, $rx:expr, $ry:expr, $rw:expr, $rh:expr, $refi:expr) => {{
2491                                let (mvx, mvy) = parse_mvd_partition(
2492                                    &mut cab, $pi, $zb, &mut mvdc, &mut refc, &mut mmvd, &mut mref,
2493                                    $refi,
2494                                );
2495                                let [na, nb, nc] = self.mv_neighbors_block(
2496                                    (mbx * 4 + $rx / 4) as isize,
2497                                    (mby * 4 + $ry / 4) as isize,
2498                                    ($rw / 4) as isize,
2499                                );
2500                                let pmv = $pred(na, nb, nc);
2501                                self.commit_inter_grid(
2502                                    mbx,
2503                                    mby,
2504                                    $rx,
2505                                    $ry,
2506                                    $rw,
2507                                    $rh,
2508                                    (pmv.0 + mvx, pmv.1 + mvy),
2509                                    $refi,
2510                                );
2511                            }};
2512                        }
2513                        match mbt {
2514                            0 => {
2515                                // Sibling of CAVLC P_16x16: one (ref, mv) for all 16
2516                                // blocks → every internal edge is strength 0 (§8.7.2.1).
2517                                // Without this, CABAC Main/High P_16x16 stayed UNSET and
2518                                // paid the blind 24-block bS gather.
2519                                if let Some(k) = self.mb_kind.get_mut(mby * self.mb_w + mbx) {
2520                                    *k = rusty_h264_common::deblock::MB_KIND_INTER_UNIFORM;
2521                                }
2522                                let r0 = refidx!(
2523                                    0,
2524                                    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
2525                                );
2526                                part!(
2527                                    0,
2528                                    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
2529                                    |a, b, c| predict_partition_mv(0, 0, a, b, c, r0 as i32),
2530                                    0,
2531                                    0,
2532                                    16,
2533                                    16,
2534                                    r0
2535                                );
2536                            }
2537                            1 => {
2538                                let r0 = refidx!(0, &[0, 1, 2, 3, 4, 5, 6, 7]);
2539                                let r1 = refidx!(8, &[8, 9, 10, 11, 12, 13, 14, 15]);
2540                                part!(
2541                                    0,
2542                                    &[0, 1, 2, 3, 4, 5, 6, 7],
2543                                    |a, b, c| predict_partition_mv(1, 0, a, b, c, r0 as i32),
2544                                    0,
2545                                    0,
2546                                    16,
2547                                    8,
2548                                    r0
2549                                );
2550                                part!(
2551                                    8,
2552                                    &[8, 9, 10, 11, 12, 13, 14, 15],
2553                                    |a, b, c| predict_partition_mv(1, 1, a, b, c, r1 as i32),
2554                                    0,
2555                                    8,
2556                                    16,
2557                                    8,
2558                                    r1
2559                                );
2560                            }
2561                            2 => {
2562                                let r0 = refidx!(0, &[0, 1, 2, 3, 8, 9, 10, 11]);
2563                                let r1 = refidx!(4, &[4, 5, 6, 7, 12, 13, 14, 15]);
2564                                part!(
2565                                    0,
2566                                    &[0, 1, 2, 3, 8, 9, 10, 11],
2567                                    |a, b, c| predict_partition_mv(2, 0, a, b, c, r0 as i32),
2568                                    0,
2569                                    0,
2570                                    8,
2571                                    16,
2572                                    r0
2573                                );
2574                                part!(
2575                                    4,
2576                                    &[4, 5, 6, 7, 12, 13, 14, 15],
2577                                    |a, b, c| predict_partition_mv(2, 1, a, b, c, r1 as i32),
2578                                    8,
2579                                    0,
2580                                    8,
2581                                    16,
2582                                    r1
2583                                );
2584                            }
2585                            _ => {
2586                                // P_8x8: 4 sub_mb_types, then 4 ref_idx (one per 8×8), then mvd.
2587                                let mut subt = [0u32; 4];
2588                                for st in &mut subt {
2589                                    *st = parse_sub_mb_type_p_cabac(&mut cab);
2590                                }
2591                                allow8 = subt.iter().all(|&t| t == 0);
2592                                let mut pr = [0i8; 4];
2593                                for (i, r) in pr.iter_mut().enumerate() {
2594                                    let b = i * 4;
2595                                    *r = refidx!(b, &[b, b + 1, b + 2, b + 3]);
2596                                }
2597                                for i in 0..4usize {
2598                                    let b = i * 4;
2599                                    let (ox, oy) = ((i % 2) * 8, (i / 2) * 8); // 8×8 pixel origin in MB
2600                                    let ri = pr[i];
2601                                    match subt[i] {
2602                                        0 => part!(
2603                                            b,
2604                                            &[b, b + 1, b + 2, b + 3],
2605                                            |a, b, c| predict_mv(a, b, c, ri as i32),
2606                                            ox,
2607                                            oy,
2608                                            8,
2609                                            8,
2610                                            ri
2611                                        ),
2612                                        1 => {
2613                                            part!(
2614                                                b,
2615                                                &[b, b + 1],
2616                                                |a, b, c| predict_mv(a, b, c, ri as i32),
2617                                                ox,
2618                                                oy,
2619                                                8,
2620                                                4,
2621                                                ri
2622                                            );
2623                                            part!(
2624                                                b + 2,
2625                                                &[b + 2, b + 3],
2626                                                |a, b, c| predict_mv(a, b, c, ri as i32),
2627                                                ox,
2628                                                oy + 4,
2629                                                8,
2630                                                4,
2631                                                ri
2632                                            );
2633                                        }
2634                                        2 => {
2635                                            part!(
2636                                                b,
2637                                                &[b, b + 2],
2638                                                |a, b, c| predict_mv(a, b, c, ri as i32),
2639                                                ox,
2640                                                oy,
2641                                                4,
2642                                                8,
2643                                                ri
2644                                            );
2645                                            part!(
2646                                                b + 1,
2647                                                &[b + 1, b + 3],
2648                                                |a, b, c| predict_mv(a, b, c, ri as i32),
2649                                                ox + 4,
2650                                                oy,
2651                                                4,
2652                                                8,
2653                                                ri
2654                                            );
2655                                        }
2656                                        _ => {
2657                                            for j in 0..4usize {
2658                                                let (sx, sy) = ((j % 2) * 4, (j / 2) * 4);
2659                                                part!(
2660                                                    b + j,
2661                                                    &[b + j],
2662                                                    |a, b, c| predict_mv(a, b, c, ri as i32),
2663                                                    ox + sx,
2664                                                    oy + sy,
2665                                                    4,
2666                                                    4,
2667                                                    ri
2668                                                );
2669                                            }
2670                                        }
2671                                    }
2672                                }
2673                            }
2674                        }
2675                        if let Some(p) = mb_ref.get_mut(addr) {
2676                            *p = mref;
2677                        }
2678                        if let Some(p) = mb_mvd.get_mut(addr) {
2679                            *p = sat_mvd(mmvd);
2680                        }
2681
2682                        // Inter cbp + residual (is_intra = false → cbf default nA=nB=0).
2683                        let cbp = parse_cbp_cabac(
2684                            &mut cab,
2685                            top.and_then(|a| mb_cbp.get(a).copied()),
2686                            left.and_then(|a| mb_cbp.get(a).copied()),
2687                        );
2688                        if let Some(p) = mb_cbp.get_mut(addr) {
2689                            *p = cbp as u8;
2690                        }
2691                        // H-49: an INTER macroblock carries transform_size_8x8_flag AFTER cbp
2692                        // (spec 7.3.5), present only when CodedBlockPatternLuma > 0 and
2693                        // noSubMbPartSizeLessThan8x8Flag. Same context as the intra read.
2694                        let t8 = self.transform_8x8_mode && (cbp & 15) != 0 && allow8 && {
2695                            let a = left
2696                                .map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
2697                            let b =
2698                                top.map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
2699                            cab.decode_decision(399 + a + b) != 0
2700                        };
2701                        if let Some(p) = self.mb_t8x8.get_mut(addr) {
2702                            *p = t8;
2703                        }
2704                        self.any_t8 |= t8;
2705                        // D9c: cbp==0 never parses residuals — skip the 2.5 KB coeff
2706                        // zero-init + PInterJob entirely when NORES is on (default).
2707                        // Current-MB nzc slots stay unset under cbp==0 and export as 0
2708                        // (same as the 0xff→0 scrub below), so mb_nzc = [0;24] is exact.
2709                        if cbp == 0 && nores_on() {
2710                            last_delta_qp = 0;
2711                            if let Some(p) = self.mb_qp.get_mut(addr) {
2712                                *p = self.cur_qp;
2713                            }
2714                            if let Some(p) = cbf_dc.get_mut(addr) {
2715                                *p = 0;
2716                            }
2717                            if let Some(p) = mb_nzc.get_mut(addr) {
2718                                *p = [0u8; 24];
2719                            }
2720                            if self.refs.is_empty() {
2721                                return Err(MbError::Unsupported("inter without reference"));
2722                            }
2723                            let (mut jgmv, mut jgref) = ([(0i16, 0i16); 16], [0u8; 16]);
2724                            {
2725                                let w4r = self.mb_w * 4;
2726                                for by in 0..4usize {
2727                                    // Row-contiguous — see the coded-inter gather.
2728                                    let row = (mby * 4 + by) * w4r + mbx * 4;
2729                                    jgmv[by * 4..by * 4 + 4]
2730                                        .copy_from_slice(&self.mv_y[row..row + 4]);
2731                                    // Row-slice the ref grid too - it was the only
2732                                    // one still indexed per block.
2733                                    let ridx = &self.ref_idx_y[row..row + 4];
2734                                    for bx in 0..4usize {
2735                                        jgref[by * 4 + bx] = ridx[bx].clamp(0, 15) as u8;
2736                                    }
2737                                }
2738                            }
2739                            let pj = PInterNoResJob {
2740                                mbx,
2741                                mby,
2742                                t8,
2743                                gmv: jgmv,
2744                                gref: jgref,
2745                            };
2746                            if self.edc_tx.is_some() {
2747                                self.edc_giveback();
2748                                self.edc_commit_nnz(mbx, mby, t8, &[0u8; 24], 0);
2749                                if edcstat::on() {
2750                                    edcstat::bump(&edcstat::J_INTER, 1);
2751                                    edcstat::bump(&edcstat::J_INTER_NORES, 1);
2752                                }
2753                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
2754                                // POOLED, like the other three construction
2755                                // sites. These two used `Box::new` and were the
2756                                // hot ones: 693,630 allocations on a 1260-frame
2757                                // decode, 87% of every allocation the decoder
2758                                // made, against a pool that already existed and
2759                                // was already being refilled at the drain.
2760                                let b = self.take_nores_job(pj);
2761                                self.edc_send_job(EdcJob::InterNoRes(b));
2762                            } else if self.edc_active {
2763                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
2764                                let b = self.take_nores_job(pj);
2765                                self.edc_jobs.push(EdcJob::InterNoRes(b));
2766                            } else {
2767                                self.recon_p_inter_nores(&pj);
2768                                if double_recon() {
2769                                    self.recon_p_inter_nores(&pj);
2770                                }
2771                            }
2772                            let eos = cab.decode_terminate();
2773                            addr += 1;
2774                            mbx += 1;
2775                            if eos || addr >= total {
2776                                break;
2777                            }
2778                            continue;
2779                        }
2780                        // POOLED JOB, BUILT IN PLACE: the residual parse writes straight into
2781                        // the boxed job (no 2.8 KB stack-then-heap copy, no malloc/free per
2782                        // macroblock), and only the blocks the cbp says will be parsed are
2783                        // zeroed -- the consumer reads a block only when its nnz is nonzero.
2784                        let mut job = self.take_pinter_job();
2785                        job.t8 = t8;
2786                        let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
2787                        let mut nzc = [0xffu8; 48];
2788                        if let Some(t) = top {
2789                            let tnz = mb_nzc.get(t).unwrap_or(&ZERO_NZC);
2790                            nzc[1..5].copy_from_slice(&tnz[12..16]);
2791                            (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
2792                            (nzc[6], nzc[7], nzc[30], nzc[31]) =
2793                                (tnz[20], tnz[21], tnz[22], tnz[23]);
2794                        }
2795                        if let Some(l) = left {
2796                            let lnz = mb_nzc.get(l).unwrap_or(&ZERO_NZC);
2797                            (nzc[8], nzc[16], nzc[24], nzc[32]) =
2798                                (lnz[3], lnz[7], lnz[11], lnz[15]);
2799                            (nzc[13], nzc[21], nzc[37], nzc[45]) =
2800                                (lnz[17], lnz[21], lnz[19], lnz[23]);
2801                        }
2802                        let mut cbfdc = 0u16;
2803                        job.nnzs = [0u8; 24]; // parsed totalCoeff per block (see add_inter_residual)
2804                                              // A cbp==0 MB codes no mb_qp_delta → the next MB's delta ctxInc sees 0.
2805                        if cbp == 0 {
2806                            last_delta_qp = 0;
2807                        }
2808                        if cbp != 0 {
2809                            let ndc = (
2810                                top.and_then(|a| cbf_dc.get(a).copied()),
2811                                left.and_then(|a| cbf_dc.get(a).copied()),
2812                            );
2813                            let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
2814                            self.step_qp(qpd)?;
2815                            parse_mb_residual_cabac::<false>(
2816                                &mut cab,
2817                                &mut nzc,
2818                                &mut cbfdc,
2819                                ndc,
2820                                cbp_luma,
2821                                cbp_chroma,
2822                                t8,
2823                                ResidualOut {
2824                                    luma: &mut job.luma_scan,
2825                                    luma8: &mut job.luma8,
2826                                    cdc: &mut job.cdc,
2827                                    cac: &mut job.cac,
2828                                    nnzs: &mut job.nnzs,
2829                                },
2830                            );
2831                        }
2832                        if let Some(p) = self.mb_qp.get_mut(addr) {
2833                            *p = self.cur_qp;
2834                        }
2835                        if let Some(p) = cbf_dc.get_mut(addr) {
2836                            *p = cbfdc;
2837                        }
2838                        let _sc = rusty_h264_common::prof::scope(
2839                            rusty_h264_common::prof::Stage::DecStateCache,
2840                        );
2841                        let mn = nnz_raster_from_z(&job.nnzs);
2842                        if let Some(p) = mb_nzc.get_mut(addr) {
2843                            *p = mn;
2844                        }
2845                        drop(_sc);
2846
2847                        if self.refs.is_empty() {
2848                            return Err(MbError::Unsupported("inter without reference"));
2849                        }
2850                        // NOTE (measured, do not "optimise" this away): EDC is
2851                        // DEFAULT ON (`edc_on()`, opt out with RS_H264_EDC=0), so
2852                        // even single-threaded this job is built and deferred —
2853                        // that batching is the E1 loop-fission win, not overhead.
2854                        // A "skip the job in 1T" bypass added here could never fire
2855                        // and was removed.
2856                        let (mut jgmv, mut jgref) = ([(0i16, 0i16); 16], [0u8; 16]);
2857                        {
2858                            let w4r = self.mb_w * 4;
2859                            for by in 0..4usize {
2860                                // Row-contiguous: one slice copy for the MVs
2861                                // instead of four bounds-checked strided loads.
2862                                let row = (mby * 4 + by) * w4r + mbx * 4;
2863                                jgmv[by * 4..by * 4 + 4].copy_from_slice(&self.mv_y[row..row + 4]);
2864                                // Row-slice the ref grid too - it was the only one
2865                                // still indexed per block.
2866                                let ridx = &self.ref_idx_y[row..row + 4];
2867                                for bx in 0..4usize {
2868                                    jgref[by * 4 + bx] = ridx[bx].clamp(0, 15) as u8;
2869                                }
2870                            }
2871                        }
2872                        // D9c: when `cbp == 0`, never materialise the 2.5 KB coeff
2873                        // arrays into a `PInterJob` — ship `InterNoRes` (or call
2874                        // `recon_p_inter_nores` inline). `RS_H264_NORES=0` keeps the
2875                        // old full-job path for A/B.
2876                        let nores = cbp == 0 && nores_on();
2877                        if nores {
2878                            let pj = PInterNoResJob {
2879                                mbx,
2880                                mby,
2881                                t8,
2882                                gmv: jgmv,
2883                                gref: jgref,
2884                            };
2885                            if self.edc_tx.is_some() {
2886                                self.edc_giveback();
2887                                self.edc_commit_nnz(mbx, mby, t8, &[0u8; 24], 0);
2888                                if edcstat::on() {
2889                                    edcstat::bump(&edcstat::J_INTER, 1);
2890                                    edcstat::bump(&edcstat::J_INTER_NORES, 1);
2891                                }
2892                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
2893                                let b = self.take_nores_job(pj);
2894                                self.edc_send_job(EdcJob::InterNoRes(b));
2895                            } else if self.edc_active {
2896                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
2897                                let b = self.take_nores_job(pj);
2898                                self.edc_jobs.push(EdcJob::InterNoRes(b));
2899                            } else {
2900                                self.recon_p_inter_nores(&pj);
2901                                if double_recon() {
2902                                    self.recon_p_inter_nores(&pj);
2903                                }
2904                            }
2905                            let eos = cab.decode_terminate();
2906                            addr += 1;
2907                            mbx += 1;
2908                            if eos || addr >= total {
2909                                break;
2910                            }
2911                            continue;
2912                        }
2913                        job.mbx = mbx;
2914                        job.mby = mby;
2915                        job.qp = self.cur_qp;
2916                        job.cbp_chroma = cbp_chroma;
2917                        job.gmv = jgmv;
2918                        job.gref = jgref;
2919                        if self.edc_tx.is_some() {
2920                            self.edc_giveback();
2921                            self.edc_commit_nnz(mbx, mby, t8, &job.nnzs, cbp_chroma);
2922                            if edcstat::on() {
2923                                edcstat::bump(&edcstat::J_INTER, 1);
2924                            }
2925                            self.edc_send_job(EdcJob::Inter(job));
2926                        } else if self.edc_active {
2927                            self.edc_jobs.push(EdcJob::Inter(job));
2928                        } else {
2929                            self.recon_p_inter(&job);
2930                            if double_recon() {
2931                                self.recon_p_inter(&job);
2932                            }
2933                            self.edc_job_pool.push(job);
2934                        }
2935
2936                        let eos = cab.decode_terminate();
2937                        addr += 1;
2938                        mbx += 1;
2939                        if eos || addr >= total {
2940                            break;
2941                        }
2942                        continue;
2943                    }
2944                    mbt - 5 // 5→0 (I_4x4), 6..29→1..24 (I_16x16)
2945                } else if self.is_b {
2946                    // NOTE: the per-macroblock edc_flush() that used to sit here is
2947                    // hoisted to slice entry (see below) — nothing inside a B slice
2948                    // pushes to edc_jobs.
2949                    if b_records {
2950                        // E3: this B macroblock's MC regions record instead of executing.
2951                        self.edc_regions = Some(Vec::with_capacity(8));
2952                    }
2953                    let _gb =
2954                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbB);
2955                    // noSubMbPartSizeLessThan8x8Flag for B: direct MBs qualify only under
2956                    // direct_8x8_inference_flag; B_8x8 needs every sub-partition 8x8.
2957                    let mut allow8 = true;
2958                    // B-slice: mb_skip_flag (ctx 24 + neighbour-not-skip), then B mb_type.
2959                    let (hl, ht) = (left.is_some(), top.is_some());
2960                    let sctx = 24
2961                        + (hl && !mb_skip.get(addr - 1).copied().unwrap_or(true)) as usize
2962                        + (ht && !mb_skip.get(addr.wrapping_sub(mbw)).copied().unwrap_or(true))
2963                            as usize;
2964                    // ONE engine view carries mb_skip_flag, the terminate bin of a skipped
2965                    // macroblock, and mb_type of a coded one (was 2-3 view/commit round
2966                    // trips of 8 memory ops each per macroblock).
2967                    let (data, mut e, ctx) = cab.view();
2968                    if e.decode_decision(data, ctx, sctx) != 0 {
2969                        if let (Some(sk), Some(di)) =
2970                            (mb_skip.get_mut(addr), mb_direct.get_mut(addr))
2971                        {
2972                            (*sk, *di) = (true, true);
2973                        }
2974                        last_delta_qp = 0; // skip codes no mb_qp_delta → delta ctxInc resets
2975                                           // B_Skip recon reuses the entropy-free CAVLC primitive (spatial/temporal
2976                                           // direct with no residual), which also commits the motion grid.
2977                                           // Hot prefix inline: forced run continuations skip the call.
2978                        if !self.b_skip_hot(mbx, mby) {
2979                            self.decode_b_skip(mbx, mby)?;
2980                        }
2981                        if let Some(p) = self.mb_qp.get_mut(addr) {
2982                            *p = self.cur_qp;
2983                        }
2984                        // Skip/direct blocks contribute mvd 0 to a later MB's mvd ctxInc; the
2985                        // ref stays in-list so |mvd|=0 is summed (same result either way).
2986                        // mb_ref/mb_ref1 stay at their -1 init: every reader is
2987                        // either a `> 0` context test (-1 and 0 both false) or the
2988                        // mvd-sum's `>= 0` gate — and a skip's mvd is (0,0), so
2989                        // exclusion (-1) and inclusion-of-zero (0) give the same
2990                        // sum. The 64-byte per-skip zero-fill was pure waste.
2991                        let eos = e.decode_terminate(data);
2992                        cab.commit(e);
2993                        addr += 1;
2994                        mbx += 1;
2995                        if eos || addr >= total {
2996                            break;
2997                        }
2998                        continue;
2999                    }
3000                    // NON-SKIP B MB: every arm below reads the grids INLINE
3001                    // (H-48: CABAC never routes through decode_b_mb) — flush
3002                    // the deferred spans. Caught by the tempete ARM-DIFF.
3003                    self.span_flush();
3004                    let bci = (hl && !mb_direct.get(addr - 1).copied().unwrap_or(true)) as usize
3005                        + (ht
3006                            && !mb_direct
3007                                .get(addr.wrapping_sub(mbw))
3008                                .copied()
3009                                .unwrap_or(true)) as usize;
3010                    let bmt = {
3011                        let _s = rusty_h264_common::prof::scope(
3012                            rusty_h264_common::prof::Stage::BTypeParse,
3013                        );
3014                        mb_type_b_eng(&mut e, data, ctx, 27, bci)
3015                    };
3016                    cab.commit(e);
3017                    if bmt < 23 {
3018                        // ---- B inter: parse motion (mvd L0/L1; ref not coded on this 1-ref
3019                        // stream) + residual. Recon (b_mc/direct) deferred to B.3. ----
3020                        let mut mvdc0 = [[0i16; 2]; 30];
3021                        let mut refc0 = [-1i8; 30];
3022                        let mut mvdc1 = [[0i16; 2]; 30];
3023                        let mut refc1 = [-1i8; 30];
3024                        // WelsFillCacheInterCabac, per list (L0 = mb_ref/mb_mvd, L1 = mb_ref1/mb_mvd1).
3025                        // The corner addresses and their guards are macroblock
3026                        // properties, resolved ONCE here rather than rebuilt inside
3027                        // each list expansion; each neighbour grid row is borrowed
3028                        // once instead of re-indexed per entry.
3029                        let tl = (mbx > 0 && mby > 0).then(|| addr - mbw - 1);
3030                        let tr = (mby > 0 && mbx + 1 < mbw).then(|| addr - mbw + 1);
3031                        macro_rules! fill {
3032                            ($mrf:expr, $mmv:expr, $rc:expr, $mc:expr) => {{
3033                                // `.get` PER PARALLEL GRID. The ref and mvd grids are
3034                                // separate Vecs indexed by the same macroblock address,
3035                                // so `$mrf[l]` proved nothing about `$mmv[l]` and each
3036                                // of the four neighbour slots carried two panic paths —
3037                                // doubled again because this macro expands once per
3038                                // list. A `None` here degrades to exactly what the
3039                                // cache already means by "neighbour unavailable" (the
3040                                // `-1` ref it is initialised to), so the fallible form
3041                                // is the honest one as well as the cheap one.
3042                                if let Some(l) = left {
3043                                    if let (Some(rr), Some(mm)) = ($mrf.get(l), $mmv.get(l)) {
3044                                        for (ci, bi) in
3045                                            [(6usize, 3usize), (12, 7), (18, 11), (24, 15)]
3046                                        {
3047                                            $rc[ci] = rr[bi];
3048                                            $mc[ci] = [mm[bi][0] as i16, mm[bi][1] as i16];
3049                                        }
3050                                    }
3051                                }
3052                                if let Some(t) = top {
3053                                    if let (Some(rr), Some(mm)) = ($mrf.get(t), $mmv.get(t)) {
3054                                        for (ci, bi) in
3055                                            [(1usize, 12usize), (2, 13), (3, 14), (4, 15)]
3056                                        {
3057                                            $rc[ci] = rr[bi];
3058                                            $mc[ci] = [mm[bi][0] as i16, mm[bi][1] as i16];
3059                                        }
3060                                    }
3061                                }
3062                                if let Some(a) = tl {
3063                                    if let (Some(rr), Some(mm)) = ($mrf.get(a), $mmv.get(a)) {
3064                                        ($rc[0], $mc[0]) = (rr[15], [mm[15][0] as i16, mm[15][1] as i16]);
3065                                    }
3066                                }
3067                                if let Some(a) = tr {
3068                                    if let (Some(rr), Some(mm)) = ($mrf.get(a), $mmv.get(a)) {
3069                                        ($rc[5], $mc[5]) = (rr[12], [mm[12][0] as i16, mm[12][1] as i16]);
3070                                    }
3071                                }
3072                            }};
3073                        }
3074                        {
3075                            let _s = rusty_h264_common::prof::scope(
3076                                rusty_h264_common::prof::Stage::BFillCache,
3077                            );
3078                            fill!(mb_ref, mb_mvd, refc0, mvdc0);
3079                            fill!(mb_ref1, mb_mvd1, refc1, mvdc1);
3080                        }
3081                        let mut _smv = Some(rusty_h264_common::prof::scope(
3082                            rusty_h264_common::prof::Stage::BMvdParse,
3083                        ));
3084                        let mut mmvd0 = [[0i16; 2]; 16];
3085                        let mut mref0 = [-1i8; 16];
3086                        let mut mmvd1 = [[0i16; 2]; 16];
3087                        let mut mref1 = [-1i8; 16];
3088                        if !b_refs_ok {
3089                            return Err(MbError::Unsupported("B without references"));
3090                        }
3091                        // Recon (mirrors CAVLC decode_b_mb / decode_b_8x8): predict each list's
3092                        // MV off the committed grid + the CABAC-parsed mvd, commit, MC (bi-pred
3093                        // blend), then add the residual. Prediction reads mmvd0/mmvd1 (the mvd
3094                        // per raster block, splatted during the parse above).
3095                        // pred_y / c_pred are slice-scope scratch (see the loop head).
3096                        // Recording is a per-macroblock property — ask once, not per
3097                        // partition, so the 1T path calls b_mc straight through.
3098                        let rec_mode = self.edc_regions.is_some();
3099
3100                        if bmt == 0 {
3101                            // B_Direct_16x16: no coded motion. A direct block contributes mvd 0
3102                            // to a later MB's mvd ctxInc with its ref in-list (|0| summed).
3103                            if let Some(p) = mb_direct.get_mut(addr) {
3104                                *p = true;
3105                            }
3106                            allow8 = self.direct_8x8_inference;
3107                            (mref0, mref1) = ([0i8; 16], [0i8; 16]);
3108                            // FORCED derivation via the zero-bi bitmap (same triple
3109                            // as b_skip_hot): a direct-16 whose left/top/topright are
3110                            // recorded ref0/(0,0) committers derives (0,0)/(0,0) bi —
3111                            // skip the gather + derivation, run the region half
3112                            // directly. Its own commit is zero-bi too, so it EXTENDS
3113                            // forcing chains through coded direct MBs.
3114                            // `mbw` is already carried by the loop, and the row-above
3115                            // address is one subtraction, not three.
3116                            let up = addr.wrapping_sub(mbw);
3117                            let forced = !b_records
3118                                && mbx > 0
3119                                && mby > 0
3120                                && mbx + 1 < mbw
3121                                && up >= self.slice_first_mb
3122                                && match self.bzero.get(..=addr) {
3123                                    // A slice ENDING at `addr` proves all three
3124                                    // neighbour reads at once — the guards above put
3125                                    // every one of them below `addr`. Same shape as
3126                                    // `b_skip_hot`.
3127                                    Some(bz) => {
3128                                        bz.get(addr - 1).copied().unwrap_or(false)
3129                                            && bz.get(up).copied().unwrap_or(false)
3130                                            && bz.get(up + 1).copied().unwrap_or(false)
3131                                    }
3132                                    None => false,
3133                                };
3134                            if forced {
3135                                self.b_direct_region(
3136                                    mbx,
3137                                    mby,
3138                                    0,
3139                                    0,
3140                                    16,
3141                                    16,
3142                                    &mut pred_y,
3143                                    &mut c_pred,
3144                                    (0, 0, (0, 0), (0, 0), false),
3145                                );
3146                                if let Some(b) = self.bzero.get_mut(addr) {
3147                                    *b = true;
3148                                }
3149                            } else {
3150                                self.decode_b_direct(
3151                                    mbx,
3152                                    mby,
3153                                    0,
3154                                    0,
3155                                    16,
3156                                    16,
3157                                    &mut pred_y,
3158                                    &mut c_pred,
3159                                );
3160                            }
3161                        } else if bmt == 22 {
3162                            // B_8x8: 4 sub_mb_types, (ref not coded on 1-ref), then mvd
3163                            // list-major → sub-MB → sub-partition (openh264 order).
3164                            let mut subt = [0u32; 4];
3165                            for s in &mut subt {
3166                                *s = parse_sub_mb_type_b_cabac(&mut cab);
3167                            }
3168                            let d8i = self.direct_8x8_inference;
3169                            allow8 =
3170                                subt.iter()
3171                                    .all(|&t| if t == 0 { d8i } else { (1..=3).contains(&t) });
3172                            // uses(list) depends only on the sub_mb_type — resolve the
3173                            // whole 4x2 table once here; the ref_idx, mvd and recon
3174                            // loops below all read it instead of re-asking.
3175                            let su = [
3176                                [b_sub_uses(subt[0], 0), b_sub_uses(subt[0], 1)],
3177                                [b_sub_uses(subt[1], 0), b_sub_uses(subt[1], 1)],
3178                                [b_sub_uses(subt[2], 0), b_sub_uses(subt[2], 1)],
3179                                [b_sub_uses(subt[3], 0), b_sub_uses(subt[3], 1)],
3180                            ];
3181                            // A direct sub-partition contributes mvd 0 / ref in-list to the
3182                            // ctxInc — both the per-MB export and the within-MB 30-cache that a
3183                            // later (non-direct) sub in this MB reads.
3184                            for i in 0..4usize {
3185                                if subt[i] == 0 {
3186                                    let b = i * 4;
3187                                    for zb in b..b + 4 {
3188                                        // each table read once, not twice
3189                                        let (g, c) = (G_SCAN4[zb & 15], CACHE30[zb & 15]);
3190                                        (mref0[g], mref1[g]) = (0, 0);
3191                                        (refc0[c], refc1[c]) = (0, 0);
3192                                    }
3193                                }
3194                            }
3195                            // ref_idx_l0 for all four 8x8s, then ref_idx_l1, then the mvds
3196                            // (spec 7.3.5.2 sub_mb_pred). ONE ref per 8x8 -- never per
3197                            // sub-partition -- and B_Direct_8x8 codes none.
3198                            let mut sref = [[0i8; 2]; 4]; // [sub-MB][list]
3199                            for list in 0..2usize {
3200                                // Single-reference lists code no ref_idx at all.
3201                                if (if list == 0 {
3202                                    self.num_ref_active
3203                                } else {
3204                                    self.num_ref_active1
3205                                }) <= 1
3206                                {
3207                                    continue;
3208                                }
3209                                let rc = if list == 0 { &mut refc0 } else { &mut refc1 };
3210                                for i in 0..4usize {
3211                                    let st = subt[i];
3212                                    if st == 0 || !su[i][list] {
3213                                        continue;
3214                                    }
3215                                    let b = i * 4;
3216                                    let s = CACHE30[b & 15].clamp(6, 29);
3217                                    let c0 =
3218                                        (rc[s - 1] > 0) as usize + 2 * (rc[s - 6] > 0) as usize;
3219                                    let r = parse_ref_idx_cabac(&mut cab, c0);
3220                                    for &zb in &[b, b + 1, b + 2, b + 3] {
3221                                        rc[CACHE30[zb]] = r;
3222                                    }
3223                                    sref[i][list] = r;
3224                                }
3225                            }
3226                            for list in 0..2usize {
3227                                let (mmv, mrf, mc, rc) = if list == 0 {
3228                                    (&mut mmvd0, &mut mref0, &mut mvdc0, &mut refc0)
3229                                } else {
3230                                    (&mut mmvd1, &mut mref1, &mut mvdc1, &mut refc1)
3231                                };
3232                                for i in 0..4usize {
3233                                    let st = subt[i];
3234                                    if st == 0 || !su[i][list] {
3235                                        continue;
3236                                    }
3237                                    let b = i * 4;
3238                                    for &(sx, sy, sw, sh) in b_sub_parts(st) {
3239                                        // Shapes are 8x8 / 8x4 / 4x8 / 4x4, so the block
3240                                        // list is closed-form: `step` is 1 when the part
3241                                        // spans both columns, 2 when it spans both rows.
3242                                        let (w4b, h4b) = (sw / 4, sh / 4);
3243                                        let base = b + (sy / 4) * 2 + sx / 4;
3244                                        let step = if w4b == 2 { 1 } else { 2 };
3245                                        let zb = [base, base + step, base + 2, base + 3];
3246                                        let n = w4b * h4b;
3247                                        parse_mvd_partition(
3248                                            &mut cab,
3249                                            zb[0],
3250                                            &zb[..n],
3251                                            mc,
3252                                            rc,
3253                                            mmv,
3254                                            mrf,
3255                                            sref[i][list],
3256                                        );
3257                                    }
3258                                }
3259                            }
3260                            // Recon each 8×8: direct sub → decode_b_direct; else per sub-part
3261                            // predict (median) + commit + MC.
3262                            // Spatial-direct A/B/C are MB-level — walk once if any sub is
3263                            // direct. `dmemo=0` rewalks every direct 8×8 (A/B oracle).
3264                            // Derivation hoist: A/B/C AND the rid/median result are
3265                            // MB-level — derive once, run only the per-8x8 region half
3266                            // (czg differs per sub) for every direct sub.
3267                            let hoisted = if self.direct_spatial
3268                                && direct_memo_on()
3269                                && subt.contains(&0)
3270                            {
3271                                let (n0, n1) = self.b_direct_nbrs(mbx, mby);
3272                                Some(Self::b_direct_refs_mvs(&n0, &n1))
3273                            } else {
3274                                None
3275                            };
3276                            for (p, &st) in subt.iter().enumerate() {
3277                                let (b8x, b8y) = ((p % 2) * 8, (p / 2) * 8);
3278                                if st == 0 {
3279                                    match hoisted {
3280                                        Some(derived) => self.b_direct_region(
3281                                            mbx,
3282                                            mby,
3283                                            b8x,
3284                                            b8y,
3285                                            8,
3286                                            8,
3287                                            &mut pred_y,
3288                                            &mut c_pred,
3289                                            derived,
3290                                        ),
3291                                        None => self.decode_b_direct(
3292                                            mbx,
3293                                            mby,
3294                                            b8x,
3295                                            b8y,
3296                                            8,
3297                                            8,
3298                                            &mut pred_y,
3299                                            &mut c_pred,
3300                                        ),
3301                                    }
3302                                    continue;
3303                                }
3304                                // From the table built at the top of this arm.
3305                                let (u0, u1) = (su[p & 3][0], su[p & 3][1]);
3306                                for &(sx, sy, sw, sh) in b_sub_parts(st) {
3307                                    let (px, py) = (b8x + sx, b8y + sy);
3308                                    let mut mv = [(0i32, 0i32); 2];
3309                                    let (bx4, by4) =
3310                                        ((mbx * 4 + px / 4) as isize, (mby * 4 + py / 4) as isize);
3311                                    let didx = (py / 4) * 4 + px / 4;
3312                                    // A BI sub-part gathers both lists at ONE position, so the
3313                                    // availability work (bounds + coded + slice) is shared —
3314                                    // the fusion the 16x16 layout path already had.
3315                                    if u0 && u1 {
3316                                        let (n0, n1) =
3317                                            self.mv_neighbors_both(bx4, by4, (sw / 4) as isize);
3318                                        for (list, nb) in [(0usize, &n0), (1, &n1)] {
3319                                            let d = (if list == 0 { &mmvd0 } else { &mmvd1 })
3320                                                [didx & 15];
3321                                            let pmv = predict_mv(
3322                                                nb[0],
3323                                                nb[1],
3324                                                nb[2],
3325                                                sref[p & 3][list & 1] as i32,
3326                                            );
3327                                            mv[list & 1] =
3328                                                (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
3329                                        }
3330                                    } else {
3331                                        for list in 0..2usize {
3332                                            if if list == 0 { u0 } else { u1 } {
3333                                                let d = (if list == 0 { &mmvd0 } else { &mmvd1 })
3334                                                    [didx & 15];
3335                                                let nb = self.mv_neighbors_list(
3336                                                    bx4,
3337                                                    by4,
3338                                                    (sw / 4) as isize,
3339                                                    list,
3340                                                );
3341                                                let pmv = predict_mv(
3342                                                    nb[0],
3343                                                    nb[1],
3344                                                    nb[2],
3345                                                    sref[p & 3][list & 1] as i32,
3346                                                );
3347                                                mv[list & 1] =
3348                                                    (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
3349                                            }
3350                                        }
3351                                    }
3352                                    let refi0 = if u0 { sref[p & 3][0] as i32 } else { -1 };
3353                                    let refi1 = if u1 { sref[p & 3][1] as i32 } else { -1 };
3354                                    self.b_set_motion(
3355                                        mbx, mby, px, py, sw, sh, refi0, mv[0], refi1, mv[1],
3356                                    );
3357                                    if rec_mode {
3358                                        self.b_mc_or_record(
3359                                            mbx,
3360                                            mby,
3361                                            px,
3362                                            py,
3363                                            sw,
3364                                            sh,
3365                                            refi0,
3366                                            mv[0],
3367                                            refi1,
3368                                            mv[1],
3369                                            &mut pred_y,
3370                                            &mut c_pred,
3371                                        );
3372                                    } else {
3373                                        self.b_mc(
3374                                            mbx,
3375                                            mby,
3376                                            px,
3377                                            py,
3378                                            sw,
3379                                            sh,
3380                                            refi0,
3381                                            mv[0],
3382                                            refi1,
3383                                            mv[1],
3384                                            &mut pred_y,
3385                                            &mut c_pred,
3386                                        );
3387                                    }
3388                                }
3389                            }
3390                        } else {
3391                            // SINGLE 16x16 B PARTITION => UNIFORM MOTION. `mvmode == 0`
3392                            // is one partition carrying one (ref0, ref1, mv0, mv1)
3393                            // for all sixteen blocks, in BOTH lists -- exactly what
3394                            // the deblock derivation's `mb_uniform` kernel spends six
3395                            // motion planes establishing. It is the class the earlier
3396                            // markings missed: of the calls that survived them, 70-75%
3397                            // still returned TRUE, i.e. found uniformity no syntax
3398                            // source had declared.
3399                            let (layout, mvmode, preds) = b_inter_layout(bmt);
3400                            if mvmode == 0 {
3401                                if let Some(p) = self.mb_umot.get_mut(mby * self.mb_w + mbx) {
3402                                    *p = true;
3403                                }
3404                            }
3405                            let parts: &[(usize, &[usize])] = match mvmode {
3406                                0 => {
3407                                    &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])]
3408                                }
3409                                1 => &[
3410                                    (0, &[0, 1, 2, 3, 4, 5, 6, 7]),
3411                                    (8, &[8, 9, 10, 11, 12, 13, 14, 15]),
3412                                ],
3413                                _ => &[
3414                                    (0, &[0, 1, 2, 3, 8, 9, 10, 11]),
3415                                    (4, &[4, 5, 6, 7, 12, 13, 14, 15]),
3416                                ],
3417                            };
3418                            // ref_idx_l0 for EVERY partition, then ref_idx_l1, then the mvds
3419                            // (spec 7.3.5.1 macroblock_prediction). This was missing entirely
3420                            // -- the B path assumed a single reference -- so any B slice with
3421                            // more than one active reference in either list desynced the
3422                            // arithmetic decoder at the first partition that codes a ref_idx,
3423                            // and the slice ended early at a phantom end_of_slice_flag.
3424                            let mut pref = [[0i8; 2]; 2]; // [partition][list]
3425                            for list in 0..2usize {
3426                                let active = if list == 0 {
3427                                    self.num_ref_active
3428                                } else {
3429                                    self.num_ref_active1
3430                                };
3431                                if active <= 1 {
3432                                    continue;
3433                                }
3434                                let rc = if list == 0 { &mut refc0 } else { &mut refc1 };
3435                                for (p, &(pidx, zb)) in parts.iter().enumerate() {
3436                                    if !preds[p & 1].uses(list) {
3437                                        continue;
3438                                    }
3439                                    let s = CACHE30[pidx & 15].clamp(6, 29);
3440                                    let c0 =
3441                                        (rc[s - 1] > 0) as usize + 2 * (rc[s - 6] > 0) as usize;
3442                                    let r = parse_ref_idx_cabac(&mut cab, c0);
3443                                    // Seed the cache so a later partition's ref/mvd ctxInc sees it.
3444                                    for &zbi in zb.iter() {
3445                                        rc[CACHE30[zbi & 15]] = r;
3446                                    }
3447                                    pref[p & 1][list & 1] = r;
3448                                }
3449                            }
3450                            // mvd parse order: list-major, partition-minor (openh264
3451                            // ParseInterBMotionInfoCabac); the ctxInc reads the same-list cache.
3452                            for list in 0..2usize {
3453                                let (mmv, mrf, mc, rc) = if list == 0 {
3454                                    (&mut mmvd0, &mut mref0, &mut mvdc0, &mut refc0)
3455                                } else {
3456                                    (&mut mmvd1, &mut mref1, &mut mvdc1, &mut refc1)
3457                                };
3458                                for (p, &(pidx, zb)) in parts.iter().enumerate() {
3459                                    if preds[p & 1].uses(list) {
3460                                        parse_mvd_partition(
3461                                            &mut cab,
3462                                            pidx,
3463                                            zb,
3464                                            mc,
3465                                            rc,
3466                                            mmv,
3467                                            mrf,
3468                                            pref[p & 1][list & 1],
3469                                        );
3470                                    }
3471                                }
3472                            }
3473                            // Per-partition recon: predict each list's MV, commit, MC.
3474                            let (mbb_x, mbb_y) = (mbx * 4, mby * 4);
3475                            for (p, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
3476                                let mut mv = [(0i32, 0i32); 2];
3477                                // uses(list) is a property of the layout — resolve both
3478                                // once instead of five times per partition, and build
3479                                // the neighbour coordinates once instead of per list.
3480                                let (u0, u1) = (preds[p & 1].uses(0), preds[p & 1].uses(1));
3481                                let (nx, ny) =
3482                                    ((mbb_x + rx / 4) as isize, (mbb_y + ry / 4) as isize);
3483                                let (nw, didx) = ((rw / 4) as isize, (ry / 4) * 4 + rx / 4);
3484                                // Bi partitions gather both lists at ONE position —
3485                                // fuse the availability work (same trick as
3486                                // b_direct_nbrs); uni partitions keep the single
3487                                // gather.
3488                                if u0 && u1 {
3489                                    let (n0, n1) = self.mv_neighbors_both(nx, ny, nw);
3490                                    for (list, n) in [(0usize, &n0), (1, &n1)] {
3491                                        let d =
3492                                            (if list == 0 { &mmvd0 } else { &mmvd1 })[didx & 15];
3493                                        let pmv = predict_partition_mv(
3494                                            mvmode,
3495                                            p,
3496                                            n[0],
3497                                            n[1],
3498                                            n[2],
3499                                            pref[p & 1][list & 1] as i32,
3500                                        );
3501                                        mv[list & 1] = (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
3502                                    }
3503                                } else {
3504                                    for list in 0..2usize {
3505                                        if if list == 0 { u0 } else { u1 } {
3506                                            let d = (if list == 0 { &mmvd0 } else { &mmvd1 })
3507                                                [didx & 15];
3508                                            let n = self.mv_neighbors_list(nx, ny, nw, list);
3509                                            let pmv = predict_partition_mv(
3510                                                mvmode,
3511                                                p,
3512                                                n[0],
3513                                                n[1],
3514                                                n[2],
3515                                                pref[p & 1][list & 1] as i32,
3516                                            );
3517                                            mv[list & 1] =
3518                                                (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
3519                                        }
3520                                    }
3521                                }
3522                                let refi0 = if u0 { pref[p & 1][0] as i32 } else { -1 };
3523                                let refi1 = if u1 { pref[p & 1][1] as i32 } else { -1 };
3524                                self.b_set_motion(
3525                                    mbx, mby, rx, ry, rw, rh, refi0, mv[0], refi1, mv[1],
3526                                );
3527                                // Proper spec bi-prediction (average of L0+L1). NOTE: the CAVLC
3528                                // decode_b_mb replicates an openh264 bug here for a Bi 16×8/8×16
3529                                // partition; our pixel gate is ffmpeg (spec-correct), so we do NOT.
3530                                if rec_mode {
3531                                    self.b_mc_or_record(
3532                                        mbx,
3533                                        mby,
3534                                        rx,
3535                                        ry,
3536                                        rw,
3537                                        rh,
3538                                        refi0,
3539                                        mv[0],
3540                                        refi1,
3541                                        mv[1],
3542                                        &mut pred_y,
3543                                        &mut c_pred,
3544                                    );
3545                                } else {
3546                                    self.b_mc(
3547                                        mbx,
3548                                        mby,
3549                                        rx,
3550                                        ry,
3551                                        rw,
3552                                        rh,
3553                                        refi0,
3554                                        mv[0],
3555                                        refi1,
3556                                        mv[1],
3557                                        &mut pred_y,
3558                                        &mut c_pred,
3559                                    );
3560                                }
3561                            }
3562                        }
3563                        // Paired stores: one index expression per grid pair.
3564                        // Four per-macroblock grids at one address; each is its own
3565                        // Vec, so each assignment carried its own check.
3566                        if let (Some(r0), Some(d0)) = (mb_ref.get_mut(addr), mb_mvd.get_mut(addr)) {
3567                            (*r0, *d0) = (mref0, sat_mvd(mmvd0));
3568                        }
3569                        if let (Some(r1), Some(d1)) = (mb_ref1.get_mut(addr), mb_mvd1.get_mut(addr))
3570                        {
3571                            (*r1, *d1) = (mref1, sat_mvd(mmvd1));
3572                        }
3573                        _smv = None; // close b:mvd-parse; the residual half follows
3574                        let _sres =
3575                            rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BResid);
3576
3577                        // Inter cbp + residual (identical to the P path).
3578                        let cbp = parse_cbp_cabac(
3579                            &mut cab,
3580                            top.and_then(|a| mb_cbp.get(a).copied()),
3581                            left.and_then(|a| mb_cbp.get(a).copied()),
3582                        );
3583                        if let Some(p) = mb_cbp.get_mut(addr) {
3584                            *p = cbp as u8;
3585                        }
3586                        // H-49: an INTER macroblock carries transform_size_8x8_flag AFTER cbp
3587                        // (spec 7.3.5), present only when CodedBlockPatternLuma > 0 and
3588                        // noSubMbPartSizeLessThan8x8Flag. Same context as the intra read.
3589                        let t8 = self.transform_8x8_mode && (cbp & 15) != 0 && allow8 && {
3590                            let a = left
3591                                .map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
3592                            let b =
3593                                top.map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
3594                            cab.decode_decision(399 + a + b) != 0
3595                        };
3596                        if let Some(p) = self.mb_t8x8.get_mut(addr) {
3597                            *p = t8;
3598                        }
3599                        self.any_t8 |= t8;
3600                        // D9c-B: coded B with cbp==0 never parses residuals — skip the
3601                        // ~2.5 KB coeff zero-init + fat BJob when NORES is on (default).
3602                        // MC already filled pred_y / edc_regions; recon == pred (same as
3603                        // B_Skip / P InterNoRes). t8 is always false here ((cbp&15)==0).
3604                        if cbp == 0 && nores_on() {
3605                            last_delta_qp = 0;
3606                            if let Some(p) = self.mb_qp.get_mut(addr) {
3607                                *p = self.cur_qp;
3608                            }
3609                            if let Some(p) = cbf_dc.get_mut(addr) {
3610                                *p = 0;
3611                            }
3612                            if let Some(p) = mb_nzc.get_mut(addr) {
3613                                *p = [0u8; 24];
3614                            }
3615                            if let Some(regions) = self.edc_regions.take() {
3616                                self.edc_giveback();
3617                                self.edc_commit_nnz(mbx, mby, false, &[0u8; 24], 0);
3618                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
3619                                self.edc_send_job(EdcJob::BSkip { mbx, mby, regions });
3620                            } else {
3621                                // Inline twin of decode_b_skip's plane copy (MC already done).
3622                                for dy in 0..16 {
3623                                    let d = (mby * 16 + dy) * self.cw + mbx * 16;
3624                                    self.rec_y[d..d + 16]
3625                                        .copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
3626                                }
3627                                for c in 0..2 {
3628                                    let plane = if c == 0 {
3629                                        &mut self.rec_u
3630                                    } else {
3631                                        &mut self.rec_v
3632                                    };
3633                                    for dy in 0..8 {
3634                                        let d = (mby * 8 + dy) * self.ccw + mbx * 8;
3635                                        plane[d..d + 8]
3636                                            .copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
3637                                    }
3638                                }
3639                                let w4 = self.mb_w * 4;
3640                                for dy in 0..4 {
3641                                    self.nnz_y[(mby * 4 + dy) * w4 + mbx * 4..][..4].fill(0);
3642                                }
3643                            }
3644                            let eos = cab.decode_terminate();
3645                            addr += 1;
3646                            mbx += 1;
3647                            if eos || addr >= total {
3648                                break;
3649                            }
3650                            continue;
3651                        }
3652                        // Scratch planes (see `scratch_luma`): zeroed per coded block by the parse.
3653                        let mut luma8 = self
3654                            .scratch_luma8
3655                            .take()
3656                            .unwrap_or_else(|| Box::new([[0i32; 64]; 4]));
3657                        let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
3658                        let mut nzc = [0xffu8; 48];
3659                        if let Some(t) = top {
3660                            let tnz = mb_nzc.get(t).unwrap_or(&ZERO_NZC);
3661                            nzc[1..5].copy_from_slice(&tnz[12..16]);
3662                            (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
3663                            (nzc[6], nzc[7], nzc[30], nzc[31]) =
3664                                (tnz[20], tnz[21], tnz[22], tnz[23]);
3665                        }
3666                        if let Some(l) = left {
3667                            let lnz = mb_nzc.get(l).unwrap_or(&ZERO_NZC);
3668                            (nzc[8], nzc[16], nzc[24], nzc[32]) =
3669                                (lnz[3], lnz[7], lnz[11], lnz[15]);
3670                            (nzc[13], nzc[21], nzc[37], nzc[45]) =
3671                                (lnz[17], lnz[21], lnz[19], lnz[23]);
3672                        }
3673                        let mut cbfdc = 0u16;
3674                        let mut nnzs = [0u8; 24]; // parsed totalCoeff per block
3675                        let mut luma_scan = self
3676                            .scratch_luma
3677                            .take()
3678                            .unwrap_or_else(|| Box::new([[0i32; 16]; 16]));
3679                        let mut cdc = [[0i32; 4]; 2];
3680                        let mut cac = self
3681                            .scratch_cac
3682                            .take()
3683                            .unwrap_or_else(|| Box::new([[[0i32; 16]; 4]; 2]));
3684                        if cbp == 0 {
3685                            last_delta_qp = 0;
3686                        }
3687                        if cbp != 0 {
3688                            let ndc = (
3689                                top.and_then(|a| cbf_dc.get(a).copied()),
3690                                left.and_then(|a| cbf_dc.get(a).copied()),
3691                            );
3692                            let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
3693                            self.step_qp(qpd)?;
3694                            parse_mb_residual_cabac::<false>(
3695                                &mut cab,
3696                                &mut nzc,
3697                                &mut cbfdc,
3698                                ndc,
3699                                cbp_luma,
3700                                cbp_chroma,
3701                                t8,
3702                                ResidualOut {
3703                                    luma: &mut luma_scan,
3704                                    luma8: &mut luma8,
3705                                    cdc: &mut cdc,
3706                                    cac: &mut cac,
3707                                    nnzs: &mut nnzs,
3708                                },
3709                            );
3710                        }
3711                        if let Some(p) = self.mb_qp.get_mut(addr) {
3712                            *p = self.cur_qp;
3713                        }
3714                        if let Some(p) = cbf_dc.get_mut(addr) {
3715                            *p = cbfdc;
3716                        }
3717                        let _sc = rusty_h264_common::prof::scope(
3718                            rusty_h264_common::prof::Stage::DecStateCache,
3719                        );
3720                        let mn = nnz_raster_from_z(&nnzs);
3721                        if let Some(p) = mb_nzc.get_mut(addr) {
3722                            *p = mn;
3723                        }
3724                        drop(_sc);
3725                        if let Some(regions) = self.edc_regions.take() {
3726                            self.edc_giveback();
3727                            self.edc_commit_nnz(mbx, mby, t8, &nnzs, cbp_chroma);
3728                            let job = BJob {
3729                                mbx,
3730                                mby,
3731                                qp: self.cur_qp,
3732                                cbp_chroma,
3733                                skip: false,
3734                                regions,
3735                                // MT-only copies out of the scratch planes (the worker owns its job).
3736                                luma_scan: (!t8 && cbp_luma != 0).then_some(*luma_scan),
3737                                luma8: t8.then_some(*luma8),
3738                                cdc,
3739                                cac: (cbp_chroma == 2).then_some(*cac),
3740                                nnzs,
3741                            };
3742                            self.edc_send_job(EdcJob::B(Box::new(job)));
3743                        } else {
3744                            self.add_inter_residual(
3745                                mbx,
3746                                mby,
3747                                &pred_y,
3748                                &c_pred,
3749                                Some(&*luma_scan),
3750                                t8.then_some(&*luma8),
3751                                &cdc,
3752                                (cbp_chroma == 2).then_some(&*cac),
3753                                cbp_chroma,
3754                                &nnzs,
3755                            );
3756                        }
3757                        self.scratch_luma = Some(luma_scan);
3758                        self.scratch_luma8 = Some(luma8);
3759                        self.scratch_cac = Some(cac);
3760
3761                        let eos = cab.decode_terminate();
3762                        addr += 1;
3763                        mbx += 1;
3764                        if eos || addr >= total {
3765                            break;
3766                        }
3767                        continue;
3768                    }
3769                    bmt - 23 // 23→0 (I_4x4), 24..=47→1..24 (I_16x16), 48→25 (PCM)
3770                } else {
3771                    let li = left.map_or(0, |a| cat.get(a).is_some_and(|&c| c >= 2) as usize);
3772                    let ti = top.map_or(0, |a| cat.get(a).is_some_and(|&c| c >= 2) as usize);
3773                    parse_mb_type_i_cabac(&mut cab, li + ti)
3774                };
3775                // H-48: the CABAC intra path is INLINED in this loop, not routed through
3776                // `decode_intra_mb` (which only the CAVLC readers call) — wiring the scope
3777                // there reported ZERO calls against 480,510 intra-pred calls. All three
3778                // intra entries (I-slice, P-slice mb_type>3, B-slice bmt>=23) converge
3779                // here, so this is the one point that sees every intra MB.
3780                self.edc_intra_sync(); // intra reconstruction reads neighbour PIXELS
3781                                       // Intra prediction ALSO reads the coded_y/modes_y/inter_y grids a
3782                                       // deferred span still owes (this arm is inline — it never passes
3783                                       // decode_b_mb's flush). Caught by the tempete ARM-DIFF.
3784                self.span_flush();
3785                let _gi = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbI);
3786                // Intra bS is a constant pattern (4 on MB edges, 3 internal) — written
3787                // here because CABAC inlines I recon and never calls decode_intra_mb.
3788                if let Some(p) = self.mb_kind.get_mut(mby * self.mb_w + mbx) {
3789                    *p = rusty_h264_common::deblock::MB_KIND_INTRA;
3790                }
3791                if mb_type == 25 {
3792                    // ---- I_PCM (spec §7.3.5): 384 raw byte-aligned sample bytes inside
3793                    // the CABAC stream. The PCM marker was a terminate bin, so the engine
3794                    // has stopped; DecodeFlush + pcm_alignment_zero_bit put the samples
3795                    // at `pcm_start_byte()`, and the engine re-initialises after them
3796                    // with its CONTEXTS KEPT (§9.3.1). All three slice types (I, P via
3797                    // mbt 30, B via bmt 48) reach here as mb_type 25.
3798                    let pcm = cab.pcm_start_byte();
3799                    let end = pcm + 384;
3800                    if end > rbsp.len() {
3801                        return Err(MbError::Truncated);
3802                    }
3803                    let mut pr = BitReader::new(&rbsp[pcm..end]);
3804                    self.decode_ipcm(&mut pr, mbx, mby)?;
3805                    cab.reinit_at(end);
3806                    // Neighbour context (§7.4.5 + §9.3.3.1.1.x inferences): intra, QPy
3807                    // unchanged (no mb_qp_delta — its ctxInc resets), CodedBlockPattern
3808                    // luma/chroma inferred 15/2, every coded_block_flag (incl. DC) 1,
3809                    // nnz 16, chroma pred mode 0.
3810                    if let Some(p) = self.mb_qp.get_mut(addr) {
3811                        *p = self.cur_qp;
3812                    }
3813                    if let Some(p) = cat.get_mut(addr) {
3814                        *p = 25;
3815                    }
3816                    if let Some(p) = cmode.get_mut(addr) {
3817                        *p = 0;
3818                    }
3819                    if let Some(p) = mb_cbp.get_mut(addr) {
3820                        *p = 0x2f;
3821                    }
3822                    if let Some(p) = cbf_dc.get_mut(addr) {
3823                        *p = (1 << RP_I16_DC) | (1 << RP_CHROMA_DC) | (1 << (RP_CHROMA_DC + 1));
3824                    }
3825                    if let Some(p) = mb_nzc.get_mut(addr) {
3826                        *p = [16u8; 24];
3827                    }
3828                    last_delta_qp = 0;
3829                    let eos = cab.decode_terminate();
3830                    addr += 1;
3831                    mbx += 1;
3832                    if eos || addr >= total {
3833                        break;
3834                    }
3835                    continue;
3836                }
3837                // chroma-pred-mode ctxInc from neighbour chroma modes (1..=3).
3838                let cci = left.map_or(0, |a| {
3839                    cmode.get(a).is_some_and(|c| (1..=3).contains(c)) as usize
3840                }) + top.map_or(0, |a| {
3841                    cmode.get(a).is_some_and(|c| (1..=3).contains(c)) as usize
3842                });
3843
3844                if mb_type != 0 {
3845                    // ---- I_16x16 (mb_type 1..=24): pred mode & cbp DERIVED from mb_type;
3846                    // luma DC always coded. Syntax order: intra_chroma_pred_mode, mb_qp_delta,
3847                    // luma DC (Hadamard), luma AC (if cbp_luma), chroma DC/AC. Mirrors the CAVLC
3848                    // decode_i16, driven by the CABAC residual. ----
3849                    let mt = mb_type - 1;
3850                    let pred_mode = I16Mode::from_id(mt % 4);
3851                    let cbp_chroma = (mt % 12) / 4;
3852                    let cbp_luma_15 = mt / 12 == 1;
3853                    let chroma_mode = parse_intra_chroma_pred_mode_cabac(&mut cab, cci) as u8;
3854                    if let Some(p) = cmode.get_mut(addr) {
3855                        *p = chroma_mode as i8;
3856                    }
3857                    if let Some(p) = cat.get_mut(addr) {
3858                        *p = 2;
3859                    }
3860                    if let Some(p) = mb_cbp.get_mut(addr) {
3861                        *p = ((cbp_chroma as u8) << 4) | if cbp_luma_15 { 15 } else { 0 };
3862                    }
3863                    let w4 = self.mb_w * 4;
3864
3865                    let mut nzc = [0xffu8; 48];
3866                    if let Some(t) = top {
3867                        let tn = mb_nzc.get(t).unwrap_or(&ZERO_NZC);
3868                        nzc[1..5].copy_from_slice(&tn[12..16]);
3869                        (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
3870                        (nzc[6], nzc[7]) = (tn[20], tn[21]);
3871                        (nzc[30], nzc[31]) = (tn[22], tn[23]);
3872                    }
3873                    if let Some(l) = left {
3874                        let ln = mb_nzc.get(l).unwrap_or(&ZERO_NZC);
3875                        (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
3876                        (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
3877                    }
3878
3879                    let ndc = (
3880                        top.and_then(|a| cbf_dc.get(a).copied()),
3881                        left.and_then(|a| cbf_dc.get(a).copied()),
3882                    );
3883                    let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
3884                    self.step_qp(qpd)?;
3885                    let qp = self.cur_qp;
3886                    let mut cbfdc = 0u16;
3887                    let nd = resolve_ndc(ndc, true);
3888                    let (data, mut e, ctx) = cab.view();
3889
3890                    // Luma DC (iz=0, category I16_LUMA_DC, 16 coeffs) → Hadamard dequant.
3891                    let mut dc_scan = [0i32; 16];
3892                    residual_block_eng::<RP_I16_DC, 16>(
3893                        &mut e,
3894                        data,
3895                        ctx,
3896                        &mut nzc,
3897                        &mut cbfdc,
3898                        0,
3899                        0,
3900                        true,
3901                        nd,
3902                        &mut dc_scan,
3903                    );
3904                    // FUSED scan-order DC: un-scan + Hadamard + scale in one kernel (was 32 moves + 253 instrs).
3905                    let recon_dc = inverse_quant_luma_dc_scan(
3906                        &dc_scan,
3907                        qp,
3908                        self.scaling.as_ref().map(|s| s[0][0]),
3909                    );
3910
3911                    // Luma AC (iz 0..15, category I16_LUMA_AC, 15 coeffs) when cbp_luma set.
3912                    // Materialised only when AC is actually coded — a DC-only
3913                    // I_16x16 zeroed 1 KB of stack for nothing.
3914                    let mut q_blocks: Option<[[i32; 16]; 16]> = None;
3915                    let mut coded16 = 0u16;
3916                    for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3917                        let total = if cbp_luma_15 {
3918                            let mut ac = [0i32; 16];
3919                            let t = residual_block_eng::<RP_I16_AC, 16>(
3920                                &mut e, data, ctx, &mut nzc, &mut cbfdc, iz, 0, true, nd, &mut ac,
3921                            );
3922                            q_blocks.get_or_insert([[0i32; 16]; 16])
3923                                [(lby & 3) * 4 + (lbx & 3)] = ac; // SCAN order (fused kernel)
3924                            t as u8
3925                        } else {
3926                            nzc[NZC_CACHE[iz.min(23)].min(47)] = 0;
3927                            0
3928                        };
3929                        coded16 |= ((total != 0) as u16) << ((lby & 3) * 4 + (lbx & 3));
3930                        if let Some(p) = self.nnz_y.get_mut((mby * 4 + lby) * w4 + (mbx * 4 + lbx))
3931                        {
3932                            *p = total;
3933                        }
3934                    }
3935
3936                    let mut cdc = [[0i32; 4]; 2];
3937                    let mut cac: Option<[[[i32; 16]; 4]; 2]> = None;
3938                    let mut cnnz = [0u8; 8];
3939                    if cbp_chroma >= 1 {
3940                        for i in 0..2usize {
3941                            residual_block_eng::<RP_CHROMA_DC, 4>(
3942                                &mut e,
3943                                data,
3944                                ctx,
3945                                &mut nzc,
3946                                &mut cbfdc,
3947                                16 + i * 4,
3948                                i,
3949                                true,
3950                                nd,
3951                                &mut cdc[i],
3952                            );
3953                        }
3954                    }
3955                    if cbp_chroma == 2 {
3956                        let cacm = cac.get_or_insert([[[0i32; 16]; 4]; 2]);
3957                        for i in 0..2usize {
3958                            for id4 in 0..4usize {
3959                                cnnz[(i * 4 + id4) & 7] = residual_block_eng::<RP_CHROMA_AC, 16>(
3960                                    &mut e,
3961                                    data,
3962                                    ctx,
3963                                    &mut nzc,
3964                                    &mut cbfdc,
3965                                    16 + i * 4 + id4,
3966                                    i,
3967                                    true,
3968                                    nd,
3969                                    &mut cacm[i][id4],
3970                                ) as u8;
3971                            }
3972                        }
3973                    }
3974
3975                    cab.commit(e);
3976                    // Luma recon: 16×16 intra prediction, then per-4×4 (dequant AC + injected DC).
3977                    let top_ok = mby > 0
3978                        && self.nbr_in_slice(mbx, mby - 1)
3979                        && self.intra_nbr_ok(mbx * 4, mby * 4 - 1);
3980                    let left_ok = mbx > 0
3981                        && self.nbr_in_slice(mbx - 1, mby)
3982                        && self.intra_nbr_ok(mbx * 4 - 1, mby * 4);
3983                    self.recon_i16_luma(
3984                        mbx,
3985                        mby,
3986                        pred_mode,
3987                        top_ok,
3988                        left_ok,
3989                        q_blocks.as_ref(),
3990                        coded16,
3991                        &recon_dc,
3992                        qp,
3993                    );
3994                    self.recon_chroma_cabac(
3995                        mbx,
3996                        mby,
3997                        chroma_mode,
3998                        &cdc,
3999                        cac.as_ref(),
4000                        &cnnz,
4001                        cbp_chroma,
4002                        top_ok,
4003                        left_ok,
4004                    );
4005
4006                    if let Some(p) = self.mb_qp.get_mut(addr) {
4007                        *p = self.cur_qp;
4008                    }
4009                    if let Some(p) = cbf_dc.get_mut(addr) {
4010                        *p = cbfdc;
4011                    }
4012                    let mut mn = [0u8; 24];
4013                    // Four CONTIGUOUS runs of four -- slice copies, not sixteen
4014                    // bounds-checked element loads.
4015                    mn[0..4].copy_from_slice(&nzc[9..13]);
4016                    mn[4..8].copy_from_slice(&nzc[17..21]);
4017                    mn[8..12].copy_from_slice(&nzc[25..29]);
4018                    mn[12..16].copy_from_slice(&nzc[33..37]);
4019                    (mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
4020                    (mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
4021                    for v in mn.iter_mut() {
4022                        if *v == 0xff {
4023                            *v = 0;
4024                        }
4025                    }
4026                    if let Some(p) = mb_nzc.get_mut(addr) {
4027                        *p = mn;
4028                    }
4029
4030                    let eos = cab.decode_terminate();
4031                    addr += 1;
4032                    mbx += 1;
4033                    if eos || addr >= total {
4034                        break;
4035                    }
4036                    continue;
4037                }
4038                if let Some(p) = cat.get_mut(addr) {
4039                    *p = 0;
4040                }
4041                let w4 = self.mb_w * 4;
4042                // H-49: transform_size_8x8_flag. For I_NxN it precedes the intra pred
4043                // modes (spec §7.3.5); ctxIdx = 399 + condTermFlagA + condTermFlagB,
4044                // each 1 when that neighbour MB carries the flag. Omitting this read is
4045                // what desynced every High-profile stream.
4046                let t8 = self.transform_8x8_mode && {
4047                    let a = left.map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
4048                    let b = top.map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
4049                    cab.decode_decision(399 + a + b) != 0
4050                };
4051                if let Some(p) = self.mb_t8x8.get_mut(addr) {
4052                    *p = t8;
4053                }
4054                self.any_t8 |= t8;
4055                // Hoisted ABOVE the mode loop (it was computed after it) so the
4056                // per-block mode prediction can use it - see `predict_i4_mode_fast`.
4057                let top_ok = mby > 0
4058                    && self.nbr_in_slice(mbx, mby - 1)
4059                    && self.intra_nbr_ok(mbx * 4, mby * 4 - 1);
4060                let left_ok = mbx > 0
4061                    && self.nbr_in_slice(mbx - 1, mby)
4062                    && self.intra_nbr_ok(mbx * 4 - 1, mby * 4);
4063                // Brick 2.4 + recon: derive & store each intra mode (prev-flag → the
4064                // neighbour-predicted mode, else rem), exactly as the CAVLC path.
4065                let mut modes = [2u8; 16]; // raster [lby*4+lbx]
4066                let mut modes8 = [2u8; 4]; // one per 8×8 when t8
4067                                           // ONE engine view for all 16 (or 4) pred-mode reads of the macroblock.
4068                let (data, mut e, ctx) = cab.view();
4069                if t8 {
4070                    // One mode per 8×8, broadcast to its four 4×4 cells so neighbour
4071                    // mode prediction keeps working unchanged.
4072                    for b8 in 0..4usize {
4073                        let (b8x, b8y) = (b8 % 2, b8 / 2);
4074                        let (bx, by) = (mbx * 4 + b8x * 2, mby * 4 + b8y * 2);
4075                        let predicted =
4076                            self.predict_i4_mode_fast(bx, by, b8x * 2, b8y * 2, top_ok, left_ok);
4077                        let rr = intra4x4_pred_mode_eng(&mut e, data, ctx);
4078                        let actual = if rr < 0 {
4079                            predicted
4080                        } else {
4081                            let rem = rr as u8;
4082                            if rem < predicted {
4083                                rem
4084                            } else {
4085                                rem + 1
4086                            }
4087                        };
4088                        modes8[b8] = actual;
4089                        for dy in 0..2 {
4090                            for dx in 0..2 {
4091                                if let Some(p) = self.modes_y.get_mut((by + dy) * w4 + (bx + dx)) {
4092                                    *p = actual;
4093                                }
4094                                modes[(b8y * 2 + dy) * 4 + (b8x * 2 + dx)] = actual;
4095                            }
4096                        }
4097                    }
4098                } else {
4099                    for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
4100                        let (bx, by) = (mbx * 4 + lbx, mby * 4 + lby);
4101                        let predicted =
4102                            self.predict_i4_mode_fast(bx, by, lbx, lby, top_ok, left_ok);
4103                        let rr = intra4x4_pred_mode_eng(&mut e, data, ctx);
4104                        let actual = if rr < 0 {
4105                            predicted
4106                        } else {
4107                            let rem = rr as u8;
4108                            if rem < predicted {
4109                                rem
4110                            } else {
4111                                rem + 1
4112                            }
4113                        };
4114                        if let Some(m) = self.modes_y.get_mut(by * w4 + bx) {
4115                            *m = actual;
4116                        }
4117                        modes[(lby & 3) * 4 + (lbx & 3)] = actual;
4118                    }
4119                }
4120                cab.commit(e);
4121                let chroma_mode = parse_intra_chroma_pred_mode_cabac(&mut cab, cci) as u8;
4122                if let Some(p) = cmode.get_mut(addr) {
4123                    *p = chroma_mode as i8;
4124                }
4125                let cbp = parse_cbp_cabac(
4126                    &mut cab,
4127                    top.and_then(|a| mb_cbp.get(a).copied()),
4128                    left.and_then(|a| mb_cbp.get(a).copied()),
4129                );
4130                if let Some(p) = mb_cbp.get_mut(addr) {
4131                    *p = cbp as u8;
4132                }
4133                let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
4134
4135                // Build the padded nzc cache from neighbours (openh264 WelsFillCacheNonZeroCount).
4136                let mut nzc = [0xffu8; 48];
4137                if let Some(t) = top {
4138                    let tn = mb_nzc.get(t).unwrap_or(&ZERO_NZC);
4139                    nzc[1..5].copy_from_slice(&tn[12..16]);
4140                    (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
4141                    (nzc[6], nzc[7]) = (tn[20], tn[21]);
4142                    (nzc[30], nzc[31]) = (tn[22], tn[23]);
4143                }
4144                if let Some(l) = left {
4145                    let ln = mb_nzc.get(l).unwrap_or(&ZERO_NZC);
4146                    (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
4147                    (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
4148                }
4149
4150                // Bricks 2.6 + 2.7: mb_qp_delta + residual (I_4x4 luma 4×4 + chroma DC/AC),
4151                // storing scan-order coefficients for recon.
4152                let mut cbfdc = 0u16;
4153                // Both materialised only when the parse writes them: an I_8x8
4154                // macroblock never touches luma_scan (it carries luma8), and
4155                // cbp_chroma < 2 never touches cac.
4156                // Scratch planes (see `scratch_luma`): zeroed per coded block by the parse.
4157                let mut luma_scan = self
4158                    .scratch_luma
4159                    .take()
4160                    .unwrap_or_else(|| Box::new([[0i32; 16]; 16]));
4161                let mut luma8 = self
4162                    .scratch_luma8
4163                    .take()
4164                    .unwrap_or_else(|| Box::new([[0i32; 64]; 4]));
4165                let mut cdc = [[0i32; 4]; 2];
4166                let mut cac = self
4167                    .scratch_cac
4168                    .take()
4169                    .unwrap_or_else(|| Box::new([[[0i32; 16]; 4]; 2]));
4170                let mut nnzs = [0u8; 24]; // parse-side per-block coeff counts
4171                if cbp == 0 {
4172                    last_delta_qp = 0;
4173                }
4174                if cbp != 0 {
4175                    let ndc = (
4176                        top.and_then(|a| cbf_dc.get(a).copied()),
4177                        left.and_then(|a| cbf_dc.get(a).copied()),
4178                    );
4179                    let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
4180                    self.step_qp(qpd)?;
4181                    parse_mb_residual_cabac::<true>(
4182                        &mut cab,
4183                        &mut nzc,
4184                        &mut cbfdc,
4185                        ndc,
4186                        cbp_luma,
4187                        cbp_chroma,
4188                        t8,
4189                        ResidualOut {
4190                            luma: &mut luma_scan,
4191                            luma8: &mut luma8,
4192                            cdc: &mut cdc,
4193                            cac: &mut cac,
4194                            nnzs: &mut nnzs,
4195                        },
4196                    );
4197                    if t8 {
4198                        // Per-cell nnz for the deblock/neighbour readers: the 8x8 total
4199                        // (0 for an uncoded 8x8) in each of its four 4x4 cells.
4200                        for id8 in 0..4usize {
4201                            let n = nnzs[id8 * 4];
4202                            let (b8x, b8y) = (id8 % 2, id8 / 2);
4203                            for sy in 0..2 {
4204                                for sx in 0..2 {
4205                                    if let Some(p) = self.nnz_y.get_mut(
4206                                        (mby * 4 + b8y * 2 + sy) * w4 + (mbx * 4 + b8x * 2 + sx),
4207                                    ) {
4208                                        *p = n;
4209                                    }
4210                                }
4211                            }
4212                        }
4213                    }
4214                }
4215                if let Some(p) = self.mb_qp.get_mut(addr) {
4216                    *p = self.cur_qp;
4217                }
4218                if let Some(p) = cbf_dc.get_mut(addr) {
4219                    *p = cbfdc;
4220                }
4221                // Extract the MB's nzc (raster luma + chroma) for future neighbours.
4222                let mn = nnz_raster_from_z(&nnzs);
4223                if let Some(p) = mb_nzc.get_mut(addr) {
4224                    *p = mn;
4225                }
4226
4227                // ---- Brick 4.3a: recon (I_4x4 luma + chroma) via the CAVLC-proven primitives.
4228                let qp = self.cur_qp;
4229
4230                if t8 {
4231                    // I_8x8 recon, reusing the CAVLC-proven primitives verbatim
4232                    // (un_scan_8x8 / inv_quant8 / gather_i8 / intra8x8_pred /
4233                    // add_residual_8x8). Only the ENTROPY half differed.
4234                    for b8 in 0..4usize {
4235                        let (b8x, b8y) = (b8 % 2, b8 / 2);
4236                        let (bx, by) = (mbx * 4 + b8x * 2, mby * 4 + b8y * 2);
4237                        let coded = cbp_luma & (1 << b8) != 0;
4238                        let avail_top = b8y > 0 || top_ok;
4239                        let avail_left = b8x > 0 || left_ok;
4240                        self.recon_i8_block(
4241                            bx,
4242                            by,
4243                            modes8[b8],
4244                            avail_top,
4245                            avail_left,
4246                            coded.then_some(&luma8[b8]),
4247                            qp,
4248                        );
4249                        for sy in 0..2 {
4250                            // Row fill: the 2x2 cell block is two contiguous PAIRS.
4251                            self.coded_y[(by + sy) * w4 + bx..][..2].fill(true);
4252                        }
4253                    }
4254                }
4255                // The `t8` guard was INSIDE the loop (breaking on the first
4256                // iteration) and the `luma_scan` Option was re-resolved on EVERY one
4257                // of the sixteen blocks. Both are macroblock-invariant.
4258                if !t8 {
4259                    let scans = &*luma_scan;
4260                    // Residual ladders decided up front (`i4_prepare`), then the serial
4261                    // predict+add walk with the SIMD IDCT+add kernel per coded block.
4262                    let lnnz: [u8; 16] = core::array::from_fn(|i| nnzs[i]);
4263                    let kinds = self.i4_prepare(scans, &lnnz, qp);
4264                    let dq0 = self.dq_const(qp, 0);
4265                    for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
4266                        let (bx, by) = (mbx * 4 + lbx, mby * 4 + lby);
4267                        let at = lby > 0 || top_ok;
4268                        let al = lbx > 0 || left_ok;
4269                        self.recon_i4_block_res(
4270                            bx,
4271                            by,
4272                            modes[(lby & 3) * 4 + (lbx & 3)],
4273                            at,
4274                            al,
4275                            kinds[blk & 15],
4276                            scans,
4277                            &dq0,
4278                        );
4279                    }
4280                    // Per-MB ROW COPIES for nnz_y (from the raster `mn` already built) and
4281                    // coded_y (routing round): sixteen + sixteen checked scattered stores
4282                    // per macroblock -> four + four row operations. Nothing inside this
4283                    // macroblock reads either grid for its own blocks (interior top-right
4284                    // availability is the z-order constant now).
4285                    for ry in 0..4usize {
4286                        let a = (mby * 4 + ry) * w4 + mbx * 4;
4287                        self.nnz_y[a..a + 4].copy_from_slice(&mn[ry * 4..ry * 4 + 4]);
4288                        self.coded_y[a..a + 4].fill(true);
4289                    }
4290                }
4291                // `nnzs` is a fixed `[u8; 24]`, so its last eight ARE a sub-array:
4292                // build it by index instead of a fallible slice conversion, which
4293                // the compiler cannot see is infallible.
4294                let chroma_nnz: [u8; 8] = core::array::from_fn(|i| nnzs[16 + i]);
4295                self.recon_chroma_cabac(
4296                    mbx,
4297                    mby,
4298                    chroma_mode,
4299                    &cdc,
4300                    (cbp_chroma == 2).then_some(&*cac),
4301                    &chroma_nnz,
4302                    cbp_chroma,
4303                    top_ok,
4304                    left_ok,
4305                );
4306                self.scratch_luma = Some(luma_scan);
4307                self.scratch_luma8 = Some(luma8);
4308                self.scratch_cac = Some(cac);
4309
4310                // Brick 2.1: end_of_slice_flag.
4311                let eos = cab.decode_terminate();
4312                addr += 1;
4313                mbx += 1;
4314                if eos || addr >= total {
4315                    break;
4316                }
4317            }
4318        }
4319        if trace {
4320            eprintln!("# CABAC decoded {} MBs (of {total})", addr - first_mb);
4321        }
4322        self.edc_flush(); // slice end: no job crosses a slice boundary
4323                          // Return the pooled slice scratch (an error exit forfeits it — the
4324                          // next slice then refills fresh allocations, correctness unchanged).
4325        self.sc_cat = cat;
4326        self.sc_cbp = mb_cbp;
4327        self.sc_cmode = cmode;
4328        self.sc_nzc = mb_nzc;
4329        self.sc_cbfdc = cbf_dc;
4330        self.sc_skip = mb_skip;
4331        self.sc_ref = mb_ref;
4332        self.sc_mvd = mb_mvd;
4333        if want_b_grids {
4334            self.sc_ref1 = mb_ref1;
4335            self.sc_mvd1 = mb_mvd1;
4336            self.sc_direct = mb_direct;
4337        }
4338        Ok(addr)
4339    }
4340
4341    /// CABAC chroma recon (mirrors `decode_chroma`'s reconstruction, driven by the
4342    /// CABAC-parsed DC/AC coefficients). `cdc[c]` = 2×2 DC (scan order); `cac[c][blk]`
4343    /// = 15 AC per 4×4 block (scan order).
4344    /// Add a CABAC-parsed inter residual to an already-built motion-comp prediction
4345    /// (`pred_y`/`c_pred`), writing the reconstruction. Shared by the P and B inter
4346    /// paths — same `reconstruct_4x4` as intra, MC output as the prediction, inter
4347    /// scaling lists (luma 3 / chroma 4+c). `luma_scan[z]`/`cdc`/`cac` are the
4348    /// scan-order coefficients; uncoded blocks are zero so recon == prediction.
4349    fn add_inter_residual(
4350        &mut self,
4351        mb_x: usize,
4352        mb_y: usize,
4353        pred_y: &[u8; 256],
4354        c_pred: &[[u8; 64]; 2],
4355        luma_scan: Option<&[[i32; 16]; 16]>,
4356        // `Some` when the macroblock carries transform_size_8x8_flag: four 8x8
4357        // blocks in 8x8 scan order, replacing the sixteen 4x4 luma blocks.
4358        luma8: Option<&[[i32; 64]; 4]>,
4359        cdc: &[[i32; 4]; 2],
4360        cac: Option<&[[[i32; 16]; 4]; 2]>,
4361        cbp_chroma: u32,
4362        // Parsed totalCoeff per block, indexed exactly as the parse's `iz`:
4363        // [0..16] luma 4x4 z-order (for t8, the 8x8 count sits at `id8*4`),
4364        // [16..24] chroma AC as `16 + c*4 + id4`. The parser already counted
4365        // every significant coefficient; re-deriving the counts here scanned
4366        // 16-64 array elements per block (~400 loads/MB) for information the
4367        // caller was holding — the diagnosis's stage-boundary re-derivation tax.
4368        nnzs: &[u8; 24],
4369    ) {
4370        // A `None` field means the parse wrote nothing there; every read below
4371        // is guarded by a zero-count test, so the shared zero plane is
4372        // read-equivalent to the per-macroblock zeroed stack array it replaces.
4373        let luma_scan = luma_scan.unwrap_or(&ZERO_LUMA_SCAN);
4374        let cac = cac.unwrap_or(&ZERO_CAC);
4375        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecResidAdd);
4376        let qp = self.cur_qp;
4377        let qpc = self.chroma_qp_for(qp);
4378        let (w4r, w2r) = (self.mb_w * 4, self.mb_w * 2);
4379        if let Some(l8) = luma8 {
4380            // INTER 8x8 luma: same primitives the I_8x8 and CAVLC paths use.
4381            for b8 in 0..4usize {
4382                let (b8x, b8y) = (b8 % 2, b8 / 2);
4383                // PER-CELL, not one aggregate broadcast over all four cells. CAVLC
4384                // codes an 8x8 block as four 4x4 sub-blocks and its nC predictor
4385                // reads these per-4x4 counts from `nnz_y`, so the broadcast
4386                // corrupted the NEXT macroblock's nC and desynced the parse -- which
4387                // is why CAVLC 8x8 streams ffmpeg accepts would not decode here. The
4388                // worker copy of this function never wrote `nnz_y` at all, so the
4389                // threaded path was unaffected and hid the defect. CABAC has no
4390                // per-4x4 counts, so its callers put the 8x8 total in all four slots.
4391                let nnz: u32 = (0..4).map(|k| nnzs[b8 * 4 + k] as u32).sum();
4392                // Two row slices, not four whole-grid indexed stores. The pair
4393                // written per row is contiguous and `nnzs` is a fixed 24-entry
4394                // array, so both sides become provable.
4395                for sy in 0..2 {
4396                    let base = (mb_y * 4 + b8y * 2 + sy) * w4r + mb_x * 4 + b8x * 2;
4397                    self.nnz_y[base..][..2].copy_from_slice(&nnzs[b8 * 4 + sy * 2..][..2]);
4398                }
4399                // The 4x4 inter path marks coded_y per block; the 8x8 branch must too,
4400                // or a later intra macroblock's neighbour availability is wrong.
4401                for sy in 0..2 {
4402                    let base = (mb_y * 4 + b8y * 2 + sy) * w4r + mb_x * 4 + b8x * 2;
4403                    self.coded_y[base..][..2].fill(true);
4404                }
4405                let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
4406                if nnz == 0 {
4407                    // Zero residual: recon == pred — the 4x4 arm zero shortcut,
4408                    // ported to the 8x8 branch.
4409                    edcstat::bump(&edcstat::T8_ZERO, 1);
4410                    for dy in 0..8 {
4411                        let d = (py + dy) * self.cw + px;
4412                        let po = (b8y * 8 + dy) * 16 + b8x * 8;
4413                        self.rec_y[d..d + 8].copy_from_slice(&pred_y[po..po + 8]);
4414                    }
4415                } else {
4416                    let raster = un_scan_8x8(&l8[b8]);
4417                    // list 1 = INTER 8x8 luma scaling list (0 is the intra one).
4418                    let res8 = self.inv_quant8(&raster, qp, 1);
4419                    let predb: [i32; 64] = core::array::from_fn(|i| {
4420                        pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
4421                    });
4422                    let recon = add_residual_8x8(&res8, &predb);
4423                    // Eight row copies. This wrote SIXTY-FOUR individually
4424                    // bounds-checked samples into the luma plane per coded 8x8
4425                    // block — the same shape whose fix carried `recon_i8_block`.
4426                    // Present in BOTH the main path and the EDC worker twin.
4427                    for dy in 0..8 {
4428                        let d = (py + dy) * self.cw + px;
4429                        self.rec_y[d..][..8].copy_from_slice(&recon[dy * 8..][..8]);
4430                    }
4431                }
4432            }
4433        }
4434        // RESIDUAL LADDER, then the SIMD IDCT+add kernel per parked block (SIMD census
4435        // 2026-09-05). The zero and DC-only ladders write the plane in the loop;
4436        // every other coded block parks its dequantised coefficients here and is
4437        // reconstructed by `reconstruct_4x4_into` = accel `idct4x4_add` (in-register
4438        // transposes, exact on i32). A lane-per-block BATCHED form was tried first
4439        // and lost on all-intra content: its scalar gathers cost more than the
4440        // butterflies they vectorised.
4441        let dq3 = self.dq_const(qp, 3);
4442        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
4443            if luma8.is_some() {
4444                break;
4445            }
4446            let nnz = nnzs[blk];
4447            if let Some(p) = self
4448                .nnz_y
4449                .get_mut((mb_y * 4 + lby) * w4r + (mb_x * 4 + lbx))
4450            {
4451                *p = nnz;
4452            }
4453            let cw = self.cw;
4454            let p_off = (lby * 4) * 16 + lbx * 4;
4455            let r_off = (mb_y * 4 + lby) * 4 * cw + (mb_x * 4 + lbx) * 4;
4456            if nnz == 0 {
4457                // Zero residual → recon == prediction EXACTLY (the integer IDCT is
4458                // linear so zeros map to zeros, and pred is already 0..=255) — copy
4459                // the pred rows straight into the plane. On real (sparse-cbp)
4460                // streams this is MOST of the 4×4 blocks.
4461                for r in 0..4 {
4462                    self.rec_y[r_off + r * cw..r_off + r * cw + 4]
4463                        .copy_from_slice(&pred_y[p_off + r * 16..p_off + r * 16 + 4]);
4464                }
4465                continue;
4466            }
4467            // DC-ONLY: the sole significant coefficient is scan position 0 (the
4468            // zig-zag starts at DC, and un_scan keeps it at raster 0), so the
4469            // whole dequant + IDCT collapses to one multiply and a flat add.
4470            if nnz == 1 && luma_scan[blk][0] != 0 {
4471                let f = self.dequant_dc4(luma_scan[blk][0], qp, 3);
4472                reconstruct_4x4_dc_into(
4473                    (f + 32) >> 6,
4474                    pred_y,
4475                    p_off,
4476                    16,
4477                    &mut self.rec_y,
4478                    r_off,
4479                    cw,
4480                );
4481            } else {
4482                // Fused un-scan + dequant over ONLY the significant coefficients,
4483                // then IDCT + add + clip straight into the plane — no `qb`, no
4484                // `deq`-from-dense, no `predb` gather, no `s`, no `store` call.
4485                //
4486                // HYBRID: the scatter walks scan positions with a data-dependent
4487                // branch per slot, which beats the branchless dense 16-multiply
4488                // loop only while the block is SPARSE. The DC/zero fast paths
4489                // already removed the sparsest blocks, so the population here
4490                // skews denser — above ~6 coefficients the dense loop wins.
4491                // FUSED: un-scan + dequant + IDCT + add in one kernel call (dense-over-scatter round).
4492                reconstruct_4x4_scan_into::<false>(
4493                    &luma_scan[blk],
4494                    &dq3,
4495                    0,
4496                    pred_y,
4497                    p_off,
4498                    16,
4499                    &mut self.rec_y,
4500                    r_off,
4501                    cw,
4502                );
4503            }
4504        }
4505        let mut c_dc = [[0i32; 4]; 2];
4506        if cbp_chroma != 0 {
4507            for c in 0..2 {
4508                c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 4 + c);
4509            }
4510        }
4511        let dqc = [self.dq_const(qpc, 4), self.dq_const(qpc, 5)];
4512        let ccw = self.ccw;
4513        for c in 0..2 {
4514            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4515                let mut ac_nz = false;
4516                if cbp_chroma == 2 {
4517                    let n = nnzs[(16 + c * 4 + by * 2 + bx).min(23)];
4518                    if let Some(p) =
4519                        self.nnz_c[c & 1].get_mut((mb_y * 2 + by) * w2r + (mb_x * 2 + bx))
4520                    {
4521                        *p = n;
4522                    }
4523                    ac_nz = n != 0;
4524                }
4525                let dc = c_dc[c & 1][(by * 2 + bx) & 3];
4526                let p_off = (by * 4) * 8 + bx * 4;
4527                let r_off = (mb_y * 2 + by) * 4 * ccw + (mb_x * 2 + bx) * 4;
4528                let plane = if c == 0 {
4529                    &mut self.rec_u
4530                } else {
4531                    &mut self.rec_v
4532                };
4533                if dc == 0 && !ac_nz {
4534                    // Zero residual (no AC, zero DC) → recon == prediction exactly.
4535                    for r in 0..4 {
4536                        plane[r_off + r * ccw..r_off + r * ccw + 4]
4537                            .copy_from_slice(&c_pred[c][p_off + r * 8..p_off + r * 8 + 4]);
4538                    }
4539                    continue;
4540                }
4541                // DC-ONLY (no coded AC — covers every cbp_chroma==1 block and the
4542                // AC-empty blocks of cbp_chroma==2): the chroma DC arrives ALREADY
4543                // dequantized, so the residual is `(dc + 32) >> 6` flat.
4544                if !ac_nz {
4545                    reconstruct_4x4_dc_into(
4546                        (dc + 32) >> 6,
4547                        &c_pred[c],
4548                        p_off,
4549                        8,
4550                        plane,
4551                        r_off,
4552                        ccw,
4553                    );
4554                    continue;
4555                }
4556                // AC-only scan: index i is overall scan position i+1 (ac_shift=1).
4557                // Same sparse/dense hybrid as luma.
4558                reconstruct_4x4_scan_into::<true>(
4559                    &cac[c & 1][(by * 2 + bx) & 3],
4560                    &dqc[c & 1],
4561                    dc,
4562                    &c_pred[c],
4563                    p_off,
4564                    8,
4565                    plane,
4566                    r_off,
4567                    ccw,
4568                );
4569            }
4570        }
4571    }
4572
4573    // ── SHARED INTRA RECON PRIMITIVES ──────────────────────────────────
4574    // ONE implementation per block family, called by BOTH entropy arms.
4575    // The per-coder recon twins are where routing rot lived: the residual
4576    // ladder and the DC-only collapse each had to be found and ported
4577    // twin-by-twin. The parse halves stay per-coder (that is the real
4578    // difference between the coders); the pixel halves live here — the
4579    // same convergence D14 gave the inter path via add_inter_residual.
4580
4581    /// The residual ladder of an intra 4x4 block, decided BEFORE the serial
4582    /// predict+add walk (SIMD census 2026-09-05); each coded block is then
4583    /// reconstructed by the SIMD IDCT+add kernel.
4584    /// Zero -> recon == pred; Flat -> DC-only, one value; Idx -> a slot in the
4585    /// batched residual array.
4586    fn i4_prepare(&self, scans: &[[i32; 16]; 16], nnzs: &[u8; 16], qp: u8) -> [I4Res; 16] {
4587        let mut kinds = [I4Res::Zero; 16];
4588        for blk in 0..16 {
4589            let (nnz, scan) = (nnzs[blk], &scans[blk]);
4590            kinds[blk] = if nnz == 0 {
4591                edcstat::bump(&edcstat::I4_ZERO, 1);
4592                I4Res::Zero
4593            } else if nnz == 1 && scan[0] != 0 {
4594                edcstat::bump(&edcstat::I4_DC, 1);
4595                let f = self.dequant_dc4(scan[0], qp, 0);
4596                I4Res::Flat((f + 32) >> 6)
4597            } else {
4598                edcstat::bump(&edcstat::I4_DENSE, 1);
4599                I4Res::Idx(blk as u8) // dense: the fused kernel takes the scan block
4600            };
4601        }
4602        kinds
4603    }
4604
4605    /// Intra 4x4 luma block with its residual PRE-TRANSFORMED by [`Self::i4_prepare`]:
4606    /// gather + predict (serial, depends on the previous block's recon) + add.
4607    fn recon_i4_block_res(
4608        &mut self,
4609        bx: usize,
4610        by: usize,
4611        mode: u8,
4612        at: bool,
4613        al: bool,
4614        kind: I4Res,
4615        scans: &[[i32; 16]; 16],
4616        dq: &DequantQp,
4617    ) {
4618        let (px, py) = (bx * 4, by * 4);
4619        let (t, l, corner) = self.gather_i4(px, py, at, al, bx, by);
4620        let pred = intra4x4_pred(mode, at, al, &t, &l, corner);
4621        let r_off = py * self.cw + px;
4622        let cw = self.cw;
4623        match kind {
4624            I4Res::Zero => {
4625                let win = &mut self.rec_y[r_off..r_off + 3 * cw + 4];
4626                for r in 0..4 {
4627                    win[r * cw..r * cw + 4].copy_from_slice(&pred[r * 4..r * 4 + 4]);
4628                }
4629            }
4630            I4Res::Flat(v) => reconstruct_4x4_dc_into(v, &pred, 0, 4, &mut self.rec_y, r_off, cw),
4631            I4Res::Idx(i) => reconstruct_4x4_scan_into::<false>(
4632                &scans[i as usize & 15],
4633                dq,
4634                0,
4635                &pred,
4636                0,
4637                4,
4638                &mut self.rec_y,
4639                r_off,
4640                cw,
4641            ),
4642        }
4643        // coded_y is filled per macroblock row by the callers (routing round).
4644    }
4645
4646    /// Intra 8x8 luma block: predict + zero-arm or un-scan/quant/add.
4647    /// `coeffs` = SCAN-order 8x8 coefficients, `None` when the cbp bit is
4648    /// unset. `coded_y` marking stays with the callers (their orders differ).
4649    fn recon_i8_block(
4650        &mut self,
4651        bx: usize,
4652        by: usize,
4653        mode: u8,
4654        avail_top: bool,
4655        avail_left: bool,
4656        coeffs: Option<&[i32; 64]>,
4657        qp: u8,
4658    ) {
4659        let (px, py) = (bx * 4, by * 4);
4660        let (t, l, corner, avail_corner) = self.gather_i8(px, py, avail_top, avail_left, bx, by);
4661        let pred = intra8x8_pred(mode, avail_top, avail_left, avail_corner, &t, &l, corner);
4662        match coeffs {
4663            None => {
4664                // Zero residual: recon == pred exactly.
4665                edcstat::bump(&edcstat::T8_ZERO, 1);
4666                let (cw, base) = (self.cw, py * self.cw + px);
4667                let win = &mut self.rec_y[base..base + 7 * cw + 8];
4668                for dy in 0..8 {
4669                    win[dy * cw..dy * cw + 8].copy_from_slice(&pred[dy * 8..dy * 8 + 8]);
4670                }
4671            }
4672            Some(scan8) => {
4673                let raster = un_scan_8x8(scan8);
4674                let res8 = self.inv_quant8(&raster, qp, 0);
4675                // Written once rather than zeroed-then-filled.
4676                let predb: [i32; 64] = core::array::from_fn(|i| pred[i] as i32);
4677                let recon = add_residual_8x8(&res8, &predb);
4678                // ROW COPIES, NOT PER-PIXEL STORES. This wrote all 64 samples
4679                // one at a time, each separately bounds-checked, where the
4680                // destination is eight contiguous 8-byte runs `cw` apart.
4681                // REFUTED, do not retry: replacing this WINDOW + sub-slice with
4682                // eight direct `rec_y[d..][..8]` slices left the panic count
4683                // EXACTLY unchanged (12) and cost +3.0% instructions. Neither
4684                // form folds the check, but the window amortises the base
4685                // address computation across the eight rows. (Contrast
4686                // `gather_tile`, where a window cost +57.6% — a window pays only
4687                // when the rows it spans are written in one tight loop.)
4688                let (cw, base) = (self.cw, py * self.cw + px);
4689                let win = &mut self.rec_y[base..base + 7 * cw + 8];
4690                for dy in 0..8 {
4691                    win[dy * cw..dy * cw + 8].copy_from_slice(&recon[dy * 8..dy * 8 + 8]);
4692                }
4693            }
4694        }
4695    }
4696
4697    /// I_16x16 luma: neighbor gather + prediction + the 16 4x4 AC blocks
4698    /// with the DC-only collapse. `q_blocks` = RASTER AC (slot 0 unused),
4699    /// `recon_dc` = the Hadamard-dequantized DC per block. Marks modes_y
4700    /// (I_16x16 predicts as DC for neighbors) + coded_y.
4701    fn recon_i16_luma(
4702        &mut self,
4703        mbx: usize,
4704        mby: usize,
4705        pred_mode: rusty_h264_common::predict::I16Mode,
4706        top_ok: bool,
4707        left_ok: bool,
4708        q_blocks: Option<&[[i32; 16]; 16]>,
4709        coded: u16,
4710        recon_dc: &[i32; 16],
4711        qp: u8,
4712    ) {
4713        // `None` on a DC-only I_16x16 macroblock (CodedBlockPatternLuma == 0):
4714        // no AC was parsed, so the shared zero plane is read-equivalent to the
4715        // 1 KB of stack this used to zero per macroblock.
4716        let q_blocks = q_blocks.unwrap_or(&ZERO_LUMA_SCAN);
4717        let w4 = self.mb_w * 4;
4718        let (lx, ly) = (mbx * 16, mby * 16);
4719        let mut t16 = [0u8; 16];
4720        let mut l16 = [0u8; 16];
4721        if top_ok {
4722            t16.copy_from_slice(self.top_y_row(ly, lx, 16));
4723        }
4724        if left_ok {
4725            // ONE check for the 16-sample column (see `gather_i4`).
4726            // `step_by` walk rather than a span + `col[i * cw]` (see `gather_i8`):
4727            // the span form's inner index stayed checked on all sixteen samples.
4728            let (cw, base) = (self.cw, ly * self.cw + lx - 1);
4729            for (s, &v) in l16.iter_mut().zip(self.rec_y[base..].iter().step_by(cw)) {
4730                *s = v;
4731            }
4732        }
4733        let corner = if top_ok && left_ok {
4734            self.top_y_px(ly, lx - 1)
4735        } else {
4736            0
4737        };
4738        let pred_l = luma16x16_pred(pred_mode, top_ok, left_ok, &t16, &l16, corner);
4739        // AC blocks parked, then the SIMD IDCT+add kernel per block (the pred is whole).
4740        let dq0 = self.dq_const(qp, 0);
4741        for by in 0..4 {
4742            for bx in 0..4 {
4743                let p_off = (by * 4) * 16 + bx * 4;
4744                let r_off = (ly + by * 4) * self.cw + lx + bx * 4;
4745                // ROUTED ON THE PARSE`S CODED MASK (routing round): was a 16-word compare.
4746                if coded & (1u16 << ((by & 3) * 4 + (bx & 3))) == 0 {
4747                    // Zero AC: the residual is the Hadamard DC alone.
4748                    edcstat::bump(&edcstat::I16_DCONLY, 1);
4749                    reconstruct_4x4_dc_into(
4750                        (recon_dc[by * 4 + bx] + 32) >> 6,
4751                        &pred_l,
4752                        p_off,
4753                        16,
4754                        &mut self.rec_y,
4755                        r_off,
4756                        self.cw,
4757                    );
4758                } else {
4759                    // q_blocks holds the SCAN-order AC; the fused kernel un-scans, dequantises
4760                    // and inserts the Hadamard DC.
4761                    reconstruct_4x4_scan_into::<true>(
4762                        &q_blocks[(by & 3) * 4 + (bx & 3)],
4763                        &dq0,
4764                        recon_dc[by * 4 + bx],
4765                        &pred_l,
4766                        p_off,
4767                        16,
4768                        &mut self.rec_y,
4769                        r_off,
4770                        self.cw,
4771                    );
4772                }
4773            }
4774        }
4775        // ROW FILLS. The mode/coded grids were written one 4x4 cell at a time
4776        // inside the recon loop; each macroblock row is four CONTIGUOUS cells,
4777        // so four fills replace sixteen indexed stores.
4778        for by in 0..4 {
4779            let r = (mby * 4 + by) * w4 + mbx * 4;
4780            self.modes_y[r..r + 4].fill(2);
4781            self.coded_y[r..r + 4].fill(true);
4782        }
4783    }
4784
4785    /// Chroma 8x8 recon, both planes: prediction + four 4x4 blocks per
4786    /// plane with the DC-only collapse. `qac` = RASTER AC per plane/block
4787    /// (all-zero when uncoded), `dc` = the 2x2-Hadamard-dequantized DC.
4788    fn recon_chroma_blocks(
4789        &mut self,
4790        mb_x: usize,
4791        mb_y: usize,
4792        chroma_mode: u8,
4793        avail_top: bool,
4794        avail_left: bool,
4795        qac: &[[[i32; 16]; 4]; 2],
4796        coded: [u8; 2],
4797        dc: &[[i32; 4]; 2],
4798        qpc: u8,
4799    ) {
4800        let (cx, cy) = (mb_x * 8, mb_y * 8);
4801        let dqc = [self.dq_const(qpc, 1), self.dq_const(qpc, 2)];
4802        for c in 0..2 {
4803            let mut ctop = [0u8; 8];
4804            let mut cleft = [0u8; 8];
4805            let mut ccorner = 0u8;
4806            {
4807                let rec_c = if c == 0 { &self.rec_u } else { &self.rec_v };
4808                if avail_top {
4809                    ctop.copy_from_slice(self.top_c_row(c, cy, cx, 8));
4810                }
4811                if avail_left {
4812                    // Strided WALK, as in `gather_i4`/`gather_i8`.
4813                    let cbase = cy * self.ccw + cx - 1;
4814                    for (o, &v) in cleft
4815                        .iter_mut()
4816                        .zip(rec_c[cbase..].iter().step_by(self.ccw))
4817                    {
4818                        *o = v;
4819                    }
4820                }
4821                if avail_top && avail_left {
4822                    ccorner = self.top_c_px(c, cy, cx - 1);
4823                }
4824            }
4825            let pred8 = chroma8x8_pred(chroma_mode, avail_top, avail_left, &ctop, &cleft, ccorner);
4826            // Coded AC blocks parked (up to 4), then the SIMD IDCT+add kernel per block.
4827            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4828                let p_off = (by * 4) * 8 + bx * 4;
4829                let ccw = self.ccw;
4830                let r_off = (cy + by * 4) * ccw + cx + bx * 4;
4831                if coded[c & 1] & (1u8 << ((by * 2 + bx) & 3)) == 0 {
4832                    // Zero AC (cbp_chroma <= 1, the common case): DC-alone flat add.
4833                    edcstat::bump(&edcstat::I16_DCONLY, 1);
4834                    let plane = if c == 0 {
4835                        &mut self.rec_u
4836                    } else {
4837                        &mut self.rec_v
4838                    };
4839                    reconstruct_4x4_dc_into(
4840                        (dc[c & 1][(by * 2 + bx) & 3] + 32) >> 6,
4841                        &pred8,
4842                        p_off,
4843                        8,
4844                        plane,
4845                        r_off,
4846                        ccw,
4847                    );
4848                } else {
4849                    let plane = if c == 0 {
4850                        &mut self.rec_u
4851                    } else {
4852                        &mut self.rec_v
4853                    };
4854                    reconstruct_4x4_scan_into::<true>(
4855                        &qac[c & 1][(by * 2 + bx) & 3],
4856                        &dqc[c & 1],
4857                        dc[c & 1][(by * 2 + bx) & 3],
4858                        &pred8,
4859                        p_off,
4860                        8,
4861                        plane,
4862                        r_off,
4863                        ccw,
4864                    );
4865                }
4866            }
4867        }
4868    }
4869
4870    fn recon_chroma_cabac(
4871        &mut self,
4872        mb_x: usize,
4873        mb_y: usize,
4874        chroma_mode: u8,
4875        cdc: &[[i32; 4]; 2],
4876        cac: Option<&[[[i32; 16]; 4]; 2]>,
4877        cnnz: &[u8; 8], // parse-side AC counts, c * 4 + by * 2 + bx (routing round: was a 128-word rescan)
4878        cbp_chroma: u32,
4879        avail_top: bool,
4880        avail_left: bool,
4881    ) {
4882        // `None` = no chroma AC was parsed; every read below is already gated
4883        // on cbp_chroma == 2, so the shared zero plane is read-equivalent.
4884        let cac = cac.unwrap_or(&ZERO_CAC);
4885        let qpc = self.chroma_qp_for(self.cur_qp);
4886        let mut c_dc = [[0i32; 4]; 2];
4887        if cbp_chroma != 0 {
4888            for c in 0..2 {
4889                c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 1 + c);
4890            }
4891        }
4892        // ENTROPY-SIDE half: un-scan the AC into raster + commit nnz_c; the
4893        // pixel half is the SHARED recon_chroma_blocks.
4894        let w2 = self.mb_w * 2;
4895        let mut ccoded = [0u8; 2];
4896        if cbp_chroma == 2 {
4897            for c in 0..2 {
4898                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4899                    let cnt = cnnz[(c * 4 + by * 2 + bx) & 7];
4900                    ccoded[c & 1] |= ((cnt != 0) as u8) << ((by * 2 + bx) & 3);
4901                    if let Some(p) =
4902                        self.nnz_c[c & 1].get_mut((mb_y * 2 + by) * w2 + (mb_x * 2 + bx))
4903                    {
4904                        *p = cnt;
4905                    }
4906                }
4907            }
4908        }
4909        self.recon_chroma_blocks(
4910            mb_x,
4911            mb_y,
4912            chroma_mode,
4913            avail_top,
4914            avail_left,
4915            cac,
4916            ccoded,
4917            &c_dc,
4918            qpc,
4919        );
4920    }
4921
4922    /// D14 — the CAVLC E-seam (P3 item 5). Mirrors `decode_slice_data_cabac`:
4923    /// overlap parse (this thread) with pixel reconstruction (a scoped worker
4924    /// owning the planes). Now possible because the CAVLC inter recon was
4925    /// converged onto `add_inter_residual`, so both entropy coders emit the SAME
4926    /// `PInterJob` and share one worker recon.
4927    pub fn decode_slice_data(
4928        &mut self,
4929        r: &mut BitReader,
4930        is_p: bool,
4931        first_mb: usize,
4932    ) -> Result<usize, MbError> {
4933        // Same cross-slice guard as the CABAC loop head.
4934        self.span_flush();
4935        let eligible = edc_on() && rowdb_on() && (is_p || self.is_b);
4936        let threaded = eligible && edc_spawn_worker(self.mb_w, self.mb_h, self.bits_per_mb, false);
4937        edcstat::bump(&edcstat::DISPATCH_ON, threaded as u64);
4938        edcstat::bump(&edcstat::DISPATCH_SEEN, eligible as u64);
4939        if !threaded {
4940            return self.decode_slice_cavlc_inner(r, is_p, first_mb);
4941        }
4942        #[cfg(feature = "std")]
4943        {
4944            let ctx = self.edc_take_ctx();
4945            let (tx, rx) = crate::sync::mpsc::sync_channel::<EdcMsg>(edc_bound());
4946            let (ctx_tx, ctx_rx) = crate::sync::mpsc::channel::<PixelCtx>();
4947            let (back_tx, back_rx) = crate::sync::mpsc::channel::<PixelCtx>();
4948            let (res, ctx, panicked) = std::thread::scope(|sc| {
4949                let h = sc.spawn(move || edc_worker(ctx, rx, ctx_tx, back_rx));
4950                self.edc_tx = Some(tx);
4951                self.edc_ctx_rx = Some(ctx_rx);
4952                self.edc_back_tx = Some(back_tx);
4953                // UNWIND SAFETY (same trap the CABAC wrapper documents): the sender
4954                // lives in `self`, which outlives an unwind, so a panic in the parse
4955                // loop would leave the channel open, the worker alive and the scope
4956                // join blocking forever — turning a diagnosable panic into a silent
4957                // deadlock. Catch, clean up, join, restore, THEN resume.
4958                let r2 = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4959                    self.decode_slice_cavlc_inner(r, is_p, first_mb)
4960                }));
4961                self.edc_flush_batch();
4962                self.edc_giveback();
4963                self.edc_tx = None;
4964                self.edc_ctx_rx = None;
4965                self.edc_back_tx = None;
4966                match (r2, h.join()) {
4967                    (Ok(res), Ok(ctx)) => (res, ctx, None),
4968                    (Err(pn), Ok(ctx)) => (Err(MbError::Truncated), ctx, Some(pn)),
4969                    (Ok(_), Err(pn)) | (Err(_), Err(pn)) => {
4970                        (Err(MbError::Truncated), None, Some(pn))
4971                    }
4972                }
4973            });
4974            if let Some(ctx) = ctx {
4975                self.edc_restore_ctx(ctx);
4976            }
4977            if let Some(pn) = panicked {
4978                std::panic::resume_unwind(pn);
4979            }
4980            res
4981        }
4982        #[cfg(not(feature = "std"))]
4983        {
4984            unreachable!("the EDC worker needs std; edc_spawn_worker is false without it")
4985        }
4986    }
4987
4988    fn decode_slice_cavlc_inner(
4989        &mut self,
4990        r: &mut BitReader,
4991        is_p: bool,
4992        first_mb: usize,
4993    ) -> Result<usize, MbError> {
4994        let total = self.mb_w * self.mb_h;
4995        self.slice_first_mb = first_mb;
4996        self.slice_bounds.push((first_mb, self.cur_idc2));
4997        self.any_idc2 |= self.cur_idc2;
4998        self.edc_active = edc_on();
4999        let mut addr = first_mb;
5000        // Same malformed-header divide-by-zero as the sibling slice loop above.
5001        let mbw_nz = self.mb_w.max(1);
5002        let (mut mbx, mut mby) = (addr % mbw_nz, addr / mbw_nz);
5003        let mbw_c = self.mb_w;
5004        while addr < total {
5005            if mbx == mbw_c {
5006                mbx = 0;
5007                mby += 1;
5008            }
5009            self.row_hook_at(addr, mby);
5010            if is_p || self.is_b {
5011                let skip_run = {
5012                    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
5013                    r.read_ue()?
5014                } as usize;
5015                // A run past the picture end is a corrupt stream (ffmpeg errors
5016                // here too); a run TO the end is legal. Silently clamping used
5017                // to fill the remainder with skip MBs.
5018                if skip_run > total - addr {
5019                    return Err(MbError::Truncated);
5020                }
5021                // The run length is KNOWN here (CAVLC codes it as one syntax
5022                // element). P runs: after the first skip commits (0,0), every
5023                // later run MB is FORCED (0,0) by the zero-MV rule — process
5024                // the remainder SEGMENT-WISE: one span extension + one mb_qp
5025                // fill per row segment instead of a per-MB call + span match +
5026                // store. Non-(0,0) runs and B runs keep the per-MB loop. The
5027                // per-MB `addr >= total` check is gone: the run was validated
5028                // against `total - addr` above.
5029                let mut remaining = skip_run;
5030                while remaining > 0 {
5031                    debug_assert!(addr < total);
5032                    if mbx == mbw_c {
5033                        mbx = 0;
5034                        mby += 1;
5035                    }
5036                    if self.is_b {
5037                        if !self.b_skip_hot(mbx, mby) {
5038                            self.decode_b_skip(mbx, mby)?;
5039                        }
5040                        if let Some(p) = self.mb_qp.get_mut(addr) {
5041                            *p = self.cur_qp;
5042                        } // skip inherits QPy
5043                        addr += 1;
5044                        mbx += 1;
5045                        remaining -= 1;
5046                        continue;
5047                    }
5048                    self.decode_p_skip(mbx, mby)?;
5049                    if let Some(p) = self.mb_qp.get_mut(addr) {
5050                        *p = self.cur_qp;
5051                    }
5052                    addr += 1;
5053                    mbx += 1;
5054                    remaining -= 1;
5055                    // Segment-wise remainder: forced (0,0) continuation holds
5056                    // exactly while skip_zero_next tracks addr (decode_p_skip
5057                    // set it iff this skip committed (0,0)).
5058                    // Worker mode (edc_tx) must route per-MB jobs through the
5059                    // channel — the parse thread no longer owns the planes.
5060                    while remaining > 0
5061                        && self.skip_zero_next == addr
5062                        && !no_runmv()
5063                        && self.edc_tx.is_none()
5064                    {
5065                        if mbx == mbw_c {
5066                            mbx = 0;
5067                            mby += 1;
5068                        }
5069                        // Row segment: run to row end or run end.
5070                        let seg = remaining.min(mbw_c - mbx);
5071                        let recon =
5072                            self.edc_tx.is_none() && (self.weights.is_none() || self.weights_id0);
5073                        for k in 0..seg {
5074                            self.pz_push(mbx + k, mby, recon);
5075                        }
5076                        edcstat::bump(&edcstat::SKIPMV_FORCED, seg as u64);
5077                        self.route_skip_mbs += seg as u32;
5078                        self.mb_qp[addr..addr + seg].fill(self.cur_qp);
5079                        if !recon {
5080                            for k in 0..seg {
5081                                self.edc_jobs.push(EdcJob::Skip {
5082                                    mbx: mbx + k,
5083                                    mby,
5084                                    mv: (0, 0),
5085                                });
5086                            }
5087                        }
5088                        addr += seg;
5089                        mbx += seg;
5090                        remaining -= seg;
5091                        self.skip_zero_next = addr;
5092                    }
5093                }
5094                if addr >= total {
5095                    break;
5096                }
5097                // A trailing skip run with no following macroblock ends the slice.
5098                if skip_run > 0 && !r.more_rbsp_data() {
5099                    break;
5100                }
5101            }
5102            // The skip run above may have crossed a row boundary within this
5103            // iteration — wrap before the non-skip MB decodes.
5104            if mbx == mbw_c {
5105                mbx = 0;
5106                mby += 1;
5107            }
5108            if self.is_b {
5109                // ORDER: B reconstructs inline (not seam-ready).
5110                self.edc_intra_sync();
5111                self.decode_b_mb(r, mbx, mby)?;
5112            } else {
5113                // Non-skip CAVLC MB: inter MV prediction + intra gathers
5114                // read the grids — flush deferred spans.
5115                self.span_flush();
5116                self.decode_mb(r, mbx, mby, is_p)?;
5117            }
5118            if let Some(p) = self.mb_qp.get_mut(addr) {
5119                *p = self.cur_qp;
5120            }
5121            addr += 1;
5122            mbx += 1;
5123            // CAVLC slice end: no more data after this macroblock.
5124            if !r.more_rbsp_data() {
5125                break;
5126            }
5127        }
5128        self.edc_flush(); // slice end: no job crosses a slice boundary
5129        Ok(addr)
5130    }
5131
5132    fn decode_mb(
5133        &mut self,
5134        r: &mut BitReader,
5135        mb_x: usize,
5136        mb_y: usize,
5137        is_p: bool,
5138    ) -> Result<(), MbError> {
5139        self.wait_refs_for_mb(mb_y);
5140        let mut mb_type = {
5141            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
5142            r.read_ue()?
5143        };
5144        if is_p {
5145            // In P-slices, mb_type 0/1/2 are inter (16×16, 16×8, 8×16),
5146            // 3 = P_8x8, 4 = P_8x8ref0 (ref_idx forced 0), 5+ intra.
5147            if mb_type <= 2 {
5148                return self.decode_inter(r, mb_x, mb_y, mb_type as u8);
5149            }
5150            if mb_type == 3 || mb_type == 4 {
5151                return self.decode_p8x8(r, mb_x, mb_y, mb_type == 4);
5152            }
5153            mb_type -= 5;
5154        }
5155        // ORDER: intra reconstruction reads neighbour PIXELS, so the worker
5156        // must have applied every deferred job before this point.
5157        self.edc_intra_sync();
5158        self.decode_intra_mb(r, mb_x, mb_y, mb_type)
5159    }
5160
5161    /// Decodes an intra macroblock given its intra `mb_type` (0 = I_4x4,
5162    /// 1..=24 = I_16x16, 25 = I_PCM) — shared by I-, P- and B-slice paths.
5163    fn decode_intra_mb(
5164        &mut self,
5165        r: &mut BitReader,
5166        mb_x: usize,
5167        mb_y: usize,
5168        mb_type: u32,
5169    ) -> Result<(), MbError> {
5170        // H-48: this scope was DECLARED and never wired, which is precisely why the
5171        // stage table left 19.8% unaccounted — 66,120 of 475,200 macroblocks on the
5172        // reference stream are I-type and had no scope at all.
5173        let _gi = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbI);
5174        if let Some(p) = self.mb_kind.get_mut(mb_y * self.mb_w + mb_x) {
5175            *p = rusty_h264_common::deblock::MB_KIND_INTRA;
5176        }
5177        if mb_type == 0 {
5178            // I_NxN: transform_size_8x8_flag (when enabled) selects I_8x8 vs I_4x4.
5179            if self.transform_8x8_mode && r.read_bit()? {
5180                self.decode_i8x8(r, mb_x, mb_y)?;
5181            } else {
5182                self.decode_i4x4(r, mb_x, mb_y)?;
5183            }
5184        } else if (1..=24).contains(&mb_type) {
5185            self.decode_i16(r, mb_x, mb_y, mb_type - 1)?;
5186        } else if mb_type == 25 {
5187            // ORDER: I_PCM writes pixels directly.
5188            self.edc_intra_sync();
5189            self.decode_ipcm(r, mb_x, mb_y)?;
5190        } else {
5191            return Err(MbError::Unsupported(
5192                "only I_4x4 / I_16x16 / I_PCM macroblocks",
5193            ));
5194        }
5195        // Mark all luma blocks coded for the next macroblock's top-right.
5196        // Four contiguous cells per macroblock row - `fill`, not sixteen
5197        // separately bounds-checked indexed stores.
5198        let w4 = self.mb_w * 4;
5199        for lby in 0..4usize {
5200            let a = (mb_y * 4 + lby) * w4 + mb_x * 4;
5201            self.coded_y[a..a + 4].fill(true);
5202        }
5203        Ok(())
5204    }
5205
5206    /// Reconstructs an inter macroblock (`mode` 0 = P_L0_16x16, 1 = P_16x8,
5207    /// 2 = P_8x16): parse the per-partition motion vectors and residual,
5208    /// motion-compensate each partition, and add the residual.
5209    fn decode_inter(
5210        &mut self,
5211        r: &mut BitReader,
5212        mb_x: usize,
5213        mb_y: usize,
5214        mode: u8,
5215    ) -> Result<(), MbError> {
5216        if self.refs.is_empty() {
5217            return Err(MbError::Unsupported("inter without reference"));
5218        }
5219        // DEBLOCK CLASS: mode 0 is P_L0_16x16 — ONE partition, so all 16 blocks
5220        // share a reference and motion vector and no internal edge can reach
5221        // strength 1. Internal strengths then follow from coefficients alone, i.e.
5222        // 16 nnz bytes instead of a 24-block gather across 5-7 grids. Modes 1/2
5223        // (P_16x8 / P_8x16) have two partitions with independent motion and stay
5224        // UNSET (blind path).
5225        if mode == 0 {
5226            if let Some(k) = self.mb_kind.get_mut(mb_y * self.mb_w + mb_x) {
5227                *k = rusty_h264_common::deblock::MB_KIND_INTER_UNIFORM;
5228            }
5229        }
5230        // QP (qp/qpc) is bound after mb_qp_delta is read below.
5231        let w4 = self.mb_w * 4;
5232        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
5233        let num_refs = self.refs.len();
5234        let layout = inter_partitions(mode);
5235
5236        // mb_pred order (spec 7.3.5.1): all ref_idx_l0 first (only when more than
5237        // one reference is active), then all mvd_l0.
5238        let nparts = layout.len();
5239        let mut ref_idxs = [0i32; 4];
5240        if self.num_ref_active > 1 {
5241            for ri in ref_idxs[..nparts].iter_mut() {
5242                *ri = read_ref_idx(r, self.num_ref_active)?;
5243                if *ri as usize >= num_refs {
5244                    return Err(MbError::Truncated); // references a non-existent picture
5245                }
5246            }
5247        }
5248
5249        // Phase 1: per partition, ref-aware MV prediction + mvd, committing the
5250        // motion grid so a later partition predicts from an earlier one.
5251        let mut part_mv = [(0i32, (0i32, 0i32)); 4];
5252        {
5253            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
5254            for (part, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
5255                let refi = ref_idxs[part];
5256                let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
5257                let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
5258                let pmv = predict_partition_mv(mode, part, a, b, c, refi);
5259                let mvd_x = read_mvd(r)?;
5260                let mvd_y = read_mvd(r)?;
5261                let mv = (pmv.0 + mvd_x, pmv.1 + mvd_y);
5262                part_mv[part] = (refi, mv);
5263                for by in ry / 4..ry / 4 + rh / 4 {
5264                    for bx in rx / 4..rx / 4 + rw / 4 {
5265                        let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
5266                        if let (Some(m), Some(it), Some(rf), Some(cd)) = (
5267                            self.mv_y.get_mut(idx),
5268                            self.inter_y.get_mut(idx),
5269                            self.ref_idx_y.get_mut(idx),
5270                            self.coded_y.get_mut(idx),
5271                        ) {
5272                            (*m, *it, *rf, *cd) = ((mv.0 as i16, mv.1 as i16), true, refi as i8, true);
5273                        }
5274                    }
5275                }
5276            }
5277        }
5278
5279        // Phase 2: motion-compensate each partition from its reference.
5280        let mut pred_y = [0u8; 256];
5281        let mut c_pred = [[0u8; 64]; 2];
5282        // D8-CAVLC: the double-stage ablation, extended to the CAVLC loop. The
5283        // CABAC measurement could not run here at all (`edc_active` is false on
5284        // this path, so nothing replays through `edc_flush` and `doubled` read
5285        // 0) — yet CAVLC is exactly the population P3 item 5 targets, and its
5286        // cheaper parse should make the PIXEL share larger. Doubling the whole
5287        // partition loop is idempotent: pass 2 overwrites `pred_y` with fresh MC
5288        // BEFORE `weight_partition` runs, so weighting cannot apply twice.
5289        // This doubles MC only (not the residual add inside `inter_finish`), so
5290        // it is a LOWER BOUND on the CAVLC pixel share.
5291        // D14 (CAVLC E-seam): when the seam is live the WORKER motion-compensates
5292        // from the committed MV grids, so skip MC here rather than computing a
5293        // prediction that would be discarded.
5294        let defer = self.edc_tx.is_some() || self.edc_active;
5295        let mc_passes = if defer {
5296            0
5297        } else if double_recon() {
5298            2
5299        } else {
5300            1
5301        };
5302        for _pass in 0..mc_passes {
5303            if _pass > 0 {
5304                edcstat::bump(&edcstat::DOUBLED, 1);
5305            }
5306            for (part, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
5307                let (refi, mv) = part_mv[part & 3];
5308                let Some(reference) = self.refs.get(refi as usize) else {
5309                    continue;
5310                };
5311                let mut tmp = [0u8; 256];
5312                mc_luma_padded(
5313                    &reference.luma_guard(reference.ch),
5314                    reference.lstride(),
5315                    crate::LPAD,
5316                    self.cw,
5317                    ch,
5318                    mb_x * 16 + rx,
5319                    mb_y * 16 + ry,
5320                    rw,
5321                    rh,
5322                    mv.0,
5323                    mv.1,
5324                    &mut tmp,
5325                );
5326                {
5327                    let _g =
5328                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
5329                    restride(&mut pred_y, 16, rx, ry, &tmp, rw, rh);
5330                }
5331                let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
5332                for cc in 0..2 {
5333                    let rc = if cc == 0 {
5334                        &*reference.chroma_guard(0, reference.ch)
5335                    } else {
5336                        &*reference.chroma_guard(1, reference.ch)
5337                    };
5338                    let mut tc = [0u8; 64];
5339                    mc_chroma_padded(
5340                        rc,
5341                        reference.cstride(),
5342                        crate::CPAD,
5343                        self.ccw,
5344                        cch,
5345                        mb_x * 8 + crx,
5346                        mb_y * 8 + cry,
5347                        crw,
5348                        crh,
5349                        mv.0,
5350                        mv.1,
5351                        &mut tc,
5352                    );
5353                    {
5354                        let _g =
5355                            rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
5356                        restride(&mut c_pred[cc], 8, crx, cry, &tc, crw, crh);
5357                    }
5358                }
5359                self.weight_partition(&mut pred_y, &mut c_pred, 0, refi as usize, rx, ry, rw, rh);
5360            }
5361        }
5362
5363        // 16×16/16×8/8×16 partitions are all ≥ 8×8, so the 8×8 transform is allowed.
5364        self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, true, defer)
5365    }
5366
5367    /// Shared inter tail: parse `coded_block_pattern` + `mb_qp_delta`, decode the
5368    /// luma/chroma residual, and add it to the already-built motion-compensated
5369    /// prediction. Used by both the 16×16/16×8/8×16 path and `P_8x8`.
5370    fn inter_finish(
5371        &mut self,
5372        r: &mut BitReader,
5373        mb_x: usize,
5374        mb_y: usize,
5375        pred_y: &[u8; 256],
5376        c_pred: &[[u8; 64]; 2],
5377        allow_8x8: bool,
5378        // D14: emit a worker job instead of reconstructing. Only the CAVLC
5379        // 16x16/16x8/8x16 path sets this; B and P_8x8 stay inline.
5380        defer: bool,
5381    ) -> Result<(), MbError> {
5382        let w4 = self.mb_w * 4;
5383        let cbp = {
5384            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
5385            read_cbp_inter(r)?
5386        };
5387        let cbp_luma = cbp & 15;
5388        let cbp_chroma = cbp >> 4;
5389        // transform_size_8x8_flag follows cbp (before mb_qp_delta) when luma has
5390        // coefficients, the 8×8 transform is enabled, and every partition ≥ 8×8.
5391        let t8x8 = cbp_luma > 0 && self.transform_8x8_mode && allow_8x8 && r.read_bit()?;
5392        if t8x8 {
5393            if let Some(f) = self.mb_t8x8.get_mut(mb_y * self.mb_w + mb_x) {
5394                *f = true;
5395            }
5396            self.any_t8 = true;
5397        }
5398        if cbp != 0 {
5399            self.step_qp(r.read_se()?)?;
5400        }
5401        let (qp, _qpc) = (self.cur_qp, self.chroma_qp_for(self.cur_qp));
5402
5403        // ---- luma residual ----
5404        self.nnz_cache_load(mb_x, mb_y);
5405        let mut nnzs = [0u8; 24];
5406        // PARSE STRAIGHT INTO THE DESTINATION. The deferred arm used to parse into
5407        // 1.5 KB of zeroed locals and then copy them into the pooled job (another
5408        // 1.5 KB, +1 KB each for t8x8); the inline arm zeroed the same locals.
5409        // Both now decode into a pre-owned plane set -- the pooled `PInterJob`
5410        // when deferring, the decoder's scratch boxes otherwise -- and zero only
5411        // the blocks the cbp codes. Uncoded slots stay dirty on purpose: the
5412        // consumer (`add_inter_residual` / the worker) skips every block whose
5413        // `nnzs` count is 0 and every chroma AC plane unless cbp_chroma == 2,
5414        // the contract the CABAC P arm has shipped on since round 4.
5415        let nores = cbp == 0 && nores_on();
5416        let mut job = (defer && !nores).then(|| self.take_pinter_job());
5417        let mut s_luma = if job.is_none() {
5418            self.scratch_luma
5419                .take()
5420                .or_else(|| Some(Box::new([[0i32; 16]; 16])))
5421        } else {
5422            None
5423        };
5424        let mut s_luma8 = if job.is_none() {
5425            self.scratch_luma8
5426                .take()
5427                .or_else(|| Some(Box::new([[0i32; 64]; 4])))
5428        } else {
5429            None
5430        };
5431        let mut s_cac = if job.is_none() {
5432            self.scratch_cac
5433                .take()
5434                .or_else(|| Some(Box::new([[[0i32; 16]; 4]; 2])))
5435        } else {
5436            None
5437        };
5438        let (luma_scan, luma8, cac): (
5439            &mut [[i32; 16]; 16],
5440            &mut [[i32; 64]; 4],
5441            &mut [[[i32; 16]; 4]; 2],
5442        ) = match job.as_deref_mut() {
5443            Some(j) => (&mut j.luma_scan, &mut j.luma8, &mut j.cac),
5444            // `get_or_insert_with` rather than `expect`: the scratch is
5445            // created unconditionally above, but nothing in the types says so.
5446            // This makes it structural, and the closures are LAZY, so the
5447            // already-Some case allocates nothing.
5448            None => (
5449                &mut **s_luma.get_or_insert_with(|| Box::new([[0i32; 16]; 16])),
5450                &mut **s_luma8.get_or_insert_with(|| Box::new([[0i32; 64]; 4])),
5451                &mut **s_cac.get_or_insert_with(|| Box::new([[[0i32; 16]; 4]; 2])),
5452            ),
5453        };
5454        // BUILD THE MACROBLOCK'S nnz RASTER ON THE STACK, COPY IT ROW-WISE ONCE.
5455        // Both arms below scattered sixteen individually bounds-checked stores
5456        // into the frame grid while the entropy parse ran. Nothing reads
5457        // `nnz_y` for THIS macroblock before the function returns - `nc_pred`
5458        // predicts from the separate `nnz_cache` - so the writes can be
5459        // deferred. The zero arm then costs NOTHING: the raster already holds
5460        // zeros.
5461        let mut nnz_raster = [0u8; 16];
5462        if t8x8 {
5463            for b8 in 0..4 {
5464                let (b8x, b8y) = (b8 % 2, b8 / 2);
5465                let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
5466                let _ = (bx, by);
5467                if cbp_luma & (1 << b8) != 0 {
5468                    // Zero this 8x8 slot only (256 B) -- was a 1 KB `[[0;64];4]` insert + a 256 B copy.
5469                    luma8[b8 & 3] = [0i32; 64];
5470                    for sub in 0..4 {
5471                        let (sx, sy) = (sub % 2, sub / 2);
5472                        let (cx, cy) = (b8x * 2 + sx, b8y * 2 + sy);
5473                        let nc = self.nc_pred(cx, cy);
5474                        let mut blk = [0i32; 16];
5475                        let total = decode_residual_block_into::<16, 16>(r, nc, &mut blk)?;
5476                        self.nnz_cache_set(cx, cy, total);
5477                        nnz_raster[cy * 4 + cx] = total;
5478                        // The PER-SUB-BLOCK count the next macroblock's nC prediction
5479                        // depends on -- summing these into one slot and letting the
5480                        // recon helper broadcast it back is what broke CAVLC 8x8.
5481                        // WIN: b8 < 4 and sub < 4, so the index is 0..=15 and the
5482                        // mask is a no-op that PROVES it -- the same idiom the
5483                        // neighbouring luma8[b8 & 3] / luma_scan[blk & 15] already use.
5484                        // Folds a panic_bounds_check out of inter_finish.
5485                        nnzs[(b8 * 4 + sub) & 15] = total;
5486                        // RAW 8x8 scan: coeff k of sub-block s at 4k + s (spec 7.3.5.3.2);
5487                        // `add_inter_residual` un-scans + dequantises, as for CABAC.
5488                        for k in 0..16 {
5489                            luma8[b8 & 3][(4 * k + sub) & 63] = blk[k];
5490                        }
5491                    }
5492                } else {
5493                    for sub in 0..4 {
5494                        let (sx, sy) = (sub % 2, sub / 2);
5495                        self.nnz_cache_set(b8x * 2 + sx, b8y * 2 + sy, 0);
5496                        // `nnz_raster` is already zero here - no store needed.
5497                    }
5498                }
5499            }
5500        } else {
5501            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5502                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
5503                let total = if cbp_luma & (1 << (blk / 4)) != 0 {
5504                    let nc = self.nc_pred(lbx, lby);
5505                    // Decoded STRAIGHT into the destination plane (RAW scan order, like
5506                    // CABAC); zero only this coded block first (the slot is pooled/dirty).
5507                    luma_scan[blk & 15] = [0i32; 16];
5508                    decode_residual_block_into::<16, 16>(r, nc, &mut luma_scan[blk & 15])?
5509                } else {
5510                    0
5511                };
5512                self.nnz_cache_set(lbx, lby, total);
5513                let _ = (bx, by);
5514                nnz_raster[(lby & 3) * 4 + (lbx & 3)] = total;
5515                // WIN: luma 4x4 index, 0..=15 (luma_scan[blk & 15] above relies on the
5516                // same range); the mask folds the second bounds check.
5517                nnzs[blk & 15] = total;
5518            }
5519        }
5520        // ONE contiguous copy per macroblock row.
5521        for by in 0..4usize {
5522            let a = (mb_y * 4 + by) * w4 + mb_x * 4;
5523            self.nnz_y[a..a + 4].copy_from_slice(&nnz_raster[by * 4..by * 4 + 4]);
5524        }
5525
5526        // ---- chroma residual ----
5527        let mut c_recon_dc = [[0i32; 4]; 2];
5528        if cbp_chroma != 0 {
5529            for slot in c_recon_dc.iter_mut() {
5530                // RAW, straight into the 4-word slot (dequantised in the helper).
5531                decode_residual_block_into::<4, 4>(r, -1, slot)?;
5532            }
5533        }
5534        if cbp_chroma == 2 {
5535            self.chroma_cache_load(mb_x, mb_y);
5536            let w2 = self.mb_w * 2;
5537            let mut cnnz = [[0u8; 4]; 2];
5538            for c in 0..2 {
5539                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
5540                    let nc = self.chroma_nc_pred(c, bx, by);
5541                    let slot = &mut cac[c & 1][(by * 2 + bx) & 3];
5542                    *slot = [0i32; 16];
5543                    let total = decode_residual_block_into::<15, 16>(r, nc, slot)?; // RAW scan order
5544                    self.chroma_nnz_cache_set(c, bx, by, total);
5545                    // WIN: by < 2 and bx < 2, so 0..=3 -- the mask proves it against
5546                    // the [[u8; 4]; 2], exactly as the cac[c & 1][(by * 2 + bx) & 3]
5547                    // slot two lines above already does. Folds the last
5548                    // panic_bounds_check out of inter_finish.
5549                    cnnz[c & 1][(by * 2 + bx) & 3] = total;
5550                    // Masks are the proof: max index 16 + 4 + 2 + 1 = 23 < 24 (was a `.min(23)`).
5551                    nnzs[16 + (c & 1) * 4 + (by & 1) * 2 + (bx & 1)] = total;
5552                }
5553                for by in 0..2usize {
5554                    let a = (mb_y * 2 + by) * w2 + mb_x * 2;
5555                    self.nnz_c[c][a..a + 2].copy_from_slice(&cnnz[c][by * 2..by * 2 + 2]);
5556                }
5557            }
5558        }
5559
5560        // ---- reconstruction ----
5561        //
5562        // D13: this used to be a 109-line hand-rolled copy of the residual add.
5563        // It now calls the SAME `add_inter_residual` the CABAC path uses, which
5564        // is what makes the CAVLC E-seam possible at all: the two paths had
5565        // different residual representations (CAVLC pre-applied un_scan and
5566        // inv_quant at PARSE time; CABAC carries raw scan-order coefficients and
5567        // dequantises inside the helper), so no job could be shared. Carrying the
5568        // raw forms — which CAVLC already had in hand — converges them, deletes
5569        // a duplicate implementation, and lets a deferred job reuse the existing
5570        // worker recon instead of needing a second copy that could drift.
5571        if defer {
5572            // The residual representation now matches CABAC exactly (the
5573            // convergence commit), so the SAME `PInterJob` and the SAME worker
5574            // `recon_p_inter` serve both entropy coders — no second recon
5575            // implementation exists that could drift.
5576            let (mut gmv, mut gref) = ([(0i16, 0i16); 16], [0u8; 16]);
5577            let w4r = self.mb_w * 4;
5578            // ROW SLICES over BOTH grids. This was the single densest panic site
5579            // left in the decoder crate: sixteen iterations reading two PARALLEL
5580            // frame grids at the same index, and `mv_y`'s bound proved nothing
5581            // about `ref_idx_y`, so all thirty-two loads carried their own check.
5582            // Each macroblock row is four contiguous cells in both.
5583            for by in 0..4usize {
5584                let base = (mb_y * 4 + by) * w4r + mb_x * 4;
5585                let mvr = &self.mv_y[base..][..4];
5586                let rfr = &self.ref_idx_y[base..][..4];
5587                gmv[by * 4..][..4].copy_from_slice(mvr);
5588                for bx in 0..4usize {
5589                    gref[by * 4 + bx] = rfr[bx].clamp(0, 15) as u8;
5590                }
5591            }
5592            // D9 applies here too: `cbp == 0` means all 2,592 coefficient bytes
5593            // of the 2,784-byte job are ZERO, so ship the 176-byte motion-only
5594            // form. Discovered on the CABAC path; it transfers for free because
5595            // the CAVLC arm now emits the SAME job type.
5596            let ej = if nores {
5597                edcstat::bump(&edcstat::J_NORES_SENT, 1);
5598                let b = self.take_nores_job(PInterNoResJob {
5599                    mbx: mb_x,
5600                    mby: mb_y,
5601                    t8: t8x8,
5602                    gmv,
5603                    gref,
5604                });
5605                EdcJob::InterNoRes(b)
5606            } else {
5607                // Pooled box (see `take_pinter_job`); the CAVLC arm still parses into
5608                // locals, so this is a copy, not an allocation.
5609                // The pool hands out a blank job when empty, so the fallback
5610                // here is the same thing `take_pinter_job` would have produced.
5611                // Reached only if the `defer && !nores` binding above did not
5612                // run, which it always does on this branch -- but that is an
5613                // argument, not a type, so do not spend a panic on it.
5614                let mut job = job.take().unwrap_or_else(|| Box::new(PInterJob::blank()));
5615                job.mbx = mb_x;
5616                job.mby = mb_y;
5617                job.qp = qp;
5618                job.cbp_chroma = cbp_chroma;
5619                job.t8 = t8x8;
5620                job.gmv = gmv;
5621                job.gref = gref;
5622                // luma_scan / luma8 / cac were parsed in place.
5623                job.cdc = c_recon_dc;
5624                job.nnzs = nnzs;
5625                EdcJob::Inter(job)
5626            };
5627            if self.edc_tx.is_some() {
5628                self.edc_giveback();
5629                self.edc_send_job(ej);
5630            } else {
5631                self.edc_jobs.push(ej);
5632            }
5633        } else {
5634            self.add_inter_residual(
5635                mb_x,
5636                mb_y,
5637                pred_y,
5638                c_pred,
5639                Some(&*luma_scan),
5640                t8x8.then_some(&*luma8),
5641                &c_recon_dc,
5642                Some(&*cac),
5643                cbp_chroma,
5644                &nnzs,
5645            );
5646            // Give the scratch planes back (taken only on this arm).
5647            self.scratch_luma = s_luma;
5648            self.scratch_luma8 = s_luma8;
5649            self.scratch_cac = s_cac;
5650        }
5651
5652        // MV grid + coded flags were set per partition; mark modes as DC.
5653        // Four contiguous cells per row - `fill`, not sixteen indexed stores.
5654        for lby in 0..4usize {
5655            let a = (mb_y * 4 + lby) * w4 + mb_x * 4;
5656            self.modes_y[a..a + 4].fill(2);
5657        }
5658        Ok(())
5659    }
5660
5661    // ---------------------------------------------------------------------
5662    // B-slice macroblock decoding
5663    // ---------------------------------------------------------------------
5664
5665    /// Per-list (`list` 0 or 1) MV-prediction neighbors for the block region at
5666    /// `(pbx, pby)` of width `pwb` blocks — the L0/L1 analogue of
5667    /// `mv_neighbors_block`.
5668    fn mv_neighbors_list(
5669        &self,
5670        pbx: isize,
5671        pby: isize,
5672        pwb: isize,
5673        list: usize,
5674    ) -> [MvNeighbor; 3] {
5675        self.mv_neighbors_block_grid(pbx, pby, pwb, list)
5676    }
5677
5678    /// Spatial-direct A/B/C at the MB origin — same for every 8×8 in the MB
5679    /// (spec §8.4.1.2.2). B_8x8 callers hoist this once; 16×16 skip/direct walk
5680    /// once inside `decode_b_direct`.
5681    #[inline]
5682    fn b_direct_nbrs(&self, mb_x: usize, mb_y: usize) -> ([MvNeighbor; 3], [MvNeighbor; 3]) {
5683        self.mv_neighbors_both((mb_x * 4) as isize, (mb_y * 4) as isize, 4)
5684    }
5685
5686    /// FUSED dual-list gather at arbitrary partition geometry: A/B/C positions
5687    /// and their availability (bounds + coded + slice) are list-independent —
5688    /// compute once, load both lists' grids from the same resolved index.
5689    fn mv_neighbors_both(
5690        &self,
5691        pbx: isize,
5692        pby: isize,
5693        pwb: isize,
5694    ) -> ([MvNeighbor; 3], [MvNeighbor; 3]) {
5695        let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
5696        let get2 = |bx: isize, by: isize| -> (MvNeighbor, MvNeighbor) {
5697            if bx < 0
5698                || by < 0
5699                || bx >= w4
5700                || by >= h4
5701                || !self
5702                    .coded_y
5703                    .get((by * w4 + bx) as usize)
5704                    .copied()
5705                    .unwrap_or(false)
5706                || !self.nbr_in_slice(bx as usize / 4, by as usize / 4)
5707            {
5708                (MvNeighbor::NONE, MvNeighbor::NONE)
5709            } else {
5710                // FOUR PARALLEL GRIDS at one index. The `coded_y` guard above
5711                // bounds none of them — they are separate Vecs — so each carried
5712                // its own panic path. `MvNeighbor::NONE` is the established
5713                // "no such neighbour" value, so the fallible form degrades into
5714                // the branch directly above it.
5715                let idx = (by * w4 + bx) as usize;
5716                match (
5717                    self.mv_y.get(idx),
5718                    self.ref_idx_y.get(idx),
5719                    self.mv1.get(idx),
5720                    self.ref_idx1.get(idx),
5721                ) {
5722                    (Some(&m0), Some(&r0), Some(&m1), Some(&r1)) => (
5723                        MvNeighbor {
5724                            available: true,
5725                            mv: (i32::from(m0.0), i32::from(m0.1)),
5726                            ref_idx: i32::from(r0),
5727                        },
5728                        MvNeighbor {
5729                            available: true,
5730                            mv: (i32::from(m1.0), i32::from(m1.1)),
5731                            ref_idx: i32::from(r1),
5732                        },
5733                    ),
5734                    _ => (MvNeighbor::NONE, MvNeighbor::NONE),
5735                }
5736            }
5737        };
5738        let (a0, a1) = get2(pbx - 1, pby);
5739        let (b0, b1) = get2(pbx, pby - 1);
5740        let (mut c0, mut c1) = get2(pbx + pwb, pby - 1);
5741        if !c0.available {
5742            // The C fallback is position-driven (topright unavailable ⇒
5743            // topleft), so both lists fall back together — same decision the
5744            // two per-list gathers made independently.
5745            let t = get2(pbx - 1, pby - 1);
5746            c0 = t.0;
5747            c1 = t.1;
5748        }
5749        ([a0, b0, c0], [a1, b1, c1])
5750    }
5751
5752    fn mv_neighbors_block_grid(
5753        &self,
5754        pbx: isize,
5755        pby: isize,
5756        pwb: isize,
5757        list: usize,
5758    ) -> [MvNeighbor; 3] {
5759        let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
5760        let (mvg, refg) = if list == 0 {
5761            (&self.mv_y, &self.ref_idx_y)
5762        } else {
5763            (&self.mv1, &self.ref_idx1)
5764        };
5765        let get = |bx: isize, by: isize| -> MvNeighbor {
5766            if bx < 0
5767                || by < 0
5768                || bx >= w4
5769                || by >= h4
5770                || !self
5771                    .coded_y
5772                    .get((by * w4 + bx) as usize)
5773                    .copied()
5774                    .unwrap_or(false)
5775                || !self.nbr_in_slice(bx as usize / 4, by as usize / 4)
5776            {
5777                MvNeighbor::NONE
5778            } else {
5779                let idx = (by * w4 + bx) as usize;
5780                match (mvg.get(idx), refg.get(idx)) {
5781                    (Some(&m), Some(&r)) => MvNeighbor {
5782                        available: true,
5783                        mv: (i32::from(m.0), i32::from(m.1)),
5784                        ref_idx: i32::from(r),
5785                    },
5786                    _ => MvNeighbor::NONE,
5787                }
5788            }
5789        };
5790        let a = get(pbx - 1, pby);
5791        let b = get(pbx, pby - 1);
5792        let mut c = get(pbx + pwb, pby - 1);
5793        if !c.available {
5794            c = get(pbx - 1, pby - 1);
5795        }
5796        [a, b, c]
5797    }
5798
5799    /// `colZeroFlag` for the 4×4 block at absolute block coords `(bx, by)`: true
5800    /// when `RefPicList1[0]` is a short-term picture whose co-located block uses
5801    /// reference 0 with a near-zero motion vector (spec §8.4.1.2.2).
5802    /// Co-located 4x4 block coords for the current block's `(bx4, by4)` within the
5803    /// macroblock, per spec 8.4.1.2.1. Under `direct_8x8_inference_flag` every 4x4
5804    /// in an 8x8 takes that 8x8's OUTER CORNER (`luma4x4BlkIdx = 5 * mbPartIdx`,
5805    /// i.e. (0,0) (3,0) (0,3) (3,3)); otherwise motion is genuinely per-4x4.
5806    ///
5807    /// 8.4.1.2.1 is SHARED by both direct modes, so spatial and temporal must map
5808    /// identically. They did not: temporal mapped the corner and spatial read the
5809    /// block's own coords, which is invisible while every 4x4 in the co-located 8x8
5810    /// carries the same motion -- true of every stream until sub-8x8 P partitions
5811    /// (x264 `--partitions p4x4`) make them differ. Hence one function.
5812    #[inline]
5813    fn col_block(&self, bx4: usize, by4: usize) -> (usize, usize) {
5814        if self.direct_8x8_inference {
5815            ((bx4 / 2) * 3, (by4 / 2) * 3)
5816        } else {
5817            (bx4, by4)
5818        }
5819    }
5820
5821    fn col_zero(&self, bx: usize, by: usize) -> bool {
5822        // Fast path (1T frozen colocated, the default): the invariant checks
5823        // were hoisted to set_b_context; two grid loads + the threshold test.
5824        if self.col_ok {
5825            let Some(col) = self.refs1.first() else {
5826                return false;
5827            };
5828            let idx = by * self.col_w4 + bx;
5829            // `.get` on EVERY parallel array, not a length test on one of them.
5830            // The `idx >= ref_idx.len()` guard proved nothing about `mv`,
5831            // `ref_idx1` or `mv1` — separate Vecs with independent lengths — so
5832            // each of those reads carried its own panic path. They are built
5833            // together and always agree; the fallible form states that instead of
5834            // asserting it, and keeps the "no panic on malformed input" contract.
5835            let (cref, cmv) = match col.ref_idx.get(idx) {
5836                Some(&r0) if r0 >= 0 => match col.mv.get(idx) {
5837                    Some(&m) => (r0, m),
5838                    None => return false,
5839                },
5840                Some(_) => match (col.ref_idx1.get(idx), col.mv1.get(idx)) {
5841                    (Some(&r1), Some(&m)) if r1 >= 0 => (r1, m),
5842                    _ => return false,
5843                },
5844                None => return false,
5845            };
5846            return cref == 0 && cmv.0.abs() <= 1 && cmv.1.abs() <= 1;
5847        }
5848        let Some(col) = self.refs1.first() else {
5849            return false;
5850        };
5851        if let Some(live) = col.live.as_ref() {
5852            col.wait_motion_ready();
5853            let meta = live.meta.read_pf();
5854            if meta.long_term || meta.w4 == 0 {
5855                return false;
5856            }
5857            let idx = by * meta.w4 + bx;
5858            // Same `.get`-per-array shape as the frozen-colocated path above.
5859            let (cref, cmv) = match meta.ref_idx.get(idx) {
5860                Some(&r0) if r0 >= 0 => match meta.mv.get(idx) {
5861                    Some(&m) => (r0, m),
5862                    None => return false,
5863                },
5864                Some(_) => match (meta.ref_idx1.get(idx), meta.mv1.get(idx)) {
5865                    (Some(&r1), Some(&m)) if r1 >= 0 => (r1, m),
5866                    _ => return false,
5867                },
5868                None => return false,
5869            };
5870            return cref == 0 && cmv.0.abs() <= 1 && cmv.1.abs() <= 1;
5871        }
5872        if col.long_term || col.w4 == 0 {
5873            return false;
5874        }
5875        let idx = by * col.w4 + bx;
5876        if idx >= col.ref_idx.len() {
5877            return false;
5878        }
5879        let (cref, cmv) = match col.ref_idx.get(idx) {
5880            Some(&r0) if r0 >= 0 => match col.mv.get(idx) {
5881                Some(&m) => (r0, m),
5882                None => return false,
5883            },
5884            Some(_) => match (col.ref_idx1.get(idx), col.mv1.get(idx)) {
5885                (Some(&r1), Some(&m)) if r1 >= 0 => (r1, m),
5886                _ => return false,
5887            },
5888            None => return false,
5889        };
5890        cref == 0 && cmv.0.abs() <= 1 && cmv.1.abs() <= 1
5891    }
5892
5893    /// Implicit bi-prediction weights `(w0, w1)` from POC distances (spec
5894    /// §8.4.2.3.2), or `None` for the plain average (idc≠2, uni-pred, or the
5895    /// equidistant / out-of-range fall-back to 32:32 which equals the average).
5896    fn implicit_weights(&self, refi0: i32, refi1: i32) -> Option<(i32, i32)> {
5897        if self.weighted_bipred_idc != 2 || refi0 < 0 || refi1 < 0 {
5898            return None;
5899        }
5900        let (Some(r0), Some(r1)) = (
5901            self.refs.get(refi0 as usize),
5902            self.refs1.get(refi1 as usize),
5903        ) else {
5904            return None;
5905        };
5906        let td = (r1.pic_poc() - r0.pic_poc()).clamp(-128, 127);
5907        let tb = (self.cur_poc - r0.pic_poc()).clamp(-128, 127);
5908        if td == 0 || r0.long_term || r1.long_term {
5909            return None; // 32:32 → identical to the average
5910        }
5911        let tx = tx_for_td(td);
5912        let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
5913        let w1 = dsf >> 2;
5914        if !(-64..=128).contains(&w1) {
5915            return None; // out of range → 32:32 average
5916        }
5917        Some((64 - w1, w1))
5918    }
5919
5920    /// Motion-compensates a region with the given per-list refs/MVs. Bi-prediction
5921    /// is the simple `(a+b+1)>>1` average, or POC-weighted when implicit weighting
5922    /// (idc 2) is active. Writes into `pred_y`/`c_pred`.
5923    fn b_mc(
5924        &self,
5925        mb_x: usize,
5926        mb_y: usize,
5927        px: usize,
5928        py: usize,
5929        rw: usize,
5930        rh: usize,
5931        refi0: i32,
5932        mv0: (i32, i32),
5933        refi1: i32,
5934        mv1: (i32, i32),
5935        pred_y: &mut [u8; 256],
5936        c_pred: &mut [[u8; 64]; 2],
5937    ) {
5938        let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBMc);
5939        // Malformed-stream armor, mirroring the P path: now that B slices actually
5940        // PARSE ref_idx (they used to be hardcoded to 0), a mutated stream can hand
5941        // us an index past the end of either list. Clamp rather than panic — the
5942        // crate is `forbid(unsafe_code)` and fuzz-gated to never panic, and a
5943        // wrong picture on garbage input carries no conformance duty.
5944        let refi0 = if refi0 >= 0 {
5945            (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32
5946        } else {
5947            -1
5948        };
5949        let refi1 = if refi1 >= 0 {
5950            (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32
5951        } else {
5952            -1
5953        };
5954        if (refi0 >= 0 && self.refs.is_empty()) || (refi1 >= 0 && self.refs1.is_empty()) {
5955            return;
5956        }
5957        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
5958        let weights = {
5959            let _gw = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBWeights);
5960            self.implicit_weights(refi0, refi1)
5961        };
5962        // Bi-prediction blend: the weights decision is LOOP-INVARIANT, so every
5963        // blend site below matches on `weights` ONCE and runs a branch-free
5964        // pixel loop — the unweighted `(p+q+1)>>1` average then autovectorizes
5965        // (the per-pixel closure this replaces hid the invariant behind a
5966        // capture, and its chroma form was a &dyn call PER PIXEL).
5967        // FULL-WIDTH regions (px == 0, rw == 16 — every 16×16/16×8 partition and
5968        // most direct regions) occupy contiguous rows of `pred_y`, so MC writes
5969        // the destination DIRECTLY: uni-pred needs no staging at all, bi-pred
5970        // stages only the second list and blends in place. The staging arrays
5971        // (512 B zeroed per call before this) now exist only on the branches
5972        // that read them. Same fusion as the P path's mc_rect (WHYS Part 8).
5973        let full = px == 0 && rw == 16;
5974        if refi0 >= 0
5975            && refi1 >= 0
5976            && mv0.0 % 4 == 0
5977            && mv0.1 % 4 == 0
5978            && mv1.0 % 4 == 0
5979            && mv1.1 % 4 == 0
5980        {
5981            edcstat::bump(&edcstat::BMC_BI_FP, 1);
5982        }
5983        let _gl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBLuma);
5984        // One scratch borrow for the whole region — both bi-pred passes included.
5985        // The closure yields whether the arm already ran the chroma half (the
5986        // bi-pred full-width arm does, to keep its staging alive) — a plain
5987        // `return` inside would exit the CLOSURE only and chroma would run twice.
5988        let chroma_done =
5989            rusty_h264_common::inter::with_mc_scratch(|scr| match (refi0 >= 0, refi1 >= 0, full) {
5990                (true, false, true) => {
5991                    let Some(rf) = self.refs.get(refi0 as usize) else {
5992                        return false;
5993                    };
5994                    rusty_h264_common::inter::mc_luma_padded_pre(
5995                        scr,
5996                        &rf.luma_guard(rf.ch),
5997                        rf.lstride(),
5998                        crate::LPAD,
5999                        self.cw,
6000                        ch,
6001                        mb_x * 16,
6002                        mb_y * 16 + py,
6003                        rw,
6004                        rh,
6005                        mv0.0,
6006                        mv0.1,
6007                        &mut pred_y[py * 16..py * 16 + rw * rh],
6008                    );
6009                    false
6010                }
6011                (false, true, true) => {
6012                    let Some(rf) = self.refs1.get(refi1 as usize) else {
6013                        return false;
6014                    };
6015                    rusty_h264_common::inter::mc_luma_padded_pre(
6016                        scr,
6017                        &rf.luma_guard(rf.ch),
6018                        rf.lstride(),
6019                        crate::LPAD,
6020                        self.cw,
6021                        ch,
6022                        mb_x * 16,
6023                        mb_y * 16 + py,
6024                        rw,
6025                        rh,
6026                        mv1.0,
6027                        mv1.1,
6028                        &mut pred_y[py * 16..py * 16 + rw * rh],
6029                    );
6030                    false
6031                }
6032                (true, true, true) => {
6033                    let Some(rf) = self.refs.get(refi0 as usize) else {
6034                        return false;
6035                    };
6036                    rusty_h264_common::inter::mc_luma_padded_pre(
6037                        scr,
6038                        &rf.luma_guard(rf.ch),
6039                        rf.lstride(),
6040                        crate::LPAD,
6041                        self.cw,
6042                        ch,
6043                        mb_x * 16,
6044                        mb_y * 16 + py,
6045                        rw,
6046                        rh,
6047                        mv0.0,
6048                        mv0.1,
6049                        &mut pred_y[py * 16..py * 16 + rw * rh],
6050                    );
6051                    let mut b = [0u8; 256];
6052                    let Some(rf) = self.refs1.get(refi1 as usize) else {
6053                        return false;
6054                    };
6055                    rusty_h264_common::inter::mc_luma_padded_pre(
6056                        scr,
6057                        &rf.luma_guard(rf.ch),
6058                        rf.lstride(),
6059                        crate::LPAD,
6060                        self.cw,
6061                        ch,
6062                        mb_x * 16,
6063                        mb_y * 16 + py,
6064                        rw,
6065                        rh,
6066                        mv1.0,
6067                        mv1.1,
6068                        &mut b[..rw * rh],
6069                    );
6070                    drop(_gl);
6071                    let _gbl =
6072                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
6073                    // SLICE-then-zip: proving the bounds ONCE lets rustc emit the whole
6074                    // 256-byte average as 8 straight-line vpavgb ops (verified in
6075                    // isolation, x86-64-v3); the indexed form kept a per-iteration
6076                    // bounds check and a loop. A hand AVX2 kernel is refuted — the
6077                    // compiler already emits the ideal instruction.
6078                    let dst = &mut pred_y[py * 16..py * 16 + rw * rh];
6079                    match weights {
6080                        None => {
6081                            for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
6082                                *d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
6083                            }
6084                        }
6085                        Some((w0, w1)) => {
6086                            for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
6087                                *d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255)
6088                                    as u8;
6089                            }
6090                        }
6091                    }
6092                    let _gc =
6093                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
6094                    self.b_mc_chroma(
6095                        mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch,
6096                    );
6097                    true
6098                }
6099                _ => {
6100                    // Narrow region — rows are strided in `pred_y`; stage and copy.
6101                    let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
6102                    if refi0 >= 0 {
6103                        let Some(rf) = self.refs.get(refi0 as usize) else {
6104                            return false;
6105                        };
6106                        rusty_h264_common::inter::mc_luma_padded_pre(
6107                            scr,
6108                            &rf.luma_guard(rf.ch),
6109                            rf.lstride(),
6110                            crate::LPAD,
6111                            self.cw,
6112                            ch,
6113                            mb_x * 16 + px,
6114                            mb_y * 16 + py,
6115                            rw,
6116                            rh,
6117                            mv0.0,
6118                            mv0.1,
6119                            &mut a[..rw * rh],
6120                        );
6121                    }
6122                    if refi1 >= 0 {
6123                        let Some(rf) = self.refs1.get(refi1 as usize) else {
6124                            return false;
6125                        };
6126                        rusty_h264_common::inter::mc_luma_padded_pre(
6127                            scr,
6128                            &rf.luma_guard(rf.ch),
6129                            rf.lstride(),
6130                            crate::LPAD,
6131                            self.cw,
6132                            ch,
6133                            mb_x * 16 + px,
6134                            mb_y * 16 + py,
6135                            rw,
6136                            rh,
6137                            mv1.0,
6138                            mv1.1,
6139                            &mut b[..rw * rh],
6140                        );
6141                    }
6142                    drop(_gl);
6143                    let _gbl =
6144                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
6145                    match (refi0 >= 0, refi1 >= 0) {
6146                        (true, true) => {
6147                            for dy in 0..rh {
6148                                let (ar, br) =
6149                                    (&a[dy * rw..dy * rw + rw], &b[dy * rw..dy * rw + rw]);
6150                                let base = (py + dy) * 16 + px;
6151                                let dst = &mut pred_y[base..base + rw];
6152                                match weights {
6153                                    None => {
6154                                        for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
6155                                            *d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
6156                                        }
6157                                    }
6158                                    Some((w0, w1)) => {
6159                                        for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
6160                                            *d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6)
6161                                                .clamp(0, 255)
6162                                                as u8;
6163                                        }
6164                                    }
6165                                }
6166                            }
6167                        }
6168                        (true, false) => {
6169                            for dy in 0..rh {
6170                                let d = (py + dy) * 16 + px;
6171                                pred_y[d..d + rw].copy_from_slice(&a[dy * rw..dy * rw + rw]);
6172                            }
6173                        }
6174                        _ => {
6175                            for dy in 0..rh {
6176                                let d = (py + dy) * 16 + px;
6177                                pred_y[d..d + rw].copy_from_slice(&b[dy * rw..dy * rw + rw]);
6178                            }
6179                        }
6180                    }
6181                    false
6182                }
6183            });
6184        if chroma_done {
6185            return;
6186        }
6187        let _gc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
6188        self.b_mc_chroma(
6189            mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch,
6190        );
6191    }
6192
6193    /// Chroma half of `b_mc`, with the same full-width direct-write fusion
6194    /// (crw == 8 rows are contiguous in the 8-wide `c_pred` planes).
6195    /// Chroma half of `b_mc`. U and V share every piece of MC geometry, so
6196    /// each list is ONE `mc_chroma_padded_pair` call (setup + range check paid
6197    /// once, kernels unchanged) instead of two per-plane calls — the per-plane
6198    /// pixel math is byte-identical to the old per-plane flow.
6199    fn b_mc_chroma(
6200        &self,
6201        mb_x: usize,
6202        mb_y: usize,
6203        px: usize,
6204        py: usize,
6205        rw: usize,
6206        rh: usize,
6207        refi0: i32,
6208        mv0: (i32, i32),
6209        refi1: i32,
6210        mv1: (i32, i32),
6211        c_pred: &mut [[u8; 64]; 2],
6212        weights: Option<(i32, i32)>,
6213        cch: usize,
6214    ) {
6215        use rusty_h264_common::inter::mc_chroma_padded_pair;
6216        let (crx, cry, crw, crh) = (px / 2, py / 2, rw / 2, rh / 2);
6217        let full = crx == 0 && crw == 8;
6218        let n = crw * crh;
6219        // RESOLVE BOTH SLOTS ONCE, then let the match patterns bind them. Every
6220        // arm below re-indexed `refs`/`refs1` by a raw `refi` — six checked Vec
6221        // indexes across the four arms — although `a0`/`a1` already meant
6222        // exactly "this slot is present". Carrying `Option<&RefPic>` instead of
6223        // a bool makes "present" and "in range" the same fact.
6224        let rf0 = (refi0 >= 0)
6225            .then(|| self.refs.get(refi0 as usize))
6226            .flatten();
6227        let rf1 = (refi1 >= 0)
6228            .then(|| self.refs1.get(refi1 as usize))
6229            .flatten();
6230        let (a0, a1) = (rf0.is_some(), rf1.is_some());
6231        let [cu, cv] = c_pred;
6232        match (a0, a1, full) {
6233            (true, false, true) | (false, true, true) => {
6234                let (Some(rf), mv) = (if a0 { rf0 } else { rf1 }, if a0 { mv0 } else { mv1 })
6235                else {
6236                    return;
6237                };
6238                let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
6239                mc_chroma_padded_pair(
6240                    &gu,
6241                    &gv,
6242                    rf.cstride(),
6243                    crate::CPAD,
6244                    self.ccw,
6245                    cch,
6246                    mb_x * 8,
6247                    mb_y * 8 + cry,
6248                    crw,
6249                    crh,
6250                    mv.0,
6251                    mv.1,
6252                    &mut cu[cry * 8..cry * 8 + n],
6253                    &mut cv[cry * 8..cry * 8 + n],
6254                );
6255            }
6256            (true, true, true) => {
6257                let (Some(rfa), Some(rfb)) = (rf0, rf1) else {
6258                    return;
6259                };
6260                {
6261                    let rf = rfa;
6262                    let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
6263                    mc_chroma_padded_pair(
6264                        &gu,
6265                        &gv,
6266                        rf.cstride(),
6267                        crate::CPAD,
6268                        self.ccw,
6269                        cch,
6270                        mb_x * 8,
6271                        mb_y * 8 + cry,
6272                        crw,
6273                        crh,
6274                        mv0.0,
6275                        mv0.1,
6276                        &mut cu[cry * 8..cry * 8 + n],
6277                        &mut cv[cry * 8..cry * 8 + n],
6278                    );
6279                }
6280                let (mut bu, mut bv) = ([0u8; 64], [0u8; 64]);
6281                {
6282                    let rf = rfb;
6283                    let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
6284                    mc_chroma_padded_pair(
6285                        &gu,
6286                        &gv,
6287                        rf.cstride(),
6288                        crate::CPAD,
6289                        self.ccw,
6290                        cch,
6291                        mb_x * 8,
6292                        mb_y * 8 + cry,
6293                        crw,
6294                        crh,
6295                        mv1.0,
6296                        mv1.1,
6297                        &mut bu[..n],
6298                        &mut bv[..n],
6299                    );
6300                }
6301                for (dst, stage) in [(&mut *cu, &bu), (&mut *cv, &bv)] {
6302                    let d = &mut dst[cry * 8..cry * 8 + n];
6303                    match weights {
6304                        None => {
6305                            for (o, q) in d.iter_mut().zip(&stage[..n]) {
6306                                *o = ((*o as u16 + *q as u16 + 1) >> 1) as u8;
6307                            }
6308                        }
6309                        Some((w0, w1)) => {
6310                            for (o, q) in d.iter_mut().zip(&stage[..n]) {
6311                                *o = ((*o as i32 * w0 + *q as i32 * w1 + 32) >> 6).clamp(0, 255)
6312                                    as u8;
6313                            }
6314                        }
6315                    }
6316                }
6317            }
6318            _ => {
6319                // Narrow region — rows are strided in the 8-wide pred planes;
6320                // stage per list (paired), then copy/blend strided per plane.
6321                let (mut au, mut av, mut bu, mut bv) = ([0u8; 64], [0u8; 64], [0u8; 64], [0u8; 64]);
6322                if let Some(rf) = rf0 {
6323                    let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
6324                    mc_chroma_padded_pair(
6325                        &gu,
6326                        &gv,
6327                        rf.cstride(),
6328                        crate::CPAD,
6329                        self.ccw,
6330                        cch,
6331                        mb_x * 8 + crx,
6332                        mb_y * 8 + cry,
6333                        crw,
6334                        crh,
6335                        mv0.0,
6336                        mv0.1,
6337                        &mut au[..n],
6338                        &mut av[..n],
6339                    );
6340                }
6341                if let Some(rf) = rf1 {
6342                    let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
6343                    mc_chroma_padded_pair(
6344                        &gu,
6345                        &gv,
6346                        rf.cstride(),
6347                        crate::CPAD,
6348                        self.ccw,
6349                        cch,
6350                        mb_x * 8 + crx,
6351                        mb_y * 8 + cry,
6352                        crw,
6353                        crh,
6354                        mv1.0,
6355                        mv1.1,
6356                        &mut bu[..n],
6357                        &mut bv[..n],
6358                    );
6359                }
6360                for (dst, sa, sb) in [(&mut *cu, &au, &bu), (&mut *cv, &av, &bv)] {
6361                    for dy in 0..crh {
6362                        let base = (cry + dy) * 8 + crx;
6363                        let d = &mut dst[base..base + crw];
6364                        match (a0, a1) {
6365                            (true, true) => {
6366                                let (pr, qr) =
6367                                    (&sa[dy * crw..dy * crw + crw], &sb[dy * crw..dy * crw + crw]);
6368                                match weights {
6369                                    None => {
6370                                        for ((o, pp), q) in d.iter_mut().zip(pr).zip(qr) {
6371                                            *o = ((*pp as u16 + *q as u16 + 1) >> 1) as u8;
6372                                        }
6373                                    }
6374                                    Some((w0, w1)) => {
6375                                        for ((o, pp), q) in d.iter_mut().zip(pr).zip(qr) {
6376                                            *o = ((*pp as i32 * w0 + *q as i32 * w1 + 32) >> 6)
6377                                                .clamp(0, 255)
6378                                                as u8;
6379                                        }
6380                                    }
6381                                }
6382                            }
6383                            (true, false) => d.copy_from_slice(&sa[dy * crw..dy * crw + crw]),
6384                            _ => d.copy_from_slice(&sb[dy * crw..dy * crw + crw]),
6385                        }
6386                    }
6387                }
6388            }
6389        }
6390    }
6391
6392    /// Commits a region's per-list motion to the 4×4 grids (and marks coded).
6393    fn b_set_motion(
6394        &mut self,
6395        mb_x: usize,
6396        mb_y: usize,
6397        px: usize,
6398        py: usize,
6399        rw: usize,
6400        rh: usize,
6401        refi0: i32,
6402        mv0: (i32, i32),
6403        refi1: i32,
6404        mv1: (i32, i32),
6405    ) {
6406        let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBSet);
6407        let w4 = self.mb_w * 4;
6408        let mv0w = if refi0 >= 0 { mv0 } else { (0, 0) };
6409        let mv1w = if refi1 >= 0 { mv1 } else { (0, 0) };
6410        let by0 = mb_y * 4 + py / 4;
6411        let bx0 = mb_x * 4 + px / 4;
6412        let (bw, bh) = (rw / 4, rh / 4);
6413        // Contiguous per-row slices: fill instead of per-4x4 stores (same values).
6414        // SEVEN parallel grids, seven separate range checks — they are disjoint
6415        // fields of `self`, so one `get_mut` apiece resolves them all together
6416        // and a short row simply writes nothing rather than panicking.
6417        for by in by0..by0 + bh {
6418            let row = by * w4 + bx0;
6419            let end = row + bw;
6420            let (Some(r0), Some(m0), Some(r1), Some(m1)) = (
6421                self.ref_idx_y.get_mut(row..end),
6422                self.mv_y.get_mut(row..end),
6423                self.ref_idx1.get_mut(row..end),
6424                self.mv1.get_mut(row..end),
6425            ) else {
6426                continue;
6427            };
6428            r0.fill(refi0 as i8);
6429            m0.fill((mv0w.0 as i16, mv0w.1 as i16));
6430            r1.fill(refi1 as i8);
6431            m1.fill((mv1w.0 as i16, mv1w.1 as i16));
6432            let (Some(it), Some(cd), Some(md)) = (
6433                self.inter_y.get_mut(row..end),
6434                self.coded_y.get_mut(row..end),
6435                self.modes_y.get_mut(row..end),
6436            ) else {
6437                continue;
6438            };
6439            it.fill(true);
6440            cd.fill(true);
6441            md.fill(2);
6442        }
6443    }
6444
6445    /// Spatial direct prediction for a region (whole MB or an 8×8): derives the
6446    /// per-list reference indices and base MVs, then motion-compensates each 4×4
6447    /// sub-block (applying `colZeroFlag`) and commits the motion (spec §8.4.1.2.2).
6448    /// Splits a `w`×`h` block region (4×4-block units) into the fewest rectangles
6449    /// whose contents are `uniform`, preferring partition-shaped cuts (whole →
6450    /// horizontal halves → vertical halves → quadrants). Emits at most w·h rects
6451    /// (the all-different worst case degenerates to per-block, i.e. the old loop).
6452    fn coalesce_region<
6453        U: Fn(usize, usize, usize, usize) -> bool,
6454        E: FnMut(usize, usize, usize, usize),
6455    >(
6456        x: usize,
6457        y: usize,
6458        w: usize,
6459        h: usize,
6460        uniform: &U,
6461        emit: &mut E,
6462    ) {
6463        if uniform(x, y, w, h) {
6464            emit(x, y, w, h);
6465            return;
6466        }
6467        if h > 1 && uniform(x, y, w, h / 2) && uniform(x, y + h / 2, w, h / 2) {
6468            emit(x, y, w, h / 2);
6469            emit(x, y + h / 2, w, h / 2);
6470            return;
6471        }
6472        if w > 1 && uniform(x, y, w / 2, h) && uniform(x + w / 2, y, w / 2, h) {
6473            emit(x, y, w / 2, h);
6474            emit(x + w / 2, y, w / 2, h);
6475            return;
6476        }
6477        match (w > 1, h > 1) {
6478            (true, true) => {
6479                for q in 0..4usize {
6480                    Self::coalesce_region(
6481                        x + (q % 2) * (w / 2),
6482                        y + (q / 2) * (h / 2),
6483                        w / 2,
6484                        h / 2,
6485                        uniform,
6486                        emit,
6487                    );
6488                }
6489            }
6490            (true, false) => {
6491                Self::coalesce_region(x, y, w / 2, h, uniform, emit);
6492                Self::coalesce_region(x + w / 2, y, w / 2, h, uniform, emit);
6493            }
6494            (false, true) => {
6495                Self::coalesce_region(x, y, w, h / 2, uniform, emit);
6496                Self::coalesce_region(x, y + h / 2, w, h / 2, uniform, emit);
6497            }
6498            (false, false) => emit(x, y, 1, 1),
6499        }
6500    }
6501
6502    fn decode_b_direct(
6503        &mut self,
6504        mb_x: usize,
6505        mb_y: usize,
6506        px: usize,
6507        py: usize,
6508        rw: usize,
6509        rh: usize,
6510        pred_y: &mut [u8; 256],
6511        c_pred: &mut [[u8; 64]; 2],
6512    ) {
6513        if !self.direct_spatial {
6514            return self.decode_b_direct_temporal(mb_x, mb_y, px, py, rw, rh, pred_y, c_pred);
6515        }
6516        let (n0, n1) = self.b_direct_nbrs(mb_x, mb_y);
6517        self.decode_b_direct_n(mb_x, mb_y, px, py, rw, rh, pred_y, c_pred, n0, n1);
6518    }
6519
6520    /// Spec §8.4.1.2.2: the spatial-direct reference indices (min positive over
6521    /// the three neighbors, per list) and the predicted MVs. `direct_zero` is
6522    /// the both-lists-unavailable case, which forces ref 0 / mv (0,0). Shared
6523    /// by `decode_b_direct_n` and the B_Skip zero-bi fast path — ONE derivation,
6524    /// no drift.
6525    #[inline]
6526    fn b_direct_refs_mvs(
6527        n0: &[MvNeighbor; 3],
6528        n1: &[MvNeighbor; 3],
6529    ) -> (i32, i32, (i32, i32), (i32, i32), bool) {
6530        let min_pos = |a: i32, b: i32| {
6531            if a < 0 {
6532                b
6533            } else if b < 0 {
6534                a
6535            } else {
6536                a.min(b)
6537            }
6538        };
6539        let rid = |n: &[MvNeighbor; 3]| min_pos(min_pos(n[0].ref_idx, n[1].ref_idx), n[2].ref_idx);
6540        let (mut refi0, mut refi1) = (rid(n0), rid(n1));
6541        let direct_zero = refi0 < 0 && refi1 < 0;
6542        if direct_zero {
6543            refi0 = 0;
6544            refi1 = 0;
6545        }
6546        let mv0 = if refi0 >= 0 && !direct_zero {
6547            predict_mv(n0[0], n0[1], n0[2], refi0)
6548        } else {
6549            (0, 0)
6550        };
6551        let mv1 = if refi1 >= 0 && !direct_zero {
6552            predict_mv(n1[0], n1[1], n1[2], refi1)
6553        } else {
6554            (0, 0)
6555        };
6556        (refi0, refi1, mv0, mv1, direct_zero)
6557    }
6558
6559    fn decode_b_direct_n(
6560        &mut self,
6561        mb_x: usize,
6562        mb_y: usize,
6563        px: usize,
6564        py: usize,
6565        rw: usize,
6566        rh: usize,
6567        pred_y: &mut [u8; 256],
6568        c_pred: &mut [[u8; 64]; 2],
6569        n0: [MvNeighbor; 3],
6570        n1: [MvNeighbor; 3],
6571    ) {
6572        let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBDirect);
6573        // H-48: DERIVATION-ONLY scope, dropped before the MC loop below. DecBDirect
6574        // wraps this function whole and therefore INCLUDES the `b_mc` calls it makes,
6575        // so its 1460 ns/call was never "MV derivation is slow" — that read was wrong.
6576        // This guard is what separates the two.
6577        let gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBDeriv);
6578        let derived = Self::b_direct_refs_mvs(&n0, &n1);
6579        drop(gd);
6580        self.b_direct_region(mb_x, mb_y, px, py, rw, rh, pred_y, c_pred, derived);
6581    }
6582
6583    /// The post-derivation half of spatial direct: czg probing (gated), region
6584    /// coalescing, MC + motion commit. Split out so B_8x8 direct subs derive
6585    /// ONCE per MB (the A/B/C neighbours and the rid/median result are
6586    /// MB-level; only czg varies per 8x8).
6587    fn b_direct_region(
6588        &mut self,
6589        mb_x: usize,
6590        mb_y: usize,
6591        px: usize,
6592        py: usize,
6593        rw: usize,
6594        rh: usize,
6595        pred_y: &mut [u8; 256],
6596        c_pred: &mut [[u8; 64]; 2],
6597        derived: (i32, i32, (i32, i32), (i32, i32), bool),
6598    ) {
6599        let (refi0, refi1, mv0, mv1, direct_zero) = derived;
6600        // Per 4×4 sub-block: colZeroFlag zeroes the ref-0 motion vector. cz is the
6601        // ONLY per-block variable (two possible (m0,m1) values for the region), and
6602        // the MC filters + bi-blend are per-output-pixel — so sub-blocks with equal
6603        // cz coalesce into one wider `b_mc`, BIT-IDENTICAL. A 16×16 direct MB paid
6604        // 16 bi-pred b_mc calls (~96 MC kernel entries) before this; typically 1 now.
6605        let (bx0, by0, bw, bh) = (px / 4, py / 4, rw / 4, rh / 4);
6606        // MASKED at every use below. The loop bounds are the region's `bw`/`bh` in
6607        // 4x4 units, which are always <= 4 for a macroblock but are RUNTIME values,
6608        // so none of the eight indexes into this fixed 4x4 grid was provable.
6609        let mut czg = [[false; 4]; 4]; // region-local, [dy][dx]
6610                                       // colZeroFlag can only change a list whose ref is 0 AND whose predicted
6611                                       // MV is nonzero (it zeroes MVs; zeroing (0,0) is a no-op). When neither
6612                                       // list qualifies, skip the probing entirely: czg stays false, the
6613                                       // region is uniform, and the m values are identical — the only change
6614                                       // is FEWER b_mc calls where the old czg would have split rects with
6615                                       // equal values (tiles of the same math, byte-identical).
6616        let cz_matters = (refi0 == 0 && mv0 != (0, 0)) || (refi1 == 0 && mv1 != (0, 0));
6617        // Uniformity is KNOWN after the probe fill — when every probe agreed,
6618        // the 16-bool coalesce scan and its recursion are skipped outright.
6619        let mut cz_mixed = false;
6620        if cz_matters && !direct_zero && self.direct_8x8_inference {
6621            // Under direct_8x8_inference every 4×4 in an 8×8 shares one colZeroFlag
6622            // (col_block collapses to the MB-corner). Probe once per 8×8 — same
6623            // czg, fewer wait_motion_ready / meta locks on the live-ref path.
6624            let mut oy = 0usize;
6625            while oy < bh {
6626                let h = (bh - oy).min(2);
6627                let mut ox = 0usize;
6628                while ox < bw {
6629                    let w = (bw - ox).min(2);
6630                    let (colx, coly) = self.col_block(bx0 + ox, by0 + oy);
6631                    let cz = self.col_zero(mb_x * 4 + colx, mb_y * 4 + coly);
6632                    cz_mixed |= cz != czg[0][0] && !(ox == 0 && oy == 0);
6633                    for dy in oy..oy + h {
6634                        for dx in ox..ox + w {
6635                            czg[dy & 3][dx & 3] = cz;
6636                        }
6637                    }
6638                    ox += w;
6639                }
6640                oy += h;
6641            }
6642        } else if cz_matters && !direct_zero {
6643            for dy in 0..bh {
6644                for dx in 0..bw {
6645                    let (colx, coly) = self.col_block(bx0 + dx, by0 + dy);
6646                    let cz = self.col_zero(mb_x * 4 + colx, mb_y * 4 + coly);
6647                    cz_mixed |= cz != czg[0][0] && !(dx == 0 && dy == 0);
6648                    czg[dy & 3][dx & 3] = cz;
6649                }
6650            }
6651        }
6652        let mut rects: [(usize, usize, usize, usize); 16] = [(0, 0, 0, 0); 16];
6653        let mut n = 0usize;
6654        if !cz_mixed {
6655            rects[0] = (0, 0, bw, bh);
6656            n = 1;
6657        } else {
6658            let uniform = |x: usize, y: usize, w: usize, h: usize| -> bool {
6659                let t = czg[y & 3][x & 3];
6660                (y..y + h).all(|dy| (x..x + w).all(|dx| czg[dy & 3][dx & 3] == t))
6661            };
6662            Self::coalesce_region(0, 0, bw, bh, &uniform, &mut |x, y, w, h| {
6663                rects[n & 15] = (x, y, w, h);
6664                n += 1;
6665            });
6666        }
6667        let recording = self.edc_regions.is_some();
6668        if rw == 16 && rh == 16 {
6669            edcstat::bump(&edcstat::BSK_FULLMB, 1);
6670            if n == 1 {
6671                edcstat::bump(&edcstat::BSK_1RECT, 1);
6672                // WHOLE-MACROBLOCK, SINGLE RECTANGLE: `derived` is one motion set
6673                // for the region, `cz` is the only per-block variable, and `n == 1`
6674                // means every block agreed on it. So all sixteen blocks share one
6675                // (ref0, ref1, mv0, mv1) -- exactly what `mb_uniform` scans six
6676                // motion planes to establish. Record it and the derivation skips
6677                // that kernel. Asserted against the kernel on the whole corpus by
6678                // bench/verify_hint_sweep.sh.
6679                if let Some(p) = self.mb_umot.get_mut(mb_y * self.mb_w + mb_x) {
6680                    *p = true;
6681                }
6682                let cz = czg[0][0];
6683                let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
6684                let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
6685                let (a0, a1) = (refi0 >= 0, refi1 >= 0);
6686                if a0 && a1 && m0 == (0, 0) && m1 == (0, 0) {
6687                    edcstat::bump(&edcstat::BSK_ZBI, 1);
6688                } else if (a0 != a1) && (if a0 { m0 } else { m1 }) == (0, 0) {
6689                    edcstat::bump(&edcstat::BSK_ZUNI, 1);
6690                } else if (!a0 || (m0.0 % 4 == 0 && m0.1 % 4 == 0))
6691                    && (!a1 || (m1.0 % 4 == 0 && m1.1 % 4 == 0))
6692                {
6693                    edcstat::bump(&edcstat::BSK_FP, 1);
6694                }
6695                if self.bsk_last == Some((mb_x.wrapping_sub(1), mb_y, refi0, refi1, m0, m1)) {
6696                    edcstat::bump(&edcstat::BSK_RUNCONT, 1);
6697                }
6698                self.bsk_last = Some((mb_x, mb_y, refi0, refi1, m0, m1));
6699            } else {
6700                self.bsk_last = None;
6701            }
6702        }
6703        for &(x, y, w, h) in &rects[..n] {
6704            let cz = czg[y & 3][x & 3];
6705            let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
6706            let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
6707            let (lx, ly, lw, lh) = ((bx0 + x) * 4, (by0 + y) * 4, w * 4, h * 4);
6708            if recording {
6709                self.b_mc_or_record(
6710                    mb_x, mb_y, lx, ly, lw, lh, refi0, m0, refi1, m1, pred_y, c_pred,
6711                );
6712            } else {
6713                self.b_mc(
6714                    mb_x, mb_y, lx, ly, lw, lh, refi0, m0, refi1, m1, pred_y, c_pred,
6715                );
6716            }
6717            self.b_set_motion(mb_x, mb_y, lx, ly, lw, lh, refi0, m0, refi1, m1);
6718        }
6719    }
6720
6721    /// Temporal direct prediction for a region (spec §8.4.1.2.3): for each 4×4
6722    /// (or per-8×8 corner under `direct_8x8_inference`), take the co-located
6723    /// List-0 motion from `RefPicList1[0]`, map its reference into the current
6724    /// List-0 by POC, and scale the motion vector by the POC distances.
6725    fn decode_b_direct_temporal(
6726        &mut self,
6727        mb_x: usize,
6728        mb_y: usize,
6729        px: usize,
6730        py: usize,
6731        rw: usize,
6732        rh: usize,
6733        pred_y: &mut [u8; 256],
6734        c_pred: &mut [[u8; 64]; 2],
6735    ) {
6736        let poc1 = self.refs1.first().map_or(0, |f| f.pic_poc());
6737        let infer = self.direct_8x8_inference;
6738        // Under direct_8x8_inference every 4×4 in an 8×8 takes the same MB-corner
6739        // co-located motion, so motion-compensate the whole 8×8 in one call — this
6740        // hits the width-8 MC asm and pays the per-call tile/blend setup 4× less.
6741        // Without inference, motion is genuinely per-4×4. Bit-identical either way
6742        // (MC of an 8×8 with one MV == four 4×4 MCs with that same MV).
6743        let step = if infer { 8 } else { 4 };
6744        let mut sy = py;
6745        while sy < py + rh {
6746            let mut sx = px;
6747            while sx < px + rw {
6748                // Co-located 4×4 (the 8×8's MB-corner under inference) — shared with
6749                // the spatial path's colZeroFlag, which must map identically.
6750                let (colx, coly) = self.col_block(sx / 4, sy / 4);
6751                let (mvcol, refpoc) = {
6752                    let Some(col) = self.refs1.first() else {
6753                        return;
6754                    };
6755                    if let Some(live) = col.live.as_ref() {
6756                        col.wait_motion_ready();
6757                        let meta = live.meta.read_pf();
6758                        let idx = (mb_y * 4 + coly) * meta.w4 + (mb_x * 4 + colx);
6759                        // `mv` and `ref_poc` are PARALLEL Vecs: the `mv.len()`
6760                        // guard said nothing about `ref_poc`.
6761                        // `ref_poc[idx]` was `poc_lut[ref_idx[idx]]` materialised
6762                        // per block; do the lookup instead. The bounds test is the
6763                        // same one the expansion applied when it was built, so an
6764                        // out-of-range index still reads i32::MIN.
6765                        match (meta.mv.get(idx), meta.ref_idx.get(idx)) {
6766                            (Some(&m), Some(&r)) if meta.w4 != 0 => {
6767                                let pc = if (0..32).contains(&r) {
6768                                    meta.poc_lut[r as usize & 31]
6769                                } else {
6770                                    i32::MIN
6771                                };
6772                                if pc != i32::MIN {
6773                                    (m, pc)
6774                                } else {
6775                                    ((0, 0), i32::MIN)
6776                                }
6777                            }
6778                            _ => ((0, 0), i32::MIN),
6779                        }
6780                    } else {
6781                        let idx = (mb_y * 4 + coly) * col.w4 + (mb_x * 4 + colx);
6782                        // intra co-located → zero motion, refIdxL0 = 0
6783                        match (col.mv.get(idx), col.ref_idx.get(idx)) {
6784                            (Some(&m), Some(&r)) if col.w4 != 0 => {
6785                                let pc = if (0..32).contains(&r) {
6786                                    col.poc_lut[r as usize & 31]
6787                                } else {
6788                                    i32::MIN
6789                                };
6790                                if pc != i32::MIN {
6791                                    (m, pc)
6792                                } else {
6793                                    ((0, 0), i32::MIN)
6794                                }
6795                            }
6796                            _ => ((0, 0), i32::MIN),
6797                        }
6798                    }
6799                };
6800                // MapColToList0: the current-list index of the co-located reference.
6801                let (refi0, mvc) = if refpoc == i32::MIN {
6802                    (0, (0, 0))
6803                } else {
6804                    let r = self
6805                        .refs
6806                        .iter()
6807                        .position(|f| f.pic_poc() == refpoc)
6808                        .unwrap_or(0) as i32;
6809                    (r, mvcol)
6810                };
6811                let Some(r0) = self.refs.get(refi0 as usize) else {
6812                    return;
6813                };
6814                let poc0 = r0.pic_poc();
6815                let td = (poc1 - poc0).clamp(-128, 127);
6816                let tb = (self.cur_poc - poc0).clamp(-128, 127);
6817                let (mv0, mv1) = if td == 0 || r0.long_term {
6818                    ((i32::from(mvc.0), i32::from(mvc.1)), (0, 0))
6819                } else {
6820                    let tx = tx_for_td(td);
6821                    let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
6822                    let m0 = ((dsf * i32::from(mvc.0) + 128) >> 8, (dsf * i32::from(mvc.1) + 128) >> 8);
6823                    (m0, (m0.0 - i32::from(mvc.0), m0.1 - i32::from(mvc.1)))
6824                };
6825                self.b_mc_or_record(
6826                    mb_x, mb_y, sx, sy, step, step, refi0, mv0, 0, mv1, pred_y, c_pred,
6827                );
6828                self.b_set_motion(mb_x, mb_y, sx, sy, step, step, refi0, mv0, 0, mv1);
6829                sx += step;
6830            }
6831            sy += step;
6832        }
6833    }
6834
6835    /// Reads `ref_idx_lX` for a B partition (te(v)/ue(v) by the list's active
6836    /// count), bounds-checked against the available reference count.
6837    fn read_b_ref(&self, r: &mut BitReader, list: usize) -> Result<i32, MbError> {
6838        let (active, avail) = if list == 0 {
6839            (self.num_ref_active, self.refs.len())
6840        } else {
6841            (self.num_ref_active1, self.refs1.len())
6842        };
6843        let v = if active > 1 {
6844            read_ref_idx(r, active)?
6845        } else {
6846            0
6847        };
6848        if v as usize >= avail {
6849            return Err(MbError::Truncated);
6850        }
6851        Ok(v)
6852    }
6853
6854    /// Reconstructs a `B_Skip` macroblock: spatial-direct prediction, no residual.
6855    /// Defer one fast B_Skip's grid commit + recon into the pending row span.
6856    /// Continuation requires position adjacency AND kind equality.
6857    #[inline]
6858    fn bz_push(&mut self, mb_x: usize, mb_y: usize, kind: BzKind) {
6859        // EVERY `BzKind` IS A UNIFORM WHOLE-MACROBLOCK MOTION SET, by its own
6860        // definition: `ZeroBi` is ref 0 in both lists at (0,0), `ZeroUni` is one
6861        // active list at (0,0), and `Fp` is the uniform full-pel direct case. So
6862        // the sixteen blocks of any macroblock queued here agree in all six motion
6863        // planes -- exactly what the deblock derivation's `mb_uniform` kernel
6864        // scans 256 bytes to establish. Marked at the single push site so no
6865        // caller can add a fourth kind and silently miss it.
6866        //
6867        // This is the class the B-skip band path carries, and on a talking-head
6868        // stream it is the LARGE one: FourPeople routes 404,755 macroblocks
6869        // through here against 24,525 through the direct-region marking.
6870        if let Some(p) = self.mb_umot.get_mut(mb_y * self.mb_w + mb_x) {
6871            *p = true;
6872        }
6873        match self.bzspan {
6874            Some((row, x0, ref mut n, k)) if row == mb_y && x0 + *n == mb_x && k == kind => *n += 1,
6875            _ => {
6876                self.bz_flush();
6877                self.bzspan = Some((mb_y, mb_x, 1, kind));
6878            }
6879        }
6880    }
6881
6882    /// Range-fill the pending span's grids. MUST run before anything reads
6883    /// the motion/coded/mode/nnz grids of a deferred MB: decode_b_mb entry,
6884    /// decode_b_skip's b_direct_nbrs, bS derivation (derive_bs_row callers
6885    /// via row_hook + deblock), and slice end (edc_flush covers it).
6886    #[inline(always)]
6887    fn bz_flush(&mut self) {
6888        if self.bzspan.is_some() {
6889            self.bz_flush_slow();
6890        }
6891    }
6892
6893    fn bz_flush_slow(&mut self) {
6894        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BSpanRecon);
6895        let Some((row, x0, n, kind)) = self.bzspan.take() else {
6896            return;
6897        };
6898        edcstat::bump(&edcstat::BZ_SPANS, 1);
6899        edcstat::bump(&edcstat::BZ_SPAN_MBS, n as u64);
6900        // Grid values per kind (mirrors b_set_motion's inactive-list zeroing).
6901        let (g_r0, g_r1, g_m0, g_m1): (i32, i32, (i32, i32), (i32, i32)) = match kind {
6902            BzKind::ZeroBi => (0, 0, (0, 0), (0, 0)),
6903            BzKind::ZeroUni(0, ri) => (ri as i32, -1, (0, 0), (0, 0)),
6904            BzKind::ZeroUni(_, ri) => (-1, ri as i32, (0, 0), (0, 0)),
6905            BzKind::Fp { r0, r1, m0, m1 } => {
6906                let mm0 = if r0 >= 0 {
6907                    (m0.0 as i32, m0.1 as i32)
6908                } else {
6909                    (0, 0)
6910                };
6911                let mm1 = if r1 >= 0 {
6912                    (m1.0 as i32, m1.1 as i32)
6913                } else {
6914                    (0, 0)
6915                };
6916                (r0 as i32, r1 as i32, mm0, mm1)
6917            }
6918        };
6919        let w4 = self.mb_w * 4;
6920        let (b0, len) = (x0 * 4, n * 4);
6921        for dy in 0..4 {
6922            let a = (row * 4 + dy) * w4 + b0;
6923            self.ref_idx_y[a..a + len].fill(g_r0 as i8);
6924            self.mv_y[a..a + len].fill((g_m0.0 as i16, g_m0.1 as i16));
6925            self.ref_idx1[a..a + len].fill(g_r1 as i8);
6926            self.mv1[a..a + len].fill((g_m1.0 as i16, g_m1.1 as i16));
6927            self.inter_y[a..a + len].fill(true);
6928            self.coded_y[a..a + len].fill(true);
6929            self.modes_y[a..a + len].fill(2);
6930            self.nnz_y[a..a + len].fill(0);
6931        }
6932        match kind {
6933            BzKind::ZeroBi => self.bz_recon_band_bi(row, x0, n),
6934            BzKind::ZeroUni(list, ri) => {
6935                self.bz_recon_band_copy(row, x0, n, list as usize, ri as usize, (0, 0))
6936            }
6937            BzKind::Fp { r0, r1, m0, m1 } => {
6938                let mv0 = (m0.0 as i32, m0.1 as i32);
6939                let mv1 = (m1.0 as i32, m1.1 as i32);
6940                match (r0 >= 0, r1 >= 0) {
6941                    (true, true) => {
6942                        self.bz_recon_band_fp_bi(row, x0, n, r0 as usize, r1 as usize, mv0, mv1)
6943                    }
6944                    (true, false) => self.bz_recon_band_copy(row, x0, n, 0, r0 as usize, mv0),
6945                    _ => self.bz_recon_band_copy(row, x0, n, 1, r1 as usize, mv1),
6946                }
6947            }
6948        }
6949    }
6950
6951    /// ZeroBi band: rows of 16n averaged from both padded refs (index 0).
6952    fn bz_recon_band_bi(&mut self, row: usize, x0: usize, n: usize) {
6953        // Byte-identical to n recon_b_skip_zero_bi(_, _, 0, 0) calls: each
6954        // output byte is the same (a + b + 1) >> 1 of the same source bytes.
6955        let w = n * 16;
6956        let Some(rf0) = self.refs.first() else { return };
6957        let Some(rf1) = self.refs1.first() else {
6958            return;
6959        };
6960        let (l0st, l1st) = (rf0.lstride(), rf1.lstride());
6961        {
6962            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
6963            let (ly0, ly1) = (rf0.luma_guard(rf0.ch), rf1.luma_guard(rf1.ch));
6964            for dy in 0..16 {
6965                let y = row * 16 + dy;
6966                let s0 = (y + crate::LPAD) * l0st + crate::LPAD + x0 * 16;
6967                let s1 = (y + crate::LPAD) * l1st + crate::LPAD + x0 * 16;
6968                let d = y * self.cw + x0 * 16;
6969                // WIN: this bi-average was a scalar byte loop; `pixel_avg` computes
6970                // (a + b + 1) >> 1 exactly with pavgb, and the row helper walks the
6971                // span in kernel-legal widths.
6972                rusty_h264_common::inter::avg_row_into(
6973                    &ly0[s0..s0 + w],
6974                    &ly1[s1..s1 + w],
6975                    w,
6976                    &mut self.rec_y[d..d + w],
6977                );
6978            }
6979        }
6980        let (c0st, c1st) = (rf0.cstride(), rf1.cstride());
6981        let wc = n * 8;
6982        for c in 0..2 {
6983            let rc0 = rf0.chroma_guard(c, rf0.ch);
6984            let rc1 = rf1.chroma_guard(c, rf1.ch);
6985            let plane = if c == 0 {
6986                &mut self.rec_u
6987            } else {
6988                &mut self.rec_v
6989            };
6990            for dy in 0..8 {
6991                let y = row * 8 + dy;
6992                let s0 = (y + crate::CPAD) * c0st + crate::CPAD + x0 * 8;
6993                let s1 = (y + crate::CPAD) * c1st + crate::CPAD + x0 * 8;
6994                let d = y * self.ccw + x0 * 8;
6995                // WIN: this bi-average was a scalar byte loop; `pixel_avg` computes
6996                // (a + b + 1) >> 1 exactly with pavgb, and the row helper walks the
6997                // span in kernel-legal widths.
6998                rusty_h264_common::inter::avg_row_into(
6999                    &rc0[s0..s0 + wc],
7000                    &rc1[s1..s1 + wc],
7001                    wc,
7002                    &mut plane[d..d + wc],
7003                );
7004            }
7005        }
7006    }
7007
7008    /// Full-pel offset copy band from ONE list's padded planes. The caller
7009    /// prevalidated (per MB, at push): mv%8 == 0 both components and luma +
7010    /// chroma windows inside the padded planes — contiguous MB windows tile,
7011    /// so the span's union window is valid by induction.
7012    fn bz_recon_band_copy(
7013        &mut self,
7014        row: usize,
7015        x0: usize,
7016        n: usize,
7017        list: usize,
7018        ri: usize,
7019        mv: (i32, i32),
7020    ) {
7021        let Some(rf) = (if list == 0 {
7022            self.refs.get(ri)
7023        } else {
7024            self.refs1.get(ri)
7025        }) else {
7026            return;
7027        };
7028        let lst = rf.lstride();
7029        let (dx, dy) = ((mv.0 / 4) as isize, (mv.1 / 4) as isize);
7030        let w = n * 16;
7031        {
7032            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
7033            let ly = rf.luma_guard(rf.ch);
7034            for r in 0..16 {
7035                let y = (row * 16 + r) as isize + dy;
7036                let src = ((y + crate::LPAD as isize) * lst as isize
7037                    + crate::LPAD as isize
7038                    + x0 as isize * 16
7039                    + dx) as usize;
7040                let d = (row * 16 + r) * self.cw + x0 * 16;
7041                self.rec_y[d..d + w].copy_from_slice(&ly[src..src + w]);
7042            }
7043        }
7044        let cst = rf.cstride();
7045        let (cdx, cdy) = ((mv.0 / 8) as isize, (mv.1 / 8) as isize);
7046        let wc = n * 8;
7047        for c in 0..2 {
7048            let rc = rf.chroma_guard(c, rf.ch);
7049            let plane = if c == 0 {
7050                &mut self.rec_u
7051            } else {
7052                &mut self.rec_v
7053            };
7054            for r in 0..8 {
7055                let y = (row * 8 + r) as isize + cdy;
7056                let src = ((y + crate::CPAD as isize) * cst as isize
7057                    + crate::CPAD as isize
7058                    + x0 as isize * 8
7059                    + cdx) as usize;
7060                let d = (row * 8 + r) * self.ccw + x0 * 8;
7061                plane[d..d + wc].copy_from_slice(&rc[src..src + wc]);
7062            }
7063        }
7064    }
7065
7066    /// Full-pel bi band: rows of 16n averaged from two offset windows
7067    /// (prevalidated as above; implicit weights None/(32,32) guaranteed by
7068    /// the pushing arm).
7069    fn bz_recon_band_fp_bi(
7070        &mut self,
7071        row: usize,
7072        x0: usize,
7073        n: usize,
7074        r0: usize,
7075        r1: usize,
7076        mv0: (i32, i32),
7077        mv1: (i32, i32),
7078    ) {
7079        let (Some(rf0), Some(rf1)) = (self.refs.get(r0), self.refs1.get(r1)) else {
7080            return;
7081        };
7082        let (l0, l1) = (rf0.lstride(), rf1.lstride());
7083        let off = |st: usize, pad: isize, yy: isize, xx: isize| {
7084            (((yy + pad) * st as isize) + pad + xx) as usize
7085        };
7086        let w = n * 16;
7087        {
7088            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
7089            let (ly0, ly1) = (rf0.luma_guard(rf0.ch), rf1.luma_guard(rf1.ch));
7090            for r in 0..16 {
7091                let y = (row * 16 + r) as isize;
7092                let s0 = off(
7093                    l0,
7094                    crate::LPAD as isize,
7095                    y + (mv0.1 / 4) as isize,
7096                    x0 as isize * 16 + (mv0.0 / 4) as isize,
7097                );
7098                let s1 = off(
7099                    l1,
7100                    crate::LPAD as isize,
7101                    y + (mv1.1 / 4) as isize,
7102                    x0 as isize * 16 + (mv1.0 / 4) as isize,
7103                );
7104                let d = (row * 16 + r) * self.cw + x0 * 16;
7105                // WIN: this bi-average was a scalar byte loop; `pixel_avg` computes
7106                // (a + b + 1) >> 1 exactly with pavgb, and the row helper walks the
7107                // span in kernel-legal widths.
7108                rusty_h264_common::inter::avg_row_into(
7109                    &ly0[s0..s0 + w],
7110                    &ly1[s1..s1 + w],
7111                    w,
7112                    &mut self.rec_y[d..d + w],
7113                );
7114            }
7115        }
7116        let (c0, c1) = (rf0.cstride(), rf1.cstride());
7117        let wc = n * 8;
7118        for c in 0..2 {
7119            let rc0 = rf0.chroma_guard(c, rf0.ch);
7120            let rc1 = rf1.chroma_guard(c, rf1.ch);
7121            let plane = if c == 0 {
7122                &mut self.rec_u
7123            } else {
7124                &mut self.rec_v
7125            };
7126            for r in 0..8 {
7127                let y = (row * 8 + r) as isize;
7128                let s0 = off(
7129                    c0,
7130                    crate::CPAD as isize,
7131                    y + (mv0.1 / 8) as isize,
7132                    x0 as isize * 8 + (mv0.0 / 8) as isize,
7133                );
7134                let s1 = off(
7135                    c1,
7136                    crate::CPAD as isize,
7137                    y + (mv1.1 / 8) as isize,
7138                    x0 as isize * 8 + (mv1.0 / 8) as isize,
7139                );
7140                let d = (row * 8 + r) * self.ccw + x0 * 8;
7141                // WIN: this bi-average was a scalar byte loop; `pixel_avg` computes
7142                // (a + b + 1) >> 1 exactly with pavgb, and the row helper walks the
7143                // span in kernel-legal widths.
7144                rusty_h264_common::inter::avg_row_into(
7145                    &rc0[s0..s0 + wc],
7146                    &rc1[s1..s1 + wc],
7147                    wc,
7148                    &mut plane[d..d + wc],
7149                );
7150            }
7151        }
7152    }
7153
7154    /// Per-MB prevalidation for an Fp span push: mv%8 both components (chroma
7155    /// copies at mv/8) and luma + chroma windows inside the padded planes for
7156    /// every active list. False ⇒ the arm recons immediately instead.
7157    fn bz_fp_valid(
7158        &self,
7159        mbx: usize,
7160        mby: usize,
7161        r0: Option<usize>,
7162        r1: Option<usize>,
7163        mv0: (i32, i32),
7164        mv1: (i32, i32),
7165    ) -> bool {
7166        let chk = |rf: &crate::Ref, mv: (i32, i32)| -> bool {
7167            if mv.0 % 8 != 0 || mv.1 % 8 != 0 {
7168                return false;
7169            }
7170            let lst = rf.lstride();
7171            let lpad = crate::LPAD as isize;
7172            let cx = mbx as isize * 16 + (mv.0 / 4) as isize + lpad;
7173            let cy = mby as isize * 16 + (mv.1 / 4) as isize + lpad;
7174            let lrows = rf.lrows() as isize;
7175            if cx < 0 || cx + 16 > lst as isize || cy < 0 || cy + 16 > lrows {
7176                return false;
7177            }
7178            let cst = rf.cstride();
7179            let cpad = crate::CPAD as isize;
7180            let ccx = mbx as isize * 8 + (mv.0 / 8) as isize + cpad;
7181            let ccy = mby as isize * 8 + (mv.1 / 8) as isize + cpad;
7182            let crows = rf.crows() as isize;
7183            ccx >= 0 && ccx + 8 <= cst as isize && ccy >= 0 && ccy + 8 <= crows
7184        };
7185        r0.is_none_or(|i| self.refs.get(i).is_some_and(|r| chk(r, mv0)))
7186            && r1.is_none_or(|i| self.refs1.get(i).is_some_and(|r| chk(r, mv1)))
7187    }
7188
7189    /// Defer one (0,0) P_Skip's grid commit into the pending P span.
7190    #[inline]
7191    fn pz_push(&mut self, mb_x: usize, mb_y: usize, recon: bool) {
7192        match self.pzspan {
7193            Some((row, x0, ref mut n, r)) if row == mb_y && x0 + *n == mb_x && r == recon => {
7194                *n += 1
7195            }
7196            _ => {
7197                self.pz_flush();
7198                self.pzspan = Some((mb_y, mb_x, 1, recon));
7199            }
7200        }
7201    }
7202
7203    /// Range-fill the pending P span's grids (the deferral constants of
7204    /// decode_p_skip's commit: list-0 ref 0, mv (0,0), inter, coded, DC mode,
7205    /// deblock kind SKIP). Same flush points as the B span (span_flush).
7206    #[inline(always)]
7207    fn pz_flush(&mut self) {
7208        if self.pzspan.is_some() {
7209            self.pz_flush_slow();
7210        }
7211    }
7212
7213    fn pz_flush_slow(&mut self) {
7214        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BSpanRecon);
7215        let Some((row, x0, n, recon)) = self.pzspan.take() else {
7216            return;
7217        };
7218        edcstat::bump(&edcstat::PZ_SPANS, 1);
7219        edcstat::bump(&edcstat::PZ_SPAN_MBS, n as u64);
7220        let w4 = self.mb_w * 4;
7221        let (b0, len) = (x0 * 4, n * 4);
7222        for dy in 0..4 {
7223            let a = (row * 4 + dy) * w4 + b0;
7224            self.mv_y[a..a + len].fill((0, 0));
7225            self.inter_y[a..a + len].fill(true);
7226            self.ref_idx_y[a..a + len].fill(0);
7227            self.coded_y[a..a + len].fill(true);
7228            self.modes_y[a..a + len].fill(2);
7229        }
7230        self.mb_kind[row * self.mb_w + x0..row * self.mb_w + x0 + n]
7231            .fill(rusty_h264_common::deblock::MB_KIND_SKIP);
7232        if recon {
7233            // The recon deferred with the commit: one band copy from ref[0]
7234            // replaces n queued EdcJob::Skip pushes AND the flush-time
7235            // run-coalescing scan that used to rediscover this very run.
7236            self.recon_p_skip_band(x0, row, n);
7237        }
7238    }
7239
7240    /// Flush BOTH pending grid spans — the one call every grid reader makes.
7241    #[inline]
7242    fn span_flush(&mut self) {
7243        self.bz_flush();
7244        self.pz_flush();
7245    }
7246
7247    /// Hot prefix of decode_b_skip: the FORCED zero-bi run continuation,
7248    /// small enough to inline into the slice loops. Returns true when the MB
7249    /// was fully handled (deferred into the span); false falls to the cold
7250    /// body. Exactly the forced arm's conditions — no behavior change.
7251    #[inline(always)]
7252    fn b_skip_hot(&mut self, mb_x: usize, mb_y: usize) -> bool {
7253        if !self.direct_spatial || self.edc_tx.is_some() || no_bskipfast() {
7254            return false;
7255        }
7256        let mbw = self.mb_w;
7257        let addr = mb_y * mbw + mb_x;
7258        // ONE slice that ENDS at `addr` proves all three neighbour reads. Each
7259        // was a separate check against the whole `bzero` grid even though every
7260        // index is strictly below `addr` (the guards above establish that), and
7261        // the trailing `bzero[addr] = true` a fourth. `get(..=addr)` states the
7262        // bound once, in the form the neighbour indexes are already inside.
7263        // The `get(..=addr)` slice did NOT fold these: it bounds the reads from
7264        // above, but LLVM still has to chain `mb_x > 0 => addr >= 1` and
7265        // `mb_y > 0 => addr >= mbw` through the `&&` sequence, and it will not.
7266        // Fallible reads state each one where it is made; a missing neighbour is
7267        // "not zero", which is the conservative answer this guard wants.
7268        let up = addr.wrapping_sub(mbw);
7269        if !(mb_x > 0
7270            && mb_y > 0
7271            && mb_x + 1 < mbw
7272            && up >= self.slice_first_mb
7273            && self.bzero.get(addr - 1).copied().unwrap_or(false)
7274            && self.bzero.get(up).copied().unwrap_or(false)
7275            && self.bzero.get(up + 1).copied().unwrap_or(false))
7276        {
7277            return false;
7278        }
7279        let wgt = self.iw00();
7280        if !(wgt.is_none() || wgt == Some((32, 32))) {
7281            return false;
7282        }
7283        self.route_skip_mbs += 1;
7284        edcstat::bump(&edcstat::BSKB_FORCED, 1);
7285        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BSkipHot);
7286        edcstat::bump(&edcstat::BSKB_FAST, 1);
7287        self.bz_push(mb_x, mb_y, BzKind::ZeroBi);
7288        if let Some(b) = self.bzero.get_mut(addr) {
7289            *b = true;
7290        }
7291        true
7292    }
7293
7294    /// Zero this macroblock's 16 luma nnz entries, walking rows by adding `w4`
7295    /// instead of recomputing `(mb_y * 4 + dy) * w4 + mb_x * 4` four times.
7296    /// Shared by the three sites in decode_b_skip that had it open-coded.
7297    #[inline]
7298    fn clear_mb_nnz(&mut self, mb_x: usize, mb_y: usize, w4: usize) {
7299        let mut a = (mb_y * 4) * w4 + mb_x * 4;
7300        for _ in 0..4 {
7301            self.nnz_y[a..a + 4].fill(0);
7302            a += w4;
7303        }
7304    }
7305
7306    /// The B_Skip SLOW half: full spatial-direct recon plus the zero-residual
7307    /// plane copy. Split out so `pred_y`/`c_pred` (384 B) are built ONLY when a
7308    /// macroblock actually reaches it — the zero-bi / zero-uni / full-pel fast
7309    /// paths in decode_b_skip all return before this, and on LIGHT streams they
7310    /// take 90%+ of the calls.
7311    fn b_skip_slow(
7312        &mut self,
7313        mb_x: usize,
7314        mb_y: usize,
7315        nbrs: Option<([MvNeighbor; 3], [MvNeighbor; 3])>,
7316        w4: usize,
7317    ) -> Result<(), MbError> {
7318        let mut pred_y = [0u8; 256];
7319        let mut c_pred = [[0u8; 64]; 2];
7320        match nbrs {
7321            // Reuses the already-probed neighbours — the grid walk is the
7322            // expensive half of the derivation and paying it twice leaned on
7323            // fall-through-heavy streams (stockholm).
7324            Some((n0, n1)) => {
7325                self.decode_b_direct_n(mb_x, mb_y, 0, 0, 16, 16, &mut pred_y, &mut c_pred, n0, n1)
7326            }
7327            None => self.decode_b_direct(mb_x, mb_y, 0, 0, 16, 16, &mut pred_y, &mut c_pred),
7328        }
7329        if let Some(regions) = self.edc_regions.take() {
7330            // nnz clears are PARSE state; the pixel copy is the worker's.
7331            self.clear_mb_nnz(mb_x, mb_y, w4);
7332            self.edc_giveback();
7333            self.edc_send_job(EdcJob::BSkip {
7334                mbx: mb_x,
7335                mby: mb_y,
7336                regions,
7337            });
7338            return Ok(());
7339        }
7340        // Zero residual: the prediction IS the reconstruction — copy it row-wise,
7341        // stepping the destination by the stride rather than multiplying per row.
7342        let mut d = (mb_y * 16) * self.cw + mb_x * 16;
7343        for dy in 0..16 {
7344            self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
7345            d += self.cw;
7346        }
7347        let (ccw, cx0) = (self.ccw, mb_x * 8);
7348        for c in 0..2 {
7349            let plane = if c == 0 {
7350                &mut self.rec_u
7351            } else {
7352                &mut self.rec_v
7353            };
7354            let mut d = (mb_y * 8) * ccw + cx0;
7355            for dy in 0..8 {
7356                plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
7357                d += ccw;
7358            }
7359        }
7360        // nnz stays 0 (no residual) — clear the grids for neighbour context.
7361        self.clear_mb_nnz(mb_x, mb_y, w4);
7362        Ok(())
7363    }
7364
7365    fn decode_b_skip(&mut self, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
7366        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BSkipCold);
7367        self.route_skip_mbs += 1;
7368        self.wait_refs_for_mb(mb_y);
7369        // Each list length read ONCE: the emptiness test and every later
7370        // `len() - 1` clamp (six reads in all) come from these two.
7371        let (nref0, nref1) = (self.refs.len(), self.refs1.len());
7372        if nref0 == 0 || nref1 == 0 {
7373            return Err(MbError::Unsupported("B without references"));
7374        }
7375        let (lref0, lref1) = (nref0 - 1, nref1 - 1);
7376        let mbw = self.mb_w;
7377        let addr = mb_y * mbw + mb_x;
7378        let w4 = mbw * 4;
7379        // ZERO-BI FAST PATH: when spatial direct derives (0,0)/(0,0) bi motion,
7380        // colZeroFlag is IRRELEVANT (it only zeroes MVs that are already zero),
7381        // so the colocated probing, region split, b_mc staging and the pred
7382        // round-trip all collapse into one fused row-average from the two
7383        // padded refs straight into the recon planes. b_mc's bi blend applies
7384        // only IMPLICIT weights, so `None` or (32,32) (both exactly
7385        // (a+b+1)>>1) is the whole identity condition. FourPeople-class LIGHT
7386        // streams put 90%+ of their B_Skips here (BSK_ZBI counter).
7387        // pred_y / c_pred now live in `b_skip_slow` — see there.
7388        if self.direct_spatial && self.edc_tx.is_none() && !no_bskipfast() {
7389            // (The FORCED zero-bi run continuation lives in b_skip_hot, inlined
7390            // at the loop call sites — this cold body only sees non-forced MBs.)
7391            // A pending span can only occupy the LEFT gather position, and the
7392            // left member's committed grid values are fully determined by the
7393            // span KIND — synthesize them instead of flushing, for EVERY kind
7394            // (the same mapping bz_flush writes).
7395            let patch = match self.bzspan {
7396                Some((r, x0, n, k)) if r == mb_y && x0 + n == mb_x => Some(k),
7397                _ => None,
7398            };
7399            if patch.is_none() {
7400                self.bz_flush();
7401            }
7402            let (mut n0, mut n1) = self.b_direct_nbrs(mb_x, mb_y);
7403            if let Some(k) = patch {
7404                let (r0, r1, m0, m1): (i32, i32, (i32, i32), (i32, i32)) = match k {
7405                    BzKind::ZeroBi => (0, 0, (0, 0), (0, 0)),
7406                    BzKind::ZeroUni(0, ri) => (ri as i32, -1, (0, 0), (0, 0)),
7407                    BzKind::ZeroUni(_, ri) => (-1, ri as i32, (0, 0), (0, 0)),
7408                    BzKind::Fp { r0, r1, m0, m1 } => {
7409                        let mm0 = if r0 >= 0 {
7410                            (m0.0 as i32, m0.1 as i32)
7411                        } else {
7412                            (0, 0)
7413                        };
7414                        let mm1 = if r1 >= 0 {
7415                            (m1.0 as i32, m1.1 as i32)
7416                        } else {
7417                            (0, 0)
7418                        };
7419                        (r0 as i32, r1 as i32, mm0, mm1)
7420                    }
7421                };
7422                n0[0] = MvNeighbor {
7423                    available: true,
7424                    mv: m0,
7425                    ref_idx: r0,
7426                };
7427                n1[0] = MvNeighbor {
7428                    available: true,
7429                    mv: m1,
7430                    ref_idx: r1,
7431                };
7432            }
7433            let (refi0, refi1, mv0, mv1, _dz) = Self::b_direct_refs_mvs(&n0, &n1);
7434            let (a0, a1) = (refi0 >= 0, refi1 >= 0);
7435            let fast = if a0 && a1 && mv0 == (0, 0) && mv1 == (0, 0) {
7436                // Same malformed-stream ref clamp as b_mc.
7437                let r0 = (refi0 as usize).min(lref0);
7438                let r1 = (refi1 as usize).min(lref1);
7439                // (0,0) dominates here and is already cached slice-wide by iw00.
7440                let wgt = if r0 == 0 && r1 == 0 {
7441                    self.iw00()
7442                } else {
7443                    self.implicit_weights(r0 as i32, r1 as i32)
7444                };
7445                if wgt.is_none() || wgt == Some((32, 32)) {
7446                    if refi0 == 0 && refi1 == 0 {
7447                        if let Some(b) = self.bzero.get_mut(addr) {
7448                            *b = true;
7449                        }
7450                        // Same constants as the forced arm: recon AND commit
7451                        // both defer into the span.
7452                        self.bz_push(mb_x, mb_y, BzKind::ZeroBi);
7453                        edcstat::bump(&edcstat::BSKB_FAST, 1);
7454                        return Ok(());
7455                    }
7456                    self.recon_b_skip_zero_bi(mb_x, mb_y, r0, r1);
7457                    true
7458                } else {
7459                    false
7460                }
7461            } else if a0 != a1 && (if a0 { mv0 } else { mv1 }) == (0, 0) {
7462                // ZERO-UNI: one active list at (0,0) — b_mc's uni arms are plain
7463                // unweighted copies. Defer as a memcpy-band span.
7464                let (list, ri) = if a0 {
7465                    (0u8, (refi0 as usize).min(lref0) as u8)
7466                } else {
7467                    (1u8, (refi1 as usize).min(lref1) as u8)
7468                };
7469                self.bz_push(mb_x, mb_y, BzKind::ZeroUni(list, ri));
7470                edcstat::bump(&edcstat::BSKB_FAST, 1);
7471                return Ok(());
7472            } else if (!a0 || (mv0.0 % 4 == 0 && mv0.1 % 4 == 0))
7473                && (!a1 || (mv1.0 % 4 == 0 && mv1.1 % 4 == 0))
7474                && self.direct_8x8_inference
7475            {
7476                // FULL-PEL arm (the pan case — shields): nonzero MVs make
7477                // colZeroFlag matter, so probe the four 8x8 corners; a UNIFORM
7478                // czg keeps the MB one region and the MC is an offset read.
7479                // Non-uniform czg or an out-of-pad window falls through.
7480                let mut cz_ok = true;
7481                let mut czv = false;
7482                // Same cz-relevance gate as decode_b_direct_n: only a ref-0
7483                // list with a nonzero MV can be changed by colZeroFlag (fp-arm
7484                // MVs are nonzero by construction, so refs alone decide).
7485                let cz_need = refi0 == 0 || refi1 == 0;
7486                // LOOP-INVARIANT: this gated a `break` INSIDE the probe loop, so
7487                // a fact known before the loop was re-tested on every probe.
7488                if cz_need {
7489                    // Absolute block base, hoisted out of the four probes.
7490                    let (bx4, by4) = (mb_x * 4, mb_y * 4);
7491                    for (k, &(ox, oy)) in [(0usize, 0usize), (2, 0), (0, 2), (2, 2)]
7492                        .iter()
7493                        .enumerate()
7494                    {
7495                        // col_block takes MB-LOCAL block coords (the whole MB is
7496                        // the region here); col_zero takes absolute — same
7497                        // convention as decode_b_direct_n's probe loop.
7498                        let (colx, coly) = self.col_block(ox, oy);
7499                        let cz = self.col_zero(bx4 + colx, by4 + coly);
7500                        if k == 0 {
7501                            czv = cz;
7502                        } else if cz != czv {
7503                            cz_ok = false;
7504                            break;
7505                        }
7506                    }
7507                }
7508                if cz_ok {
7509                    let m0 = if refi0 == 0 && czv { (0, 0) } else { mv0 };
7510                    let m1 = if refi1 == 0 && czv { (0, 0) } else { mv1 };
7511                    let r0c = (refi0 as usize).min(lref0);
7512                    let r1c = (refi1 as usize).min(lref1);
7513                    let (or0, or1) = (a0.then_some(r0c), a1.then_some(r1c));
7514                    let wgt_ok = !(a0 && a1) || {
7515                        let wgt = self.implicit_weights(r0c as i32, r1c as i32);
7516                        wgt.is_none() || wgt == Some((32, 32))
7517                    };
7518                    // SPAN path: prevalidated windows + %8 chroma ⇒ defer as
7519                    // an offset copy/avg band; otherwise the immediate per-MB
7520                    // recon below (which handles interpolating chroma).
7521                    if wgt_ok && self.bz_fp_valid(mb_x, mb_y, or0, or1, m0, m1) {
7522                        let kind = BzKind::Fp {
7523                            r0: if a0 { r0c as i8 } else { -1 },
7524                            r1: if a1 { r1c as i8 } else { -1 },
7525                            m0: (m0.0 as i16, m0.1 as i16),
7526                            m1: (m1.0 as i16, m1.1 as i16),
7527                        };
7528                        self.bz_push(mb_x, mb_y, kind);
7529                        edcstat::bump(&edcstat::BSKB_FP, 1);
7530                        return Ok(());
7531                    }
7532                    let ok = wgt_ok && self.recon_b_skip_fp(mb_x, mb_y, or0, or1, m0, m1);
7533                    if ok {
7534                        edcstat::bump(&edcstat::BSKB_FP, 1);
7535                        self.b_set_motion(mb_x, mb_y, 0, 0, 16, 16, refi0, m0, refi1, m1);
7536                        self.clear_mb_nnz(mb_x, mb_y, w4);
7537                        return Ok(());
7538                    }
7539                }
7540                false
7541            } else {
7542                false
7543            };
7544            if fast {
7545                edcstat::bump(&edcstat::BSKB_FAST, 1);
7546                self.b_set_motion(mb_x, mb_y, 0, 0, 16, 16, refi0, (0, 0), refi1, (0, 0));
7547                self.clear_mb_nnz(mb_x, mb_y, w4);
7548                return Ok(());
7549            }
7550            // Fall-through hands the ALREADY-probed neighbours to the slow
7551            // half so the grid walk is not paid twice.
7552            self.b_skip_slow(mb_x, mb_y, Some((n0, n1)), w4)
7553        } else {
7554            if self.edc_tx.is_some() {
7555                self.edc_regions = Some(Vec::with_capacity(4));
7556            }
7557            self.b_skip_slow(mb_x, mb_y, None, w4)
7558        }
7559    }
7560
7561    /// Reconstructs a B macroblock (spec Table 7-14): direct, L0/L1/Bi partitions,
7562    /// `B_8x8`, or intra.
7563    fn decode_b_mb(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
7564        // Non-skip B MBs gather neighbor motion/modes — flush any deferred
7565        // spans before those grids are read.
7566        self.span_flush();
7567        self.wait_refs_for_mb(mb_y);
7568        let mb_type = r.read_ue()?;
7569        if mb_type >= 23 {
7570            return self.decode_intra_mb(r, mb_x, mb_y, mb_type - 23);
7571        }
7572        if self.refs.is_empty() || self.refs1.is_empty() {
7573            return Err(MbError::Unsupported("B without references"));
7574        }
7575        let mut pred_y = [0u8; 256];
7576        let mut c_pred = [[0u8; 64]; 2];
7577
7578        if mb_type == 0 {
7579            // B_Direct_16x16 — 8×8 transform allowed only with direct_8x8_inference.
7580            // FORCED derivation via the zero-bi bitmap (CAVLC parity with the
7581            // CABAC direct arm): gather + rid/median skipped, chains extended.
7582            let addr_f = mb_y * self.mb_w + mb_x;
7583            let forced = self.direct_spatial
7584                && self.edc_tx.is_none()
7585                && mb_x > 0
7586                && mb_y > 0
7587                && mb_x + 1 < self.mb_w
7588                && addr_f - self.mb_w >= self.slice_first_mb
7589                && self.bzero.get(addr_f - 1).copied().unwrap_or(false)
7590                && self
7591                    .bzero
7592                    .get(addr_f.wrapping_sub(self.mb_w))
7593                    .copied()
7594                    .unwrap_or(false)
7595                && self
7596                    .bzero
7597                    .get(addr_f.wrapping_sub(self.mb_w) + 1)
7598                    .copied()
7599                    .unwrap_or(false);
7600            if forced {
7601                self.b_direct_region(
7602                    mb_x,
7603                    mb_y,
7604                    0,
7605                    0,
7606                    16,
7607                    16,
7608                    &mut pred_y,
7609                    &mut c_pred,
7610                    (0, 0, (0, 0), (0, 0), false),
7611                );
7612                if let Some(b) = self.bzero.get_mut(addr_f) {
7613                    *b = true;
7614                }
7615            } else {
7616                self.decode_b_direct(mb_x, mb_y, 0, 0, 16, 16, &mut pred_y, &mut c_pred);
7617            }
7618            return self.inter_finish(
7619                r,
7620                mb_x,
7621                mb_y,
7622                &pred_y,
7623                &c_pred,
7624                self.direct_8x8_inference,
7625                false,
7626            );
7627        }
7628        if mb_type == 22 {
7629            return self.decode_b_8x8(r, mb_x, mb_y);
7630        }
7631
7632        // 16x16 / 16x8 / 8x16 partitions with per-partition L0/L1/Bi.
7633        let (layout, mvmode, preds) = b_inter_layout(mb_type);
7634        // Single 16x16 partition => one motion set for all sixteen blocks in both
7635        // lists; see the CABAC twin of this marking.
7636        if mvmode == 0 {
7637            if let Some(p) = self.mb_umot.get_mut(mb_y * self.mb_w + mb_x) {
7638                *p = true;
7639            }
7640        }
7641        // mb_pred order: ref_idx_l0 (all L0 parts), ref_idx_l1, mvd_l0, mvd_l1.
7642        let mut refi = [[-1i32; 2]; 2]; // [part][list]
7643        for (p, &(_, _, _, _)) in layout.iter().enumerate() {
7644            if preds[p & 1].uses(0) {
7645                refi[p][0] = self.read_b_ref(r, 0)?;
7646            }
7647        }
7648        for (p, _) in layout.iter().enumerate() {
7649            if preds[p & 1].uses(1) {
7650                refi[p][1] = self.read_b_ref(r, 1)?;
7651            }
7652        }
7653        let mut mvd = [[(0i32, 0i32); 2]; 2];
7654        for (p, _) in layout.iter().enumerate() {
7655            if preds[p & 1].uses(0) {
7656                mvd[p][0] = (read_mvd(r)?, read_mvd(r)?);
7657            }
7658        }
7659        for (p, _) in layout.iter().enumerate() {
7660            if preds[p & 1].uses(1) {
7661                mvd[p][1] = (read_mvd(r)?, read_mvd(r)?);
7662            }
7663        }
7664        // Per partition: predict + commit each list's MV, then motion-compensate.
7665        for (p, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
7666            let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
7667            let pwb = (rw / 4) as isize;
7668            let mut mv = [(0i32, 0i32); 2];
7669            for list in 0..2 {
7670                if refi[p][list] >= 0 {
7671                    let n = self.mv_neighbors_list(pbx, pby, pwb, list);
7672                    let pmv = predict_partition_mv(mvmode, p, n[0], n[1], n[2], refi[p][list]);
7673                    mv[list] = (pmv.0 + mvd[p][list].0, pmv.1 + mvd[p][list].1);
7674                }
7675            }
7676            self.b_set_motion(
7677                mb_x,
7678                mb_y,
7679                rx,
7680                ry,
7681                rw,
7682                rh,
7683                refi[p & 3][0],
7684                mv[0],
7685                refi[p & 3][1],
7686                mv[1],
7687            );
7688            // Spec-correct bi-prediction (average of L0 and L1), matching the CABAC
7689            // path. This used to replicate an openh264 bug for a Bi 16x8/8x16
7690            // partition -- openh264 mis-handles the destination buffer there, so
7691            // partition 0 came out List-1-only and partition 1 List-0-only. That was
7692            // deliberate when openh264's h264dec WAS the conformance oracle, but the
7693            // gate is ffmpeg now and the CABAC path already went spec-correct; the
7694            // CAVLC path was simply left behind. Measured: mb_type 12..21 (every B
7695            // 16x8/8x16 with at least one Bi partition) were 100% wrong vs ffmpeg,
7696            // while 1..11 (no Bi partition) were only collaterally damaged.
7697            self.b_mc_or_record(
7698                mb_x,
7699                mb_y,
7700                rx,
7701                ry,
7702                rw,
7703                rh,
7704                refi[p & 3][0],
7705                mv[0],
7706                refi[p & 3][1],
7707                mv[1],
7708                &mut pred_y,
7709                &mut c_pred,
7710            );
7711        }
7712        self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, true, false)
7713    }
7714
7715    /// Reconstructs a `B_8x8` macroblock: four 8×8 sub-macroblock partitions, each
7716    /// direct or L0/L1/Bi with its own sub-partitioning (spec Table 7-18).
7717    fn decode_b_8x8(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
7718        let mut sub = [0u32; 4];
7719        for s in sub.iter_mut() {
7720            let v = r.read_ue()?;
7721            if v > 12 {
7722                return Err(MbError::Unsupported("invalid B sub_mb_type"));
7723            }
7724            *s = v;
7725        }
7726        let mut pred_y = [0u8; 256];
7727        let mut c_pred = [[0u8; 64]; 2];
7728        // ref_idx for all 8×8 partitions (L0 batch, then L1 batch), for the
7729        // non-direct sub-partitions.
7730        let mut refi = [[-1i32; 2]; 4];
7731        for (p, &st) in sub.iter().enumerate() {
7732            if st != 0 && b_sub_uses(st, 0) {
7733                refi[p][0] = self.read_b_ref(r, 0)?;
7734            }
7735        }
7736        for (p, &st) in sub.iter().enumerate() {
7737            if st != 0 && b_sub_uses(st, 1) {
7738                refi[p][1] = self.read_b_ref(r, 1)?;
7739            }
7740        }
7741        // mvd: all mvd_l0 (partition-major, sub-partition order), then all mvd_l1.
7742        //
7743        // FIXED ARRAYS, NOT `Vec::new()` + push. These are per-MACROBLOCK on every
7744        // B_8x8, and a growing Vec allocated (and reallocated) twice per MB — on a
7745        // B-heavy stream that is thousands of allocations per frame for data whose
7746        // maximum size is a compile-time constant: 4 partitions x at most 4
7747        // sub-partitions = 16 entries. Indexing a fixed array cannot exceed that, and
7748        // an out-of-range index would panic rather than misbehave, so the bound is
7749        // enforced either way and this crate stays forbid(unsafe).
7750        const MAX_MVD: usize = 16;
7751        let mut mvd0 = [(0i32, 0i32); MAX_MVD];
7752        let mut mvd1 = [(0i32, 0i32); MAX_MVD];
7753        let (mut n0, mut n1) = (0usize, 0usize);
7754        for &st in &sub {
7755            if st != 0 && b_sub_uses(st, 0) {
7756                for _ in b_sub_parts(st) {
7757                    mvd0[n0] = (read_mvd(r)?, read_mvd(r)?);
7758                    n0 += 1;
7759                }
7760            }
7761        }
7762        for &st in &sub {
7763            if st != 0 && b_sub_uses(st, 1) {
7764                for _ in b_sub_parts(st) {
7765                    mvd1[n1] = (read_mvd(r)?, read_mvd(r)?);
7766                    n1 += 1;
7767                }
7768            }
7769        }
7770        // Decode each 8×8 partition.
7771        // Spatial-direct A/B/C are MB-level — walk once if any sub is direct.
7772        // `dmemo=0` rewalks every direct 8×8 (A/B oracle).
7773        let hoisted = if self.direct_spatial && direct_memo_on() && sub.contains(&0) {
7774            Some(self.b_direct_nbrs(mb_x, mb_y))
7775        } else {
7776            None
7777        };
7778        let (mut i0, mut i1) = (0usize, 0usize);
7779        for (p, &st) in sub.iter().enumerate() {
7780            let (b8x, b8y) = ((p % 2) * 8, (p / 2) * 8);
7781            if st == 0 {
7782                match hoisted {
7783                    Some((n0, n1)) => self.decode_b_direct_n(
7784                        mb_x,
7785                        mb_y,
7786                        b8x,
7787                        b8y,
7788                        8,
7789                        8,
7790                        &mut pred_y,
7791                        &mut c_pred,
7792                        n0,
7793                        n1,
7794                    ),
7795                    None => {
7796                        self.decode_b_direct(mb_x, mb_y, b8x, b8y, 8, 8, &mut pred_y, &mut c_pred)
7797                    }
7798                }
7799                continue;
7800            }
7801            for &(sx, sy, sw, sh) in b_sub_parts(st) {
7802                let (px, py) = (b8x + sx, b8y + sy);
7803                let (pbx, pby) = ((mb_x * 4 + px / 4) as isize, (mb_y * 4 + py / 4) as isize);
7804                let pwb = (sw / 4) as isize;
7805                let mut mv = [(0i32, 0i32); 2];
7806                if b_sub_uses(st, 0) {
7807                    let n = self.mv_neighbors_list(pbx, pby, pwb, 0);
7808                    let pmv = predict_mv(n[0], n[1], n[2], refi[p & 3][0]);
7809                    let d = mvd0[i0 & 15];
7810                    i0 += 1;
7811                    mv[0] = (pmv.0 + d.0, pmv.1 + d.1);
7812                }
7813                if b_sub_uses(st, 1) {
7814                    let n = self.mv_neighbors_list(pbx, pby, pwb, 1);
7815                    let pmv = predict_mv(n[0], n[1], n[2], refi[p & 3][1]);
7816                    let d = mvd1[i1 & 15];
7817                    i1 += 1;
7818                    mv[1] = (pmv.0 + d.0, pmv.1 + d.1);
7819                }
7820                self.b_set_motion(
7821                    mb_x,
7822                    mb_y,
7823                    px,
7824                    py,
7825                    sw,
7826                    sh,
7827                    refi[p & 3][0],
7828                    mv[0],
7829                    refi[p & 3][1],
7830                    mv[1],
7831                );
7832                self.b_mc_or_record(
7833                    mb_x,
7834                    mb_y,
7835                    px,
7836                    py,
7837                    sw,
7838                    sh,
7839                    refi[p & 3][0],
7840                    mv[0],
7841                    refi[p & 3][1],
7842                    mv[1],
7843                    &mut pred_y,
7844                    &mut c_pred,
7845                );
7846            }
7847        }
7848        // noSubMbPartSizeLessThan8x8: each sub-partition must be ≥ 8×8 (direct
7849        // counts only with the 8×8 inference flag).
7850        let allow_8x8 = sub.iter().all(|&st| {
7851            if st == 0 {
7852                self.direct_8x8_inference
7853            } else {
7854                st <= 3
7855            }
7856        });
7857        self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, allow_8x8, false)
7858    }
7859
7860    /// Reconstructs a `P_8x8` macroblock: four 8×8 sub-macroblock partitions,
7861    /// each independently split (8×8 / 8×4 / 4×8 / 4×4) with its own motion
7862    /// vector(s). `ref0` is `P_8x8ref0` (every `ref_idx` forced to 0, not coded).
7863    fn decode_p8x8(
7864        &mut self,
7865        r: &mut BitReader,
7866        mb_x: usize,
7867        mb_y: usize,
7868        ref0: bool,
7869    ) -> Result<(), MbError> {
7870        if self.refs.is_empty() {
7871            return Err(MbError::Unsupported("inter without reference"));
7872        }
7873        let w4 = self.mb_w * 4;
7874        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
7875        let num_refs = self.refs.len();
7876
7877        // mb_pred order (spec §7.3.5.2): all sub_mb_type, then all ref_idx_l0,
7878        // then all mvd_l0 (partition-major, sub-partition order within each).
7879        let mut sub_types = [0u32; 4];
7880        for st in sub_types.iter_mut() {
7881            let v = r.read_ue()?;
7882            if v > 3 {
7883                return Err(MbError::Unsupported("B-slice / invalid sub_mb_type"));
7884            }
7885            *st = v;
7886        }
7887        let mut ref_idxs = [0i32; 4];
7888        if self.num_ref_active > 1 && !ref0 {
7889            for ri in ref_idxs.iter_mut() {
7890                *ri = read_ref_idx(r, self.num_ref_active)?;
7891                if *ri as usize >= num_refs {
7892                    return Err(MbError::Truncated); // references a non-existent picture
7893                }
7894            }
7895        }
7896
7897        // Per sub-partition (in decoding order): median MV prediction from the
7898        // committed neighbor grid, mvd, commit, then motion-compensate. Committing
7899        // before the next prediction is what lets sub-partitions chain correctly.
7900        // D14b: P_8x8 defers too. Syncing before it instead cost 9,772 pipeline
7901        // drains per stream (45 per 1000 MBs, vs CABAC's 3.3) because P_8x8 is
7902        // common in CAVLC P slices — and the seam measured 1.65-1.97x SLOWER
7903        // for it. Deferring is byte-identical for sub-partitions: the worker
7904        // motion-compensates per 4x4 from the committed grids, which is exactly
7905        // how the CABAC path already handles P_8x8, and a 6-tap filter applied
7906        // per-4x4 with the same MV gives the same pixels as one 8x8 call.
7907        let defer = self.edc_tx.is_some() || self.edc_active;
7908
7909        // ── PHASE 1: PARSE + COMMIT. Must always run to completion. ──────────
7910        //
7911        // These two are interleaved BY NECESSITY: each sub-partition's MV
7912        // prediction reads the grids the previous one committed, so they cannot
7913        // be separated from each other. But they consume BITSTREAM, so nothing
7914        // here may ever be skipped conditionally.
7915        //
7916        // This phase split is a STRUCTURAL guard, not a tidy-up. When MC lived
7917        // inside this loop, deferring it was written as a `break` — which also
7918        // skipped the `read_se` mvd reads, desynced the bitstream, and
7919        // mis-parsed as a B-slice sub_mb_type on a Baseline stream. It happened
7920        // to crash; a desync that stayed IN RANGE would have produced plausible
7921        // garbage instead. With MC in its own pass below, skipping pixel work
7922        // cannot reach the bitstream at all — the mistake is unavailable.
7923        let mut regions: [(usize, usize, usize, usize, i32, (i32, i32)); 16] =
7924            [(0, 0, 0, 0, 0, (0, 0)); 16];
7925        let mut nreg = 0usize;
7926        for part in 0..4usize {
7927            let refi = ref_idxs[part];
7928            let (b8x, b8y) = ((part % 2) * 8, (part / 2) * 8);
7929            for &(srx, sry, srw, srh) in sub_mb_partitions(sub_types[part]) {
7930                let (px, py) = (b8x + srx, b8y + sry);
7931                let (pbx, pby) = ((mb_x * 4 + px / 4) as isize, (mb_y * 4 + py / 4) as isize);
7932                let [a, b, c] = self.mv_neighbors_block(pbx, pby, (srw / 4) as isize);
7933                let pmv = predict_mv(a, b, c, refi);
7934                let mvd_x = read_mvd(r)?;
7935                let mvd_y = read_mvd(r)?;
7936                let mv = (pmv.0 + mvd_x, pmv.1 + mvd_y);
7937                for by in py / 4..py / 4 + srh / 4 {
7938                    for bx in px / 4..px / 4 + srw / 4 {
7939                        let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
7940                        if let (Some(m), Some(it), Some(rf), Some(cd)) = (
7941                            self.mv_y.get_mut(idx),
7942                            self.inter_y.get_mut(idx),
7943                            self.ref_idx_y.get_mut(idx),
7944                            self.coded_y.get_mut(idx),
7945                        ) {
7946                            (*m, *it, *rf, *cd) = ((mv.0 as i16, mv.1 as i16), true, refi as i8, true);
7947                        }
7948                    }
7949                }
7950                regions[nreg & 15] = (px, py, srw, srh, refi, mv);
7951                nreg += 1;
7952            }
7953        }
7954
7955        // ── PHASE 2: PIXEL WORK ONLY. Reads no bitstream; safe to skip. ──────
7956        let mut pred_y = [0u8; 256];
7957        let mut c_pred = [[0u8; 64]; 2];
7958        if !defer {
7959            for &(px, py, srw, srh, refi, mv) in &regions[..nreg] {
7960                let Some(reference) = self.refs.get(refi as usize) else {
7961                    continue;
7962                };
7963                let mut tmp = [0u8; 256];
7964                mc_luma_padded(
7965                    &reference.luma_guard(reference.ch),
7966                    reference.lstride(),
7967                    crate::LPAD,
7968                    self.cw,
7969                    ch,
7970                    mb_x * 16 + px,
7971                    mb_y * 16 + py,
7972                    srw,
7973                    srh,
7974                    mv.0,
7975                    mv.1,
7976                    &mut tmp,
7977                );
7978                restride(&mut pred_y, 16, px, py, &tmp, srw, srh);
7979                let (crx, cry, crw, crh) = (px / 2, py / 2, srw / 2, srh / 2);
7980                for cc in 0..2 {
7981                    let rc = if cc == 0 {
7982                        &*reference.chroma_guard(0, reference.ch)
7983                    } else {
7984                        &*reference.chroma_guard(1, reference.ch)
7985                    };
7986                    let mut tc = [0u8; 64];
7987                    mc_chroma_padded(
7988                        rc,
7989                        reference.cstride(),
7990                        crate::CPAD,
7991                        self.ccw,
7992                        cch,
7993                        mb_x * 8 + crx,
7994                        mb_y * 8 + cry,
7995                        crw,
7996                        crh,
7997                        mv.0,
7998                        mv.1,
7999                        &mut tc,
8000                    );
8001                    restride(&mut c_pred[cc], 8, crx, cry, &tc, crw, crh);
8002                }
8003                self.weight_partition(&mut pred_y, &mut c_pred, 0, refi as usize, px, py, srw, srh);
8004            }
8005        }
8006
8007        // P_8x8 allows the 8×8 transform only when every sub-partition is 8×8.
8008        let allow_8x8 = sub_types.iter().all(|&t| t == 0);
8009        self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, allow_8x8, defer)
8010    }
8011
8012    /// Reconstructs a `P_Skip` macroblock: motion-compensate from the reference
8013    /// at the skip MV, with no residual.
8014    /// Records a B MC region (threaded mode) or executes it inline — the SAME
8015    /// arguments as `b_mc`; the recorded arm resolves the implicit weights at
8016    /// parse time (identical function, parse-side data).
8017    fn b_mc_or_record(
8018        &mut self,
8019        mb_x: usize,
8020        mb_y: usize,
8021        px: usize,
8022        py: usize,
8023        rw: usize,
8024        rh: usize,
8025        refi0: i32,
8026        mv0: (i32, i32),
8027        refi1: i32,
8028        mv1: (i32, i32),
8029        pred_y: &mut [u8; 256],
8030        c_pred: &mut [[u8; 64]; 2],
8031    ) {
8032        if self.edc_regions.is_some() {
8033            // Mirror `b_mc`'s malformed-stream armor EXACTLY before touching the
8034            // ref lists: the inline path clamps the indices and bails on empty
8035            // lists BEFORE computing weights; calling `implicit_weights` with the
8036            // raw indices re-introduced the panic the armor exists to prevent
8037            // (found by the fuzzer, via the unwind-guard that turned the
8038            // resulting worker deadlock back into a diagnosable failure).
8039            let cr0 = if refi0 >= 0 {
8040                (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32
8041            } else {
8042                -1
8043            };
8044            let cr1 = if refi1 >= 0 {
8045                (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32
8046            } else {
8047                -1
8048            };
8049            let w = if (cr0 >= 0 && self.refs.is_empty()) || (cr1 >= 0 && self.refs1.is_empty()) {
8050                None // the worker's port returns before reading the weights
8051            } else {
8052                self.implicit_weights(cr0, cr1)
8053            };
8054            // Cannot bind above: `implicit_weights` needs `&mut self` between the
8055            // guard and here. The guard already proved this is `Some`, so the
8056            // `else` is unreachable -- but an unreachable no-op is the correct
8057            // shape for armor code, not an `unwrap` that can abort a decode.
8058            if let Some(regions) = self.edc_regions.as_mut() {
8059                regions.push(BRegion {
8060                    px,
8061                    py,
8062                    rw,
8063                    rh,
8064                    refi0,
8065                    refi1,
8066                    mv0,
8067                    mv1,
8068                    w,
8069                });
8070            }
8071            return;
8072        }
8073        self.b_mc(
8074            mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, pred_y, c_pred,
8075        );
8076    }
8077
8078    /// Builds the worker's owned pixel context from `self` (planes MOVED out,
8079    /// shared read-only state cloned, filter inputs snapshotted).
8080    fn edc_take_ctx(&mut self) -> PixelCtx {
8081        PixelCtx {
8082            rec_y: core::mem::take(&mut self.rec_y),
8083            rec_u: core::mem::take(&mut self.rec_u),
8084            rec_v: core::mem::take(&mut self.rec_v),
8085            bak_y: core::mem::take(&mut self.bak_y),
8086            bak_u: core::mem::take(&mut self.bak_u),
8087            bak_v: core::mem::take(&mut self.bak_v),
8088            refs: self.refs.clone(),
8089            refs1: self.refs1.clone(),
8090            weights: self.weights.clone(),
8091            weights_l0id: self.weights_l0id,
8092            scaling: self.scaling,
8093            scaling8: self.scaling8,
8094            cw: self.cw,
8095            ccw: self.ccw,
8096            mb_w: self.mb_w,
8097            mb_h: self.mb_h,
8098            chroma_qp_offset: self.chroma_qp_offset,
8099            flt_rows: self.flt_rows,
8100            db_ena: self.db_ena,
8101            db_oa: self.db_oa,
8102            db_ob: self.db_ob,
8103            cur_qp: self.cur_qp,
8104            qp_grid: self.mb_qp.clone(),
8105            t8_grid: self.mb_t8x8.clone(),
8106            bs_store: self.bs_frame.clone(),
8107            progress: self.progress.clone(),
8108        }
8109    }
8110
8111    /// Restores the planes (and the filter watermark) from a returned context.
8112    fn edc_restore_ctx(&mut self, ctx: PixelCtx) {
8113        self.rec_y = ctx.rec_y;
8114        self.rec_u = ctx.rec_u;
8115        self.rec_v = ctx.rec_v;
8116        self.bak_y = ctx.bak_y;
8117        self.bak_u = ctx.bak_u;
8118        self.bak_v = ctx.bak_v;
8119        self.flt_rows = ctx.flt_rows;
8120    }
8121
8122    /// Intra macroblocks read neighbour PIXELS: fetch the context from the
8123    /// worker (which drains all prior jobs first — the channel is FIFO) and
8124    /// install the planes so the inline intra path runs unchanged. The context
8125    /// is given back lazily at the next job/row/slice-end (`edc_giveback`), so
8126    /// consecutive intra macroblocks pay ONE round-trip.
8127    /// Queue a pixel job for the worker (D10). Batched per row; see `EdcMsg::Batch`.
8128    /// Record the FIRST internal fault; later ones do not overwrite it, so the
8129    /// reported cause is the original one rather than its consequences.
8130    #[inline]
8131    fn note_fault(&mut self, what: &'static str) {
8132        if self.fault.is_none() {
8133            self.fault = Some(what);
8134        }
8135    }
8136
8137    /// The picture's internal fault, if one was recorded.
8138    #[inline]
8139    pub fn fault(&self) -> Option<&'static str> {
8140        self.fault
8141    }
8142
8143    /// Post to the EDC worker, recording a fault rather than panicking when the
8144    /// worker is gone or the channel was never built.
8145    #[inline]
8146    fn edc_post(&mut self, msg: EdcMsg) {
8147        let dead = match self.edc_tx.as_ref() {
8148            Some(tx) => tx.send(msg).is_err(),
8149            None => true,
8150        };
8151        if dead {
8152            self.note_fault("EDC worker gone; a deblock job was not delivered");
8153        }
8154    }
8155
8156    #[inline]
8157    fn edc_send_job(&mut self, job: EdcJob) {
8158        edcstat::bump(&edcstat::JOBS, 1);
8159        if !batch_on() {
8160            self.edc_post(EdcMsg::Job(job));
8161            return;
8162        }
8163        self.edc_batch.push(job);
8164    }
8165
8166    /// Ship the accumulated row batch. MUST be called before anything that
8167    /// depends on those jobs having been applied: the row's `Row` filter
8168    /// message, a `NeedCtx` handover, and slice end.
8169    fn edc_flush_batch(&mut self) {
8170        if self.edc_batch.is_empty() {
8171            return;
8172        }
8173        // Replace with a PRE-RESERVED buffer rather than the empty Vec
8174        // `mem::take` would leave: otherwise each row reallocs and regrows from
8175        // zero, trading 208k channel sends for ~7 reallocs x 3k rows.
8176        let cap = self.edc_batch.capacity().max(self.mb_w);
8177        let jobs = core::mem::replace(&mut self.edc_batch, Vec::with_capacity(cap));
8178        edcstat::bump(&edcstat::BATCHES, 1);
8179        self.edc_post(EdcMsg::Batch(jobs));
8180    }
8181
8182    fn edc_intra_sync(&mut self) {
8183        if self.edc_tx.is_none() {
8184            self.edc_flush();
8185            return;
8186        }
8187        if self.edc_parked.is_some() {
8188            return; // already holding
8189        }
8190        // ORDER: drain the batch before taking the planes, or those jobs would
8191        // be applied to a context the parse thread is concurrently holding.
8192        self.edc_flush_batch();
8193        edcstat::bump(&edcstat::NEEDCTX, 1);
8194        self.edc_post(EdcMsg::NeedCtx);
8195        // The worker owes us the pixel context back. If it is gone there is
8196        // nothing to continue with, so record it and leave the state untouched
8197        // -- finalize turns this into `DecodeError::Internal`.
8198        let Some(mut ctx) = self.edc_ctx_rx.as_ref().and_then(|rx| rx.recv().ok()) else {
8199            self.note_fault("EDC worker did not return the pixel context");
8200            return;
8201        };
8202        self.rec_y = core::mem::take(&mut ctx.rec_y);
8203        self.rec_u = core::mem::take(&mut ctx.rec_u);
8204        self.rec_v = core::mem::take(&mut ctx.rec_v);
8205        self.bak_y = core::mem::take(&mut ctx.bak_y);
8206        self.bak_u = core::mem::take(&mut ctx.bak_u);
8207        self.bak_v = core::mem::take(&mut ctx.bak_v);
8208        self.flt_rows = ctx.flt_rows;
8209        self.edc_parked = Some(ctx);
8210    }
8211
8212    /// Returns a held context to the worker (inverse of `edc_intra_sync`).
8213    /// NOTE (D10): this is called per-macroblock on the job paths, so it must
8214    /// NOT flush the batch — that would undo the batching. It is safe: the
8215    /// parked state is only ever entered through `edc_intra_sync`, which
8216    /// flushes before taking the planes, so nothing can be queued-but-unsent
8217    /// while the parse thread holds them.
8218    fn edc_giveback(&mut self) {
8219        if let Some(mut parked) = self.edc_parked.take() {
8220            parked.rec_y = core::mem::take(&mut self.rec_y);
8221            parked.rec_u = core::mem::take(&mut self.rec_u);
8222            parked.rec_v = core::mem::take(&mut self.rec_v);
8223            parked.bak_y = core::mem::take(&mut self.bak_y);
8224            parked.bak_u = core::mem::take(&mut self.bak_u);
8225            parked.bak_v = core::mem::take(&mut self.bak_v);
8226            parked.flt_rows = self.flt_rows;
8227            // Fail-soft: on the unwind path the worker may already be gone;
8228            // dropping the parked context is acceptable there (the planes are
8229            // lost, but the panic is being propagated anyway).
8230            if let Some(tx) = self.edc_back_tx.as_ref() {
8231                let _ = tx.send(parked);
8232            }
8233        }
8234    }
8235
8236    /// Parse-side twin of the nnz/coded grid writes `add_inter_residual` does
8237    /// inline — the worker's port omits them (they are PARSE state: deblock
8238    /// derivation and the CAVLC nC contexts read them), so the threaded path
8239    /// commits them here, from the same parsed counts (their equality with the
8240    /// recon-side recount is the Part 8 nnz-threading brick's own invariant).
8241    fn edc_commit_nnz(
8242        &mut self,
8243        mbx: usize,
8244        mby: usize,
8245        t8: bool,
8246        nnzs: &[u8; 24],
8247        cbp_chroma: u32,
8248    ) {
8249        // BUILD THE RASTER LOCALLY, THEN COPY ROW-WISE. Both luma arms scattered
8250        // sixteen individually bounds-checked stores into the frame grid (the t8
8251        // arm through a 2x2-of-2x2 nest, the other through the z-order scan
8252        // table); chroma scattered eight more. Assembling the macroblock's own
8253        // 4x4 raster on the stack first turns that into four contiguous row
8254        // copies per plane.
8255        let (w4r, w2r) = (self.mb_w * 4, self.mb_w * 2);
8256        let mut raster = [0u8; 16];
8257        if t8 {
8258            for b8 in 0..4usize {
8259                let (b8x, b8y) = (b8 % 2, b8 / 2);
8260                let n = nnzs[b8 * 4];
8261                for sy in 0..2 {
8262                    for sx in 0..2 {
8263                        raster[(b8y * 2 + sy) * 4 + b8x * 2 + sx] = n;
8264                    }
8265                }
8266            }
8267        } else {
8268            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
8269                raster[lby * 4 + lbx] = nnzs[blk];
8270            }
8271        }
8272        for by in 0..4usize {
8273            let a = (mby * 4 + by) * w4r + mbx * 4;
8274            self.nnz_y[a..a + 4].copy_from_slice(&raster[by * 4..by * 4 + 4]);
8275        }
8276        if cbp_chroma == 2 {
8277            for c in 0..2usize {
8278                let mut cr = [0u8; 4];
8279                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
8280                    cr[by * 2 + bx] = nnzs[(16 + c * 4 + by * 2 + bx).min(23)];
8281                }
8282                for by in 0..2usize {
8283                    let a = (mby * 2 + by) * w2r + mbx * 2;
8284                    self.nnz_c[c][a..a + 2].copy_from_slice(&cr[by * 2..by * 2 + 2]);
8285                }
8286            }
8287        }
8288    }
8289
8290    /// Flush the entropy-decouple job queue: replay every deferred pixel job
8291    /// in parse order. Called before any intra macroblock (its reconstruction
8292    /// reads neighbour PIXELS), before row filtering, at B-branch entry, at
8293    /// slice end, and at `deblock()` as a backstop.
8294    /// Per-macroblock guard: the B arm calls this once per B MB — the empty
8295    /// test inlines, the drain body stays outlined.
8296    /// A job box from the pool (or a fresh one when the pool is empty -- the
8297    /// first row of a picture, or MT mode where boxes cross to the worker).
8298    #[inline]
8299    fn take_pinter_job(&mut self) -> Box<PInterJob> {
8300        self.edc_job_pool
8301            .pop()
8302            .unwrap_or_else(|| Box::new(PInterJob::blank()))
8303    }
8304
8305    #[inline]
8306    fn take_nores_job(&mut self, pj: PInterNoResJob) -> Box<PInterNoResJob> {
8307        match self.edc_nores_pool.pop() {
8308            Some(mut b) => {
8309                *b = pj;
8310                b
8311            }
8312            None => Box::new(pj),
8313        }
8314    }
8315
8316    #[inline(always)]
8317    fn edc_flush(&mut self) {
8318        if self.edc_jobs.is_empty() {
8319            return;
8320        }
8321        self.edc_flush_slow();
8322    }
8323
8324    fn edc_flush_slow(&mut self) {
8325        let mut jobs = core::mem::take(&mut self.edc_jobs);
8326        // Band identity holds unweighted OR under identity weights (the x264
8327        // weightp=2 common case: a table in every P slice, identity outside fades).
8328        let band_ok = self.weights.is_none() || self.weights_id0;
8329        // Measurement: nonzero-MV skips sitting in same-row runs of >=2 EQUAL
8330        // MVs (the next band candidate). counted_until stops double-counting.
8331        let mut counted_until = 0usize;
8332        let mut i = 0usize;
8333        while i < jobs.len() {
8334            let j = &jobs[i];
8335            match j {
8336                EdcJob::Skip { mbx, mby, mv } => {
8337                    // mb_skip_run band coalescing: gather the maximal run of
8338                    // consecutive same-row (0,0)-skips and recon it as ONE
8339                    // band copy (weighted-P falls back — the copy identity
8340                    // only holds unweighted).
8341                    if *mv == (0, 0) && band_ok && !no_skipband() {
8342                        let (x0, y0) = (*mbx, *mby);
8343                        let mut n = 1usize;
8344                        while let Some(EdcJob::Skip {
8345                            mbx: nx,
8346                            mby: ny,
8347                            mv: nmv,
8348                        }) = jobs.get(i + n)
8349                        {
8350                            if *ny == y0 && *nx == x0 + n && *nmv == (0, 0) {
8351                                n += 1;
8352                            } else {
8353                                break;
8354                            }
8355                        }
8356                        self.recon_p_skip_band(x0, y0, n);
8357                        if double_recon() {
8358                            edcstat::bump(&edcstat::DOUBLED, n as u64);
8359                            self.recon_p_skip_band(x0, y0, n);
8360                        }
8361                        i += n;
8362                        continue;
8363                    }
8364                    edcstat::bump(&edcstat::SKIP_SINGLES, 1);
8365                    if *mv != (0, 0) && i >= counted_until && edcstat::on() {
8366                        let (x0, y0) = (*mbx, *mby);
8367                        let mut n2 = 1usize;
8368                        while let Some(EdcJob::Skip {
8369                            mbx: nx,
8370                            mby: ny,
8371                            mv: nmv,
8372                        }) = jobs.get(i + n2)
8373                        {
8374                            if *ny == y0 && *nx == x0 + n2 && nmv == mv {
8375                                n2 += 1;
8376                            } else {
8377                                break;
8378                            }
8379                        }
8380                        if n2 >= 2 {
8381                            let fp = mv.0 % 4 == 0 && mv.1 % 4 == 0;
8382                            edcstat::bump(
8383                                if fp {
8384                                    &edcstat::PEQ_FP
8385                                } else {
8386                                    &edcstat::PEQ_FRAC
8387                                },
8388                                n2 as u64,
8389                            );
8390                        }
8391                        counted_until = i + n2;
8392                    }
8393                    self.recon_p_skip(*mbx, *mby, *mv);
8394                    if double_recon() {
8395                        edcstat::bump(&edcstat::DOUBLED, 1);
8396                        self.recon_p_skip(*mbx, *mby, *mv);
8397                    }
8398                }
8399                EdcJob::Inter(job) => {
8400                    self.recon_p_inter(job);
8401                    if double_recon() {
8402                        edcstat::bump(&edcstat::DOUBLED, 1);
8403                        self.recon_p_inter(job);
8404                    }
8405                }
8406                EdcJob::InterNoRes(job) => {
8407                    // D9b: do NOT to_full() — that memset ~2.5 KB of zeros per MB
8408                    // then walked add_inter_residual's all-zero path. MC + plane copy.
8409                    self.recon_p_inter_nores(job);
8410                    if double_recon() {
8411                        edcstat::bump(&edcstat::DOUBLED, 1);
8412                        self.recon_p_inter_nores(job);
8413                    }
8414                }
8415                // B jobs exist only in worker (MT) mode and are never queued
8416                // here — the single-thread seam keeps B inline. Not reachable
8417                // from any input (the push sites are gated on `edc_tx`).
8418                EdcJob::B(_) | EdcJob::BSkip { .. } => unreachable!("B jobs are worker-only"),
8419            }
8420            i += 1;
8421        }
8422        // RECYCLE the job boxes (was: drop = one `free` per coded inter MB).
8423        for j in jobs.drain(..) {
8424            match j {
8425                EdcJob::Inter(b) => self.edc_job_pool.push(b),
8426                EdcJob::InterNoRes(b) => self.edc_nores_pool.push(b),
8427                _ => {}
8428            }
8429        }
8430        // Hand the (now empty) Vec back so its allocation is reused.
8431        self.edc_jobs = jobs;
8432    }
8433
8434    /// Reconstructs one CABAC P inter macroblock from its parse job — the
8435    /// pixel half of the entropy-decouple seam (docs/entropy-decouple-plan.md
8436    /// E1). Reads NOTHING from parse state except the frame grids this MB's
8437    /// parse already committed (its own block MVs/refs, re-gathered below —
8438    /// stable after commit) and the immutable DPB; called either inline
8439    /// (seam off / flush disabled) or in-order at a flush point. Byte-
8440    /// identical to the former inline block by construction: replay order
8441    /// equals inline order at every pixel-observable point (intra reads, row
8442    /// filtering) because flushes precede both.
8443    /// The P-inter reconstruction body, taking its inputs directly rather than
8444    /// through a `PInterJob`. Split out so the residual planes can be passed as
8445    /// `Option`s (absent == nothing was parsed) instead of forcing every caller
8446    /// to own a zeroed copy. NOTE: it re-gathers `gmv`/`gref` from the frame
8447    /// grids rather than reading the job's copies — those are for the worker,
8448    /// which must not touch the parse thread's grids.
8449    fn recon_p_inter_parts(
8450        &mut self,
8451        mbx: usize,
8452        mby: usize,
8453        qp: u8,
8454        cbp_chroma: u32,
8455        luma_scan: Option<&[[i32; 16]; 16]>,
8456        luma8: Option<&[[i32; 64]; 4]>,
8457        cdc: &[[i32; 4]; 2],
8458        cac: Option<&[[[i32; 16]; 4]; 2]>,
8459        nnzs: &[u8; 24],
8460    ) {
8461        let mbw = self.mb_w;
8462        // `add_inter_residual` (and anything under it) reads `self.cur_qp`,
8463        // which at FLUSH time belongs to a later macroblock — replay must
8464        // restore this MB's qp. The x264 corpus (near-constant QP) could not
8465        // see this; the encoder's delta-QP roundtrip stream caught it.
8466        let saved_qp = self.cur_qp;
8467        self.cur_qp = qp;
8468        // ---- Recon: motion-comp (per 4×4 luma / co-located 2×2 chroma using the
8469        // committed grid MV — the 6-tap/bilinear filter is per-output-pixel, so
8470        // per-block MC is bit-identical to per-partition MC) + residual add via the
8471        // SAME reconstruct_4x4 as intra, with the MC output as the prediction.
8472        let w4r = mbw * 4;
8473        let mut pred_y = [0u8; 256];
8474        let mut c_pred = [[0u8; 64]; 2];
8475        {
8476            // MC-CALL COALESCING (side-by-side descent, dec target #2): the old
8477            // loop paid 16 mc_luma(4×4) + 32 mc_chroma(2×2) per MB regardless of
8478            // partitioning — 48 calls even for a single-MV 16×16 MB, and the
8479            // per-call glue around 2.4M calls was ~40% of decoding real-world
8480            // (x264) streams. The 6-tap/bilinear filters are per-output-pixel,
8481            // so merging blocks with equal (mv, ref) into one wider MC call is
8482            // BIT-IDENTICAL; the rect ladder mirrors the partition shapes.
8483            let _ms = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMcStage);
8484            let (rh16, cch) = (self.mb_h * 16, self.mb_h * 8);
8485            let mut gmv = [(0i16, 0i16); 16];
8486            let mut gref = [0usize; 16];
8487            let _gg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
8488            let nrefs = self.refs.len() - 1;
8489            for by in 0..4usize {
8490                // Row-contiguous: one slice copy for the MVs.
8491                let row = (mby * 4 + by) * w4r + mbx * 4;
8492                gmv[by * 4..by * 4 + 4].copy_from_slice(&self.mv_y[row..row + 4]);
8493                let ridx = &self.ref_idx_y[row..row + 4];
8494                for bx in 0..4usize {
8495                    // Per-block reference (multi-ref P): ref_idx_l0 committed to the
8496                    // grid. Clamp — a corrupt stream can over-range it (never panic).
8497                    gref[by * 4 + bx] = (ridx[bx].max(0) as usize).min(nrefs);
8498                }
8499            }
8500            drop(_gg);
8501            // All blocks of the rect (in 4×4-block units) match its top-left?
8502            let rect_eq = |x4: usize, y4: usize, w4: usize, h4: usize| -> bool {
8503                let t = y4 * 4 + x4;
8504                (0..h4).all(|dy| {
8505                    (0..w4).all(|dx| {
8506                        let b = ((y4 + dy) * 4 + (x4 + dx)) & 15;
8507                        gmv[b] == gmv[t] && gref[b] == gref[t]
8508                    })
8509                })
8510            };
8511            let refs = &self.refs;
8512            let (cw, ccw) = (self.cw, self.ccw);
8513            let mc_rect = |x4: usize,
8514                           y4: usize,
8515                           w4: usize,
8516                           h4: usize,
8517                           pred_y: &mut [u8; 256],
8518                           c_pred: &mut [[u8; 64]; 2]| {
8519                let b = y4 * 4 + x4;
8520                let (mv, gr) = (gmv[b & 15], gref[b & 15]);
8521                // `gref` holds a reference-list slot; a slot the list
8522                // does not have means there is nothing to predict from,
8523                // so the rect is left as it stands.
8524                let Some(reference) = refs.get(gr) else {
8525                    return;
8526                };
8527                let (w, h) = (w4 * 4, h4 * 4);
8528                // A FULL-WIDTH rect (w == 16, so x4 == 0) occupies contiguous
8529                // whole rows of `pred_y` — the MC output layout and the
8530                // destination layout coincide, so MC writes the prediction
8531                // buffer DIRECTLY. The staging copy exists only for narrow
8532                // rects, whose rows really are strided in `pred_y`. This is
8533                // the diagnosis's "stage-boundary materialization" tax paid
8534                // by the dominant 16×16/16×8 shapes: 256 B of `t` zeroing
8535                // plus a 256 B copy per rect, for nothing.
8536                if w == 16 {
8537                    rusty_h264_common::inter::with_mc_scratch(|scr| {
8538                        rusty_h264_common::inter::mc_luma_padded_pre(
8539                            scr,
8540                            &reference.luma_guard(reference.ch),
8541                            reference.lstride(),
8542                            crate::LPAD,
8543                            cw,
8544                            rh16,
8545                            mbx * 16,
8546                            mby * 16 + y4 * 4,
8547                            w,
8548                            h,
8549                            i32::from(mv.0),
8550                            i32::from(mv.1),
8551                            &mut pred_y[y4 * 64..y4 * 64 + w * h],
8552                        )
8553                    });
8554                } else {
8555                    let mut t = [0u8; 256];
8556                    rusty_h264_common::inter::with_mc_scratch(|scr| {
8557                        rusty_h264_common::inter::mc_luma_padded_pre(
8558                            scr,
8559                            &reference.luma_guard(reference.ch),
8560                            reference.lstride(),
8561                            crate::LPAD,
8562                            cw,
8563                            rh16,
8564                            mbx * 16 + x4 * 4,
8565                            mby * 16 + y4 * 4,
8566                            w,
8567                            h,
8568                            i32::from(mv.0),
8569                            i32::from(mv.1),
8570                            &mut t[..w * h],
8571                        )
8572                    });
8573                    let _pb =
8574                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
8575                    for dy in 0..h {
8576                        pred_y[(y4 * 4 + dy) * 16 + x4 * 4..][..w]
8577                            .copy_from_slice(&t[dy * w..dy * w + w]);
8578                    }
8579                }
8580                let (cw4, ch4) = (w4 * 2, h4 * 2);
8581                let nc = cw4 * ch4;
8582                let (gu, gv) = (
8583                    reference.chroma_guard(0, reference.ch),
8584                    reference.chroma_guard(1, reference.ch),
8585                );
8586                // U+V paired: one setup serves both planes (see
8587                // mc_chroma_padded_pair). Full-width coincidence:
8588                // cw4 == 8 rows are contiguous in the 8-wide plane.
8589                if cw4 == 8 {
8590                    let [cu, cv] = &mut *c_pred;
8591                    rusty_h264_common::inter::mc_chroma_padded_pair(
8592                        &gu,
8593                        &gv,
8594                        reference.cstride(),
8595                        crate::CPAD,
8596                        ccw,
8597                        cch,
8598                        mbx * 8,
8599                        mby * 8 + y4 * 2,
8600                        cw4,
8601                        ch4,
8602                        i32::from(mv.0),
8603                        i32::from(mv.1),
8604                        &mut cu[y4 * 16..y4 * 16 + nc],
8605                        &mut cv[y4 * 16..y4 * 16 + nc],
8606                    );
8607                } else {
8608                    let (mut tu, mut tv) = ([0u8; 64], [0u8; 64]);
8609                    rusty_h264_common::inter::mc_chroma_padded_pair(
8610                        &gu,
8611                        &gv,
8612                        reference.cstride(),
8613                        crate::CPAD,
8614                        ccw,
8615                        cch,
8616                        mbx * 8 + x4 * 2,
8617                        mby * 8 + y4 * 2,
8618                        cw4,
8619                        ch4,
8620                        i32::from(mv.0),
8621                        i32::from(mv.1),
8622                        &mut tu[..nc],
8623                        &mut tv[..nc],
8624                    );
8625                    let _pb =
8626                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
8627                    for (cc, tc) in [(0usize, &tu), (1, &tv)] {
8628                        for dy in 0..ch4 {
8629                            c_pred[cc][(y4 * 2 + dy) * 8 + x4 * 2..][..cw4]
8630                                .copy_from_slice(&tc[dy * cw4..dy * cw4 + cw4]);
8631                        }
8632                    }
8633                }
8634            };
8635            if rect_eq(0, 0, 4, 4) {
8636                mc_rect(0, 0, 4, 4, &mut pred_y, &mut c_pred);
8637            } else if rect_eq(0, 0, 4, 2) && rect_eq(0, 2, 4, 2) {
8638                mc_rect(0, 0, 4, 2, &mut pred_y, &mut c_pred);
8639                mc_rect(0, 2, 4, 2, &mut pred_y, &mut c_pred);
8640            } else if rect_eq(0, 0, 2, 4) && rect_eq(2, 0, 2, 4) {
8641                mc_rect(0, 0, 2, 4, &mut pred_y, &mut c_pred);
8642                mc_rect(2, 0, 2, 4, &mut pred_y, &mut c_pred);
8643            } else {
8644                for q in 0..4usize {
8645                    let (qx, qy) = ((q % 2) * 2, (q / 2) * 2);
8646                    if rect_eq(qx, qy, 2, 2) {
8647                        mc_rect(qx, qy, 2, 2, &mut pred_y, &mut c_pred);
8648                    } else if rect_eq(qx, qy, 2, 1) && rect_eq(qx, qy + 1, 2, 1) {
8649                        mc_rect(qx, qy, 2, 1, &mut pred_y, &mut c_pred);
8650                        mc_rect(qx, qy + 1, 2, 1, &mut pred_y, &mut c_pred);
8651                    } else if rect_eq(qx, qy, 1, 2) && rect_eq(qx + 1, qy, 1, 2) {
8652                        mc_rect(qx, qy, 1, 2, &mut pred_y, &mut c_pred);
8653                        mc_rect(qx + 1, qy, 1, 2, &mut pred_y, &mut c_pred);
8654                    } else {
8655                        for j in 0..4usize {
8656                            mc_rect(qx + (j % 2), qy + (j / 2), 1, 1, &mut pred_y, &mut c_pred);
8657                        }
8658                    }
8659                }
8660            }
8661            // EXPLICIT WEIGHTED PREDICTION (spec 8.4.2.3). The CAVLC inter
8662            // path weights each partition after MC; the MC-call-coalescing
8663            // rewrite of this CABAC path lost it, and nothing caught that
8664            // because the effect is invisible unless a stream actually
8665            // carries non-default weights. x264's `weightp` DUPLICATES a
8666            // reference and distinguishes the copy ONLY by its weights, so
8667            // every macroblock picking the weighted index decoded unweighted
8668            // -- a silent, accumulating luma drift.
8669            //
8670            // Applied per 4x4 block rather than per partition: the weight
8671            // depends solely on the block's reference index, so the two are
8672            // equivalent, and `gref` already holds it for every block
8673            // regardless of which rect ladder rung ran.
8674            if self.weights.is_some() {
8675                for by in 0..4usize {
8676                    for bx in 0..4usize {
8677                        let refi = gref[by * 4 + bx];
8678                        self.weight_partition(
8679                            &mut pred_y,
8680                            &mut c_pred,
8681                            0,
8682                            refi,
8683                            bx * 4,
8684                            by * 4,
8685                            4,
8686                            4,
8687                        );
8688                    }
8689                }
8690            }
8691        }
8692        // Residual add — the SAME helper the B path uses (this inline
8693        // copy was a duplicate; deduped when the zero-block fast path
8694        // landed so both paths share it).
8695        self.add_inter_residual(
8696            mbx, mby, &pred_y, &c_pred, luma_scan, luma8, cdc, cac, cbp_chroma, nnzs,
8697        );
8698        self.cur_qp = saved_qp;
8699    }
8700
8701    /// Job-shaped entry point (EDC replay + the `double_recon` A/B knob).
8702    fn recon_p_inter(&mut self, j: &PInterJob) {
8703        self.recon_p_inter_parts(
8704            j.mbx,
8705            j.mby,
8706            j.qp,
8707            j.cbp_chroma,
8708            Some(&j.luma_scan),
8709            j.t8.then_some(&j.luma8),
8710            &j.cdc,
8711            Some(&j.cac),
8712            &j.nnzs,
8713        );
8714    }
8715
8716    /// D9b: P inter with `cbp == 0` — MC + plane copy, no coeff memset / residual walk.
8717    /// Byte-identical to `recon_p_inter` on a zero-residual job: prediction is the recon.
8718    fn recon_p_inter_nores(&mut self, j: &PInterNoResJob) {
8719        let w4r = self.mb_w * 4;
8720        // Parse-side nnz for single-thread EDC flush (MT commits earlier via edc_commit_nnz).
8721        // ROW FILLS. Both arms touch the same sixteen cells - four contiguous
8722        // per macroblock row - and wrote them one indexed store at a time (the
8723        // t8 arm through a 2x2-of-2x2 nest, the other through the z-order scan).
8724        for by in 0..4usize {
8725            let r = (j.mby * 4 + by) * w4r + j.mbx * 4;
8726            self.nnz_y[r..r + 4].fill(0);
8727            if j.t8 {
8728                self.coded_y[r..r + 4].fill(true);
8729            }
8730        }
8731        let mut pred_y = [0u8; 256];
8732        let mut c_pred = [[0u8; 64]; 2];
8733        // `self.refs.len() - 1` was re-loaded on every one of the sixteen
8734        // iterations; it is a per-macroblock invariant. Written once, not
8735        // zeroed-then-filled.
8736        let nrefs = self.refs.len() - 1;
8737        let gref: [usize; 16] = core::array::from_fn(|k| (j.gref[k] as usize).min(nrefs));
8738        coalesce_p_inter_mc(
8739            &self.refs,
8740            self.cw,
8741            self.ccw,
8742            self.mb_h,
8743            j.mbx,
8744            j.mby,
8745            &j.gmv,
8746            &gref,
8747            &mut pred_y,
8748            &mut c_pred,
8749        );
8750        // The identity early-out lives INSIDE `weight_partition`, so an x264
8751        // stream - which carries a pred_weight_table in EVERY P slice, identity
8752        // outside fades - still paid sixteen calls per macroblock to be told
8753        // there was nothing to do. Hoisted to one test.
8754        if self.weights.is_some() && !self.weights_l0id {
8755            for by in 0..4usize {
8756                for bx in 0..4usize {
8757                    let refi = gref[by * 4 + bx];
8758                    self.weight_partition(&mut pred_y, &mut c_pred, 0, refi, bx * 4, by * 4, 4, 4);
8759                }
8760            }
8761        }
8762        // ONE span per plane: the destination is a stack of contiguous runs a
8763        // fixed stride apart, so the rows share a single bounds check instead of
8764        // one each (16 + 8 + 8 of them).
8765        let (cw, ccw) = (self.cw, self.ccw);
8766        let ybase = (j.mby * 16) * cw + j.mbx * 16;
8767        let ywin = &mut self.rec_y[ybase..ybase + 15 * cw + 16];
8768        for dy in 0..16 {
8769            ywin[dy * cw..dy * cw + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
8770        }
8771        let cbase = (j.mby * 8) * ccw + j.mbx * 8;
8772        for c in 0..2 {
8773            let plane = if c == 0 {
8774                &mut self.rec_u
8775            } else {
8776                &mut self.rec_v
8777            };
8778            let cwin = &mut plane[cbase..cbase + 7 * ccw + 8];
8779            for dy in 0..8 {
8780                cwin[dy * ccw..dy * ccw + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
8781            }
8782        }
8783    }
8784
8785    fn decode_p_skip(&mut self, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
8786        self.route_skip_mbs += 1;
8787        self.wait_refs_for_mb(mb_y);
8788        // DEBLOCK CLASS: a P_Skip macroblock carries no coefficients and one
8789        // (ref, mv) for all 16 blocks, so every internal boundary strength is 0 by
8790        // §8.7.2.1 and the loop filter needs 9 block loads instead of 24. This is
8791        // the single highest-value classification: the MB-kind census measures Skip
8792        // at 36.4% (CAVLC) / 65.0% (main) / 57.8% (high) of real x264 corpora.
8793        // Written HERE because both the CAVLC and the CABAC slice loops funnel
8794        // through this one function.
8795        //
8796        // Deliberately NOT done for `B_Skip` — its motion is direct-derived and can
8797        // differ per 4×4 sub-block, so its internal edges can legally reach
8798        // strength 1. B_Skip stays UNSET and takes the blind path.
8799        // (kind commit moved into the span/immediate arms below.)
8800        // P_Skip always references index 0 (the most recent picture). Borrow it —
8801        // a full-frame `.cloned()` here was ~86% of total decode time (one ~3 MB
8802        // plane copy per skip MB, thousands per frame).
8803        if self.refs.is_empty() {
8804            return Err(MbError::Unsupported("P_Skip without reference"));
8805        }
8806        let addr = mb_y * self.mb_w + mb_x;
8807        // mb_x == 0: the left neighbor is off-frame, so §8.4.1.1's
8808        // unavailability rule forces (0,0) with no gather at all.
8809        let mv = if (mb_x == 0 || self.skip_zero_next == addr) && !no_runmv() {
8810            edcstat::bump(&edcstat::SKIPMV_FORCED, 1);
8811            (0, 0)
8812        } else {
8813            // The gather reads the grids; the pending spans cannot contain
8814            // this MB's left (a non-forced skip means the previous MB was not
8815            // a (0,0) committer) — flush both before deriving.
8816            self.span_flush();
8817            edcstat::bump(&edcstat::SKIPMV_DERIVED, 1);
8818            self.skip_mv(mb_x, mb_y)
8819        };
8820        self.skip_zero_next = if mv == (0, 0) { addr + 1 } else { usize::MAX };
8821        // Grid commits are PARSE state — (0,0) skips defer them into the P
8822        // span (the deferral constants); nonzero-MV skips commit immediately.
8823        if mv == (0, 0) {
8824            // Recon defers too when the band identity holds (1T, no/identity
8825            // weights) — the span flush band-copies instead of queueing jobs.
8826            let recon = self.edc_tx.is_none() && (self.weights.is_none() || self.weights_id0);
8827            self.pz_push(mb_x, mb_y, recon);
8828            if recon {
8829                return Ok(());
8830            }
8831        } else {
8832            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8833            if let Some(p) = self.mb_kind.get_mut(mb_y * self.mb_w + mb_x) {
8834                *p = rusty_h264_common::deblock::MB_KIND_SKIP;
8835            }
8836            self.set_mb_mv(mb_x, mb_y, mv, true, 0);
8837            let w4 = self.mb_w * 4;
8838            for dy in 0..4 {
8839                let a = (mb_y * 4 + dy) * w4 + mb_x * 4;
8840                self.coded_y[a..a + 4].fill(true);
8841                self.modes_y[a..a + 4].fill(2);
8842            }
8843        }
8844        if self.edc_tx.is_some() {
8845            self.edc_giveback();
8846            self.edc_send_job(EdcJob::Skip {
8847                mbx: mb_x,
8848                mby: mb_y,
8849                mv,
8850            });
8851            return Ok(());
8852        }
8853        if self.edc_active {
8854            self.edc_jobs.push(EdcJob::Skip {
8855                mbx: mb_x,
8856                mby: mb_y,
8857                mv,
8858            });
8859            return Ok(());
8860        }
8861        self.recon_p_skip(mb_x, mb_y, mv);
8862        if double_recon() {
8863            self.recon_p_skip(mb_x, mb_y, mv);
8864        }
8865        Ok(())
8866    }
8867
8868    /// Pixel half of P_Skip (see the E1 seam note on `recon_p_inter`).
8869    /// Run-coalesced P_Skip reconstruction: `n` consecutive skip MBs on one
8870    /// row, all with `mv == (0,0)` and NO weighted prediction. At full-pel
8871    /// zero motion, MC is the identity read of `refs[0]` — so the recon is a
8872    /// straight band copy from the padded reference plane, no staging, no MC
8873    /// calls. Byte-identical to `n` calls of `recon_p_skip` by construction
8874    /// (mc_luma_padded/mc_chroma_padded at mv 0 return exactly these bytes).
8875    ///
8876    /// The (0,0) run theorem that makes ONE derivation cover the run: once a
8877    /// skip MB commits (ref 0, mv (0,0)), every later MB of the run derives
8878    /// (0,0) too — its left neighbour (or a row-start's missing neighbour)
8879    /// triggers §8.4.1.1's zero-MV rule in `skip_mv`.
8880    fn recon_p_skip_band(&mut self, mbx0: usize, mby: usize, n: usize) {
8881        let w = n * 16;
8882        let Some(rf0) = self.refs.first() else { return };
8883        let lst = rf0.lstride();
8884        {
8885            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8886            let ly = rf0.luma_guard(rf0.ch);
8887            for dy in 0..16 {
8888                let y = mby * 16 + dy;
8889                let src = (y + crate::LPAD) * lst + crate::LPAD + mbx0 * 16;
8890                let dst = y * self.cw + mbx0 * 16;
8891                // ARMOR (fuzz find, inline-execution.md 11.11): on a MALFORMED
8892                // stream a reference can carry geometry that does not match the
8893                // open picture (mutated parameter sets / truncated ref planes),
8894                // so the in-frame band's ref window can leave the guard slice.
8895                // The band REFUSES rather than panics; a conformant stream
8896                // cannot take this arm (repro: scratchpad repro.264, plane 6400
8897                // vs index 6416). Output on malformed input is unspecified —
8898                // absence of panic is the contract the fuzz gate enforces.
8899                let (Some(d), Some(sr)) = (self.rec_y.get_mut(dst..dst + w), ly.get(src..src + w))
8900                else {
8901                    return;
8902                };
8903                d.copy_from_slice(sr);
8904            }
8905        }
8906        let cst = rf0.cstride();
8907        let wc = n * 8;
8908        for c in 0..2 {
8909            let rc = rf0.chroma_guard(c, rf0.ch);
8910            let plane = if c == 0 {
8911                &mut self.rec_u
8912            } else {
8913                &mut self.rec_v
8914            };
8915            for dy in 0..8 {
8916                let y = mby * 8 + dy;
8917                let src = (y + crate::CPAD) * cst + crate::CPAD + mbx0 * 8;
8918                let dst = y * self.ccw + mbx0 * 8;
8919                // Same armor as the luma band above.
8920                let (Some(d), Some(sr)) = (plane.get_mut(dst..dst + wc), rc.get(src..src + wc))
8921                else {
8922                    return;
8923                };
8924                d.copy_from_slice(sr);
8925            }
8926        }
8927        edcstat::bump(&edcstat::SKIPBAND_MBS, n as u64);
8928        edcstat::bump(&edcstat::SKIPBAND_RUNS, 1);
8929    }
8930
8931    /// B_Skip zero-bi recon: rec = avg(ref0, ref1') row-wise, straight from the
8932    /// padded planes. Byte-identical to b_mc at mv (0,0)/(0,0): full-pel MC is
8933    /// an identity read and the unweighted bi blend is exactly (a+b+1)>>1 —
8934    /// which the compiler emits as pavgb over the sliced zips, same as b_mc's
8935    /// blend site.
8936    fn recon_b_skip_zero_bi(&mut self, mbx: usize, mby: usize, r0i: usize, r1i: usize) {
8937        let (Some(rf0), Some(rf1)) = (self.refs.get(r0i), self.refs1.get(r1i)) else {
8938            return;
8939        };
8940        let (l0st, l1st) = (rf0.lstride(), rf1.lstride());
8941        {
8942            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8943            let (ly0, ly1) = (rf0.luma_guard(rf0.ch), rf1.luma_guard(rf1.ch));
8944            for dy in 0..16 {
8945                let y = mby * 16 + dy;
8946                let s0 = (y + crate::LPAD) * l0st + crate::LPAD + mbx * 16;
8947                let s1 = (y + crate::LPAD) * l1st + crate::LPAD + mbx * 16;
8948                let d = y * self.cw + mbx * 16;
8949                // WIN: STRIDED destination, so rustc cannot do what it does for the
8950                // contiguous pred_y averages (which are already ideal vpavgb and are
8951                // left alone). Route the row through the pixel_avg kernel instead.
8952                rusty_h264_common::inter::avg_row_into(
8953                    &ly0[s0..s0 + 16],
8954                    &ly1[s1..s1 + 16],
8955                    16,
8956                    &mut self.rec_y[d..d + 16],
8957                );
8958            }
8959        }
8960        let (c0st, c1st) = (rf0.cstride(), rf1.cstride());
8961        for c in 0..2 {
8962            let rc0 = rf0.chroma_guard(c, rf0.ch);
8963            let rc1 = rf1.chroma_guard(c, rf1.ch);
8964            let plane = if c == 0 {
8965                &mut self.rec_u
8966            } else {
8967                &mut self.rec_v
8968            };
8969            for dy in 0..8 {
8970                let y = mby * 8 + dy;
8971                let s0 = (y + crate::CPAD) * c0st + crate::CPAD + mbx * 8;
8972                let s1 = (y + crate::CPAD) * c1st + crate::CPAD + mbx * 8;
8973                let d = y * self.ccw + mbx * 8;
8974                // WIN: STRIDED destination, so rustc cannot do what it does for the
8975                // contiguous pred_y averages (which are already ideal vpavgb and are
8976                // left alone). Route the row through the pixel_avg kernel instead.
8977                rusty_h264_common::inter::avg_row_into(
8978                    &rc0[s0..s0 + 8],
8979                    &rc1[s1..s1 + 8],
8980                    8,
8981                    &mut plane[d..d + 8],
8982                );
8983            }
8984        }
8985    }
8986
8987    /// Full-pel P_Skip single: rec window = ref\[0\] window at an integer offset.
8988    /// Returns false (touching nothing) when any window leaves the padded
8989    /// plane — mc's edge clamping takes over there. Luma needs mv%4==0;
8990    /// chroma needs mv%8==0 (its frac is mv&7), so odd full-pel MVs copy luma
8991    /// and interpolate chroma.
8992    fn recon_p_skip_fullpel(&mut self, mbx: usize, mby: usize, mv: (i32, i32)) -> bool {
8993        let (dx, dy) = ((mv.0 / 4) as isize, (mv.1 / 4) as isize);
8994        let Some(rf0) = self.refs.first() else {
8995            return false;
8996        };
8997        let lst = rf0.lstride();
8998        let lpad = crate::LPAD as isize;
8999        let cx = mbx as isize * 16 + dx + lpad;
9000        let cy = mby as isize * 16 + dy + lpad;
9001        {
9002            let ly = rf0.luma_guard(rf0.ch);
9003            let lrows = rf0.lrows() as isize;
9004            if cx < 0 || cx + 16 > lst as isize || cy < 0 || cy + 16 > lrows {
9005                return false;
9006            }
9007            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
9008            // ONE span each side: sixteen rows a fixed stride apart, so the
9009            // source and destination checks are paid once instead of per row.
9010            let (cw, sb) = (self.cw, cy as usize * lst + cx as usize);
9011            let db = (mby * 16) * cw + mbx * 16;
9012            let sw = &ly[sb..sb + 15 * lst + 16];
9013            let dw = &mut self.rec_y[db..db + 15 * cw + 16];
9014            for r in 0..16 {
9015                dw[r * cw..r * cw + 16].copy_from_slice(&sw[r * lst..r * lst + 16]);
9016            }
9017        }
9018        let cst = rf0.cstride();
9019        let cpad = crate::CPAD as isize;
9020        if mv.0 % 8 == 0 && mv.1 % 8 == 0 {
9021            let ccx = mbx as isize * 8 + (mv.0 / 8) as isize + cpad;
9022            let ccy = mby as isize * 8 + (mv.1 / 8) as isize + cpad;
9023            let crows = rf0.crows() as isize;
9024            if ccx >= 0 && ccx + 8 <= cst as isize && ccy >= 0 && ccy + 8 <= crows {
9025                for c in 0..2 {
9026                    let rc = rf0.chroma_guard(c, rf0.ch);
9027                    let plane = if c == 0 {
9028                        &mut self.rec_u
9029                    } else {
9030                        &mut self.rec_v
9031                    };
9032                    let (ccw, sb) = (self.ccw, ccy as usize * cst + ccx as usize);
9033                    let db = (mby * 8) * ccw + mbx * 8;
9034                    let sw = &rc[sb..sb + 7 * cst + 8];
9035                    let dw = &mut plane[db..db + 7 * ccw + 8];
9036                    for r in 0..8 {
9037                        dw[r * ccw..r * ccw + 8].copy_from_slice(&sw[r * cst..r * cst + 8]);
9038                    }
9039                }
9040                edcstat::bump(&edcstat::SKIP_FP_FULL, 1);
9041                return true;
9042            }
9043        }
9044        // Odd full-pel (or chroma window out of pad): luma is already copied,
9045        // chroma still interpolates — identical to the mc path's chroma half.
9046        let cch = self.mb_h * 8;
9047        for c in 0..2 {
9048            let mut pc = [0u8; 64];
9049            let rc = if c == 0 {
9050                &*rf0.chroma_guard(0, rf0.ch)
9051            } else {
9052                &*rf0.chroma_guard(1, rf0.ch)
9053            };
9054            mc_chroma_padded(
9055                rc,
9056                cst,
9057                crate::CPAD,
9058                self.ccw,
9059                cch,
9060                mbx * 8,
9061                mby * 8,
9062                8,
9063                8,
9064                mv.0,
9065                mv.1,
9066                &mut pc,
9067            );
9068            let plane = if c == 0 {
9069                &mut self.rec_u
9070            } else {
9071                &mut self.rec_v
9072            };
9073            for r in 0..8 {
9074                let d = (mby * 8 + r) * self.ccw + mbx * 8;
9075                plane[d..d + 8].copy_from_slice(&pc[r * 8..r * 8 + 8]);
9076            }
9077        }
9078        edcstat::bump(&edcstat::SKIP_FP_LUMA, 1);
9079        true
9080    }
9081
9082    /// B_Skip full-pel recon with per-list integer offsets: uni = offset row
9083    /// copy, bi = offset row average ((a+b+1)>>1). Luma always copies here
9084    /// (caller guarantees mv%4==0); chroma copies at mv%8==0 and otherwise
9085    /// interpolates via b_mc_chroma — identical to the b_mc path's chroma
9086    /// half with no implicit weights. Returns false untouched when a luma
9087    /// window leaves the padded plane.
9088    fn recon_b_skip_fp(
9089        &mut self,
9090        mbx: usize,
9091        mby: usize,
9092        r0: Option<usize>,
9093        r1: Option<usize>,
9094        mv0: (i32, i32),
9095        mv1: (i32, i32),
9096    ) -> bool {
9097        let lpad = crate::LPAD as isize;
9098        // (start-row, start-col, stride, rows) of each active list's luma window.
9099        let win = |rf: &crate::RefFrame, mv: (i32, i32)| -> Option<(usize, usize)> {
9100            let lst = rf.lstride();
9101            let cx = mbx as isize * 16 + (mv.0 / 4) as isize + lpad;
9102            let cy = mby as isize * 16 + (mv.1 / 4) as isize + lpad;
9103            let lrows = rf.lrows() as isize;
9104            if cx < 0 || cx + 16 > lst as isize || cy < 0 || cy + 16 > lrows {
9105                None
9106            } else {
9107                Some((cy as usize * lst + cx as usize, lst))
9108            }
9109        };
9110        let w0 = match r0 {
9111            Some(i) => match self.refs.get(i).and_then(|rr| win(rr, mv0)) {
9112                Some(w) => Some(w),
9113                None => return false,
9114            },
9115            None => None,
9116        };
9117        let w1 = match r1 {
9118            Some(i) => match self.refs1.get(i).and_then(|rr| win(rr, mv1)) {
9119                Some(w) => Some(w),
9120                None => return false,
9121            },
9122            None => None,
9123        };
9124        {
9125            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
9126            match (w0, w1) {
9127                (Some((s0, l0)), Some((s1, l1))) => {
9128                    let Some(rf0) = r0.and_then(|i| self.refs.get(i)) else {
9129                        return false;
9130                    };
9131                    // Same shape as the `rf0` line above: `w1` is `Some` only when
9132                    // `r1` is `Some(i)` AND `refs1.get(i)` returned a frame, so the
9133                    // `else` is unreachable -- but it is a REFUSAL, not a panic, and
9134                    // `recon_b_skip_fp` already answers `false` for every case it
9135                    // cannot fast-path. Indexing a `Vec` with an unwrapped `Option`
9136                    // spent a discriminant test, an `unwrap_failed` call and a bounds
9137                    // check to restate an invariant the caller had already proven.
9138                    let Some(rf1) = r1.and_then(|i| self.refs1.get(i)) else {
9139                        return false;
9140                    };
9141                    let (ly0, ly1) = (rf0.luma_guard(rf0.ch), rf1.luma_guard(rf1.ch));
9142                    // WIN: this was 256 scalar (p + q + 1) >> 1 per macroblock, three
9143                    // modules from the  kernel that computes exactly that
9144                    // with  and already takes both source strides. One kernel
9145                    // call over the 16x16, then sixteen row copies out to the strided
9146                    // recon plane.
9147                    for r in 0..16 {
9148                        let d = (mby * 16 + r) * self.cw + mbx * 16;
9149                        rusty_h264_common::inter::avg_row_into(
9150                            &ly0[s0 + r * l0..],
9151                            &ly1[s1 + r * l1..],
9152                            16,
9153                            &mut self.rec_y[d..d + 16],
9154                        );
9155                    }
9156                }
9157                (Some((s0, l0)), None) => {
9158                    let Some(rf0) = r0.and_then(|i| self.refs.get(i)) else {
9159                        return false;
9160                    };
9161                    let ly = rf0.luma_guard(rf0.ch);
9162                    for r in 0..16 {
9163                        let d = (mby * 16 + r) * self.cw + mbx * 16;
9164                        self.rec_y[d..d + 16].copy_from_slice(&ly[s0 + r * l0..s0 + r * l0 + 16]);
9165                    }
9166                }
9167                (None, Some((s1, l1))) => {
9168                    // Same shape as the `rf0` line above: `w1` is `Some` only when
9169                    // `r1` is `Some(i)` AND `refs1.get(i)` returned a frame, so the
9170                    // `else` is unreachable -- but it is a REFUSAL, not a panic, and
9171                    // `recon_b_skip_fp` already answers `false` for every case it
9172                    // cannot fast-path. Indexing a `Vec` with an unwrapped `Option`
9173                    // spent a discriminant test, an `unwrap_failed` call and a bounds
9174                    // check to restate an invariant the caller had already proven.
9175                    let Some(rf1) = r1.and_then(|i| self.refs1.get(i)) else {
9176                        return false;
9177                    };
9178                    let ly = rf1.luma_guard(rf1.ch);
9179                    for r in 0..16 {
9180                        let d = (mby * 16 + r) * self.cw + mbx * 16;
9181                        self.rec_y[d..d + 16].copy_from_slice(&ly[s1 + r * l1..s1 + r * l1 + 16]);
9182                    }
9183                }
9184                (None, None) => unreachable!("caller guarantees an active list"),
9185            }
9186        }
9187        // Chroma: interpolating half — route through b_mc_chroma into a stage
9188        // (weights None: the caller only enters at implicit None/(32,32), and
9189        // (32,32) IS the plain average b_mc_chroma computes for None).
9190        let cch = self.mb_h * 8;
9191        let mut c_pred = [[0u8; 64]; 2];
9192        let (ri0, ri1) = (r0.map_or(-1, |i| i as i32), r1.map_or(-1, |i| i as i32));
9193        self.b_mc_chroma(
9194            mbx,
9195            mby,
9196            0,
9197            0,
9198            16,
9199            16,
9200            ri0,
9201            mv0,
9202            ri1,
9203            mv1,
9204            &mut c_pred,
9205            None,
9206            cch,
9207        );
9208        for c in 0..2 {
9209            let plane = if c == 0 {
9210                &mut self.rec_u
9211            } else {
9212                &mut self.rec_v
9213            };
9214            for r in 0..8 {
9215                let d = (mby * 8 + r) * self.ccw + mbx * 8;
9216                plane[d..d + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
9217            }
9218        }
9219        true
9220    }
9221
9222    fn recon_p_skip(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32)) {
9223        // Full-pel + identity/no weights: MC is a pure offset read, so copy the
9224        // ref window straight into rec — no pred staging, no weight pass. The
9225        // window must sit inside the PADDED plane (outside it, mc's clamping
9226        // differs from a raw offset read, so fall through).
9227        if (self.weights.is_none() || self.weights_id0)
9228            && mv.0 % 4 == 0
9229            && mv.1 % 4 == 0
9230            && !no_skipfp()
9231            && self.recon_p_skip_fullpel(mb_x, mb_y, mv)
9232        {
9233            return;
9234        }
9235        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
9236
9237        let mut pred = [0u8; 256];
9238        let Some(rf0) = self.refs.first() else { return };
9239        mc_luma_padded(
9240            &rf0.luma_guard(rf0.ch),
9241            rf0.lstride(),
9242            crate::LPAD,
9243            self.cw,
9244            ch,
9245            mb_x * 16,
9246            mb_y * 16,
9247            16,
9248            16,
9249            mv.0,
9250            mv.1,
9251            &mut pred,
9252        );
9253        if let Some(wt) = &self.weights {
9254            if !self.weights_id0 || no_skipfp() {
9255                for p in pred.iter_mut() {
9256                    *p = wt.apply_luma(*p, 0, 0);
9257                }
9258            }
9259        }
9260        {
9261            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
9262            // ONE span for the sixteen rows (see `recon_p_inter_nores`).
9263            let (cw, base) = (self.cw, (mb_y * 16) * self.cw + mb_x * 16);
9264            let win = &mut self.rec_y[base..base + 15 * cw + 16];
9265            for dy in 0..16 {
9266                win[dy * cw..dy * cw + 16].copy_from_slice(&pred[dy * 16..dy * 16 + 16]);
9267            }
9268        }
9269        let (mut pu, mut pv) = ([0u8; 64], [0u8; 64]);
9270        {
9271            let Some(rf0) = self.refs.first() else { return };
9272            let (gu, gv) = (rf0.chroma_guard(0, rf0.ch), rf0.chroma_guard(1, rf0.ch));
9273            rusty_h264_common::inter::mc_chroma_padded_pair(
9274                &gu,
9275                &gv,
9276                rf0.cstride(),
9277                crate::CPAD,
9278                self.ccw,
9279                cch,
9280                mb_x * 8,
9281                mb_y * 8,
9282                8,
9283                8,
9284                mv.0,
9285                mv.1,
9286                &mut pu,
9287                &mut pv,
9288            );
9289        }
9290        if let Some(wt) = &self.weights {
9291            if !self.weights_id0 || no_skipfp() {
9292                for p in pu.iter_mut() {
9293                    *p = wt.apply_chroma(*p, 0, 0, 0);
9294                }
9295                for p in pv.iter_mut() {
9296                    *p = wt.apply_chroma(*p, 0, 0, 1);
9297                }
9298            }
9299        }
9300        for (pc, plane) in [(&pu, &mut self.rec_u), (&pv, &mut self.rec_v)] {
9301            for dy in 0..8 {
9302                let d = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
9303                plane[d..d + 8].copy_from_slice(&pc[dy * 8..dy * 8 + 8]);
9304            }
9305        }
9306    }
9307
9308    /// Predicted `Intra_4x4` mode for the block at absolute coords `(bx, by)`.
9309    /// If either the left or top neighbor is outside the frame or in another
9310    /// slice, the prediction is DC (mode 2) (spec §8.3.1.1).
9311    /// `predict_i4_mode` with the MACROBLOCK-level availability already
9312    /// resolved by the caller (`top_ok` / `left_ok`).
9313    ///
9314    /// Twelve of a macroblock's sixteen blocks have BOTH neighbours inside the
9315    /// same macroblock, so their slice test is a foregone conclusion - yet the
9316    /// general form recomputed two `nbr_in_slice` products and two
9317    /// `intra_nbr_ok` tests for every one of them, ~1M times on all-intra
9318    /// content.
9319    ///
9320    /// `constrained_intra` is excluded deliberately: there the general form
9321    /// tests THIS macroblock's own `inter_y` cells for interior blocks, which
9322    /// the macroblock-level flags do not model, so that case defers unchanged.
9323    #[inline]
9324    fn predict_i4_mode_fast(
9325        &self,
9326        bx: usize,
9327        by: usize,
9328        lbx: usize,
9329        lby: usize,
9330        top_ok: bool,
9331        left_ok: bool,
9332    ) -> u8 {
9333        if self.constrained_intra {
9334            return self.predict_i4_mode(bx, by);
9335        }
9336        if !((lbx > 0 || left_ok) && (lby > 0 || top_ok)) {
9337            return 2;
9338        }
9339        let w4 = self.mb_w * 4;
9340        // Fallible reads: two checks on the same grid at two indexes. `2` is DC,
9341        // which is exactly what every unavailable-neighbour path above already
9342        // returns, so the fallback is the function's own existing semantics.
9343        let l = self.modes_y.get(by * w4 + (bx - 1)).copied().unwrap_or(2);
9344        let t = self.modes_y.get((by - 1) * w4 + bx).copied().unwrap_or(2);
9345        l.min(t)
9346    }
9347
9348    fn predict_i4_mode(&self, bx: usize, by: usize) -> u8 {
9349        if bx == 0 || by == 0 {
9350            return 2;
9351        }
9352        // Left neighbor block (bx-1,by); top neighbor block (bx,by-1). A neighbor
9353        // in another slice — or, under constrained_intra, an inter neighbor — is
9354        // unavailable, forcing the predicted mode to DC.
9355        if !self.nbr_in_slice((bx - 1) / 4, by / 4)
9356            || !self.nbr_in_slice(bx / 4, (by - 1) / 4)
9357            || !self.intra_nbr_ok(bx - 1, by)
9358            || !self.intra_nbr_ok(bx, by - 1)
9359        {
9360            return 2;
9361        }
9362        let w4 = self.mb_w * 4;
9363        // Fallible reads: two checks on the same grid at two indexes. `2` is DC,
9364        // which is exactly what every unavailable-neighbour path above already
9365        // returns, so the fallback is the function's own existing semantics.
9366        let l = self.modes_y.get(by * w4 + (bx - 1)).copied().unwrap_or(2);
9367        let t = self.modes_y.get((by - 1) * w4 + bx).copied().unwrap_or(2);
9368        l.min(t)
9369    }
9370
9371    /// Gathers 4×4 luma intra neighbors at pixel `(px, py)` from `rec_y`.
9372    fn gather_i4(
9373        &self,
9374        px: usize,
9375        py: usize,
9376        avail_top: bool,
9377        avail_left: bool,
9378        bx: usize,
9379        by: usize,
9380    ) -> ([u8; 8], [u8; 4], u8) {
9381        let (cw, w4) = (self.cw, self.mb_w * 4);
9382        let mut top = [0u8; 8];
9383        let mut left = [0u8; 4];
9384        let mut corner = 0;
9385        let (lbx, lby) = (bx & 3, by & 3);
9386        if avail_top {
9387            // Row-slice loads: the bak-vs-rec source branch runs once per row
9388            // segment instead of once per PIXEL (top_y_px paid it 8 times).
9389            top[..4].copy_from_slice(self.top_y_row(py, px, 4));
9390            // ROUTED BY POSITION (routing round): below the macroblock's top row the
9391            // top-right neighbour is inside this macroblock (or the undecoded right
9392            // neighbour), so its availability is the z-order constant I4_TR_IN_MB --
9393            // no grid load, no slice test, no constrained-intra call.
9394            let tr_avail = if lby > 0 {
9395                lbx < 3 && (I4_TR_IN_MB >> I4_Z_OF_XY[lby][lbx]) & 1 == 1
9396            } else {
9397                bx + 1 < w4
9398                    && self
9399                        .coded_y
9400                        .get((by - 1) * w4 + (bx + 1))
9401                        .copied()
9402                        .unwrap_or(false)
9403                    && self.nbr_in_slice((bx + 1) / 4, (by - 1) / 4)
9404                    && self.intra_nbr_ok(bx + 1, by - 1)
9405            };
9406            if tr_avail {
9407                top[4..8].copy_from_slice(self.top_y_row(py, px + 4, 4));
9408            } else {
9409                let t3 = top[3];
9410                top[4..8].fill(t3);
9411            }
9412        }
9413        if avail_left {
9414            // STRIDED WALK. The span form this replaces claimed `i * cw <= 3 * cw`
9415            // was provable against the span's length; it is not — the same shape
9416            // left five live checks on `recon_i16_luma`'s sixteen-sample column.
9417            // `step_by` does the stride with no index, and `zip` bounds the count.
9418            let base = py * cw + px - 1;
9419            for (s, &v) in left.iter_mut().zip(self.rec_y[base..].iter().step_by(cw)) {
9420                *s = v;
9421            }
9422        }
9423        // The above-left corner has its own availability (block D); under
9424        // constrained_intra it is gone if that block is inter.
9425        // Interior corner is this macroblock's own (intra) sample: no constrained-intra call.
9426        if avail_top && avail_left && ((lbx > 0 && lby > 0) || self.intra_nbr_ok(bx - 1, by - 1)) {
9427            corner = self.top_y_px(py, px - 1);
9428        }
9429        (top, left, corner)
9430    }
9431
9432    /// Reconstructs an `I_PCM` macroblock: byte-aligned raw 8-bit samples, no
9433    /// prediction/transform/quant (spec §7.3.5, §8.3.5).
9434    fn decode_ipcm(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
9435        r.align_to_byte()?;
9436        // ROW SLICES throughout. A PCM macroblock wrote 384 samples one at a
9437        // time, each a separately bounds-checked index into a whole plane, and
9438        // then 104 more scattered context stores below. Every extent here is a
9439        // literal (16, 8, 4, 2), so one slice per row makes the inner index
9440        // provable and the stores become a straight walk.
9441        let (lx, ly) = (mb_x * 16, mb_y * 16);
9442        for dy in 0..16 {
9443            let row = &mut self.rec_y[(ly + dy) * self.cw + lx..][..16];
9444            for px in row.iter_mut() {
9445                *px = r.read_bits(8)? as u8;
9446            }
9447        }
9448        let (cx, cy) = (mb_x * 8, mb_y * 8);
9449        let ccw = self.ccw;
9450        for plane in [&mut self.rec_u, &mut self.rec_v] {
9451            for dy in 0..8 {
9452                let row = &mut plane[(cy + dy) * ccw + cx..][..8];
9453                for px in row.iter_mut() {
9454                    *px = r.read_bits(8)? as u8;
9455                }
9456            }
9457        }
9458        // Neighbor context: an I_PCM block contributes TotalCoeff = 16, counts as
9459        // intra with DC mode for prediction, and has no motion (§9.2.1, §8.3.1.2.2).
9460        let (w4, w2) = (self.mb_w * 4, self.mb_w * 2);
9461        // The scan order is irrelevant when every written value is a constant:
9462        // the sixteen `LUMA_4X4_SCAN_XY` positions are exactly the macroblock's
9463        // 4x4 rectangle, so four row fills per array cover them.
9464        for ry in 0..4 {
9465            let base = (mb_y * 4 + ry) * w4 + mb_x * 4;
9466            self.nnz_y[base..][..4].fill(16);
9467            self.modes_y[base..][..4].fill(2);
9468            self.coded_y[base..][..4].fill(true);
9469            self.inter_y[base..][..4].fill(false);
9470            self.ref_idx_y[base..][..4].fill(-1);
9471            self.mv_y[base..][..4].fill((0, 0));
9472        }
9473        for c in 0..2 {
9474            for by in 0..2 {
9475                self.nnz_c[c][(mb_y * 2 + by) * w2 + mb_x * 2..][..2].fill(16);
9476            }
9477        }
9478        Ok(())
9479    }
9480
9481    fn decode_i4x4(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
9482        let w4 = self.mb_w * 4;
9483
9484        // intra4x4 mode signalling
9485        let mut modes = [2u8; 16]; // raster [lby*4+lbx]
9486        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
9487            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
9488            let predicted = self.predict_i4_mode(bx, by);
9489            let actual = if r.read_bit()? {
9490                predicted
9491            } else {
9492                let rem = r.read_bits(3)? as u8;
9493                if rem < predicted {
9494                    rem
9495                } else {
9496                    rem + 1
9497                }
9498            };
9499            if let Some(p) = self.modes_y.get_mut(by * w4 + bx) {
9500                *p = actual;
9501            }
9502            modes[(lby & 3) * 4 + (lbx & 3)] = actual;
9503        }
9504
9505        // VALIDATED, not merely narrowed. `read_ue` is exp-Golomb and therefore
9506        // UNBOUNDED, so `as u8` silently truncates: a stream sending 260 would
9507        // arrive here as 4. §7.4.5 bounds intra_chroma_pred_mode to 0..=3, so
9508        // anything else is a malformed stream and is rejected rather than
9509        // predicted with an undefined mode. (Found by the H-17 cast review: this
9510        // was the only one of the decoder's ten narrowing casts on a parsed
9511        // value that could truncate -- the other nine are `read_bits(N) as u8`
9512        // with N <= 8, which is exact by construction.)
9513        let chroma_mode = match r.read_ue()? {
9514            m @ 0..=3 => m as u8,
9515            _ => return Err(MbError::Unsupported("intra_chroma_pred_mode out of range (7.4.5: 0..=3)")),
9516        };
9517        let cbp = read_cbp_intra(r)?;
9518        let cbp_luma = cbp & 15;
9519        let cbp_chroma = cbp >> 4;
9520        if cbp != 0 {
9521            self.step_qp(r.read_se()?)?;
9522        }
9523        let qp = self.cur_qp;
9524
9525        // luma residuals + serial reconstruction. Cross-MB neighbors are only
9526        // available when the adjacent macroblock is in this slice (and, under
9527        // constrained_intra_pred, is itself intra-coded).
9528        let top_mb_avail = mb_y > 0
9529            && self.nbr_in_slice(mb_x, mb_y - 1)
9530            && self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
9531        let left_mb_avail = mb_x > 0
9532            && self.nbr_in_slice(mb_x - 1, mb_y)
9533            && self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
9534        self.nnz_cache_load(mb_x, mb_y);
9535        let mut nnz_raster = [0u8; 16];
9536        // PARSE all sixteen blocks first (nC prediction needs only the counts), so
9537        // the residual transforms run as ONE batched IDCT before the serial
9538        // predict+add walk. Parse and recon were interleaved per block before.
9539        let mut scans = [[0i32; 16]; 16];
9540        let mut totals = [0u8; 16];
9541        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
9542            let total = if cbp_luma & (1 << (blk / 4)) != 0 {
9543                let nc = self.nc_pred(lbx, lby);
9544                decode_residual_block_into::<16, 16>(r, nc, &mut scans[blk & 15])?
9545            } else {
9546                0
9547            };
9548            self.nnz_cache_set(lbx, lby, total);
9549            nnz_raster[(lby & 3) * 4 + (lbx & 3)] = total;
9550            totals[blk & 15] = total;
9551        }
9552        let kinds = self.i4_prepare(&scans, &totals, qp);
9553        let dq0 = self.dq_const(qp, 0);
9554        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
9555            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
9556            let avail_top = lby > 0 || top_mb_avail;
9557            let avail_left = lbx > 0 || left_mb_avail;
9558            self.recon_i4_block_res(
9559                bx,
9560                by,
9561                modes[(lby & 3) * 4 + (lbx & 3)],
9562                avail_top,
9563                avail_left,
9564                kinds[blk & 15],
9565                &scans,
9566                &dq0,
9567            );
9568        }
9569        for ry in 0..4usize {
9570            self.coded_y[(mb_y * 4 + ry) * w4 + mb_x * 4..][..4].fill(true);
9571        }
9572        // Deferred: `recon_i4_block` gathers from `coded_y` / `rec_y`, never
9573        // from `nnz_y`, so one contiguous copy per row replaces sixteen stores.
9574        for by in 0..4usize {
9575            let a = (mb_y * 4 + by) * w4 + mb_x * 4;
9576            self.nnz_y[a..a + 4].copy_from_slice(&nnz_raster[by * 4..by * 4 + 4]);
9577        }
9578
9579        self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
9580    }
9581
9582    /// Decodes an `I_8x8` macroblock (High profile): four 8×8 luma blocks, each
9583    /// with its own intra mode, 8×8 transform residual (CAVLC = four interleaved
9584    /// 4×4 blocks), and 8×8 intra prediction.
9585    fn decode_i8x8(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
9586        let w4 = self.mb_w * 4;
9587        if let Some(p) = self.mb_t8x8.get_mut(mb_y * self.mb_w + mb_x) {
9588            *p = true;
9589        }
9590        self.any_t8 = true;
9591
9592        // intra8x8 mode signalling — one mode per 8×8 block (raster 0..3),
9593        // stored into all four of its 4×4 cells so neighbors can read it.
9594        let mut modes8 = [2u8; 4];
9595        for (b8, mode) in modes8.iter_mut().enumerate() {
9596            let (b8x, b8y) = (b8 % 2, b8 / 2);
9597            let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
9598            let predicted = self.predict_i4_mode(bx, by);
9599            let actual = if r.read_bit()? {
9600                predicted
9601            } else {
9602                let rem = r.read_bits(3)? as u8;
9603                if rem < predicted {
9604                    rem
9605                } else {
9606                    rem + 1
9607                }
9608            };
9609            *mode = actual;
9610            for sy in 0..2 {
9611                // Row fill: the 2x2 cell block is two contiguous PAIRS.
9612                self.modes_y[(by + sy) * w4 + bx..][..2].fill(actual);
9613            }
9614        }
9615
9616        // VALIDATED, not merely narrowed. `read_ue` is exp-Golomb and therefore
9617        // UNBOUNDED, so `as u8` silently truncates: a stream sending 260 would
9618        // arrive here as 4. §7.4.5 bounds intra_chroma_pred_mode to 0..=3, so
9619        // anything else is a malformed stream and is rejected rather than
9620        // predicted with an undefined mode. (Found by the H-17 cast review: this
9621        // was the only one of the decoder's ten narrowing casts on a parsed
9622        // value that could truncate -- the other nine are `read_bits(N) as u8`
9623        // with N <= 8, which is exact by construction.)
9624        let chroma_mode = match r.read_ue()? {
9625            m @ 0..=3 => m as u8,
9626            _ => return Err(MbError::Unsupported("intra_chroma_pred_mode out of range (7.4.5: 0..=3)")),
9627        };
9628        let cbp = read_cbp_intra(r)?;
9629        let cbp_luma = cbp & 15;
9630        let cbp_chroma = cbp >> 4;
9631        if cbp != 0 {
9632            self.step_qp(r.read_se()?)?;
9633        }
9634        let qp = self.cur_qp;
9635
9636        let top_mb_avail = mb_y > 0
9637            && self.nbr_in_slice(mb_x, mb_y - 1)
9638            && self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
9639        let left_mb_avail = mb_x > 0
9640            && self.nbr_in_slice(mb_x - 1, mb_y)
9641            && self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
9642        self.nnz_cache_load(mb_x, mb_y);
9643
9644        let mut nnz_raster = [0u8; 16];
9645        for b8 in 0..4 {
9646            let (b8x, b8y) = (b8 % 2, b8 / 2);
9647            let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
9648
9649            // residual: 8×8 CAVLC = four 4×4 sub-blocks, coeff k of sub-block s
9650            // mapping to 8×8 scan position 4·k + s (spec §7.3.5.3.2).
9651            let mut scan8 = [0i32; 64];
9652            let coded8 = cbp_luma & (1 << b8) != 0;
9653            if coded8 {
9654                for sub in 0..4 {
9655                    let (sx, sy) = (sub % 2, sub / 2);
9656                    let (cx, cy) = (b8x * 2 + sx, b8y * 2 + sy);
9657                    let nc = self.nc_pred(cx, cy);
9658                    let mut blk = [0i32; 16];
9659                    let total = decode_residual_block_into::<16, 16>(r, nc, &mut blk)?;
9660                    self.nnz_cache_set(cx, cy, total);
9661                    // Deferred to one row copy per MB row after the loop (`recon_i8_block`
9662                    // gathers from `coded_y` / `rec_y`, never `nnz_y`) -- was a checked store per sub.
9663                    nnz_raster[(cy & 3) * 4 + (cx & 3)] = total;
9664                    for k in 0..16 {
9665                        scan8[4 * k + sub] = blk[k];
9666                    }
9667                }
9668            } else {
9669                for sub in 0..4 {
9670                    let (sx, sy) = (sub % 2, sub / 2);
9671                    self.nnz_cache_set(b8x * 2 + sx, b8y * 2 + sy, 0);
9672                    // `nnz_raster` is already zero here.
9673                }
9674            }
9675
9676            let avail_top = b8y > 0 || top_mb_avail;
9677            let avail_left = b8x > 0 || left_mb_avail;
9678            self.recon_i8_block(
9679                bx,
9680                by,
9681                modes8[b8],
9682                avail_top,
9683                avail_left,
9684                coded8.then_some(&scan8),
9685                qp,
9686            );
9687            for sy in 0..2 {
9688                // Row fill: the 2x2 cell block is two contiguous PAIRS.
9689                self.coded_y[(by + sy) * w4 + bx..][..2].fill(true);
9690            }
9691        }
9692
9693        // ONE contiguous copy per macroblock row (see the I4x4 arm).
9694        for ry in 0..4usize {
9695            let a = (mb_y * 4 + ry) * w4 + mb_x * 4;
9696            self.nnz_y[a..a + 4].copy_from_slice(&nnz_raster[ry * 4..ry * 4 + 4]);
9697        }
9698
9699        self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
9700    }
9701
9702    /// Dequantizes + inverse-transforms an 8×8 luma block, applying the scaling
9703    /// matrix `list` (0 = intra, 1 = inter) or flat weights.
9704    fn inv_quant8(&self, raster: &[i32; 64], qp: u8, list: usize) -> [i32; 64] {
9705        // Prebuilt per-(qp, list) constants: the flat table at zero cost, or one
9706        // 64-entry build per macroblock for scaling lists (was 64 x two lookups +
9707        // a multiply per coefficient on every block).
9708        match &self.scaling8 {
9709            Some(s) => inverse_quant_8x8_dq(raster, &Dequant8Qp::build(qp, &s[list])),
9710            None => inverse_quant_8x8_dq(raster, &DQ8_FLAT[(qp as usize).min(51)]),
9711        }
9712    }
9713
9714    /// Gathers the 8×8 luma intra reference samples at pixel `(px, py)`: the 16
9715    /// top samples (8..15 substituted from the last when no top-right), 8 left
9716    /// samples, the above-left corner, and whether the corner is available.
9717    fn gather_i8(
9718        &self,
9719        px: usize,
9720        py: usize,
9721        avail_top: bool,
9722        avail_left: bool,
9723        bx: usize,
9724        by: usize,
9725    ) -> ([u8; 16], [u8; 8], u8, bool) {
9726        let (cw, w4) = (self.cw, self.mb_w * 4);
9727        let mut top = [0u8; 16];
9728        let mut left = [0u8; 8];
9729        let mut corner = 0;
9730        if avail_top {
9731            // Row-slice loads: one source branch per segment, not per pixel.
9732            top[..8].copy_from_slice(self.top_y_row(py, px, 8));
9733            let tr_avail = bx + 2 < w4
9734                && self
9735                    .coded_y
9736                    .get((by - 1) * w4 + (bx + 2))
9737                    .copied()
9738                    .unwrap_or(false)
9739                && self.nbr_in_slice((bx + 2) / 4, (by - 1) / 4)
9740                && self.intra_nbr_ok(bx + 2, by - 1);
9741            if tr_avail {
9742                top[8..16].copy_from_slice(self.top_y_row(py, px + 8, 8));
9743            } else {
9744                let t7 = top[7];
9745                top[8..16].fill(t7);
9746            }
9747        }
9748        if avail_left {
9749            // STRIDED WALK, NOT STRIDED INDEXING. A span plus `col[i * cw]` still
9750            // checks, because `i * cw <= 7 * cw` is not something LLVM derives
9751            // against the span's own length; `step_by` on the iterator does the
9752            // stride with no index at all, and `zip` bounds the count.
9753            let base = py * cw + px - 1;
9754            for (s, &v) in left.iter_mut().zip(self.rec_y[base..].iter().step_by(cw)) {
9755                *s = v;
9756            }
9757        }
9758        let avail_corner = avail_top && avail_left && self.intra_nbr_ok(bx - 1, by - 1);
9759        if avail_corner {
9760            corner = self.top_y_px(py, px - 1);
9761        }
9762        (top, left, corner, avail_corner)
9763    }
9764
9765    fn decode_i16(
9766        &mut self,
9767        r: &mut BitReader,
9768        mb_x: usize,
9769        mb_y: usize,
9770        mt: u32,
9771    ) -> Result<(), MbError> {
9772        let pred_mode = I16Mode::from_id(mt % 4);
9773        let cbp_chroma = (mt % 12) / 4;
9774        let cbp_luma_15 = mt / 12 == 1;
9775        // VALIDATED, not merely narrowed. `read_ue` is exp-Golomb and therefore
9776        // UNBOUNDED, so `as u8` silently truncates: a stream sending 260 would
9777        // arrive here as 4. §7.4.5 bounds intra_chroma_pred_mode to 0..=3, so
9778        // anything else is a malformed stream and is rejected rather than
9779        // predicted with an undefined mode. (Found by the H-17 cast review: this
9780        // was the only one of the decoder's ten narrowing casts on a parsed
9781        // value that could truncate -- the other nine are `read_bits(N) as u8`
9782        // with N <= 8, which is exact by construction.)
9783        let chroma_mode = match r.read_ue()? {
9784            m @ 0..=3 => m as u8,
9785            _ => return Err(MbError::Unsupported("intra_chroma_pred_mode out of range (7.4.5: 0..=3)")),
9786        };
9787        self.step_qp(r.read_se()?)?;
9788        let qp = self.cur_qp;
9789        let w4 = self.mb_w * 4;
9790
9791        // luma DC
9792        self.nnz_cache_load(mb_x, mb_y);
9793        let nc_dc = self.nc_pred(0, 0);
9794        let mut dc_scan = [0i32; 16];
9795        decode_residual_block_into::<16, 16>(r, nc_dc, &mut dc_scan)?;
9796        let recon_dc =
9797            inverse_quant_luma_dc_scan(&dc_scan, qp, self.scaling.as_ref().map(|s| s[0][0]));
9798
9799        // luma AC (nnz set for all 16 blocks: 0 when DC-only, matching the encoder)
9800        let mut nnz_raster = [0u8; 16];
9801        let mut q_blocks = [[0i32; 16]; 16];
9802        for &(bx, by) in &LUMA_4X4_SCAN_XY {
9803            let total = if cbp_luma_15 {
9804                let nc = self.nc_pred(bx, by);
9805                let mut ac = [0i32; 16];
9806                let t = decode_residual_block_into::<15, 16>(r, nc, &mut ac)?;
9807                // Zero-skip: an empty AC block leaves the fresh-zero raster
9808                // block untouched (un-scanning 16 zeros wrote zeros on zeros).
9809                if t != 0 {
9810                    q_blocks[(by & 3) * 4 + (bx & 3)] = ac; // SCAN order (fused kernel)
9811                }
9812                t
9813            } else {
9814                0
9815            };
9816            self.nnz_cache_set(bx, by, total);
9817            nnz_raster[(by & 3) * 4 + (bx & 3)] = total;
9818        }
9819        let coded16 = nnz_raster
9820            .iter()
9821            .enumerate()
9822            .fold(0u16, |m, (i, &n)| m | (((n != 0) as u16) << i));
9823        // ONE contiguous copy per row (nothing reads `nnz_y` for this
9824        // macroblock in between - `nc_pred` predicts from `nnz_cache`).
9825        for by in 0..4usize {
9826            let a = (mb_y * 4 + by) * w4 + mb_x * 4;
9827            self.nnz_y[a..a + 4].copy_from_slice(&nnz_raster[by * 4..by * 4 + 4]);
9828        }
9829
9830        // prediction + reconstruction
9831        let avail_top = mb_y > 0
9832            && self.nbr_in_slice(mb_x, mb_y - 1)
9833            && self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
9834        let avail_left = mb_x > 0
9835            && self.nbr_in_slice(mb_x - 1, mb_y)
9836            && self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
9837        self.recon_i16_luma(
9838            mb_x,
9839            mb_y,
9840            pred_mode,
9841            avail_top,
9842            avail_left,
9843            Some(&q_blocks),
9844            coded16,
9845            &recon_dc,
9846            qp,
9847        );
9848        // I_16x16 blocks are treated as DC for neighbor mode prediction.
9849        for lby in 0..4usize {
9850            let a = (mb_y * 4 + lby) * w4 + mb_x * 4;
9851            self.modes_y[a..a + 4].fill(2);
9852        }
9853
9854        self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
9855    }
9856
9857    /// Reads and reconstructs the chroma residual (shared by both luma types).
9858    fn decode_chroma(
9859        &mut self,
9860        r: &mut BitReader,
9861        mb_x: usize,
9862        mb_y: usize,
9863        cbp_chroma: u32,
9864        chroma_mode: u8,
9865    ) -> Result<(), MbError> {
9866        let qpc = self.chroma_qp_for(self.cur_qp);
9867        let avail_top = mb_y > 0
9868            && self.nbr_in_slice(mb_x, mb_y - 1)
9869            && self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
9870        let avail_left = mb_x > 0
9871            && self.nbr_in_slice(mb_x - 1, mb_y)
9872            && self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
9873
9874        let mut c_recon_dc = [[0i32; 4]; 2];
9875        if cbp_chroma != 0 {
9876            for (c, slot) in c_recon_dc.iter_mut().enumerate() {
9877                let mut dc = [0i32; 4];
9878                decode_residual_block_into::<4, 4>(r, -1, &mut dc)?;
9879                *slot = self.dequant_chroma_dc(&dc, qpc, 1 + c);
9880            }
9881        }
9882        let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
9883        let mut ccoded = [0u8; 2];
9884        if cbp_chroma == 2 {
9885            self.chroma_cache_load(mb_x, mb_y);
9886            let w2 = self.mb_w * 2;
9887            for c in 0..2 {
9888                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
9889                    let nc = self.chroma_nc_pred(c, bx, by);
9890                    let mut ac = [0i32; 16];
9891                    let total = decode_residual_block_into::<15, 16>(r, nc, &mut ac)?;
9892                    self.chroma_nnz_cache_set(c, bx, by, total);
9893                    if let Some(n) =
9894                        self.nnz_c[c & 1].get_mut((mb_y * 2 + by) * w2 + (mb_x * 2 + bx))
9895                    {
9896                        *n = total;
9897                    }
9898                    // Zero-skip: empty AC leaves the fresh-zero raster block.
9899                    if total != 0 {
9900                        ccoded[c & 1] |= 1u8 << ((by * 2 + bx) & 3);
9901                        // WIN: c < 2, by < 2, bx < 2, so both indexes are in range and
9902                        // the masks PROVE it -- the ccoded[c & 1] |= 1 << ((by*2+bx) & 3)
9903                        // on the line above already uses exactly these. This was the last
9904                        // panic_bounds_check on the decoder hot path.
9905                        c_q_blocks[c & 1][(by * 2 + bx) & 3] = ac; // SCAN order (fused kernel)
9906                    }
9907                }
9908            }
9909        }
9910        self.recon_chroma_blocks(
9911            mb_x,
9912            mb_y,
9913            chroma_mode,
9914            avail_top,
9915            avail_left,
9916            &c_q_blocks,
9917            ccoded,
9918            &c_recon_dc,
9919            qpc,
9920        );
9921        Ok(())
9922    }
9923
9924    /// Applies the in-loop deblocking filter to the reconstructed frame, with
9925    /// the slice's `FilterOffsetA`/`FilterOffsetB` (each = the coded `*_div2`
9926    /// value × 2).
9927    /// Per-frame per-MB dump for conformance bisection, keyed on `RH264_DUMP_MB`.
9928    /// Prints one char per macroblock: `i` = intra, otherwise the List-0 reference
9929    /// index of the MB's top-left 4x4 block. Directly comparable with ffmpeg's
9930    /// `-debug mb_type` map, which is the only per-MB ground truth we can get out
9931    /// of the reference decoder.
9932    fn dump_mb_map(&self) {
9933        if !dump_mb_on() {
9934            return;
9935        }
9936        let w4 = self.mb_w * 4;
9937        let mut hist = [0usize; 4];
9938        eprintln!("--- frame poc {} ---", self.cur_poc);
9939        for mb_y in 0..self.mb_h {
9940            let mut row = String::new();
9941            for mb_x in 0..self.mb_w {
9942                let b = (mb_y * 4) * w4 + mb_x * 4;
9943                let Some(&r) = self.ref_idx_y.get(b) else {
9944                    continue;
9945                };
9946                if r < 0 {
9947                    row.push('i');
9948                } else {
9949                    if (r as usize) < 4 {
9950                        hist[r as usize] += 1;
9951                    }
9952                    row.push((b'0' + (r as u8).min(9)) as char);
9953                }
9954            }
9955            eprintln!("{row}");
9956        }
9957        eprintln!(
9958            "ref histogram: {hist:?}   num_ref_active={} refs.len()={}   OUT-OF-RANGE={}",
9959            self.num_ref_active,
9960            self.refs.len(),
9961            hist.iter().skip(self.refs.len()).sum::<usize>()
9962        );
9963        let list: Vec<String> = self
9964            .refs
9965            .iter()
9966            .enumerate()
9967            .map(|(i, f)| {
9968                // A synthesized frame_num-gap frame is uniform grey with w4 == 0;
9969                // flag it, because it silently displaces real pictures in the list.
9970                let synth = if f.w4 == 0 { " SYNTH-GREY" } else { "" };
9971                format!("[{i}] poc={} fn={}{synth}", f.poc, f.frame_num)
9972            })
9973            .collect();
9974        eprintln!("  RefPicList0: {}", list.join("  "));
9975    }
9976
9977    /// Finish the work `deblock` would have flushed first, for a picture with the
9978    /// loop filter off (`disable_deblocking_filter_idc == 1`). Without it a slice
9979    /// ending in a run of P_Skip macroblocks leaves its last deferred span
9980    /// unreconstructed: zero samples in the bottom rows of the picture.
9981    pub fn flush_deferred(&mut self) {
9982        self.span_flush();
9983        self.edc_flush();
9984    }
9985
9986    pub fn deblock(&mut self, offset_a: i32, offset_b: i32) {
9987        self.span_flush(); // deferred grid spans feed bS derivation below
9988        self.edc_flush(); // backstop: no pixel job may survive to filtering
9989        self.dump_mb_map();
9990        // ROW MODE: finish any rows not derived during decode (mid-row slice
9991        // ends, error paths) FIRST, while `self` is still mutably borrowable.
9992        if rowdb_on() {
9993            while self.bs_rows < self.mb_h {
9994                let r = self.bs_rows;
9995                self.derive_bs_row(r);
9996                self.bs_rows += 1;
9997            }
9998        }
9999        // Deblock boundary strength uses the *transform block's* coded status. For
10000        // an 8×8-transform macroblock the unit is the whole 8×8, so every 4×4 cell
10001        // shares the 8×8's coefficient presence (OR of its four sub-block counts)
10002        // — distinct from the per-sub-block `nnz_y` used for the CAVLC nC context.
10003        // Only differs from `nnz_y` when some MB uses the 8×8 transform (High
10004        // profile). On Baseline (no 8×8) it's identical — skip the clone + rewrite.
10005        // Row-interleave already derived bS into `bs_frame`; the filter then
10006        // reads only `bs`+`t8x8`+qp — do not clone nnz or rebuild POC maps.
10007        let rowdb = rowdb_on();
10008        let nnz_db_storage;
10009        let nnz_db: &[u8] = if rowdb {
10010            &[]
10011        } else if self.mb_t8x8.iter().any(|&t| t) {
10012            let mut n = self.nnz_y.clone();
10013            let w4 = self.mb_w * 4;
10014            for mb_y in 0..self.mb_h {
10015                for mb_x in 0..self.mb_w {
10016                    if !self
10017                        .mb_t8x8
10018                        .get(mb_y * self.mb_w + mb_x)
10019                        .copied()
10020                        .unwrap_or(false)
10021                    {
10022                        continue;
10023                    }
10024                    for b8 in 0..4 {
10025                        let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
10026                        let any = (0..2).any(|sy| {
10027                            self.nnz_y[(by + sy) * w4 + bx..][..2]
10028                                .iter()
10029                                .any(|&v| v > 0)
10030                        });
10031                        for sy in 0..2 {
10032                            // Row fill: the 2x2 cell block is two contiguous PAIRS.
10033                            n[(by + sy) * w4 + bx..][..2].fill(u8::from(any));
10034                        }
10035                    }
10036                }
10037            }
10038            nnz_db_storage = n;
10039            &nnz_db_storage
10040        } else {
10041            &self.nnz_y
10042        };
10043        let mut info = rusty_h264_common::deblock::BlockInfo {
10044            inter: if rowdb { &[] } else { &self.inter_y },
10045            nnz: nnz_db,
10046            mv: if rowdb { &[] } else { &self.mv_y },
10047            ref_id: if rowdb { &[] } else { &self.ref_idx_y },
10048            mv1: if rowdb { &[] } else { &self.mv1 },
10049            ref_id1: if rowdb || self.ref_poc1.is_empty() {
10050                &[]
10051            } else {
10052                &self.ref_idx1
10053            },
10054            w4: self.mb_w * 4,
10055            t8x8: &self.mb_t8x8,
10056            bs: &[],
10057            poc0: if rowdb { &[] } else { &self.ref_poc0 },
10058            poc1: if rowdb { &[] } else { &self.ref_poc1 },
10059            kind: &self.mb_kind,
10060        };
10061        // ROW MODE (R2): rows were derived during decode; the remainder was
10062        // finished above (before `info` borrowed the grids). Fallback: the
10063        // Part 16/17 picture-end precompute; `RS_H264_BS_PRE=0` further falls
10064        // back to the pack-then-derive-in-loop pipeline.
10065        let bs_store;
10066        if rowdb {
10067            bs_store = core::mem::take(&mut self.bs_frame);
10068            info.bs = &bs_store;
10069        } else if bs_pre_on() {
10070            let mut buf = Vec::new();
10071            rusty_h264_common::deblock::precompute_bs_frame(&info, self.mb_w, self.mb_h, &mut buf);
10072            bs_store = buf;
10073            info.bs = &bs_store;
10074        } else {
10075            bs_store = Vec::new();
10076        }
10077        let first_row = if rowdb { self.flt_rows } else { 0 };
10078        rusty_h264_common::deblock::filter_frame_rows_pre(
10079            &mut self.rec_y,
10080            &mut self.rec_u,
10081            &mut self.rec_v,
10082            self.mb_w,
10083            self.mb_h,
10084            first_row..self.mb_h,
10085            &self.mb_qp,
10086            self.chroma_qp_offset,
10087            offset_a,
10088            offset_b,
10089            &info,
10090        );
10091        drop(info);
10092        if rowdb {
10093            self.bs_frame = bs_store;
10094        }
10095    }
10096
10097    /// Crops the reconstructed coded-size planes to the display window.
10098    /// `into_frame`, additionally handing the per-picture grids back for reuse by
10099    /// the next picture. See `GridPool` for why this is worth doing.
10100    pub fn into_frame_recycle(mut self, crop_r: usize, crop_b: usize) -> (YuvFrame, GridPool) {
10101        let [c0, c1] = core::mem::take(&mut self.nnz_c);
10102        let pool = GridPool {
10103            job_pool: core::mem::take(&mut self.edc_job_pool),
10104            nores_pool: core::mem::take(&mut self.edc_nores_pool),
10105            bits_per_mb: self.bits_per_mb,
10106            mb_qp: core::mem::take(&mut self.mb_qp),
10107            bs_frame: core::mem::take(&mut self.bs_frame),
10108            pk_prev: core::mem::take(&mut self.pk_prev),
10109            pk_cur: core::mem::take(&mut self.pk_cur),
10110            nnz_dbr: core::mem::take(&mut self.nnz_dbr),
10111            bak_y: core::mem::take(&mut self.bak_y),
10112            bak_u: core::mem::take(&mut self.bak_u),
10113            bak_v: core::mem::take(&mut self.bak_v),
10114            nnz_y: core::mem::take(&mut self.nnz_y),
10115            nnz_c0: c0,
10116            nnz_c1: c1,
10117            modes_y: core::mem::take(&mut self.modes_y),
10118            coded_y: core::mem::take(&mut self.coded_y),
10119            mv_y: core::mem::take(&mut self.mv_y),
10120            inter_y: core::mem::take(&mut self.inter_y),
10121            ref_idx_y: core::mem::take(&mut self.ref_idx_y),
10122            mv1: core::mem::take(&mut self.mv1),
10123            ref_idx1: core::mem::take(&mut self.ref_idx1),
10124            mb_t8x8: core::mem::take(&mut self.mb_t8x8),
10125            mb_umot: core::mem::take(&mut self.mb_umot),
10126            mb_kind: core::mem::take(&mut self.mb_kind),
10127            bzero: core::mem::take(&mut self.bzero),
10128            sc_cat: core::mem::take(&mut self.sc_cat),
10129            sc_cbp: core::mem::take(&mut self.sc_cbp),
10130            sc_cmode: core::mem::take(&mut self.sc_cmode),
10131            sc_nzc: core::mem::take(&mut self.sc_nzc),
10132            sc_cbfdc: core::mem::take(&mut self.sc_cbfdc),
10133            sc_skip: core::mem::take(&mut self.sc_skip),
10134            sc_ref: core::mem::take(&mut self.sc_ref),
10135            sc_mvd: core::mem::take(&mut self.sc_mvd),
10136            sc_ref1: core::mem::take(&mut self.sc_ref1),
10137            sc_mvd1: core::mem::take(&mut self.sc_mvd1),
10138            sc_direct: core::mem::take(&mut self.sc_direct),
10139            ref_poc0: core::mem::take(&mut self.ref_poc0),
10140            ref_poc1: core::mem::take(&mut self.ref_poc1),
10141        };
10142        (self.into_frame(crop_r, crop_b), pool)
10143    }
10144
10145    pub fn into_frame(self, crop_r: usize, crop_b: usize) -> YuvFrame {
10146        // No cropping (the common case): the reconstruction planes ARE the output —
10147        // move them out instead of allocating + copying three full planes per frame.
10148        if crop_r == 0 && crop_b == 0 {
10149            return YuvFrame {
10150                width: self.cw,
10151                height: self.ch,
10152                y: self.rec_y,
10153                u: self.rec_u,
10154                v: self.rec_v,
10155            };
10156        }
10157        let dw = self.cw - 2 * crop_r;
10158        let dh = self.ch - 2 * crop_b;
10159        let mut y = vec![0u8; dw * dh];
10160        for row in 0..dh {
10161            y[row * dw..row * dw + dw]
10162                .copy_from_slice(&self.rec_y[row * self.cw..row * self.cw + dw]);
10163        }
10164        let (cdw, cdh) = (dw / 2, dh / 2);
10165        let mut u = vec![0u8; cdw * cdh];
10166        let mut v = vec![0u8; cdw * cdh];
10167        for row in 0..cdh {
10168            u[row * cdw..row * cdw + cdw]
10169                .copy_from_slice(&self.rec_u[row * self.ccw..row * self.ccw + cdw]);
10170            v[row * cdw..row * cdw + cdw]
10171                .copy_from_slice(&self.rec_v[row * self.ccw..row * self.ccw + cdw]);
10172        }
10173        let _ = self.cch;
10174        YuvFrame {
10175            width: dw,
10176            height: dh,
10177            y,
10178            u,
10179            v,
10180        }
10181    }
10182}
10183
10184/// Reads `ref_idx_l0` as `te(v)` with range `num_ref_active - 1`: a single flag
10185/// when exactly two references are active (cMax == 1), else `ue(v)`.
10186// ---- CABAC binarization engine helpers (openh264 cabac_decoder.cpp) ----
10187
10188/// Unary bin (`DecodeUnaryBinCabac`): bin0 at `ctx`; if 1, count bins at `ctx+off`
10189/// (including the terminating 0) until a 0.
10190/// CAVLC `mvd_lX` with a sanity bound. `se(v)` can legally code ±2^31-1, but
10191/// every profile/level caps |MV| far below ±2^17 quarter-pel units; beyond
10192/// that is a corrupt stream, and the unchecked `pmv + mvd` addition would
10193/// overflow i32 (debug panic / release wrap into an absurd vector).
10194fn read_mvd(r: &mut BitReader) -> Result<i32, MbError> {
10195    let v = r.read_se()?;
10196    if v.unsigned_abs() > (1 << 17) {
10197        return Err(MbError::Truncated);
10198    }
10199    Ok(v)
10200}
10201
10202type Eng = crate::cabac::Engine;
10203type Ctx = crate::cabac::Ctx;
10204
10205fn cabac_unary(e: &mut Eng, data: &[u8], ctx: &mut Ctx, c: usize, off: usize) -> u32 {
10206    if e.decode_decision(data, ctx, c) == 0 {
10207        return 0;
10208    }
10209    let mut sym = 0;
10210    loop {
10211        let bin = e.decode_decision(data, ctx, c + off);
10212        sym += 1;
10213        // Cap the unary run: no valid H.264 element coded through this helper
10214        // (mb_qp_delta) exceeds a few dozen bins, but on malformed / buffer-exhausted
10215        // input the arithmetic engine keeps yielding 1s (it zero-fills past the end),
10216        // which would loop forever. 512 is far beyond any legal value.
10217        if bin == 0 || sym >= 512 {
10218            break;
10219        }
10220    }
10221    sym
10222}
10223
10224/// k-th order Exp-Golomb in bypass (`DecodeExpBypassCabac`). Out of line (it is the
10225/// rare escape arm of levels and mvds), so it takes the engine BY VALUE and hands
10226/// it back: a `&mut Engine` here would be the one address escape in the hot
10227/// callers and LLVM would then home their engine copy on the stack for EVERY bin.
10228#[inline(never)]
10229fn cabac_exp_bypass(mut e: Eng, data: &[u8], mut count: i32) -> (u32, Eng) {
10230    let mut sym = 0u32;
10231    loop {
10232        let c = e.decode_bypass(data);
10233        if c == 1 {
10234            sym += 1 << count;
10235            count += 1;
10236        }
10237        if c == 0 || count == 16 {
10238            break;
10239        }
10240    }
10241    let mut sym2 = 0u32;
10242    while count > 0 {
10243        count -= 1;
10244        if e.decode_bypass(data) != 0 {
10245            sym2 |= 1 << count;
10246        }
10247    }
10248    (sym + sym2, e)
10249}
10250
10251/// UEG0 coeff-level suffix (`DecodeUEGLevelCabac`): TU prefix at `c` (<=13) then an
10252/// EG0 bypass suffix. INLINED ALWAYS into the four residual-parser instantiations:
10253/// out of line it took `&mut Engine`, which put the engine back in memory for
10254/// every level >= 2 and spilled the caller around the call.
10255#[inline(always)]
10256fn cabac_ueg_level(e: &mut Eng, data: &[u8], ctx: &mut Ctx, c: usize) -> u32 {
10257    if e.decode_decision(data, ctx, c) == 0 {
10258        return 0;
10259    }
10260    let mut code = 0u32;
10261    let mut tmp;
10262    loop {
10263        tmp = e.decode_decision(data, ctx, c);
10264        code += 1;
10265        if tmp == 0 || code == 12 {
10266            break;
10267        }
10268    }
10269    if tmp != 0 {
10270        let (v, e2) = cabac_exp_bypass(*e, data, 0);
10271        *e = e2;
10272        code += v + 1;
10273    }
10274    code
10275}
10276
10277/// `mb_qp_delta` CABAC (`ParseDeltaQpCabac`): ctxIdxOffset 60, ctxInc = (prev delta != 0).
10278pub fn parse_mb_qp_delta_cabac(cab: &mut crate::cabac::Cabac, last_delta_qp: &mut i32) -> i32 {
10279    const O: usize = 60;
10280    let ctx_inc = (*last_delta_qp != 0) as usize;
10281    let mut qp_delta = 0;
10282    let (data, mut e, ctx) = cab.view();
10283    if e.decode_decision(data, ctx, O + ctx_inc) != 0 {
10284        let code = cabac_unary(&mut e, data, ctx, O + 2, 1) + 1;
10285        qp_delta = ((code + 1) >> 1) as i32;
10286        if code & 1 == 0 {
10287            qp_delta = -qp_delta;
10288        }
10289    }
10290    cab.commit(e);
10291    *last_delta_qp = qp_delta;
10292    qp_delta
10293}
10294
10295// Shared CABAC residual glue tables (NZC_CACHE, RES_*) now live in
10296// `cabac_tables` — both coders read the ONE copy.
10297use rusty_h264_common::cabac_tables::{
10298    NZC_CACHE, RES_CBF, RES_MAP, RES_MAXC2, RES_MAXPOS, RES_ONE,
10299};
10300// res-property values (post GetMbResProperty, CABAC): the ctx-table index.
10301const RP_I16_DC: usize = 1;
10302const RP_I16_AC: usize = 2;
10303const RP_LUMA_4X4: usize = 3;
10304const RP_CHROMA_DC: usize = 7; // U (V=8, same offsets)
10305const RP_CHROMA_AC: usize = 9; // U (V=10, same offsets)
10306/// Luma 8×8 (ctxBlockCat 5). Its RES_MAP/RES_CBF entries stay 0: cat 5 does NOT
10307/// share the `105 + off` / `166 + off` context bases the 4×4 categories use — it
10308/// has its own absolute bases (402 sig, 417 last) and its own per-position
10309/// ctxIdxInc maps below. RES_ONE[6] = 199 IS used, because 227 + 199 = 426 and
10310/// 232 + 199 = 431 reproduce the spec's coeff_abs_level_minus1 base exactly, so
10311/// the level loop needs no special case at all.
10312const RP_LUMA_8X8: usize = 6;
10313
10314// SIG8X8 / LAST8X8 moved to `rusty_h264_common::cabac_tables` (R6-1) so the encoder's
10315// ctxBlockCat 5 writer shares the exact spec data this reader is validated against.
10316use rusty_h264_common::cabac_tables::{LAST8X8, SIG8X8};
10317
10318/// One residual block (openh264 `ParseResidualBlockCabac`), generic over the 5 CABAC
10319/// block categories. `rp` selects the context offsets. DC categories (I16 luma DC,
10320/// chroma DC) take the cbf context from the per-MB `cbf_dc` bitmask + neighbour MB DC
10321/// cbf; AC categories from the padded nzc cache. Returns totalCoeffNum.
10322#[inline(always)]
10323fn residual_block_eng<const RP: usize, const N: usize>(
10324    e: &mut Eng,
10325    data: &[u8],
10326    ctx: &mut Ctx,
10327    nzc: &mut [u8; 48],
10328    cbf_dc: &mut u16,
10329    iz: usize,
10330    plane: usize,
10331    is_intra: bool,
10332    ndc: (u16, u16),    // resolved neighbour DC cbf words (see `resolve_ndc`)
10333    out: &mut [i32; N], // scan-order coefficients written here (fresh-zero on entry)
10334) -> u32 {
10335    // CONST-GENERIC over the block category (`RP`, one of the RP_* literals every
10336    // call site already passed) and the block length (`N` = 4 / 16 / 64): the
10337    // category tests, the five `RES_*` table reads, the 8x8 map selects and the
10338    // output bound all fold at compile time. Chroma U/V share every table
10339    // entry (RES_*[7] == RES_*[8], RES_*[9] == RES_*[10]); only the cbf_dc bit
10340    // differs, so the plane rides in `plane` (0 for luma categories).
10341    const { assert!(N == 4 || N == 16 || N == 64) };
10342    // The CABAC residual parse IS the entropy stage of the decoder on Main-profile
10343    // streams -- it was invisible (a ~47% residue) until this scope named it.
10344    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Entropy);
10345    // ---- coded_block_flag ----
10346    // ctxBlockCat 5 is the ONLY category with no coded_block_flag: its presence is
10347    // inferred from CodedBlockPatternLuma, so parsing one here would desync.
10348    let is8 = RP == RP_LUMA_8X8;
10349    let is_dc = RP == RP_I16_DC || RP == RP_CHROMA_DC;
10350    let bit = RP + plane;
10351    let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
10352    // `nzc` is [u8; 48] and every `NZC_CACHE` entry lies in [9, 47], so this
10353    // clamp is a semantic no-op that hands LLVM BOTH bounds - which is what
10354    // makes `nzc[scan]`, `nzc[scan - 8]` and `nzc[scan - 1]` below provable
10355    // instead of three bounds checks. Same trick as `parse_mvd_partition`.
10356    let scan = NZC_CACHE[iz.min(23)].clamp(8, 47);
10357    if is_dc {
10358        nb = ((ndc.0 >> bit) & 1) as u8;
10359        na = ((ndc.1 >> bit) & 1) as u8;
10360    } else {
10361        let (nbc, nac) = (nzc[scan - 8], nzc[scan - 1]);
10362        if nbc != 0xff {
10363            nb = (nbc != 0) as u8;
10364        }
10365        if nac != 0xff {
10366            na = (nac != 0) as u8;
10367        }
10368    }
10369    if !is8 {
10370        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntCbf);
10371        let cbf = e.decode_decision(data, ctx, 85 + RES_CBF[RP] + (na + (nb << 1)) as usize);
10372        if cbf == 0 {
10373            if !is_dc {
10374                nzc[scan] = 0;
10375            }
10376            return 0;
10377        }
10378        if is_dc {
10379            *cbf_dc |= 1 << bit;
10380        }
10381    }
10382    // ---- significance map ----
10383    let maxpos = RES_MAXPOS[RP] as usize;
10384    debug_assert!(maxpos < N);
10385    // cat 5 uses its own absolute bases; the 4x4 categories share 105/166 + offset.
10386    let (map, last) = if is8 {
10387        (402, 417)
10388    } else {
10389        let m = RES_MAP[RP];
10390        (105 + m, 166 + m)
10391    };
10392    // SPARSE significance map: record each significant POSITION in `pos[..n]`.
10393    // Bin ORDER is unchanged: levels are decoded at descending significant
10394    // positions, which is exactly `pos[..n]` reversed. `pos` is sized to the
10395    // block (N), not 64: a 4x4 block zeroes 16 bytes, not 64.
10396    //
10397    // CONTRACT with the callers (all 10 sites): `out` is freshly zeroed, so
10398    // writing only the significant entries leaves the same contents a dense
10399    // copy would produce. A reused non-zero `out` would be a correctness bug.
10400    // Significant positions as a BITMASK (bit i = scan position i): one OR per
10401    // significant coefficient instead of a store + count increment into a
10402    // zero-initialised array; the level loop walks the set bits from the top
10403    // (descending positions = the spec order).
10404    let mut sig = 0u64;
10405    let mut last_hit = false;
10406    let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntSig);
10407    // 4x4: ctxIdxInc IS the scan position. 8x8: it comes from the folded maps.
10408    // NOTE (4:2:2 landmine): `(i, i)` is correct for every 4:2:0 category
10409    // only because chroma-DC (cat 3) has NumC8x8 == 1 here; spec 9.3.3.1.3
10410    // wants `Min(i / NumC8x8, 2)` for its sig/last ctxIdxInc, which
10411    // diverges the day 4:2:2 (NumC8x8 == 2) is admitted.
10412    for i in 0..maxpos {
10413        // Both maps are [u8; 64] and `i < maxpos <= 63`: `& 63` folds the checks.
10414        let (mi, li) = if is8 {
10415            (
10416                (SIG8X8[i & 63] & 15) as usize,
10417                (LAST8X8[i & 63] & 15) as usize,
10418            )
10419        } else {
10420            (i, i)
10421        };
10422        if e.decode_decision(data, ctx, map + mi) != 0 {
10423            // `n <= maxpos < N` at every write: `& (N - 1)` is the own bound of `pos`.
10424            sig |= 1u64 << i;
10425            if e.decode_decision(data, ctx, last + li) != 0 {
10426                last_hit = true;
10427                break;
10428            }
10429        }
10430    }
10431    if !last_hit {
10432        sig |= 1u64 << maxpos;
10433    }
10434    let coeff_num = sig.count_ones();
10435    // ---- levels ----
10436    let one = 227 + RES_ONE[RP];
10437    let abs = one + 5;
10438    // `usize` with visible `min` bounds: as i32 the casts hid the ranges and every
10439    // `one + c1` / `abs + c2` context index kept its `& 511` (one op per bin).
10440    let maxc2 = RES_MAXC2[RP] as usize;
10441    let (mut c1, mut c2) = (1usize, 0usize);
10442    drop(_sg);
10443    let _lg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntLvl);
10444    let mut m = sig;
10445    while m != 0 {
10446        let i = 63 - m.leading_zeros() as usize;
10447        m &= !(1u64 << i);
10448        let mut level = 1 + e.decode_decision(data, ctx, one + c1) as i32;
10449        if level == 2 {
10450            level += cabac_ueg_level(e, data, ctx, abs + c2) as i32;
10451            c2 = (c2 + 1).min(maxc2);
10452            c1 = 0;
10453        } else if c1 != 0 {
10454            c1 = (c1 + 1).min(4);
10455        }
10456        if e.decode_bypass_sign() != 0 {
10457            level = -level;
10458        }
10459        // `out` is `[i32; N]` and every recorded position is <= maxpos < N, so
10460        // `& (N - 1)` is the own bound of the array -- a proof, not a relocation
10461        // (the runtime-length-slice form this replaces needed `.get_mut`).
10462        out[i & (N - 1)] = level;
10463    }
10464    if is8 {
10465        // One 8x8 covers four consecutive z-order 4x4 cells. Every later
10466        // coded_block_flag ctxIdxInc reads this cache, so all four must carry the
10467        // count -- writing only `scan` would corrupt the contexts of the NEXT
10468        // macroblock.
10469        let cn = coeff_num as u8;
10470        for k in 0..4 {
10471            nzc[NZC_CACHE[(iz + k).min(23)].min(47)] = cn;
10472        }
10473    } else if !is_dc {
10474        nzc[scan] = coeff_num as u8;
10475    }
10476    coeff_num
10477}
10478
10479/// Where a macroblock residual parse writes: plain arrays whose UNCODED blocks
10480/// are stale by contract (every consumer reads a block only when its `nnzs`
10481/// entry is nonzero, or copies the prediction). The parse zeroes exactly the
10482/// blocks it is about to write.
10483struct ResidualOut<'a> {
10484    luma: &'a mut [[i32; 16]; 16],
10485    luma8: &'a mut [[i32; 64]; 4],
10486    cdc: &'a mut [[i32; 4]; 2],
10487    cac: &'a mut [[[i32; 16]; 4]; 2],
10488    /// Per-block totalCoeff, `iz`-indexed; must be zero on entry.
10489    nnzs: &'a mut [u8; 24],
10490}
10491
10492/// The neighbour-record form of a macroblock per-block coefficient counts:
10493/// luma in RASTER order (from the z-order `nnzs`, via G_SCAN4, which is its own
10494/// inverse) and chroma Cb/Cr in the interleaved slot order the readers expect.
10495/// Uncoded blocks are already 0 in `nnzs`, so the 0xff->0 scrub of the old
10496/// cache-based export is gone with the 24 scattered cache reads.
10497/// Explicit-weighted-prediction sample: `((s*w + round) >> denom) + o`, clipped.
10498#[inline(always)]
10499fn weight_sample(sample: u8, w: i32, o: i32, denom: i32) -> u8 {
10500    let v = if denom >= 1 {
10501        ((sample as i32 * w + (1 << (denom - 1))) >> denom) + o
10502    } else {
10503        sample as i32 * w + o
10504    };
10505    v.clamp(0, 255) as u8
10506}
10507
10508/// CONST-WIDTH rows of the weighted-prediction loop (SIMD census 2026-09-05,
10509/// finding #9). The two recon twins carried the IDENTICAL runtime-width loop
10510/// (`for dx in 0..rw` over a masked index) and LLVM vectorised it to 500 packed
10511/// ops in one inlining context and 53 in the other. A partition width is one
10512/// of 16/8/4 (chroma 8/4/2); handing LLVM the trip count makes both contexts
10513/// vectorise the same way. One row slice per row instead of a masked index per
10514/// sample; the sample formula is unchanged.
10515#[inline(always)]
10516fn weight_rows<const W: usize>(
10517    plane: &mut [u8],
10518    stride: usize,
10519    rx: usize,
10520    ry: usize,
10521    rh: usize,
10522    w: i32,
10523    o: i32,
10524    denom: i32,
10525) {
10526    for dy in 0..rh {
10527        let row = &mut plane[(ry + dy) * stride + rx..][..W];
10528        for s in row.iter_mut() {
10529            *s = weight_sample(*s, w, o, denom);
10530        }
10531    }
10532}
10533
10534#[inline]
10535fn weight_block(
10536    plane: &mut [u8],
10537    stride: usize,
10538    rx: usize,
10539    ry: usize,
10540    rw: usize,
10541    rh: usize,
10542    w: i32,
10543    o: i32,
10544    denom: i32,
10545) {
10546    match rw {
10547        16 => weight_rows::<16>(plane, stride, rx, ry, rh, w, o, denom),
10548        8 => weight_rows::<8>(plane, stride, rx, ry, rh, w, o, denom),
10549        4 => weight_rows::<4>(plane, stride, rx, ry, rh, w, o, denom),
10550        2 => weight_rows::<2>(plane, stride, rx, ry, rh, w, o, denom),
10551        _ => {
10552            for dy in 0..rh {
10553                for dx in 0..rw {
10554                    let i = (ry + dy) * stride + rx + dx;
10555                    if let Some(s) = plane.get_mut(i) {
10556                        *s = weight_sample(*s, w, o, denom);
10557                    }
10558                }
10559            }
10560        }
10561    }
10562}
10563
10564/// Residual class of an intra 4x4 block (see `FrameDecoder::i4_prepare`).
10565#[derive(Clone, Copy)]
10566enum I4Res {
10567    Zero,
10568    Flat(i32),
10569    Idx(u8),
10570}
10571
10572/// z-order index of the 4x4 block at raster (x, y) -- inverse of `LUMA_4X4_SCAN_XY`.
10573const I4_Z_OF_XY: [[usize; 4]; 4] = {
10574    let mut t = [[0usize; 4]; 4];
10575    let mut i = 0;
10576    while i < 16 {
10577        let (x, y) = LUMA_4X4_SCAN_XY[i];
10578        t[y][x] = i;
10579        i += 1;
10580    }
10581    t
10582};
10583/// Bit i set iff the top-right 4x4 neighbour of z-order block i lies INSIDE the
10584/// macroblock and is already reconstructed when block i is (spec 6.4.11.4 via the
10585/// decode order): the classic i4x4 top-right availability table, here derived from
10586/// the scan table at compile time instead of hand-typed.
10587const I4_TR_IN_MB: u16 = {
10588    let mut m = 0u16;
10589    let mut i = 0;
10590    while i < 16 {
10591        let (x, y) = LUMA_4X4_SCAN_XY[i];
10592        if y > 0 && x < 3 && I4_Z_OF_XY[y - 1][x + 1] < i {
10593            m |= 1 << i;
10594        }
10595        i += 1;
10596    }
10597    m
10598};
10599
10600// SPARSE-vs-DENSE DEQUANT ROUTE (routing round C, 2026-09-05): the DQROUTE census
10601// (scatter = 37 + 6L + 9nnz vs dense-AVX2 ~60; crowd 134.8M -> 80.3M instrs / 60 f)
10602// retired the scan-walking scatter; every coded block now takes the FUSED
10603// scan-order kernel (`reconstruct_4x4_scan_into`: un-scan + dequant + IDCT + add).
10604// The (nnz, L) histogram tap `edcstat::dq_note` stays for the next route question.
10605
10606/// Neighbour DC coded_block_flags as plain bit words: an unavailable neighbour
10607/// contributes the intra default for EVERY category, so the per-block
10608/// `Option` test becomes one shift. Resolved once per macroblock.
10609#[inline(always)]
10610fn resolve_ndc(ndc: (Option<u16>, Option<u16>), is_intra: bool) -> (u16, u16) {
10611    let d = if is_intra { 0xFFFF } else { 0 };
10612    (ndc.0.unwrap_or(d), ndc.1.unwrap_or(d))
10613}
10614
10615/// The WHOLE residual of one non-I16 macroblock (luma 4x4 or 8x8 by cbp bit,
10616/// chroma DC, chroma AC) on ONE engine view. The block body is inlined here,
10617/// so a macroblock costs one call and one engine load/store, where each block
10618/// used to pay a call, eight arguments (five on the stack), an eight-register
10619/// prologue/epilogue and its own view/commit round trip -- ~40 instructions
10620/// per block beside the bins, on 7-24 blocks per coded macroblock.
10621fn parse_mb_residual_cabac<const INTRA: bool>(
10622    cab: &mut crate::cabac::Cabac,
10623    nzc: &mut [u8; 48],
10624    cbf_dc: &mut u16,
10625    ndc: (Option<u16>, Option<u16>),
10626    cbp_luma: u32,
10627    cbp_chroma: u32,
10628    t8: bool,
10629    o: ResidualOut,
10630) {
10631    // INTRA is a const generic: the cbf defaults and the neighbour-word resolve
10632    // fold per instantiation (every call site passes a literal).
10633    let is_intra = INTRA;
10634    let nd = resolve_ndc(ndc, INTRA);
10635    let (data, mut e, ctx) = cab.view();
10636    for id8 in 0..4usize {
10637        if cbp_luma & (1 << id8) != 0 {
10638            if t8 {
10639                // ctxBlockCat 5: one 64-coefficient block per 8x8, no cbf. All
10640                // four nnzs slots carry the 8x8 total (the recon reads one per cell).
10641                o.luma8[id8] = [0i32; 64];
10642                let n = residual_block_eng::<RP_LUMA_8X8, 64>(
10643                    &mut e,
10644                    data,
10645                    ctx,
10646                    nzc,
10647                    cbf_dc,
10648                    id8 * 4,
10649                    0,
10650                    is_intra,
10651                    nd,
10652                    &mut o.luma8[id8],
10653                ) as u8;
10654                o.nnzs[id8 * 4..id8 * 4 + 4].fill(n);
10655            } else {
10656                o.luma[id8 * 4..id8 * 4 + 4].fill([0i32; 16]);
10657                for id4 in 0..4usize {
10658                    let iz = id8 * 4 + id4;
10659                    o.nnzs[iz] = residual_block_eng::<RP_LUMA_4X4, 16>(
10660                        &mut e,
10661                        data,
10662                        ctx,
10663                        nzc,
10664                        cbf_dc,
10665                        iz,
10666                        0,
10667                        is_intra,
10668                        nd,
10669                        &mut o.luma[iz],
10670                    ) as u8;
10671                }
10672            }
10673        } else {
10674            for k in 0..4 {
10675                nzc[NZC_CACHE[(id8 * 4 + k).min(23)].min(47)] = 0;
10676            }
10677        }
10678    }
10679    if cbp_chroma >= 1 {
10680        *o.cdc = [[0i32; 4]; 2];
10681        for i in 0..2usize {
10682            residual_block_eng::<RP_CHROMA_DC, 4>(
10683                &mut e,
10684                data,
10685                ctx,
10686                nzc,
10687                cbf_dc,
10688                16 + i * 4,
10689                i,
10690                is_intra,
10691                nd,
10692                &mut o.cdc[i],
10693            );
10694        }
10695    }
10696    if cbp_chroma == 2 {
10697        *o.cac = [[[0i32; 16]; 4]; 2];
10698        for i in 0..2usize {
10699            for id4 in 0..4usize {
10700                o.nnzs[16 + i * 4 + id4] = residual_block_eng::<RP_CHROMA_AC, 16>(
10701                    &mut e,
10702                    data,
10703                    ctx,
10704                    nzc,
10705                    cbf_dc,
10706                    16 + i * 4 + id4,
10707                    i,
10708                    is_intra,
10709                    nd,
10710                    &mut o.cac[i][id4],
10711                ) as u8;
10712            }
10713        }
10714    }
10715    cab.commit(e);
10716}
10717
10718// ============================================================================
10719// Entropy-decouple E2: the OWNED pixel context that crosses the thread
10720// boundary (docs/entropy-decouple-plan.md). The worker owns the planes, the
10721// backup rows, the DPB Arcs and its own qp/t8/bs grids (fed by Row messages);
10722// the parse thread keeps every syntax grid. The methods below are ports of
10723// the FrameDecoder pixel halves — grid writes removed (parse commits its own
10724// grids), motion carried in the job instead of re-gathered.
10725// ============================================================================
10726
10727/// The committed-value class of a deferred B_Skip span. Continuation
10728/// requires kind EQUALITY: the flush range-fills grids and recons the band
10729/// from these values alone.
10730#[derive(Clone, Copy, PartialEq, Eq)]
10731enum BzKind {
10732    /// ref 0 both lists, (0,0)/(0,0) — band = avg of both padded refs.
10733    ZeroBi,
10734    /// One active list at (0,0): (list, clamped ref index) — band = memcpy.
10735    ZeroUni(u8, u8),
10736    /// Uniform full-pel direct: clamped refs (-1 = inactive) + adjusted MVs,
10737    /// windows PREVALIDATED per MB at push (contiguous tiles ⇒ the union
10738    /// window is valid) — band = offset copy/avg. Chroma requires mv%8==0
10739    /// (also prevalidated).
10740    Fp {
10741        r0: i8,
10742        r1: i8,
10743        m0: (i16, i16),
10744        m1: (i16, i16),
10745    },
10746}
10747
10748/// Messages from the parse thread to the pixel worker.
10749enum EdcMsg {
10750    Job(EdcJob),
10751    /// A ROW's worth of pixel jobs in one message (D10).
10752    ///
10753    /// The seam sent ONE message per macroblock: 208k sends per 60-frame pass
10754    /// against 2,596 rows. The overhead is per-JOB (channel lock, park/unpark)
10755    /// while the prize is proportional to pixel WORK, so the per-job send was
10756    /// dividing the payoff by ~80 for nothing. Batching per row cuts the
10757    /// synchronisation events by that factor and moves not one byte of pixel
10758    /// work off the worker.
10759    ///
10760    /// ORDER IS THE CORRECTNESS CONDITION: the worker must see a row's jobs
10761    /// before that row's `Row` filter message, so the batch is flushed at every
10762    /// row boundary, before `NeedCtx`, and at slice end.
10763    Batch(Vec<EdcJob>),
10764    /// A macroblock row finished parsing: install its qp/t8/bs and filter it.
10765    Row {
10766        r: usize,
10767        bs: Vec<rusty_h264_common::deblock::MbBs>,
10768        qp: Vec<u8>,
10769        t8: Vec<bool>,
10770    },
10771    /// An intra macroblock needs the planes on the parse thread: send the
10772    /// context over and wait for it to come back.
10773    NeedCtx,
10774}
10775
10776pub(crate) struct PixelCtx {
10777    rec_y: Vec<u8>,
10778    rec_u: Vec<u8>,
10779    rec_v: Vec<u8>,
10780    bak_y: Vec<u8>,
10781    bak_u: Vec<u8>,
10782    bak_v: Vec<u8>,
10783    refs: Vec<crate::Ref>,
10784    refs1: Vec<crate::Ref>,
10785    weights: Option<WeightTable>,
10786    /// `weights.list_identity(0)`, cached - mirrors `FrameDecoder::weights_l0id`
10787    /// so the worker twin does not have to walk the table per macroblock.
10788    weights_l0id: bool,
10789    scaling: Option<[[i32; 16]; 6]>,
10790    scaling8: Option<[[i32; 64]; 2]>,
10791    cw: usize,
10792    ccw: usize,
10793    mb_w: usize,
10794    mb_h: usize,
10795    chroma_qp_offset: i32,
10796    flt_rows: usize,
10797    db_ena: bool,
10798    db_oa: i32,
10799    db_ob: i32,
10800    cur_qp: u8,
10801    qp_grid: Vec<u8>,
10802    t8_grid: Vec<bool>,
10803    bs_store: Vec<rusty_h264_common::deblock::MbBs>,
10804    /// Frame-MT Phase B progress Arc (row publish from the EDC worker).
10805    progress: Option<crate::Ref>,
10806}
10807
10808impl PixelCtx {
10809    /// Frame-MT Phase B: copy filtered MB rows into the shared progress Arc.
10810    fn publish_progress_rows(&self) {
10811        let Some(slot) = &self.progress else {
10812            return;
10813        };
10814        if !self.db_ena || self.flt_rows == 0 {
10815            return;
10816        }
10817        publish_filtered_rows_to_slot(
10818            slot,
10819            &self.rec_y,
10820            &self.rec_u,
10821            &self.rec_v,
10822            self.cw,
10823            self.ccw,
10824            self.mb_h * 16,
10825            self.flt_rows,
10826        );
10827    }
10828
10829    fn chroma_qp_for(&self, qp: u8) -> u8 {
10830        rusty_h264_common::predict::chroma_qp(
10831            ((qp as i32 + self.chroma_qp_offset).clamp(0, 51)) as u8,
10832        )
10833    }
10834
10835    /// Per-(qp, list) dequant constants for the fused scan-order kernel (worker twin).
10836    #[inline]
10837    fn dq_const(&self, qp: u8, list: usize) -> DequantQp {
10838        match &self.scaling {
10839            Some(s) => DequantQp::weighted(qp, &s[list]),
10840            None => DQ_FLAT[(qp as usize).min(51)],
10841        }
10842    }
10843
10844    fn dequant_dc4(&self, level: i32, qp: u8, list: usize) -> i32 {
10845        rusty_h264_common::transform::dequantize_dc4(
10846            level,
10847            qp,
10848            self.scaling.as_ref().map(|sc| sc[list][0]),
10849        )
10850    }
10851
10852    fn inv_quant8(&self, raster: &[i32; 64], qp: u8, list: usize) -> [i32; 64] {
10853        // Prebuilt per-(qp, list) constants: the flat table at zero cost, or one
10854        // 64-entry build per macroblock for scaling lists (was 64 x two lookups +
10855        // a multiply per coefficient on every block).
10856        match &self.scaling8 {
10857            Some(s) => inverse_quant_8x8_dq(raster, &Dequant8Qp::build(qp, &s[list])),
10858            None => inverse_quant_8x8_dq(raster, &DQ8_FLAT[(qp as usize).min(51)]),
10859        }
10860    }
10861
10862    fn dequant_chroma_dc(&self, levels: &[i32; 4], qp: u8, list: usize) -> [i32; 4] {
10863        match &self.scaling {
10864            Some(sc) => inverse_quant_chroma_dc_weighted(levels, qp, sc[list][0]),
10865            None => inverse_quant_chroma_dc(levels, qp),
10866        }
10867    }
10868
10869    fn weight_partition(
10870        &self,
10871        pred_y: &mut [u8; 256],
10872        c_pred: &mut [[u8; 64]; 2],
10873        list: usize,
10874        refi: usize,
10875        rx: usize,
10876        ry: usize,
10877        rw: usize,
10878        rh: usize,
10879    ) {
10880        let Some(wt) = &self.weights else { return };
10881        // REFUTED, reverted: row-slicing these loops measured +28% instructions
10882        // on `weight_partition` (the extents are runtime values, so the slice
10883        // bounds cost more than the per-sample checks they replaced). The real
10884        // win for this function was hoisting the identity test to its CALLERS,
10885        // which stands.
10886        // RESOLVE ONCE, APPLY MANY. `apply_luma` re-read `self.luma[list][refi]`
10887        // — an array index, a Vec deref and an element index, two of them bounds
10888        // checked — on EVERY sample, up to 256 luma plus 128 chroma per call.
10889        // The weight is a property of the partition, not of the pixel. (The loop
10890        // SHAPE is untouched: row-slicing it is refuted above.)
10891        // MASKED, not row-sliced. `pred_y` is a fixed `[u8; 256]` and `c_pred` a
10892        // `[[u8; 64]; 2]`, so `& 255` / `& 63` are no-ops that prove the index
10893        // outright — where SLICING these loops cost +28% (the extents are runtime
10894        // values, so the slice bounds outweigh the checks). Same lesson as
10895        // `luma_centre`: the refutation was of one SHAPE, not of the goal.
10896        let (lw, lo) = wt.luma_wo(list, refi);
10897        weight_block(pred_y, 16, rx, ry, rw, rh, lw, lo, wt.luma_log2_denom);
10898        let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
10899        for cc in 0..2 {
10900            let (cw, co) = wt.chroma_wo(list, refi, cc);
10901            weight_block(
10902                &mut c_pred[cc & 1],
10903                8,
10904                crx,
10905                cry,
10906                crw,
10907                crh,
10908                cw,
10909                co,
10910                wt.chroma_log2_denom,
10911            );
10912        }
10913    }
10914
10915    fn recon_p_inter(&mut self, j: &PInterJob) {
10916        crate::RefFrame::set_mc_row_need(j.mby, self.mb_h * 16);
10917        // `add_inter_residual` reads `self.cur_qp`; unlike the FrameDecoder copy
10918        // (which interleaves with parsing and must save/restore), every PixelCtx
10919        // job sets it from the job before any reader, so no restore is needed.
10920        self.cur_qp = j.qp;
10921        // ---- Recon: motion-comp (per 4×4 luma / co-located 2×2 chroma using the
10922        // committed grid MV — the 6-tap/bilinear filter is per-output-pixel, so
10923        // per-block MC is bit-identical to per-partition MC) + residual add via the
10924        // SAME reconstruct_4x4 as intra, with the MC output as the prediction.
10925        let mut pred_y = [0u8; 256];
10926        let mut c_pred = [[0u8; 64]; 2];
10927        {
10928            // MC-CALL COALESCING (side-by-side descent, dec target #2): the old
10929            // loop paid 16 mc_luma(4×4) + 32 mc_chroma(2×2) per MB regardless of
10930            // partitioning — 48 calls even for a single-MV 16×16 MB, and the
10931            // per-call glue around 2.4M calls was ~40% of decoding real-world
10932            // (x264) streams. The 6-tap/bilinear filters are per-output-pixel,
10933            // so merging blocks with equal (mv, ref) into one wider MC call is
10934            // BIT-IDENTICAL; the rect ladder mirrors the partition shapes.
10935            let _ms = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMcStage);
10936            let (rh16, cch) = (self.mb_h * 16, self.mb_h * 8);
10937            // E2: the worker owns no syntax grids — the job carries the
10938            // committed per-block motion (filled at parse time).
10939            let gmv = j.gmv;
10940            let mut gref = [0usize; 16];
10941            for k in 0..16 {
10942                gref[k] = (j.gref[k] as usize).min(self.refs.len() - 1);
10943            }
10944            // All blocks of the rect (in 4×4-block units) match its top-left?
10945            let rect_eq = |x4: usize, y4: usize, w4: usize, h4: usize| -> bool {
10946                let t = y4 * 4 + x4;
10947                (0..h4).all(|dy| {
10948                    (0..w4).all(|dx| {
10949                        let b = ((y4 + dy) * 4 + (x4 + dx)) & 15;
10950                        gmv[b] == gmv[t] && gref[b] == gref[t]
10951                    })
10952                })
10953            };
10954            let refs = &self.refs;
10955            let (cw, ccw) = (self.cw, self.ccw);
10956            let mc_rect = |x4: usize,
10957                           y4: usize,
10958                           w4: usize,
10959                           h4: usize,
10960                           pred_y: &mut [u8; 256],
10961                           c_pred: &mut [[u8; 64]; 2]| {
10962                let b = y4 * 4 + x4;
10963                let (mv, gr) = (gmv[b & 15], gref[b & 15]);
10964                // `gref` holds a reference-list slot; a slot the list
10965                // does not have means there is nothing to predict from,
10966                // so the rect is left as it stands.
10967                let Some(reference) = refs.get(gr) else {
10968                    return;
10969                };
10970                let (w, h) = (w4 * 4, h4 * 4);
10971                // A FULL-WIDTH rect (w == 16, so x4 == 0) occupies contiguous
10972                // whole rows of `pred_y` — the MC output layout and the
10973                // destination layout coincide, so MC writes the prediction
10974                // buffer DIRECTLY. The staging copy exists only for narrow
10975                // rects, whose rows really are strided in `pred_y`. This is
10976                // the diagnosis's "stage-boundary materialization" tax paid
10977                // by the dominant 16×16/16×8 shapes: 256 B of `t` zeroing
10978                // plus a 256 B copy per rect, for nothing.
10979                if w == 16 {
10980                    rusty_h264_common::inter::with_mc_scratch(|scr| {
10981                        rusty_h264_common::inter::mc_luma_padded_pre(
10982                            scr,
10983                            &reference.luma_guard(reference.ch),
10984                            reference.lstride(),
10985                            crate::LPAD,
10986                            cw,
10987                            rh16,
10988                            j.mbx * 16,
10989                            j.mby * 16 + y4 * 4,
10990                            w,
10991                            h,
10992                            i32::from(mv.0),
10993                            i32::from(mv.1),
10994                            &mut pred_y[y4 * 64..y4 * 64 + w * h],
10995                        )
10996                    });
10997                } else {
10998                    let mut t = [0u8; 256];
10999                    rusty_h264_common::inter::with_mc_scratch(|scr| {
11000                        rusty_h264_common::inter::mc_luma_padded_pre(
11001                            scr,
11002                            &reference.luma_guard(reference.ch),
11003                            reference.lstride(),
11004                            crate::LPAD,
11005                            cw,
11006                            rh16,
11007                            j.mbx * 16 + x4 * 4,
11008                            j.mby * 16 + y4 * 4,
11009                            w,
11010                            h,
11011                            i32::from(mv.0),
11012                            i32::from(mv.1),
11013                            &mut t[..w * h],
11014                        )
11015                    });
11016                    let _pb =
11017                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
11018                    for dy in 0..h {
11019                        pred_y[(y4 * 4 + dy) * 16 + x4 * 4..][..w]
11020                            .copy_from_slice(&t[dy * w..dy * w + w]);
11021                    }
11022                }
11023                let (cw4, ch4) = (w4 * 2, h4 * 2);
11024                let nc = cw4 * ch4;
11025                let (gu, gv) = (
11026                    reference.chroma_guard(0, reference.ch),
11027                    reference.chroma_guard(1, reference.ch),
11028                );
11029                // U+V paired: one setup serves both planes (see
11030                // mc_chroma_padded_pair). Full-width coincidence:
11031                // cw4 == 8 rows are contiguous in the 8-wide plane.
11032                if cw4 == 8 {
11033                    let [cu, cv] = &mut *c_pred;
11034                    rusty_h264_common::inter::mc_chroma_padded_pair(
11035                        &gu,
11036                        &gv,
11037                        reference.cstride(),
11038                        crate::CPAD,
11039                        ccw,
11040                        cch,
11041                        j.mbx * 8,
11042                        j.mby * 8 + y4 * 2,
11043                        cw4,
11044                        ch4,
11045                        i32::from(mv.0),
11046                        i32::from(mv.1),
11047                        &mut cu[y4 * 16..y4 * 16 + nc],
11048                        &mut cv[y4 * 16..y4 * 16 + nc],
11049                    );
11050                } else {
11051                    let (mut tu, mut tv) = ([0u8; 64], [0u8; 64]);
11052                    rusty_h264_common::inter::mc_chroma_padded_pair(
11053                        &gu,
11054                        &gv,
11055                        reference.cstride(),
11056                        crate::CPAD,
11057                        ccw,
11058                        cch,
11059                        j.mbx * 8 + x4 * 2,
11060                        j.mby * 8 + y4 * 2,
11061                        cw4,
11062                        ch4,
11063                        i32::from(mv.0),
11064                        i32::from(mv.1),
11065                        &mut tu[..nc],
11066                        &mut tv[..nc],
11067                    );
11068                    let _pb =
11069                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
11070                    for (cc, tc) in [(0usize, &tu), (1, &tv)] {
11071                        for dy in 0..ch4 {
11072                            c_pred[cc][(y4 * 2 + dy) * 8 + x4 * 2..][..cw4]
11073                                .copy_from_slice(&tc[dy * cw4..dy * cw4 + cw4]);
11074                        }
11075                    }
11076                }
11077            };
11078            if rect_eq(0, 0, 4, 4) {
11079                mc_rect(0, 0, 4, 4, &mut pred_y, &mut c_pred);
11080            } else if rect_eq(0, 0, 4, 2) && rect_eq(0, 2, 4, 2) {
11081                mc_rect(0, 0, 4, 2, &mut pred_y, &mut c_pred);
11082                mc_rect(0, 2, 4, 2, &mut pred_y, &mut c_pred);
11083            } else if rect_eq(0, 0, 2, 4) && rect_eq(2, 0, 2, 4) {
11084                mc_rect(0, 0, 2, 4, &mut pred_y, &mut c_pred);
11085                mc_rect(2, 0, 2, 4, &mut pred_y, &mut c_pred);
11086            } else {
11087                for q in 0..4usize {
11088                    let (qx, qy) = ((q % 2) * 2, (q / 2) * 2);
11089                    if rect_eq(qx, qy, 2, 2) {
11090                        mc_rect(qx, qy, 2, 2, &mut pred_y, &mut c_pred);
11091                    } else if rect_eq(qx, qy, 2, 1) && rect_eq(qx, qy + 1, 2, 1) {
11092                        mc_rect(qx, qy, 2, 1, &mut pred_y, &mut c_pred);
11093                        mc_rect(qx, qy + 1, 2, 1, &mut pred_y, &mut c_pred);
11094                    } else if rect_eq(qx, qy, 1, 2) && rect_eq(qx + 1, qy, 1, 2) {
11095                        mc_rect(qx, qy, 1, 2, &mut pred_y, &mut c_pred);
11096                        mc_rect(qx + 1, qy, 1, 2, &mut pred_y, &mut c_pred);
11097                    } else {
11098                        for j in 0..4usize {
11099                            mc_rect(qx + (j % 2), qy + (j / 2), 1, 1, &mut pred_y, &mut c_pred);
11100                        }
11101                    }
11102                }
11103            }
11104            // EXPLICIT WEIGHTED PREDICTION (spec 8.4.2.3). The CAVLC inter
11105            // path weights each partition after MC; the MC-call-coalescing
11106            // rewrite of this CABAC path lost it, and nothing caught that
11107            // because the effect is invisible unless a stream actually
11108            // carries non-default weights. x264's `weightp` DUPLICATES a
11109            // reference and distinguishes the copy ONLY by its weights, so
11110            // every macroblock picking the weighted index decoded unweighted
11111            // -- a silent, accumulating luma drift.
11112            //
11113            // Applied per 4x4 block rather than per partition: the weight
11114            // depends solely on the block's reference index, so the two are
11115            // equivalent, and `gref` already holds it for every block
11116            // regardless of which rect ladder rung ran.
11117            if self.weights.is_some() {
11118                for by in 0..4usize {
11119                    for bx in 0..4usize {
11120                        let refi = gref[by * 4 + bx];
11121                        self.weight_partition(
11122                            &mut pred_y,
11123                            &mut c_pred,
11124                            0,
11125                            refi,
11126                            bx * 4,
11127                            by * 4,
11128                            4,
11129                            4,
11130                        );
11131                    }
11132                }
11133            }
11134        }
11135        // Residual add — the SAME helper the B path uses (this inline
11136        // copy was a duplicate; deduped when the zero-block fast path
11137        // landed so both paths share it).
11138        self.add_inter_residual(
11139            j.mbx,
11140            j.mby,
11141            &pred_y,
11142            &c_pred,
11143            Some(&j.luma_scan),
11144            j.t8.then_some(&j.luma8),
11145            &j.cdc,
11146            Some(&j.cac),
11147            j.cbp_chroma,
11148            &j.nnzs,
11149        );
11150    }
11151
11152    /// D9b: P inter with `cbp == 0` — MC + plane copy (worker twin of FrameDecoder).
11153    fn recon_p_inter_nores(&mut self, j: &PInterNoResJob) {
11154        let mut pred_y = [0u8; 256];
11155        let mut c_pred = [[0u8; 64]; 2];
11156        // `self.refs.len() - 1` was re-loaded on every one of the sixteen
11157        // iterations; it is a per-macroblock invariant. Written once, not
11158        // zeroed-then-filled.
11159        let nrefs = self.refs.len() - 1;
11160        let gref: [usize; 16] = core::array::from_fn(|k| (j.gref[k] as usize).min(nrefs));
11161        coalesce_p_inter_mc(
11162            &self.refs,
11163            self.cw,
11164            self.ccw,
11165            self.mb_h,
11166            j.mbx,
11167            j.mby,
11168            &j.gmv,
11169            &gref,
11170            &mut pred_y,
11171            &mut c_pred,
11172        );
11173        // The identity early-out lives INSIDE `weight_partition`, so an x264
11174        // stream - which carries a pred_weight_table in EVERY P slice, identity
11175        // outside fades - still paid sixteen calls per macroblock to be told
11176        // there was nothing to do. Hoisted to one test.
11177        if self.weights.is_some() && !self.weights_l0id {
11178            for by in 0..4usize {
11179                for bx in 0..4usize {
11180                    let refi = gref[by * 4 + bx];
11181                    self.weight_partition(&mut pred_y, &mut c_pred, 0, refi, bx * 4, by * 4, 4, 4);
11182                }
11183            }
11184        }
11185        // ONE span per plane: the destination is a stack of contiguous runs a
11186        // fixed stride apart, so the rows share a single bounds check instead of
11187        // one each (16 + 8 + 8 of them).
11188        let (cw, ccw) = (self.cw, self.ccw);
11189        let ybase = (j.mby * 16) * cw + j.mbx * 16;
11190        let ywin = &mut self.rec_y[ybase..ybase + 15 * cw + 16];
11191        for dy in 0..16 {
11192            ywin[dy * cw..dy * cw + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
11193        }
11194        let cbase = (j.mby * 8) * ccw + j.mbx * 8;
11195        for c in 0..2 {
11196            let plane = if c == 0 {
11197                &mut self.rec_u
11198            } else {
11199                &mut self.rec_v
11200            };
11201            let cwin = &mut plane[cbase..cbase + 7 * ccw + 8];
11202            for dy in 0..8 {
11203                cwin[dy * ccw..dy * ccw + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
11204            }
11205        }
11206    }
11207
11208    fn recon_p_skip(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32)) {
11209        crate::RefFrame::set_mc_row_need(mb_y, self.mb_h * 16);
11210        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
11211
11212        let mut pred = [0u8; 256];
11213        let Some(rf0) = self.refs.first() else { return };
11214        mc_luma_padded(
11215            &rf0.luma_guard(rf0.ch),
11216            rf0.lstride(),
11217            crate::LPAD,
11218            self.cw,
11219            ch,
11220            mb_x * 16,
11221            mb_y * 16,
11222            16,
11223            16,
11224            mv.0,
11225            mv.1,
11226            &mut pred,
11227        );
11228        if let Some(wt) = &self.weights {
11229            for p in pred.iter_mut() {
11230                *p = wt.apply_luma(*p, 0, 0);
11231            }
11232        }
11233        {
11234            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
11235            // ONE span for the sixteen rows (see `recon_p_inter_nores`).
11236            let (cw, base) = (self.cw, (mb_y * 16) * self.cw + mb_x * 16);
11237            let win = &mut self.rec_y[base..base + 15 * cw + 16];
11238            for dy in 0..16 {
11239                win[dy * cw..dy * cw + 16].copy_from_slice(&pred[dy * 16..dy * 16 + 16]);
11240            }
11241        }
11242        for c in 0..2 {
11243            let mut pc = [0u8; 64];
11244            let Some(rf0) = self.refs.first() else { return };
11245            let rc = if c == 0 {
11246                &*rf0.chroma_guard(0, rf0.ch)
11247            } else {
11248                &*rf0.chroma_guard(1, rf0.ch)
11249            };
11250            mc_chroma_padded(
11251                rc,
11252                rf0.cstride(),
11253                crate::CPAD,
11254                self.ccw,
11255                cch,
11256                mb_x * 8,
11257                mb_y * 8,
11258                8,
11259                8,
11260                mv.0,
11261                mv.1,
11262                &mut pc,
11263            );
11264            if let Some(wt) = &self.weights {
11265                for p in pc.iter_mut() {
11266                    *p = wt.apply_chroma(*p, 0, 0, c);
11267                }
11268            }
11269            let plane = if c == 0 {
11270                &mut self.rec_u
11271            } else {
11272                &mut self.rec_v
11273            };
11274            for dy in 0..8 {
11275                let d = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
11276                plane[d..d + 8].copy_from_slice(&pc[dy * 8..dy * 8 + 8]);
11277            }
11278        }
11279    }
11280
11281    fn add_inter_residual(
11282        &mut self,
11283        mb_x: usize,
11284        mb_y: usize,
11285        pred_y: &[u8; 256],
11286        c_pred: &[[u8; 64]; 2],
11287        luma_scan: Option<&[[i32; 16]; 16]>,
11288        // `Some` when the macroblock carries transform_size_8x8_flag: four 8x8
11289        // blocks in 8x8 scan order, replacing the sixteen 4x4 luma blocks.
11290        luma8: Option<&[[i32; 64]; 4]>,
11291        cdc: &[[i32; 4]; 2],
11292        cac: Option<&[[[i32; 16]; 4]; 2]>,
11293        cbp_chroma: u32,
11294        // Parsed totalCoeff per block, indexed exactly as the parse's `iz`:
11295        // [0..16] luma 4x4 z-order (for t8, the 8x8 count sits at `id8*4`),
11296        // [16..24] chroma AC as `16 + c*4 + id4`. The parser already counted
11297        // every significant coefficient; re-deriving the counts here scanned
11298        // 16-64 array elements per block (~400 loads/MB) for information the
11299        // caller was holding — the diagnosis's stage-boundary re-derivation tax.
11300        nnzs: &[u8; 24],
11301    ) {
11302        // A `None` field means the parse wrote nothing there; every read below
11303        // is guarded by a zero-count test, so the shared zero plane is
11304        // read-equivalent to the per-macroblock zeroed stack array it replaces.
11305        let luma_scan = luma_scan.unwrap_or(&ZERO_LUMA_SCAN);
11306        let cac = cac.unwrap_or(&ZERO_CAC);
11307        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecResidAdd);
11308        let qp = self.cur_qp;
11309        let qpc = self.chroma_qp_for(qp);
11310        if let Some(l8) = luma8 {
11311            // INTER 8x8 luma: same primitives the I_8x8 and CAVLC paths use.
11312            for b8 in 0..4usize {
11313                let (b8x, b8y) = (b8 % 2, b8 / 2);
11314                // Summed, not slot 0: with per-4x4 counts in the CAVLC case, slot 0
11315                // is only the first sub-block and can be 0 while the block is coded.
11316                let nnz: u32 = (0..4).map(|k| nnzs[b8 * 4 + k] as u32).sum();
11317                let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
11318                if nnz == 0 {
11319                    // Zero residual: recon == pred — same shortcut as the
11320                    // FrameDecoder twin.
11321                    edcstat::bump(&edcstat::T8_ZERO, 1);
11322                    for dy in 0..8 {
11323                        let d = (py + dy) * self.cw + px;
11324                        let po = (b8y * 8 + dy) * 16 + b8x * 8;
11325                        self.rec_y[d..d + 8].copy_from_slice(&pred_y[po..po + 8]);
11326                    }
11327                } else {
11328                    let raster = un_scan_8x8(&l8[b8]);
11329                    // list 1 = INTER 8x8 luma scaling list (0 is the intra one).
11330                    let res8 = self.inv_quant8(&raster, qp, 1);
11331                    let predb: [i32; 64] = core::array::from_fn(|i| {
11332                        pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
11333                    });
11334                    let recon = add_residual_8x8(&res8, &predb);
11335                    // Eight row copies. This wrote SIXTY-FOUR individually
11336                    // bounds-checked samples into the luma plane per coded 8x8
11337                    // block — the same shape whose fix carried `recon_i8_block`.
11338                    // Present in BOTH the main path and the EDC worker twin.
11339                    for dy in 0..8 {
11340                        let d = (py + dy) * self.cw + px;
11341                        self.rec_y[d..][..8].copy_from_slice(&recon[dy * 8..][..8]);
11342                    }
11343                }
11344            }
11345        }
11346        // RESIDUAL LADDER, then the SIMD IDCT+add kernel per parked block (SIMD census
11347        // 2026-09-05). The zero and DC-only ladders write the plane in the loop;
11348        // every other coded block parks its dequantised coefficients here and is
11349        // reconstructed by `reconstruct_4x4_into` = accel `idct4x4_add` (in-register
11350        // transposes, exact on i32). A lane-per-block BATCHED form was tried first
11351        // and lost on all-intra content: its scalar gathers cost more than the
11352        // butterflies they vectorised.
11353        let dq3 = self.dq_const(qp, 3);
11354        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
11355            if luma8.is_some() {
11356                break;
11357            }
11358            let nnz = nnzs[blk];
11359            let cw = self.cw;
11360            let p_off = (lby * 4) * 16 + lbx * 4;
11361            let r_off = (mb_y * 4 + lby) * 4 * cw + (mb_x * 4 + lbx) * 4;
11362            if nnz == 0 {
11363                // Zero residual → recon == prediction EXACTLY (the integer IDCT is
11364                // linear so zeros map to zeros, and pred is already 0..=255) — copy
11365                // the pred rows straight into the plane. On real (sparse-cbp)
11366                // streams this is MOST of the 4×4 blocks.
11367                for r in 0..4 {
11368                    self.rec_y[r_off + r * cw..r_off + r * cw + 4]
11369                        .copy_from_slice(&pred_y[p_off + r * 16..p_off + r * 16 + 4]);
11370                }
11371                continue;
11372            }
11373            // DC-ONLY: the sole significant coefficient is scan position 0 (the
11374            // zig-zag starts at DC, and un_scan keeps it at raster 0), so the
11375            // whole dequant + IDCT collapses to one multiply and a flat add.
11376            if nnz == 1 && luma_scan[blk][0] != 0 {
11377                let f = self.dequant_dc4(luma_scan[blk][0], qp, 3);
11378                reconstruct_4x4_dc_into(
11379                    (f + 32) >> 6,
11380                    pred_y,
11381                    p_off,
11382                    16,
11383                    &mut self.rec_y,
11384                    r_off,
11385                    cw,
11386                );
11387            } else {
11388                // Fused un-scan + dequant over ONLY the significant coefficients,
11389                // then IDCT + add + clip straight into the plane — no `qb`, no
11390                // `deq`-from-dense, no `predb` gather, no `s`, no `store` call.
11391                //
11392                // HYBRID: the scatter walks scan positions with a data-dependent
11393                // branch per slot, which beats the branchless dense 16-multiply
11394                // loop only while the block is SPARSE. The DC/zero fast paths
11395                // already removed the sparsest blocks, so the population here
11396                // skews denser — above ~6 coefficients the dense loop wins.
11397                // FUSED: un-scan + dequant + IDCT + add in one kernel call (dense-over-scatter round).
11398                reconstruct_4x4_scan_into::<false>(
11399                    &luma_scan[blk],
11400                    &dq3,
11401                    0,
11402                    pred_y,
11403                    p_off,
11404                    16,
11405                    &mut self.rec_y,
11406                    r_off,
11407                    cw,
11408                );
11409            }
11410        }
11411        let mut c_dc = [[0i32; 4]; 2];
11412        if cbp_chroma != 0 {
11413            for c in 0..2 {
11414                c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 4 + c);
11415            }
11416        }
11417        let dqc = [self.dq_const(qpc, 4), self.dq_const(qpc, 5)];
11418        let ccw = self.ccw;
11419        for c in 0..2 {
11420            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
11421                let mut ac_nz = false;
11422                if cbp_chroma == 2 {
11423                    let n = nnzs[(16 + c * 4 + by * 2 + bx).min(23)];
11424                    ac_nz = n != 0;
11425                }
11426                let dc = c_dc[c & 1][(by * 2 + bx) & 3];
11427                let p_off = (by * 4) * 8 + bx * 4;
11428                let r_off = (mb_y * 2 + by) * 4 * ccw + (mb_x * 2 + bx) * 4;
11429                let plane = if c == 0 {
11430                    &mut self.rec_u
11431                } else {
11432                    &mut self.rec_v
11433                };
11434                if dc == 0 && !ac_nz {
11435                    // Zero residual (no AC, zero DC) → recon == prediction exactly.
11436                    for r in 0..4 {
11437                        plane[r_off + r * ccw..r_off + r * ccw + 4]
11438                            .copy_from_slice(&c_pred[c][p_off + r * 8..p_off + r * 8 + 4]);
11439                    }
11440                    continue;
11441                }
11442                // DC-ONLY (no coded AC — covers every cbp_chroma==1 block and the
11443                // AC-empty blocks of cbp_chroma==2): the chroma DC arrives ALREADY
11444                // dequantized, so the residual is `(dc + 32) >> 6` flat.
11445                if !ac_nz {
11446                    reconstruct_4x4_dc_into(
11447                        (dc + 32) >> 6,
11448                        &c_pred[c],
11449                        p_off,
11450                        8,
11451                        plane,
11452                        r_off,
11453                        ccw,
11454                    );
11455                    continue;
11456                }
11457                // AC-only scan: index i is overall scan position i+1 (ac_shift=1).
11458                // Same sparse/dense hybrid as luma.
11459                reconstruct_4x4_scan_into::<true>(
11460                    &cac[c & 1][(by * 2 + bx) & 3],
11461                    &dqc[c & 1],
11462                    dc,
11463                    &c_pred[c],
11464                    p_off,
11465                    8,
11466                    plane,
11467                    r_off,
11468                    ccw,
11469                );
11470            }
11471        }
11472    }
11473
11474    fn filter_row(&mut self, r: usize) {
11475        // The precomputed consumer path reads ONLY `bs` + `t8x8` (+ the qp grid
11476        // passed as a parameter) — verified when the path landed (WHYS Part 15).
11477        let info = rusty_h264_common::deblock::BlockInfo {
11478            inter: &[],
11479            nnz: &[],
11480            mv: &[],
11481            ref_id: &[],
11482            mv1: &[],
11483            ref_id1: &[],
11484            w4: self.mb_w * 4,
11485            t8x8: &self.t8_grid,
11486            bs: &self.bs_store,
11487            poc0: &[],
11488            poc1: &[],
11489            kind: &[],
11490        };
11491        rusty_h264_common::deblock::filter_frame_rows_pre(
11492            &mut self.rec_y,
11493            &mut self.rec_u,
11494            &mut self.rec_v,
11495            self.mb_w,
11496            self.mb_h,
11497            r..r + 1,
11498            &self.qp_grid,
11499            self.chroma_qp_offset,
11500            self.db_oa,
11501            self.db_ob,
11502            &info,
11503        );
11504    }
11505
11506    fn save_bak(&mut self, r: usize) {
11507        let y0 = (r * 16 + 15) * self.cw;
11508        self.bak_y.copy_from_slice(&self.rec_y[y0..y0 + self.cw]);
11509        let c0 = (r * 8 + 7) * self.ccw;
11510        self.bak_u.copy_from_slice(&self.rec_u[c0..c0 + self.ccw]);
11511        self.bak_v.copy_from_slice(&self.rec_v[c0..c0 + self.ccw]);
11512    }
11513}
11514
11515/// The pixel worker: replays jobs in parse order, filters rows as their
11516/// messages arrive, and hands the whole context to the parse thread (and
11517/// back) around intra macroblocks. Returns the context at slice end.
11518/// E2 SEAM COUNTERS (D7). Deterministic — one run is the verdict, no pinning.
11519/// `RS_H264_EDC_STATS=1` prints at decode end. Counts, not clocks: the question
11520/// "does one intra macroblock drain the pipeline" is a COUNT question.
11521/// COPY CENSUS (feature = "profile"): bytes moved, per site class.
11522///
11523/// The static inventory says the decoder has 101 `copy_from_slice` sites. That
11524/// number ranks nothing -- `b_skip_slow`'s 384-byte hop and the nnz grid's 4-byte
11525/// one look identical in a grep, and one of them fires on the rare path. This
11526/// counts BYTES and CALLS per class on a real stream, so "the top of the list"
11527/// means the top by bytes rather than the top by line number.
11528///
11529/// Read the result against the output size, not in isolation: a decoder that
11530/// writes 384 bytes per macroblock and copies 384 is at 1.0 and irreducible;
11531/// one at 2.0 has a hop to find.
11532///
11533/// **And price the SITE, not the bytes.** A memcpy streams at ~10-20 GB/s; if
11534/// anything else fires on the same trigger -- a clear, a re-prime, a scatter --
11535/// the copy is the cheapest member of that bundle and the census is measuring
11536/// the wrong thing. Enumerate what else runs before concluding "not copy-bound".
11537#[allow(dead_code)]
11538pub(crate) mod cpystat {
11539    use core::sync::atomic::Ordering::Relaxed;
11540    use rusty_h264_common::atomic::AtomicU64;
11541
11542    /// One counter pair per class: (calls, bytes).
11543    macro_rules! classes {
11544        ($($id:ident => $label:expr),* $(,)?) => {
11545            $(pub static $id: [AtomicU64; 2] = [const { AtomicU64::new(0) }; 2];)*
11546            pub static ALL: &[(&str, &[AtomicU64; 2])] = &[$(($label, &$id)),*];
11547        };
11548    }
11549
11550    classes! {
11551        POOL_CLEAR => "per-picture grid re-arm (refill: clear + resize)",
11552        REF_BUILD  => "per-REFERENCE-picture motion clone (ref_poc expansion GONE)",
11553        PROGRESS_SLOT => "progress-slot motion grids, allocated then replaced",
11554        PRED_REC   => "pred->rec plane store (the MB's own pixels)",
11555        RECON_REC  => "recon->rec plane store (residual path)",
11556        MC_STAGE   => "MC staging buffer (inter prediction into a local)",
11557        NNZ_GRID   => "nnz grid maintenance",
11558        MV_GRID    => "motion-vector grid",
11559        PLANE_PAD  => "per-REFERENCE-picture padded plane copy (pad_plane_into)",
11560        PLANE_MISS => "padded-plane pool MISS: fresh alloc, zeroed then fully overwritten",
11561        BAK_ROW    => "deblock row backup (bak_y/u/v)",
11562        NEIGH      => "neighbour row/col caches (top_y_row etc)",
11563        EDC_ROW    => "EDC worker row messages",
11564        OTHER      => "everything else instrumented",
11565    }
11566
11567    #[inline(always)]
11568    pub fn note(c: &[AtomicU64; 2], bytes: usize) {
11569        #[cfg(feature = "profile")]
11570        {
11571            c[0].fetch_add(1, Relaxed);
11572            c[1].fetch_add(bytes as u64, Relaxed);
11573        }
11574        #[cfg(not(feature = "profile"))]
11575        let _ = (c, bytes);
11576    }
11577
11578    /// Per-CALLER-LINE byte totals, so "which refill dominates" is a lookup rather
11579    /// than an argument. A single funnel prices the whole re-arm; only the line
11580    /// buckets say WHICH grid is worth narrowing.
11581    #[cfg(feature = "profile")]
11582    pub static BY_LINE: std::sync::Mutex<Option<std::collections::BTreeMap<u32, (u64, u64)>>> =
11583        std::sync::Mutex::new(None);
11584
11585    #[cfg(feature = "profile")]
11586    pub fn note_line(line: u32, bytes: usize) {
11587        if let Ok(mut g) = BY_LINE.lock() {
11588            let m = g.get_or_insert_with(Default::default);
11589            let e = m.entry(line).or_insert((0, 0));
11590            e.0 += 1;
11591            e.1 += bytes as u64;
11592        }
11593    }
11594
11595    /// Output bytes produced, so the census can be read as copies-per-output-byte.
11596    pub static OUT_BYTES: AtomicU64 = AtomicU64::new(0);
11597
11598    pub fn report() {
11599        let out = OUT_BYTES.load(Relaxed).max(1);
11600        let tot: u64 = ALL.iter().map(|(_, c)| c[1].load(Relaxed)).sum();
11601        eprintln!(
11602            "CPYSTAT output={out} B  instrumented_copies={tot} B  ratio={:.3} B/out",
11603            tot as f64 / out as f64
11604        );
11605        #[cfg(feature = "profile")]
11606        if let Ok(g) = BY_LINE.lock() {
11607            if let Some(m) = g.as_ref() {
11608                let mut v: Vec<_> = m.iter().map(|(l, (n, b))| (*b, *n, *l)).collect();
11609                v.sort_by(|a, b| b.0.cmp(&a.0));
11610                eprintln!("  --- refill by caller line (top 14) ---");
11611                for (b, n, l) in v.iter().take(14) {
11612                    eprintln!(
11613                        "    mb16.rs:{:<6} calls={:<8} bytes={:<13} {:>8.1} KB/call",
11614                        l,
11615                        n,
11616                        b,
11617                        *b as f64 / *n as f64 / 1024.0
11618                    );
11619                }
11620            }
11621        }
11622        for (name, c) in ALL {
11623            let (n, b) = (c[0].load(Relaxed), c[1].load(Relaxed));
11624            if n == 0 {
11625                continue;
11626            }
11627            eprintln!(
11628                "  {:<46} calls={:<12} bytes={:<14} {:>6.3} B/out  {:>5.1} B/call",
11629                name,
11630                n,
11631                b,
11632                b as f64 / out as f64,
11633                b as f64 / n as f64
11634            );
11635        }
11636    }
11637}
11638
11639pub(crate) mod edcstat {
11640    use core::sync::atomic::Ordering::Relaxed;
11641    use rusty_h264_common::atomic::AtomicU64;
11642    pub static NEEDCTX: AtomicU64 = AtomicU64::new(0);
11643    pub static JOBS: AtomicU64 = AtomicU64::new(0);
11644    pub static ROWS: AtomicU64 = AtomicU64::new(0);
11645    pub static ROWBYTES: AtomicU64 = AtomicU64::new(0);
11646    pub static MBS: AtomicU64 = AtomicU64::new(0);
11647    /// Sparse-dequant routing histogram: [nnz class 1-2/3-4/5-6/7+][L class 1-4/5-8/9-12/13-16],
11648    /// L = highest coded scan position + 1 (the scatter loop's trip count).
11649    pub static DQ: [AtomicU64; 16] = [const { AtomicU64::new(0) }; 16];
11650    #[inline(always)]
11651    #[allow(dead_code)]
11652    pub fn dq_note(nnz: u8, scan: &[i32; 16]) {
11653        #[cfg(feature = "profile")]
11654        if on() {
11655            let l = 16 - scan.iter().rev().take_while(|&&v| v == 0).count();
11656            let nc = ((nnz.max(1) as usize - 1) / 2).min(3);
11657            let lc = ((l.max(1) - 1) / 4).min(3);
11658            DQ[nc * 4 + lc].fetch_add(1, Relaxed);
11659        }
11660        #[cfg(not(feature = "profile"))]
11661        let _ = (nnz, scan);
11662    }
11663    pub static J_INTER: AtomicU64 = AtomicU64::new(0);
11664    pub static DOUBLED: AtomicU64 = AtomicU64::new(0);
11665    pub static J_NORES_SENT: AtomicU64 = AtomicU64::new(0);
11666    pub static BATCHES: AtomicU64 = AtomicU64::new(0);
11667    pub static DISPATCH_ON: AtomicU64 = AtomicU64::new(0);
11668    pub static DISPATCH_SEEN: AtomicU64 = AtomicU64::new(0);
11669    /// mb_skip_run band path (big-oppy-decoder §6 KEY glue): P_Skip MBs
11670    /// reconstructed by run-coalesced band copies, and the runs themselves.
11671    /// DETERMINISTIC WIN counters — each banded MB removed 1 luma MC call,
11672    /// 2 chroma MC calls and the 256+128-byte pred staging round-trip.
11673    pub static SKIPBAND_MBS: AtomicU64 = AtomicU64::new(0);
11674    pub static SKIPBAND_RUNS: AtomicU64 = AtomicU64::new(0);
11675    pub static SKIP_SINGLES: AtomicU64 = AtomicU64::new(0);
11676    // MEASUREMENT counters sizing the next skip-band extensions (no behavior).
11677    // P side: singles that sit in a same-row run of >=2 EQUAL nonzero MVs,
11678    // split by full-pel (band-copy-with-offset eligible) vs fractional.
11679    pub static PEQ_FP: AtomicU64 = AtomicU64::new(0);
11680    pub static PEQ_FRAC: AtomicU64 = AtomicU64::new(0);
11681    // B side: full-16x16 spatial-direct calls (B_Skip + direct-16), how many
11682    // collapse to ONE uniform rect, and of those the zero-motion bi/uni and
11683    // full-pel populations + run adjacency (previous MB same params, same row).
11684    pub static BSK_FULLMB: AtomicU64 = AtomicU64::new(0);
11685    pub static BSK_1RECT: AtomicU64 = AtomicU64::new(0);
11686    pub static BSK_ZBI: AtomicU64 = AtomicU64::new(0);
11687    pub static BSK_ZUNI: AtomicU64 = AtomicU64::new(0);
11688    pub static BSK_FP: AtomicU64 = AtomicU64::new(0);
11689    pub static BSK_RUNCONT: AtomicU64 = AtomicU64::new(0);
11690    /// DETERMINISTIC WIN counter — B_Skip MBs taken by the zero-bi fast path.
11691    pub static BSKB_FAST: AtomicU64 = AtomicU64::new(0);
11692    /// P_Skip singles taken by the full-pel direct-copy path (both planes /
11693    /// luma only, chroma still interpolating at mv%8!=0).
11694    pub static SKIP_FP_FULL: AtomicU64 = AtomicU64::new(0);
11695    pub static SKIP_FP_LUMA: AtomicU64 = AtomicU64::new(0);
11696    /// B_Skip full-pel nonzero-MV MBs taken by the offset copy/avg path.
11697    pub static BSKB_FP: AtomicU64 = AtomicU64::new(0);
11698    /// P_Skip parse side: MBs whose skip MV was FORCED (0,0) by the run
11699    /// theorem (left = just-committed (0,0) skip, or out-of-slice) vs MBs
11700    /// that ran the 3-neighbor gather + rule.
11701    pub static SKIPMV_FORCED: AtomicU64 = AtomicU64::new(0);
11702    pub static SKIPMV_DERIVED: AtomicU64 = AtomicU64::new(0);
11703    /// B_Skips whose zero-bi derivation was FORCED by the known-zero bitmap
11704    /// (left+top+topright all recorded ref0/(0,0)-both-lists) — the 6-gather
11705    /// b_direct_nbrs walk skipped entirely.
11706    pub static BSKB_FORCED: AtomicU64 = AtomicU64::new(0);
11707    /// Zero-bi fast B_Skip grid commits batched into row spans: MBs covered
11708    /// and spans flushed (fills of 4N replace N MBs x 28 per-MB fills).
11709    pub static BZ_SPAN_MBS: AtomicU64 = AtomicU64::new(0);
11710    pub static BZ_SPANS: AtomicU64 = AtomicU64::new(0);
11711    /// P_Skip (0,0) grid-commit spans (parse-side mirror of BZ_*).
11712    pub static PZ_SPAN_MBS: AtomicU64 = AtomicU64::new(0);
11713    pub static PZ_SPANS: AtomicU64 = AtomicU64::new(0);
11714    /// Census: b_mc bi-pred regions where BOTH MVs are full-pel (fusable to a
11715    /// direct offset average — sizing only, no behavior).
11716    pub static BMC_BI_FP: AtomicU64 = AtomicU64::new(0);
11717    /// weight_partition passes skipped because the whole list-0 table is
11718    /// identity (384 apply ops avoided per full-MB pass).
11719    pub static WP_SKIPPED: AtomicU64 = AtomicU64::new(0);
11720    /// Intra I_4x4 luma residual dispatch (the inter ladder, ported): how many
11721    /// blocks took the zero / DC-only / sparse-scatter / dense arms.
11722    pub static I4_ZERO: AtomicU64 = AtomicU64::new(0);
11723    pub static I4_DC: AtomicU64 = AtomicU64::new(0);
11724    pub static I4_SPARSE: AtomicU64 = AtomicU64::new(0);
11725    pub static I4_DENSE: AtomicU64 = AtomicU64::new(0);
11726    /// 8x8 residual blocks (intra + inter t8) whose residual is entirely zero:
11727    /// recon == pred, the 64-px widen + add + clip skipped.
11728    pub static T8_ZERO: AtomicU64 = AtomicU64::new(0);
11729    /// I_16x16 4x4 sub-blocks with zero AC: the residual is the (already
11730    /// Hadamard-transformed) DC alone, so the dense dequant + IDCT collapse
11731    /// to one flat add.
11732    pub static I16_DCONLY: AtomicU64 = AtomicU64::new(0);
11733    pub static J_INTER_NORES: AtomicU64 = AtomicU64::new(0);
11734    // ---- deb:derive census (derive_bs_row) — sizing the bS derivation arms.
11735    /// Rows derived, and macroblocks derived across them.
11736    pub static DBS_ROWS: AtomicU64 = AtomicU64::new(0);
11737    pub static DBS_MB: AtomicU64 = AtomicU64::new(0);
11738    /// Arm split: Intra kind-arm / Skip|InterUniform kind-arm (only under
11739    /// RS_H264_KIND_LOADS=1) / the packed `derive_mb_records` default.
11740    pub static DBS_INTRA: AtomicU64 = AtomicU64::new(0);
11741    pub static DBS_KINDARM: AtomicU64 = AtomicU64::new(0);
11742    /// Macroblocks whose kind is Skip|InterUniform: the population that used
11743    /// to evaluate the `kind_loads()` OnceLock guard once EACH.
11744    pub static DBS_KINDGUARD: AtomicU64 = AtomicU64::new(0);
11745    pub static DBS_PACKED: AtomicU64 = AtomicU64::new(0);
11746    /// Of the packed arm: macroblocks whose derivation returned `flat_inter`
11747    /// (internal strengths 0 by construction — the early-return class).
11748    pub static DBS_FLAT: AtomicU64 = AtomicU64::new(0);
11749    /// Macroblocks whose FINAL stored MbBs is all-zero: the population the
11750    /// consumer's two-16-byte-compare early-out serves, and the denominator
11751    /// for the per-MB 128-byte bs_v/bs_h zero-init that precedes it.
11752    pub static DBS_ALLZERO: AtomicU64 = AtomicU64::new(0);
11753    /// nnz_dbr maintenance: rows copied wholesale vs rows that actually
11754    /// carried a transform-8x8 macroblock needing the 8x8 OR fixup.
11755    pub static DBS_NNZ_ROWCOPY: AtomicU64 = AtomicU64::new(0);
11756    pub static DBS_T8ROW: AtomicU64 = AtomicU64::new(0);
11757    pub static DBS_T8MB: AtomicU64 = AtomicU64::new(0);
11758    /// Rows that ran the disable_deblocking_filter_idc==2 crossing-edge pass.
11759    pub static DBS_IDC2ROW: AtomicU64 = AtomicU64::new(0);
11760    #[inline]
11761    pub fn bump(c: &AtomicU64, n: u64) {
11762        #[cfg(feature = "profile")]
11763        if on() {
11764            c.fetch_add(n, Relaxed);
11765        }
11766        #[cfg(not(feature = "profile"))]
11767        let _ = (c, n);
11768    }
11769    /// Off by default, yet read on EVERY bump — up to four times per B_Skip.
11770    /// `OnceLock::get_or_init` is an acquire load plus an initialised-state
11771    /// branch behind a non-inlined call; this is the relaxed AtomicU8 tri-state
11772    /// the other knobs in this file already use, and it inlines to one load.
11773    #[inline]
11774    pub fn on() -> bool {
11775        #[cfg(not(feature = "profile"))]
11776        {
11777            false
11778        }
11779        #[cfg(feature = "profile")]
11780        {
11781            use core::sync::atomic::AtomicU8;
11782            static V: AtomicU8 = AtomicU8::new(0);
11783            match V.load(Relaxed) {
11784                1 => true,
11785                2 => false,
11786                _ => {
11787                    let b = rusty_h264_common::knob("RS_H264_EDC_STATS").is_some();
11788                    V.store(if b { 1 } else { 2 }, Relaxed);
11789                    b
11790                }
11791            }
11792        }
11793    }
11794    pub fn report() {
11795        if !on() {
11796            return;
11797        }
11798        super::cpystat::report();
11799        {
11800            // Routing model (census, 2026-09-05): scatter = 37 + 6*L + 9*nnz instrs;
11801            // dense = unscan 32 + dequantize ~63 (scalar) / ~28 (AVX2 twin).
11802            let (nm, lm) = ([1.5f64, 3.5, 5.5, 9.0], [2.5f64, 6.5, 10.5, 14.5]);
11803            let (mut tot, mut sc, mut d95, mut d60, mut best) =
11804                (0u64, 0.0f64, 0.0f64, 0.0f64, 0.0f64);
11805            eprintln!(
11806                "DQROUTE sparse-arm blocks by [nnz class][L class] (L = last coded position + 1):"
11807            );
11808            for nc in 0..4 {
11809                // `alloc::` spelled out: `Vec` is not in the no_std prelude.
11810                let row: alloc::vec::Vec<u64> =
11811                    (0..4).map(|lc| DQ[nc * 4 + lc].load(Relaxed)).collect();
11812                eprintln!(
11813                    "  nnz {:<5} L1-4={:>9} L5-8={:>9} L9-12={:>9} L13-16={:>9}",
11814                    ["1-2", "3-4", "5-6", "7+"][nc],
11815                    row[0],
11816                    row[1],
11817                    row[2],
11818                    row[3]
11819                );
11820                for lc in 0..4 {
11821                    let k = row[lc] as f64;
11822                    let s = 37.0 + 6.0 * lm[lc] + 9.0 * nm[nc];
11823                    tot += row[lc];
11824                    sc += k * s;
11825                    d95 += k * 95.0;
11826                    d60 += k * 60.0;
11827                    best += k * s.min(60.0);
11828                }
11829            }
11830            if tot > 0 {
11831                eprintln!("  modelled instrs: scatter(as routed)={:.0}  all-dense-scalar={:.0}  all-dense-avx2={:.0}  per-bin best(scatter|avx2)={:.0}  blocks={}", sc, d95, d60, best, tot);
11832            }
11833        }
11834        eprintln!(
11835            "EDCDISPATCH threaded_slices={} eligible_slices={}",
11836            DISPATCH_ON.load(Relaxed),
11837            DISPATCH_SEEN.load(Relaxed)
11838        );
11839        eprintln!(
11840            "EDCSIZE EdcMsg={} EdcJob={} PInterJob={} BJob={}",
11841            core::mem::size_of::<super::EdcMsg>(),
11842            core::mem::size_of::<super::EdcJob>(),
11843            core::mem::size_of::<super::PInterJob>(),
11844            core::mem::size_of::<super::BJob>(),
11845        );
11846        let (n, j, r, b, m) = (
11847            NEEDCTX.load(Relaxed),
11848            JOBS.load(Relaxed),
11849            ROWS.load(Relaxed),
11850            ROWBYTES.load(Relaxed),
11851            MBS.load(Relaxed),
11852        );
11853        eprintln!(
11854            "EDCSTAT needctx={n} jobs={j} rows={r} rowbytes={b} mbs={m} batches={} jobs_per_batch={:.1} needctx_per_1k_mb={:.1} jobs_per_needctx={:.1}",
11855            BATCHES.load(Relaxed),
11856            j as f64 / BATCHES.load(Relaxed).max(1) as f64,
11857            1000.0 * n as f64 / m.max(1) as f64,
11858            j as f64 / n.max(1) as f64
11859        );
11860        eprintln!(
11861            "SKIPRUN band_mbs={} band_runs={} mbs_per_run={:.1} singles={} banded_pct={:.1}",
11862            SKIPBAND_MBS.load(Relaxed),
11863            SKIPBAND_RUNS.load(Relaxed),
11864            SKIPBAND_MBS.load(Relaxed) as f64 / SKIPBAND_RUNS.load(Relaxed).max(1) as f64,
11865            SKIP_SINGLES.load(Relaxed),
11866            100.0 * SKIPBAND_MBS.load(Relaxed) as f64
11867                / (SKIPBAND_MBS.load(Relaxed) + SKIP_SINGLES.load(Relaxed)).max(1) as f64,
11868        );
11869        eprintln!(
11870            "SKIPNEXT peq_fp={} peq_frac={} bsk_fullmb={} bsk_1rect={} bsk_zbi={} bsk_zuni={} bsk_fp={} bsk_runcont={} bskb_fast={} skip_fp_full={} skip_fp_luma={} bskb_fp={} skipmv_forced={} skipmv_derived={} bskb_forced={} bz_spans={} bz_span_mbs={} pz_spans={} pz_span_mbs={} bmc_bi_fp={} wp_skipped={} i4_zero={} i4_dc={} i4_sparse={} i4_dense={} t8_zero={} i16_dconly={}",
11871            PEQ_FP.load(Relaxed), PEQ_FRAC.load(Relaxed),
11872            BSK_FULLMB.load(Relaxed), BSK_1RECT.load(Relaxed),
11873            BSK_ZBI.load(Relaxed), BSK_ZUNI.load(Relaxed),
11874            BSK_FP.load(Relaxed), BSK_RUNCONT.load(Relaxed),
11875            BSKB_FAST.load(Relaxed),
11876            SKIP_FP_FULL.load(Relaxed), SKIP_FP_LUMA.load(Relaxed),
11877            BSKB_FP.load(Relaxed),
11878            SKIPMV_FORCED.load(Relaxed), SKIPMV_DERIVED.load(Relaxed),
11879            BSKB_FORCED.load(Relaxed),
11880            BZ_SPANS.load(Relaxed), BZ_SPAN_MBS.load(Relaxed),
11881            PZ_SPANS.load(Relaxed), PZ_SPAN_MBS.load(Relaxed),
11882            BMC_BI_FP.load(Relaxed),
11883            WP_SKIPPED.load(Relaxed),
11884            I4_ZERO.load(Relaxed), I4_DC.load(Relaxed),
11885            I4_SPARSE.load(Relaxed), I4_DENSE.load(Relaxed),
11886            T8_ZERO.load(Relaxed), I16_DCONLY.load(Relaxed),
11887        );
11888        let dmb = DBS_MB.load(Relaxed).max(1);
11889        eprintln!(
11890            "DBSDERIVE rows={} mb={} intra={} kindarm={} kindguard={} packed={} flat={} ({:.1}%) allzero={} ({:.1}%) nnz_rowcopy={} t8row={} t8mb={} idc2row={}",
11891            DBS_ROWS.load(Relaxed), DBS_MB.load(Relaxed),
11892            DBS_INTRA.load(Relaxed), DBS_KINDARM.load(Relaxed),
11893            DBS_KINDGUARD.load(Relaxed), DBS_PACKED.load(Relaxed),
11894            DBS_FLAT.load(Relaxed), 100.0 * DBS_FLAT.load(Relaxed) as f64 / dmb as f64,
11895            DBS_ALLZERO.load(Relaxed), 100.0 * DBS_ALLZERO.load(Relaxed) as f64 / dmb as f64,
11896            DBS_NNZ_ROWCOPY.load(Relaxed), DBS_T8ROW.load(Relaxed),
11897            DBS_T8MB.load(Relaxed), DBS_IDC2ROW.load(Relaxed),
11898        );
11899        let (ji, jn) = (J_INTER.load(Relaxed), J_INTER_NORES.load(Relaxed));
11900        eprintln!(
11901            "EDCMIX doubled={} nores_sent={} inter={ji} inter_no_residual={jn} ({:.1}% of inter) wasted_bytes={:.1} MB of {:.1} MB total inter payload",
11902            DOUBLED.load(Relaxed),
11903            J_NORES_SENT.load(Relaxed),
11904            100.0 * jn as f64 / ji.max(1) as f64,
11905            (jn * 2784) as f64 / 1.048576e6,
11906            (ji * 2784) as f64 / 1.048576e6,
11907        );
11908    }
11909}
11910
11911#[cfg(feature = "std")]
11912fn edc_worker(
11913    mut ctx: PixelCtx,
11914    rx: crate::sync::mpsc::Receiver<EdcMsg>,
11915    ctx_tx: crate::sync::mpsc::Sender<PixelCtx>,
11916    back_rx: crate::sync::mpsc::Receiver<PixelCtx>,
11917) -> Option<PixelCtx> {
11918    while let Ok(msg) = rx.recv() {
11919        match msg {
11920            EdcMsg::Batch(jobs) => {
11921                for j in jobs {
11922                    match j {
11923                        EdcJob::Skip { mbx, mby, mv } => ctx.recon_p_skip(mbx, mby, mv),
11924                        EdcJob::Inter(j) => ctx.recon_p_inter(&j),
11925                        EdcJob::InterNoRes(j) => ctx.recon_p_inter_nores(&j),
11926                        EdcJob::B(j) => ctx.recon_b(&j),
11927                        EdcJob::BSkip { mbx, mby, regions } => ctx.recon_b_skip(mbx, mby, &regions),
11928                    }
11929                }
11930            }
11931            EdcMsg::Job(EdcJob::Skip { mbx, mby, mv }) => ctx.recon_p_skip(mbx, mby, mv),
11932            EdcMsg::Job(EdcJob::Inter(j)) => ctx.recon_p_inter(&j),
11933            EdcMsg::Job(EdcJob::InterNoRes(j)) => ctx.recon_p_inter_nores(&j),
11934            EdcMsg::Job(EdcJob::B(j)) => ctx.recon_b(&j),
11935            EdcMsg::Job(EdcJob::BSkip { mbx, mby, regions }) => {
11936                ctx.recon_b_skip(mbx, mby, &regions)
11937            }
11938            EdcMsg::Row { r, bs, qp, t8 } => {
11939                let (w, base) = (ctx.mb_w, r * ctx.mb_w);
11940                ctx.bs_store[base..base + w].copy_from_slice(&bs);
11941                ctx.qp_grid[base..base + w].copy_from_slice(&qp);
11942                ctx.t8_grid[base..base + w].copy_from_slice(&t8);
11943                if ctx.db_ena {
11944                    ctx.save_bak(r);
11945                    ctx.filter_row(r);
11946                    ctx.flt_rows = r + 1;
11947                    ctx.publish_progress_rows();
11948                }
11949            }
11950            EdcMsg::NeedCtx => {
11951                // Inside the worker thread, so there is no caller to return an
11952                // error to and a panic here would abort a thread that did
11953                // nothing wrong. If the parse side has gone there is simply no
11954                // one left to serve: hand the context back where we still hold
11955                // it (`SendError` returns the value) and end the thread.
11956                match ctx_tx.send(ctx) {
11957                    Err(e) => return Some(e.0),
11958                    Ok(()) => match back_rx.recv() {
11959                        Ok(c) => ctx = c,
11960                        // The parse side took the context and died with it.
11961                        Err(_) => return None,
11962                    },
11963                }
11964            }
11965        }
11966    }
11967    Some(ctx)
11968}
11969
11970/// One motion-compensation region of a B macroblock, recorded at parse time
11971/// (E3). Weights are the RESOLVED implicit pair — computing them needs the
11972/// ref lists' POCs, which are parse-side state.
11973pub(crate) struct BRegion {
11974    px: usize,
11975    py: usize,
11976    rw: usize,
11977    rh: usize,
11978    refi0: i32,
11979    refi1: i32,
11980    mv0: (i32, i32),
11981    mv1: (i32, i32),
11982    w: Option<(i32, i32)>,
11983}
11984
11985/// A B macroblock's deferred pixel work: replay the regions into fresh
11986/// prediction buffers, then either copy them out (skip/direct, no residual)
11987/// or run the residual add.
11988pub(crate) struct BJob {
11989    mbx: usize,
11990    mby: usize,
11991    qp: u8,
11992    cbp_chroma: u32,
11993    skip: bool,
11994    regions: Vec<BRegion>,
11995    /// `None` when no 4x4 luma block was coded — t8 macroblocks included,
11996    /// since those carry `luma8` instead.
11997    luma_scan: Option<[[i32; 16]; 16]>,
11998    /// `Some` only under transform_size_8x8_flag.
11999    luma8: Option<[[i32; 64]; 4]>,
12000    cdc: [[i32; 4]; 2],
12001    /// `None` unless cbp_chroma == 2 (chroma AC coded).
12002    cac: Option<[[[i32; 16]; 4]; 2]>,
12003    nnzs: [u8; 24],
12004}
12005
12006impl PixelCtx {
12007    fn b_mc(
12008        &self,
12009        mb_x: usize,
12010        mb_y: usize,
12011        px: usize,
12012        py: usize,
12013        rw: usize,
12014        rh: usize,
12015        refi0: i32,
12016        mv0: (i32, i32),
12017        refi1: i32,
12018        mv1: (i32, i32),
12019        pred_y: &mut [u8; 256],
12020        c_pred: &mut [[u8; 64]; 2],
12021        wparam: Option<(i32, i32)>,
12022    ) {
12023        let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBMc);
12024        // Malformed-stream armor, mirroring the P path: now that B slices actually
12025        // PARSE ref_idx (they used to be hardcoded to 0), a mutated stream can hand
12026        // us an index past the end of either list. Clamp rather than panic — the
12027        // crate is `forbid(unsafe_code)` and fuzz-gated to never panic, and a
12028        // wrong picture on garbage input carries no conformance duty.
12029        let refi0 = if refi0 >= 0 {
12030            (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32
12031        } else {
12032            -1
12033        };
12034        let refi1 = if refi1 >= 0 {
12035            (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32
12036        } else {
12037            -1
12038        };
12039        if (refi0 >= 0 && self.refs.is_empty()) || (refi1 >= 0 && self.refs1.is_empty()) {
12040            return;
12041        }
12042        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
12043        // E3: implicit weights are PARSE-side (they read the ref lists' POCs);
12044        // the region carries the resolved pair.
12045        let weights = wparam;
12046        // Bi-prediction blend: the weights decision is LOOP-INVARIANT, so every
12047        // blend site below matches on `weights` ONCE and runs a branch-free
12048        // pixel loop — the unweighted `(p+q+1)>>1` average then autovectorizes
12049        // (the per-pixel closure this replaces hid the invariant behind a
12050        // capture, and its chroma form was a &dyn call PER PIXEL).
12051        // FULL-WIDTH regions (px == 0, rw == 16 — every 16×16/16×8 partition and
12052        // most direct regions) occupy contiguous rows of `pred_y`, so MC writes
12053        // the destination DIRECTLY: uni-pred needs no staging at all, bi-pred
12054        // stages only the second list and blends in place. The staging arrays
12055        // (512 B zeroed per call before this) now exist only on the branches
12056        // that read them. Same fusion as the P path's mc_rect (WHYS Part 8).
12057        let full = px == 0 && rw == 16;
12058        let _gl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBLuma);
12059        // One scratch borrow for the whole region — both bi-pred passes included.
12060        // The closure yields whether the arm already ran the chroma half (the
12061        // bi-pred full-width arm does, to keep its staging alive) — a plain
12062        // `return` inside would exit the CLOSURE only and chroma would run twice.
12063        let chroma_done =
12064            rusty_h264_common::inter::with_mc_scratch(|scr| match (refi0 >= 0, refi1 >= 0, full) {
12065                (true, false, true) => {
12066                    let Some(rf) = self.refs.get(refi0 as usize) else {
12067                        return false;
12068                    };
12069                    rusty_h264_common::inter::mc_luma_padded_pre(
12070                        scr,
12071                        &rf.luma_guard(rf.ch),
12072                        rf.lstride(),
12073                        crate::LPAD,
12074                        self.cw,
12075                        ch,
12076                        mb_x * 16,
12077                        mb_y * 16 + py,
12078                        rw,
12079                        rh,
12080                        mv0.0,
12081                        mv0.1,
12082                        &mut pred_y[py * 16..py * 16 + rw * rh],
12083                    );
12084                    false
12085                }
12086                (false, true, true) => {
12087                    let Some(rf) = self.refs1.get(refi1 as usize) else {
12088                        return false;
12089                    };
12090                    rusty_h264_common::inter::mc_luma_padded_pre(
12091                        scr,
12092                        &rf.luma_guard(rf.ch),
12093                        rf.lstride(),
12094                        crate::LPAD,
12095                        self.cw,
12096                        ch,
12097                        mb_x * 16,
12098                        mb_y * 16 + py,
12099                        rw,
12100                        rh,
12101                        mv1.0,
12102                        mv1.1,
12103                        &mut pred_y[py * 16..py * 16 + rw * rh],
12104                    );
12105                    false
12106                }
12107                (true, true, true) => {
12108                    let Some(rf) = self.refs.get(refi0 as usize) else {
12109                        return false;
12110                    };
12111                    rusty_h264_common::inter::mc_luma_padded_pre(
12112                        scr,
12113                        &rf.luma_guard(rf.ch),
12114                        rf.lstride(),
12115                        crate::LPAD,
12116                        self.cw,
12117                        ch,
12118                        mb_x * 16,
12119                        mb_y * 16 + py,
12120                        rw,
12121                        rh,
12122                        mv0.0,
12123                        mv0.1,
12124                        &mut pred_y[py * 16..py * 16 + rw * rh],
12125                    );
12126                    let mut b = [0u8; 256];
12127                    let Some(rf) = self.refs1.get(refi1 as usize) else {
12128                        return false;
12129                    };
12130                    rusty_h264_common::inter::mc_luma_padded_pre(
12131                        scr,
12132                        &rf.luma_guard(rf.ch),
12133                        rf.lstride(),
12134                        crate::LPAD,
12135                        self.cw,
12136                        ch,
12137                        mb_x * 16,
12138                        mb_y * 16 + py,
12139                        rw,
12140                        rh,
12141                        mv1.0,
12142                        mv1.1,
12143                        &mut b[..rw * rh],
12144                    );
12145                    drop(_gl);
12146                    let _gbl =
12147                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
12148                    // SLICE-then-zip: proving the bounds ONCE lets rustc emit the whole
12149                    // 256-byte average as 8 straight-line vpavgb ops (verified in
12150                    // isolation, x86-64-v3); the indexed form kept a per-iteration
12151                    // bounds check and a loop. A hand AVX2 kernel is refuted — the
12152                    // compiler already emits the ideal instruction.
12153                    let dst = &mut pred_y[py * 16..py * 16 + rw * rh];
12154                    match weights {
12155                        None => {
12156                            for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
12157                                *d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
12158                            }
12159                        }
12160                        Some((w0, w1)) => {
12161                            for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
12162                                *d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255)
12163                                    as u8;
12164                            }
12165                        }
12166                    }
12167                    let _gc =
12168                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
12169                    self.b_mc_chroma(
12170                        mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch,
12171                    );
12172                    true
12173                }
12174                _ => {
12175                    // Narrow region — rows are strided in `pred_y`; stage and copy.
12176                    let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
12177                    if refi0 >= 0 {
12178                        let Some(rf) = self.refs.get(refi0 as usize) else {
12179                            return false;
12180                        };
12181                        rusty_h264_common::inter::mc_luma_padded_pre(
12182                            scr,
12183                            &rf.luma_guard(rf.ch),
12184                            rf.lstride(),
12185                            crate::LPAD,
12186                            self.cw,
12187                            ch,
12188                            mb_x * 16 + px,
12189                            mb_y * 16 + py,
12190                            rw,
12191                            rh,
12192                            mv0.0,
12193                            mv0.1,
12194                            &mut a[..rw * rh],
12195                        );
12196                    }
12197                    if refi1 >= 0 {
12198                        let Some(rf) = self.refs1.get(refi1 as usize) else {
12199                            return false;
12200                        };
12201                        rusty_h264_common::inter::mc_luma_padded_pre(
12202                            scr,
12203                            &rf.luma_guard(rf.ch),
12204                            rf.lstride(),
12205                            crate::LPAD,
12206                            self.cw,
12207                            ch,
12208                            mb_x * 16 + px,
12209                            mb_y * 16 + py,
12210                            rw,
12211                            rh,
12212                            mv1.0,
12213                            mv1.1,
12214                            &mut b[..rw * rh],
12215                        );
12216                    }
12217                    drop(_gl);
12218                    let _gbl =
12219                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
12220                    match (refi0 >= 0, refi1 >= 0) {
12221                        (true, true) => {
12222                            for dy in 0..rh {
12223                                let (ar, br) =
12224                                    (&a[dy * rw..dy * rw + rw], &b[dy * rw..dy * rw + rw]);
12225                                let base = (py + dy) * 16 + px;
12226                                let dst = &mut pred_y[base..base + rw];
12227                                match weights {
12228                                    None => {
12229                                        for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
12230                                            *d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
12231                                        }
12232                                    }
12233                                    Some((w0, w1)) => {
12234                                        for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
12235                                            *d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6)
12236                                                .clamp(0, 255)
12237                                                as u8;
12238                                        }
12239                                    }
12240                                }
12241                            }
12242                        }
12243                        (true, false) => {
12244                            for dy in 0..rh {
12245                                let d = (py + dy) * 16 + px;
12246                                pred_y[d..d + rw].copy_from_slice(&a[dy * rw..dy * rw + rw]);
12247                            }
12248                        }
12249                        _ => {
12250                            for dy in 0..rh {
12251                                let d = (py + dy) * 16 + px;
12252                                pred_y[d..d + rw].copy_from_slice(&b[dy * rw..dy * rw + rw]);
12253                            }
12254                        }
12255                    }
12256                    false
12257                }
12258            });
12259        if chroma_done {
12260            return;
12261        }
12262        let _gc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
12263        self.b_mc_chroma(
12264            mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch,
12265        );
12266    }
12267
12268    fn b_mc_chroma(
12269        &self,
12270        mb_x: usize,
12271        mb_y: usize,
12272        px: usize,
12273        py: usize,
12274        rw: usize,
12275        rh: usize,
12276        refi0: i32,
12277        mv0: (i32, i32),
12278        refi1: i32,
12279        mv1: (i32, i32),
12280        c_pred: &mut [[u8; 64]; 2],
12281        weights: Option<(i32, i32)>,
12282        cch: usize,
12283    ) {
12284        let (crx, cry, crw, crh) = (px / 2, py / 2, rw / 2, rh / 2);
12285        let full = crx == 0 && crw == 8;
12286        for c in 0..2 {
12287            match (refi0 >= 0, refi1 >= 0, full) {
12288                (true, false, true) => {
12289                    let Some(rf) = self.refs.get(refi0 as usize) else {
12290                        return;
12291                    };
12292                    let pl = if c == 0 {
12293                        &*rf.chroma_guard(0, rf.ch)
12294                    } else {
12295                        &*rf.chroma_guard(1, rf.ch)
12296                    };
12297                    mc_chroma_padded(
12298                        pl,
12299                        rf.cstride(),
12300                        crate::CPAD,
12301                        self.ccw,
12302                        cch,
12303                        mb_x * 8,
12304                        mb_y * 8 + cry,
12305                        crw,
12306                        crh,
12307                        mv0.0,
12308                        mv0.1,
12309                        &mut c_pred[c][cry * 8..cry * 8 + crw * crh],
12310                    );
12311                }
12312                (false, true, true) => {
12313                    let Some(rf) = self.refs1.get(refi1 as usize) else {
12314                        return;
12315                    };
12316                    let pl = if c == 0 {
12317                        &*rf.chroma_guard(0, rf.ch)
12318                    } else {
12319                        &*rf.chroma_guard(1, rf.ch)
12320                    };
12321                    mc_chroma_padded(
12322                        pl,
12323                        rf.cstride(),
12324                        crate::CPAD,
12325                        self.ccw,
12326                        cch,
12327                        mb_x * 8,
12328                        mb_y * 8 + cry,
12329                        crw,
12330                        crh,
12331                        mv1.0,
12332                        mv1.1,
12333                        &mut c_pred[c][cry * 8..cry * 8 + crw * crh],
12334                    );
12335                }
12336                (true, true, true) => {
12337                    let Some(rf) = self.refs.get(refi0 as usize) else {
12338                        return;
12339                    };
12340                    let pl = if c == 0 {
12341                        &*rf.chroma_guard(0, rf.ch)
12342                    } else {
12343                        &*rf.chroma_guard(1, rf.ch)
12344                    };
12345                    mc_chroma_padded(
12346                        pl,
12347                        rf.cstride(),
12348                        crate::CPAD,
12349                        self.ccw,
12350                        cch,
12351                        mb_x * 8,
12352                        mb_y * 8 + cry,
12353                        crw,
12354                        crh,
12355                        mv0.0,
12356                        mv0.1,
12357                        &mut c_pred[c][cry * 8..cry * 8 + crw * crh],
12358                    );
12359                    let mut cb = [0u8; 64];
12360                    let Some(rf) = self.refs1.get(refi1 as usize) else {
12361                        return;
12362                    };
12363                    let pl = if c == 0 {
12364                        &*rf.chroma_guard(0, rf.ch)
12365                    } else {
12366                        &*rf.chroma_guard(1, rf.ch)
12367                    };
12368                    mc_chroma_padded(
12369                        pl,
12370                        rf.cstride(),
12371                        crate::CPAD,
12372                        self.ccw,
12373                        cch,
12374                        mb_x * 8,
12375                        mb_y * 8 + cry,
12376                        crw,
12377                        crh,
12378                        mv1.0,
12379                        mv1.1,
12380                        &mut cb[..crw * crh],
12381                    );
12382                    let dst = &mut c_pred[c][cry * 8..cry * 8 + crw * crh];
12383                    match weights {
12384                        None => {
12385                            for (d, s) in dst.iter_mut().zip(&cb[..crw * crh]) {
12386                                *d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
12387                            }
12388                        }
12389                        Some((w0, w1)) => {
12390                            for (d, s) in dst.iter_mut().zip(&cb[..crw * crh]) {
12391                                *d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255)
12392                                    as u8;
12393                            }
12394                        }
12395                    }
12396                }
12397                _ => {
12398                    let (mut ca, mut cb) = ([0u8; 64], [0u8; 64]);
12399                    if refi0 >= 0 {
12400                        let Some(rf) = self.refs.get(refi0 as usize) else {
12401                            return;
12402                        };
12403                        let pl = if c == 0 {
12404                            &*rf.chroma_guard(0, rf.ch)
12405                        } else {
12406                            &*rf.chroma_guard(1, rf.ch)
12407                        };
12408                        mc_chroma_padded(
12409                            pl,
12410                            rf.cstride(),
12411                            crate::CPAD,
12412                            self.ccw,
12413                            cch,
12414                            mb_x * 8 + crx,
12415                            mb_y * 8 + cry,
12416                            crw,
12417                            crh,
12418                            mv0.0,
12419                            mv0.1,
12420                            &mut ca[..crw * crh],
12421                        );
12422                    }
12423                    if refi1 >= 0 {
12424                        let Some(rf) = self.refs1.get(refi1 as usize) else {
12425                            return;
12426                        };
12427                        let pl = if c == 0 {
12428                            &*rf.chroma_guard(0, rf.ch)
12429                        } else {
12430                            &*rf.chroma_guard(1, rf.ch)
12431                        };
12432                        mc_chroma_padded(
12433                            pl,
12434                            rf.cstride(),
12435                            crate::CPAD,
12436                            self.ccw,
12437                            cch,
12438                            mb_x * 8 + crx,
12439                            mb_y * 8 + cry,
12440                            crw,
12441                            crh,
12442                            mv1.0,
12443                            mv1.1,
12444                            &mut cb[..crw * crh],
12445                        );
12446                    }
12447                    match (refi0 >= 0, refi1 >= 0) {
12448                        (true, true) => {
12449                            for dy in 0..crh {
12450                                let (pr, qr) =
12451                                    (&ca[dy * crw..dy * crw + crw], &cb[dy * crw..dy * crw + crw]);
12452                                let base = (cry + dy) * 8 + crx;
12453                                let dst = &mut c_pred[c][base..base + crw];
12454                                match weights {
12455                                    None => {
12456                                        for ((d, p), q) in dst.iter_mut().zip(pr).zip(qr) {
12457                                            *d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
12458                                        }
12459                                    }
12460                                    Some((w0, w1)) => {
12461                                        for ((d, p), q) in dst.iter_mut().zip(pr).zip(qr) {
12462                                            *d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6)
12463                                                .clamp(0, 255)
12464                                                as u8;
12465                                        }
12466                                    }
12467                                }
12468                            }
12469                        }
12470                        (true, false) => {
12471                            for dy in 0..crh {
12472                                let d = (cry + dy) * 8 + crx;
12473                                c_pred[c][d..d + crw]
12474                                    .copy_from_slice(&ca[dy * crw..dy * crw + crw]);
12475                            }
12476                        }
12477                        _ => {
12478                            for dy in 0..crh {
12479                                let d = (cry + dy) * 8 + crx;
12480                                c_pred[c][d..d + crw]
12481                                    .copy_from_slice(&cb[dy * crw..dy * crw + crw]);
12482                            }
12483                        }
12484                    }
12485                }
12486            }
12487        }
12488    }
12489
12490    /// B_Skip replay: regions into fresh prediction buffers, then the plane
12491    /// copy — no residual, no coefficient arrays.
12492    fn recon_b_skip(&mut self, mbx: usize, mby: usize, regions: &[BRegion]) {
12493        crate::RefFrame::set_mc_row_need(mby, self.mb_h * 16);
12494        let mut pred_y = [0u8; 256];
12495        let mut c_pred = [[0u8; 64]; 2];
12496        for r in regions {
12497            self.b_mc(
12498                mbx,
12499                mby,
12500                r.px,
12501                r.py,
12502                r.rw,
12503                r.rh,
12504                r.refi0,
12505                r.mv0,
12506                r.refi1,
12507                r.mv1,
12508                &mut pred_y,
12509                &mut c_pred,
12510                r.w,
12511            );
12512        }
12513        for dy in 0..16 {
12514            let d = (mby * 16 + dy) * self.cw + mbx * 16;
12515            self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
12516        }
12517        for c in 0..2 {
12518            let plane = if c == 0 {
12519                &mut self.rec_u
12520            } else {
12521                &mut self.rec_v
12522            };
12523            for dy in 0..8 {
12524                let d = (mby * 8 + dy) * self.ccw + mbx * 8;
12525                plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
12526            }
12527        }
12528    }
12529
12530    /// Replays one B macroblock's regions + residual (the worker half of the
12531    /// E3 seam). Mirrors the inline order exactly: MC regions in parse order
12532    /// into the prediction buffers, then the residual add (or the skip copy).
12533    fn recon_b(&mut self, j: &BJob) {
12534        crate::RefFrame::set_mc_row_need(j.mby, self.mb_h * 16);
12535        let mut pred_y = [0u8; 256];
12536        let mut c_pred = [[0u8; 64]; 2];
12537        for r in &j.regions {
12538            self.b_mc(
12539                j.mbx,
12540                j.mby,
12541                r.px,
12542                r.py,
12543                r.rw,
12544                r.rh,
12545                r.refi0,
12546                r.mv0,
12547                r.refi1,
12548                r.mv1,
12549                &mut pred_y,
12550                &mut c_pred,
12551                r.w,
12552            );
12553        }
12554        if j.skip {
12555            for dy in 0..16 {
12556                let d = (j.mby * 16 + dy) * self.cw + j.mbx * 16;
12557                self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
12558            }
12559            for c in 0..2 {
12560                let plane = if c == 0 {
12561                    &mut self.rec_u
12562                } else {
12563                    &mut self.rec_v
12564                };
12565                for dy in 0..8 {
12566                    let d = (j.mby * 8 + dy) * self.ccw + j.mbx * 8;
12567                    plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
12568                }
12569            }
12570        } else {
12571            self.cur_qp = j.qp;
12572            self.add_inter_residual(
12573                j.mbx,
12574                j.mby,
12575                &pred_y,
12576                &c_pred,
12577                j.luma_scan.as_ref(),
12578                j.luma8.as_ref(),
12579                &j.cdc,
12580                j.cac.as_ref(),
12581                j.cbp_chroma,
12582                &j.nnzs,
12583            );
12584        }
12585    }
12586}
12587
12588/// D9b: MC-call coalescing for a P inter MB from committed per-block (mv, ref).
12589/// Shared by residual and no-residual recon — filters are per-output-pixel so
12590/// wider rects are bit-identical to sixteen 4×4 calls.
12591fn coalesce_p_inter_mc(
12592    refs: &[crate::Ref],
12593    cw: usize,
12594    ccw: usize,
12595    mb_h: usize,
12596    mbx: usize,
12597    mby: usize,
12598    // Grid-width motion: widened by the callee where the MC kernels need i32.
12599    gmv: &[(i16, i16); 16],
12600    gref: &[usize; 16],
12601    pred_y: &mut [u8; 256],
12602    c_pred: &mut [[u8; 64]; 2],
12603) {
12604    let _ms = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMcStage);
12605    let (rh16, cch) = (mb_h * 16, mb_h * 8);
12606    let rect_eq = |x4: usize, y4: usize, w4: usize, h4: usize| -> bool {
12607        let t = y4 * 4 + x4;
12608        (0..h4).all(|dy| {
12609            (0..w4).all(|dx| {
12610                // 4x4 block grid: the index is always < 16, but LLVM cannot
12611                // infer it from the nested ranges. `& 15` states it.
12612                let b = ((y4 + dy) * 4 + (x4 + dx)) & 15;
12613                gmv[b] == gmv[t] && gref[b] == gref[t]
12614            })
12615        })
12616    };
12617    let mut mc_rect = |x4: usize, y4: usize, w4: usize, h4: usize| {
12618        let b = y4 * 4 + x4;
12619        let (mv, gr) = (gmv[b & 15], gref[b & 15]);
12620        let Some(reference) = refs.get(gr) else {
12621            return;
12622        };
12623        let (w, h) = (w4 * 4, h4 * 4);
12624        if w == 16 {
12625            rusty_h264_common::inter::with_mc_scratch(|scr| {
12626                rusty_h264_common::inter::mc_luma_padded_pre(
12627                    scr,
12628                    &reference.luma_guard(reference.ch),
12629                    reference.lstride(),
12630                    crate::LPAD,
12631                    cw,
12632                    rh16,
12633                    mbx * 16,
12634                    mby * 16 + y4 * 4,
12635                    w,
12636                    h,
12637                    i32::from(mv.0),
12638                    i32::from(mv.1),
12639                    &mut pred_y[y4 * 64..y4 * 64 + w * h],
12640                )
12641            });
12642        } else {
12643            let mut t = [0u8; 256];
12644            rusty_h264_common::inter::with_mc_scratch(|scr| {
12645                rusty_h264_common::inter::mc_luma_padded_pre(
12646                    scr,
12647                    &reference.luma_guard(reference.ch),
12648                    reference.lstride(),
12649                    crate::LPAD,
12650                    cw,
12651                    rh16,
12652                    mbx * 16 + x4 * 4,
12653                    mby * 16 + y4 * 4,
12654                    w,
12655                    h,
12656                    i32::from(mv.0),
12657                    i32::from(mv.1),
12658                    &mut t[..w * h],
12659                )
12660            });
12661            let _pb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
12662            for dy in 0..h {
12663                pred_y[(y4 * 4 + dy) * 16 + x4 * 4..][..w].copy_from_slice(&t[dy * w..dy * w + w]);
12664            }
12665        }
12666        let (cw4, ch4) = (w4 * 2, h4 * 2);
12667        let nc = cw4 * ch4;
12668        let (gu, gv) = (
12669            reference.chroma_guard(0, reference.ch),
12670            reference.chroma_guard(1, reference.ch),
12671        );
12672        // U+V paired: one setup serves both planes (see mc_chroma_padded_pair).
12673        if cw4 == 8 {
12674            let [cu, cv] = &mut *c_pred;
12675            rusty_h264_common::inter::mc_chroma_padded_pair(
12676                &gu,
12677                &gv,
12678                reference.cstride(),
12679                crate::CPAD,
12680                ccw,
12681                cch,
12682                mbx * 8,
12683                mby * 8 + y4 * 2,
12684                cw4,
12685                ch4,
12686                i32::from(mv.0),
12687                i32::from(mv.1),
12688                &mut cu[y4 * 16..y4 * 16 + nc],
12689                &mut cv[y4 * 16..y4 * 16 + nc],
12690            );
12691        } else {
12692            let (mut tu, mut tv) = ([0u8; 64], [0u8; 64]);
12693            rusty_h264_common::inter::mc_chroma_padded_pair(
12694                &gu,
12695                &gv,
12696                reference.cstride(),
12697                crate::CPAD,
12698                ccw,
12699                cch,
12700                mbx * 8 + x4 * 2,
12701                mby * 8 + y4 * 2,
12702                cw4,
12703                ch4,
12704                i32::from(mv.0),
12705                i32::from(mv.1),
12706                &mut tu[..nc],
12707                &mut tv[..nc],
12708            );
12709            let _pb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
12710            for (cc, tc) in [(0usize, &tu), (1, &tv)] {
12711                for dy in 0..ch4 {
12712                    c_pred[cc][(y4 * 2 + dy) * 8 + x4 * 2..][..cw4]
12713                        .copy_from_slice(&tc[dy * cw4..dy * cw4 + cw4]);
12714                }
12715            }
12716        }
12717    };
12718    if rect_eq(0, 0, 4, 4) {
12719        mc_rect(0, 0, 4, 4);
12720    } else if rect_eq(0, 0, 4, 2) && rect_eq(0, 2, 4, 2) {
12721        mc_rect(0, 0, 4, 2);
12722        mc_rect(0, 2, 4, 2);
12723    } else if rect_eq(0, 0, 2, 4) && rect_eq(2, 0, 2, 4) {
12724        mc_rect(0, 0, 2, 4);
12725        mc_rect(2, 0, 2, 4);
12726    } else {
12727        for q in 0..4usize {
12728            let (qx, qy) = ((q % 2) * 2, (q / 2) * 2);
12729            if rect_eq(qx, qy, 2, 2) {
12730                mc_rect(qx, qy, 2, 2);
12731            } else if rect_eq(qx, qy, 2, 1) && rect_eq(qx, qy + 1, 2, 1) {
12732                mc_rect(qx, qy, 2, 1);
12733                mc_rect(qx, qy + 1, 2, 1);
12734            } else if rect_eq(qx, qy, 1, 2) && rect_eq(qx + 1, qy, 1, 2) {
12735                mc_rect(qx, qy, 1, 2);
12736                mc_rect(qx + 1, qy, 1, 2);
12737            } else {
12738                for j in 0..4usize {
12739                    mc_rect(qx + (j % 2), qy + (j / 2), 1, 1);
12740                }
12741            }
12742        }
12743    }
12744}
12745
12746/// One deferred pixel-reconstruction job (entropy-decouple E1 seam).
12747enum EdcJob {
12748    Skip {
12749        mbx: usize,
12750        mby: usize,
12751        mv: (i32, i32),
12752    },
12753    Inter(Box<PInterJob>),
12754    B(Box<BJob>),
12755    /// B_Skip / no-residual direct: regions only. The full `BJob` carried
12756    /// 2.6 KB of ZEROED coefficient arrays for ~60% of B macroblocks — the
12757    /// wall-time regression's main CPU tax (B-heavy MT arm measured +45% CPU).
12758    BSkip {
12759        mbx: usize,
12760        mby: usize,
12761        regions: Vec<BRegion>,
12762    },
12763    /// P inter with `cbp == 0` — the P-side twin of `BSkip` (D9).
12764    InterNoRes(Box<PInterNoResJob>),
12765}
12766
12767/// A P inter macroblock with NO residual (`cbp == 0`) — motion only.
12768///
12769/// D9. `PInterJob` is 2,784 bytes and **93% of that is coefficient arrays**
12770/// (`luma_scan` 1024 + `luma8` 1024 + `cac` 512 + `cdc` 32 = 2,592). When
12771/// `cbp == 0` every one of them is ZERO, and the seam was heap-allocating,
12772/// filling, channel-passing and freeing all 2,592 bytes of nothing —
12773/// 12.8-37.5% of inter jobs on the x264 corpus, 15.8-44.1 MB per 60-frame pass.
12774///
12775/// This is the same pathology `EdcJob::BSkip` was introduced to fix on the B
12776/// side ("2.6 KB of ZEROED coefficient arrays for ~60% of B macroblocks — the
12777/// wall-time regression's main CPU tax"). It was never ported to P.
12778///
12779/// Consumer uses `recon_p_inter_nores` (MC + plane copy) — bit-identical to
12780/// `recon_p_inter` on zero residuals, without memset of 2.5 KB coeff arrays.
12781/// (`RS_H264_NORES=0` keeps the old full-job path for A/B — routed at the
12782/// CONSTRUCTOR, so this job carries only what nores recon reads.)
12783struct PInterNoResJob {
12784    mbx: usize,
12785    mby: usize,
12786    t8: bool,
12787    gmv: [(i16, i16); 16],
12788    gref: [u8; 16],
12789}
12790
12791/// The compact inputs of one CABAC P inter macroblock's reconstruction.
12792/// Shared all-zero residual planes. An Option-shaped residual field is `None`
12793/// exactly when the parse wrote nothing into it, and every consumer read is
12794/// already guarded by a zero-count test — so a `None` reader would have seen
12795/// zeros anyway. Pointing at one static beats zero-initialising 1 KB (luma) or
12796/// 512 B (chroma AC) of stack per macroblock.
12797static ZERO_LUMA_SCAN: [[i32; 16]; 16] = [[0; 16]; 16];
12798/// `(16384 + |td|/2) / td` for every `td` the spec can present. `td` is
12799/// `.clamp(-128, 127)` at both call sites (spec 8.4.1.2.3), so the divide has
12800/// exactly 256 possible inputs and a table replaces it EXACTLY -- this is not an
12801/// approximation, it is the same arithmetic evaluated ahead of time. `td == 0`
12802/// is guarded by the caller and maps to 0 here so the table has no hole.
12803static TX_FOR_TD: [i32; 256] = {
12804    let mut t = [0i32; 256];
12805    let mut i = 0usize;
12806    while i < 256 {
12807        let td = i as i32 - 128; // index 0 => -128 .. index 255 => 127
12808        t[i] = if td == 0 {
12809            0
12810        } else {
12811            (16384 + td.abs() / 2) / td
12812        };
12813        i += 1;
12814    }
12815    t
12816};
12817
12818#[inline(always)]
12819fn tx_for_td(td: i32) -> i32 {
12820    debug_assert!((-128..=127).contains(&td), "td must be clamped before this");
12821    TX_FOR_TD[(td.clamp(-128, 127) + 128) as usize]
12822}
12823
12824/// The "no coefficients" neighbour record. `mb_nzc` reads are reached only
12825/// through `if let Some(..) = top/left`, so an out-of-range index is
12826/// unreachable; this is what the unavailable branches already imply.
12827static ZERO_NZC: [u8; 24] = [0u8; 24];
12828static ZERO_CAC: [[[i32; 16]; 4]; 2] = [[[0; 16]; 4]; 2];
12829
12830struct PInterJob {
12831    mbx: usize,
12832    mby: usize,
12833    qp: u8,
12834    cbp_chroma: u32,
12835    /// transform_size_8x8_flag: the residual is `luma8`, not `luma_scan`.
12836    t8: bool,
12837    /// The committed per-block motion, copied at parse time so the worker
12838    /// never reads the parse thread's grids (E2). Ref indices clamped by the
12839    /// consumer, kept u8 (spec max 15).
12840    gmv: [(i16, i16); 16],
12841    gref: [u8; 16],
12842    /// `None` when no 4x4 luma block was coded — t8 macroblocks included,
12843    /// since those carry `luma8` instead.
12844    /// POOLED and reused across macroblocks (see `take_pinter_job`): only the
12845    /// blocks whose `nnzs` entry is nonzero hold the data of this macroblock; every
12846    /// other block is STALE and the consumer never reads it (it copies the
12847    /// prediction when nnz == 0). The parse zeroes exactly the blocks it is
12848    /// about to write.
12849    luma_scan: [[i32; 16]; 16],
12850    luma8: [[i32; 64]; 4],
12851    cdc: [[i32; 4]; 2],
12852    cac: [[[i32; 16]; 4]; 2],
12853    nnzs: [u8; 24],
12854}
12855
12856impl PInterJob {
12857    fn blank() -> Self {
12858        PInterJob {
12859            mbx: 0,
12860            mby: 0,
12861            qp: 0,
12862            cbp_chroma: 0,
12863            t8: false,
12864            gmv: [(0i16, 0i16); 16],
12865            gref: [0; 16],
12866            luma_scan: [[0; 16]; 16],
12867            luma8: [[0; 64]; 4],
12868            cdc: [[0; 4]; 2],
12869            cac: [[[0; 16]; 4]; 2],
12870            nnzs: [0; 24],
12871        }
12872    }
12873}
12874
12875/// Entropy-decouple master knob — DEFAULT ON since 2026-08-05 (`RS_H264_EDC=0`
12876/// opts out). E1 was expected to be cost-neutral scaffolding for the E2
12877/// thread; it BANKED on its own: 13/15 pairs, z=2.84, median +4.0% (pooled
12878/// 19/24, z=2.86). Mechanism: LOOP FISSION — batching a row's parsing and
12879/// then a row's reconstruction keeps each large code path's I-cache and
12880/// branch state hot, instead of alternating two giant bodies per macroblock.
12881fn edc_on() -> bool {
12882    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12883    // constant below; the env A/B arm exists only under `--features knobs`.
12884    #[cfg(not(feature = "knobs"))]
12885    {
12886        true
12887    }
12888    #[cfg(feature = "knobs")]
12889    {
12890        use core::sync::atomic::{AtomicU8, Ordering};
12891        static ON: AtomicU8 = AtomicU8::new(0);
12892        match ON.load(Ordering::Relaxed) {
12893            0 => {
12894                let v = !rusty_h264_common::knob("RS_H264_EDC").is_some_and(|v| v == "0");
12895                ON.store(if v { 1 } else { 2 }, Ordering::Relaxed);
12896                v
12897            }
12898            n => n == 1,
12899        }
12900    }
12901}
12902
12903/// E2/E3 worker-thread knob.
12904///
12905/// **Default OFF (2026-08-11).** ffmpeg's parallel unit is the PICTURE
12906/// (`decode_slice` + `hl_decode_mb` on the same thread). `edc_worker` is a
12907/// nested recon thread — the wrong function:
12908///   * 1T (`ffmpeg -threads 1`, pinvs pin): two threads thrash one core
12909///   * frame-MT (`ffmpeg -threads N`): each picture worker would spawn another
12910/// Frame-MT (`RS_H264_FRAME_THREADS=N`) is the ffmpeg-shaped pool. This
12911/// pipeline stays as an explicit oracle (`RS_H264_EDC_MT=1`) / old auto-gate
12912/// (`=auto`). `=0` forces inline.
12913/// D8: run every pixel reconstruction TWICE (the second pass is discarded work,
12914/// not discarded output -- recon is idempotent, so the bytes are unchanged).
12915/// `t(double) - t(single)` IS the pixel half's cost, which is the parallel
12916/// fraction the E2 seam can address. Byte-identity is the proof the ablation
12917/// did not change the program (unlike removing the stage, which cascades).
12918/// D9 compact no-residual P inter jobs. `RS_H264_NORES=0` restores the old
12919/// always-full-payload path for A/B (the arm must PIN the value, never inherit
12920/// a default -- an "off" arm that only omits an override measures
12921/// default-vs-default and prints all zeros).
12922/// D10 row batching — **DEFAULT ON. A DELIBERATE THROUGHPUT-OVER-LATENCY TRADE.**
12923///
12924/// Ships a row's pixel jobs in one message instead of one per macroblock:
12925/// 207,949 sends -> 3,086 (**67-70x fewer**), and **~3% less CPU**.
12926///
12927/// ⚠ IT COSTS 2-4% WALL on a single stream — 0.963x / 0.980x, **11/11 pairs on
12928/// two clips**, A/B'd against itself at a fixed queue bound. That is measured,
12929/// reproducible, and ACCEPTED, not an oversight. Do not "fix" it by flipping
12930/// the default; read this first.
12931///
12932/// WHY IT COSTS WALL: batching trades pipelining for synchronisation.
12933/// Per-macroblock sends let the worker start on job 1 immediately; a row batch
12934/// makes it idle until ~70 macroblocks are parsed, then hands it a burst.
12935///
12936/// WHY IT IS STILL THE RIGHT DEFAULT: wall time here is SINGLE-STREAM LATENCY;
12937/// CPU is THROUGHPUT. A host decoding many streams concurrently is CPU-bound,
12938/// not latency-bound, so 3% less CPU is ~3% more capacity while the 2-4% wall
12939/// cost falls on a dimension that is not the constraint. The 67x drop in
12940/// channel operations also removes a park/unpark storm that scales with the
12941/// number of runnable threads — it gets better, not worse, as the box fills.
12942///
12943/// `RS_H264_BATCH=0` restores per-macroblock sends for the latency-sensitive
12944/// single-stream case (playback, seek preview, anything where first-frame time
12945/// dominates).
12946fn batch_on() -> bool {
12947    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12948    // constant below; the env A/B arm exists only under `--features knobs`.
12949    #[cfg(not(feature = "knobs"))]
12950    {
12951        true
12952    }
12953    #[cfg(feature = "knobs")]
12954    {
12955        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12956        *V.get_or_init(|| !rusty_h264_common::knob("RS_H264_BATCH").is_some_and(|v| v == "0"))
12957    }
12958}
12959
12960fn nores_on() -> bool {
12961    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12962    // constant below; the env A/B arm exists only under `--features knobs`.
12963    #[cfg(not(feature = "knobs"))]
12964    {
12965        true
12966    }
12967    #[cfg(feature = "knobs")]
12968    {
12969        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12970        *V.get_or_init(|| !rusty_h264_common::knob("RS_H264_NORES").is_some_and(|v| v == "0"))
12971    }
12972}
12973
12974/// D13 A/B: always allocate B-only CABAC neighbour grids even on P/I slices.
12975fn fat_slice_on() -> bool {
12976    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12977    // constant below; the env A/B arm exists only under `--features knobs`.
12978    #[cfg(not(feature = "knobs"))]
12979    {
12980        false
12981    }
12982    #[cfg(feature = "knobs")]
12983    {
12984        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12985        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_FAT_SLICE").is_some_and(|v| v == "1"))
12986    }
12987}
12988
12989/// MEASUREMENT KNOB — `RS_H264_NO_SKIPBAND=1` forces the per-MB skip path so the
12990/// mb_skip_run band coalescer can be A/B'd paired on ONE binary. Inert when unset.
12991fn no_skipband() -> bool {
12992    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12993    // constant below; the env A/B arm exists only under `--features knobs`.
12994    #[cfg(not(feature = "knobs"))]
12995    {
12996        false
12997    }
12998    #[cfg(feature = "knobs")]
12999    {
13000        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13001        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_NO_SKIPBAND").is_some_and(|v| v == "1"))
13002    }
13003}
13004
13005/// MEASUREMENT KNOB — `RS_H264_NO_RUNMV=1` forces the full skip_mv derivation
13006/// on every P_Skip (disables the run-theorem forced-(0,0) branch) for paired A/B.
13007fn no_runmv() -> bool {
13008    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13009    // constant below; the env A/B arm exists only under `--features knobs`.
13010    #[cfg(not(feature = "knobs"))]
13011    {
13012        false
13013    }
13014    #[cfg(feature = "knobs")]
13015    {
13016        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13017        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_NO_RUNMV").is_some_and(|v| v == "1"))
13018    }
13019}
13020
13021/// MEASUREMENT KNOB — `RS_H264_NO_SKIPFP=1` disables the P_Skip single fast
13022/// paths (full-pel direct copy + identity-weight-pass skip) for paired A/B.
13023fn no_skipfp() -> bool {
13024    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13025    // constant below; the env A/B arm exists only under `--features knobs`.
13026    #[cfg(not(feature = "knobs"))]
13027    {
13028        false
13029    }
13030    #[cfg(feature = "knobs")]
13031    {
13032        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13033        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_NO_SKIPFP").is_some_and(|v| v == "1"))
13034    }
13035}
13036
13037/// MEASUREMENT KNOB — `RS_H264_NO_BSKIPFAST=1` forces the full decode_b_direct
13038/// path for B_Skip so the zero-bi fast path can be A/B'd paired on ONE binary.
13039fn no_bskipfast() -> bool {
13040    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13041    // constant below; the env A/B arm exists only under `--features knobs`.
13042    #[cfg(not(feature = "knobs"))]
13043    {
13044        false
13045    }
13046    #[cfg(feature = "knobs")]
13047    {
13048        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13049        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_NO_BSKIPFAST").is_some_and(|v| v == "1"))
13050    }
13051}
13052
13053fn double_recon() -> bool {
13054    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13055    // constant below; the env A/B arm exists only under `--features knobs`.
13056    #[cfg(not(feature = "knobs"))]
13057    {
13058        false
13059    }
13060    #[cfg(feature = "knobs")]
13061    {
13062        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13063        // `== "1"`, not `is_some()`: the old presence test meant even
13064        // `RS_H264_DOUBLE_RECON=0` DOUBLED the recon work — the one knob in the
13065        // inventory whose "off" spelling turned it on (2026-08-27 audit, site 8).
13066        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_DOUBLE_RECON").is_some_and(|v| v == "1"))
13067    }
13068}
13069
13070fn edc_bound() -> usize {
13071    static V: rusty_h264_common::once::OnceLock<usize> = rusty_h264_common::once::OnceLock::new();
13072    *V.get_or_init(|| {
13073        rusty_h264_common::knob("RS_H264_EDC_BOUND")
13074            .and_then(|v| v.parse().ok())
13075            .unwrap_or(256)
13076    })
13077}
13078
13079/// D12 — E2 THREADING DISPATCH. Fires on `720p-or-smaller AND bits/MB > 38.4`.
13080///
13081/// The seam threads unconditionally before this, and that shipped a REGRESSION:
13082/// 8-10% slower wall on main profile for 38-56% more CPU. It cannot pay in
13083/// general — the pixel half is only ~15.6% of decode, so Amdahl caps two
13084/// threads at 1.085x — but it DOES win on some streams, so the answer is a
13085/// dispatch, not abandonment.
13086///
13087/// Fitted with `bench/examples/gate_optimizer.rs` over **28 interleaved
13088/// configurations** (12 clips x core counts 4-8, `bench/pinmtx.ps1`):
13089/// **net +29.40 of +30.70 perfect**, train +25.10 AND holdout +4.30, worst
13090/// fired class **+2.94**, precision 0.80. It forgoes +0.40 of wins to avoid
13091/// **169.90** of losses. Calibration: depth-2 **2/300** rules passed (0.67%),
13092/// so the separation carries information.
13093///
13094/// Per clause, both load-bearing (dropping either: -20.50 / -50.60, 6-7 big
13095/// losers):
13096/// * `bits/MB > 38.4` — coefficient density is the runtime proxy for PIXEL
13097///   SHARE: more coefficients means more residual work on the far side of the
13098///   seam. Threshold sits in an open gap (highest excluded 35.4, lowest firing
13099///   41.4). Note this is the OPPOSITE direction to an earlier hand-fitted
13100///   `bits < 65`, which was fitted to one clip and falsified.
13101/// * frame <= 720p — every 1080p configuration measured loses, including a
13102///   low-density one, so density alone is not sufficient.
13103/// * `cabac` — ADDED 2026-08-07 after the CAVLC arm made those units
13104///   measurable. bits/MB DOES NOT TRANSFER ACROSS ENTROPY CODERS: CAVLC needs
13105///   more bits for the SAME coefficients, so its density (62-65) reads deep
13106///   inside the firing region while its pixel work is unchanged. Without this
13107///   clause the rule routed CAVLC into threading, where it measured 1.29-1.49x
13108///   SLOWER — net -52.30, worst class -40.85. With it, +29.40 and worst class
13109///   +2.94. `gate_optimizer` could not find this: the rule needs THREE clauses
13110///   and the search is depth-2 (both depth-2 pairs fail, -20.50 / -50.60).
13111///
13112/// The estimate comes from ALREADY-DECODED slices, so the first slice of a
13113/// stream runs INLINE (the safe arm) until a measurement exists. Both arms are
13114/// byte-identical, so the choice can never affect output.
13115/// Invoked only by `RS_H264_EDC_MT=auto` (the pre-2026-08-11 default).
13116fn edc_dispatch(mb_w: usize, mb_h: usize, bits_per_mb: f64, cabac: bool) -> bool {
13117    const BITS_MIN: f64 = 38.4;
13118    const MAX_MBS: usize = 5000; // 720p = 3600, 1080p = 8160
13119                                 // The `cabac` clause is NOT cosmetic — see the header note. Without it this
13120                                 // rule scores net -52.30 with worst class -40.85 once CAVLC units are in the
13121                                 // corpus, because CAVLC's bits/MB is inflated by a less efficient entropy
13122                                 // coder rather than by more pixel work.
13123    cabac && bits_per_mb > BITS_MIN && mb_w * mb_h <= MAX_MBS
13124}
13125
13126fn edc_mt() -> Option<bool> {
13127    static V: rusty_h264_common::once::OnceLock<Option<bool>> =
13128        rusty_h264_common::once::OnceLock::new();
13129    *V.get_or_init(
13130        || match rusty_h264_common::knob("RS_H264_EDC_MT").as_deref() {
13131            Some("0") => Some(false),
13132            Some("1") => Some(true),
13133            Some("auto") => None,
13134            _ => Some(false),
13135        },
13136    )
13137}
13138
13139/// Spawn `edc_worker`? ffmpeg never does this: the picture thread owns recon.
13140/// Frame-MT workers (`FRAME_THREADS>1`) always inline. Otherwise honor the knob
13141/// (`1` / `0` / `auto`→[`edc_dispatch`]; unset = inline).
13142fn edc_spawn_worker(mb_w: usize, mb_h: usize, bits_per_mb: f64, cabac: bool) -> bool {
13143    #[cfg(not(feature = "std"))]
13144    {
13145        let _ = (mb_w, mb_h, bits_per_mb, cabac);
13146        false
13147    }
13148    #[cfg(feature = "std")]
13149    {
13150        if crate::frame_threads() > 1 {
13151            return false;
13152        }
13153        edc_mt().unwrap_or_else(|| edc_dispatch(mb_w, mb_h, bits_per_mb, cabac))
13154    }
13155}
13156
13157/// Picture-end bS precompute (rowdb-off fallback). `RS_H264_BS_PRE=0` opts out.
13158fn bs_pre_on() -> bool {
13159    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13160    // constant below; the env A/B arm exists only under `--features knobs`.
13161    #[cfg(not(feature = "knobs"))]
13162    {
13163        true
13164    }
13165    #[cfg(feature = "knobs")]
13166    {
13167        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13168        *V.get_or_init(|| !rusty_h264_common::knob("RS_H264_BS_PRE").is_some_and(|v| v == "0"))
13169    }
13170}
13171
13172/// Row-interleaved deblocking master knob: `RS_H264_ROWDB=0` opts out,
13173/// restoring the picture-end pipeline (WHYS Part 17) as the A/B comparator.
13174/// MEASUREMENT KNOB — `RS_H264_KIND_LOADS=1` restores the Blk::load-based
13175/// kind arms in derive_bs_row (see the routing comment there) for paired A/B.
13176fn kind_loads() -> bool {
13177    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13178    // constant below; the env A/B arm exists only under `--features knobs`.
13179    #[cfg(not(feature = "knobs"))]
13180    {
13181        false
13182    }
13183    #[cfg(feature = "knobs")]
13184    {
13185        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13186        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_KIND_LOADS").is_some_and(|v| v == "1"))
13187    }
13188}
13189
13190fn rowdb_on() -> bool {
13191    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13192    // constant below; the env A/B arm exists only under `--features knobs`.
13193    #[cfg(not(feature = "knobs"))]
13194    {
13195        true
13196    }
13197    #[cfg(feature = "knobs")]
13198    {
13199        use core::sync::atomic::{AtomicU8, Ordering};
13200        static ON: AtomicU8 = AtomicU8::new(0);
13201        match ON.load(Ordering::Relaxed) {
13202            0 => {
13203                let v = !rusty_h264_common::knob("RS_H264_ROWDB").is_some_and(|v| v == "0");
13204                ON.store(if v { 1 } else { 2 }, Ordering::Relaxed);
13205                v
13206            }
13207            n => n == 1,
13208        }
13209    }
13210}
13211
13212/// A/B: per-MB `row_hook` body even when no row has completed (old behaviour).
13213fn rowhook_eager() -> bool {
13214    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13215    // constant below; the env A/B arm exists only under `--features knobs`.
13216    #[cfg(not(feature = "knobs"))]
13217    {
13218        false
13219    }
13220    #[cfg(feature = "knobs")]
13221    {
13222        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13223        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_ROWHOOK_EAGER").is_some_and(|v| v == "1"))
13224    }
13225}
13226
13227/// A/B: `RS_H264_DIRECT_MEMO=0` rewalks spatial-direct neighbours every 8×8.
13228#[inline]
13229fn direct_memo_on() -> bool {
13230    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
13231    // constant below; the env A/B arm exists only under `--features knobs`.
13232    #[cfg(not(feature = "knobs"))]
13233    {
13234        true
13235    }
13236    #[cfg(feature = "knobs")]
13237    {
13238        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
13239        *V.get_or_init(|| !rusty_h264_common::knob("RS_H264_DIRECT_MEMO").is_some_and(|v| v == "0"))
13240    }
13241}
13242
13243/// 4×4-block (z-order) → 30-entry (6-stride) mv/ref/mvd cache index (openh264
13244/// g_kCache30ScanIdx). Top neighbour = cache[idx-6], left = cache[idx-1].
13245use rusty_h264_common::cabac_tables::CACHE30;
13246
13247/// z-order 4×4-block → raster index (openh264 g_kuiScan4). Per-MB mvd/ref state is
13248/// stored raster-indexed (matching how neighbour blocks 3/7/11/15 and 12..15 are read).
13249use rusty_h264_common::cabac_tables::G_SCAN4;
13250
13251/// P `sub_mb_type` CABAC (openh264 `ParseSubMBTypeCabac`, ctx 21). 0=8×8, 1=8×4, 2=4×8, 3=4×4.
13252fn parse_sub_mb_type_p_cabac(cab: &mut crate::cabac::Cabac) -> u32 {
13253    const S: usize = 21;
13254    let (data, mut e, ctx) = cab.view();
13255    let v = if e.decode_decision(data, ctx, S) != 0 {
13256        0
13257    } else if e.decode_decision(data, ctx, S + 1) != 0 {
13258        3 - e.decode_decision(data, ctx, S + 2)
13259    } else {
13260        1
13261    };
13262    cab.commit(e);
13263    v
13264}
13265
13266/// Intra `mb_type` sub-parse for P/B slices (openh264 `DecodeCabacIntraMbType`, `base`=32
13267/// for B), on a live engine view. Returns 0 = I_4x4, 1..=24 = I_16x16, 25 = I_PCM (in
13268/// the intra numbering).
13269#[inline(always)]
13270fn intra_mb_type_eng(e: &mut Eng, data: &[u8], ctx: &mut Ctx, base: usize) -> u32 {
13271    if e.decode_decision(data, ctx, base) == 0 {
13272        return 0; // I_4x4
13273    }
13274    if e.decode_terminate(data) {
13275        return 25; // I_PCM
13276    }
13277    let mut t = 1 + 12 * e.decode_decision(data, ctx, base + 1); // cbp_luma != 0
13278    if e.decode_decision(data, ctx, base + 2) != 0 {
13279        t += 4 + 4 * e.decode_decision(data, ctx, base + 2);
13280    }
13281    t += 2 * e.decode_decision(data, ctx, base + 3);
13282    t += e.decode_decision(data, ctx, base + 3);
13283    t
13284}
13285
13286/// B `mb_type` CABAC (openh264 `ParseMBTypeBSliceCabac`, ctx base 27). `ctx_inc` = (left
13287/// avail & !direct) + (top avail & !direct). Returns 0 = B_Direct_16x16, 1..=21 = the
13288/// L0/L1/Bi 16x16/16x8/8x16 shapes, 22 = B_8x8, 23.. = intra (mb_type - 23).
13289/// Test-only alias so the ENCODER crate can gate `cb_mb_type_b` against this
13290/// parser directly -- they are exact inverses, so a round-trip is a complete gate.
13291#[doc(hidden)]
13292pub fn parse_mb_type_b(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
13293    parse_mb_type_b_cabac(cab, ctx_inc)
13294}
13295
13296#[inline]
13297fn parse_mb_type_b_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
13298    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
13299    const B: usize = 27;
13300    let (data, mut e, ctx) = cab.view();
13301    let v = mb_type_b_eng(&mut e, data, ctx, B, ctx_inc);
13302    cab.commit(e);
13303    v
13304}
13305
13306#[inline(always)]
13307fn mb_type_b_eng(e: &mut Eng, data: &[u8], ctx: &mut Ctx, b: usize, ctx_inc: usize) -> u32 {
13308    if e.decode_decision(data, ctx, b + ctx_inc) == 0 {
13309        return 0; // B_Direct_16x16
13310    }
13311    if e.decode_decision(data, ctx, b + 3) == 0 {
13312        return 1 + e.decode_decision(data, ctx, b + 5); // 16x16 L0 / L1
13313    }
13314    let mut m = e.decode_decision(data, ctx, b + 4) << 3;
13315    m |= e.decode_decision(data, ctx, b + 5) << 2;
13316    m |= e.decode_decision(data, ctx, b + 5) << 1;
13317    m |= e.decode_decision(data, ctx, b + 5);
13318    if m < 8 {
13319        return m + 3;
13320    }
13321    if m == 13 {
13322        return intra_mb_type_eng(e, data, ctx, 32) + 23;
13323    }
13324    if m == 14 {
13325        return 11; // B_Bi_8x16
13326    }
13327    if m == 15 {
13328        return 22; // B_8x8
13329    }
13330    m = (m << 1) | e.decode_decision(data, ctx, b + 5);
13331    m - 4
13332}
13333
13334/// B `sub_mb_type` CABAC (openh264 `ParseBSubMBTypeCabac`, ctx base 36). Returns 0..=12
13335/// per spec Table 7-18 (0 = B_Direct_8x8, 1 = B_L0_8x8, ..., 12 = B_Bi_4x4).
13336fn parse_sub_mb_type_b_cabac(cab: &mut crate::cabac::Cabac) -> u32 {
13337    const B: usize = 36;
13338    let (data, mut e, ctx) = cab.view();
13339    let v = 'v: {
13340        if e.decode_decision(data, ctx, B) == 0 {
13341            break 'v 0; // B_Direct_8x8
13342        }
13343        if e.decode_decision(data, ctx, B + 1) == 0 {
13344            break 'v 1 + e.decode_decision(data, ctx, B + 3); // B_L0_8x8 / B_L1_8x8
13345        }
13346        let mut st = 3u32;
13347        if e.decode_decision(data, ctx, B + 2) != 0 {
13348            if e.decode_decision(data, ctx, B + 3) != 0 {
13349                break 'v 11 + e.decode_decision(data, ctx, B + 3); // B_L1_4x4 / B_Bi_4x4
13350            }
13351            st += 4;
13352        }
13353        st += 2 * e.decode_decision(data, ctx, B + 3);
13354        st += e.decode_decision(data, ctx, B + 3);
13355        st
13356    };
13357    cab.commit(e);
13358    v
13359}
13360
13361/// Parse one motion partition's `mvd` (x,y) and splat it into the 30-entry cache + the
13362/// per-MB raster mvd/ref state. `part_idx` = the partition's top-left z-order block (for
13363/// the ctxInc neighbour lookup); `zblocks` = every z-order 4×4 block the partition covers.
13364fn parse_mvd_partition(
13365    cab: &mut crate::cabac::Cabac,
13366    part_idx: usize,
13367    zblocks: &[usize],
13368    mvdc: &mut [[i16; 2]; 30],
13369    refc: &mut [i8; 30],
13370    mmvd: &mut [[i16; 2]; 16],
13371    mref: &mut [i8; 16],
13372    ref_idx: i8,
13373) -> (i32, i32) {
13374    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
13375    // `CACHE30` is [usize; 16] and every entry lies in [7, 28]; the mask and
13376    // clamp are semantic no-ops that hand LLVM the ranges it cannot infer, so
13377    // the `refc[s - 6]` / `mvdc[s - 1]` indexes into the 30-entry caches become
13378    // provable instead of bounds-checked.
13379    let s = CACHE30[part_idx & 15].clamp(6, 29);
13380    // Neighbour AVAILABILITY is per-neighbour, not per-component: the old closure
13381    // re-tested both refc slots for each of x and y, four loads to answer two
13382    // questions. Resolve once, then sum each component.
13383    let (av_b, av_a) = (refc[s - 6] >= 0, refc[s - 1] >= 0);
13384    let (nb, na) = (mvdc[s - 6], mvdc[s - 1]);
13385    let ctx = |comp: usize| -> usize {
13386        let mut a = 0i32;
13387        if av_b {
13388            a += nb[comp].unsigned_abs() as i32;
13389        }
13390        if av_a {
13391            a += na[comp].unsigned_abs() as i32;
13392        }
13393        if a >= 3 {
13394            1 + (a > 32) as usize
13395        } else {
13396            0
13397        }
13398    };
13399    let (cx, cy) = (ctx(0), ctx(1));
13400    // Both components on ONE engine view (see `Cabac::view`).
13401    let (data, mut e, ctx) = cab.view();
13402    let mvx = mvd_component(&mut e, data, ctx, 0, cx);
13403    let mvy = mvd_component(&mut e, data, ctx, 1, cy);
13404    cab.commit(e);
13405    let mvd = [mvx, mvy];
13406    if zblocks.len() == 16 {
13407        // Whole-macroblock partition (the dominant B shape): every raster slot
13408        // takes the same value — one fill each instead of 16 indexed stores.
13409        mmvd.fill(mvd);
13410        mref.fill(ref_idx);
13411        // The 16 z-blocks land on FOUR contiguous 4-entry runs of the 6-stride
13412        // cache (rows 7.., 13.., 19.., 25..): eight fills instead of 32 stores.
13413        for r in [7usize, 13, 19, 25] {
13414            mvdc[r..r + 4].fill(mvd);
13415            refc[r..r + 4].fill(ref_idx);
13416        }
13417    } else {
13418        for &zb in zblocks {
13419            // Each table was being read twice per block.
13420            let (c, g) = (CACHE30[zb & 15], G_SCAN4[zb & 15]);
13421            mvdc[c] = mvd;
13422            refc[c] = ref_idx;
13423            mmvd[g] = mvd;
13424            mref[g] = ref_idx;
13425        }
13426    }
13427    (mvx as i32, mvy as i32)
13428}
13429
13430/// `ref_idx_l0` (P) CABAC — mirror of the encoder `cb_ref_idx`. Unary, ctxIdxOffset
13431/// 54: binIdx 0 → `ctx0` (condTermFlagA + 2·condTermFlagB), binIdx 1 → 4, binIdx ≥2 → 5.
13432pub fn parse_ref_idx_cabac(cab: &mut crate::cabac::Cabac, ctx0: usize) -> i8 {
13433    const B: usize = 54;
13434    let (data, mut e, ctx) = cab.view();
13435    let mut r = 0i8;
13436    let mut bin_idx = 0u32;
13437    // Cap the unary length: valid ref_idx <= 15 (16 refs max); the cap keeps a corrupt
13438    // stream from looping unboundedly. The MC clamps the index, so an over-range value
13439    // is decoded as garbage (never a panic) -- the robustness contract, not correctness.
13440    while bin_idx < 32 {
13441        let c = match bin_idx {
13442            0 => ctx0,
13443            1 => 4,
13444            _ => 5,
13445        };
13446        if e.decode_decision(data, ctx, B + c) == 0 {
13447            break;
13448        }
13449        r += 1;
13450        bin_idx += 1;
13451    }
13452    cab.commit(e);
13453    r
13454}
13455
13456/// UEG3 mvd suffix (openh264 `DecodeUEGMvCabac`): TU prefix at `base + {0,1,2,3,3,..}`
13457/// (<=7), then EG3 bypass. Inlined into `parse_mvd_partition` for the same
13458/// register-residency reason as `cabac_ueg_level`.
13459#[inline(always)]
13460fn decode_ueg_mv(e: &mut Eng, data: &[u8], ctx: &mut Ctx, base: usize) -> u32 {
13461    const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
13462    if e.decode_decision(data, ctx, base) == 0 {
13463        return 0;
13464    }
13465    let mut code = 0u32;
13466    let mut count = 1usize;
13467    let mut tmp;
13468    loop {
13469        // `count` is 1..=7 here; `& 7` is the own bound of the 8-entry table.
13470        tmp = e.decode_decision(data, ctx, base + P2C[count & 7]);
13471        code += 1;
13472        count += 1;
13473        if tmp == 0 || count == 8 {
13474            break;
13475        }
13476    }
13477    if tmp != 0 {
13478        let (v, e2) = cabac_exp_bypass(*e, data, 3);
13479        *e = e2;
13480        code += v + 1;
13481    }
13482    code
13483}
13484
13485/// One `mvd` component (openh264 `ParseMvdInfoCabac`) on a live engine view.
13486/// `ctx_inc` (0/1/2) from the neighbour |mvd| sum. ctxIdxOffset 40 (x) / 47 (y).
13487#[inline(always)]
13488fn mvd_component(e: &mut Eng, data: &[u8], ctx: &mut Ctx, comp: usize, ctx_inc: usize) -> i16 {
13489    let base = 40 + comp * 7; // NEW_CTX_OFFSET_MVD + comp*CTX_NUM_MVD
13490    if e.decode_decision(data, ctx, base + ctx_inc) == 0 {
13491        return 0;
13492    }
13493    let mag = (decode_ueg_mv(e, data, ctx, base + 3) + 1) as i16;
13494    if e.decode_bypass(data) != 0 {
13495        -mag
13496    } else {
13497        mag
13498    }
13499}
13500
13501/// P-slice `mb_type` CABAC (openh264 `ParseMBTypePSliceCabac`). Returns 0..3 = inter
13502/// (P_L0_16x16 / P_16x8 / P_8x16 / P_8x8), 5 = I_4x4, 6..29 = I_16x16, 30 = I_PCM.
13503#[inline(always)]
13504fn mb_type_p_eng(e: &mut Eng, data: &[u8], ctx: &mut Ctx) -> u32 {
13505    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
13506    const S: usize = 11; // NEW_CTX_OFFSET_SKIP; P mb_type contexts hang off it
13507    
13508    'v: {
13509        if e.decode_decision(data, ctx, S + 3) == 0 {
13510            // inter
13511            break 'v if e.decode_decision(data, ctx, S + 4) != 0 {
13512                if e.decode_decision(data, ctx, S + 6) != 0 {
13513                    1
13514                } else {
13515                    2
13516                }
13517            } else if e.decode_decision(data, ctx, S + 5) != 0 {
13518                3
13519            } else {
13520                0
13521            };
13522        }
13523        // intra (prefix bit was 1)
13524        if e.decode_decision(data, ctx, S + 6) == 0 {
13525            break 'v 5; // I_4x4
13526        }
13527        if e.decode_terminate(data) {
13528            break 'v 30; // I_PCM
13529        }
13530        let mut t = 6 + e.decode_decision(data, ctx, S + 7) * 12;
13531        if e.decode_decision(data, ctx, S + 8) != 0 {
13532            t += 4;
13533            if e.decode_decision(data, ctx, S + 8) != 0 {
13534                t += 4;
13535            }
13536        }
13537        t += e.decode_decision(data, ctx, S + 9) << 1;
13538        t += e.decode_decision(data, ctx, S + 9);
13539        t
13540    }
13541}
13542
13543/// I-slice `mb_type` CABAC parse (spec §9.3.2.5 / openh264 `ParseMBTypeISliceCabac`).
13544/// `ctx_inc` = (left MB is I_16x16/non-intra) + (top MB is …), i.e. 0..2; the corner
13545/// MB has no neighbours so `ctx_inc = 0`. Returns the raw mb_type: 0 = I_NxN (I_4x4/
13546/// I_8x8), 1..24 = I_16x16 (pred-mode/cbp packed), 25 = I_PCM.
13547fn parse_mb_type_i_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
13548    const O: usize = 3; // ctxIdxOffset for I-slice mb_type
13549    let (data, mut e, ctx) = cab.view();
13550    let v = 'v: {
13551        if e.decode_decision(data, ctx, O + ctx_inc) == 0 {
13552            break 'v 0; // I_NxN
13553        }
13554        if e.decode_terminate(data) {
13555            break 'v 25; // I_PCM
13556        }
13557        let mut t = 1 + e.decode_decision(data, ctx, O + 3) * 12; // CBP luma: 0 or 12
13558        if e.decode_decision(data, ctx, O + 4) != 0 {
13559            t += 4; // CBP chroma 1 or 2
13560            if e.decode_decision(data, ctx, O + 5) != 0 {
13561                t += 4;
13562            }
13563        }
13564        t += e.decode_decision(data, ctx, O + 6) << 1; // I_16x16 pred mode (2 bins)
13565        t += e.decode_decision(data, ctx, O + 7);
13566        t
13567    };
13568    cab.commit(e);
13569    v
13570}
13571
13572/// One `Intra_4x4` (or `8x8`) pred-mode parse on a live engine view (openh264
13573/// `ParseIntraPredModeLumaCabac`): `prev_intra4x4_pred_mode_flag` (ctx 68) then, if 0,
13574/// `rem_intra4x4_pred_mode` (3 bins at ctx 69). Returns `-1` for "use predicted
13575/// mode", else the 0..7 remainder. The 16 (or 4) reads of a macroblock share ONE view.
13576#[inline(always)]
13577fn intra4x4_pred_mode_eng(e: &mut Eng, data: &[u8], ctx: &mut Ctx) -> i32 {
13578    const IPR: usize = 68;
13579    if e.decode_decision(data, ctx, IPR) == 1 {
13580        return -1; // prev_intra4x4_pred_mode_flag = 1
13581    }
13582    let mut m = e.decode_decision(data, ctx, IPR + 1) as i32;
13583    m |= (e.decode_decision(data, ctx, IPR + 1) as i32) << 1;
13584    m |= (e.decode_decision(data, ctx, IPR + 1) as i32) << 2;
13585    m
13586}
13587
13588/// One `Intra_4x4` (or `8x8`) pred-mode CABAC parse (openh264 `ParseIntraPredModeLuma
13589/// Cabac`): `prev_intra4x4_pred_mode_flag` (ctx 68) then, if 0, `rem_intra4x4_pred_mode`
13590/// (3 bins at ctx 69). Returns `-1` for "use predicted mode", else the 0..7 remainder.
13591
13592/// `intra_chroma_pred_mode` CABAC parse (openh264 `ParseIntraPredModeChromaCabac`):
13593/// TU(cMax=3) — bin0 at ctx `64 + ctx_inc` (ctx_inc from neighbour chroma modes, 0 for
13594/// the corner MB), the rest at ctx 67. Returns the mode 0..3.
13595fn parse_intra_chroma_pred_mode_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
13596    const CIPR: usize = 64;
13597    if cab.decode_decision(CIPR + ctx_inc) == 0 {
13598        return 0;
13599    }
13600    if cab.decode_decision(CIPR + 3) == 0 {
13601        return 1;
13602    }
13603    if cab.decode_decision(CIPR + 3) == 0 {
13604        return 2;
13605    }
13606    3
13607}
13608
13609/// `coded_block_pattern` CABAC parse (openh264 `ParseCbpInfoCabac`), corner-MB variant
13610/// (top/left neighbours unavailable → their terms are 0). ctxIdxOffset 73 (luma) with 4
13611/// z-order 8×8 bins whose ctxInc uses the EARLIER-decoded bits within this MB, then
13612/// chroma bits at 77/81. Returns cbp: bits 0-3 = luma 8×8, bits 4-5 = chroma pattern.
13613pub fn parse_cbp_cabac(cab: &mut crate::cabac::Cabac, top: Option<u8>, left: Option<u8>) -> u32 {
13614    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
13615    const CBP: usize = 73;
13616    // Neighbour terms folded ONCE into two 4-bit masks: bit k of `tz` / `lz` is
13617    // the condTermFlag "the 8x8 block k of that neighbour is NOT coded"
13618    // (unavailable => 0). The six per-bin `Option::map_or` closures this
13619    // replaces re-matched the same two Options for every luma bin.
13620    let tz = top.map_or(0u32, |c| !(c as u32) & 0xF);
13621    let lz = left.map_or(0u32, |c| !(c as u32) & 0xF);
13622    let tc = top.map_or(0u32, |c| (c >> 4) as u32);
13623    let lc = left.map_or(0u32, |c| (c >> 4) as u32);
13624    let (data, mut e, ctx) = cab.view();
13625    // Luma, 4 8x8 blocks in z-order. Top uses cbp bits 2/3, left uses 1/3; a
13626    // block already decoded in THIS macroblock contributes `bin ^ 1`.
13627    let b0 = e.decode_decision(
13628        data,
13629        ctx,
13630        CBP + (((lz >> 1) & 1) + (((tz >> 2) & 1) << 1)) as usize,
13631    );
13632    let b1 = e.decode_decision(
13633        data,
13634        ctx,
13635        CBP + ((b0 ^ 1) + (((tz >> 3) & 1) << 1)) as usize,
13636    );
13637    let b2 = e.decode_decision(
13638        data,
13639        ctx,
13640        CBP + (((lz >> 3) & 1) + ((b0 ^ 1) << 1)) as usize,
13641    );
13642    let b3 = e.decode_decision(data, ctx, CBP + ((b2 ^ 1) + ((b1 ^ 1) << 1)) as usize);
13643    let mut cbp = b0 | (b1 << 1) | (b2 << 2) | (b3 << 3);
13644    // Chroma (4:2:0). ctxInc from neighbour chroma cbp (>>4).
13645    let (ct, cl) = ((tc != 0) as u32, (lc != 0) as u32);
13646    if e.decode_decision(data, ctx, CBP + 4 + (cl + (ct << 1)) as usize) != 0 {
13647        let (ct2, cl2) = ((tc == 2) as u32, (lc == 2) as u32);
13648        let c1 = e.decode_decision(data, ctx, CBP + 8 + (cl2 + (ct2 << 1)) as usize);
13649        cbp |= 1 << (4 + c1);
13650    }
13651    cab.commit(e);
13652    cbp
13653}
13654
13655/// Inlined: `num_ref_active` is loop-invariant at every partition site, so the
13656/// te(v) branch folds and the bit/ue read lands in the caller with no call.
13657#[inline]
13658fn read_ref_idx(r: &mut BitReader, num_ref_active: usize) -> Result<i32, OutOfData> {
13659    if num_ref_active == 2 {
13660        Ok(if r.read_bit()? { 0 } else { 1 }) // te(v): value = !bit
13661    } else {
13662        Ok(r.read_ue()? as i32)
13663    }
13664}
13665
13666/// B-partition prediction direction.
13667#[derive(Clone, Copy, PartialEq)]
13668enum BPred {
13669    L0,
13670    L1,
13671    Bi,
13672}
13673impl BPred {
13674    /// Whether this direction uses reference list `list` (0 or 1).
13675    fn uses(self, list: usize) -> bool {
13676        matches!(
13677            (self, list),
13678            (BPred::L0, 0) | (BPred::L1, 1) | (BPred::Bi, 0) | (BPred::Bi, 1)
13679        )
13680    }
13681}
13682
13683const B16X16: &[(usize, usize, usize, usize)] = &[(0, 0, 16, 16)];
13684const B16X8: &[(usize, usize, usize, usize)] = &[(0, 0, 16, 8), (0, 8, 16, 8)];
13685const B8X16: &[(usize, usize, usize, usize)] = &[(0, 0, 8, 16), (8, 0, 8, 16)];
13686
13687/// A partition region `(x, y, w, h)` in samples.
13688type Region = (usize, usize, usize, usize);
13689
13690/// B `mb_type` 1..=21 → (partition layout, MV-prediction mode 0/1/2 for 16×16/
13691/// 16×8/8×16, per-partition prediction direction) (spec Table 7-14).
13692/// Test-only view of [`b_inter_layout`] for the ENCODER crate: `(mvmode, p0, p1)`
13693/// with pred coded 1 = L0, 2 = L1, 3 = Bi — the encoder's `b_part_mb_type` is the
13694/// exact inverse, so a round-trip over 4..=21 gates the two tables against drift.
13695pub fn b_inter_shape(mb_type: u32) -> (u8, u8, u8) {
13696    let (_, mvmode, preds) = b_inter_layout(mb_type);
13697    let code = |p: BPred| match (p.uses(0), p.uses(1)) {
13698        (true, true) => 3,
13699        (true, false) => 1,
13700        _ => 2,
13701    };
13702    (mvmode, code(preds[0]), code(preds[1]))
13703}
13704
13705fn b_inter_layout(mb_type: u32) -> (&'static [Region], u8, [BPred; 2]) {
13706    use BPred::*;
13707    match mb_type {
13708        1 => (B16X16, 0, [L0, L0]),
13709        2 => (B16X16, 0, [L1, L1]),
13710        3 => (B16X16, 0, [Bi, Bi]),
13711        4 => (B16X8, 1, [L0, L0]),
13712        5 => (B8X16, 2, [L0, L0]),
13713        6 => (B16X8, 1, [L1, L1]),
13714        7 => (B8X16, 2, [L1, L1]),
13715        8 => (B16X8, 1, [L0, L1]),
13716        9 => (B8X16, 2, [L0, L1]),
13717        10 => (B16X8, 1, [L1, L0]),
13718        11 => (B8X16, 2, [L1, L0]),
13719        12 => (B16X8, 1, [L0, Bi]),
13720        13 => (B8X16, 2, [L0, Bi]),
13721        14 => (B16X8, 1, [L1, Bi]),
13722        15 => (B8X16, 2, [L1, Bi]),
13723        16 => (B16X8, 1, [Bi, L0]),
13724        17 => (B8X16, 2, [Bi, L0]),
13725        18 => (B16X8, 1, [Bi, L1]),
13726        19 => (B8X16, 2, [Bi, L1]),
13727        20 => (B16X8, 1, [Bi, Bi]),
13728        _ => (B8X16, 2, [Bi, Bi]), // 21
13729    }
13730}
13731
13732/// Whether a B `sub_mb_type` (1..=12) uses reference list `list`.
13733fn b_sub_uses(st: u32, list: usize) -> bool {
13734    let pred = match st {
13735        1 | 4 | 5 | 10 => 0, // L0
13736        2 | 6 | 7 | 11 => 1, // L1
13737        _ => 2,              // Bi (3, 8, 9, 12)
13738    };
13739    (list == 0 && pred != 1) || (list == 1 && pred != 0)
13740}
13741
13742/// Sub-partition shapes within an 8×8 for a B `sub_mb_type` (1..=12).
13743fn b_sub_parts(st: u32) -> &'static [(usize, usize, usize, usize)] {
13744    match st {
13745        1..=3 => &[(0, 0, 8, 8)],
13746        4 | 6 | 8 => &[(0, 0, 8, 4), (0, 4, 8, 4)],
13747        5 | 7 | 9 => &[(0, 0, 4, 8), (4, 0, 4, 8)],
13748        _ => &[(0, 0, 4, 4), (4, 0, 4, 4), (0, 4, 4, 4), (4, 4, 4, 4)], // 10/11/12
13749    }
13750}
13751
13752/// Sub-macroblock partition layout `(x, y, w, h)` in samples within an 8×8, for
13753/// a P-slice `sub_mb_type` (0 = 8×8, 1 = 8×4, 2 = 4×8, 3 = 4×4).
13754fn sub_mb_partitions(sub_type: u32) -> &'static [(usize, usize, usize, usize)] {
13755    match sub_type {
13756        0 => &[(0, 0, 8, 8)],
13757        1 => &[(0, 0, 8, 4), (0, 4, 8, 4)],
13758        2 => &[(0, 0, 4, 8), (4, 0, 4, 8)],
13759        _ => &[(0, 0, 4, 4), (4, 0, 4, 4), (0, 4, 4, 4), (4, 4, 4, 4)],
13760    }
13761}
13762
13763/// Copy a contiguous `w`x`h` block into a strided destination at `(x0, y0)`.
13764///
13765/// The width is SPECIALISED. Written as a per-pixel loop bounded by a runtime `w`,
13766/// this lowers to a bounds-checked store per pixel — and where it is a row copy of
13767/// runtime length, to a variable-length `memcpy` CALL per row. Both are the same
13768/// codegen trap the ENCODER fixed long ago ("H-17"); the decoder's copy of it was
13769/// never fixed, and it costs the most on exactly the streams a real encoder emits,
13770/// because x264's sub-16x16 partitions call it far more often than our own
13771/// 16x16-dominated bitstreams ever did. Byte-identical to the scalar form.
13772#[inline]
13773fn restride(
13774    dst: &mut [u8],
13775    dst_stride: usize,
13776    x0: usize,
13777    y0: usize,
13778    src: &[u8],
13779    w: usize,
13780    h: usize,
13781) {
13782    macro_rules! rows {
13783        ($n:expr) => {{
13784            for dy in 0..h {
13785                dst[(y0 + dy) * dst_stride + x0..][..$n].copy_from_slice(&src[dy * $n..][..$n]);
13786            }
13787        }};
13788    }
13789    match w {
13790        16 => rows!(16),
13791        8 => rows!(8),
13792        4 => rows!(4),
13793        2 => rows!(2),
13794        _ => {
13795            for dy in 0..h {
13796                dst[(y0 + dy) * dst_stride + x0..][..w].copy_from_slice(&src[dy * w..][..w]);
13797            }
13798        }
13799    }
13800}
13801
13802/// Un-scans an 8×8 block from frame zig-zag scan order to raster (spec Table 8-12).
13803fn un_scan_8x8(scan: &[i32; 64]) -> [i32; 64] {
13804    const ZZ8: [usize; 64] = [
13805        0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27,
13806        20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
13807        58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
13808    ];
13809    let mut out = [0i32; 64];
13810    for k in 0..64 {
13811        out[ZZ8[k]] = scan[k];
13812    }
13813    out
13814}
13815
13816#[cfg(test)]
13817mod tests {
13818    use super::*;
13819
13820    fn fd(qp: u8, offset: i32) -> FrameDecoder {
13821        FrameDecoder::new(1, 1, qp, offset, Vec::new(), 1, false, false, true)
13822    }
13823
13824    #[test]
13825    fn mb_qp_delta_accumulates_mod_52() {
13826        let mut d = fd(26, 0);
13827        assert_eq!(d.cur_qp, 26, "QPy starts at the slice QP");
13828        d.step_qp(4).unwrap();
13829        assert_eq!(d.cur_qp, 30); // 26 + 4
13830        d.step_qp(-10).unwrap();
13831        assert_eq!(d.cur_qp, 20); // carries from the previous MB, not the slice
13832                                  // Wrap-around: (20 + 40 + 52) % 52 = 112 % 52 = 8.
13833        d.step_qp(40).unwrap();
13834        assert_eq!(d.cur_qp, 8);
13835        // Negative wrap: (8 - 20 + 52) % 52 = 40.
13836        d.step_qp(-20).unwrap();
13837        assert_eq!(d.cur_qp, 40);
13838    }
13839
13840    #[test]
13841    fn chroma_qp_index_offset_applied_and_clamped() {
13842        // Offset 0 reproduces the bare luma->chroma table (QP30 -> 29).
13843        assert_eq!(fd(0, 0).chroma_qp_for(30), 29);
13844        // Positive offset shifts the table lookup (QP30 + 2 -> table[2] = 31).
13845        assert_eq!(fd(0, 2).chroma_qp_for(30), 31);
13846        // The qPi index is clamped into 0..=51 before the lookup.
13847        assert_eq!(fd(0, -12).chroma_qp_for(5), chroma_qp(0));
13848        assert_eq!(fd(0, 99).chroma_qp_for(40), chroma_qp(51));
13849    }
13850}