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)]
11use alloc::boxed::Box;
12#[allow(unused_imports)]
13use alloc::format;
14#[allow(unused_imports)]
15use alloc::string::{String, ToString};
16#[allow(unused_imports)]
17use alloc::vec;
18#[allow(unused_imports)]
19use alloc::vec::Vec;
20use rusty_h264_common::bit_reader::OutOfData;
21use rusty_h264_common::cavlc::{decode_residual_block_into, read_cbp_inter, read_cbp_intra};
22use rusty_h264_common::inter::{
23    inter_partitions, mc_chroma_padded, mc_luma_padded, predict_mv, predict_partition_mv,
24    MvNeighbor,
25};
26use rusty_h264_common::predict::{
27    add_residual_8x8, chroma8x8_pred, chroma_qp, intra4x4_pred, intra8x8_pred, luma16x16_pred,
28    nnz_raster_from_z, reconstruct_4x4_dc_into, reconstruct_4x4_scan_into, I16Mode,
29    CHROMA_4X4_SCAN_XY, LUMA_4X4_SCAN_XY,
30};
31use rusty_h264_common::transform::{
32    inverse_quant_8x8_dq, inverse_quant_chroma_dc, inverse_quant_chroma_dc_weighted,
33    inverse_quant_luma_dc_scan, Dequant8Qp, DequantQp, DQ8_FLAT, DQ_FLAT,
34};
35use rusty_h264_common::{BitReader, YuvFrame};
36
37/// One frame's motion field, in 4x4-block raster (`mb_w*4` wide).
38///
39/// Captured from any conformant stream this decoder parses — including x264's —
40/// so a harness can compare motion fields between encoders without depending on
41/// external MV-export tooling.
42pub struct MvField {
43    pub mb_w: usize,
44    pub mb_h: usize,
45    pub mv: Vec<(i32, i32)>,
46    pub ref_idx: Vec<i32>,
47    pub inter: Vec<bool>,
48}
49
50/// Frames captured in decode order when `RFF_MV_DUMP=1`. Diagnostic only.
51#[cfg(feature = "std")]
52pub static MV_DUMP: crate::sync::Mutex<Vec<MvField>> = crate::sync::Mutex::new(Vec::new());
53
54/// `RH264_DUMP_MB` -- the per-frame/per-slice conformance-bisection dump.
55///
56/// ROUTED AT BUILD TIME, like the rest of the knob inventory. It was a bare
57/// `knob("RH264_DUMP_MB").is_some()` at four call sites, which is
58/// `std::env::var` PLUS a String allocation on EVERY slice and every DPB add --
59/// and it kept each dump body, with its `eprintln!` formatting machinery and
60/// String building, compiled into the shipping decoder. `deblock` alone carried
61/// four `Stderr::write_fmt`, four `panic_fmt` and thirteen alloc/free calls that
62/// exist only for a dump nobody enables.
63/// `RS_H264_ROUTE_DUMP` -- the per-picture content-route dump. Build-time routed
64/// for the same reason as `dump_mb_on`.
65pub(crate) fn route_dump_on() -> bool {
66    #[cfg(not(feature = "knobs"))]
67    {
68        return false;
69    }
70    #[cfg(feature = "knobs")]
71    {
72        static ON: rusty_h264_common::once::OnceLock<bool> =
73            rusty_h264_common::once::OnceLock::new();
74        *ON.get_or_init(|| rusty_h264_common::knob("RS_H264_ROUTE_DUMP").is_some())
75    }
76}
77
78pub(crate) fn dump_mb_on() -> bool {
79    #[cfg(not(feature = "knobs"))]
80    {
81        return false;
82    }
83    #[cfg(feature = "knobs")]
84    {
85        static ON: rusty_h264_common::once::OnceLock<bool> =
86            rusty_h264_common::once::OnceLock::new();
87        *ON.get_or_init(|| rusty_h264_common::knob("RH264_DUMP_MB").is_some())
88    }
89}
90
91pub fn mv_dump_on() -> bool {
92    #[cfg(not(feature = "knobs"))]
93    {
94        return false;
95    }
96    #[cfg(feature = "knobs")]
97    {
98        static ON: rusty_h264_common::once::OnceLock<bool> =
99            rusty_h264_common::once::OnceLock::new();
100        *ON.get_or_init(|| rusty_h264_common::knob("RFF_MV_DUMP").map_or(false, |v| v != "0"))
101    }
102}
103
104/// Copy filtered MB rows `[prev..mb_rows)` from coded-size planes into a
105/// Frame-MT progress slot's live padded planes, then bump `ready_rows`.
106/// Coarser publish: only advance the watermark every 2 MB rows (and always
107/// on the last row) to cut lock/notify traffic.
108fn publish_filtered_rows_to_slot(
109    slot: &crate::RefFrame,
110    rec_y: &[u8],
111    rec_u: &[u8],
112    rec_v: &[u8],
113    cw: usize,
114    ccw: usize,
115    ch: usize,
116    mb_rows: usize,
117) {
118    if mb_rows == 0 || slot.frozen.get().is_some() {
119        return;
120    }
121    if !crate::row_publish_on() {
122        return;
123    }
124    let mb_h = (ch + 15) / 16;
125    // Batch: defer watermark until even MB rows (or picture end).
126    if mb_rows < mb_h && (mb_rows & 1) != 0 {
127        return;
128    }
129    let Some(live) = &slot.live else {
130        slot.publish_ready_rows(mb_rows * 16);
131        return;
132    };
133    let cch = ch / 2;
134    let prev = slot.ready_rows.load(core::sync::atomic::Ordering::Acquire) / 16;
135    if mb_rows <= prev {
136        return;
137    }
138    let mut py = live.py.write().unwrap();
139    let mut pu = live.pu.write().unwrap();
140    let mut pv = live.pv.write().unwrap();
141    let ls = cw + 2 * crate::LPAD;
142    let cs = ccw + 2 * crate::CPAD;
143    for mr in prev..mb_rows {
144        for dy in 0..16 {
145            let y = mr * 16 + dy;
146            if y >= ch {
147                break;
148            }
149            let src = &rec_y[y * cw..(y + 1) * cw];
150            let dst_y = y + crate::LPAD;
151            // ONE row segment covering pad+picture+pad, then `split_at_mut` and
152            // two fills — the edge reads and the 2*LPAD writes were separate
153            // whole-plane indexes. Same shape as `expand_plane`.
154            let seg = &mut py[dst_y * ls..][..2 * crate::LPAD + cw];
155            let (lpad, rest) = seg.split_at_mut(crate::LPAD);
156            let (mid, rpad) = rest.split_at_mut(cw);
157            mid.copy_from_slice(src);
158            let (Some(&left), Some(&right)) = (mid.first(), mid.last()) else {
159                continue;
160            };
161            lpad.fill(left);
162            rpad[..crate::LPAD].fill(right);
163        }
164        for dy in 0..8 {
165            let cy = mr * 8 + dy;
166            if cy >= cch {
167                break;
168            }
169            for (rec, plane) in [(rec_u, &mut *pu), (rec_v, &mut *pv)] {
170                let src = &rec[cy * ccw..(cy + 1) * ccw];
171                let dst_y = cy + crate::CPAD;
172                let seg = &mut plane[dst_y * cs..][..2 * crate::CPAD + ccw];
173                let (lpad, rest) = seg.split_at_mut(crate::CPAD);
174                let (mid, rpad) = rest.split_at_mut(ccw);
175                mid.copy_from_slice(src);
176                let (Some(&left), Some(&right)) = (mid.first(), mid.last()) else {
177                    continue;
178                };
179                lpad.fill(left);
180                rpad[..crate::CPAD].fill(right);
181            }
182        }
183    }
184    if prev == 0 {
185        // Copy the whole first picture ROW upward instead of walking columns:
186        // `split_at_mut` at the pad boundary proves both sides at once.
187        let (pad, rest) = py.split_at_mut(crate::LPAD * ls);
188        if let Some(first) = rest.get(..ls) {
189            for row in pad.chunks_mut(ls) {
190                row.copy_from_slice(first);
191            }
192        }
193        for plane in [&mut *pu, &mut *pv] {
194            let (pad, rest) = plane.split_at_mut(crate::CPAD * cs);
195            if let Some(first) = rest.get(..cs) {
196                for row in pad.chunks_mut(cs) {
197                    row.copy_from_slice(first);
198                }
199            }
200        }
201    }
202    slot.publish_ready_rows(mb_rows * 16);
203}
204
205/// Reconstructed coded-size planes plus CAVLC `nnz` context grids.
206pub struct FrameDecoder {
207    mb_w: usize,
208    mb_h: usize,
209    /// Slice QP (`SliceQPy`) — the deblock filter's frame-level QP.
210    qp: u8,
211    /// Running luma QP (`QPy`), carried across macroblocks and stepped by each
212    /// `mb_qp_delta` (spec §7.4.5). Equals `qp` on constant-QP streams.
213    cur_qp: u8,
214    /// `chroma_qp_index_offset` from the active PPS (§8.5.8).
215    chroma_qp_offset: i32,
216    cw: usize,
217    ch: usize,
218    ccw: usize,
219    cch: usize,
220    rec_y: Vec<u8>,
221    rec_u: Vec<u8>,
222    rec_v: Vec<u8>,
223    /// Per-macroblock luma QP (`QPy`), for per-edge deblock strength.
224    mb_qp: Vec<u8>,
225    /// First macroblock address of the slice currently being decoded. Neighbors
226    /// with a lower address belong to an earlier slice and are "not available"
227    /// for prediction (spec §8.3/§8.4). Slices are contiguous raster ranges (we
228    /// reject FMO/slice-groups), so address ≥ this ⇔ same slice.
229    slice_first_mb: usize,
230    nnz_y: Vec<u8>,
231    nnz_c: [Vec<u8>; 2],
232    modes_y: Vec<u8>,
233    coded_y: Vec<bool>,
234    /// Per-4×4-block List-0 motion (mv + ref index, `-1` = no L0). For P slices
235    /// this is the only motion; B slices add the List-1 grids below.
236    mv_y: Vec<(i32, i32)>,
237    inter_y: Vec<bool>,
238    ref_idx_y: Vec<i32>,
239    /// Per-4×4-block List-1 motion for B slices (`ref_idx1 = -1` = no L1).
240    mv1: Vec<(i32, i32)>,
241    ref_idx1: Vec<i32>,
242    /// `RefPicList1` and B-slice flags (unused outside B slices).
243    refs1: Vec<crate::Ref>,
244    num_ref_active1: usize,
245    is_b: bool,
246    /// True if the stream's profile permits B-slices (`profile_idc != 66`). When
247    /// false (Baseline / Constrained Baseline), `as_reference_pooled` skips the per-block
248    /// motion (mv/ref_idx/ref_poc) that only B temporal/spatial direct ever reads.
249    b_possible: bool,
250    direct_spatial: bool,
251    nnz_l_cache: [u8; 25],
252    nnz_c_cache: [[u8; 9]; 2],
253    /// Decoded-picture buffer (most-recent first); empty in I-slices. `ref_idx`
254    /// indexes into this list.
255    refs: Vec<crate::Ref>,
256    /// `num_ref_idx_l0_active` for the current slice — drives whether `ref_idx`
257    /// is coded (active > 1) and its te(v)/ue(v) form, independently of how many
258    /// reference pictures actually exist (spec §7.4.5.1, §9.1).
259    num_ref_active: usize,
260    /// `constrained_intra_pred_flag`: when set, intra prediction may only use
261    /// samples from intra-coded neighbors (inter neighbors are "not available").
262    constrained_intra: bool,
263    /// High-profile 4×4 scaling matrices in **raster** order, indexed by
264    /// `[Y-intra, Cb-intra, Cr-intra, Y-inter, Cb-inter, Cr-inter]`. `None` = flat.
265    scaling: Option<[[i32; 16]; 6]>,
266    /// High-profile 8×8 luma scaling matrices in raster order `[Y-intra, Y-inter]`
267    /// (4:2:0 has only these two). `None` = flat.
268    scaling8: Option<[[i32; 64]; 2]>,
269    /// `transform_8x8_mode_flag` from the PPS: enables `transform_size_8x8_flag`.
270    transform_8x8_mode: bool,
271    /// SPS `qpprime_y_zero_transform_bypass_flag` — refusal gate in `step_qp`.
272    transform_bypass: bool,
273    /// GATE 1 router counters (big-oppy-decoder §2): skip MBs and
274    /// residual-coded MBs this picture. Two integer adds per MB, no atomics.
275    route_skip_mbs: u32,
276    route_coded_mbs: u32,
277    /// Per-slice `(first_mb, idc == 2)` in decode order — drives the
278    /// `disable_deblocking_filter_idc == 2` cross-slice-edge suppression.
279    slice_bounds: Vec<(usize, bool)>,
280    /// The CURRENT slice's `disable_deblocking_filter_idc == 2`, latched by
281    /// `set_deblock_params` and recorded per slice by the decode loops.
282    cur_idc2: bool,
283    /// Per-macroblock `transform_size_8x8_flag` (for deblocking: internal 4×4
284    /// luma edges of 8×8-transform MBs are not filtered).
285    mb_t8x8: Vec<bool>,
286    // ---- Row-interleaved deblocking state (docs/row-interleave-plan.md) ----
287    /// Per-MB boundary strengths, filled row-by-row as decode completes rows.
288    bs_frame: Vec<rusty_h264_common::deblock::MbBs>,
289    /// Rows whose bS is derived (watermark).
290    bs_rows: usize,
291    /// Rows already deblock-FILTERED (watermark; R3).
292    flt_rows: usize,
293    /// Two-row rolling window of packed records (prev = row r-1, cur = row r).
294    pk_prev: Vec<rusty_h264_common::deblock::MbPack>,
295    pk_cur: Vec<rusty_h264_common::deblock::MbPack>,
296    /// Transform-block coded mask (nnz with the 8x8 OR applied), filled per row.
297    nnz_dbr: Vec<u8>,
298    /// Unfiltered bottom rows of the last-filtered MB row (intra reads these).
299    bak_y: Vec<u8>,
300    bak_u: Vec<u8>,
301    bak_v: Vec<u8>,
302    /// Entropy-decouple: deferred pixel jobs + the per-slice activation flag
303    /// (CABAC slices only — the CAVLC loop has no flush hooks).
304    edc_jobs: Vec<EdcJob>,
305    /// Recycled job boxes (see `take_pinter_job`): a coded inter macroblock
306    /// used to malloc, fill, memcpy and free a 2.8 KB box every time.
307    edc_job_pool: Vec<Box<PInterJob>>,
308    edc_nores_pool: Vec<Box<PInterNoResJob>>,
309    /// Coefficient planes for the B and intra arms (which reconstruct inline,
310    /// so no job carries them): taken for a macroblock, zeroed per CODED block
311    /// by the parse, put back after recon. `None` only while in use.
312    scratch_luma: Option<Box<[[i32; 16]; 16]>>,
313    scratch_luma8: Option<Box<[[i32; 64]; 4]>>,
314    scratch_cac: Option<Box<[[[i32; 16]; 4]; 2]>>,
315    edc_active: bool,
316    // ---- E2: the worker-thread plumbing (all None outside a threaded slice) ----
317    edc_tx: Option<crate::sync::mpsc::SyncSender<EdcMsg>>,
318    edc_ctx_rx: Option<crate::sync::mpsc::Receiver<PixelCtx>>,
319    edc_back_tx: Option<crate::sync::mpsc::Sender<PixelCtx>>,
320    /// While the parse thread holds the pixel context for an intra macroblock
321    /// (planes moved into `self`), the rest of the context parks here.
322    edc_parked: Option<PixelCtx>,
323    /// E3: while parsing a B macroblock in threaded mode, its MC regions
324    /// accumulate here instead of executing (the pixel side is the worker's).
325    edc_regions: Option<Vec<BRegion>>,
326    /// Measurement only: previous full-MB single-rect direct (x, y, refi0,
327    /// refi1, m0, m1) for run-adjacency counting. No decode behavior.
328    bsk_last: Option<(usize, usize, i32, i32, (i32, i32), (i32, i32))>,
329    /// D10: jobs accumulated for the current row, sent as ONE message.
330    edc_batch: Vec<EdcJob>,
331    /// D12: bits/MB carried in from previous slices (0 = not yet known).
332    bits_per_mb: f64,
333    /// Current slice's deblock parameters (set per slice by the caller).
334    db_ena: bool,
335    db_oa: i32,
336    db_ob: i32,
337    /// Per-macroblock deblock derivation CLASS (`MB_KIND_*`), so the loop filter
338    /// can skip the 24-block neighbourhood gather on macroblocks whose strengths
339    /// are determined by syntax alone. Starts UNSET; anything left UNSET simply
340    /// takes the blind path, so a missed producer site costs speed, not
341    /// correctness. Only classes that are uniform BY SYNTAX are written — notably
342    /// NOT `B_Skip`/`B_Direct`, whose direct-derived motion varies per 4×4.
343    mb_kind: Vec<u8>,
344    /// Explicit weighted-prediction tables, when active for this slice.
345    weights: Option<WeightTable>,
346    /// `frame_mt::row_progress_on()` cached at construction (see
347    /// wait_refs_for_mb).
348    row_wait: bool,
349    /// Colocated-picture fast-probe cache (set_b_context): true when refs1[0]
350    /// is frozen (1T), short-term, with motion grids — col_zero then skips
351    /// the Option + live + long_term + w4 branch chain per probe.
352    col_ok: bool,
353    col_w4: usize,
354    /// Measurement knobs cached at construction — these were OnceLock derefs
355    /// on EVERY skip macroblock (no_bskipfast per B skip, no_runmv per P
356    /// skip); one field test now.
357    /// rowdb_on()/rowhook_eager() cached at construction — these were atomic
358    /// loads inside row_hook_at on EVERY macroblock.
359    /// MEASUREMENT KNOB, cached. `kind_loads()` is a `OnceLock` deref and was
360    /// evaluated as a MATCH GUARD on every Skip/InterUniform macroblock in
361    /// `derive_bs_row` — the dominant class. The census (DBSDERIVE kindarm=0 on
362    /// 6/6 corpus streams) shows the arm it guards never fires by default, so
363    /// every one of those derefs was pure tax. Same treatment `k_rowdb` /
364    /// `k_roweager` already had.
365    /// True once ANY macroblock in this picture used the 8x8 transform. The
366    /// `nnz_dbr` row copy + the per-row t8 scan in `derive_bs_row` exist ONLY
367    /// to apply the 8x8 coded-status OR; with no t8 macroblock `nnz_dbr` is a
368    /// byte-for-byte copy of `nnz_y`, so both are skipped and the derivation
369    /// reads `nnz_y` directly. Census: t8mb=0 on every cavlc/main stream.
370    any_t8: bool,
371    /// True once any slice in this picture carries
372    /// `disable_deblocking_filter_idc == 2`. Replaces a per-ROW
373    /// `slice_bounds.iter().any(..)` scan (census: idc2row=0 on the corpus).
374    any_idc2: bool,
375    /// Raster address whose P_Skip MV is FORCED (0,0): the previous
376    /// decode_p_skip was at addr-1 and committed (0,0), so this MB's left
377    /// neighbor either fires §8.4.1.1's zero-MV rule (in-slice: ref 0,
378    /// (0,0)) or is unavailable (out-of-slice — same rule). Interior MBs of
379    /// a skip run skip the 3-neighbor gather entirely.
380    skip_zero_next: usize,
381    /// Pending B_Skip grid+recon span: (mb row, x0, n, kind) of CONSECUTIVE
382    /// fast B_Skips with IDENTICAL committed values, deferred and range-
383    /// filled/band-reconned at flush. Flushed before ANY grid or pixel
384    /// reader runs — see span_flush callers.
385    bzspan: Option<(usize, usize, usize, BzKind)>,
386    /// Pending P_Skip (0,0) grid-commit span — the P mirror of `bzspan`
387    /// (values: ref 0 list-0, mv (0,0), inter, coded, DC mode, kind SKIP).
388    /// The bool records whether the RECON is deferred too (1T, identity/no
389    /// weights) — decided at push time, not re-derived at flush (begin_slice
390    /// may have changed the fields in between).
391    pzspan: Option<(usize, usize, usize, bool)>,
392    /// Per-picture bitmap: MB decoded as a ref0/(0,0)-both-lists zero-bi fast
393    /// B_Skip. Never cleared mid-picture — false only ever means "derive
394    /// normally" — so no MB path carries a clearing duty.
395    bzero: Vec<bool>,
396    /// Pooled CABAC slice scratch — refilled (not reallocated) at slice entry;
397    /// carried across pictures via `GridPool`. Taken out of `self` at
398    /// `decode_slice_cabac_inner` entry and put back at its normal exit (an
399    /// error exit simply forfeits the pooled allocation).
400    sc_cat: Vec<u8>,
401    sc_cbp: Vec<u8>,
402    sc_cmode: Vec<i32>,
403    sc_nzc: Vec<[u8; 24]>,
404    sc_cbfdc: Vec<u16>,
405    sc_skip: Vec<bool>,
406    sc_ref: Vec<[i8; 16]>,
407    sc_mvd: Vec<[[i16; 2]; 16]>,
408    sc_ref1: Vec<[i8; 16]>,
409    sc_mvd1: Vec<[[i16; 2]; 16]>,
410    sc_direct: Vec<bool>,
411    /// Lazily cached `implicit_weights(0, 0)` — slice-constant (POCs of
412    /// refs[0]/refs1[0] and cur don't change within a slice). Reset in
413    /// set_b_context.
414    iw00: Option<Option<(i32, i32)>>,
415    /// Cached `weights.list_identity(0)` — all list-0 refs identity; gates the
416    /// whole weight_partition pass for P inter (one choke point, all sites).
417    weights_l0id: bool,
418    /// Cached `weights.ref0_identity()` — identity tables (the x264 weightp=2
419    /// common case) make every ref-0 weighting pass an exact no-op, and the
420    /// skip fast paths check this per MB, so it is computed once per slice.
421    weights_id0: bool,
422    /// Current picture's `PicOrderCnt` (for temporal direct + implicit weighting).
423    cur_poc: i32,
424    /// `weighted_bipred_idc` (0 = none/average, 1 = explicit, 2 = implicit).
425    weighted_bipred_idc: u8,
426    /// `direct_8x8_inference_flag` (B direct co-located sub-block selection).
427    direct_8x8_inference: bool,
428    /// Frame-MT Phase B: shared progress Arc for the picture being decoded.
429    progress: Option<crate::Ref>,
430    /// Slice-stable ref→POC tables for bS (≤16 entries). `derive_bs_row` used
431    /// to `collect()` these every row — same values for the whole slice.
432    ref_poc0: Vec<i32>,
433    ref_poc1: Vec<i32>,
434}
435
436/// Explicit weighted-prediction tables (spec §7.4.3.2 / §8.4.2.3.2). Per
437/// reference list, per ref index: a luma `(weight, offset)` and two chroma
438/// `(weight, offset)` (Cb, Cr). `log2` denominators are shared.
439#[derive(Clone, Default)]
440pub struct WeightTable {
441    pub luma_log2_denom: i32,
442    pub chroma_log2_denom: i32,
443    /// `[list][ref_idx] = (weight, offset)`.
444    pub luma: [Vec<(i32, i32)>; 2],
445    /// `[list][ref_idx][cb=0/cr=1] = (weight, offset)`.
446    pub chroma: [Vec<[(i32, i32); 2]>; 2],
447}
448
449impl WeightTable {
450    /// True when list-0 ref-0 carries identity weights (w == 1<<denom, offset 0)
451    /// for luma and both chroma planes. x264's weightp=2 puts a pred_weight_table
452    /// in EVERY P slice, but outside fades the entries are identity — and the
453    /// identity weight is an EXACT no-op ((s*2^d + 2^(d-1))>>d + 0 == s), so a
454    /// P_Skip recon may bypass the weighting pass byte-identically.
455    fn ref0_identity(&self) -> bool {
456        self.luma[0]
457            .first()
458            .is_some_and(|&(w, o)| w == 1 << self.luma_log2_denom && o == 0)
459            && self.chroma[0].first().is_some_and(|c| {
460                c.iter()
461                    .all(|&(w, o)| w == 1 << self.chroma_log2_denom && o == 0)
462            })
463    }
464
465    /// True when EVERY entry of `list` (all refs, luma + both chroma) is the
466    /// identity weight — the x264 weightp=2 shape outside fades. Identity is
467    /// an exact per-pixel no-op (see ref0_identity), so the whole per-ref
468    /// weighting pass may be skipped for this list.
469    fn list_identity(&self, list: usize) -> bool {
470        self.luma[list]
471            .iter()
472            .all(|&(w, o)| w == 1 << self.luma_log2_denom && o == 0)
473            && self.chroma[list].iter().all(|c| {
474                c.iter()
475                    .all(|&(w, o)| w == 1 << self.chroma_log2_denom && o == 0)
476            })
477    }
478
479    /// Applies a single-list (uni-prediction) luma weight (spec §8.4.2.3.2).
480    /// Resolves a list/ref slot's (weight, offset) ONCE. `list` is 0..1 and the
481    /// fallback is the IDENTITY weight, which is what an absent entry means.
482    #[inline]
483    fn luma_wo(&self, list: usize, refi: usize) -> (i32, i32) {
484        self.luma[list & 1]
485            .get(refi)
486            .copied()
487            .unwrap_or((1 << self.luma_log2_denom, 0))
488    }
489
490    /// Ditto for chroma component `cc`.
491    #[inline]
492    fn chroma_wo(&self, list: usize, refi: usize, cc: usize) -> (i32, i32) {
493        self.chroma[list & 1]
494            .get(refi)
495            .map(|c| c[cc & 1])
496            .unwrap_or((1 << self.chroma_log2_denom, 0))
497    }
498
499    fn apply_luma(&self, sample: u8, list: usize, refi: usize) -> u8 {
500        let (w, o) = self.luma_wo(list, refi);
501        let lwd = self.luma_log2_denom;
502        let v = if lwd >= 1 {
503            ((sample as i32 * w + (1 << (lwd - 1))) >> lwd) + o
504        } else {
505            sample as i32 * w + o
506        };
507        v.clamp(0, 255) as u8
508    }
509
510    /// Applies a single-list (uni-prediction) chroma weight for component `cc`.
511    fn apply_chroma(&self, sample: u8, list: usize, refi: usize, cc: usize) -> u8 {
512        let (w, o) = self.chroma_wo(list, refi, cc);
513        let cwd = self.chroma_log2_denom;
514        let v = if cwd >= 1 {
515            ((sample as i32 * w + (1 << (cwd - 1))) >> cwd) + o
516        } else {
517            sample as i32 * w + o
518        };
519        v.clamp(0, 255) as u8
520    }
521}
522
523/// Why a macroblock could not be decoded.
524#[derive(Debug, Clone, PartialEq, Eq)]
525pub enum MbError {
526    Truncated,
527    Unsupported(&'static str),
528}
529
530impl From<OutOfData> for MbError {
531    fn from(_: OutOfData) -> Self {
532        MbError::Truncated
533    }
534}
535
536/// Recycled per-picture scratch grids.
537///
538/// `FrameDecoder::new` used to allocate ~1.65 MB of frame-wide grids for EVERY
539/// coded picture and drop them when the picture finished. The sampled profiler
540/// prices that (stage `dec-setup`) at 6.7% of decode — larger than dequant,
541/// reconstruct and intra prediction combined, and none of it is codec work.
542///
543/// Two costs are being paid, and the allocation is the bigger one. A ~460 KB
544/// `Vec` goes straight to the OS, so every page is a fresh zero page and the
545/// decoder takes a soft page fault on FIRST TOUCH of each 4 KB — a cost charged
546/// to whatever per-macroblock stage happens to touch it first, not to the
547/// allocation. Handing the same buffers back keeps the pages mapped and warm.
548///
549/// The initialising fill is NOT skipped: these grids are read as neighbour
550/// context (`modes_y` must read 2/DC, `ref_idx_y` must read -1) before every
551/// block that writes them, so a stale value from the previous picture is a
552/// correctness bug, not a performance trade. `clear()` + `resize()` keeps the
553/// fill and drops only the allocation.
554///
555/// The reconstruction planes are deliberately NOT pooled: `into_frame` MOVES
556/// them out as the caller's output frame, so there is nothing to hand back.
557#[derive(Default)]
558pub struct GridPool {
559    /// Deferred-job boxes carried across pictures (see `take_pinter_job`).
560    job_pool: Vec<Box<PInterJob>>,
561    nores_pool: Vec<Box<PInterNoResJob>>,
562    /// D12: running bits-per-macroblock of decoded slices, the E2 dispatch's
563    /// density signal. Lives here because `GridPool` is the only state that
564    /// survives a picture (`FrameDecoder` is rebuilt per picture).
565    bits_per_mb: f64,
566    mb_qp: Vec<u8>,
567    bs_frame: Vec<rusty_h264_common::deblock::MbBs>,
568    pk_prev: Vec<rusty_h264_common::deblock::MbPack>,
569    pk_cur: Vec<rusty_h264_common::deblock::MbPack>,
570    nnz_dbr: Vec<u8>,
571    bak_y: Vec<u8>,
572    bak_u: Vec<u8>,
573    bak_v: Vec<u8>,
574    nnz_y: Vec<u8>,
575    nnz_c0: Vec<u8>,
576    nnz_c1: Vec<u8>,
577    modes_y: Vec<u8>,
578    coded_y: Vec<bool>,
579    mv_y: Vec<(i32, i32)>,
580    inter_y: Vec<bool>,
581    ref_idx_y: Vec<i32>,
582    mv1: Vec<(i32, i32)>,
583    ref_idx1: Vec<i32>,
584    mb_t8x8: Vec<bool>,
585    mb_kind: Vec<u8>,
586    bzero: Vec<bool>,
587    // CABAC slice scratch (D13 follow-through): these were fresh `vec![..]`
588    // allocations at EVERY slice entry — ~407 KB per P slice, ~700 KB per B
589    // slice at 720p, the same fresh-page class GridPool exists to kill.
590    sc_cat: Vec<u8>,
591    sc_cbp: Vec<u8>,
592    sc_cmode: Vec<i32>,
593    sc_nzc: Vec<[u8; 24]>,
594    sc_cbfdc: Vec<u16>,
595    sc_skip: Vec<bool>,
596    sc_ref: Vec<[i8; 16]>,
597    sc_mvd: Vec<[[i16; 2]; 16]>,
598    sc_ref1: Vec<[i8; 16]>,
599    sc_mvd1: Vec<[[i16; 2]; 16]>,
600    sc_direct: Vec<bool>,
601    // D24 (inline-execution.md 11.10): the ref-POC mirrors were the only
602    // per-picture Vecs NOT recycled - a fresh alloc + collect per picture each.
603    ref_poc0: Vec<i32>,
604    ref_poc1: Vec<i32>,
605}
606
607/// Reuse `v`'s allocation for `n` copies of `val`. Identical OBSERVABLE result to
608/// `vec![val; n]`; differs only in that it reuses the existing allocation when the
609/// capacity already suffices.
610#[inline]
611fn refill<T: Clone>(mut v: Vec<T>, n: usize, val: T) -> Vec<T> {
612    v.clear();
613    v.resize(n, val);
614    v
615}
616
617impl FrameDecoder {
618    /// Non-pooled ctor — tests only; production always enters via `with_pool`
619    /// (`GridPool` recycling, see lib.rs).
620    #[cfg(test)]
621    pub fn new(
622        mb_w: usize,
623        mb_h: usize,
624        qp: u8,
625        chroma_qp_offset: i32,
626        refs: Vec<crate::Ref>,
627        num_ref_active: usize,
628        constrained_intra: bool,
629        transform_8x8_mode: bool,
630        b_possible: bool,
631    ) -> Self {
632        Self::with_pool(
633            mb_w,
634            mb_h,
635            qp,
636            chroma_qp_offset,
637            refs,
638            num_ref_active,
639            constrained_intra,
640            transform_8x8_mode,
641            b_possible,
642            GridPool::default(),
643        )
644    }
645
646    /// As `new`, but reusing a previous picture's grid allocations. See `GridPool`.
647    #[allow(clippy::too_many_arguments)]
648    pub fn with_pool(
649        mb_w: usize,
650        mb_h: usize,
651        qp: u8,
652        chroma_qp_offset: i32,
653        refs: Vec<crate::Ref>,
654        num_ref_active: usize,
655        constrained_intra: bool,
656        transform_8x8_mode: bool,
657        b_possible: bool,
658        pool: GridPool,
659    ) -> Self {
660        let (cw, ch) = (mb_w * 16, mb_h * 16);
661        let (ccw, cch) = (cw / 2, ch / 2);
662        let bits_per_mb = pool.bits_per_mb;
663        let ref_poc0 = {
664            let mut v = pool.ref_poc0;
665            v.clear();
666            v.extend(refs.iter().map(|f| f.pic_poc()));
667            v
668        };
669        Self {
670            mb_w,
671            mb_h,
672            qp,
673            cur_qp: qp,
674            chroma_qp_offset,
675            cw,
676            ch,
677            ccw,
678            cch,
679            rec_y: vec![0; cw * ch],
680            rec_u: vec![0; ccw * cch],
681            rec_v: vec![0; ccw * cch],
682            mb_qp: refill(pool.mb_qp, mb_w * mb_h, qp),
683            slice_first_mb: 0,
684            nnz_y: refill(pool.nnz_y, (mb_w * 4) * (mb_h * 4), 0),
685            nnz_c: [
686                refill(pool.nnz_c0, (mb_w * 2) * (mb_h * 2), 0),
687                refill(pool.nnz_c1, (mb_w * 2) * (mb_h * 2), 0),
688            ],
689            modes_y: refill(pool.modes_y, (mb_w * 4) * (mb_h * 4), 2),
690            coded_y: refill(pool.coded_y, (mb_w * 4) * (mb_h * 4), false),
691            mv_y: refill(pool.mv_y, (mb_w * 4) * (mb_h * 4), (0, 0)),
692            inter_y: refill(pool.inter_y, (mb_w * 4) * (mb_h * 4), false),
693            ref_idx_y: refill(pool.ref_idx_y, (mb_w * 4) * (mb_h * 4), -1),
694            mv1: refill(pool.mv1, (mb_w * 4) * (mb_h * 4), (0, 0)),
695            ref_idx1: refill(pool.ref_idx1, (mb_w * 4) * (mb_h * 4), -1),
696            refs1: Vec::new(),
697            num_ref_active1: 0,
698            is_b: false,
699            b_possible,
700            direct_spatial: true,
701            nnz_l_cache: [0x80; 25],
702            nnz_c_cache: [[0x80; 9]; 2],
703            refs,
704            num_ref_active,
705            constrained_intra,
706            scaling: None,
707            scaling8: None,
708            transform_8x8_mode,
709            transform_bypass: false,
710            route_skip_mbs: 0,
711            route_coded_mbs: 0,
712            slice_bounds: Vec::new(),
713            cur_idc2: false,
714            mb_t8x8: refill(pool.mb_t8x8, mb_w * mb_h, false),
715            bs_frame: refill(pool.bs_frame, mb_w * mb_h, Default::default()),
716            bs_rows: 0,
717            flt_rows: 0,
718            pk_prev: {
719                let mut v = pool.pk_prev;
720                v.clear();
721                v
722            },
723            pk_cur: {
724                let mut v = pool.pk_cur;
725                v.clear();
726                v
727            },
728            nnz_dbr: refill(pool.nnz_dbr, (mb_w * 4) * (mb_h * 4), 0),
729            bak_y: refill(pool.bak_y, cw, 0),
730            bak_u: refill(pool.bak_u, ccw, 0),
731            bak_v: refill(pool.bak_v, ccw, 0),
732            edc_jobs: Vec::new(),
733            edc_job_pool: pool.job_pool,
734            edc_nores_pool: pool.nores_pool,
735            scratch_luma: None,
736            scratch_luma8: None,
737            scratch_cac: None,
738            edc_active: false,
739            edc_tx: None,
740            edc_ctx_rx: None,
741            edc_back_tx: None,
742            edc_parked: None,
743            edc_regions: None,
744            bsk_last: None,
745            edc_batch: Vec::new(),
746            bits_per_mb,
747            db_ena: false,
748            db_oa: 0,
749            db_ob: 0,
750            mb_kind: refill(
751                pool.mb_kind,
752                mb_w * mb_h,
753                rusty_h264_common::deblock::MB_KIND_UNSET,
754            ),
755            weights: None,
756            row_wait: crate::row_progress_on(),
757            col_ok: false,
758            col_w4: 0,
759            any_t8: false,
760            any_idc2: false,
761            skip_zero_next: usize::MAX,
762            bzero: refill(pool.bzero, mb_w * mb_h, false),
763            sc_cat: pool.sc_cat,
764            sc_cbp: pool.sc_cbp,
765            sc_cmode: pool.sc_cmode,
766            sc_nzc: pool.sc_nzc,
767            sc_cbfdc: pool.sc_cbfdc,
768            sc_skip: pool.sc_skip,
769            sc_ref: pool.sc_ref,
770            sc_mvd: pool.sc_mvd,
771            sc_ref1: pool.sc_ref1,
772            sc_mvd1: pool.sc_mvd1,
773            sc_direct: pool.sc_direct,
774            bzspan: None,
775            pzspan: None,
776            iw00: None,
777            weights_id0: false,
778            weights_l0id: false,
779            cur_poc: 0,
780            weighted_bipred_idc: 0,
781            direct_8x8_inference: false,
782            progress: None,
783            ref_poc0,
784            ref_poc1: {
785                let mut v = pool.ref_poc1;
786                v.clear();
787                v
788            },
789        }
790    }
791
792    fn refresh_ref_pocs(&mut self) {
793        self.ref_poc0.clear();
794        self.ref_poc0.extend(self.refs.iter().map(|f| f.pic_poc()));
795        self.ref_poc1.clear();
796        self.ref_poc1.extend(self.refs1.iter().map(|f| f.pic_poc()));
797    }
798
799    /// Frame-MT Phase B: attach the shared progress Arc for row watermarks.
800    pub fn set_progress_slot(&mut self, slot: crate::Ref) {
801        self.progress = Some(slot);
802    }
803
804    /// Wait until every L0/L1 ref has enough ready luma rows for MB row `mb_y`
805    /// (pad conservatively for MV overshoot).
806    #[inline(always)]
807    fn wait_refs_for_mb(&self, mb_y: usize) {
808        // Cached at construction: in 1T (row-progress off, the default) this
809        // is one field test instead of a fn call + OnceLock read per MB.
810        if !self.row_wait {
811            return;
812        }
813        let need = crate::RefFrame::rows_needed_for_mb(mb_y, self.ch);
814        crate::RefFrame::set_mc_row_need(mb_y, self.ch);
815        for r in self.refs.iter().chain(self.refs1.iter()) {
816            // Only pay the wait when the ref may still be in-flight (Phase B).
817            if r.live.is_some() && r.frozen.get().is_none() {
818                r.wait_ready_rows(need);
819            }
820        }
821    }
822
823    fn publish_progress(&mut self) {
824        let Some(slot) = &self.progress else {
825            return;
826        };
827        // Under EDC the worker publishes (PixelCtx::publish_progress_rows).
828        if self.edc_tx.is_some() {
829            return;
830        }
831        // Cached field, not the knob function: this runs per ROW.
832        if !rowdb_on() || !self.db_ena || self.flt_rows == 0 {
833            return;
834        }
835        publish_filtered_rows_to_slot(
836            slot,
837            &self.rec_y,
838            &self.rec_u,
839            &self.rec_v,
840            self.cw,
841            self.ccw,
842            self.ch,
843            self.flt_rows,
844        );
845    }
846
847    /// Sets the explicit weighted-prediction tables for this slice.
848    pub fn set_weights(&mut self, weights: WeightTable) {
849        self.weights_id0 = weights.ref0_identity();
850        self.weights_l0id = weights.list_identity(0);
851        self.weights = Some(weights);
852    }
853
854    /// Applies explicit uni-prediction weighting to a motion-compensated partition
855    /// (luma `pred_y` region + the two chroma planes), if weighting is active.
856    /// `list` is the reference list and `refi` the partition's reference index.
857    fn weight_partition(
858        &self,
859        pred_y: &mut [u8; 256],
860        c_pred: &mut [[u8; 64]; 2],
861        list: usize,
862        refi: usize,
863        rx: usize,
864        ry: usize,
865        rw: usize,
866        rh: usize,
867    ) {
868        let Some(wt) = &self.weights else { return };
869        // Identity list-0 table (x264 weightp=2 outside fades): the whole pass
870        // is an exact per-pixel no-op — skip it. RS_H264_NO_SKIPFP restores it
871        // for paired A/B (same knob family as the P_Skip weight skip).
872        if list == 0 && self.weights_l0id && !no_skipfp() {
873            edcstat::bump(&edcstat::WP_SKIPPED, 1);
874            return;
875        }
876        // REFUTED, reverted: row-slicing these loops measured +28% instructions
877        // on `weight_partition` (the extents are runtime values, so the slice
878        // bounds cost more than the per-sample checks they replaced). The real
879        // win for this function was hoisting the identity test to its CALLERS,
880        // which stands.
881        // RESOLVE ONCE, APPLY MANY. `apply_luma` re-read `self.luma[list][refi]`
882        // — an array index, a Vec deref and an element index, two of them bounds
883        // checked — on EVERY sample, up to 256 luma plus 128 chroma per call.
884        // The weight is a property of the partition, not of the pixel. (The loop
885        // SHAPE is untouched: row-slicing it is refuted above.)
886        // MASKED, not row-sliced. `pred_y` is a fixed `[u8; 256]` and `c_pred` a
887        // `[[u8; 64]; 2]`, so `& 255` / `& 63` are no-ops that prove the index
888        // outright — where SLICING these loops cost +28% (the extents are runtime
889        // values, so the slice bounds outweigh the checks). Same lesson as
890        // `luma_centre`: the refutation was of one SHAPE, not of the goal.
891        let (lw, lo) = wt.luma_wo(list, refi);
892        weight_block(pred_y, 16, rx, ry, rw, rh, lw, lo, wt.luma_log2_denom);
893        let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
894        for cc in 0..2 {
895            let (cw, co) = wt.chroma_wo(list, refi, cc);
896            weight_block(
897                &mut c_pred[cc & 1],
898                8,
899                crx,
900                cry,
901                crw,
902                crh,
903                cw,
904                co,
905                wt.chroma_log2_denom,
906            );
907        }
908    }
909
910    /// Sets the High-profile scaling matrices (raster order: six 4×4 lists, two
911    /// 8×8 luma lists). The caller un-zig-zags the SPS lists. Flat is the default.
912    /// GATE 1 router counters: (skip MBs, residual-coded MBs) this picture.
913    pub fn route_counters(&self) -> (u32, u32) {
914        (self.route_skip_mbs, self.route_coded_mbs)
915    }
916
917    /// SPS `qpprime_y_zero_transform_bypass_flag` — see [`Self::step_qp`].
918    pub fn set_transform_bypass(&mut self, on: bool) {
919        self.transform_bypass = on;
920    }
921
922    pub fn set_scaling(&mut self, scaling: [[i32; 16]; 6], scaling8: [[i32; 64]; 2]) {
923        self.scaling = Some(scaling);
924        self.scaling8 = Some(scaling8);
925    }
926
927    /// Dequantizes a 4×4 AC block with scaling list `list` (flat if none active).
928    /// Per-(qp, list) dequant constants for the fused scan-order kernel: the flat
929    /// table at zero cost, or the scaling-list build once per macroblock.
930    #[inline]
931    fn dq_const(&self, qp: u8, list: usize) -> DequantQp {
932        match &self.scaling {
933            Some(s) => DequantQp::weighted(qp, &s[list]),
934            None => DQ_FLAT[(qp as usize).min(51)],
935        }
936    }
937
938    /// Single-coefficient twin of `dequant` for position 0 (DC-only fast path).
939    fn dequant_dc4(&self, level: i32, qp: u8, list: usize) -> i32 {
940        rusty_h264_common::transform::dequantize_dc4(
941            level,
942            qp,
943            self.scaling.as_ref().map(|s| s[list][0]),
944        )
945    }
946
947    /// Inverse-quantizes a chroma DC block with scaling list `list`'s DC weight.
948    fn dequant_chroma_dc(&self, levels: &[i32; 4], qp: u8, list: usize) -> [i32; 4] {
949        match &self.scaling {
950            Some(s) => inverse_quant_chroma_dc_weighted(levels, qp, s[list][0]),
951            None => inverse_quant_chroma_dc(levels, qp),
952        }
953    }
954
955    /// Sets the B-slice context for the slice about to be decoded: `RefPicList1`,
956    /// its active count, and the direct-mode flag.
957    #[allow(clippy::too_many_arguments)]
958    pub fn set_b_context(
959        &mut self,
960        refs1: Vec<crate::Ref>,
961        num_ref_active1: usize,
962        direct_spatial: bool,
963        cur_poc: i32,
964        weighted_bipred_idc: u8,
965        direct_8x8_inference: bool,
966    ) {
967        self.is_b = true;
968        self.refs1 = refs1;
969        self.num_ref_active1 = num_ref_active1;
970        self.direct_spatial = direct_spatial;
971        self.cur_poc = cur_poc;
972        self.weighted_bipred_idc = weighted_bipred_idc;
973        self.direct_8x8_inference = direct_8x8_inference;
974        self.iw00 = None; // slice-scoped cache: lists/POC may have changed
975                          // Colocated fast-probe cache (see col_zero / col_zero_fast).
976        self.col_ok = self
977            .refs1
978            .first()
979            .is_some_and(|c| c.live.is_none() && !c.long_term && c.w4 != 0);
980        self.col_w4 = self.refs1.first().map_or(0, |c| c.w4);
981        self.refresh_ref_pocs();
982    }
983
984    /// Cached `implicit_weights(0, 0)` — constant within a slice.
985    fn iw00(&mut self) -> Option<(i32, i32)> {
986        // Read the discriminant ONCE. The `is_none()` + `unwrap()` pair tested it
987        // twice and carried an `unwrap_failed` path for a value this very function
988        // had just written. `get_or_insert_with` cannot be used here: the closure
989        // would need `&mut self` while the `Option` is already borrowed.
990        match self.iw00 {
991            Some(v) => v,
992            None => {
993                let v = if self.refs.is_empty() || self.refs1.is_empty() {
994                    None
995                } else {
996                    self.implicit_weights(0, 0)
997                };
998                self.iw00 = Some(v);
999                v
1000            }
1001        }
1002    }
1003
1004    /// Steps the running luma QP by a `mb_qp_delta` (spec §7.4.5, 8-bit depth):
1005    /// `QPy = (QPy_prev + delta + 52) % 52`.
1006    /// Steps QPy by `mb_qp_delta` (spec §7.4.5 modulo). Called exactly for
1007    /// residual-coded macroblocks (mb_qp_delta presence ⇔ cbp != 0 or I_16x16),
1008    /// which makes it the one chokepoint for the transform-bypass refusal:
1009    /// with `qpprime_y_zero_transform_bypass_flag` set and QP'Y == 0 the
1010    /// residual is LOSSLESS-bypassed (no transform, no quant — and DPCM intra
1011    /// forms), which this decoder does not implement. Refusing here is loud
1012    /// and exact: all-PCM lossless streams (no mb_qp_delta) still decode.
1013    fn step_qp(&mut self, delta: i32) -> Result<(), MbError> {
1014        // Router counter: step_qp fires exactly once per residual-coded MB.
1015        self.route_coded_mbs += 1;
1016        self.cur_qp = (self.cur_qp as i32 + delta + 52).rem_euclid(52) as u8;
1017        if self.transform_bypass && self.cur_qp == 0 {
1018            return Err(MbError::Unsupported(
1019                "transform-bypass (lossless) macroblock",
1020            ));
1021        }
1022        Ok(())
1023    }
1024
1025    /// Maps a luma QP to its chroma QP, applying `chroma_qp_index_offset`
1026    /// (spec §8.5.8): `QPc = qpc_table(Clip3(0, 51, QPy + offset))`.
1027    fn chroma_qp_for(&self, qp_y: u8) -> u8 {
1028        let qpi = (qp_y as i32 + self.chroma_qp_offset).clamp(0, 51) as u8;
1029        chroma_qp(qpi)
1030    }
1031
1032    /// Resets per-slice state before decoding a continuation slice of the same
1033    /// picture: the running QP (each slice carries its own `slice_qp`) and the
1034    /// reference list (each slice may reorder it).
1035    pub fn begin_slice(&mut self, slice_qp: u8, refs: Vec<crate::Ref>, num_ref_active: usize) {
1036        self.cur_qp = slice_qp;
1037        self.qp = slice_qp;
1038        self.refs = refs;
1039        self.num_ref_active = num_ref_active;
1040        self.weights = None; // re-set per slice if a pred_weight_table is present
1041        self.weights_id0 = false;
1042        self.weights_l0id = false;
1043        self.refresh_ref_pocs();
1044    }
1045
1046    /// Whether the neighbor macroblock at `(nbx, nby)` is in the slice currently
1047    /// being decoded (address ≥ the slice's first MB). For single-slice pictures
1048    /// `slice_first_mb == 0`, so this is always true and prediction is unchanged.
1049    #[inline]
1050    fn nbr_in_slice(&self, nbx: usize, nby: usize) -> bool {
1051        nby * self.mb_w + nbx >= self.slice_first_mb
1052    }
1053
1054    /// Whether the neighbor 4×4 block at `(nbx, nby)` may contribute to intra
1055    /// prediction. With `constrained_intra_pred`, an inter-coded neighbor is
1056    /// treated as unavailable (spec §8.3.1.2.{1,2}); otherwise always usable.
1057    #[inline]
1058    fn intra_nbr_ok(&self, nbx: usize, nby: usize) -> bool {
1059        // Fallible read: this is inlined at eleven sites across the intra paths,
1060        // and `constrained_intra` short-circuits it on nearly every stream, so
1061        // the check was pure tax on a value that is usually never loaded.
1062        // `unwrap_or(true)` is the conservative direction — an out-of-range
1063        // neighbour reads as inter, i.e. UNAVAILABLE, which is what the
1064        // constrained-intra rule means by a neighbour it cannot use.
1065        !self.constrained_intra
1066            || !self
1067                .inter_y
1068                .get(nby * (self.mb_w * 4) + nbx)
1069                .copied()
1070                .unwrap_or(true)
1071    }
1072
1073    fn mv_neighbors(&self, mb_x: usize, mb_y: usize) -> [MvNeighbor; 3] {
1074        let w4 = self.mb_w * 4;
1075        let get = |avail: bool, bx: isize, by: isize| {
1076            if avail {
1077                let idx = by as usize * w4 + bx as usize;
1078                match (self.mv_y.get(idx), self.ref_idx_y.get(idx)) {
1079                    (Some(&m), Some(&r)) => MvNeighbor {
1080                        available: true,
1081                        mv: m,
1082                        ref_idx: r,
1083                    },
1084                    _ => MvNeighbor::NONE,
1085                }
1086            } else {
1087                MvNeighbor::NONE
1088            }
1089        };
1090        let (bx, by) = (mb_x as isize * 4, mb_y as isize * 4);
1091        let a = get(mb_x > 0 && self.nbr_in_slice(mb_x - 1, mb_y), bx - 1, by);
1092        let b = get(mb_y > 0 && self.nbr_in_slice(mb_x, mb_y - 1), bx, by - 1);
1093        let c = if mb_y > 0 && mb_x + 1 < self.mb_w && self.nbr_in_slice(mb_x + 1, mb_y - 1) {
1094            get(true, bx + 4, by - 1)
1095        } else {
1096            get(
1097                mb_x > 0 && mb_y > 0 && self.nbr_in_slice(mb_x - 1, mb_y - 1),
1098                bx - 1,
1099                by - 1,
1100            )
1101        };
1102        [a, b, c]
1103    }
1104
1105    fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
1106        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
1107        self.mv_neighbors_block_grid(pbx, pby, pwb, 0)
1108    }
1109
1110    fn skip_mv(&self, mb_x: usize, mb_y: usize) -> (i32, i32) {
1111        let [a, b, c] = self.mv_neighbors(mb_x, mb_y);
1112        if !a.available
1113            || !b.available
1114            || (a.ref_idx == 0 && a.mv == (0, 0))
1115            || (b.ref_idx == 0 && b.mv == (0, 0))
1116        {
1117            (0, 0)
1118        } else {
1119            predict_mv(a, b, c, 0)
1120        }
1121    }
1122
1123    fn set_mb_mv(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32), inter: bool, refi: i32) {
1124        // ROW FILLS. This wrote all sixteen cells of three grids one indexed
1125        // store at a time - FORTY-EIGHT separate bounds checks per call - and
1126        // re-evaluated `if inter` inside the inner loop. Each macroblock row is
1127        // four CONTIGUOUS cells, so twelve `fill`s cover it.
1128        let w4 = self.mb_w * 4;
1129        let r = if inter { refi } else { -1 };
1130        for dy in 0..4 {
1131            let a = (mb_y * 4 + dy) * w4 + mb_x * 4;
1132            self.mv_y[a..a + 4].fill(mv);
1133            self.inter_y[a..a + 4].fill(inter);
1134            self.ref_idx_y[a..a + 4].fill(r);
1135        }
1136    }
1137
1138    /// Commit one inter partition's motion into the 4×4 grid (ref 0, 1-ref P).
1139    /// `(rx,ry,rw,rh)` are MB-relative luma pixels; committing before the next
1140    /// partition's prediction is what lets a later partition predict from it.
1141    fn commit_inter_grid(
1142        &mut self,
1143        mb_x: usize,
1144        mb_y: usize,
1145        rx: usize,
1146        ry: usize,
1147        rw: usize,
1148        rh: usize,
1149        mv: (i32, i32),
1150        refi: i8,
1151    ) {
1152        // Row-range fills instead of the per-4x4 scatter: partitions are
1153        // rectangles, so each grid row is one contiguous run (same values).
1154        let w4 = self.mb_w * 4;
1155        let (bx0, bw) = (mb_x * 4 + rx / 4, rw / 4);
1156        for by in ry / 4..ry / 4 + rh / 4 {
1157            let a = (mb_y * 4 + by) * w4 + bx0;
1158            self.mv_y[a..a + bw].fill(mv);
1159            self.inter_y[a..a + bw].fill(true);
1160            self.ref_idx_y[a..a + bw].fill(refi as i32);
1161            self.coded_y[a..a + bw].fill(true);
1162        }
1163    }
1164
1165    /// Per-slice deblock parameters, needed DURING decode by the row-interleave
1166    /// path. `ena` is already resolved against `RFF_ABL_DEBLOCK` by the caller.
1167    pub fn set_deblock_params(&mut self, ena: bool, oa: i32, ob: i32, idc2: bool) {
1168        // Latch: the FIRST disabling slice turns row filtering off for the rest
1169        // of the picture (see `row_hook`); rows already filtered stay counted
1170        // in `flt_rows` and the picture-end tail handles the remainder.
1171        self.db_ena = ena && (self.flt_rows == 0 || self.db_ena);
1172        self.db_oa = oa;
1173        self.db_ob = ob;
1174        self.cur_idc2 = idc2;
1175    }
1176
1177    /// Slice index owning `addr` (slices are raster-contiguous, bounds ascending).
1178    fn slice_of(&self, addr: usize) -> usize {
1179        match self.slice_bounds.binary_search_by(|&(f, _)| f.cmp(&addr)) {
1180            Ok(i) => i,
1181            Err(i) => i - 1,
1182        }
1183    }
1184
1185    /// Derives bS for macroblock row `r` from the just-decoded (hot) grids into
1186    /// `bs_frame`, maintaining the two-row rolling record window (R2 of
1187    /// docs/row-interleave-plan.md).
1188    fn derive_bs_row(&mut self, r: usize) {
1189        // bS derivation reads the motion/nnz grids — flush deferred spans.
1190        self.span_flush();
1191        // Same stage label the in-filter derivation used, so profiles keep
1192        // pricing bS derivation wherever it lives.
1193        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DebDerive);
1194        use rusty_h264_common::deblock::{
1195            derive_mb_kind, derive_mb_records, pack_mb, BlockInfo, MbBs, MbKind,
1196        };
1197        let (mb_w, w4) = (self.mb_w, self.mb_w * 4);
1198        edcstat::bump(&edcstat::DBS_ROWS, 1);
1199        // Transform-block coded mask for this row: raw nnz, then the 8x8 OR for
1200        // t8 macroblocks (spec §8.7: the 8x8 transform's coded status is per 8x8).
1201        //
1202        // T8 GATE. `nnz_dbr` exists ONLY to carry that OR. With no 8x8
1203        // macroblock anywhere in the picture it is a byte-for-byte copy of
1204        // `nnz_y`, so both the 4-row copy and the per-row scan below are pure
1205        // work: the derivation reads `nnz_y` directly instead. Census
1206        // (DBSDERIVE) measured t8mb=0 on EVERY cavlc and main stream while
1207        // nnz_rowcopy ran 10800-16320 sub-rows per decode.
1208        if self.any_t8 {
1209            for br in r * 4..r * 4 + 4 {
1210                let a = br * w4;
1211                self.nnz_dbr[a..a + w4].copy_from_slice(&self.nnz_y[a..a + w4]);
1212            }
1213            edcstat::bump(&edcstat::DBS_NNZ_ROWCOPY, 4);
1214            // WHOLE-MACROBLOCK ROW SLICES. The 2x2-of-2x2 nest did sixteen
1215            // bounds-checked reads and sixteen bounds-checked writes per t8
1216            // macroblock; the same cells are four contiguous runs of four, so
1217            // four slice reads and four slice writes cover them. The OR is over
1218            // bytes, so `a | b | c | d > 0` is exactly the old per-cell `> 0`.
1219            let t8_row_pre = &self.mb_t8x8[r * mb_w..][..mb_w];
1220            for mb_x in 0..mb_w {
1221                if !t8_row_pre[mb_x] {
1222                    continue;
1223                }
1224                edcstat::bump(&edcstat::DBS_T8MB, 1);
1225                let base = r * 4 * w4 + mb_x * 4;
1226                let mut src = [[0u8; 4]; 4];
1227                for (k, row) in src.iter_mut().enumerate() {
1228                    row.copy_from_slice(&self.nnz_y[base + k * w4..][..4]);
1229                }
1230                let mut out = [[0u8; 4]; 4];
1231                for b8 in 0..4usize {
1232                    let (cx, cy) = ((b8 % 2) * 2, (b8 / 2) * 2);
1233                    let any =
1234                        (src[cy][cx] | src[cy][cx + 1] | src[cy + 1][cx] | src[cy + 1][cx + 1]) > 0;
1235                    for sy in 0..2 {
1236                        for sx in 0..2 {
1237                            out[cy + sy][cx + sx] = any as u8;
1238                        }
1239                    }
1240                }
1241                for (k, row) in out.iter().enumerate() {
1242                    self.nnz_dbr[base + k * w4..][..4].copy_from_slice(row);
1243                }
1244            }
1245        }
1246        let info = BlockInfo {
1247            inter: &self.inter_y,
1248            nnz: if self.any_t8 {
1249                &self.nnz_dbr
1250            } else {
1251                &self.nnz_y
1252            },
1253            mv: &self.mv_y,
1254            ref_id: &self.ref_idx_y,
1255            mv1: &self.mv1,
1256            ref_id1: if self.ref_poc1.is_empty() {
1257                &[]
1258            } else {
1259                &self.ref_idx1
1260            },
1261            w4,
1262            t8x8: &self.mb_t8x8,
1263            bs: &[],
1264            poc0: &self.ref_poc0,
1265            poc1: &self.ref_poc1,
1266            kind: &self.mb_kind,
1267        };
1268        let has1 = !info.ref_id1.is_empty();
1269        core::mem::swap(&mut self.pk_prev, &mut self.pk_cur);
1270        self.pk_cur.clear();
1271        // WIN: read the knob at the USE SITE, not through the struct field.
1272        //  is  under cfg(not(knobs)), so LLVM folds
1273        // this to a constant and the kind-loads arm below becomes dead -- which
1274        // is what finally lets  (1543 instrs of Skip/InterUniform/
1275        // Inter gathering the decoder never runs) leave the decode binary.
1276        // Storing a build-time-constant knob in a FIELD erases the constant:
1277        // the field is a runtime value and every arm it guards stays live.
1278        let kl = kind_loads();
1279        // Census gate resolved ONCE per row: `edcstat::on()` is a relaxed atomic
1280        // load and LLVM will not CSE atomic loads, so a per-MB bump costs a load
1281        // each. Same rule as hoisting an A/B arm selector out of the loop under
1282        // test (codec-measurement 15).
1283        let stats = edcstat::on();
1284        // ROW SLICES for the three per-macroblock grids. Each was indexed
1285        // `r * mb_w + mb_x` against a whole-FRAME Vec, which the compiler
1286        // cannot prove in range; sliced to THIS row, `[mb_x]` is provably
1287        // `< mb_w`. These are disjoint FIELDS of `self`, so the `&mut` on
1288        // `bs_frame` coexists with the `&` borrows `info` holds.
1289        let row0 = r * mb_w;
1290        let bs_row = &mut self.bs_frame[row0..][..mb_w];
1291        let t8_row = &self.mb_t8x8[row0..][..mb_w];
1292        let kind_row: &[u8] = if self.mb_kind.is_empty() {
1293            &[]
1294        } else {
1295            &self.mb_kind[row0..][..mb_w]
1296        };
1297        for mb_x in 0..mb_w {
1298            // Always pack: UNSET / Inter neighbours in this row and the next
1299            // read left/top MbPack. Kind stores MbBs directly (no i32 hop).
1300            self.pk_cur.push(pack_mb(&info, has1, mb_x, r));
1301            if stats {
1302                edcstat::bump(&edcstat::DBS_MB, 1);
1303            }
1304            // KIND ECONOMICS ON THE PACKED PATH: derive_mb_kind(Skip) does 9
1305            // fresh strided Blk::loads per MB, but pack_mb already ran for this
1306            // MB (the line above) — the packed `_` arm derives from those
1307            // records with no further gathers. A B_Skip kind experiment
1308            // measured kind-arm 1.8% SLOWER (z=-2.69) with +1.07M loads, so
1309            // Skip/InterUniform route to the packed arm; Intra keeps the kind
1310            // arm (pure constants, no loads). `RS_H264_KIND_LOADS=1` restores
1311            // the old routing for paired A/B.
1312            match kind_row.get(mb_x).copied().and_then(MbKind::from_u8) {
1313                Some(MbKind::Intra) => {
1314                    if stats {
1315                        edcstat::bump(&edcstat::DBS_INTRA, 1);
1316                    }
1317                    // WIN: intra strengths are a constant table keyed on availability;
1318                    // asking derive_mb_kind for them dragged its whole gathering body
1319                    // (Skip/InterUniform/Inter, 1654 instrs) into the decode binary,
1320                    // even though the only other arm that calls it is guarded by a
1321                    // build-time false knob.
1322                    bs_row[mb_x] = rusty_h264_common::deblock::intra_mb_bs(mb_x, r);
1323                }
1324                Some(k @ (MbKind::Skip | MbKind::InterUniform)) if kl => {
1325                    if stats {
1326                        edcstat::bump(&edcstat::DBS_KINDARM, 1);
1327                    }
1328                    bs_row[mb_x] = derive_mb_kind(&info, mb_x, r, k);
1329                }
1330                _ => {
1331                    let Some(cur) = self.pk_cur.get(mb_x) else {
1332                        continue;
1333                    };
1334                    let left = if mb_x > 0 {
1335                        self.pk_cur.get(mb_x - 1)
1336                    } else {
1337                        None
1338                    };
1339                    let top = if r > 0 { self.pk_prev.get(mb_x) } else { None };
1340                    let mb_t8 = t8_row[mb_x];
1341                    let (mut bv, mut bh) = ([[0i32; 4]; 4], [[0i32; 4]; 4]);
1342                    rusty_h264_common::deblock::census_note_packed();
1343                    let flat = derive_mb_records(cur, left, top, mb_t8, &mut bv, &mut bh);
1344                    if stats {
1345                        edcstat::bump(&edcstat::DBS_PACKED, 1);
1346                    }
1347                    // MEASUREMENT ONLY: sizes the `kind_loads()` match guard that
1348                    // used to be a OnceLock deref here (now `k_kindloads`).
1349                    if stats
1350                        && matches!(
1351                            // WIN: kind_row is already this row of mb_kind, proven in range
1352                            // above, so [mb_x] needs no whole-frame bounds check. Identical
1353                            // when mb_kind is empty: kind_row is &[] and both answer None.
1354                            kind_row.get(mb_x).copied().and_then(MbKind::from_u8),
1355                            Some(MbKind::Skip | MbKind::InterUniform)
1356                        )
1357                    {
1358                        edcstat::bump(&edcstat::DBS_KINDGUARD, 1);
1359                    }
1360                    // FLAT-AWARE NARROWING. `derive_mb_records` returns early on a
1361                    // flat inter macroblock having written ONLY edge 0 of each
1362                    // orientation; edges 1..4 are still the caller's zero-init, so
1363                    // widening all 32 entries copies 24 known zeros onto 24 known
1364                    // zeros - after a `MbBs::default()` that zeroed them a third
1365                    // time. Census DBSDERIVE flat: 96.8% screen_text, 90.2%
1366                    // FourPeople, 82.1% akiyo - the dominant class.
1367                    let m = if flat {
1368                        if stats {
1369                            edcstat::bump(&edcstat::DBS_FLAT, 1);
1370                        }
1371                        debug_assert!(bv[1..] == [[0i32; 4]; 3] && bh[1..] == [[0i32; 4]; 3]);
1372                        let mut m = MbBs::default();
1373                        m.v[0] = core::array::from_fn(|sg| bv[0][sg] as u8);
1374                        m.h[0] = core::array::from_fn(|sg| bh[0][sg] as u8);
1375                        m
1376                    } else {
1377                        // Written once, not zeroed by `default()` and then written.
1378                        MbBs {
1379                            v: core::array::from_fn(|e| core::array::from_fn(|sg| bv[e][sg] as u8)),
1380                            h: core::array::from_fn(|e| core::array::from_fn(|sg| bh[e][sg] as u8)),
1381                        }
1382                    };
1383                    // MEASUREMENT ONLY: `on()` first, so the 32-byte compare
1384                    // never runs in a shipped decode (it is not otherwise needed
1385                    // here — the consumer in filter_frame_rows makes it).
1386                    if stats && m.v == [[0u8; 4]; 4] && m.h == [[0u8; 4]; 4] {
1387                        edcstat::bump(&edcstat::DBS_ALLZERO, 1);
1388                    }
1389                    bs_row[mb_x] = m;
1390                }
1391            }
1392        }
1393        // disable_deblocking_filter_idc == 2 (spec §7.4.3): a slice may forbid
1394        // filtering ITS macroblocks' edges against OTHER slices. bS = 0 on the
1395        // crossing MB edges kills exactly those filters; interior edges keep
1396        // their derived strengths. Guarded so single-slice / idc 0-1 pictures
1397        // pay one branch per row.
1398        if self.any_idc2 && self.slice_bounds.len() > 1 {
1399            edcstat::bump(&edcstat::DBS_IDC2ROW, 1);
1400            for mb_x in 0..mb_w {
1401                let slot = r * mb_w + mb_x;
1402                let si = self.slice_of(slot);
1403                if !self.slice_bounds.get(si).is_some_and(|b| b.1) {
1404                    continue;
1405                }
1406                if mb_x > 0 && self.slice_of(slot - 1) != si {
1407                    if let Some(b) = self.bs_frame.get_mut(slot) {
1408                        b.v[0] = [0; 4];
1409                    }
1410                }
1411                if r > 0 && self.slice_of(slot - mb_w) != si {
1412                    if let Some(b) = self.bs_frame.get_mut(slot) {
1413                        b.h[0] = [0; 4];
1414                    }
1415                }
1416            }
1417        }
1418    }
1419
1420    /// Decode-loop hook: called at each MB-loop head with the NEXT address to be
1421    /// decoded; derives AND FILTERS (R3) every fully-decoded row. Filtering a
1422    /// row here preserves the spec's raster per-MB filter order exactly (every
1423    /// MB the row's edges touch is already decoded; bottom-adjacent edges
1424    /// belong to the NEXT row's MBs, which filter later).
1425    ///
1426    /// Fast path: mid-row heads (`done <= bs_rows`) do no row work — return
1427    /// before the profiler scope (this hook is entered once per MB; scoping
1428    /// every call was measuring the timer, not the filter).
1429    /// row_hook with the caller's carried row — avoids the per-MB division.
1430    #[inline(always)]
1431    fn row_hook_at(&mut self, addr: usize, mby: usize) {
1432        if rowdb_on() {
1433            // ~44/45 of calls are mid-row: one compare, no atomic loads.
1434            if !rowhook_eager() && mby <= self.bs_rows {
1435                return;
1436            }
1437        }
1438        self.row_hook(addr, mby);
1439    }
1440
1441    /// `mby` is the row `addr` sits in. It is a PARAMETER rather than
1442    /// `addr / self.mb_w` because both callers already carry it: the slice loops
1443    /// track `(mbx, mby)` with a compare-and-wrap precisely so the per-macroblock
1444    /// div+mod never runs, and this hook -- entered PER MACROBLOCK on the
1445    /// non-rowdb arm -- was reconstructing it with an integer divide anyway.
1446    fn row_hook(&mut self, addr: usize, mby: usize) {
1447        debug_assert_eq!(mby, addr / self.mb_w, "row_hook: mby must be addr's row");
1448        // `RS_H264_ROWHOOK_EAGER=1` restores per-MB profiler scoping (A/B oracle).
1449        // READ THE KNOB FUNCTIONS, NOT THE CACHED FIELDS. The old note here said
1450        // the functions cost a OnceLock deref plus a relaxed atomic load per
1451        // entry; that was true BEFORE the routing round, and is not true now --
1452        // they are `return false` under cfg(not(knobs)), i.e. a constant. Going
1453        // through a struct FIELD erases that constant, because the field is a
1454        // runtime value, and every arm the knob guards then stays live in the
1455        // binary. That is what kept `derive_mb_kind` (1543 instrs the decoder
1456        // never runs) linked in until the same change was made for kind_loads.
1457        let eager = rowhook_eager();
1458        if !rowdb_on() {
1459            edcstat::bump(&edcstat::MBS, 1);
1460            let _rh = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecRowHook);
1461            self.edc_flush();
1462            let done = mby;
1463            if done > self.bs_rows {
1464                self.bs_rows = done;
1465                self.publish_progress();
1466            }
1467            return;
1468        }
1469        let done = mby;
1470        // ~44/45 of calls are mid-row: no derive/filter/handoff yet.
1471        if !eager && done <= self.bs_rows {
1472            return;
1473        }
1474        edcstat::bump(&edcstat::MBS, 1);
1475        let _rh = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecRowHook);
1476        if done <= self.bs_rows {
1477            return;
1478        }
1479        if self.edc_tx.is_some() {
1480            // E2: derivation stays here (it reads the syntax grids); filtering
1481            // is the worker's, fed the row's bs/qp/t8 snapshot. `flt_rows`
1482            // advances on the worker and comes home with the context.
1483            self.edc_giveback();
1484            while self.bs_rows < done {
1485                let r = self.bs_rows;
1486                self.derive_bs_row(r);
1487                self.bs_rows += 1;
1488                let base = r * self.mb_w;
1489                // ORDER: this row's pixel jobs must reach the worker BEFORE the
1490                // filter message for the same row.
1491                self.edc_flush_batch();
1492                edcstat::bump(&edcstat::ROWS, 1);
1493                edcstat::bump(
1494                    &edcstat::ROWBYTES,
1495                    (self.mb_w * (core::mem::size_of::<rusty_h264_common::deblock::MbBs>() + 2))
1496                        as u64,
1497                );
1498                let msg = EdcMsg::Row {
1499                    r,
1500                    bs: self.bs_frame[base..base + self.mb_w].to_vec(),
1501                    qp: self.mb_qp[base..base + self.mb_w].to_vec(),
1502                    t8: self.mb_t8x8[base..base + self.mb_w].to_vec(),
1503                };
1504                self.edc_tx
1505                    .as_ref()
1506                    .unwrap()
1507                    .send(msg)
1508                    .expect("worker alive");
1509            }
1510            return;
1511        }
1512        self.edc_flush();
1513        while self.bs_rows < done {
1514            let r = self.bs_rows;
1515            self.derive_bs_row(r);
1516            self.bs_rows += 1;
1517            // Row filtering requires deblock enabled on EVERY slice so far
1518            // (`db_ena` latches false once any slice disables it): a mixed
1519            // picture falls back to the picture-end tail so "latest slice
1520            // wins" semantics are preserved.
1521            if self.db_ena {
1522                self.save_bak(r);
1523                self.filter_row(r);
1524                self.flt_rows = r + 1;
1525            }
1526            self.publish_progress();
1527        }
1528    }
1529
1530    /// Saves the UNFILTERED bottom pixel rows of MB row `r` before filtering
1531    /// modifies them: the next row's intra prediction must read pre-deblock
1532    /// samples (spec §8.3), and filtering touches the bottom three rows while
1533    /// intra reads exactly the bottom ONE (+ the corner) — so one backup row
1534    /// per plane suffices, overwritten per row.
1535    fn save_bak(&mut self, r: usize) {
1536        let y0 = (r * 16 + 15) * self.cw;
1537        self.bak_y.copy_from_slice(&self.rec_y[y0..y0 + self.cw]);
1538        let c0 = (r * 8 + 7) * self.ccw;
1539        self.bak_u.copy_from_slice(&self.rec_u[c0..c0 + self.ccw]);
1540        self.bak_v.copy_from_slice(&self.rec_v[c0..c0 + self.ccw]);
1541    }
1542
1543    /// Filters one MB row against the stored strengths, using the CURRENT
1544    /// slice's alpha/beta offsets (single-offset streams — the whole corpus —
1545    /// are bit-identical to the picture-end call; the plan's risk register
1546    /// documents the multi-offset divergence).
1547    fn filter_row(&mut self, r: usize) {
1548        let info = rusty_h264_common::deblock::BlockInfo {
1549            inter: &self.inter_y,
1550            // Same source `derive_bs_row` used (see the T8 GATE there). This
1551            // path passes a non-empty `bs`, so it takes the PRECOMPUTED arm and
1552            // never reads `nnz` at all; kept in step so the two cannot diverge.
1553            nnz: if self.any_t8 {
1554                &self.nnz_dbr
1555            } else {
1556                &self.nnz_y
1557            },
1558            mv: &self.mv_y,
1559            ref_id: &self.ref_idx_y,
1560            mv1: &self.mv1,
1561            ref_id1: &self.ref_idx1,
1562            w4: self.mb_w * 4,
1563            t8x8: &self.mb_t8x8,
1564            bs: &self.bs_frame,
1565            poc0: &[],
1566            poc1: &[],
1567            kind: &self.mb_kind,
1568        };
1569        rusty_h264_common::deblock::filter_frame_rows_pre(
1570            &mut self.rec_y,
1571            &mut self.rec_u,
1572            &mut self.rec_v,
1573            self.mb_w,
1574            self.mb_h,
1575            r..r + 1,
1576            &self.mb_qp,
1577            self.chroma_qp_offset,
1578            self.db_oa,
1579            self.db_ob,
1580            &info,
1581        );
1582    }
1583
1584    /// Top-neighbour LUMA pixel for intra prediction: reads the unfiltered
1585    /// backup row when the row above has already been deblock-filtered by the
1586    /// row-interleave (flt_rows gates it; 0 when the interleave is off, so
1587    /// this compiles to the plain read on the fallback path).
1588    #[inline]
1589    fn top_y_px(&self, py: usize, x: usize) -> u8 {
1590        // 128 is the spec's value for an unavailable sample (1 << (BitDepth - 1),
1591        // §8.3.1.2), so the fallback is the one the prediction rules already use
1592        // — and unreachable anyway, since callers gate on availability first.
1593        if py % 16 == 0 && self.flt_rows * 16 >= py {
1594            self.bak_y.get(x).copied().unwrap_or(128)
1595        } else {
1596            self.rec_y
1597                .get((py - 1) * self.cw + x)
1598                .copied()
1599                .unwrap_or(128)
1600        }
1601    }
1602
1603    /// Slice form of [`Self::top_y_px`] for the contiguous 16-wide I16 gather.
1604    #[inline]
1605    fn top_y_row(&self, py: usize, x: usize, n: usize) -> &[u8] {
1606        if py % 16 == 0 && self.flt_rows * 16 >= py {
1607            &self.bak_y[x..x + n]
1608        } else {
1609            &self.rec_y[(py - 1) * self.cw + x..][..n]
1610        }
1611    }
1612
1613    /// Top-neighbour CHROMA pixel (plane `c`: 0 = U, 1 = V).
1614    #[inline]
1615    fn top_c_px(&self, c: usize, cy: usize, x: usize) -> u8 {
1616        // 128 = the spec's unavailable sample, as in `top_y_px`.
1617        if cy % 8 == 0 && self.flt_rows * 8 >= cy {
1618            let bak = if c == 0 { &self.bak_u } else { &self.bak_v };
1619            bak.get(x).copied().unwrap_or(128)
1620        } else {
1621            let rec = if c == 0 { &self.rec_u } else { &self.rec_v };
1622            rec.get((cy - 1) * self.ccw + x).copied().unwrap_or(128)
1623        }
1624    }
1625
1626    /// Slice form of [`Self::top_c_px`] for the 8-wide chroma gather.
1627    #[inline]
1628    fn top_c_row(&self, c: usize, cy: usize, x: usize, n: usize) -> &[u8] {
1629        if cy % 8 == 0 && self.flt_rows * 8 >= cy {
1630            if c == 0 {
1631                &self.bak_u[x..x + n]
1632            } else {
1633                &self.bak_v[x..x + n]
1634            }
1635        } else {
1636            let rec = if c == 0 { &self.rec_u } else { &self.rec_v };
1637            &rec[(cy - 1) * self.ccw + x..][..n]
1638        }
1639    }
1640
1641    /// Snapshots the (deblocked) reconstruction as a reference picture, drawing
1642    /// its padded-plane allocations from `pool` (recycled
1643    /// planes of evicted DPB frames — see `Decoder::reclaim_retired`). ~1.9 MB of
1644    /// fresh allocation per reference picture otherwise (`dpb-clone` stage, 3-4%
1645    /// of decode, mostly first-touch page faults).
1646    pub fn as_reference_pooled(&self, pool: &mut Vec<Vec<u8>>) -> crate::RefFrame {
1647        // MV CAPTURE (`RFF_MV_DUMP=1`) — lets a harness read the motion field any
1648        // conformant H.264 stream carries, including x264's, using this decoder as
1649        // the parser. Diagnostic only; inert unless the env var is set.
1650        #[cfg(feature = "std")]
1651        if mv_dump_on() {
1652            MV_DUMP.lock().unwrap().push(MvField {
1653                mb_w: self.mb_w,
1654                mb_h: self.mb_h,
1655                mv: self.mv_y.clone(),
1656                ref_idx: self.ref_idx_y.clone(),
1657                inter: self.inter_y.clone(),
1658            });
1659        }
1660
1661        // The per-block motion (mv/ref_idx/ref_poc) is read ONLY by B temporal/spatial
1662        // direct (`col.mv/ref_idx/ref_poc`, guarded on `w4 != 0` + `idx < len`). On
1663        // Baseline/Constrained-Baseline streams (no B) it's pure waste — skip the two
1664        // grid clones + the per-block ref_poc resolve/alloc. `w4 = 0` makes the B
1665        // readers no-op even on malformed input.
1666        let (mv, ref_idx, mv1, ref_idx1, ref_poc, w4) = if self.b_possible {
1667            (
1668                self.mv_y.clone(),
1669                self.ref_idx_y.clone(),
1670                self.mv1.clone(),
1671                self.ref_idx1.clone(),
1672                // Resolve each block's List-0 ref index to the referenced picture's
1673                // POC, so temporal direct can map it into the current list.
1674                // Via a tiny per-ref LUT: the per-block bounds + Option chain +
1675                // Ref pointer chase (57k blocks at 720p, once per reference
1676                // frame) becomes one table index. Identical output: LUT slots
1677                // past refs.len() hold MIN, exactly what .get() returned.
1678                {
1679                    let mut poc_lut = [i32::MIN; 32];
1680                    for (i, f) in self.refs.iter().take(32).enumerate() {
1681                        poc_lut[i & 31] = f.pic_poc();
1682                    }
1683                    self.ref_idx_y
1684                        .iter()
1685                        .map(|&r| {
1686                            if (0..32).contains(&r) {
1687                                poc_lut[r as usize]
1688                            } else {
1689                                i32::MIN
1690                            }
1691                        })
1692                        .collect()
1693                },
1694                self.mb_w * 4,
1695            )
1696        } else {
1697            (
1698                Vec::new(),
1699                Vec::new(),
1700                Vec::new(),
1701                Vec::new(),
1702                Vec::new(),
1703                0,
1704            )
1705        };
1706        // Pop an exact-size recycled buffer per plane; a miss falls back to a
1707        // fresh allocation inside `pad_plane_into`.
1708        let mut take = |len: usize| -> Vec<u8> {
1709            match pool.iter().position(|v| v.len() == len) {
1710                Some(i) => pool.swap_remove(i),
1711                None => Vec::new(),
1712            }
1713        };
1714        let (lpw, lph) = (self.cw + 2 * crate::LPAD, self.ch + 2 * crate::LPAD);
1715        let (cpw, cph) = (self.ccw + 2 * crate::CPAD, self.ch / 2 + 2 * crate::CPAD);
1716        crate::RefFrame {
1717            // Pad once here (ExpandPicture) instead of extracting a clamped tile
1718            // on every MC call — same copy class as the old plane clone.
1719            py: rusty_h264_common::inter::pad_plane_into(
1720                take(lpw * lph),
1721                &self.rec_y,
1722                self.cw,
1723                self.ch,
1724                crate::LPAD,
1725            ),
1726            pu: rusty_h264_common::inter::pad_plane_into(
1727                take(cpw * cph),
1728                &self.rec_u,
1729                self.ccw,
1730                self.ch / 2,
1731                crate::CPAD,
1732            ),
1733            pv: rusty_h264_common::inter::pad_plane_into(
1734                take(cpw * cph),
1735                &self.rec_v,
1736                self.ccw,
1737                self.ch / 2,
1738                crate::CPAD,
1739            ),
1740            cw: self.cw,
1741            ch: self.ch,
1742            ready_rows: core::sync::atomic::AtomicUsize::new(0),
1743            live: None,
1744            frozen: crate::sync::Once::new(),
1745            frame_num: 0, // set by the caller (decode_slice knows frame_num)
1746            poc: 0,       // set by the caller
1747            mv,
1748            ref_idx,
1749            mv1,
1750            ref_idx1,
1751            ref_poc,
1752            w4,
1753            long_term: false,
1754            long_term_idx: 0,
1755        }
1756    }
1757
1758    fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
1759        let w4 = self.mb_w * 4;
1760        let top_unavail = mb_y == 0 || !self.nbr_in_slice(mb_x, mb_y - 1);
1761        let left_unavail = mb_x == 0 || !self.nbr_in_slice(mb_x - 1, mb_y);
1762        // The four TOP neighbours are one contiguous run of the nnz grid; only
1763        // the left column is strided. Hoisting the uniform `top_unavail` test out
1764        // of the loop turns four checked grid loads into one slice copy.
1765        if top_unavail {
1766            self.nnz_l_cache[1..5].fill(0x80);
1767        } else {
1768            let src = &self.nnz_y[(mb_y * 4 - 1) * w4 + mb_x * 4..][..4];
1769            self.nnz_l_cache[1..5].copy_from_slice(src);
1770        }
1771        for lby in 0..4 {
1772            self.nnz_l_cache[(lby + 1) * 5] = if left_unavail {
1773                0x80
1774            } else {
1775                self.nnz_y
1776                    .get((mb_y * 4 + lby) * w4 + (mb_x * 4 - 1))
1777                    .copied()
1778                    .unwrap_or(0)
1779            };
1780        }
1781    }
1782    #[inline]
1783    fn nc_pred(&self, lbx: usize, lby: usize) -> i32 {
1784        // MASKED, and it pays twice over: this helper and `nnz_cache_set` are
1785        // inlined into the intra path, the inter path AND both slice loops, so
1786        // the two unprovable indexes were replicated at every call site. The
1787        // cache is a 5x5 grid in a `[u8; 25]` and every caller passes a 4x4 block
1788        // coordinate (`LUMA_4X4_SCAN_XY`, `b8*2 + s`, or a literal), so `& 3` is
1789        // a no-op that puts the worst case at 4 * 5 + 4 = 24.
1790        let (lbx, lby) = (lbx & 3, lby & 3);
1791        let left = self.nnz_l_cache[(lby + 1) * 5 + lbx] as i32;
1792        let top = self.nnz_l_cache[lby * 5 + (lbx + 1)] as i32;
1793        let r = left + top;
1794        if r < 0x80 {
1795            (r + 1) >> 1
1796        } else {
1797            r & 0x7f
1798        }
1799    }
1800    #[inline]
1801    fn nnz_cache_set(&mut self, lbx: usize, lby: usize, total: u8) {
1802        self.nnz_l_cache[((lby & 3) + 1) * 5 + ((lbx & 3) + 1)] = total;
1803    }
1804    fn chroma_cache_load(&mut self, mb_x: usize, mb_y: usize) {
1805        let w2 = self.mb_w * 2;
1806        let top_unavail = mb_y == 0 || !self.nbr_in_slice(mb_x, mb_y - 1);
1807        let left_unavail = mb_x == 0 || !self.nbr_in_slice(mb_x - 1, mb_y);
1808        for c in 0..2 {
1809            if top_unavail {
1810                self.nnz_c_cache[c][1..3].fill(0x80);
1811            } else {
1812                let src = &self.nnz_c[c][(mb_y * 2 - 1) * w2 + mb_x * 2..][..2];
1813                self.nnz_c_cache[c][1..3].copy_from_slice(src);
1814            }
1815            for by in 0..2 {
1816                self.nnz_c_cache[c][(by + 1) * 3] = if left_unavail {
1817                    0x80
1818                } else {
1819                    self.nnz_c[c & 1]
1820                        .get((mb_y * 2 + by) * w2 + (mb_x * 2 - 1))
1821                        .copied()
1822                        .unwrap_or(0)
1823                };
1824            }
1825        }
1826    }
1827    #[inline]
1828    fn chroma_nc_pred(&self, c: usize, bx: usize, by: usize) -> i32 {
1829        // Same shape as `nc_pred`, one size down: a 3x3 grid in `[u8; 9]`, two
1830        // planes, and every caller passes 0..1 for all three coordinates.
1831        let (c, bx, by) = (c & 1, bx & 1, by & 1);
1832        let left = self.nnz_c_cache[c][(by + 1) * 3 + bx] as i32;
1833        let top = self.nnz_c_cache[c][by * 3 + (bx + 1)] as i32;
1834        let r = left + top;
1835        if r < 0x80 {
1836            (r + 1) >> 1
1837        } else {
1838            r & 0x7f
1839        }
1840    }
1841    #[inline]
1842    fn chroma_nnz_cache_set(&mut self, c: usize, bx: usize, by: usize, total: u8) {
1843        self.nnz_c_cache[c & 1][((by & 1) + 1) * 3 + ((bx & 1) + 1)] = total;
1844    }
1845
1846    /// Decodes one slice's macroblocks (raster order) starting at `first_mb`,
1847    /// until `more_rbsp_data()` is exhausted or the picture is full. Returns the
1848    /// next macroblock address (= total when the picture is complete). In a
1849    /// P-slice each macroblock is preceded by `mb_skip_run`.
1850    /// CABAC slice-data decode (docs/cabac-decode-plan.md), brought up brick by brick
1851    /// against the instrumented openh264 oracle. Phase 1: verify engine init; the
1852    /// syntax layer (Phase 2+) is WIP.
1853    #[allow(clippy::too_many_arguments)]
1854    pub fn decode_slice_data_cabac(
1855        &mut self,
1856        rbsp: &[u8],
1857        start_byte: usize,
1858        slice_qp: u8,
1859        cabac_init_idc: u32,
1860        is_i: bool,
1861        is_p: bool,
1862        first_mb: usize,
1863    ) -> Result<usize, MbError> {
1864        // E2: overlap parse (this thread) with pixel reconstruction (a scoped
1865        // worker owning the planes) for P slices. I slices and B slices keep
1866        // the inline path (their pixel coupling is per-MB); the ownership
1867        // ping-pong around intra-in-P macroblocks is `edc_intra_sync`.
1868        let eligible = edc_on() && rowdb_on() && !is_i && (is_p || self.is_b);
1869        let threaded = eligible && edc_spawn_worker(self.mb_w, self.mb_h, self.bits_per_mb, true);
1870        edcstat::bump(&edcstat::DISPATCH_ON, threaded as u64);
1871        edcstat::bump(&edcstat::DISPATCH_SEEN, eligible as u64);
1872        if !threaded {
1873            let r = self.decode_slice_cabac_inner(
1874                rbsp,
1875                start_byte,
1876                slice_qp,
1877                cabac_init_idc,
1878                is_i,
1879                is_p,
1880                first_mb,
1881            );
1882            self.note_slice_density(rbsp.len().saturating_sub(start_byte), first_mb, &r);
1883            return r;
1884        }
1885        #[cfg(feature = "std")]
1886        {
1887            let ctx = self.edc_take_ctx();
1888            // D7 PROBE: is the CPU overhead PAYLOAD (alloc/copy per job) or
1889            // SYNCHRONISATION (blocking on a full queue, park/unpark)? The bound
1890            // separates them: raising it removes send-blocking without changing a
1891            // single byte copied. `RS_H264_EDC_BOUND` sweeps it.
1892            let (tx, rx) = crate::sync::mpsc::sync_channel::<EdcMsg>(edc_bound());
1893            let (ctx_tx, ctx_rx) = crate::sync::mpsc::channel::<PixelCtx>();
1894            let (back_tx, back_rx) = crate::sync::mpsc::channel::<PixelCtx>();
1895            let (res, ctx, panicked) = std::thread::scope(|sc| {
1896                let h = sc.spawn(move || edc_worker(ctx, rx, ctx_tx, back_rx));
1897                self.edc_tx = Some(tx);
1898                self.edc_ctx_rx = Some(ctx_rx);
1899                self.edc_back_tx = Some(back_tx);
1900                // UNWIND SAFETY (found by the fuzzer as a HANG, not a failure): a
1901                // panic inside the parse loop would skip the cleanup below — but
1902                // the sender lives in `self`, which outlives the unwind, so the
1903                // channel would never close, the worker would never exit, and the
1904                // scope's join would block forever, converting a diagnosable panic
1905                // into a silent deadlock under `catch_unwind` harnesses. Catch,
1906                // clean up, join, restore the planes, THEN resume the panic.
1907                let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1908                    self.decode_slice_cabac_inner(
1909                        rbsp,
1910                        start_byte,
1911                        slice_qp,
1912                        cabac_init_idc,
1913                        is_i,
1914                        is_p,
1915                        first_mb,
1916                    )
1917                }));
1918                self.edc_flush_batch(); // ORDER: no job may outlive the channel
1919                self.edc_giveback(); // if an intra macroblock left us holding
1920                self.edc_tx = None; // closes the channel -> worker drains + returns
1921                self.edc_ctx_rx = None;
1922                self.edc_back_tx = None;
1923                match (r, h.join()) {
1924                    (Ok(res), Ok(ctx)) => (res, Some(ctx), None),
1925                    (Err(p), Ok(ctx)) => (Err(MbError::Truncated), Some(ctx), Some(p)),
1926                    (Ok(_), Err(p)) | (Err(_), Err(p)) => (Err(MbError::Truncated), None, Some(p)),
1927                }
1928            });
1929            if let Some(ctx) = ctx {
1930                self.edc_restore_ctx(ctx);
1931            }
1932            if let Some(p) = panicked {
1933                std::panic::resume_unwind(p);
1934            }
1935            self.note_slice_density(rbsp.len().saturating_sub(start_byte), first_mb, &res);
1936            return res;
1937        }
1938        #[cfg(not(feature = "std"))]
1939        {
1940            unreachable!("the EDC worker needs std; edc_spawn_worker is false without it")
1941        }
1942    }
1943
1944    /// Feed the D12 dispatch its density signal from a slice just decoded.
1945    /// Exponentially smoothed so one atypical slice cannot flip the arm, and
1946    /// only ever read on the NEXT slice — the current one is already committed.
1947    fn note_slice_density(&mut self, bytes: usize, first_mb: usize, r: &Result<usize, MbError>) {
1948        let Ok(end) = r else { return };
1949        let mbs = end.saturating_sub(first_mb);
1950        if mbs == 0 {
1951            return;
1952        }
1953        let bpm = (bytes * 8) as f64 / mbs as f64;
1954        self.bits_per_mb = if self.bits_per_mb == 0.0 {
1955            bpm
1956        } else {
1957            0.75 * self.bits_per_mb + 0.25 * bpm
1958        };
1959    }
1960
1961    fn decode_slice_cabac_inner(
1962        &mut self,
1963        rbsp: &[u8],
1964        start_byte: usize,
1965        slice_qp: u8,
1966        cabac_init_idc: u32,
1967        is_i: bool,
1968        is_p: bool,
1969        first_mb: usize,
1970    ) -> Result<usize, MbError> {
1971        self.edc_active = edc_on();
1972        let mut cab =
1973            crate::cabac::Cabac::new(rbsp, start_byte, slice_qp as i32, cabac_init_idc, is_i);
1974        let (range, _offset) = cab.dbg_state();
1975        // The crate already HAS a `cabac-trace` feature for this; the bare knob()
1976        // was env::var plus a String allocation per SLICE, inside the hottest
1977        // function in the decoder, and it kept every trace arm compiled in.
1978        #[cfg(not(feature = "cabac-trace"))]
1979        let trace = false;
1980        #[cfg(feature = "cabac-trace")]
1981        let trace = rusty_h264_common::knob("RH_CABAC_TRACE").is_some();
1982        debug_assert_eq!(range, 510, "CABAC init range must be 510");
1983
1984        let mbw = self.mb_w;
1985        let total = self.mb_w * self.mb_h;
1986        // Per-MB neighbour state (single-slice assumption: avail == in-bounds).
1987        // SCOPED: zero-initialised allocations sized by MB count, once per slice.
1988        // D13: B-only grids (List-1 ref/mvd + direct flags) are ~292 KB at 720p and
1989        // are ONLY written on the B branch below — allocating them on every P/I
1990        // slice was the same fresh-page class GridPool fixed for the frame grids.
1991        // `RS_H264_FAT_SLICE=1` restores the always-alloc path for A/B.
1992        let _alloc_g =
1993            rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecSliceAlloc);
1994        // POOLED (was fresh `vec![..]` per slice): refill reuses the pooled
1995        // allocation with contents identical to a fresh vec, so the body below
1996        // is untouched. Taken out of `self` here; put back at the normal exit.
1997        let mut cat = refill(core::mem::take(&mut self.sc_cat), total, 255u8); // 0=I4x4, 2=I16, 255=unavailable
1998        let mut mb_cbp = refill(core::mem::take(&mut self.sc_cbp), total, 0u8);
1999        let mut cmode = refill(core::mem::take(&mut self.sc_cmode), total, -1i32); // chroma pred mode
2000        let mut mb_nzc = refill(core::mem::take(&mut self.sc_nzc), total, [0u8; 24]); // 16 luma raster + 8 chroma
2001        let mut cbf_dc = refill(core::mem::take(&mut self.sc_cbfdc), total, 0u16);
2002        let mut mb_skip = refill(core::mem::take(&mut self.sc_skip), total, false);
2003        let mut mb_ref = refill(core::mem::take(&mut self.sc_ref), total, [-1i8; 16]); // per-4×4-block List-0 ref (-1 = intra)
2004        let mut mb_mvd = refill(core::mem::take(&mut self.sc_mvd), total, [[0i16; 2]; 16]); // per-block mvd (for mvd ctxInc)
2005                                                                                            // D13: B-only grids (~292 KB @720p) only on B slices (or FAT_SLICE A/B).
2006        let want_b_grids = self.is_b || fat_slice_on();
2007        let mut mb_ref1 = if want_b_grids {
2008            refill(core::mem::take(&mut self.sc_ref1), total, [-1i8; 16])
2009        } else {
2010            Vec::new()
2011        };
2012        let mut mb_mvd1 = if want_b_grids {
2013            refill(core::mem::take(&mut self.sc_mvd1), total, [[0i16; 2]; 16])
2014        } else {
2015            Vec::new()
2016        };
2017        let mut mb_direct = if want_b_grids {
2018            refill(core::mem::take(&mut self.sc_direct), total, false)
2019        } else {
2020            Vec::new()
2021        };
2022        drop(_alloc_g);
2023        // Multi-slice availability (spec §6.4.x): a macroblock before this
2024        // slice's first_mb is NOT available — for pixel-domain prediction
2025        // (`nbr_in_slice`, like the CAVLC twin sets) AND for every CABAC ctx
2026        // neighbour below. The per-slice ctx arrays' defaults are not the
2027        // "unavailable" value (cat 255 reads as available-I16, mb_cbp 0 as
2028        // all-zero-cbp, mb_t8x8 is a frame grid…), so the `left`/`top`
2029        // options themselves are gated on slice membership — one gate that
2030        // every downstream ctxIdxInc read inherits. Confirmed against ffmpeg
2031        // on an x264 `--slices 4` stream, which desynced without this.
2032        self.slice_first_mb = first_mb;
2033        self.slice_bounds.push((first_mb, self.cur_idc2));
2034        self.any_idc2 |= self.cur_idc2;
2035        let mut last_delta_qp = 0i32;
2036        let mut addr = first_mb;
2037
2038        let _mbloop_g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbLoop);
2039        // A new slice's first MBs may read grids a previous slice's deferred
2040        // spans still owe (P-after-B in one picture included).
2041        self.span_flush();
2042        // `mbw` is `pic_width_in_mbs_minus1 + 1` from the SPS. It cannot be 0 in
2043        // a conformant stream, but it is a runtime value, so this div+rem pair
2044        // carried a divide-by-zero panic reachable from a MALFORMED header --
2045        // on the untrusted-input path, at slice entry. `.max(1)` retires both
2046        // checks and cannot change a conformant decode.
2047        let mbw_nz = mbw.max(1);
2048        let (mut mbx, mut mby) = (addr % mbw_nz, addr / mbw_nz);
2049        // Slice-invariant B properties, formerly re-derived per macroblock.
2050        if self.is_b {
2051            self.edc_flush(); // drain anything a preceding P slice queued
2052        }
2053        let b_records = self.edc_tx.is_some();
2054        let b_refs_ok = !self.refs.is_empty() && !self.refs1.is_empty();
2055        // Loop-invariant: the lowest `addr` whose top neighbour is in this slice.
2056        let top_lim = first_mb + mbw;
2057        // Reused across macroblocks: MC overwrites every byte it reads back (all
2058        // B layouts cover the full macroblock), so the per-MB 384-byte zero-init
2059        // these used to pay was dead. Byte-identity is the guard.
2060        let mut pred_y = [0u8; 256];
2061        let mut c_pred = [[0u8; 64]; 2];
2062        // The loop wraps mbx at entry; bias so the first iteration is exact.
2063        // BOUND the entropy-coded loop (fuzzer-surfaced): a mutated stream can
2064        // never produce decode_terminate and the engine zero-fills forever. The
2065        // bound needs checking ONCE at entry — every in-loop path that advances
2066        // `addr` already breaks on `addr >= total` before continuing.
2067        if addr >= total {
2068            return Err(MbError::Truncated);
2069        }
2070        // BIND THE LENGTH FOR THE LOOP. Every grid below is `refill(.., total, ..)`
2071        // so its length is EXACTLY `total`, and the loop is entered only with
2072        // `addr < total` (checked above, re-asserted each turn) — but nothing
2073        // related the two, so every `[addr]` carried a check. Twenty-eight of
2074        // those became `get_mut` in the previous pass and cost ~2% on CABAC
2075        // content (crowd_run-main 1.021x, z=+2.11): a branch per write on the
2076        // decoder's hottest loop. Reborrowing at the literal `total` makes
2077        // `addr < total` and `addr < len` the SAME fact — no check, no branch.
2078        // The borrows end with this block, before the grids go back to the pool.
2079        // `mb_direct`/`mb_ref1`/`mb_mvd1` are deliberately NOT bound here: they
2080        // are `Vec::new()` on non-B slices, so slicing them to `total` would be
2081        // the very panic this campaign is removing.
2082        {
2083            let cat = &mut cat[..total];
2084            let mb_cbp = &mut mb_cbp[..total];
2085            let cmode = &mut cmode[..total];
2086            let mb_nzc = &mut mb_nzc[..total];
2087            let cbf_dc = &mut cbf_dc[..total];
2088            let mb_skip = &mut mb_skip[..total];
2089            let mb_ref = &mut mb_ref[..total];
2090            let mb_mvd = &mut mb_mvd[..total];
2091            loop {
2092                // A REAL check, not `debug_assert` — which compiles OUT in release,
2093                // so nothing carried `addr < total` across the loop's back-edge and
2094                // the reborrows above folded nothing on their own. This cannot fire
2095                // (every path that advances `addr` already breaks on `addr >= total`,
2096                // and entry is guarded above), but stating it once per macroblock
2097                // replaces FIFTEEN per-write branches with one that never taken.
2098                if addr >= total {
2099                    break;
2100                }
2101                // Carried coordinates: one compare-and-wrap replaces the per-MB
2102                // div+mod pair (and row_hook's own division).
2103                if mbx == mbw {
2104                    mbx = 0;
2105                    mby += 1;
2106                }
2107                self.row_hook_at(addr, mby);
2108                self.wait_refs_for_mb(mby);
2109                let left = (mbx > 0 && addr > first_mb).then(|| addr - 1);
2110                let top = (mby > 0 && addr >= top_lim).then(|| addr - mbw);
2111
2112                // Brick 3.1/3.2: P-slice mb_skip_flag, then mb_type (P mb_type is neighbour-
2113                // independent; intra sub-types map to the I dispatch below).
2114                let mb_type;
2115                if is_p {
2116                    // Direct bool arithmetic — no Option chain on the hot path.
2117                    let sctx = 11
2118                        + (left.is_some() && !mb_skip.get(addr - 1).copied().unwrap_or(true))
2119                            as usize
2120                        + (top.is_some()
2121                            && !mb_skip.get(addr.wrapping_sub(mbw)).copied().unwrap_or(true))
2122                            as usize;
2123                    // ONE engine view carries mb_skip_flag, the terminate bin of a skipped
2124                    // macroblock, and mb_type of a coded one (was 2-3 view/commit round
2125                    // trips of 8 memory ops each per macroblock).
2126                    let (data, mut e, ctx) = cab.view();
2127                    if e.decode_decision(data, ctx, sctx) != 0 {
2128                        if let Some(p) = mb_skip.get_mut(addr) {
2129                            *p = true;
2130                        }
2131                        last_delta_qp = 0; // skip codes no mb_qp_delta → delta ctxInc resets
2132                                           // P_Skip recon reuses the entropy-free CAVLC primitive verbatim: it
2133                                           // takes no bit-reader (skip has no coded syntax past the flag), just
2134                                           // predicts the skip MV, motion-compensates, and commits the grid.
2135                        self.decode_p_skip(mbx, mby)?;
2136                        if let Some(p) = self.mb_qp.get_mut(addr) {
2137                            *p = self.cur_qp;
2138                        } // skip inherits QPy
2139                        let eos = e.decode_terminate(data);
2140                        cab.commit(e);
2141                        addr += 1;
2142                        mbx += 1;
2143                        if eos || addr >= total {
2144                            break;
2145                        }
2146                        continue;
2147                    }
2148                    let mbt = mb_type_p_eng(&mut e, data, ctx);
2149                    cab.commit(e);
2150                    // NON-SKIP P MB: the inter arms below predict MVs from the
2151                    // grids (mv_neighbors_block) and the intra arm gathers — flush
2152                    // the deferred P span (B span cannot be pending in a P slice,
2153                    // but span_flush is one Option check each).
2154                    self.span_flush();
2155                    if mbt <= 3 {
2156                        let _gb =
2157                            rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbP);
2158                        // noSubMbPartSizeLessThan8x8Flag (spec 7.3.5): P_8x8 permits the
2159                        // 8x8 transform only when every sub-partition is itself 8x8.
2160                        let mut allow8 = true;
2161                        // Inter MB (Bricks 3.3/3.4/3.5). 1-ref stream → ref_idx not coded (ref=0).
2162                        // Build the 30-entry mvd/ref neighbour cache (openh264 WelsFillCacheInterCabac).
2163                        let mut mvdc = [[0i16; 2]; 30];
2164                        let mut refc = [-1i8; 30];
2165                        // Index the neighbour's RECORD once, then read within it.
2166                        // `mb_ref[l][bi]` is two bounds checks - one on the Vec, one
2167                        // on the array - repeated for each of the four entries and
2168                        // again for `mb_mvd`, i.e. sixteen checks per macroblock for
2169                        // four neighbours. Binding the record hoists the Vec check
2170                        // out; the inner index is a literal and folds.
2171                        if let Some(l) = left {
2172                            let (Some(lr), Some(lm)) = (mb_ref.get(l), mb_mvd.get(l)) else {
2173                                return Err(MbError::Truncated);
2174                            };
2175                            for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
2176                                refc[ci] = lr[bi];
2177                                mvdc[ci] = lm[bi];
2178                            }
2179                        }
2180                        if let Some(t) = top {
2181                            let (Some(tr), Some(tm)) = (mb_ref.get(t), mb_mvd.get(t)) else {
2182                                return Err(MbError::Truncated);
2183                            };
2184                            for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
2185                                refc[ci] = tr[bi];
2186                                mvdc[ci] = tm[bi];
2187                            }
2188                        }
2189                        if mbx > 0 && mby > 0 {
2190                            let a = addr - mbw - 1;
2191                            if let (Some(r), Some(m)) = (mb_ref.get(a), mb_mvd.get(a)) {
2192                                (refc[0], mvdc[0]) = (r[15], m[15]);
2193                            }
2194                        }
2195                        if mby > 0 && mbx + 1 < mbw {
2196                            let a = addr - mbw + 1;
2197                            if let (Some(r), Some(m)) = (mb_ref.get(a), mb_mvd.get(a)) {
2198                                (refc[5], mvdc[5]) = (r[12], m[12]);
2199                            }
2200                        }
2201                        let mut mmvd = [[0i16; 2]; 16];
2202                        let mut mref = [0i8; 16];
2203                        // mb_pred (spec 7.3.5.1): all ref_idx_l0 FIRST (only when >1 active
2204                        // ref), then all mvd + ref-aware predict + commit. `refidx!` parses one
2205                        // partition's ref_idx (ctxIdxOffset 54, ctx from neighbour refc) and
2206                        // seeds refc so a later partition's ref/mvd context sees it — mirror
2207                        // of the encoder's two-phase emit_mb_cabac_p_inter.
2208                        macro_rules! refidx {
2209                            ($pi:expr, $zb:expr) => {{
2210                                if self.num_ref_active > 1 {
2211                                    let s = CACHE30[$pi & 15].clamp(6, 29);
2212                                    let c0 =
2213                                        (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
2214                                    let r = parse_ref_idx_cabac(&mut cab, c0);
2215                                    for &zb in $zb.iter() {
2216                                        refc[CACHE30[zb & 15]] = r;
2217                                    }
2218                                    r
2219                                } else {
2220                                    0i8
2221                                }
2222                            }};
2223                        }
2224                        macro_rules! part {
2225                            ($pi:expr, $zb:expr, $pred:expr, $rx:expr, $ry:expr, $rw:expr, $rh:expr, $refi:expr) => {{
2226                                let (mvx, mvy) = parse_mvd_partition(
2227                                    &mut cab, $pi, $zb, &mut mvdc, &mut refc, &mut mmvd, &mut mref,
2228                                    $refi,
2229                                );
2230                                let [na, nb, nc] = self.mv_neighbors_block(
2231                                    (mbx * 4 + $rx / 4) as isize,
2232                                    (mby * 4 + $ry / 4) as isize,
2233                                    ($rw / 4) as isize,
2234                                );
2235                                let pmv = $pred(na, nb, nc);
2236                                self.commit_inter_grid(
2237                                    mbx,
2238                                    mby,
2239                                    $rx,
2240                                    $ry,
2241                                    $rw,
2242                                    $rh,
2243                                    (pmv.0 + mvx, pmv.1 + mvy),
2244                                    $refi,
2245                                );
2246                            }};
2247                        }
2248                        match mbt {
2249                            0 => {
2250                                // Sibling of CAVLC P_16x16: one (ref, mv) for all 16
2251                                // blocks → every internal edge is strength 0 (§8.7.2.1).
2252                                // Without this, CABAC Main/High P_16x16 stayed UNSET and
2253                                // paid the blind 24-block bS gather.
2254                                if let Some(k) = self.mb_kind.get_mut(mby * self.mb_w + mbx) {
2255                                    *k = rusty_h264_common::deblock::MB_KIND_INTER_UNIFORM;
2256                                }
2257                                let r0 = refidx!(
2258                                    0,
2259                                    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
2260                                );
2261                                part!(
2262                                    0,
2263                                    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
2264                                    |a, b, c| predict_partition_mv(0, 0, a, b, c, r0 as i32),
2265                                    0,
2266                                    0,
2267                                    16,
2268                                    16,
2269                                    r0
2270                                );
2271                            }
2272                            1 => {
2273                                let r0 = refidx!(0, &[0, 1, 2, 3, 4, 5, 6, 7]);
2274                                let r1 = refidx!(8, &[8, 9, 10, 11, 12, 13, 14, 15]);
2275                                part!(
2276                                    0,
2277                                    &[0, 1, 2, 3, 4, 5, 6, 7],
2278                                    |a, b, c| predict_partition_mv(1, 0, a, b, c, r0 as i32),
2279                                    0,
2280                                    0,
2281                                    16,
2282                                    8,
2283                                    r0
2284                                );
2285                                part!(
2286                                    8,
2287                                    &[8, 9, 10, 11, 12, 13, 14, 15],
2288                                    |a, b, c| predict_partition_mv(1, 1, a, b, c, r1 as i32),
2289                                    0,
2290                                    8,
2291                                    16,
2292                                    8,
2293                                    r1
2294                                );
2295                            }
2296                            2 => {
2297                                let r0 = refidx!(0, &[0, 1, 2, 3, 8, 9, 10, 11]);
2298                                let r1 = refidx!(4, &[4, 5, 6, 7, 12, 13, 14, 15]);
2299                                part!(
2300                                    0,
2301                                    &[0, 1, 2, 3, 8, 9, 10, 11],
2302                                    |a, b, c| predict_partition_mv(2, 0, a, b, c, r0 as i32),
2303                                    0,
2304                                    0,
2305                                    8,
2306                                    16,
2307                                    r0
2308                                );
2309                                part!(
2310                                    4,
2311                                    &[4, 5, 6, 7, 12, 13, 14, 15],
2312                                    |a, b, c| predict_partition_mv(2, 1, a, b, c, r1 as i32),
2313                                    8,
2314                                    0,
2315                                    8,
2316                                    16,
2317                                    r1
2318                                );
2319                            }
2320                            _ => {
2321                                // P_8x8: 4 sub_mb_types, then 4 ref_idx (one per 8×8), then mvd.
2322                                let mut subt = [0u32; 4];
2323                                for st in &mut subt {
2324                                    *st = parse_sub_mb_type_p_cabac(&mut cab);
2325                                }
2326                                allow8 = subt.iter().all(|&t| t == 0);
2327                                let mut pr = [0i8; 4];
2328                                for (i, r) in pr.iter_mut().enumerate() {
2329                                    let b = i * 4;
2330                                    *r = refidx!(b, &[b, b + 1, b + 2, b + 3]);
2331                                }
2332                                for i in 0..4usize {
2333                                    let b = i * 4;
2334                                    let (ox, oy) = ((i % 2) * 8, (i / 2) * 8); // 8×8 pixel origin in MB
2335                                    let ri = pr[i];
2336                                    match subt[i] {
2337                                        0 => part!(
2338                                            b,
2339                                            &[b, b + 1, b + 2, b + 3],
2340                                            |a, b, c| predict_mv(a, b, c, ri as i32),
2341                                            ox,
2342                                            oy,
2343                                            8,
2344                                            8,
2345                                            ri
2346                                        ),
2347                                        1 => {
2348                                            part!(
2349                                                b,
2350                                                &[b, b + 1],
2351                                                |a, b, c| predict_mv(a, b, c, ri as i32),
2352                                                ox,
2353                                                oy,
2354                                                8,
2355                                                4,
2356                                                ri
2357                                            );
2358                                            part!(
2359                                                b + 2,
2360                                                &[b + 2, b + 3],
2361                                                |a, b, c| predict_mv(a, b, c, ri as i32),
2362                                                ox,
2363                                                oy + 4,
2364                                                8,
2365                                                4,
2366                                                ri
2367                                            );
2368                                        }
2369                                        2 => {
2370                                            part!(
2371                                                b,
2372                                                &[b, b + 2],
2373                                                |a, b, c| predict_mv(a, b, c, ri as i32),
2374                                                ox,
2375                                                oy,
2376                                                4,
2377                                                8,
2378                                                ri
2379                                            );
2380                                            part!(
2381                                                b + 1,
2382                                                &[b + 1, b + 3],
2383                                                |a, b, c| predict_mv(a, b, c, ri as i32),
2384                                                ox + 4,
2385                                                oy,
2386                                                4,
2387                                                8,
2388                                                ri
2389                                            );
2390                                        }
2391                                        _ => {
2392                                            for j in 0..4usize {
2393                                                let (sx, sy) = ((j % 2) * 4, (j / 2) * 4);
2394                                                part!(
2395                                                    b + j,
2396                                                    &[b + j],
2397                                                    |a, b, c| predict_mv(a, b, c, ri as i32),
2398                                                    ox + sx,
2399                                                    oy + sy,
2400                                                    4,
2401                                                    4,
2402                                                    ri
2403                                                );
2404                                            }
2405                                        }
2406                                    }
2407                                }
2408                            }
2409                        }
2410                        if let Some(p) = mb_ref.get_mut(addr) {
2411                            *p = mref;
2412                        }
2413                        if let Some(p) = mb_mvd.get_mut(addr) {
2414                            *p = mmvd;
2415                        }
2416
2417                        // Inter cbp + residual (is_intra = false → cbf default nA=nB=0).
2418                        let cbp = parse_cbp_cabac(
2419                            &mut cab,
2420                            top.and_then(|a| mb_cbp.get(a).copied()),
2421                            left.and_then(|a| mb_cbp.get(a).copied()),
2422                        );
2423                        if let Some(p) = mb_cbp.get_mut(addr) {
2424                            *p = cbp as u8;
2425                        }
2426                        // H-49: an INTER macroblock carries transform_size_8x8_flag AFTER cbp
2427                        // (spec 7.3.5), present only when CodedBlockPatternLuma > 0 and
2428                        // noSubMbPartSizeLessThan8x8Flag. Same context as the intra read.
2429                        let t8 = self.transform_8x8_mode && (cbp & 15) != 0 && allow8 && {
2430                            let a = left
2431                                .map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
2432                            let b =
2433                                top.map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
2434                            cab.decode_decision(399 + a + b) != 0
2435                        };
2436                        if let Some(p) = self.mb_t8x8.get_mut(addr) {
2437                            *p = t8;
2438                        }
2439                        self.any_t8 |= t8;
2440                        // D9c: cbp==0 never parses residuals — skip the 2.5 KB coeff
2441                        // zero-init + PInterJob entirely when NORES is on (default).
2442                        // Current-MB nzc slots stay unset under cbp==0 and export as 0
2443                        // (same as the 0xff→0 scrub below), so mb_nzc = [0;24] is exact.
2444                        if cbp == 0 && nores_on() {
2445                            last_delta_qp = 0;
2446                            if let Some(p) = self.mb_qp.get_mut(addr) {
2447                                *p = self.cur_qp;
2448                            }
2449                            if let Some(p) = cbf_dc.get_mut(addr) {
2450                                *p = 0;
2451                            }
2452                            if let Some(p) = mb_nzc.get_mut(addr) {
2453                                *p = [0u8; 24];
2454                            }
2455                            if self.refs.is_empty() {
2456                                return Err(MbError::Unsupported("inter without reference"));
2457                            }
2458                            let (mut jgmv, mut jgref) = ([(0i32, 0i32); 16], [0u8; 16]);
2459                            {
2460                                let w4r = self.mb_w * 4;
2461                                for by in 0..4usize {
2462                                    // Row-contiguous — see the coded-inter gather.
2463                                    let row = (mby * 4 + by) * w4r + mbx * 4;
2464                                    jgmv[by * 4..by * 4 + 4]
2465                                        .copy_from_slice(&self.mv_y[row..row + 4]);
2466                                    // Row-slice the ref grid too - it was the only
2467                                    // one still indexed per block.
2468                                    let ridx = &self.ref_idx_y[row..row + 4];
2469                                    for bx in 0..4usize {
2470                                        jgref[by * 4 + bx] = ridx[bx].clamp(0, 15) as u8;
2471                                    }
2472                                }
2473                            }
2474                            let pj = PInterNoResJob {
2475                                mbx,
2476                                mby,
2477                                t8,
2478                                gmv: jgmv,
2479                                gref: jgref,
2480                            };
2481                            if self.edc_tx.is_some() {
2482                                self.edc_giveback();
2483                                self.edc_commit_nnz(mbx, mby, t8, &[0u8; 24], 0);
2484                                if edcstat::on() {
2485                                    edcstat::bump(&edcstat::J_INTER, 1);
2486                                    edcstat::bump(&edcstat::J_INTER_NORES, 1);
2487                                }
2488                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
2489                                self.edc_send_job(EdcJob::InterNoRes(Box::new(pj)));
2490                            } else if self.edc_active {
2491                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
2492                                self.edc_jobs.push(EdcJob::InterNoRes(Box::new(pj)));
2493                            } else {
2494                                self.recon_p_inter_nores(&pj);
2495                                if double_recon() {
2496                                    self.recon_p_inter_nores(&pj);
2497                                }
2498                            }
2499                            let eos = cab.decode_terminate();
2500                            addr += 1;
2501                            mbx += 1;
2502                            if eos || addr >= total {
2503                                break;
2504                            }
2505                            continue;
2506                        }
2507                        // POOLED JOB, BUILT IN PLACE: the residual parse writes straight into
2508                        // the boxed job (no 2.8 KB stack-then-heap copy, no malloc/free per
2509                        // macroblock), and only the blocks the cbp says will be parsed are
2510                        // zeroed -- the consumer reads a block only when its nnz is nonzero.
2511                        let mut job = self.take_pinter_job();
2512                        job.t8 = t8;
2513                        let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
2514                        let mut nzc = [0xffu8; 48];
2515                        if let Some(t) = top {
2516                            let tnz = mb_nzc.get(t).unwrap_or(&ZERO_NZC);
2517                            nzc[1..5].copy_from_slice(&tnz[12..16]);
2518                            (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
2519                            (nzc[6], nzc[7], nzc[30], nzc[31]) =
2520                                (tnz[20], tnz[21], tnz[22], tnz[23]);
2521                        }
2522                        if let Some(l) = left {
2523                            let lnz = mb_nzc.get(l).unwrap_or(&ZERO_NZC);
2524                            (nzc[8], nzc[16], nzc[24], nzc[32]) =
2525                                (lnz[3], lnz[7], lnz[11], lnz[15]);
2526                            (nzc[13], nzc[21], nzc[37], nzc[45]) =
2527                                (lnz[17], lnz[21], lnz[19], lnz[23]);
2528                        }
2529                        let mut cbfdc = 0u16;
2530                        job.nnzs = [0u8; 24]; // parsed totalCoeff per block (see add_inter_residual)
2531                                              // A cbp==0 MB codes no mb_qp_delta → the next MB's delta ctxInc sees 0.
2532                        if cbp == 0 {
2533                            last_delta_qp = 0;
2534                        }
2535                        if cbp != 0 {
2536                            let ndc = (
2537                                top.and_then(|a| cbf_dc.get(a).copied()),
2538                                left.and_then(|a| cbf_dc.get(a).copied()),
2539                            );
2540                            let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
2541                            self.step_qp(qpd)?;
2542                            parse_mb_residual_cabac::<false>(
2543                                &mut cab,
2544                                &mut nzc,
2545                                &mut cbfdc,
2546                                ndc,
2547                                cbp_luma,
2548                                cbp_chroma,
2549                                t8,
2550                                ResidualOut {
2551                                    luma: &mut job.luma_scan,
2552                                    luma8: &mut job.luma8,
2553                                    cdc: &mut job.cdc,
2554                                    cac: &mut job.cac,
2555                                    nnzs: &mut job.nnzs,
2556                                },
2557                            );
2558                        }
2559                        if let Some(p) = self.mb_qp.get_mut(addr) {
2560                            *p = self.cur_qp;
2561                        }
2562                        if let Some(p) = cbf_dc.get_mut(addr) {
2563                            *p = cbfdc;
2564                        }
2565                        let _sc = rusty_h264_common::prof::scope(
2566                            rusty_h264_common::prof::Stage::DecStateCache,
2567                        );
2568                        let mn = nnz_raster_from_z(&job.nnzs);
2569                        if let Some(p) = mb_nzc.get_mut(addr) {
2570                            *p = mn;
2571                        }
2572                        drop(_sc);
2573
2574                        if self.refs.is_empty() {
2575                            return Err(MbError::Unsupported("inter without reference"));
2576                        }
2577                        // NOTE (measured, do not "optimise" this away): EDC is
2578                        // DEFAULT ON (`edc_on()`, opt out with RS_H264_EDC=0), so
2579                        // even single-threaded this job is built and deferred —
2580                        // that batching is the E1 loop-fission win, not overhead.
2581                        // A "skip the job in 1T" bypass added here could never fire
2582                        // and was removed.
2583                        let (mut jgmv, mut jgref) = ([(0i32, 0i32); 16], [0u8; 16]);
2584                        {
2585                            let w4r = self.mb_w * 4;
2586                            for by in 0..4usize {
2587                                // Row-contiguous: one slice copy for the MVs
2588                                // instead of four bounds-checked strided loads.
2589                                let row = (mby * 4 + by) * w4r + mbx * 4;
2590                                jgmv[by * 4..by * 4 + 4].copy_from_slice(&self.mv_y[row..row + 4]);
2591                                // Row-slice the ref grid too - it was the only one
2592                                // still indexed per block.
2593                                let ridx = &self.ref_idx_y[row..row + 4];
2594                                for bx in 0..4usize {
2595                                    jgref[by * 4 + bx] = ridx[bx].clamp(0, 15) as u8;
2596                                }
2597                            }
2598                        }
2599                        // D9c: when `cbp == 0`, never materialise the 2.5 KB coeff
2600                        // arrays into a `PInterJob` — ship `InterNoRes` (or call
2601                        // `recon_p_inter_nores` inline). `RS_H264_NORES=0` keeps the
2602                        // old full-job path for A/B.
2603                        let nores = cbp == 0 && nores_on();
2604                        if nores {
2605                            let pj = PInterNoResJob {
2606                                mbx,
2607                                mby,
2608                                t8,
2609                                gmv: jgmv,
2610                                gref: jgref,
2611                            };
2612                            if self.edc_tx.is_some() {
2613                                self.edc_giveback();
2614                                self.edc_commit_nnz(mbx, mby, t8, &[0u8; 24], 0);
2615                                if edcstat::on() {
2616                                    edcstat::bump(&edcstat::J_INTER, 1);
2617                                    edcstat::bump(&edcstat::J_INTER_NORES, 1);
2618                                }
2619                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
2620                                let b = self.take_nores_job(pj);
2621                                self.edc_send_job(EdcJob::InterNoRes(b));
2622                            } else if self.edc_active {
2623                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
2624                                let b = self.take_nores_job(pj);
2625                                self.edc_jobs.push(EdcJob::InterNoRes(b));
2626                            } else {
2627                                self.recon_p_inter_nores(&pj);
2628                                if double_recon() {
2629                                    self.recon_p_inter_nores(&pj);
2630                                }
2631                            }
2632                            let eos = cab.decode_terminate();
2633                            addr += 1;
2634                            mbx += 1;
2635                            if eos || addr >= total {
2636                                break;
2637                            }
2638                            continue;
2639                        }
2640                        job.mbx = mbx;
2641                        job.mby = mby;
2642                        job.qp = self.cur_qp;
2643                        job.cbp_chroma = cbp_chroma;
2644                        job.gmv = jgmv;
2645                        job.gref = jgref;
2646                        if self.edc_tx.is_some() {
2647                            self.edc_giveback();
2648                            self.edc_commit_nnz(mbx, mby, t8, &job.nnzs, cbp_chroma);
2649                            if edcstat::on() {
2650                                edcstat::bump(&edcstat::J_INTER, 1);
2651                            }
2652                            self.edc_send_job(EdcJob::Inter(job));
2653                        } else if self.edc_active {
2654                            self.edc_jobs.push(EdcJob::Inter(job));
2655                        } else {
2656                            self.recon_p_inter(&job);
2657                            if double_recon() {
2658                                self.recon_p_inter(&job);
2659                            }
2660                            self.edc_job_pool.push(job);
2661                        }
2662
2663                        let eos = cab.decode_terminate();
2664                        addr += 1;
2665                        mbx += 1;
2666                        if eos || addr >= total {
2667                            break;
2668                        }
2669                        continue;
2670                    }
2671                    mb_type = mbt - 5; // 5→0 (I_4x4), 6..29→1..24 (I_16x16)
2672                } else if self.is_b {
2673                    // NOTE: the per-macroblock edc_flush() that used to sit here is
2674                    // hoisted to slice entry (see below) — nothing inside a B slice
2675                    // pushes to edc_jobs.
2676                    if b_records {
2677                        // E3: this B macroblock's MC regions record instead of executing.
2678                        self.edc_regions = Some(Vec::with_capacity(8));
2679                    }
2680                    let _gb =
2681                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbB);
2682                    // noSubMbPartSizeLessThan8x8Flag for B: direct MBs qualify only under
2683                    // direct_8x8_inference_flag; B_8x8 needs every sub-partition 8x8.
2684                    let mut allow8 = true;
2685                    // B-slice: mb_skip_flag (ctx 24 + neighbour-not-skip), then B mb_type.
2686                    let (hl, ht) = (left.is_some(), top.is_some());
2687                    let sctx = 24
2688                        + (hl && !mb_skip.get(addr - 1).copied().unwrap_or(true)) as usize
2689                        + (ht && !mb_skip.get(addr.wrapping_sub(mbw)).copied().unwrap_or(true))
2690                            as usize;
2691                    // ONE engine view carries mb_skip_flag, the terminate bin of a skipped
2692                    // macroblock, and mb_type of a coded one (was 2-3 view/commit round
2693                    // trips of 8 memory ops each per macroblock).
2694                    let (data, mut e, ctx) = cab.view();
2695                    if e.decode_decision(data, ctx, sctx) != 0 {
2696                        if let (Some(sk), Some(di)) =
2697                            (mb_skip.get_mut(addr), mb_direct.get_mut(addr))
2698                        {
2699                            (*sk, *di) = (true, true);
2700                        }
2701                        last_delta_qp = 0; // skip codes no mb_qp_delta → delta ctxInc resets
2702                                           // B_Skip recon reuses the entropy-free CAVLC primitive (spatial/temporal
2703                                           // direct with no residual), which also commits the motion grid.
2704                                           // Hot prefix inline: forced run continuations skip the call.
2705                        if !self.b_skip_hot(mbx, mby) {
2706                            self.decode_b_skip(mbx, mby)?;
2707                        }
2708                        if let Some(p) = self.mb_qp.get_mut(addr) {
2709                            *p = self.cur_qp;
2710                        }
2711                        // Skip/direct blocks contribute mvd 0 to a later MB's mvd ctxInc; the
2712                        // ref stays in-list so |mvd|=0 is summed (same result either way).
2713                        // mb_ref/mb_ref1 stay at their -1 init: every reader is
2714                        // either a `> 0` context test (-1 and 0 both false) or the
2715                        // mvd-sum's `>= 0` gate — and a skip's mvd is (0,0), so
2716                        // exclusion (-1) and inclusion-of-zero (0) give the same
2717                        // sum. The 64-byte per-skip zero-fill was pure waste.
2718                        let eos = e.decode_terminate(data);
2719                        cab.commit(e);
2720                        addr += 1;
2721                        mbx += 1;
2722                        if eos || addr >= total {
2723                            break;
2724                        }
2725                        continue;
2726                    }
2727                    // NON-SKIP B MB: every arm below reads the grids INLINE
2728                    // (H-48: CABAC never routes through decode_b_mb) — flush
2729                    // the deferred spans. Caught by the tempete ARM-DIFF.
2730                    self.span_flush();
2731                    let bci = (hl && !mb_direct.get(addr - 1).copied().unwrap_or(true)) as usize
2732                        + (ht
2733                            && !mb_direct
2734                                .get(addr.wrapping_sub(mbw))
2735                                .copied()
2736                                .unwrap_or(true)) as usize;
2737                    let bmt = {
2738                        let _s = rusty_h264_common::prof::scope(
2739                            rusty_h264_common::prof::Stage::BTypeParse,
2740                        );
2741                        mb_type_b_eng(&mut e, data, ctx, 27, bci)
2742                    };
2743                    cab.commit(e);
2744                    if bmt < 23 {
2745                        // ---- B inter: parse motion (mvd L0/L1; ref not coded on this 1-ref
2746                        // stream) + residual. Recon (b_mc/direct) deferred to B.3. ----
2747                        let mut mvdc0 = [[0i16; 2]; 30];
2748                        let mut refc0 = [-1i8; 30];
2749                        let mut mvdc1 = [[0i16; 2]; 30];
2750                        let mut refc1 = [-1i8; 30];
2751                        // WelsFillCacheInterCabac, per list (L0 = mb_ref/mb_mvd, L1 = mb_ref1/mb_mvd1).
2752                        // The corner addresses and their guards are macroblock
2753                        // properties, resolved ONCE here rather than rebuilt inside
2754                        // each list expansion; each neighbour grid row is borrowed
2755                        // once instead of re-indexed per entry.
2756                        let tl = (mbx > 0 && mby > 0).then(|| addr - mbw - 1);
2757                        let tr = (mby > 0 && mbx + 1 < mbw).then(|| addr - mbw + 1);
2758                        macro_rules! fill {
2759                            ($mrf:expr, $mmv:expr, $rc:expr, $mc:expr) => {{
2760                                // `.get` PER PARALLEL GRID. The ref and mvd grids are
2761                                // separate Vecs indexed by the same macroblock address,
2762                                // so `$mrf[l]` proved nothing about `$mmv[l]` and each
2763                                // of the four neighbour slots carried two panic paths —
2764                                // doubled again because this macro expands once per
2765                                // list. A `None` here degrades to exactly what the
2766                                // cache already means by "neighbour unavailable" (the
2767                                // `-1` ref it is initialised to), so the fallible form
2768                                // is the honest one as well as the cheap one.
2769                                if let Some(l) = left {
2770                                    if let (Some(rr), Some(mm)) = ($mrf.get(l), $mmv.get(l)) {
2771                                        for (ci, bi) in
2772                                            [(6usize, 3usize), (12, 7), (18, 11), (24, 15)]
2773                                        {
2774                                            $rc[ci] = rr[bi];
2775                                            $mc[ci] = mm[bi];
2776                                        }
2777                                    }
2778                                }
2779                                if let Some(t) = top {
2780                                    if let (Some(rr), Some(mm)) = ($mrf.get(t), $mmv.get(t)) {
2781                                        for (ci, bi) in
2782                                            [(1usize, 12usize), (2, 13), (3, 14), (4, 15)]
2783                                        {
2784                                            $rc[ci] = rr[bi];
2785                                            $mc[ci] = mm[bi];
2786                                        }
2787                                    }
2788                                }
2789                                if let Some(a) = tl {
2790                                    if let (Some(rr), Some(mm)) = ($mrf.get(a), $mmv.get(a)) {
2791                                        ($rc[0], $mc[0]) = (rr[15], mm[15]);
2792                                    }
2793                                }
2794                                if let Some(a) = tr {
2795                                    if let (Some(rr), Some(mm)) = ($mrf.get(a), $mmv.get(a)) {
2796                                        ($rc[5], $mc[5]) = (rr[12], mm[12]);
2797                                    }
2798                                }
2799                            }};
2800                        }
2801                        {
2802                            let _s = rusty_h264_common::prof::scope(
2803                                rusty_h264_common::prof::Stage::BFillCache,
2804                            );
2805                            fill!(mb_ref, mb_mvd, refc0, mvdc0);
2806                            fill!(mb_ref1, mb_mvd1, refc1, mvdc1);
2807                        }
2808                        let mut _smv = Some(rusty_h264_common::prof::scope(
2809                            rusty_h264_common::prof::Stage::BMvdParse,
2810                        ));
2811                        let mut mmvd0 = [[0i16; 2]; 16];
2812                        let mut mref0 = [-1i8; 16];
2813                        let mut mmvd1 = [[0i16; 2]; 16];
2814                        let mut mref1 = [-1i8; 16];
2815                        if !b_refs_ok {
2816                            return Err(MbError::Unsupported("B without references"));
2817                        }
2818                        // Recon (mirrors CAVLC decode_b_mb / decode_b_8x8): predict each list's
2819                        // MV off the committed grid + the CABAC-parsed mvd, commit, MC (bi-pred
2820                        // blend), then add the residual. Prediction reads mmvd0/mmvd1 (the mvd
2821                        // per raster block, splatted during the parse above).
2822                        // pred_y / c_pred are slice-scope scratch (see the loop head).
2823                        // Recording is a per-macroblock property — ask once, not per
2824                        // partition, so the 1T path calls b_mc straight through.
2825                        let rec_mode = self.edc_regions.is_some();
2826
2827                        if bmt == 0 {
2828                            // B_Direct_16x16: no coded motion. A direct block contributes mvd 0
2829                            // to a later MB's mvd ctxInc with its ref in-list (|0| summed).
2830                            if let Some(p) = mb_direct.get_mut(addr) {
2831                                *p = true;
2832                            }
2833                            allow8 = self.direct_8x8_inference;
2834                            (mref0, mref1) = ([0i8; 16], [0i8; 16]);
2835                            // FORCED derivation via the zero-bi bitmap (same triple
2836                            // as b_skip_hot): a direct-16 whose left/top/topright are
2837                            // recorded ref0/(0,0) committers derives (0,0)/(0,0) bi —
2838                            // skip the gather + derivation, run the region half
2839                            // directly. Its own commit is zero-bi too, so it EXTENDS
2840                            // forcing chains through coded direct MBs.
2841                            // `mbw` is already carried by the loop, and the row-above
2842                            // address is one subtraction, not three.
2843                            let up = addr.wrapping_sub(mbw);
2844                            let forced = !b_records
2845                                && mbx > 0
2846                                && mby > 0
2847                                && mbx + 1 < mbw
2848                                && up >= self.slice_first_mb
2849                                && match self.bzero.get(..=addr) {
2850                                    // A slice ENDING at `addr` proves all three
2851                                    // neighbour reads at once — the guards above put
2852                                    // every one of them below `addr`. Same shape as
2853                                    // `b_skip_hot`.
2854                                    Some(bz) => {
2855                                        bz.get(addr - 1).copied().unwrap_or(false)
2856                                            && bz.get(up).copied().unwrap_or(false)
2857                                            && bz.get(up + 1).copied().unwrap_or(false)
2858                                    }
2859                                    None => false,
2860                                };
2861                            if forced {
2862                                self.b_direct_region(
2863                                    mbx,
2864                                    mby,
2865                                    0,
2866                                    0,
2867                                    16,
2868                                    16,
2869                                    &mut pred_y,
2870                                    &mut c_pred,
2871                                    (0, 0, (0, 0), (0, 0), false),
2872                                );
2873                                if let Some(b) = self.bzero.get_mut(addr) {
2874                                    *b = true;
2875                                }
2876                            } else {
2877                                self.decode_b_direct(
2878                                    mbx,
2879                                    mby,
2880                                    0,
2881                                    0,
2882                                    16,
2883                                    16,
2884                                    &mut pred_y,
2885                                    &mut c_pred,
2886                                );
2887                            }
2888                        } else if bmt == 22 {
2889                            // B_8x8: 4 sub_mb_types, (ref not coded on 1-ref), then mvd
2890                            // list-major → sub-MB → sub-partition (openh264 order).
2891                            let mut subt = [0u32; 4];
2892                            for s in &mut subt {
2893                                *s = parse_sub_mb_type_b_cabac(&mut cab);
2894                            }
2895                            let d8i = self.direct_8x8_inference;
2896                            allow8 =
2897                                subt.iter()
2898                                    .all(|&t| if t == 0 { d8i } else { (1..=3).contains(&t) });
2899                            // uses(list) depends only on the sub_mb_type — resolve the
2900                            // whole 4x2 table once here; the ref_idx, mvd and recon
2901                            // loops below all read it instead of re-asking.
2902                            let su = [
2903                                [b_sub_uses(subt[0], 0), b_sub_uses(subt[0], 1)],
2904                                [b_sub_uses(subt[1], 0), b_sub_uses(subt[1], 1)],
2905                                [b_sub_uses(subt[2], 0), b_sub_uses(subt[2], 1)],
2906                                [b_sub_uses(subt[3], 0), b_sub_uses(subt[3], 1)],
2907                            ];
2908                            // A direct sub-partition contributes mvd 0 / ref in-list to the
2909                            // ctxInc — both the per-MB export and the within-MB 30-cache that a
2910                            // later (non-direct) sub in this MB reads.
2911                            for i in 0..4usize {
2912                                if subt[i] == 0 {
2913                                    let b = i * 4;
2914                                    for zb in b..b + 4 {
2915                                        // each table read once, not twice
2916                                        let (g, c) = (G_SCAN4[zb & 15], CACHE30[zb & 15]);
2917                                        (mref0[g], mref1[g]) = (0, 0);
2918                                        (refc0[c], refc1[c]) = (0, 0);
2919                                    }
2920                                }
2921                            }
2922                            // ref_idx_l0 for all four 8x8s, then ref_idx_l1, then the mvds
2923                            // (spec 7.3.5.2 sub_mb_pred). ONE ref per 8x8 -- never per
2924                            // sub-partition -- and B_Direct_8x8 codes none.
2925                            let mut sref = [[0i8; 2]; 4]; // [sub-MB][list]
2926                            for list in 0..2usize {
2927                                // Single-reference lists code no ref_idx at all.
2928                                if (if list == 0 {
2929                                    self.num_ref_active
2930                                } else {
2931                                    self.num_ref_active1
2932                                }) <= 1
2933                                {
2934                                    continue;
2935                                }
2936                                let rc = if list == 0 { &mut refc0 } else { &mut refc1 };
2937                                for i in 0..4usize {
2938                                    let st = subt[i];
2939                                    if st == 0 || !su[i][list] {
2940                                        continue;
2941                                    }
2942                                    let b = i * 4;
2943                                    let s = CACHE30[b & 15].clamp(6, 29);
2944                                    let c0 =
2945                                        (rc[s - 1] > 0) as usize + 2 * (rc[s - 6] > 0) as usize;
2946                                    let r = parse_ref_idx_cabac(&mut cab, c0);
2947                                    for &zb in &[b, b + 1, b + 2, b + 3] {
2948                                        rc[CACHE30[zb]] = r;
2949                                    }
2950                                    sref[i][list] = r;
2951                                }
2952                            }
2953                            for list in 0..2usize {
2954                                let (mmv, mrf, mc, rc) = if list == 0 {
2955                                    (&mut mmvd0, &mut mref0, &mut mvdc0, &mut refc0)
2956                                } else {
2957                                    (&mut mmvd1, &mut mref1, &mut mvdc1, &mut refc1)
2958                                };
2959                                for i in 0..4usize {
2960                                    let st = subt[i];
2961                                    if st == 0 || !su[i][list] {
2962                                        continue;
2963                                    }
2964                                    let b = i * 4;
2965                                    for &(sx, sy, sw, sh) in b_sub_parts(st) {
2966                                        // Shapes are 8x8 / 8x4 / 4x8 / 4x4, so the block
2967                                        // list is closed-form: `step` is 1 when the part
2968                                        // spans both columns, 2 when it spans both rows.
2969                                        let (w4b, h4b) = (sw / 4, sh / 4);
2970                                        let base = b + (sy / 4) * 2 + sx / 4;
2971                                        let step = if w4b == 2 { 1 } else { 2 };
2972                                        let zb = [base, base + step, base + 2, base + 3];
2973                                        let n = w4b * h4b;
2974                                        parse_mvd_partition(
2975                                            &mut cab,
2976                                            zb[0],
2977                                            &zb[..n],
2978                                            mc,
2979                                            rc,
2980                                            mmv,
2981                                            mrf,
2982                                            sref[i][list],
2983                                        );
2984                                    }
2985                                }
2986                            }
2987                            // Recon each 8×8: direct sub → decode_b_direct; else per sub-part
2988                            // predict (median) + commit + MC.
2989                            // Spatial-direct A/B/C are MB-level — walk once if any sub is
2990                            // direct. `dmemo=0` rewalks every direct 8×8 (A/B oracle).
2991                            // Derivation hoist: A/B/C AND the rid/median result are
2992                            // MB-level — derive once, run only the per-8x8 region half
2993                            // (czg differs per sub) for every direct sub.
2994                            let hoisted = if self.direct_spatial
2995                                && direct_memo_on()
2996                                && subt.iter().any(|&t| t == 0)
2997                            {
2998                                let (n0, n1) = self.b_direct_nbrs(mbx, mby);
2999                                Some(Self::b_direct_refs_mvs(&n0, &n1))
3000                            } else {
3001                                None
3002                            };
3003                            for (p, &st) in subt.iter().enumerate() {
3004                                let (b8x, b8y) = ((p % 2) * 8, (p / 2) * 8);
3005                                if st == 0 {
3006                                    match hoisted {
3007                                        Some(derived) => self.b_direct_region(
3008                                            mbx,
3009                                            mby,
3010                                            b8x,
3011                                            b8y,
3012                                            8,
3013                                            8,
3014                                            &mut pred_y,
3015                                            &mut c_pred,
3016                                            derived,
3017                                        ),
3018                                        None => self.decode_b_direct(
3019                                            mbx,
3020                                            mby,
3021                                            b8x,
3022                                            b8y,
3023                                            8,
3024                                            8,
3025                                            &mut pred_y,
3026                                            &mut c_pred,
3027                                        ),
3028                                    }
3029                                    continue;
3030                                }
3031                                // From the table built at the top of this arm.
3032                                let (u0, u1) = (su[p & 3][0], su[p & 3][1]);
3033                                for &(sx, sy, sw, sh) in b_sub_parts(st) {
3034                                    let (px, py) = (b8x + sx, b8y + sy);
3035                                    let mut mv = [(0i32, 0i32); 2];
3036                                    let (bx4, by4) =
3037                                        ((mbx * 4 + px / 4) as isize, (mby * 4 + py / 4) as isize);
3038                                    let didx = (py / 4) * 4 + px / 4;
3039                                    // A BI sub-part gathers both lists at ONE position, so the
3040                                    // availability work (bounds + coded + slice) is shared —
3041                                    // the fusion the 16x16 layout path already had.
3042                                    if u0 && u1 {
3043                                        let (n0, n1) =
3044                                            self.mv_neighbors_both(bx4, by4, (sw / 4) as isize);
3045                                        for (list, nb) in [(0usize, &n0), (1, &n1)] {
3046                                            let d = (if list == 0 { &mmvd0 } else { &mmvd1 })
3047                                                [didx & 15];
3048                                            let pmv = predict_mv(
3049                                                nb[0],
3050                                                nb[1],
3051                                                nb[2],
3052                                                sref[p & 3][list & 1] as i32,
3053                                            );
3054                                            mv[list & 1] =
3055                                                (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
3056                                        }
3057                                    } else {
3058                                        for list in 0..2usize {
3059                                            if if list == 0 { u0 } else { u1 } {
3060                                                let d = (if list == 0 { &mmvd0 } else { &mmvd1 })
3061                                                    [didx & 15];
3062                                                let nb = self.mv_neighbors_list(
3063                                                    bx4,
3064                                                    by4,
3065                                                    (sw / 4) as isize,
3066                                                    list,
3067                                                );
3068                                                let pmv = predict_mv(
3069                                                    nb[0],
3070                                                    nb[1],
3071                                                    nb[2],
3072                                                    sref[p & 3][list & 1] as i32,
3073                                                );
3074                                                mv[list & 1] =
3075                                                    (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
3076                                            }
3077                                        }
3078                                    }
3079                                    let refi0 = if u0 { sref[p & 3][0] as i32 } else { -1 };
3080                                    let refi1 = if u1 { sref[p & 3][1] as i32 } else { -1 };
3081                                    self.b_set_motion(
3082                                        mbx, mby, px, py, sw, sh, refi0, mv[0], refi1, mv[1],
3083                                    );
3084                                    if rec_mode {
3085                                        self.b_mc_or_record(
3086                                            mbx,
3087                                            mby,
3088                                            px,
3089                                            py,
3090                                            sw,
3091                                            sh,
3092                                            refi0,
3093                                            mv[0],
3094                                            refi1,
3095                                            mv[1],
3096                                            &mut pred_y,
3097                                            &mut c_pred,
3098                                        );
3099                                    } else {
3100                                        self.b_mc(
3101                                            mbx,
3102                                            mby,
3103                                            px,
3104                                            py,
3105                                            sw,
3106                                            sh,
3107                                            refi0,
3108                                            mv[0],
3109                                            refi1,
3110                                            mv[1],
3111                                            &mut pred_y,
3112                                            &mut c_pred,
3113                                        );
3114                                    }
3115                                }
3116                            }
3117                        } else {
3118                            let (layout, mvmode, preds) = b_inter_layout(bmt);
3119                            let parts: &[(usize, &[usize])] = match mvmode {
3120                                0 => {
3121                                    &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])]
3122                                }
3123                                1 => &[
3124                                    (0, &[0, 1, 2, 3, 4, 5, 6, 7]),
3125                                    (8, &[8, 9, 10, 11, 12, 13, 14, 15]),
3126                                ],
3127                                _ => &[
3128                                    (0, &[0, 1, 2, 3, 8, 9, 10, 11]),
3129                                    (4, &[4, 5, 6, 7, 12, 13, 14, 15]),
3130                                ],
3131                            };
3132                            // ref_idx_l0 for EVERY partition, then ref_idx_l1, then the mvds
3133                            // (spec 7.3.5.1 macroblock_prediction). This was missing entirely
3134                            // -- the B path assumed a single reference -- so any B slice with
3135                            // more than one active reference in either list desynced the
3136                            // arithmetic decoder at the first partition that codes a ref_idx,
3137                            // and the slice ended early at a phantom end_of_slice_flag.
3138                            let mut pref = [[0i8; 2]; 2]; // [partition][list]
3139                            for list in 0..2usize {
3140                                let active = if list == 0 {
3141                                    self.num_ref_active
3142                                } else {
3143                                    self.num_ref_active1
3144                                };
3145                                if active <= 1 {
3146                                    continue;
3147                                }
3148                                let rc = if list == 0 { &mut refc0 } else { &mut refc1 };
3149                                for (p, &(pidx, zb)) in parts.iter().enumerate() {
3150                                    if !preds[p & 1].uses(list) {
3151                                        continue;
3152                                    }
3153                                    let s = CACHE30[pidx & 15].clamp(6, 29);
3154                                    let c0 =
3155                                        (rc[s - 1] > 0) as usize + 2 * (rc[s - 6] > 0) as usize;
3156                                    let r = parse_ref_idx_cabac(&mut cab, c0);
3157                                    // Seed the cache so a later partition's ref/mvd ctxInc sees it.
3158                                    for &zbi in zb.iter() {
3159                                        rc[CACHE30[zbi & 15]] = r;
3160                                    }
3161                                    pref[p & 1][list & 1] = r;
3162                                }
3163                            }
3164                            // mvd parse order: list-major, partition-minor (openh264
3165                            // ParseInterBMotionInfoCabac); the ctxInc reads the same-list cache.
3166                            for list in 0..2usize {
3167                                let (mmv, mrf, mc, rc) = if list == 0 {
3168                                    (&mut mmvd0, &mut mref0, &mut mvdc0, &mut refc0)
3169                                } else {
3170                                    (&mut mmvd1, &mut mref1, &mut mvdc1, &mut refc1)
3171                                };
3172                                for (p, &(pidx, zb)) in parts.iter().enumerate() {
3173                                    if preds[p & 1].uses(list) {
3174                                        parse_mvd_partition(
3175                                            &mut cab,
3176                                            pidx,
3177                                            zb,
3178                                            mc,
3179                                            rc,
3180                                            mmv,
3181                                            mrf,
3182                                            pref[p & 1][list & 1],
3183                                        );
3184                                    }
3185                                }
3186                            }
3187                            // Per-partition recon: predict each list's MV, commit, MC.
3188                            let (mbb_x, mbb_y) = (mbx * 4, mby * 4);
3189                            for (p, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
3190                                let mut mv = [(0i32, 0i32); 2];
3191                                // uses(list) is a property of the layout — resolve both
3192                                // once instead of five times per partition, and build
3193                                // the neighbour coordinates once instead of per list.
3194                                let (u0, u1) = (preds[p & 1].uses(0), preds[p & 1].uses(1));
3195                                let (nx, ny) =
3196                                    ((mbb_x + rx / 4) as isize, (mbb_y + ry / 4) as isize);
3197                                let (nw, didx) = ((rw / 4) as isize, (ry / 4) * 4 + rx / 4);
3198                                // Bi partitions gather both lists at ONE position —
3199                                // fuse the availability work (same trick as
3200                                // b_direct_nbrs); uni partitions keep the single
3201                                // gather.
3202                                if u0 && u1 {
3203                                    let (n0, n1) = self.mv_neighbors_both(nx, ny, nw);
3204                                    for (list, n) in [(0usize, &n0), (1, &n1)] {
3205                                        let d =
3206                                            (if list == 0 { &mmvd0 } else { &mmvd1 })[didx & 15];
3207                                        let pmv = predict_partition_mv(
3208                                            mvmode,
3209                                            p,
3210                                            n[0],
3211                                            n[1],
3212                                            n[2],
3213                                            pref[p & 1][list & 1] as i32,
3214                                        );
3215                                        mv[list & 1] = (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
3216                                    }
3217                                } else {
3218                                    for list in 0..2usize {
3219                                        if if list == 0 { u0 } else { u1 } {
3220                                            let d = (if list == 0 { &mmvd0 } else { &mmvd1 })
3221                                                [didx & 15];
3222                                            let n = self.mv_neighbors_list(nx, ny, nw, list);
3223                                            let pmv = predict_partition_mv(
3224                                                mvmode,
3225                                                p,
3226                                                n[0],
3227                                                n[1],
3228                                                n[2],
3229                                                pref[p & 1][list & 1] as i32,
3230                                            );
3231                                            mv[list & 1] =
3232                                                (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
3233                                        }
3234                                    }
3235                                }
3236                                let refi0 = if u0 { pref[p & 1][0] as i32 } else { -1 };
3237                                let refi1 = if u1 { pref[p & 1][1] as i32 } else { -1 };
3238                                self.b_set_motion(
3239                                    mbx, mby, rx, ry, rw, rh, refi0, mv[0], refi1, mv[1],
3240                                );
3241                                // Proper spec bi-prediction (average of L0+L1). NOTE: the CAVLC
3242                                // decode_b_mb replicates an openh264 bug here for a Bi 16×8/8×16
3243                                // partition; our pixel gate is ffmpeg (spec-correct), so we do NOT.
3244                                if rec_mode {
3245                                    self.b_mc_or_record(
3246                                        mbx,
3247                                        mby,
3248                                        rx,
3249                                        ry,
3250                                        rw,
3251                                        rh,
3252                                        refi0,
3253                                        mv[0],
3254                                        refi1,
3255                                        mv[1],
3256                                        &mut pred_y,
3257                                        &mut c_pred,
3258                                    );
3259                                } else {
3260                                    self.b_mc(
3261                                        mbx,
3262                                        mby,
3263                                        rx,
3264                                        ry,
3265                                        rw,
3266                                        rh,
3267                                        refi0,
3268                                        mv[0],
3269                                        refi1,
3270                                        mv[1],
3271                                        &mut pred_y,
3272                                        &mut c_pred,
3273                                    );
3274                                }
3275                            }
3276                        }
3277                        // Paired stores: one index expression per grid pair.
3278                        // Four per-macroblock grids at one address; each is its own
3279                        // Vec, so each assignment carried its own check.
3280                        if let (Some(r0), Some(d0)) = (mb_ref.get_mut(addr), mb_mvd.get_mut(addr)) {
3281                            (*r0, *d0) = (mref0, mmvd0);
3282                        }
3283                        if let (Some(r1), Some(d1)) = (mb_ref1.get_mut(addr), mb_mvd1.get_mut(addr))
3284                        {
3285                            (*r1, *d1) = (mref1, mmvd1);
3286                        }
3287                        _smv = None; // close b:mvd-parse; the residual half follows
3288                        let _sres =
3289                            rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BResid);
3290
3291                        // Inter cbp + residual (identical to the P path).
3292                        let cbp = parse_cbp_cabac(
3293                            &mut cab,
3294                            top.and_then(|a| mb_cbp.get(a).copied()),
3295                            left.and_then(|a| mb_cbp.get(a).copied()),
3296                        );
3297                        if let Some(p) = mb_cbp.get_mut(addr) {
3298                            *p = cbp as u8;
3299                        }
3300                        // H-49: an INTER macroblock carries transform_size_8x8_flag AFTER cbp
3301                        // (spec 7.3.5), present only when CodedBlockPatternLuma > 0 and
3302                        // noSubMbPartSizeLessThan8x8Flag. Same context as the intra read.
3303                        let t8 = self.transform_8x8_mode && (cbp & 15) != 0 && allow8 && {
3304                            let a = left
3305                                .map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
3306                            let b =
3307                                top.map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
3308                            cab.decode_decision(399 + a + b) != 0
3309                        };
3310                        if let Some(p) = self.mb_t8x8.get_mut(addr) {
3311                            *p = t8;
3312                        }
3313                        self.any_t8 |= t8;
3314                        // D9c-B: coded B with cbp==0 never parses residuals — skip the
3315                        // ~2.5 KB coeff zero-init + fat BJob when NORES is on (default).
3316                        // MC already filled pred_y / edc_regions; recon == pred (same as
3317                        // B_Skip / P InterNoRes). t8 is always false here ((cbp&15)==0).
3318                        if cbp == 0 && nores_on() {
3319                            last_delta_qp = 0;
3320                            if let Some(p) = self.mb_qp.get_mut(addr) {
3321                                *p = self.cur_qp;
3322                            }
3323                            if let Some(p) = cbf_dc.get_mut(addr) {
3324                                *p = 0;
3325                            }
3326                            if let Some(p) = mb_nzc.get_mut(addr) {
3327                                *p = [0u8; 24];
3328                            }
3329                            if let Some(regions) = self.edc_regions.take() {
3330                                self.edc_giveback();
3331                                self.edc_commit_nnz(mbx, mby, false, &[0u8; 24], 0);
3332                                edcstat::bump(&edcstat::J_NORES_SENT, 1);
3333                                self.edc_send_job(EdcJob::BSkip { mbx, mby, regions });
3334                            } else {
3335                                // Inline twin of decode_b_skip's plane copy (MC already done).
3336                                for dy in 0..16 {
3337                                    let d = (mby * 16 + dy) * self.cw + mbx * 16;
3338                                    self.rec_y[d..d + 16]
3339                                        .copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
3340                                }
3341                                for c in 0..2 {
3342                                    let plane = if c == 0 {
3343                                        &mut self.rec_u
3344                                    } else {
3345                                        &mut self.rec_v
3346                                    };
3347                                    for dy in 0..8 {
3348                                        let d = (mby * 8 + dy) * self.ccw + mbx * 8;
3349                                        plane[d..d + 8]
3350                                            .copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
3351                                    }
3352                                }
3353                                let w4 = self.mb_w * 4;
3354                                for dy in 0..4 {
3355                                    self.nnz_y[(mby * 4 + dy) * w4 + mbx * 4..][..4].fill(0);
3356                                }
3357                            }
3358                            let eos = cab.decode_terminate();
3359                            addr += 1;
3360                            mbx += 1;
3361                            if eos || addr >= total {
3362                                break;
3363                            }
3364                            continue;
3365                        }
3366                        // Scratch planes (see `scratch_luma`): zeroed per coded block by the parse.
3367                        let mut luma8 = self
3368                            .scratch_luma8
3369                            .take()
3370                            .unwrap_or_else(|| Box::new([[0i32; 64]; 4]));
3371                        let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
3372                        let mut nzc = [0xffu8; 48];
3373                        if let Some(t) = top {
3374                            let tnz = mb_nzc.get(t).unwrap_or(&ZERO_NZC);
3375                            nzc[1..5].copy_from_slice(&tnz[12..16]);
3376                            (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
3377                            (nzc[6], nzc[7], nzc[30], nzc[31]) =
3378                                (tnz[20], tnz[21], tnz[22], tnz[23]);
3379                        }
3380                        if let Some(l) = left {
3381                            let lnz = mb_nzc.get(l).unwrap_or(&ZERO_NZC);
3382                            (nzc[8], nzc[16], nzc[24], nzc[32]) =
3383                                (lnz[3], lnz[7], lnz[11], lnz[15]);
3384                            (nzc[13], nzc[21], nzc[37], nzc[45]) =
3385                                (lnz[17], lnz[21], lnz[19], lnz[23]);
3386                        }
3387                        let mut cbfdc = 0u16;
3388                        let mut nnzs = [0u8; 24]; // parsed totalCoeff per block
3389                        let mut luma_scan = self
3390                            .scratch_luma
3391                            .take()
3392                            .unwrap_or_else(|| Box::new([[0i32; 16]; 16]));
3393                        let mut cdc = [[0i32; 4]; 2];
3394                        let mut cac = self
3395                            .scratch_cac
3396                            .take()
3397                            .unwrap_or_else(|| Box::new([[[0i32; 16]; 4]; 2]));
3398                        if cbp == 0 {
3399                            last_delta_qp = 0;
3400                        }
3401                        if cbp != 0 {
3402                            let ndc = (
3403                                top.and_then(|a| cbf_dc.get(a).copied()),
3404                                left.and_then(|a| cbf_dc.get(a).copied()),
3405                            );
3406                            let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
3407                            self.step_qp(qpd)?;
3408                            parse_mb_residual_cabac::<false>(
3409                                &mut cab,
3410                                &mut nzc,
3411                                &mut cbfdc,
3412                                ndc,
3413                                cbp_luma,
3414                                cbp_chroma,
3415                                t8,
3416                                ResidualOut {
3417                                    luma: &mut luma_scan,
3418                                    luma8: &mut luma8,
3419                                    cdc: &mut cdc,
3420                                    cac: &mut cac,
3421                                    nnzs: &mut nnzs,
3422                                },
3423                            );
3424                        }
3425                        if let Some(p) = self.mb_qp.get_mut(addr) {
3426                            *p = self.cur_qp;
3427                        }
3428                        if let Some(p) = cbf_dc.get_mut(addr) {
3429                            *p = cbfdc;
3430                        }
3431                        let _sc = rusty_h264_common::prof::scope(
3432                            rusty_h264_common::prof::Stage::DecStateCache,
3433                        );
3434                        let mn = nnz_raster_from_z(&nnzs);
3435                        if let Some(p) = mb_nzc.get_mut(addr) {
3436                            *p = mn;
3437                        }
3438                        drop(_sc);
3439                        if let Some(regions) = self.edc_regions.take() {
3440                            self.edc_giveback();
3441                            self.edc_commit_nnz(mbx, mby, t8, &nnzs, cbp_chroma);
3442                            let job = BJob {
3443                                mbx,
3444                                mby,
3445                                qp: self.cur_qp,
3446                                cbp_chroma,
3447                                skip: false,
3448                                regions,
3449                                // MT-only copies out of the scratch planes (the worker owns its job).
3450                                luma_scan: (!t8 && cbp_luma != 0).then(|| *luma_scan),
3451                                luma8: t8.then(|| *luma8),
3452                                cdc,
3453                                cac: (cbp_chroma == 2).then(|| *cac),
3454                                nnzs,
3455                            };
3456                            self.edc_send_job(EdcJob::B(Box::new(job)));
3457                        } else {
3458                            self.add_inter_residual(
3459                                mbx,
3460                                mby,
3461                                &pred_y,
3462                                &c_pred,
3463                                Some(&*luma_scan),
3464                                t8.then_some(&*luma8),
3465                                &cdc,
3466                                (cbp_chroma == 2).then_some(&*cac),
3467                                cbp_chroma,
3468                                &nnzs,
3469                            );
3470                        }
3471                        self.scratch_luma = Some(luma_scan);
3472                        self.scratch_luma8 = Some(luma8);
3473                        self.scratch_cac = Some(cac);
3474
3475                        let eos = cab.decode_terminate();
3476                        addr += 1;
3477                        mbx += 1;
3478                        if eos || addr >= total {
3479                            break;
3480                        }
3481                        continue;
3482                    }
3483                    mb_type = bmt - 23; // 23→0 (I_4x4), 24..=47→1..24 (I_16x16), 48→25 (PCM)
3484                } else {
3485                    let li = left.map_or(0, |a| cat.get(a).is_some_and(|&c| c >= 2) as usize);
3486                    let ti = top.map_or(0, |a| cat.get(a).is_some_and(|&c| c >= 2) as usize);
3487                    mb_type = parse_mb_type_i_cabac(&mut cab, li + ti);
3488                }
3489                // H-48: the CABAC intra path is INLINED in this loop, not routed through
3490                // `decode_intra_mb` (which only the CAVLC readers call) — wiring the scope
3491                // there reported ZERO calls against 480,510 intra-pred calls. All three
3492                // intra entries (I-slice, P-slice mb_type>3, B-slice bmt>=23) converge
3493                // here, so this is the one point that sees every intra MB.
3494                self.edc_intra_sync(); // intra reconstruction reads neighbour PIXELS
3495                                       // Intra prediction ALSO reads the coded_y/modes_y/inter_y grids a
3496                                       // deferred span still owes (this arm is inline — it never passes
3497                                       // decode_b_mb's flush). Caught by the tempete ARM-DIFF.
3498                self.span_flush();
3499                let _gi = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbI);
3500                // Intra bS is a constant pattern (4 on MB edges, 3 internal) — written
3501                // here because CABAC inlines I recon and never calls decode_intra_mb.
3502                if let Some(p) = self.mb_kind.get_mut(mby * self.mb_w + mbx) {
3503                    *p = rusty_h264_common::deblock::MB_KIND_INTRA;
3504                }
3505                if mb_type == 25 {
3506                    // ---- I_PCM (spec §7.3.5): 384 raw byte-aligned sample bytes inside
3507                    // the CABAC stream. The PCM marker was a terminate bin, so the engine
3508                    // has stopped; DecodeFlush + pcm_alignment_zero_bit put the samples
3509                    // at `pcm_start_byte()`, and the engine re-initialises after them
3510                    // with its CONTEXTS KEPT (§9.3.1). All three slice types (I, P via
3511                    // mbt 30, B via bmt 48) reach here as mb_type 25.
3512                    let pcm = cab.pcm_start_byte();
3513                    let end = pcm + 384;
3514                    if end > rbsp.len() {
3515                        return Err(MbError::Truncated);
3516                    }
3517                    let mut pr = BitReader::new(&rbsp[pcm..end]);
3518                    self.decode_ipcm(&mut pr, mbx, mby)?;
3519                    cab.reinit_at(end);
3520                    // Neighbour context (§7.4.5 + §9.3.3.1.1.x inferences): intra, QPy
3521                    // unchanged (no mb_qp_delta — its ctxInc resets), CodedBlockPattern
3522                    // luma/chroma inferred 15/2, every coded_block_flag (incl. DC) 1,
3523                    // nnz 16, chroma pred mode 0.
3524                    if let Some(p) = self.mb_qp.get_mut(addr) {
3525                        *p = self.cur_qp;
3526                    }
3527                    if let Some(p) = cat.get_mut(addr) {
3528                        *p = 25;
3529                    }
3530                    if let Some(p) = cmode.get_mut(addr) {
3531                        *p = 0;
3532                    }
3533                    if let Some(p) = mb_cbp.get_mut(addr) {
3534                        *p = 0x2f;
3535                    }
3536                    if let Some(p) = cbf_dc.get_mut(addr) {
3537                        *p = (1 << RP_I16_DC) | (1 << RP_CHROMA_DC) | (1 << (RP_CHROMA_DC + 1));
3538                    }
3539                    if let Some(p) = mb_nzc.get_mut(addr) {
3540                        *p = [16u8; 24];
3541                    }
3542                    last_delta_qp = 0;
3543                    let eos = cab.decode_terminate();
3544                    addr += 1;
3545                    mbx += 1;
3546                    if eos || addr >= total {
3547                        break;
3548                    }
3549                    continue;
3550                }
3551                // chroma-pred-mode ctxInc from neighbour chroma modes (1..=3).
3552                let cci = left.map_or(0, |a| {
3553                    cmode.get(a).is_some_and(|c| (1..=3).contains(c)) as usize
3554                }) + top.map_or(0, |a| {
3555                    cmode.get(a).is_some_and(|c| (1..=3).contains(c)) as usize
3556                });
3557
3558                if mb_type != 0 {
3559                    // ---- I_16x16 (mb_type 1..=24): pred mode & cbp DERIVED from mb_type;
3560                    // luma DC always coded. Syntax order: intra_chroma_pred_mode, mb_qp_delta,
3561                    // luma DC (Hadamard), luma AC (if cbp_luma), chroma DC/AC. Mirrors the CAVLC
3562                    // decode_i16, driven by the CABAC residual. ----
3563                    let mt = mb_type - 1;
3564                    let pred_mode = I16Mode::from_id(mt % 4);
3565                    let cbp_chroma = (mt % 12) / 4;
3566                    let cbp_luma_15 = mt / 12 == 1;
3567                    let chroma_mode = parse_intra_chroma_pred_mode_cabac(&mut cab, cci) as u8;
3568                    if let Some(p) = cmode.get_mut(addr) {
3569                        *p = chroma_mode as i32;
3570                    }
3571                    if let Some(p) = cat.get_mut(addr) {
3572                        *p = 2;
3573                    }
3574                    if let Some(p) = mb_cbp.get_mut(addr) {
3575                        *p = ((cbp_chroma as u8) << 4) | if cbp_luma_15 { 15 } else { 0 };
3576                    }
3577                    let w4 = self.mb_w * 4;
3578
3579                    let mut nzc = [0xffu8; 48];
3580                    if let Some(t) = top {
3581                        let tn = mb_nzc.get(t).unwrap_or(&ZERO_NZC);
3582                        nzc[1..5].copy_from_slice(&tn[12..16]);
3583                        (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
3584                        (nzc[6], nzc[7]) = (tn[20], tn[21]);
3585                        (nzc[30], nzc[31]) = (tn[22], tn[23]);
3586                    }
3587                    if let Some(l) = left {
3588                        let ln = mb_nzc.get(l).unwrap_or(&ZERO_NZC);
3589                        (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
3590                        (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
3591                    }
3592
3593                    let ndc = (
3594                        top.and_then(|a| cbf_dc.get(a).copied()),
3595                        left.and_then(|a| cbf_dc.get(a).copied()),
3596                    );
3597                    let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
3598                    self.step_qp(qpd)?;
3599                    let qp = self.cur_qp;
3600                    let mut cbfdc = 0u16;
3601                    let nd = resolve_ndc(ndc, true);
3602                    let (data, mut e, ctx) = cab.view();
3603
3604                    // Luma DC (iz=0, category I16_LUMA_DC, 16 coeffs) → Hadamard dequant.
3605                    let mut dc_scan = [0i32; 16];
3606                    residual_block_eng::<RP_I16_DC, 16>(
3607                        &mut e,
3608                        data,
3609                        ctx,
3610                        &mut nzc,
3611                        &mut cbfdc,
3612                        0,
3613                        0,
3614                        true,
3615                        nd,
3616                        &mut dc_scan,
3617                    );
3618                    // FUSED scan-order DC: un-scan + Hadamard + scale in one kernel (was 32 moves + 253 instrs).
3619                    let recon_dc = inverse_quant_luma_dc_scan(
3620                        &dc_scan,
3621                        qp,
3622                        self.scaling.as_ref().map(|s| s[0][0]),
3623                    );
3624
3625                    // Luma AC (iz 0..15, category I16_LUMA_AC, 15 coeffs) when cbp_luma set.
3626                    // Materialised only when AC is actually coded — a DC-only
3627                    // I_16x16 zeroed 1 KB of stack for nothing.
3628                    let mut q_blocks: Option<[[i32; 16]; 16]> = None;
3629                    let mut coded16 = 0u16;
3630                    for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3631                        let total = if cbp_luma_15 {
3632                            let mut ac = [0i32; 16];
3633                            let t = residual_block_eng::<RP_I16_AC, 16>(
3634                                &mut e, data, ctx, &mut nzc, &mut cbfdc, iz, 0, true, nd, &mut ac,
3635                            );
3636                            q_blocks.get_or_insert_with(|| [[0i32; 16]; 16])
3637                                [(lby & 3) * 4 + (lbx & 3)] = ac; // SCAN order (fused kernel)
3638                            t as u8
3639                        } else {
3640                            nzc[NZC_CACHE[iz.min(23)].min(47)] = 0;
3641                            0
3642                        };
3643                        coded16 |= ((total != 0) as u16) << ((lby & 3) * 4 + (lbx & 3));
3644                        if let Some(p) = self.nnz_y.get_mut((mby * 4 + lby) * w4 + (mbx * 4 + lbx))
3645                        {
3646                            *p = total;
3647                        }
3648                    }
3649
3650                    let mut cdc = [[0i32; 4]; 2];
3651                    let mut cac: Option<[[[i32; 16]; 4]; 2]> = None;
3652                    let mut cnnz = [0u8; 8];
3653                    if cbp_chroma >= 1 {
3654                        for i in 0..2usize {
3655                            residual_block_eng::<RP_CHROMA_DC, 4>(
3656                                &mut e,
3657                                data,
3658                                ctx,
3659                                &mut nzc,
3660                                &mut cbfdc,
3661                                16 + i * 4,
3662                                i,
3663                                true,
3664                                nd,
3665                                &mut cdc[i],
3666                            );
3667                        }
3668                    }
3669                    if cbp_chroma == 2 {
3670                        let cacm = cac.get_or_insert_with(|| [[[0i32; 16]; 4]; 2]);
3671                        for i in 0..2usize {
3672                            for id4 in 0..4usize {
3673                                cnnz[(i * 4 + id4) & 7] = residual_block_eng::<RP_CHROMA_AC, 16>(
3674                                    &mut e,
3675                                    data,
3676                                    ctx,
3677                                    &mut nzc,
3678                                    &mut cbfdc,
3679                                    16 + i * 4 + id4,
3680                                    i,
3681                                    true,
3682                                    nd,
3683                                    &mut cacm[i][id4],
3684                                ) as u8;
3685                            }
3686                        }
3687                    }
3688
3689                    cab.commit(e);
3690                    // Luma recon: 16×16 intra prediction, then per-4×4 (dequant AC + injected DC).
3691                    let top_ok = mby > 0
3692                        && self.nbr_in_slice(mbx, mby - 1)
3693                        && self.intra_nbr_ok(mbx * 4, mby * 4 - 1);
3694                    let left_ok = mbx > 0
3695                        && self.nbr_in_slice(mbx - 1, mby)
3696                        && self.intra_nbr_ok(mbx * 4 - 1, mby * 4);
3697                    self.recon_i16_luma(
3698                        mbx,
3699                        mby,
3700                        pred_mode,
3701                        top_ok,
3702                        left_ok,
3703                        q_blocks.as_ref(),
3704                        coded16,
3705                        &recon_dc,
3706                        qp,
3707                    );
3708                    self.recon_chroma_cabac(
3709                        mbx,
3710                        mby,
3711                        chroma_mode,
3712                        &cdc,
3713                        cac.as_ref(),
3714                        &cnnz,
3715                        cbp_chroma,
3716                        top_ok,
3717                        left_ok,
3718                    );
3719
3720                    if let Some(p) = self.mb_qp.get_mut(addr) {
3721                        *p = self.cur_qp;
3722                    }
3723                    if let Some(p) = cbf_dc.get_mut(addr) {
3724                        *p = cbfdc;
3725                    }
3726                    let mut mn = [0u8; 24];
3727                    for k in 0..4 {
3728                        mn[k] = nzc[9 + k];
3729                        mn[4 + k] = nzc[17 + k];
3730                        mn[8 + k] = nzc[25 + k];
3731                        mn[12 + k] = nzc[33 + k];
3732                    }
3733                    (mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
3734                    (mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
3735                    for v in mn.iter_mut() {
3736                        if *v == 0xff {
3737                            *v = 0;
3738                        }
3739                    }
3740                    if let Some(p) = mb_nzc.get_mut(addr) {
3741                        *p = mn;
3742                    }
3743
3744                    let eos = cab.decode_terminate();
3745                    addr += 1;
3746                    mbx += 1;
3747                    if eos || addr >= total {
3748                        break;
3749                    }
3750                    continue;
3751                }
3752                if let Some(p) = cat.get_mut(addr) {
3753                    *p = 0;
3754                }
3755                let w4 = self.mb_w * 4;
3756                // H-49: transform_size_8x8_flag. For I_NxN it precedes the intra pred
3757                // modes (spec §7.3.5); ctxIdx = 399 + condTermFlagA + condTermFlagB,
3758                // each 1 when that neighbour MB carries the flag. Omitting this read is
3759                // what desynced every High-profile stream.
3760                let t8 = self.transform_8x8_mode && {
3761                    let a = left.map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
3762                    let b = top.map_or(0, |x| self.mb_t8x8.get(x).is_some_and(|&f| f) as usize);
3763                    cab.decode_decision(399 + a + b) != 0
3764                };
3765                if let Some(p) = self.mb_t8x8.get_mut(addr) {
3766                    *p = t8;
3767                }
3768                self.any_t8 |= t8;
3769                // Hoisted ABOVE the mode loop (it was computed after it) so the
3770                // per-block mode prediction can use it - see `predict_i4_mode_fast`.
3771                let top_ok = mby > 0
3772                    && self.nbr_in_slice(mbx, mby - 1)
3773                    && self.intra_nbr_ok(mbx * 4, mby * 4 - 1);
3774                let left_ok = mbx > 0
3775                    && self.nbr_in_slice(mbx - 1, mby)
3776                    && self.intra_nbr_ok(mbx * 4 - 1, mby * 4);
3777                // Brick 2.4 + recon: derive & store each intra mode (prev-flag → the
3778                // neighbour-predicted mode, else rem), exactly as the CAVLC path.
3779                let mut modes = [2u8; 16]; // raster [lby*4+lbx]
3780                let mut modes8 = [2u8; 4]; // one per 8×8 when t8
3781                                           // ONE engine view for all 16 (or 4) pred-mode reads of the macroblock.
3782                let (data, mut e, ctx) = cab.view();
3783                if t8 {
3784                    // One mode per 8×8, broadcast to its four 4×4 cells so neighbour
3785                    // mode prediction keeps working unchanged.
3786                    for b8 in 0..4usize {
3787                        let (b8x, b8y) = (b8 % 2, b8 / 2);
3788                        let (bx, by) = (mbx * 4 + b8x * 2, mby * 4 + b8y * 2);
3789                        let predicted =
3790                            self.predict_i4_mode_fast(bx, by, b8x * 2, b8y * 2, top_ok, left_ok);
3791                        let rr = intra4x4_pred_mode_eng(&mut e, data, ctx);
3792                        let actual = if rr < 0 {
3793                            predicted
3794                        } else {
3795                            let rem = rr as u8;
3796                            if rem < predicted {
3797                                rem
3798                            } else {
3799                                rem + 1
3800                            }
3801                        };
3802                        modes8[b8] = actual;
3803                        for dy in 0..2 {
3804                            for dx in 0..2 {
3805                                if let Some(p) = self.modes_y.get_mut((by + dy) * w4 + (bx + dx)) {
3806                                    *p = actual;
3807                                }
3808                                modes[(b8y * 2 + dy) * 4 + (b8x * 2 + dx)] = actual;
3809                            }
3810                        }
3811                    }
3812                } else {
3813                    for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3814                        let (bx, by) = (mbx * 4 + lbx, mby * 4 + lby);
3815                        let predicted =
3816                            self.predict_i4_mode_fast(bx, by, lbx, lby, top_ok, left_ok);
3817                        let rr = intra4x4_pred_mode_eng(&mut e, data, ctx);
3818                        let actual = if rr < 0 {
3819                            predicted
3820                        } else {
3821                            let rem = rr as u8;
3822                            if rem < predicted {
3823                                rem
3824                            } else {
3825                                rem + 1
3826                            }
3827                        };
3828                        if let Some(m) = self.modes_y.get_mut(by * w4 + bx) {
3829                            *m = actual;
3830                        }
3831                        modes[(lby & 3) * 4 + (lbx & 3)] = actual;
3832                    }
3833                }
3834                cab.commit(e);
3835                let chroma_mode = parse_intra_chroma_pred_mode_cabac(&mut cab, cci) as u8;
3836                if let Some(p) = cmode.get_mut(addr) {
3837                    *p = chroma_mode as i32;
3838                }
3839                let cbp = parse_cbp_cabac(
3840                    &mut cab,
3841                    top.and_then(|a| mb_cbp.get(a).copied()),
3842                    left.and_then(|a| mb_cbp.get(a).copied()),
3843                );
3844                if let Some(p) = mb_cbp.get_mut(addr) {
3845                    *p = cbp as u8;
3846                }
3847                let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
3848
3849                // Build the padded nzc cache from neighbours (openh264 WelsFillCacheNonZeroCount).
3850                let mut nzc = [0xffu8; 48];
3851                if let Some(t) = top {
3852                    let tn = mb_nzc.get(t).unwrap_or(&ZERO_NZC);
3853                    nzc[1..5].copy_from_slice(&tn[12..16]);
3854                    (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
3855                    (nzc[6], nzc[7]) = (tn[20], tn[21]);
3856                    (nzc[30], nzc[31]) = (tn[22], tn[23]);
3857                }
3858                if let Some(l) = left {
3859                    let ln = mb_nzc.get(l).unwrap_or(&ZERO_NZC);
3860                    (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
3861                    (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
3862                }
3863
3864                // Bricks 2.6 + 2.7: mb_qp_delta + residual (I_4x4 luma 4×4 + chroma DC/AC),
3865                // storing scan-order coefficients for recon.
3866                let mut cbfdc = 0u16;
3867                // Both materialised only when the parse writes them: an I_8x8
3868                // macroblock never touches luma_scan (it carries luma8), and
3869                // cbp_chroma < 2 never touches cac.
3870                // Scratch planes (see `scratch_luma`): zeroed per coded block by the parse.
3871                let mut luma_scan = self
3872                    .scratch_luma
3873                    .take()
3874                    .unwrap_or_else(|| Box::new([[0i32; 16]; 16]));
3875                let mut luma8 = self
3876                    .scratch_luma8
3877                    .take()
3878                    .unwrap_or_else(|| Box::new([[0i32; 64]; 4]));
3879                let mut cdc = [[0i32; 4]; 2];
3880                let mut cac = self
3881                    .scratch_cac
3882                    .take()
3883                    .unwrap_or_else(|| Box::new([[[0i32; 16]; 4]; 2]));
3884                let mut nnzs = [0u8; 24]; // parse-side per-block coeff counts
3885                if cbp == 0 {
3886                    last_delta_qp = 0;
3887                }
3888                if cbp != 0 {
3889                    let ndc = (
3890                        top.and_then(|a| cbf_dc.get(a).copied()),
3891                        left.and_then(|a| cbf_dc.get(a).copied()),
3892                    );
3893                    let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
3894                    self.step_qp(qpd)?;
3895                    parse_mb_residual_cabac::<true>(
3896                        &mut cab,
3897                        &mut nzc,
3898                        &mut cbfdc,
3899                        ndc,
3900                        cbp_luma,
3901                        cbp_chroma,
3902                        t8,
3903                        ResidualOut {
3904                            luma: &mut luma_scan,
3905                            luma8: &mut luma8,
3906                            cdc: &mut cdc,
3907                            cac: &mut cac,
3908                            nnzs: &mut nnzs,
3909                        },
3910                    );
3911                    if t8 {
3912                        // Per-cell nnz for the deblock/neighbour readers: the 8x8 total
3913                        // (0 for an uncoded 8x8) in each of its four 4x4 cells.
3914                        for id8 in 0..4usize {
3915                            let n = nnzs[id8 * 4];
3916                            let (b8x, b8y) = (id8 % 2, id8 / 2);
3917                            for sy in 0..2 {
3918                                for sx in 0..2 {
3919                                    if let Some(p) = self.nnz_y.get_mut(
3920                                        (mby * 4 + b8y * 2 + sy) * w4 + (mbx * 4 + b8x * 2 + sx),
3921                                    ) {
3922                                        *p = n;
3923                                    }
3924                                }
3925                            }
3926                        }
3927                    }
3928                }
3929                if let Some(p) = self.mb_qp.get_mut(addr) {
3930                    *p = self.cur_qp;
3931                }
3932                if let Some(p) = cbf_dc.get_mut(addr) {
3933                    *p = cbfdc;
3934                }
3935                // Extract the MB's nzc (raster luma + chroma) for future neighbours.
3936                let mn = nnz_raster_from_z(&nnzs);
3937                if let Some(p) = mb_nzc.get_mut(addr) {
3938                    *p = mn;
3939                }
3940
3941                // ---- Brick 4.3a: recon (I_4x4 luma + chroma) via the CAVLC-proven primitives.
3942                let qp = self.cur_qp;
3943
3944                if t8 {
3945                    // I_8x8 recon, reusing the CAVLC-proven primitives verbatim
3946                    // (un_scan_8x8 / inv_quant8 / gather_i8 / intra8x8_pred /
3947                    // add_residual_8x8). Only the ENTROPY half differed.
3948                    for b8 in 0..4usize {
3949                        let (b8x, b8y) = (b8 % 2, b8 / 2);
3950                        let (bx, by) = (mbx * 4 + b8x * 2, mby * 4 + b8y * 2);
3951                        let coded = cbp_luma & (1 << b8) != 0;
3952                        let avail_top = b8y > 0 || top_ok;
3953                        let avail_left = b8x > 0 || left_ok;
3954                        self.recon_i8_block(
3955                            bx,
3956                            by,
3957                            modes8[b8],
3958                            avail_top,
3959                            avail_left,
3960                            coded.then_some(&luma8[b8]),
3961                            qp,
3962                        );
3963                        for sy in 0..2 {
3964                            // Row fill: the 2x2 cell block is two contiguous PAIRS.
3965                            self.coded_y[(by + sy) * w4 + bx..][..2].fill(true);
3966                        }
3967                    }
3968                }
3969                // The `t8` guard was INSIDE the loop (breaking on the first
3970                // iteration) and the `luma_scan` Option was re-resolved on EVERY one
3971                // of the sixteen blocks. Both are macroblock-invariant.
3972                if !t8 {
3973                    let scans = &*luma_scan;
3974                    // Residual ladders decided up front (`i4_prepare`), then the serial
3975                    // predict+add walk with the SIMD IDCT+add kernel per coded block.
3976                    let lnnz: [u8; 16] = core::array::from_fn(|i| nnzs[i]);
3977                    let kinds = self.i4_prepare(scans, &lnnz, qp);
3978                    let dq0 = self.dq_const(qp, 0);
3979                    for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3980                        let (bx, by) = (mbx * 4 + lbx, mby * 4 + lby);
3981                        let at = lby > 0 || top_ok;
3982                        let al = lbx > 0 || left_ok;
3983                        self.recon_i4_block_res(
3984                            bx,
3985                            by,
3986                            modes[(lby & 3) * 4 + (lbx & 3)],
3987                            at,
3988                            al,
3989                            kinds[blk & 15],
3990                            scans,
3991                            &dq0,
3992                        );
3993                    }
3994                    // Per-MB ROW COPIES for nnz_y (from the raster `mn` already built) and
3995                    // coded_y (routing round): sixteen + sixteen checked scattered stores
3996                    // per macroblock -> four + four row operations. Nothing inside this
3997                    // macroblock reads either grid for its own blocks (interior top-right
3998                    // availability is the z-order constant now).
3999                    for ry in 0..4usize {
4000                        let a = (mby * 4 + ry) * w4 + mbx * 4;
4001                        self.nnz_y[a..a + 4].copy_from_slice(&mn[ry * 4..ry * 4 + 4]);
4002                        self.coded_y[a..a + 4].fill(true);
4003                    }
4004                }
4005                self.recon_chroma_cabac(
4006                    mbx,
4007                    mby,
4008                    chroma_mode,
4009                    &cdc,
4010                    (cbp_chroma == 2).then_some(&*cac),
4011                    nnzs[16..24].try_into().expect("8 chroma counts"),
4012                    cbp_chroma,
4013                    top_ok,
4014                    left_ok,
4015                );
4016                self.scratch_luma = Some(luma_scan);
4017                self.scratch_luma8 = Some(luma8);
4018                self.scratch_cac = Some(cac);
4019
4020                // Brick 2.1: end_of_slice_flag.
4021                let eos = cab.decode_terminate();
4022                addr += 1;
4023                mbx += 1;
4024                if eos || addr >= total {
4025                    break;
4026                }
4027            }
4028        }
4029        if trace {
4030            eprintln!("# CABAC decoded {} MBs (of {total})", addr - first_mb);
4031        }
4032        self.edc_flush(); // slice end: no job crosses a slice boundary
4033                          // Return the pooled slice scratch (an error exit forfeits it — the
4034                          // next slice then refills fresh allocations, correctness unchanged).
4035        self.sc_cat = cat;
4036        self.sc_cbp = mb_cbp;
4037        self.sc_cmode = cmode;
4038        self.sc_nzc = mb_nzc;
4039        self.sc_cbfdc = cbf_dc;
4040        self.sc_skip = mb_skip;
4041        self.sc_ref = mb_ref;
4042        self.sc_mvd = mb_mvd;
4043        if want_b_grids {
4044            self.sc_ref1 = mb_ref1;
4045            self.sc_mvd1 = mb_mvd1;
4046            self.sc_direct = mb_direct;
4047        }
4048        Ok(addr)
4049    }
4050
4051    /// CABAC chroma recon (mirrors `decode_chroma`'s reconstruction, driven by the
4052    /// CABAC-parsed DC/AC coefficients). `cdc[c]` = 2×2 DC (scan order); `cac[c][blk]`
4053    /// = 15 AC per 4×4 block (scan order).
4054    #[allow(clippy::too_many_arguments)]
4055    /// Add a CABAC-parsed inter residual to an already-built motion-comp prediction
4056    /// (`pred_y`/`c_pred`), writing the reconstruction. Shared by the P and B inter
4057    /// paths — same `reconstruct_4x4` as intra, MC output as the prediction, inter
4058    /// scaling lists (luma 3 / chroma 4+c). `luma_scan[z]`/`cdc`/`cac` are the
4059    /// scan-order coefficients; uncoded blocks are zero so recon == prediction.
4060    #[allow(clippy::too_many_arguments)]
4061    fn add_inter_residual(
4062        &mut self,
4063        mb_x: usize,
4064        mb_y: usize,
4065        pred_y: &[u8; 256],
4066        c_pred: &[[u8; 64]; 2],
4067        luma_scan: Option<&[[i32; 16]; 16]>,
4068        // `Some` when the macroblock carries transform_size_8x8_flag: four 8x8
4069        // blocks in 8x8 scan order, replacing the sixteen 4x4 luma blocks.
4070        luma8: Option<&[[i32; 64]; 4]>,
4071        cdc: &[[i32; 4]; 2],
4072        cac: Option<&[[[i32; 16]; 4]; 2]>,
4073        cbp_chroma: u32,
4074        // Parsed totalCoeff per block, indexed exactly as the parse's `iz`:
4075        // [0..16] luma 4x4 z-order (for t8, the 8x8 count sits at `id8*4`),
4076        // [16..24] chroma AC as `16 + c*4 + id4`. The parser already counted
4077        // every significant coefficient; re-deriving the counts here scanned
4078        // 16-64 array elements per block (~400 loads/MB) for information the
4079        // caller was holding — the diagnosis's stage-boundary re-derivation tax.
4080        nnzs: &[u8; 24],
4081    ) {
4082        // A `None` field means the parse wrote nothing there; every read below
4083        // is guarded by a zero-count test, so the shared zero plane is
4084        // read-equivalent to the per-macroblock zeroed stack array it replaces.
4085        let luma_scan = luma_scan.unwrap_or(&ZERO_LUMA_SCAN);
4086        let cac = cac.unwrap_or(&ZERO_CAC);
4087        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecResidAdd);
4088        let qp = self.cur_qp;
4089        let qpc = self.chroma_qp_for(qp);
4090        let (w4r, w2r) = (self.mb_w * 4, self.mb_w * 2);
4091        if let Some(l8) = luma8 {
4092            // INTER 8x8 luma: same primitives the I_8x8 and CAVLC paths use.
4093            for b8 in 0..4usize {
4094                let (b8x, b8y) = (b8 % 2, b8 / 2);
4095                // PER-CELL, not one aggregate broadcast over all four cells. CAVLC
4096                // codes an 8x8 block as four 4x4 sub-blocks and its nC predictor
4097                // reads these per-4x4 counts from `nnz_y`, so the broadcast
4098                // corrupted the NEXT macroblock's nC and desynced the parse -- which
4099                // is why CAVLC 8x8 streams ffmpeg accepts would not decode here. The
4100                // worker copy of this function never wrote `nnz_y` at all, so the
4101                // threaded path was unaffected and hid the defect. CABAC has no
4102                // per-4x4 counts, so its callers put the 8x8 total in all four slots.
4103                let nnz: u32 = (0..4).map(|k| nnzs[b8 * 4 + k] as u32).sum();
4104                // Two row slices, not four whole-grid indexed stores. The pair
4105                // written per row is contiguous and `nnzs` is a fixed 24-entry
4106                // array, so both sides become provable.
4107                for sy in 0..2 {
4108                    let base = (mb_y * 4 + b8y * 2 + sy) * w4r + mb_x * 4 + b8x * 2;
4109                    self.nnz_y[base..][..2].copy_from_slice(&nnzs[b8 * 4 + sy * 2..][..2]);
4110                }
4111                // The 4x4 inter path marks coded_y per block; the 8x8 branch must too,
4112                // or a later intra macroblock's neighbour availability is wrong.
4113                for sy in 0..2 {
4114                    let base = (mb_y * 4 + b8y * 2 + sy) * w4r + mb_x * 4 + b8x * 2;
4115                    self.coded_y[base..][..2].fill(true);
4116                }
4117                let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
4118                if nnz == 0 {
4119                    // Zero residual: recon == pred — the 4x4 arm zero shortcut,
4120                    // ported to the 8x8 branch.
4121                    edcstat::bump(&edcstat::T8_ZERO, 1);
4122                    for dy in 0..8 {
4123                        let d = (py + dy) * self.cw + px;
4124                        let po = (b8y * 8 + dy) * 16 + b8x * 8;
4125                        self.rec_y[d..d + 8].copy_from_slice(&pred_y[po..po + 8]);
4126                    }
4127                } else {
4128                    let raster = un_scan_8x8(&l8[b8]);
4129                    // list 1 = INTER 8x8 luma scaling list (0 is the intra one).
4130                    let res8 = self.inv_quant8(&raster, qp, 1);
4131                    let predb: [i32; 64] = core::array::from_fn(|i| {
4132                        pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
4133                    });
4134                    let recon = add_residual_8x8(&res8, &predb);
4135                    // Eight row copies. This wrote SIXTY-FOUR individually
4136                    // bounds-checked samples into the luma plane per coded 8x8
4137                    // block — the same shape whose fix carried `recon_i8_block`.
4138                    // Present in BOTH the main path and the EDC worker twin.
4139                    for dy in 0..8 {
4140                        let d = (py + dy) * self.cw + px;
4141                        self.rec_y[d..][..8].copy_from_slice(&recon[dy * 8..][..8]);
4142                    }
4143                }
4144            }
4145        }
4146        // RESIDUAL LADDER, then the SIMD IDCT+add kernel per parked block (SIMD census
4147        // 2026-09-05). The zero and DC-only ladders write the plane in the loop;
4148        // every other coded block parks its dequantised coefficients here and is
4149        // reconstructed by `reconstruct_4x4_into` = accel `idct4x4_add` (in-register
4150        // transposes, exact on i32). A lane-per-block BATCHED form was tried first
4151        // and lost on all-intra content: its scalar gathers cost more than the
4152        // butterflies they vectorised.
4153        let dq3 = self.dq_const(qp, 3);
4154        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
4155            if luma8.is_some() {
4156                break;
4157            }
4158            let nnz = nnzs[blk];
4159            if let Some(p) = self
4160                .nnz_y
4161                .get_mut((mb_y * 4 + lby) * w4r + (mb_x * 4 + lbx))
4162            {
4163                *p = nnz;
4164            }
4165            let cw = self.cw;
4166            let p_off = (lby * 4) * 16 + lbx * 4;
4167            let r_off = (mb_y * 4 + lby) * 4 * cw + (mb_x * 4 + lbx) * 4;
4168            if nnz == 0 {
4169                // Zero residual → recon == prediction EXACTLY (the integer IDCT is
4170                // linear so zeros map to zeros, and pred is already 0..=255) — copy
4171                // the pred rows straight into the plane. On real (sparse-cbp)
4172                // streams this is MOST of the 4×4 blocks.
4173                for r in 0..4 {
4174                    self.rec_y[r_off + r * cw..r_off + r * cw + 4]
4175                        .copy_from_slice(&pred_y[p_off + r * 16..p_off + r * 16 + 4]);
4176                }
4177                continue;
4178            }
4179            // DC-ONLY: the sole significant coefficient is scan position 0 (the
4180            // zig-zag starts at DC, and un_scan keeps it at raster 0), so the
4181            // whole dequant + IDCT collapses to one multiply and a flat add.
4182            if nnz == 1 && luma_scan[blk][0] != 0 {
4183                let f = self.dequant_dc4(luma_scan[blk][0], qp, 3);
4184                reconstruct_4x4_dc_into(
4185                    (f + 32) >> 6,
4186                    pred_y,
4187                    p_off,
4188                    16,
4189                    &mut self.rec_y,
4190                    r_off,
4191                    cw,
4192                );
4193            } else {
4194                // Fused un-scan + dequant over ONLY the significant coefficients,
4195                // then IDCT + add + clip straight into the plane — no `qb`, no
4196                // `deq`-from-dense, no `predb` gather, no `s`, no `store` call.
4197                //
4198                // HYBRID: the scatter walks scan positions with a data-dependent
4199                // branch per slot, which beats the branchless dense 16-multiply
4200                // loop only while the block is SPARSE. The DC/zero fast paths
4201                // already removed the sparsest blocks, so the population here
4202                // skews denser — above ~6 coefficients the dense loop wins.
4203                // FUSED: un-scan + dequant + IDCT + add in one kernel call (dense-over-scatter round).
4204                reconstruct_4x4_scan_into::<false>(
4205                    &luma_scan[blk],
4206                    &dq3,
4207                    0,
4208                    pred_y,
4209                    p_off,
4210                    16,
4211                    &mut self.rec_y,
4212                    r_off,
4213                    cw,
4214                );
4215            }
4216        }
4217        let mut c_dc = [[0i32; 4]; 2];
4218        if cbp_chroma != 0 {
4219            for c in 0..2 {
4220                c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 4 + c);
4221            }
4222        }
4223        let dqc = [self.dq_const(qpc, 4), self.dq_const(qpc, 5)];
4224        let ccw = self.ccw;
4225        for c in 0..2 {
4226            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4227                let mut ac_nz = false;
4228                if cbp_chroma == 2 {
4229                    let n = nnzs[(16 + c * 4 + by * 2 + bx).min(23)];
4230                    if let Some(p) =
4231                        self.nnz_c[c & 1].get_mut((mb_y * 2 + by) * w2r + (mb_x * 2 + bx))
4232                    {
4233                        *p = n;
4234                    }
4235                    ac_nz = n != 0;
4236                }
4237                let dc = c_dc[c & 1][(by * 2 + bx) & 3];
4238                let p_off = (by * 4) * 8 + bx * 4;
4239                let r_off = (mb_y * 2 + by) * 4 * ccw + (mb_x * 2 + bx) * 4;
4240                let plane = if c == 0 {
4241                    &mut self.rec_u
4242                } else {
4243                    &mut self.rec_v
4244                };
4245                if dc == 0 && !ac_nz {
4246                    // Zero residual (no AC, zero DC) → recon == prediction exactly.
4247                    for r in 0..4 {
4248                        plane[r_off + r * ccw..r_off + r * ccw + 4]
4249                            .copy_from_slice(&c_pred[c][p_off + r * 8..p_off + r * 8 + 4]);
4250                    }
4251                    continue;
4252                }
4253                // DC-ONLY (no coded AC — covers every cbp_chroma==1 block and the
4254                // AC-empty blocks of cbp_chroma==2): the chroma DC arrives ALREADY
4255                // dequantized, so the residual is `(dc + 32) >> 6` flat.
4256                if !ac_nz {
4257                    reconstruct_4x4_dc_into(
4258                        (dc + 32) >> 6,
4259                        &c_pred[c],
4260                        p_off,
4261                        8,
4262                        plane,
4263                        r_off,
4264                        ccw,
4265                    );
4266                    continue;
4267                }
4268                // AC-only scan: index i is overall scan position i+1 (ac_shift=1).
4269                // Same sparse/dense hybrid as luma.
4270                reconstruct_4x4_scan_into::<true>(
4271                    &cac[c & 1][(by * 2 + bx) & 3],
4272                    &dqc[c & 1],
4273                    dc,
4274                    &c_pred[c],
4275                    p_off,
4276                    8,
4277                    plane,
4278                    r_off,
4279                    ccw,
4280                );
4281            }
4282        }
4283    }
4284
4285    // ── SHARED INTRA RECON PRIMITIVES ──────────────────────────────────
4286    // ONE implementation per block family, called by BOTH entropy arms.
4287    // The per-coder recon twins are where routing rot lived: the residual
4288    // ladder and the DC-only collapse each had to be found and ported
4289    // twin-by-twin. The parse halves stay per-coder (that is the real
4290    // difference between the coders); the pixel halves live here — the
4291    // same convergence D14 gave the inter path via add_inter_residual.
4292
4293    /// The residual ladder of an intra 4x4 block, decided BEFORE the serial
4294    /// predict+add walk (SIMD census 2026-09-05); each coded block is then
4295    /// reconstructed by the SIMD IDCT+add kernel.
4296    /// Zero -> recon == pred; Flat -> DC-only, one value; Idx -> a slot in the
4297    /// batched residual array.
4298    fn i4_prepare(&self, scans: &[[i32; 16]; 16], nnzs: &[u8; 16], qp: u8) -> [I4Res; 16] {
4299        let mut kinds = [I4Res::Zero; 16];
4300        for blk in 0..16 {
4301            let (nnz, scan) = (nnzs[blk], &scans[blk]);
4302            kinds[blk] = if nnz == 0 {
4303                edcstat::bump(&edcstat::I4_ZERO, 1);
4304                I4Res::Zero
4305            } else if nnz == 1 && scan[0] != 0 {
4306                edcstat::bump(&edcstat::I4_DC, 1);
4307                let f = self.dequant_dc4(scan[0], qp, 0);
4308                I4Res::Flat((f + 32) >> 6)
4309            } else {
4310                edcstat::bump(&edcstat::I4_DENSE, 1);
4311                I4Res::Idx(blk as u8) // dense: the fused kernel takes the scan block
4312            };
4313        }
4314        kinds
4315    }
4316
4317    /// Intra 4x4 luma block with its residual PRE-TRANSFORMED by [`Self::i4_prepare`]:
4318    /// gather + predict (serial, depends on the previous block's recon) + add.
4319    #[allow(clippy::too_many_arguments)]
4320    fn recon_i4_block_res(
4321        &mut self,
4322        bx: usize,
4323        by: usize,
4324        mode: u8,
4325        at: bool,
4326        al: bool,
4327        kind: I4Res,
4328        scans: &[[i32; 16]; 16],
4329        dq: &DequantQp,
4330    ) {
4331        let (px, py) = (bx * 4, by * 4);
4332        let (t, l, corner) = self.gather_i4(px, py, at, al, bx, by);
4333        let pred = intra4x4_pred(mode, at, al, &t, &l, corner);
4334        let r_off = py * self.cw + px;
4335        let cw = self.cw;
4336        match kind {
4337            I4Res::Zero => {
4338                let win = &mut self.rec_y[r_off..r_off + 3 * cw + 4];
4339                for r in 0..4 {
4340                    win[r * cw..r * cw + 4].copy_from_slice(&pred[r * 4..r * 4 + 4]);
4341                }
4342            }
4343            I4Res::Flat(v) => reconstruct_4x4_dc_into(v, &pred, 0, 4, &mut self.rec_y, r_off, cw),
4344            I4Res::Idx(i) => reconstruct_4x4_scan_into::<false>(
4345                &scans[i as usize & 15],
4346                dq,
4347                0,
4348                &pred,
4349                0,
4350                4,
4351                &mut self.rec_y,
4352                r_off,
4353                cw,
4354            ),
4355        }
4356        // coded_y is filled per macroblock row by the callers (routing round).
4357    }
4358
4359    /// Intra 8x8 luma block: predict + zero-arm or un-scan/quant/add.
4360    /// `coeffs` = SCAN-order 8x8 coefficients, `None` when the cbp bit is
4361    /// unset. `coded_y` marking stays with the callers (their orders differ).
4362    fn recon_i8_block(
4363        &mut self,
4364        bx: usize,
4365        by: usize,
4366        mode: u8,
4367        avail_top: bool,
4368        avail_left: bool,
4369        coeffs: Option<&[i32; 64]>,
4370        qp: u8,
4371    ) {
4372        let (px, py) = (bx * 4, by * 4);
4373        let (t, l, corner, avail_corner) = self.gather_i8(px, py, avail_top, avail_left, bx, by);
4374        let pred = intra8x8_pred(mode, avail_top, avail_left, avail_corner, &t, &l, corner);
4375        match coeffs {
4376            None => {
4377                // Zero residual: recon == pred exactly.
4378                edcstat::bump(&edcstat::T8_ZERO, 1);
4379                let (cw, base) = (self.cw, py * self.cw + px);
4380                let win = &mut self.rec_y[base..base + 7 * cw + 8];
4381                for dy in 0..8 {
4382                    win[dy * cw..dy * cw + 8].copy_from_slice(&pred[dy * 8..dy * 8 + 8]);
4383                }
4384            }
4385            Some(scan8) => {
4386                let raster = un_scan_8x8(scan8);
4387                let res8 = self.inv_quant8(&raster, qp, 0);
4388                // Written once rather than zeroed-then-filled.
4389                let predb: [i32; 64] = core::array::from_fn(|i| pred[i] as i32);
4390                let recon = add_residual_8x8(&res8, &predb);
4391                // ROW COPIES, NOT PER-PIXEL STORES. This wrote all 64 samples
4392                // one at a time, each separately bounds-checked, where the
4393                // destination is eight contiguous 8-byte runs `cw` apart.
4394                // REFUTED, do not retry: replacing this WINDOW + sub-slice with
4395                // eight direct `rec_y[d..][..8]` slices left the panic count
4396                // EXACTLY unchanged (12) and cost +3.0% instructions. Neither
4397                // form folds the check, but the window amortises the base
4398                // address computation across the eight rows. (Contrast
4399                // `gather_tile`, where a window cost +57.6% — a window pays only
4400                // when the rows it spans are written in one tight loop.)
4401                let (cw, base) = (self.cw, py * self.cw + px);
4402                let win = &mut self.rec_y[base..base + 7 * cw + 8];
4403                for dy in 0..8 {
4404                    win[dy * cw..dy * cw + 8].copy_from_slice(&recon[dy * 8..dy * 8 + 8]);
4405                }
4406            }
4407        }
4408    }
4409
4410    /// I_16x16 luma: neighbor gather + prediction + the 16 4x4 AC blocks
4411    /// with the DC-only collapse. `q_blocks` = RASTER AC (slot 0 unused),
4412    /// `recon_dc` = the Hadamard-dequantized DC per block. Marks modes_y
4413    /// (I_16x16 predicts as DC for neighbors) + coded_y.
4414    #[allow(clippy::too_many_arguments)]
4415    fn recon_i16_luma(
4416        &mut self,
4417        mbx: usize,
4418        mby: usize,
4419        pred_mode: rusty_h264_common::predict::I16Mode,
4420        top_ok: bool,
4421        left_ok: bool,
4422        q_blocks: Option<&[[i32; 16]; 16]>,
4423        coded: u16,
4424        recon_dc: &[i32; 16],
4425        qp: u8,
4426    ) {
4427        // `None` on a DC-only I_16x16 macroblock (CodedBlockPatternLuma == 0):
4428        // no AC was parsed, so the shared zero plane is read-equivalent to the
4429        // 1 KB of stack this used to zero per macroblock.
4430        let q_blocks = q_blocks.unwrap_or(&ZERO_LUMA_SCAN);
4431        let w4 = self.mb_w * 4;
4432        let (lx, ly) = (mbx * 16, mby * 16);
4433        let mut t16 = [0u8; 16];
4434        let mut l16 = [0u8; 16];
4435        if top_ok {
4436            t16.copy_from_slice(self.top_y_row(ly, lx, 16));
4437        }
4438        if left_ok {
4439            // ONE check for the 16-sample column (see `gather_i4`).
4440            // `step_by` walk rather than a span + `col[i * cw]` (see `gather_i8`):
4441            // the span form's inner index stayed checked on all sixteen samples.
4442            let (cw, base) = (self.cw, ly * self.cw + lx - 1);
4443            for (s, &v) in l16.iter_mut().zip(self.rec_y[base..].iter().step_by(cw)) {
4444                *s = v;
4445            }
4446        }
4447        let corner = if top_ok && left_ok {
4448            self.top_y_px(ly, lx - 1)
4449        } else {
4450            0
4451        };
4452        let pred_l = luma16x16_pred(pred_mode, top_ok, left_ok, &t16, &l16, corner);
4453        // AC blocks parked, then the SIMD IDCT+add kernel per block (the pred is whole).
4454        let dq0 = self.dq_const(qp, 0);
4455        for by in 0..4 {
4456            for bx in 0..4 {
4457                let p_off = (by * 4) * 16 + bx * 4;
4458                let r_off = (ly + by * 4) * self.cw + lx + bx * 4;
4459                // ROUTED ON THE PARSE`S CODED MASK (routing round): was a 16-word compare.
4460                if coded & (1u16 << ((by & 3) * 4 + (bx & 3))) == 0 {
4461                    // Zero AC: the residual is the Hadamard DC alone.
4462                    edcstat::bump(&edcstat::I16_DCONLY, 1);
4463                    reconstruct_4x4_dc_into(
4464                        (recon_dc[by * 4 + bx] + 32) >> 6,
4465                        &pred_l,
4466                        p_off,
4467                        16,
4468                        &mut self.rec_y,
4469                        r_off,
4470                        self.cw,
4471                    );
4472                } else {
4473                    // q_blocks holds the SCAN-order AC; the fused kernel un-scans, dequantises
4474                    // and inserts the Hadamard DC.
4475                    reconstruct_4x4_scan_into::<true>(
4476                        &q_blocks[(by & 3) * 4 + (bx & 3)],
4477                        &dq0,
4478                        recon_dc[by * 4 + bx],
4479                        &pred_l,
4480                        p_off,
4481                        16,
4482                        &mut self.rec_y,
4483                        r_off,
4484                        self.cw,
4485                    );
4486                }
4487            }
4488        }
4489        // ROW FILLS. The mode/coded grids were written one 4x4 cell at a time
4490        // inside the recon loop; each macroblock row is four CONTIGUOUS cells,
4491        // so four fills replace sixteen indexed stores.
4492        for by in 0..4 {
4493            let r = (mby * 4 + by) * w4 + mbx * 4;
4494            self.modes_y[r..r + 4].fill(2);
4495            self.coded_y[r..r + 4].fill(true);
4496        }
4497    }
4498
4499    /// Chroma 8x8 recon, both planes: prediction + four 4x4 blocks per
4500    /// plane with the DC-only collapse. `qac` = RASTER AC per plane/block
4501    /// (all-zero when uncoded), `dc` = the 2x2-Hadamard-dequantized DC.
4502    #[allow(clippy::too_many_arguments)]
4503    fn recon_chroma_blocks(
4504        &mut self,
4505        mb_x: usize,
4506        mb_y: usize,
4507        chroma_mode: u8,
4508        avail_top: bool,
4509        avail_left: bool,
4510        qac: &[[[i32; 16]; 4]; 2],
4511        coded: [u8; 2],
4512        dc: &[[i32; 4]; 2],
4513        qpc: u8,
4514    ) {
4515        let (cx, cy) = (mb_x * 8, mb_y * 8);
4516        let dqc = [self.dq_const(qpc, 1), self.dq_const(qpc, 2)];
4517        for c in 0..2 {
4518            let mut ctop = [0u8; 8];
4519            let mut cleft = [0u8; 8];
4520            let mut ccorner = 0u8;
4521            {
4522                let rec_c = if c == 0 { &self.rec_u } else { &self.rec_v };
4523                if avail_top {
4524                    ctop.copy_from_slice(self.top_c_row(c, cy, cx, 8));
4525                }
4526                if avail_left {
4527                    // Strided WALK, as in `gather_i4`/`gather_i8`.
4528                    let cbase = cy * self.ccw + cx - 1;
4529                    for (o, &v) in cleft
4530                        .iter_mut()
4531                        .zip(rec_c[cbase..].iter().step_by(self.ccw))
4532                    {
4533                        *o = v;
4534                    }
4535                }
4536                if avail_top && avail_left {
4537                    ccorner = self.top_c_px(c, cy, cx - 1);
4538                }
4539            }
4540            let pred8 = chroma8x8_pred(chroma_mode, avail_top, avail_left, &ctop, &cleft, ccorner);
4541            // Coded AC blocks parked (up to 4), then the SIMD IDCT+add kernel per block.
4542            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4543                let p_off = (by * 4) * 8 + bx * 4;
4544                let ccw = self.ccw;
4545                let r_off = (cy + by * 4) * ccw + cx + bx * 4;
4546                if coded[c & 1] & (1u8 << ((by * 2 + bx) & 3)) == 0 {
4547                    // Zero AC (cbp_chroma <= 1, the common case): DC-alone flat add.
4548                    edcstat::bump(&edcstat::I16_DCONLY, 1);
4549                    let plane = if c == 0 {
4550                        &mut self.rec_u
4551                    } else {
4552                        &mut self.rec_v
4553                    };
4554                    reconstruct_4x4_dc_into(
4555                        (dc[c & 1][(by * 2 + bx) & 3] + 32) >> 6,
4556                        &pred8,
4557                        p_off,
4558                        8,
4559                        plane,
4560                        r_off,
4561                        ccw,
4562                    );
4563                } else {
4564                    let plane = if c == 0 {
4565                        &mut self.rec_u
4566                    } else {
4567                        &mut self.rec_v
4568                    };
4569                    reconstruct_4x4_scan_into::<true>(
4570                        &qac[c & 1][(by * 2 + bx) & 3],
4571                        &dqc[c & 1],
4572                        dc[c & 1][(by * 2 + bx) & 3],
4573                        &pred8,
4574                        p_off,
4575                        8,
4576                        plane,
4577                        r_off,
4578                        ccw,
4579                    );
4580                }
4581            }
4582        }
4583    }
4584
4585    fn recon_chroma_cabac(
4586        &mut self,
4587        mb_x: usize,
4588        mb_y: usize,
4589        chroma_mode: u8,
4590        cdc: &[[i32; 4]; 2],
4591        cac: Option<&[[[i32; 16]; 4]; 2]>,
4592        cnnz: &[u8; 8], // parse-side AC counts, c * 4 + by * 2 + bx (routing round: was a 128-word rescan)
4593        cbp_chroma: u32,
4594        avail_top: bool,
4595        avail_left: bool,
4596    ) {
4597        // `None` = no chroma AC was parsed; every read below is already gated
4598        // on cbp_chroma == 2, so the shared zero plane is read-equivalent.
4599        let cac = cac.unwrap_or(&ZERO_CAC);
4600        let qpc = self.chroma_qp_for(self.cur_qp);
4601        let mut c_dc = [[0i32; 4]; 2];
4602        if cbp_chroma != 0 {
4603            for c in 0..2 {
4604                c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 1 + c);
4605            }
4606        }
4607        // ENTROPY-SIDE half: un-scan the AC into raster + commit nnz_c; the
4608        // pixel half is the SHARED recon_chroma_blocks.
4609        let w2 = self.mb_w * 2;
4610        let mut ccoded = [0u8; 2];
4611        if cbp_chroma == 2 {
4612            for c in 0..2 {
4613                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4614                    let cnt = cnnz[(c * 4 + by * 2 + bx) & 7];
4615                    ccoded[c & 1] |= ((cnt != 0) as u8) << ((by * 2 + bx) & 3);
4616                    if let Some(p) =
4617                        self.nnz_c[c & 1].get_mut((mb_y * 2 + by) * w2 + (mb_x * 2 + bx))
4618                    {
4619                        *p = cnt;
4620                    }
4621                }
4622            }
4623        }
4624        self.recon_chroma_blocks(
4625            mb_x,
4626            mb_y,
4627            chroma_mode,
4628            avail_top,
4629            avail_left,
4630            cac,
4631            ccoded,
4632            &c_dc,
4633            qpc,
4634        );
4635    }
4636
4637    /// D14 — the CAVLC E-seam (P3 item 5). Mirrors `decode_slice_data_cabac`:
4638    /// overlap parse (this thread) with pixel reconstruction (a scoped worker
4639    /// owning the planes). Now possible because the CAVLC inter recon was
4640    /// converged onto `add_inter_residual`, so both entropy coders emit the SAME
4641    /// `PInterJob` and share one worker recon.
4642    pub fn decode_slice_data(
4643        &mut self,
4644        r: &mut BitReader,
4645        is_p: bool,
4646        first_mb: usize,
4647    ) -> Result<usize, MbError> {
4648        // Same cross-slice guard as the CABAC loop head.
4649        self.span_flush();
4650        let eligible = edc_on() && rowdb_on() && (is_p || self.is_b);
4651        let threaded = eligible && edc_spawn_worker(self.mb_w, self.mb_h, self.bits_per_mb, false);
4652        edcstat::bump(&edcstat::DISPATCH_ON, threaded as u64);
4653        edcstat::bump(&edcstat::DISPATCH_SEEN, eligible as u64);
4654        if !threaded {
4655            return self.decode_slice_cavlc_inner(r, is_p, first_mb);
4656        }
4657        #[cfg(feature = "std")]
4658        {
4659            let ctx = self.edc_take_ctx();
4660            let (tx, rx) = crate::sync::mpsc::sync_channel::<EdcMsg>(edc_bound());
4661            let (ctx_tx, ctx_rx) = crate::sync::mpsc::channel::<PixelCtx>();
4662            let (back_tx, back_rx) = crate::sync::mpsc::channel::<PixelCtx>();
4663            let (res, ctx, panicked) = std::thread::scope(|sc| {
4664                let h = sc.spawn(move || edc_worker(ctx, rx, ctx_tx, back_rx));
4665                self.edc_tx = Some(tx);
4666                self.edc_ctx_rx = Some(ctx_rx);
4667                self.edc_back_tx = Some(back_tx);
4668                // UNWIND SAFETY (same trap the CABAC wrapper documents): the sender
4669                // lives in `self`, which outlives an unwind, so a panic in the parse
4670                // loop would leave the channel open, the worker alive and the scope
4671                // join blocking forever — turning a diagnosable panic into a silent
4672                // deadlock. Catch, clean up, join, restore, THEN resume.
4673                let r2 = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4674                    self.decode_slice_cavlc_inner(r, is_p, first_mb)
4675                }));
4676                self.edc_flush_batch();
4677                self.edc_giveback();
4678                self.edc_tx = None;
4679                self.edc_ctx_rx = None;
4680                self.edc_back_tx = None;
4681                match (r2, h.join()) {
4682                    (Ok(res), Ok(ctx)) => (res, Some(ctx), None),
4683                    (Err(pn), Ok(ctx)) => (Err(MbError::Truncated), Some(ctx), Some(pn)),
4684                    (Ok(_), Err(pn)) | (Err(_), Err(pn)) => {
4685                        (Err(MbError::Truncated), None, Some(pn))
4686                    }
4687                }
4688            });
4689            if let Some(ctx) = ctx {
4690                self.edc_restore_ctx(ctx);
4691            }
4692            if let Some(pn) = panicked {
4693                std::panic::resume_unwind(pn);
4694            }
4695            return res;
4696        }
4697        #[cfg(not(feature = "std"))]
4698        {
4699            unreachable!("the EDC worker needs std; edc_spawn_worker is false without it")
4700        }
4701    }
4702
4703    fn decode_slice_cavlc_inner(
4704        &mut self,
4705        r: &mut BitReader,
4706        is_p: bool,
4707        first_mb: usize,
4708    ) -> Result<usize, MbError> {
4709        let total = self.mb_w * self.mb_h;
4710        self.slice_first_mb = first_mb;
4711        self.slice_bounds.push((first_mb, self.cur_idc2));
4712        self.any_idc2 |= self.cur_idc2;
4713        self.edc_active = edc_on();
4714        let mut addr = first_mb;
4715        // Same malformed-header divide-by-zero as the sibling slice loop above.
4716        let mbw_nz = self.mb_w.max(1);
4717        let (mut mbx, mut mby) = (addr % mbw_nz, addr / mbw_nz);
4718        let mbw_c = self.mb_w;
4719        while addr < total {
4720            if mbx == mbw_c {
4721                mbx = 0;
4722                mby += 1;
4723            }
4724            self.row_hook_at(addr, mby);
4725            if is_p || self.is_b {
4726                let skip_run = {
4727                    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
4728                    r.read_ue()?
4729                } as usize;
4730                // A run past the picture end is a corrupt stream (ffmpeg errors
4731                // here too); a run TO the end is legal. Silently clamping used
4732                // to fill the remainder with skip MBs.
4733                if skip_run > total - addr {
4734                    return Err(MbError::Truncated);
4735                }
4736                // The run length is KNOWN here (CAVLC codes it as one syntax
4737                // element). P runs: after the first skip commits (0,0), every
4738                // later run MB is FORCED (0,0) by the zero-MV rule — process
4739                // the remainder SEGMENT-WISE: one span extension + one mb_qp
4740                // fill per row segment instead of a per-MB call + span match +
4741                // store. Non-(0,0) runs and B runs keep the per-MB loop. The
4742                // per-MB `addr >= total` check is gone: the run was validated
4743                // against `total - addr` above.
4744                let mut remaining = skip_run;
4745                while remaining > 0 {
4746                    debug_assert!(addr < total);
4747                    if mbx == mbw_c {
4748                        mbx = 0;
4749                        mby += 1;
4750                    }
4751                    if self.is_b {
4752                        if !self.b_skip_hot(mbx, mby) {
4753                            self.decode_b_skip(mbx, mby)?;
4754                        }
4755                        if let Some(p) = self.mb_qp.get_mut(addr) {
4756                            *p = self.cur_qp;
4757                        } // skip inherits QPy
4758                        addr += 1;
4759                        mbx += 1;
4760                        remaining -= 1;
4761                        continue;
4762                    }
4763                    self.decode_p_skip(mbx, mby)?;
4764                    if let Some(p) = self.mb_qp.get_mut(addr) {
4765                        *p = self.cur_qp;
4766                    }
4767                    addr += 1;
4768                    mbx += 1;
4769                    remaining -= 1;
4770                    // Segment-wise remainder: forced (0,0) continuation holds
4771                    // exactly while skip_zero_next tracks addr (decode_p_skip
4772                    // set it iff this skip committed (0,0)).
4773                    // Worker mode (edc_tx) must route per-MB jobs through the
4774                    // channel — the parse thread no longer owns the planes.
4775                    while remaining > 0
4776                        && self.skip_zero_next == addr
4777                        && !no_runmv()
4778                        && self.edc_tx.is_none()
4779                    {
4780                        if mbx == mbw_c {
4781                            mbx = 0;
4782                            mby += 1;
4783                        }
4784                        // Row segment: run to row end or run end.
4785                        let seg = remaining.min(mbw_c - mbx);
4786                        let recon =
4787                            self.edc_tx.is_none() && (self.weights.is_none() || self.weights_id0);
4788                        for k in 0..seg {
4789                            self.pz_push(mbx + k, mby, recon);
4790                        }
4791                        edcstat::bump(&edcstat::SKIPMV_FORCED, seg as u64);
4792                        self.route_skip_mbs += seg as u32;
4793                        self.mb_qp[addr..addr + seg].fill(self.cur_qp);
4794                        if !recon {
4795                            for k in 0..seg {
4796                                self.edc_jobs.push(EdcJob::Skip {
4797                                    mbx: mbx + k,
4798                                    mby,
4799                                    mv: (0, 0),
4800                                });
4801                            }
4802                        }
4803                        addr += seg;
4804                        mbx += seg;
4805                        remaining -= seg;
4806                        self.skip_zero_next = addr;
4807                    }
4808                }
4809                if addr >= total {
4810                    break;
4811                }
4812                // A trailing skip run with no following macroblock ends the slice.
4813                if skip_run > 0 && !r.more_rbsp_data() {
4814                    break;
4815                }
4816            }
4817            // The skip run above may have crossed a row boundary within this
4818            // iteration — wrap before the non-skip MB decodes.
4819            if mbx == mbw_c {
4820                mbx = 0;
4821                mby += 1;
4822            }
4823            if self.is_b {
4824                // ORDER: B reconstructs inline (not seam-ready).
4825                self.edc_intra_sync();
4826                self.decode_b_mb(r, mbx, mby)?;
4827            } else {
4828                // Non-skip CAVLC MB: inter MV prediction + intra gathers
4829                // read the grids — flush deferred spans.
4830                self.span_flush();
4831                self.decode_mb(r, mbx, mby, is_p)?;
4832            }
4833            if let Some(p) = self.mb_qp.get_mut(addr) {
4834                *p = self.cur_qp;
4835            }
4836            addr += 1;
4837            mbx += 1;
4838            // CAVLC slice end: no more data after this macroblock.
4839            if !r.more_rbsp_data() {
4840                break;
4841            }
4842        }
4843        self.edc_flush(); // slice end: no job crosses a slice boundary
4844        Ok(addr)
4845    }
4846
4847    fn decode_mb(
4848        &mut self,
4849        r: &mut BitReader,
4850        mb_x: usize,
4851        mb_y: usize,
4852        is_p: bool,
4853    ) -> Result<(), MbError> {
4854        self.wait_refs_for_mb(mb_y);
4855        let mut mb_type = {
4856            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
4857            r.read_ue()?
4858        };
4859        if is_p {
4860            // In P-slices, mb_type 0/1/2 are inter (16×16, 16×8, 8×16),
4861            // 3 = P_8x8, 4 = P_8x8ref0 (ref_idx forced 0), 5+ intra.
4862            if mb_type <= 2 {
4863                return self.decode_inter(r, mb_x, mb_y, mb_type as u8);
4864            }
4865            if mb_type == 3 || mb_type == 4 {
4866                return self.decode_p8x8(r, mb_x, mb_y, mb_type == 4);
4867            }
4868            mb_type -= 5;
4869        }
4870        // ORDER: intra reconstruction reads neighbour PIXELS, so the worker
4871        // must have applied every deferred job before this point.
4872        self.edc_intra_sync();
4873        self.decode_intra_mb(r, mb_x, mb_y, mb_type)
4874    }
4875
4876    /// Decodes an intra macroblock given its intra `mb_type` (0 = I_4x4,
4877    /// 1..=24 = I_16x16, 25 = I_PCM) — shared by I-, P- and B-slice paths.
4878    fn decode_intra_mb(
4879        &mut self,
4880        r: &mut BitReader,
4881        mb_x: usize,
4882        mb_y: usize,
4883        mb_type: u32,
4884    ) -> Result<(), MbError> {
4885        // H-48: this scope was DECLARED and never wired, which is precisely why the
4886        // stage table left 19.8% unaccounted — 66,120 of 475,200 macroblocks on the
4887        // reference stream are I-type and had no scope at all.
4888        let _gi = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbI);
4889        if let Some(p) = self.mb_kind.get_mut(mb_y * self.mb_w + mb_x) {
4890            *p = rusty_h264_common::deblock::MB_KIND_INTRA;
4891        }
4892        if mb_type == 0 {
4893            // I_NxN: transform_size_8x8_flag (when enabled) selects I_8x8 vs I_4x4.
4894            if self.transform_8x8_mode && r.read_bit()? {
4895                self.decode_i8x8(r, mb_x, mb_y)?;
4896            } else {
4897                self.decode_i4x4(r, mb_x, mb_y)?;
4898            }
4899        } else if (1..=24).contains(&mb_type) {
4900            self.decode_i16(r, mb_x, mb_y, mb_type - 1)?;
4901        } else if mb_type == 25 {
4902            // ORDER: I_PCM writes pixels directly.
4903            self.edc_intra_sync();
4904            self.decode_ipcm(r, mb_x, mb_y)?;
4905        } else {
4906            return Err(MbError::Unsupported(
4907                "only I_4x4 / I_16x16 / I_PCM macroblocks",
4908            ));
4909        }
4910        // Mark all luma blocks coded for the next macroblock's top-right.
4911        // Four contiguous cells per macroblock row - `fill`, not sixteen
4912        // separately bounds-checked indexed stores.
4913        let w4 = self.mb_w * 4;
4914        for lby in 0..4usize {
4915            let a = (mb_y * 4 + lby) * w4 + mb_x * 4;
4916            self.coded_y[a..a + 4].fill(true);
4917        }
4918        Ok(())
4919    }
4920
4921    /// Reconstructs an inter macroblock (`mode` 0 = P_L0_16x16, 1 = P_16x8,
4922    /// 2 = P_8x16): parse the per-partition motion vectors and residual,
4923    /// motion-compensate each partition, and add the residual.
4924    fn decode_inter(
4925        &mut self,
4926        r: &mut BitReader,
4927        mb_x: usize,
4928        mb_y: usize,
4929        mode: u8,
4930    ) -> Result<(), MbError> {
4931        if self.refs.is_empty() {
4932            return Err(MbError::Unsupported("inter without reference"));
4933        }
4934        // DEBLOCK CLASS: mode 0 is P_L0_16x16 — ONE partition, so all 16 blocks
4935        // share a reference and motion vector and no internal edge can reach
4936        // strength 1. Internal strengths then follow from coefficients alone, i.e.
4937        // 16 nnz bytes instead of a 24-block gather across 5-7 grids. Modes 1/2
4938        // (P_16x8 / P_8x16) have two partitions with independent motion and stay
4939        // UNSET (blind path).
4940        if mode == 0 {
4941            if let Some(k) = self.mb_kind.get_mut(mb_y * self.mb_w + mb_x) {
4942                *k = rusty_h264_common::deblock::MB_KIND_INTER_UNIFORM;
4943            }
4944        }
4945        // QP (qp/qpc) is bound after mb_qp_delta is read below.
4946        let w4 = self.mb_w * 4;
4947        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
4948        let num_refs = self.refs.len();
4949        let layout = inter_partitions(mode);
4950
4951        // mb_pred order (spec 7.3.5.1): all ref_idx_l0 first (only when more than
4952        // one reference is active), then all mvd_l0.
4953        let nparts = layout.len();
4954        let mut ref_idxs = [0i32; 4];
4955        if self.num_ref_active > 1 {
4956            for ri in ref_idxs[..nparts].iter_mut() {
4957                *ri = read_ref_idx(r, self.num_ref_active)?;
4958                if *ri as usize >= num_refs {
4959                    return Err(MbError::Truncated); // references a non-existent picture
4960                }
4961            }
4962        }
4963
4964        // Phase 1: per partition, ref-aware MV prediction + mvd, committing the
4965        // motion grid so a later partition predicts from an earlier one.
4966        let mut part_mv = [(0i32, (0i32, 0i32)); 4];
4967        {
4968            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
4969            for (part, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
4970                let refi = ref_idxs[part];
4971                let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
4972                let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
4973                let pmv = predict_partition_mv(mode, part, a, b, c, refi);
4974                let mvd_x = read_mvd(r)?;
4975                let mvd_y = read_mvd(r)?;
4976                let mv = (pmv.0 + mvd_x, pmv.1 + mvd_y);
4977                part_mv[part] = (refi, mv);
4978                for by in ry / 4..ry / 4 + rh / 4 {
4979                    for bx in rx / 4..rx / 4 + rw / 4 {
4980                        let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
4981                        if let (Some(m), Some(it), Some(rf), Some(cd)) = (
4982                            self.mv_y.get_mut(idx),
4983                            self.inter_y.get_mut(idx),
4984                            self.ref_idx_y.get_mut(idx),
4985                            self.coded_y.get_mut(idx),
4986                        ) {
4987                            (*m, *it, *rf, *cd) = (mv, true, refi, true);
4988                        }
4989                    }
4990                }
4991            }
4992        }
4993
4994        // Phase 2: motion-compensate each partition from its reference.
4995        let mut pred_y = [0u8; 256];
4996        let mut c_pred = [[0u8; 64]; 2];
4997        // D8-CAVLC: the double-stage ablation, extended to the CAVLC loop. The
4998        // CABAC measurement could not run here at all (`edc_active` is false on
4999        // this path, so nothing replays through `edc_flush` and `doubled` read
5000        // 0) — yet CAVLC is exactly the population P3 item 5 targets, and its
5001        // cheaper parse should make the PIXEL share larger. Doubling the whole
5002        // partition loop is idempotent: pass 2 overwrites `pred_y` with fresh MC
5003        // BEFORE `weight_partition` runs, so weighting cannot apply twice.
5004        // This doubles MC only (not the residual add inside `inter_finish`), so
5005        // it is a LOWER BOUND on the CAVLC pixel share.
5006        // D14 (CAVLC E-seam): when the seam is live the WORKER motion-compensates
5007        // from the committed MV grids, so skip MC here rather than computing a
5008        // prediction that would be discarded.
5009        let defer = self.edc_tx.is_some() || self.edc_active;
5010        let mc_passes = if defer {
5011            0
5012        } else if double_recon() {
5013            2
5014        } else {
5015            1
5016        };
5017        for _pass in 0..mc_passes {
5018            if _pass > 0 {
5019                edcstat::bump(&edcstat::DOUBLED, 1);
5020            }
5021            for (part, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
5022                let (refi, mv) = part_mv[part & 3];
5023                let Some(reference) = self.refs.get(refi as usize) else {
5024                    continue;
5025                };
5026                let mut tmp = [0u8; 256];
5027                mc_luma_padded(
5028                    &*reference.luma_guard(reference.ch),
5029                    reference.lstride(),
5030                    crate::LPAD,
5031                    self.cw,
5032                    ch,
5033                    mb_x * 16 + rx,
5034                    mb_y * 16 + ry,
5035                    rw,
5036                    rh,
5037                    mv.0,
5038                    mv.1,
5039                    &mut tmp,
5040                );
5041                {
5042                    let _g =
5043                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
5044                    restride(&mut pred_y, 16, rx, ry, &tmp, rw, rh);
5045                }
5046                let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
5047                for cc in 0..2 {
5048                    let rc = if cc == 0 {
5049                        &*reference.chroma_guard(0, reference.ch)
5050                    } else {
5051                        &*reference.chroma_guard(1, reference.ch)
5052                    };
5053                    let mut tc = [0u8; 64];
5054                    mc_chroma_padded(
5055                        rc,
5056                        reference.cstride(),
5057                        crate::CPAD,
5058                        self.ccw,
5059                        cch,
5060                        mb_x * 8 + crx,
5061                        mb_y * 8 + cry,
5062                        crw,
5063                        crh,
5064                        mv.0,
5065                        mv.1,
5066                        &mut tc,
5067                    );
5068                    {
5069                        let _g =
5070                            rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
5071                        restride(&mut c_pred[cc], 8, crx, cry, &tc, crw, crh);
5072                    }
5073                }
5074                self.weight_partition(&mut pred_y, &mut c_pred, 0, refi as usize, rx, ry, rw, rh);
5075            }
5076        }
5077
5078        // 16×16/16×8/8×16 partitions are all ≥ 8×8, so the 8×8 transform is allowed.
5079        self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, true, defer)
5080    }
5081
5082    /// Shared inter tail: parse `coded_block_pattern` + `mb_qp_delta`, decode the
5083    /// luma/chroma residual, and add it to the already-built motion-compensated
5084    /// prediction. Used by both the 16×16/16×8/8×16 path and `P_8x8`.
5085    fn inter_finish(
5086        &mut self,
5087        r: &mut BitReader,
5088        mb_x: usize,
5089        mb_y: usize,
5090        pred_y: &[u8; 256],
5091        c_pred: &[[u8; 64]; 2],
5092        allow_8x8: bool,
5093        // D14: emit a worker job instead of reconstructing. Only the CAVLC
5094        // 16x16/16x8/8x16 path sets this; B and P_8x8 stay inline.
5095        defer: bool,
5096    ) -> Result<(), MbError> {
5097        let w4 = self.mb_w * 4;
5098        let cbp = {
5099            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
5100            read_cbp_inter(r)?
5101        };
5102        let cbp_luma = cbp & 15;
5103        let cbp_chroma = cbp >> 4;
5104        // transform_size_8x8_flag follows cbp (before mb_qp_delta) when luma has
5105        // coefficients, the 8×8 transform is enabled, and every partition ≥ 8×8.
5106        let t8x8 = cbp_luma > 0 && self.transform_8x8_mode && allow_8x8 && r.read_bit()?;
5107        if t8x8 {
5108            if let Some(f) = self.mb_t8x8.get_mut(mb_y * self.mb_w + mb_x) {
5109                *f = true;
5110            }
5111            self.any_t8 = true;
5112        }
5113        if cbp != 0 {
5114            self.step_qp(r.read_se()?)?;
5115        }
5116        let (qp, _qpc) = (self.cur_qp, self.chroma_qp_for(self.cur_qp));
5117
5118        // ---- luma residual ----
5119        self.nnz_cache_load(mb_x, mb_y);
5120        let mut nnzs = [0u8; 24];
5121        // PARSE STRAIGHT INTO THE DESTINATION. The deferred arm used to parse into
5122        // 1.5 KB of zeroed locals and then copy them into the pooled job (another
5123        // 1.5 KB, +1 KB each for t8x8); the inline arm zeroed the same locals.
5124        // Both now decode into a pre-owned plane set -- the pooled `PInterJob`
5125        // when deferring, the decoder's scratch boxes otherwise -- and zero only
5126        // the blocks the cbp codes. Uncoded slots stay dirty on purpose: the
5127        // consumer (`add_inter_residual` / the worker) skips every block whose
5128        // `nnzs` count is 0 and every chroma AC plane unless cbp_chroma == 2,
5129        // the contract the CABAC P arm has shipped on since round 4.
5130        let nores = cbp == 0 && nores_on();
5131        let mut job = (defer && !nores).then(|| self.take_pinter_job());
5132        let mut s_luma = if job.is_none() {
5133            self.scratch_luma
5134                .take()
5135                .or_else(|| Some(Box::new([[0i32; 16]; 16])))
5136        } else {
5137            None
5138        };
5139        let mut s_luma8 = if job.is_none() {
5140            self.scratch_luma8
5141                .take()
5142                .or_else(|| Some(Box::new([[0i32; 64]; 4])))
5143        } else {
5144            None
5145        };
5146        let mut s_cac = if job.is_none() {
5147            self.scratch_cac
5148                .take()
5149                .or_else(|| Some(Box::new([[[0i32; 16]; 4]; 2])))
5150        } else {
5151            None
5152        };
5153        let (luma_scan, luma8, cac): (
5154            &mut [[i32; 16]; 16],
5155            &mut [[i32; 64]; 4],
5156            &mut [[[i32; 16]; 4]; 2],
5157        ) = match job.as_deref_mut() {
5158            Some(j) => (&mut j.luma_scan, &mut j.luma8, &mut j.cac),
5159            None => (
5160                s_luma.as_deref_mut().expect("scratch"),
5161                s_luma8.as_deref_mut().expect("scratch"),
5162                s_cac.as_deref_mut().expect("scratch"),
5163            ),
5164        };
5165        // BUILD THE MACROBLOCK'S nnz RASTER ON THE STACK, COPY IT ROW-WISE ONCE.
5166        // Both arms below scattered sixteen individually bounds-checked stores
5167        // into the frame grid while the entropy parse ran. Nothing reads
5168        // `nnz_y` for THIS macroblock before the function returns - `nc_pred`
5169        // predicts from the separate `nnz_cache` - so the writes can be
5170        // deferred. The zero arm then costs NOTHING: the raster already holds
5171        // zeros.
5172        let mut nnz_raster = [0u8; 16];
5173        if t8x8 {
5174            for b8 in 0..4 {
5175                let (b8x, b8y) = (b8 % 2, b8 / 2);
5176                let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
5177                let _ = (bx, by);
5178                if cbp_luma & (1 << b8) != 0 {
5179                    // Zero this 8x8 slot only (256 B) -- was a 1 KB `[[0;64];4]` insert + a 256 B copy.
5180                    luma8[b8 & 3] = [0i32; 64];
5181                    for sub in 0..4 {
5182                        let (sx, sy) = (sub % 2, sub / 2);
5183                        let (cx, cy) = (b8x * 2 + sx, b8y * 2 + sy);
5184                        let nc = self.nc_pred(cx, cy);
5185                        let mut blk = [0i32; 16];
5186                        let total = decode_residual_block_into::<16, 16>(r, nc, &mut blk)?;
5187                        self.nnz_cache_set(cx, cy, total);
5188                        nnz_raster[cy * 4 + cx] = total;
5189                        // The PER-SUB-BLOCK count the next macroblock's nC prediction
5190                        // depends on -- summing these into one slot and letting the
5191                        // recon helper broadcast it back is what broke CAVLC 8x8.
5192                        // WIN: b8 < 4 and sub < 4, so the index is 0..=15 and the
5193                        // mask is a no-op that PROVES it -- the same idiom the
5194                        // neighbouring luma8[b8 & 3] / luma_scan[blk & 15] already use.
5195                        // Folds a panic_bounds_check out of inter_finish.
5196                        nnzs[(b8 * 4 + sub) & 15] = total;
5197                        // RAW 8x8 scan: coeff k of sub-block s at 4k + s (spec 7.3.5.3.2);
5198                        // `add_inter_residual` un-scans + dequantises, as for CABAC.
5199                        for k in 0..16 {
5200                            luma8[b8 & 3][(4 * k + sub) & 63] = blk[k];
5201                        }
5202                    }
5203                } else {
5204                    for sub in 0..4 {
5205                        let (sx, sy) = (sub % 2, sub / 2);
5206                        self.nnz_cache_set(b8x * 2 + sx, b8y * 2 + sy, 0);
5207                        // `nnz_raster` is already zero here - no store needed.
5208                    }
5209                }
5210            }
5211        } else {
5212            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5213                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
5214                let total = if cbp_luma & (1 << (blk / 4)) != 0 {
5215                    let nc = self.nc_pred(lbx, lby);
5216                    // Decoded STRAIGHT into the destination plane (RAW scan order, like
5217                    // CABAC); zero only this coded block first (the slot is pooled/dirty).
5218                    luma_scan[blk & 15] = [0i32; 16];
5219                    decode_residual_block_into::<16, 16>(r, nc, &mut luma_scan[blk & 15])?
5220                } else {
5221                    0
5222                };
5223                self.nnz_cache_set(lbx, lby, total);
5224                let _ = (bx, by);
5225                nnz_raster[(lby & 3) * 4 + (lbx & 3)] = total;
5226                // WIN: luma 4x4 index, 0..=15 (luma_scan[blk & 15] above relies on the
5227                // same range); the mask folds the second bounds check.
5228                nnzs[blk & 15] = total;
5229            }
5230        }
5231        // ONE contiguous copy per macroblock row.
5232        for by in 0..4usize {
5233            let a = (mb_y * 4 + by) * w4 + mb_x * 4;
5234            self.nnz_y[a..a + 4].copy_from_slice(&nnz_raster[by * 4..by * 4 + 4]);
5235        }
5236
5237        // ---- chroma residual ----
5238        let mut c_recon_dc = [[0i32; 4]; 2];
5239        if cbp_chroma != 0 {
5240            for slot in c_recon_dc.iter_mut() {
5241                // RAW, straight into the 4-word slot (dequantised in the helper).
5242                decode_residual_block_into::<4, 4>(r, -1, slot)?;
5243            }
5244        }
5245        if cbp_chroma == 2 {
5246            self.chroma_cache_load(mb_x, mb_y);
5247            let w2 = self.mb_w * 2;
5248            let mut cnnz = [[0u8; 4]; 2];
5249            for c in 0..2 {
5250                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
5251                    let nc = self.chroma_nc_pred(c, bx, by);
5252                    let slot = &mut cac[c & 1][(by * 2 + bx) & 3];
5253                    *slot = [0i32; 16];
5254                    let total = decode_residual_block_into::<15, 16>(r, nc, slot)?; // RAW scan order
5255                    self.chroma_nnz_cache_set(c, bx, by, total);
5256                    // WIN: by < 2 and bx < 2, so 0..=3 -- the mask proves it against
5257                    // the [[u8; 4]; 2], exactly as the cac[c & 1][(by * 2 + bx) & 3]
5258                    // slot two lines above already does. Folds the last
5259                    // panic_bounds_check out of inter_finish.
5260                    cnnz[c & 1][(by * 2 + bx) & 3] = total;
5261                    // Masks are the proof: max index 16 + 4 + 2 + 1 = 23 < 24 (was a `.min(23)`).
5262                    nnzs[16 + (c & 1) * 4 + (by & 1) * 2 + (bx & 1)] = total;
5263                }
5264                for by in 0..2usize {
5265                    let a = (mb_y * 2 + by) * w2 + mb_x * 2;
5266                    self.nnz_c[c][a..a + 2].copy_from_slice(&cnnz[c][by * 2..by * 2 + 2]);
5267                }
5268            }
5269        }
5270
5271        // ---- reconstruction ----
5272        //
5273        // D13: this used to be a 109-line hand-rolled copy of the residual add.
5274        // It now calls the SAME `add_inter_residual` the CABAC path uses, which
5275        // is what makes the CAVLC E-seam possible at all: the two paths had
5276        // different residual representations (CAVLC pre-applied un_scan and
5277        // inv_quant at PARSE time; CABAC carries raw scan-order coefficients and
5278        // dequantises inside the helper), so no job could be shared. Carrying the
5279        // raw forms — which CAVLC already had in hand — converges them, deletes
5280        // a duplicate implementation, and lets a deferred job reuse the existing
5281        // worker recon instead of needing a second copy that could drift.
5282        if defer {
5283            // The residual representation now matches CABAC exactly (the
5284            // convergence commit), so the SAME `PInterJob` and the SAME worker
5285            // `recon_p_inter` serve both entropy coders — no second recon
5286            // implementation exists that could drift.
5287            let (mut gmv, mut gref) = ([(0i32, 0i32); 16], [0u8; 16]);
5288            let w4r = self.mb_w * 4;
5289            // ROW SLICES over BOTH grids. This was the single densest panic site
5290            // left in the decoder crate: sixteen iterations reading two PARALLEL
5291            // frame grids at the same index, and `mv_y`'s bound proved nothing
5292            // about `ref_idx_y`, so all thirty-two loads carried their own check.
5293            // Each macroblock row is four contiguous cells in both.
5294            for by in 0..4usize {
5295                let base = (mb_y * 4 + by) * w4r + mb_x * 4;
5296                let mvr = &self.mv_y[base..][..4];
5297                let rfr = &self.ref_idx_y[base..][..4];
5298                gmv[by * 4..][..4].copy_from_slice(mvr);
5299                for bx in 0..4usize {
5300                    gref[by * 4 + bx] = rfr[bx].clamp(0, 15) as u8;
5301                }
5302            }
5303            // D9 applies here too: `cbp == 0` means all 2,592 coefficient bytes
5304            // of the 2,784-byte job are ZERO, so ship the 176-byte motion-only
5305            // form. Discovered on the CABAC path; it transfers for free because
5306            // the CAVLC arm now emits the SAME job type.
5307            let ej = if nores {
5308                edcstat::bump(&edcstat::J_NORES_SENT, 1);
5309                let b = self.take_nores_job(PInterNoResJob {
5310                    mbx: mb_x,
5311                    mby: mb_y,
5312                    t8: t8x8,
5313                    gmv,
5314                    gref,
5315                });
5316                EdcJob::InterNoRes(b)
5317            } else {
5318                // Pooled box (see `take_pinter_job`); the CAVLC arm still parses into
5319                // locals, so this is a copy, not an allocation.
5320                let mut job = job.take().expect("pooled job taken above");
5321                job.mbx = mb_x;
5322                job.mby = mb_y;
5323                job.qp = qp;
5324                job.cbp_chroma = cbp_chroma;
5325                job.t8 = t8x8;
5326                job.gmv = gmv;
5327                job.gref = gref;
5328                // luma_scan / luma8 / cac were parsed in place.
5329                job.cdc = c_recon_dc;
5330                job.nnzs = nnzs;
5331                EdcJob::Inter(job)
5332            };
5333            if self.edc_tx.is_some() {
5334                self.edc_giveback();
5335                self.edc_send_job(ej);
5336            } else {
5337                self.edc_jobs.push(ej);
5338            }
5339        } else {
5340            self.add_inter_residual(
5341                mb_x,
5342                mb_y,
5343                pred_y,
5344                c_pred,
5345                Some(&*luma_scan),
5346                t8x8.then_some(&*luma8),
5347                &c_recon_dc,
5348                Some(&*cac),
5349                cbp_chroma,
5350                &nnzs,
5351            );
5352            // Give the scratch planes back (taken only on this arm).
5353            self.scratch_luma = s_luma;
5354            self.scratch_luma8 = s_luma8;
5355            self.scratch_cac = s_cac;
5356        }
5357
5358        // MV grid + coded flags were set per partition; mark modes as DC.
5359        // Four contiguous cells per row - `fill`, not sixteen indexed stores.
5360        for lby in 0..4usize {
5361            let a = (mb_y * 4 + lby) * w4 + mb_x * 4;
5362            self.modes_y[a..a + 4].fill(2);
5363        }
5364        Ok(())
5365    }
5366
5367    // ---------------------------------------------------------------------
5368    // B-slice macroblock decoding
5369    // ---------------------------------------------------------------------
5370
5371    /// Per-list (`list` 0 or 1) MV-prediction neighbors for the block region at
5372    /// `(pbx, pby)` of width `pwb` blocks — the L0/L1 analogue of
5373    /// `mv_neighbors_block`.
5374    fn mv_neighbors_list(
5375        &self,
5376        pbx: isize,
5377        pby: isize,
5378        pwb: isize,
5379        list: usize,
5380    ) -> [MvNeighbor; 3] {
5381        self.mv_neighbors_block_grid(pbx, pby, pwb, list)
5382    }
5383
5384    /// Spatial-direct A/B/C at the MB origin — same for every 8×8 in the MB
5385    /// (spec §8.4.1.2.2). B_8x8 callers hoist this once; 16×16 skip/direct walk
5386    /// once inside `decode_b_direct`.
5387    #[inline]
5388    fn b_direct_nbrs(&self, mb_x: usize, mb_y: usize) -> ([MvNeighbor; 3], [MvNeighbor; 3]) {
5389        self.mv_neighbors_both((mb_x * 4) as isize, (mb_y * 4) as isize, 4)
5390    }
5391
5392    /// FUSED dual-list gather at arbitrary partition geometry: A/B/C positions
5393    /// and their availability (bounds + coded + slice) are list-independent —
5394    /// compute once, load both lists' grids from the same resolved index.
5395    fn mv_neighbors_both(
5396        &self,
5397        pbx: isize,
5398        pby: isize,
5399        pwb: isize,
5400    ) -> ([MvNeighbor; 3], [MvNeighbor; 3]) {
5401        let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
5402        let get2 = |bx: isize, by: isize| -> (MvNeighbor, MvNeighbor) {
5403            if bx < 0
5404                || by < 0
5405                || bx >= w4
5406                || by >= h4
5407                || !self
5408                    .coded_y
5409                    .get((by * w4 + bx) as usize)
5410                    .copied()
5411                    .unwrap_or(false)
5412                || !self.nbr_in_slice(bx as usize / 4, by as usize / 4)
5413            {
5414                (MvNeighbor::NONE, MvNeighbor::NONE)
5415            } else {
5416                // FOUR PARALLEL GRIDS at one index. The `coded_y` guard above
5417                // bounds none of them — they are separate Vecs — so each carried
5418                // its own panic path. `MvNeighbor::NONE` is the established
5419                // "no such neighbour" value, so the fallible form degrades into
5420                // the branch directly above it.
5421                let idx = (by * w4 + bx) as usize;
5422                match (
5423                    self.mv_y.get(idx),
5424                    self.ref_idx_y.get(idx),
5425                    self.mv1.get(idx),
5426                    self.ref_idx1.get(idx),
5427                ) {
5428                    (Some(&m0), Some(&r0), Some(&m1), Some(&r1)) => (
5429                        MvNeighbor {
5430                            available: true,
5431                            mv: m0,
5432                            ref_idx: r0,
5433                        },
5434                        MvNeighbor {
5435                            available: true,
5436                            mv: m1,
5437                            ref_idx: r1,
5438                        },
5439                    ),
5440                    _ => (MvNeighbor::NONE, MvNeighbor::NONE),
5441                }
5442            }
5443        };
5444        let (a0, a1) = get2(pbx - 1, pby);
5445        let (b0, b1) = get2(pbx, pby - 1);
5446        let (mut c0, mut c1) = get2(pbx + pwb, pby - 1);
5447        if !c0.available {
5448            // The C fallback is position-driven (topright unavailable ⇒
5449            // topleft), so both lists fall back together — same decision the
5450            // two per-list gathers made independently.
5451            let t = get2(pbx - 1, pby - 1);
5452            c0 = t.0;
5453            c1 = t.1;
5454        }
5455        ([a0, b0, c0], [a1, b1, c1])
5456    }
5457
5458    fn mv_neighbors_block_grid(
5459        &self,
5460        pbx: isize,
5461        pby: isize,
5462        pwb: isize,
5463        list: usize,
5464    ) -> [MvNeighbor; 3] {
5465        let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
5466        let (mvg, refg) = if list == 0 {
5467            (&self.mv_y, &self.ref_idx_y)
5468        } else {
5469            (&self.mv1, &self.ref_idx1)
5470        };
5471        let get = |bx: isize, by: isize| -> MvNeighbor {
5472            if bx < 0
5473                || by < 0
5474                || bx >= w4
5475                || by >= h4
5476                || !self
5477                    .coded_y
5478                    .get((by * w4 + bx) as usize)
5479                    .copied()
5480                    .unwrap_or(false)
5481                || !self.nbr_in_slice(bx as usize / 4, by as usize / 4)
5482            {
5483                MvNeighbor::NONE
5484            } else {
5485                let idx = (by * w4 + bx) as usize;
5486                match (mvg.get(idx), refg.get(idx)) {
5487                    (Some(&m), Some(&r)) => MvNeighbor {
5488                        available: true,
5489                        mv: m,
5490                        ref_idx: r,
5491                    },
5492                    _ => MvNeighbor::NONE,
5493                }
5494            }
5495        };
5496        let a = get(pbx - 1, pby);
5497        let b = get(pbx, pby - 1);
5498        let mut c = get(pbx + pwb, pby - 1);
5499        if !c.available {
5500            c = get(pbx - 1, pby - 1);
5501        }
5502        [a, b, c]
5503    }
5504
5505    /// `colZeroFlag` for the 4×4 block at absolute block coords `(bx, by)`: true
5506    /// when `RefPicList1[0]` is a short-term picture whose co-located block uses
5507    /// reference 0 with a near-zero motion vector (spec §8.4.1.2.2).
5508    /// Co-located 4x4 block coords for the current block's `(bx4, by4)` within the
5509    /// macroblock, per spec 8.4.1.2.1. Under `direct_8x8_inference_flag` every 4x4
5510    /// in an 8x8 takes that 8x8's OUTER CORNER (`luma4x4BlkIdx = 5 * mbPartIdx`,
5511    /// i.e. (0,0) (3,0) (0,3) (3,3)); otherwise motion is genuinely per-4x4.
5512    ///
5513    /// 8.4.1.2.1 is SHARED by both direct modes, so spatial and temporal must map
5514    /// identically. They did not: temporal mapped the corner and spatial read the
5515    /// block's own coords, which is invisible while every 4x4 in the co-located 8x8
5516    /// carries the same motion -- true of every stream until sub-8x8 P partitions
5517    /// (x264 `--partitions p4x4`) make them differ. Hence one function.
5518    #[inline]
5519    fn col_block(&self, bx4: usize, by4: usize) -> (usize, usize) {
5520        if self.direct_8x8_inference {
5521            ((bx4 / 2) * 3, (by4 / 2) * 3)
5522        } else {
5523            (bx4, by4)
5524        }
5525    }
5526
5527    fn col_zero(&self, bx: usize, by: usize) -> bool {
5528        // Fast path (1T frozen colocated, the default): the invariant checks
5529        // were hoisted to set_b_context; two grid loads + the threshold test.
5530        if self.col_ok {
5531            let Some(col) = self.refs1.first() else {
5532                return false;
5533            };
5534            let idx = by * self.col_w4 + bx;
5535            // `.get` on EVERY parallel array, not a length test on one of them.
5536            // The `idx >= ref_idx.len()` guard proved nothing about `mv`,
5537            // `ref_idx1` or `mv1` — separate Vecs with independent lengths — so
5538            // each of those reads carried its own panic path. They are built
5539            // together and always agree; the fallible form states that instead of
5540            // asserting it, and keeps the "no panic on malformed input" contract.
5541            let (cref, cmv) = match col.ref_idx.get(idx) {
5542                Some(&r0) if r0 >= 0 => match col.mv.get(idx) {
5543                    Some(&m) => (r0, m),
5544                    None => return false,
5545                },
5546                Some(_) => match (col.ref_idx1.get(idx), col.mv1.get(idx)) {
5547                    (Some(&r1), Some(&m)) if r1 >= 0 => (r1, m),
5548                    _ => return false,
5549                },
5550                None => return false,
5551            };
5552            return cref == 0 && cmv.0.abs() <= 1 && cmv.1.abs() <= 1;
5553        }
5554        let Some(col) = self.refs1.first() else {
5555            return false;
5556        };
5557        if let Some(live) = col.live.as_ref() {
5558            col.wait_motion_ready();
5559            let meta = live.meta.read().unwrap();
5560            if meta.long_term || meta.w4 == 0 {
5561                return false;
5562            }
5563            let idx = by * meta.w4 + bx;
5564            // Same `.get`-per-array shape as the frozen-colocated path above.
5565            let (cref, cmv) = match meta.ref_idx.get(idx) {
5566                Some(&r0) if r0 >= 0 => match meta.mv.get(idx) {
5567                    Some(&m) => (r0, m),
5568                    None => return false,
5569                },
5570                Some(_) => match (meta.ref_idx1.get(idx), meta.mv1.get(idx)) {
5571                    (Some(&r1), Some(&m)) if r1 >= 0 => (r1, m),
5572                    _ => return false,
5573                },
5574                None => return false,
5575            };
5576            return cref == 0 && cmv.0.abs() <= 1 && cmv.1.abs() <= 1;
5577        }
5578        if col.long_term || col.w4 == 0 {
5579            return false;
5580        }
5581        let idx = by * col.w4 + bx;
5582        if idx >= col.ref_idx.len() {
5583            return false;
5584        }
5585        let (cref, cmv) = match col.ref_idx.get(idx) {
5586            Some(&r0) if r0 >= 0 => match col.mv.get(idx) {
5587                Some(&m) => (r0, m),
5588                None => return false,
5589            },
5590            Some(_) => match (col.ref_idx1.get(idx), col.mv1.get(idx)) {
5591                (Some(&r1), Some(&m)) if r1 >= 0 => (r1, m),
5592                _ => return false,
5593            },
5594            None => return false,
5595        };
5596        cref == 0 && cmv.0.abs() <= 1 && cmv.1.abs() <= 1
5597    }
5598
5599    /// Implicit bi-prediction weights `(w0, w1)` from POC distances (spec
5600    /// §8.4.2.3.2), or `None` for the plain average (idc≠2, uni-pred, or the
5601    /// equidistant / out-of-range fall-back to 32:32 which equals the average).
5602    fn implicit_weights(&self, refi0: i32, refi1: i32) -> Option<(i32, i32)> {
5603        if self.weighted_bipred_idc != 2 || refi0 < 0 || refi1 < 0 {
5604            return None;
5605        }
5606        let (Some(r0), Some(r1)) = (
5607            self.refs.get(refi0 as usize),
5608            self.refs1.get(refi1 as usize),
5609        ) else {
5610            return None;
5611        };
5612        let td = (r1.pic_poc() - r0.pic_poc()).clamp(-128, 127);
5613        let tb = (self.cur_poc - r0.pic_poc()).clamp(-128, 127);
5614        if td == 0 || r0.long_term || r1.long_term {
5615            return None; // 32:32 → identical to the average
5616        }
5617        let tx = tx_for_td(td);
5618        let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
5619        let w1 = dsf >> 2;
5620        if !(-64..=128).contains(&w1) {
5621            return None; // out of range → 32:32 average
5622        }
5623        Some((64 - w1, w1))
5624    }
5625
5626    /// Motion-compensates a region with the given per-list refs/MVs. Bi-prediction
5627    /// is the simple `(a+b+1)>>1` average, or POC-weighted when implicit weighting
5628    /// (idc 2) is active. Writes into `pred_y`/`c_pred`.
5629    #[allow(clippy::too_many_arguments)]
5630    fn b_mc(
5631        &self,
5632        mb_x: usize,
5633        mb_y: usize,
5634        px: usize,
5635        py: usize,
5636        rw: usize,
5637        rh: usize,
5638        refi0: i32,
5639        mv0: (i32, i32),
5640        refi1: i32,
5641        mv1: (i32, i32),
5642        pred_y: &mut [u8; 256],
5643        c_pred: &mut [[u8; 64]; 2],
5644    ) {
5645        let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBMc);
5646        // Malformed-stream armor, mirroring the P path: now that B slices actually
5647        // PARSE ref_idx (they used to be hardcoded to 0), a mutated stream can hand
5648        // us an index past the end of either list. Clamp rather than panic — the
5649        // crate is `forbid(unsafe_code)` and fuzz-gated to never panic, and a
5650        // wrong picture on garbage input carries no conformance duty.
5651        let refi0 = if refi0 >= 0 {
5652            (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32
5653        } else {
5654            -1
5655        };
5656        let refi1 = if refi1 >= 0 {
5657            (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32
5658        } else {
5659            -1
5660        };
5661        if (refi0 >= 0 && self.refs.is_empty()) || (refi1 >= 0 && self.refs1.is_empty()) {
5662            return;
5663        }
5664        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
5665        let weights = {
5666            let _gw = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBWeights);
5667            self.implicit_weights(refi0, refi1)
5668        };
5669        // Bi-prediction blend: the weights decision is LOOP-INVARIANT, so every
5670        // blend site below matches on `weights` ONCE and runs a branch-free
5671        // pixel loop — the unweighted `(p+q+1)>>1` average then autovectorizes
5672        // (the per-pixel closure this replaces hid the invariant behind a
5673        // capture, and its chroma form was a &dyn call PER PIXEL).
5674        // FULL-WIDTH regions (px == 0, rw == 16 — every 16×16/16×8 partition and
5675        // most direct regions) occupy contiguous rows of `pred_y`, so MC writes
5676        // the destination DIRECTLY: uni-pred needs no staging at all, bi-pred
5677        // stages only the second list and blends in place. The staging arrays
5678        // (512 B zeroed per call before this) now exist only on the branches
5679        // that read them. Same fusion as the P path's mc_rect (WHYS Part 8).
5680        let full = px == 0 && rw == 16;
5681        if refi0 >= 0
5682            && refi1 >= 0
5683            && mv0.0 % 4 == 0
5684            && mv0.1 % 4 == 0
5685            && mv1.0 % 4 == 0
5686            && mv1.1 % 4 == 0
5687        {
5688            edcstat::bump(&edcstat::BMC_BI_FP, 1);
5689        }
5690        let _gl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBLuma);
5691        // One scratch borrow for the whole region — both bi-pred passes included.
5692        // The closure yields whether the arm already ran the chroma half (the
5693        // bi-pred full-width arm does, to keep its staging alive) — a plain
5694        // `return` inside would exit the CLOSURE only and chroma would run twice.
5695        let chroma_done =
5696            rusty_h264_common::inter::with_mc_scratch(|scr| match (refi0 >= 0, refi1 >= 0, full) {
5697                (true, false, true) => {
5698                    let Some(rf) = self.refs.get(refi0 as usize) else {
5699                        return false;
5700                    };
5701                    rusty_h264_common::inter::mc_luma_padded_pre(
5702                        scr,
5703                        &*rf.luma_guard(rf.ch),
5704                        rf.lstride(),
5705                        crate::LPAD,
5706                        self.cw,
5707                        ch,
5708                        mb_x * 16,
5709                        mb_y * 16 + py,
5710                        rw,
5711                        rh,
5712                        mv0.0,
5713                        mv0.1,
5714                        &mut pred_y[py * 16..py * 16 + rw * rh],
5715                    );
5716                    false
5717                }
5718                (false, true, true) => {
5719                    let Some(rf) = self.refs1.get(refi1 as usize) else {
5720                        return false;
5721                    };
5722                    rusty_h264_common::inter::mc_luma_padded_pre(
5723                        scr,
5724                        &*rf.luma_guard(rf.ch),
5725                        rf.lstride(),
5726                        crate::LPAD,
5727                        self.cw,
5728                        ch,
5729                        mb_x * 16,
5730                        mb_y * 16 + py,
5731                        rw,
5732                        rh,
5733                        mv1.0,
5734                        mv1.1,
5735                        &mut pred_y[py * 16..py * 16 + rw * rh],
5736                    );
5737                    false
5738                }
5739                (true, true, true) => {
5740                    let Some(rf) = self.refs.get(refi0 as usize) else {
5741                        return false;
5742                    };
5743                    rusty_h264_common::inter::mc_luma_padded_pre(
5744                        scr,
5745                        &*rf.luma_guard(rf.ch),
5746                        rf.lstride(),
5747                        crate::LPAD,
5748                        self.cw,
5749                        ch,
5750                        mb_x * 16,
5751                        mb_y * 16 + py,
5752                        rw,
5753                        rh,
5754                        mv0.0,
5755                        mv0.1,
5756                        &mut pred_y[py * 16..py * 16 + rw * rh],
5757                    );
5758                    let mut b = [0u8; 256];
5759                    let Some(rf) = self.refs1.get(refi1 as usize) else {
5760                        return false;
5761                    };
5762                    rusty_h264_common::inter::mc_luma_padded_pre(
5763                        scr,
5764                        &*rf.luma_guard(rf.ch),
5765                        rf.lstride(),
5766                        crate::LPAD,
5767                        self.cw,
5768                        ch,
5769                        mb_x * 16,
5770                        mb_y * 16 + py,
5771                        rw,
5772                        rh,
5773                        mv1.0,
5774                        mv1.1,
5775                        &mut b[..rw * rh],
5776                    );
5777                    drop(_gl);
5778                    let _gbl =
5779                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
5780                    // SLICE-then-zip: proving the bounds ONCE lets rustc emit the whole
5781                    // 256-byte average as 8 straight-line vpavgb ops (verified in
5782                    // isolation, x86-64-v3); the indexed form kept a per-iteration
5783                    // bounds check and a loop. A hand AVX2 kernel is refuted — the
5784                    // compiler already emits the ideal instruction.
5785                    let dst = &mut pred_y[py * 16..py * 16 + rw * rh];
5786                    match weights {
5787                        None => {
5788                            for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
5789                                *d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
5790                            }
5791                        }
5792                        Some((w0, w1)) => {
5793                            for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
5794                                *d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255)
5795                                    as u8;
5796                            }
5797                        }
5798                    }
5799                    let _gc =
5800                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
5801                    self.b_mc_chroma(
5802                        mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch,
5803                    );
5804                    true
5805                }
5806                _ => {
5807                    // Narrow region — rows are strided in `pred_y`; stage and copy.
5808                    let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
5809                    if refi0 >= 0 {
5810                        let Some(rf) = self.refs.get(refi0 as usize) else {
5811                            return false;
5812                        };
5813                        rusty_h264_common::inter::mc_luma_padded_pre(
5814                            scr,
5815                            &*rf.luma_guard(rf.ch),
5816                            rf.lstride(),
5817                            crate::LPAD,
5818                            self.cw,
5819                            ch,
5820                            mb_x * 16 + px,
5821                            mb_y * 16 + py,
5822                            rw,
5823                            rh,
5824                            mv0.0,
5825                            mv0.1,
5826                            &mut a[..rw * rh],
5827                        );
5828                    }
5829                    if refi1 >= 0 {
5830                        let Some(rf) = self.refs1.get(refi1 as usize) else {
5831                            return false;
5832                        };
5833                        rusty_h264_common::inter::mc_luma_padded_pre(
5834                            scr,
5835                            &*rf.luma_guard(rf.ch),
5836                            rf.lstride(),
5837                            crate::LPAD,
5838                            self.cw,
5839                            ch,
5840                            mb_x * 16 + px,
5841                            mb_y * 16 + py,
5842                            rw,
5843                            rh,
5844                            mv1.0,
5845                            mv1.1,
5846                            &mut b[..rw * rh],
5847                        );
5848                    }
5849                    drop(_gl);
5850                    let _gbl =
5851                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
5852                    match (refi0 >= 0, refi1 >= 0) {
5853                        (true, true) => {
5854                            for dy in 0..rh {
5855                                let (ar, br) =
5856                                    (&a[dy * rw..dy * rw + rw], &b[dy * rw..dy * rw + rw]);
5857                                let base = (py + dy) * 16 + px;
5858                                let dst = &mut pred_y[base..base + rw];
5859                                match weights {
5860                                    None => {
5861                                        for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
5862                                            *d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
5863                                        }
5864                                    }
5865                                    Some((w0, w1)) => {
5866                                        for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
5867                                            *d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6)
5868                                                .clamp(0, 255)
5869                                                as u8;
5870                                        }
5871                                    }
5872                                }
5873                            }
5874                        }
5875                        (true, false) => {
5876                            for dy in 0..rh {
5877                                let d = (py + dy) * 16 + px;
5878                                pred_y[d..d + rw].copy_from_slice(&a[dy * rw..dy * rw + rw]);
5879                            }
5880                        }
5881                        _ => {
5882                            for dy in 0..rh {
5883                                let d = (py + dy) * 16 + px;
5884                                pred_y[d..d + rw].copy_from_slice(&b[dy * rw..dy * rw + rw]);
5885                            }
5886                        }
5887                    }
5888                    false
5889                }
5890            });
5891        if chroma_done {
5892            return;
5893        }
5894        let _gc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
5895        self.b_mc_chroma(
5896            mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch,
5897        );
5898    }
5899
5900    /// Chroma half of `b_mc`, with the same full-width direct-write fusion
5901    /// (crw == 8 rows are contiguous in the 8-wide `c_pred` planes).
5902    #[allow(clippy::too_many_arguments)]
5903    /// Chroma half of `b_mc`. U and V share every piece of MC geometry, so
5904    /// each list is ONE `mc_chroma_padded_pair` call (setup + range check paid
5905    /// once, kernels unchanged) instead of two per-plane calls — the per-plane
5906    /// pixel math is byte-identical to the old per-plane flow.
5907    #[allow(clippy::too_many_arguments)]
5908    fn b_mc_chroma(
5909        &self,
5910        mb_x: usize,
5911        mb_y: usize,
5912        px: usize,
5913        py: usize,
5914        rw: usize,
5915        rh: usize,
5916        refi0: i32,
5917        mv0: (i32, i32),
5918        refi1: i32,
5919        mv1: (i32, i32),
5920        c_pred: &mut [[u8; 64]; 2],
5921        weights: Option<(i32, i32)>,
5922        cch: usize,
5923    ) {
5924        use rusty_h264_common::inter::mc_chroma_padded_pair;
5925        let (crx, cry, crw, crh) = (px / 2, py / 2, rw / 2, rh / 2);
5926        let full = crx == 0 && crw == 8;
5927        let n = crw * crh;
5928        // RESOLVE BOTH SLOTS ONCE, then let the match patterns bind them. Every
5929        // arm below re-indexed `refs`/`refs1` by a raw `refi` — six checked Vec
5930        // indexes across the four arms — although `a0`/`a1` already meant
5931        // exactly "this slot is present". Carrying `Option<&RefPic>` instead of
5932        // a bool makes "present" and "in range" the same fact.
5933        let rf0 = (refi0 >= 0)
5934            .then(|| self.refs.get(refi0 as usize))
5935            .flatten();
5936        let rf1 = (refi1 >= 0)
5937            .then(|| self.refs1.get(refi1 as usize))
5938            .flatten();
5939        let (a0, a1) = (rf0.is_some(), rf1.is_some());
5940        let [cu, cv] = c_pred;
5941        match (a0, a1, full) {
5942            (true, false, true) | (false, true, true) => {
5943                let (Some(rf), mv) = (if a0 { rf0 } else { rf1 }, if a0 { mv0 } else { mv1 })
5944                else {
5945                    return;
5946                };
5947                let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
5948                mc_chroma_padded_pair(
5949                    &gu,
5950                    &gv,
5951                    rf.cstride(),
5952                    crate::CPAD,
5953                    self.ccw,
5954                    cch,
5955                    mb_x * 8,
5956                    mb_y * 8 + cry,
5957                    crw,
5958                    crh,
5959                    mv.0,
5960                    mv.1,
5961                    &mut cu[cry * 8..cry * 8 + n],
5962                    &mut cv[cry * 8..cry * 8 + n],
5963                );
5964            }
5965            (true, true, true) => {
5966                let (Some(rfa), Some(rfb)) = (rf0, rf1) else {
5967                    return;
5968                };
5969                {
5970                    let rf = rfa;
5971                    let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
5972                    mc_chroma_padded_pair(
5973                        &gu,
5974                        &gv,
5975                        rf.cstride(),
5976                        crate::CPAD,
5977                        self.ccw,
5978                        cch,
5979                        mb_x * 8,
5980                        mb_y * 8 + cry,
5981                        crw,
5982                        crh,
5983                        mv0.0,
5984                        mv0.1,
5985                        &mut cu[cry * 8..cry * 8 + n],
5986                        &mut cv[cry * 8..cry * 8 + n],
5987                    );
5988                }
5989                let (mut bu, mut bv) = ([0u8; 64], [0u8; 64]);
5990                {
5991                    let rf = rfb;
5992                    let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
5993                    mc_chroma_padded_pair(
5994                        &gu,
5995                        &gv,
5996                        rf.cstride(),
5997                        crate::CPAD,
5998                        self.ccw,
5999                        cch,
6000                        mb_x * 8,
6001                        mb_y * 8 + cry,
6002                        crw,
6003                        crh,
6004                        mv1.0,
6005                        mv1.1,
6006                        &mut bu[..n],
6007                        &mut bv[..n],
6008                    );
6009                }
6010                for (dst, stage) in [(&mut *cu, &bu), (&mut *cv, &bv)] {
6011                    let d = &mut dst[cry * 8..cry * 8 + n];
6012                    match weights {
6013                        None => {
6014                            for (o, q) in d.iter_mut().zip(&stage[..n]) {
6015                                *o = ((*o as u16 + *q as u16 + 1) >> 1) as u8;
6016                            }
6017                        }
6018                        Some((w0, w1)) => {
6019                            for (o, q) in d.iter_mut().zip(&stage[..n]) {
6020                                *o = ((*o as i32 * w0 + *q as i32 * w1 + 32) >> 6).clamp(0, 255)
6021                                    as u8;
6022                            }
6023                        }
6024                    }
6025                }
6026            }
6027            _ => {
6028                // Narrow region — rows are strided in the 8-wide pred planes;
6029                // stage per list (paired), then copy/blend strided per plane.
6030                let (mut au, mut av, mut bu, mut bv) = ([0u8; 64], [0u8; 64], [0u8; 64], [0u8; 64]);
6031                if let Some(rf) = rf0 {
6032                    let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
6033                    mc_chroma_padded_pair(
6034                        &gu,
6035                        &gv,
6036                        rf.cstride(),
6037                        crate::CPAD,
6038                        self.ccw,
6039                        cch,
6040                        mb_x * 8 + crx,
6041                        mb_y * 8 + cry,
6042                        crw,
6043                        crh,
6044                        mv0.0,
6045                        mv0.1,
6046                        &mut au[..n],
6047                        &mut av[..n],
6048                    );
6049                }
6050                if let Some(rf) = rf1 {
6051                    let (gu, gv) = (rf.chroma_guard(0, rf.ch), rf.chroma_guard(1, rf.ch));
6052                    mc_chroma_padded_pair(
6053                        &gu,
6054                        &gv,
6055                        rf.cstride(),
6056                        crate::CPAD,
6057                        self.ccw,
6058                        cch,
6059                        mb_x * 8 + crx,
6060                        mb_y * 8 + cry,
6061                        crw,
6062                        crh,
6063                        mv1.0,
6064                        mv1.1,
6065                        &mut bu[..n],
6066                        &mut bv[..n],
6067                    );
6068                }
6069                for (dst, sa, sb) in [(&mut *cu, &au, &bu), (&mut *cv, &av, &bv)] {
6070                    for dy in 0..crh {
6071                        let base = (cry + dy) * 8 + crx;
6072                        let d = &mut dst[base..base + crw];
6073                        match (a0, a1) {
6074                            (true, true) => {
6075                                let (pr, qr) =
6076                                    (&sa[dy * crw..dy * crw + crw], &sb[dy * crw..dy * crw + crw]);
6077                                match weights {
6078                                    None => {
6079                                        for ((o, pp), q) in d.iter_mut().zip(pr).zip(qr) {
6080                                            *o = ((*pp as u16 + *q as u16 + 1) >> 1) as u8;
6081                                        }
6082                                    }
6083                                    Some((w0, w1)) => {
6084                                        for ((o, pp), q) in d.iter_mut().zip(pr).zip(qr) {
6085                                            *o = ((*pp as i32 * w0 + *q as i32 * w1 + 32) >> 6)
6086                                                .clamp(0, 255)
6087                                                as u8;
6088                                        }
6089                                    }
6090                                }
6091                            }
6092                            (true, false) => d.copy_from_slice(&sa[dy * crw..dy * crw + crw]),
6093                            _ => d.copy_from_slice(&sb[dy * crw..dy * crw + crw]),
6094                        }
6095                    }
6096                }
6097            }
6098        }
6099    }
6100
6101    /// Commits a region's per-list motion to the 4×4 grids (and marks coded).
6102    #[allow(clippy::too_many_arguments)]
6103    fn b_set_motion(
6104        &mut self,
6105        mb_x: usize,
6106        mb_y: usize,
6107        px: usize,
6108        py: usize,
6109        rw: usize,
6110        rh: usize,
6111        refi0: i32,
6112        mv0: (i32, i32),
6113        refi1: i32,
6114        mv1: (i32, i32),
6115    ) {
6116        let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBSet);
6117        let w4 = self.mb_w * 4;
6118        let mv0w = if refi0 >= 0 { mv0 } else { (0, 0) };
6119        let mv1w = if refi1 >= 0 { mv1 } else { (0, 0) };
6120        let by0 = mb_y * 4 + py / 4;
6121        let bx0 = mb_x * 4 + px / 4;
6122        let (bw, bh) = (rw / 4, rh / 4);
6123        // Contiguous per-row slices: fill instead of per-4x4 stores (same values).
6124        // SEVEN parallel grids, seven separate range checks — they are disjoint
6125        // fields of `self`, so one `get_mut` apiece resolves them all together
6126        // and a short row simply writes nothing rather than panicking.
6127        for by in by0..by0 + bh {
6128            let row = by * w4 + bx0;
6129            let end = row + bw;
6130            let (Some(r0), Some(m0), Some(r1), Some(m1)) = (
6131                self.ref_idx_y.get_mut(row..end),
6132                self.mv_y.get_mut(row..end),
6133                self.ref_idx1.get_mut(row..end),
6134                self.mv1.get_mut(row..end),
6135            ) else {
6136                continue;
6137            };
6138            r0.fill(refi0);
6139            m0.fill(mv0w);
6140            r1.fill(refi1);
6141            m1.fill(mv1w);
6142            let (Some(it), Some(cd), Some(md)) = (
6143                self.inter_y.get_mut(row..end),
6144                self.coded_y.get_mut(row..end),
6145                self.modes_y.get_mut(row..end),
6146            ) else {
6147                continue;
6148            };
6149            it.fill(true);
6150            cd.fill(true);
6151            md.fill(2);
6152        }
6153    }
6154
6155    /// Spatial direct prediction for a region (whole MB or an 8×8): derives the
6156    /// per-list reference indices and base MVs, then motion-compensates each 4×4
6157    /// sub-block (applying `colZeroFlag`) and commits the motion (spec §8.4.1.2.2).
6158    #[allow(clippy::too_many_arguments)]
6159    /// Splits a `w`×`h` block region (4×4-block units) into the fewest rectangles
6160    /// whose contents are `uniform`, preferring partition-shaped cuts (whole →
6161    /// horizontal halves → vertical halves → quadrants). Emits at most w·h rects
6162    /// (the all-different worst case degenerates to per-block, i.e. the old loop).
6163    fn coalesce_region<
6164        U: Fn(usize, usize, usize, usize) -> bool,
6165        E: FnMut(usize, usize, usize, usize),
6166    >(
6167        x: usize,
6168        y: usize,
6169        w: usize,
6170        h: usize,
6171        uniform: &U,
6172        emit: &mut E,
6173    ) {
6174        if uniform(x, y, w, h) {
6175            emit(x, y, w, h);
6176            return;
6177        }
6178        if h > 1 && uniform(x, y, w, h / 2) && uniform(x, y + h / 2, w, h / 2) {
6179            emit(x, y, w, h / 2);
6180            emit(x, y + h / 2, w, h / 2);
6181            return;
6182        }
6183        if w > 1 && uniform(x, y, w / 2, h) && uniform(x + w / 2, y, w / 2, h) {
6184            emit(x, y, w / 2, h);
6185            emit(x + w / 2, y, w / 2, h);
6186            return;
6187        }
6188        match (w > 1, h > 1) {
6189            (true, true) => {
6190                for q in 0..4usize {
6191                    Self::coalesce_region(
6192                        x + (q % 2) * (w / 2),
6193                        y + (q / 2) * (h / 2),
6194                        w / 2,
6195                        h / 2,
6196                        uniform,
6197                        emit,
6198                    );
6199                }
6200            }
6201            (true, false) => {
6202                Self::coalesce_region(x, y, w / 2, h, uniform, emit);
6203                Self::coalesce_region(x + w / 2, y, w / 2, h, uniform, emit);
6204            }
6205            (false, true) => {
6206                Self::coalesce_region(x, y, w, h / 2, uniform, emit);
6207                Self::coalesce_region(x, y + h / 2, w, h / 2, uniform, emit);
6208            }
6209            (false, false) => emit(x, y, 1, 1),
6210        }
6211    }
6212
6213    fn decode_b_direct(
6214        &mut self,
6215        mb_x: usize,
6216        mb_y: usize,
6217        px: usize,
6218        py: usize,
6219        rw: usize,
6220        rh: usize,
6221        pred_y: &mut [u8; 256],
6222        c_pred: &mut [[u8; 64]; 2],
6223    ) {
6224        if !self.direct_spatial {
6225            return self.decode_b_direct_temporal(mb_x, mb_y, px, py, rw, rh, pred_y, c_pred);
6226        }
6227        let (n0, n1) = self.b_direct_nbrs(mb_x, mb_y);
6228        self.decode_b_direct_n(mb_x, mb_y, px, py, rw, rh, pred_y, c_pred, n0, n1);
6229    }
6230
6231    /// Spec §8.4.1.2.2: the spatial-direct reference indices (min positive over
6232    /// the three neighbors, per list) and the predicted MVs. `direct_zero` is
6233    /// the both-lists-unavailable case, which forces ref 0 / mv (0,0). Shared
6234    /// by `decode_b_direct_n` and the B_Skip zero-bi fast path — ONE derivation,
6235    /// no drift.
6236    #[inline]
6237    fn b_direct_refs_mvs(
6238        n0: &[MvNeighbor; 3],
6239        n1: &[MvNeighbor; 3],
6240    ) -> (i32, i32, (i32, i32), (i32, i32), bool) {
6241        let min_pos = |a: i32, b: i32| {
6242            if a < 0 {
6243                b
6244            } else if b < 0 {
6245                a
6246            } else {
6247                a.min(b)
6248            }
6249        };
6250        let rid = |n: &[MvNeighbor; 3]| min_pos(min_pos(n[0].ref_idx, n[1].ref_idx), n[2].ref_idx);
6251        let (mut refi0, mut refi1) = (rid(n0), rid(n1));
6252        let direct_zero = refi0 < 0 && refi1 < 0;
6253        if direct_zero {
6254            refi0 = 0;
6255            refi1 = 0;
6256        }
6257        let mv0 = if refi0 >= 0 && !direct_zero {
6258            predict_mv(n0[0], n0[1], n0[2], refi0)
6259        } else {
6260            (0, 0)
6261        };
6262        let mv1 = if refi1 >= 0 && !direct_zero {
6263            predict_mv(n1[0], n1[1], n1[2], refi1)
6264        } else {
6265            (0, 0)
6266        };
6267        (refi0, refi1, mv0, mv1, direct_zero)
6268    }
6269
6270    fn decode_b_direct_n(
6271        &mut self,
6272        mb_x: usize,
6273        mb_y: usize,
6274        px: usize,
6275        py: usize,
6276        rw: usize,
6277        rh: usize,
6278        pred_y: &mut [u8; 256],
6279        c_pred: &mut [[u8; 64]; 2],
6280        n0: [MvNeighbor; 3],
6281        n1: [MvNeighbor; 3],
6282    ) {
6283        let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBDirect);
6284        // H-48: DERIVATION-ONLY scope, dropped before the MC loop below. DecBDirect
6285        // wraps this function whole and therefore INCLUDES the `b_mc` calls it makes,
6286        // so its 1460 ns/call was never "MV derivation is slow" — that read was wrong.
6287        // This guard is what separates the two.
6288        let gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBDeriv);
6289        let derived = Self::b_direct_refs_mvs(&n0, &n1);
6290        drop(gd);
6291        self.b_direct_region(mb_x, mb_y, px, py, rw, rh, pred_y, c_pred, derived);
6292    }
6293
6294    /// The post-derivation half of spatial direct: czg probing (gated), region
6295    /// coalescing, MC + motion commit. Split out so B_8x8 direct subs derive
6296    /// ONCE per MB (the A/B/C neighbours and the rid/median result are
6297    /// MB-level; only czg varies per 8x8).
6298    #[allow(clippy::too_many_arguments)]
6299    fn b_direct_region(
6300        &mut self,
6301        mb_x: usize,
6302        mb_y: usize,
6303        px: usize,
6304        py: usize,
6305        rw: usize,
6306        rh: usize,
6307        pred_y: &mut [u8; 256],
6308        c_pred: &mut [[u8; 64]; 2],
6309        derived: (i32, i32, (i32, i32), (i32, i32), bool),
6310    ) {
6311        let (refi0, refi1, mv0, mv1, direct_zero) = derived;
6312        // Per 4×4 sub-block: colZeroFlag zeroes the ref-0 motion vector. cz is the
6313        // ONLY per-block variable (two possible (m0,m1) values for the region), and
6314        // the MC filters + bi-blend are per-output-pixel — so sub-blocks with equal
6315        // cz coalesce into one wider `b_mc`, BIT-IDENTICAL. A 16×16 direct MB paid
6316        // 16 bi-pred b_mc calls (~96 MC kernel entries) before this; typically 1 now.
6317        let (bx0, by0, bw, bh) = (px / 4, py / 4, rw / 4, rh / 4);
6318        // MASKED at every use below. The loop bounds are the region's `bw`/`bh` in
6319        // 4x4 units, which are always <= 4 for a macroblock but are RUNTIME values,
6320        // so none of the eight indexes into this fixed 4x4 grid was provable.
6321        let mut czg = [[false; 4]; 4]; // region-local, [dy][dx]
6322                                       // colZeroFlag can only change a list whose ref is 0 AND whose predicted
6323                                       // MV is nonzero (it zeroes MVs; zeroing (0,0) is a no-op). When neither
6324                                       // list qualifies, skip the probing entirely: czg stays false, the
6325                                       // region is uniform, and the m values are identical — the only change
6326                                       // is FEWER b_mc calls where the old czg would have split rects with
6327                                       // equal values (tiles of the same math, byte-identical).
6328        let cz_matters = (refi0 == 0 && mv0 != (0, 0)) || (refi1 == 0 && mv1 != (0, 0));
6329        // Uniformity is KNOWN after the probe fill — when every probe agreed,
6330        // the 16-bool coalesce scan and its recursion are skipped outright.
6331        let mut cz_mixed = false;
6332        if cz_matters && !direct_zero && self.direct_8x8_inference {
6333            // Under direct_8x8_inference every 4×4 in an 8×8 shares one colZeroFlag
6334            // (col_block collapses to the MB-corner). Probe once per 8×8 — same
6335            // czg, fewer wait_motion_ready / meta locks on the live-ref path.
6336            let mut oy = 0usize;
6337            while oy < bh {
6338                let h = (bh - oy).min(2);
6339                let mut ox = 0usize;
6340                while ox < bw {
6341                    let w = (bw - ox).min(2);
6342                    let (colx, coly) = self.col_block(bx0 + ox, by0 + oy);
6343                    let cz = self.col_zero(mb_x * 4 + colx, mb_y * 4 + coly);
6344                    cz_mixed |= cz != czg[0][0] && !(ox == 0 && oy == 0);
6345                    for dy in oy..oy + h {
6346                        for dx in ox..ox + w {
6347                            czg[dy & 3][dx & 3] = cz;
6348                        }
6349                    }
6350                    ox += w;
6351                }
6352                oy += h;
6353            }
6354        } else if cz_matters && !direct_zero {
6355            for dy in 0..bh {
6356                for dx in 0..bw {
6357                    let (colx, coly) = self.col_block(bx0 + dx, by0 + dy);
6358                    let cz = self.col_zero(mb_x * 4 + colx, mb_y * 4 + coly);
6359                    cz_mixed |= cz != czg[0][0] && !(dx == 0 && dy == 0);
6360                    czg[dy & 3][dx & 3] = cz;
6361                }
6362            }
6363        }
6364        let mut rects: [(usize, usize, usize, usize); 16] = [(0, 0, 0, 0); 16];
6365        let mut n = 0usize;
6366        if !cz_mixed {
6367            rects[0] = (0, 0, bw, bh);
6368            n = 1;
6369        } else {
6370            let uniform = |x: usize, y: usize, w: usize, h: usize| -> bool {
6371                let t = czg[y & 3][x & 3];
6372                (y..y + h).all(|dy| (x..x + w).all(|dx| czg[dy & 3][dx & 3] == t))
6373            };
6374            Self::coalesce_region(0, 0, bw, bh, &uniform, &mut |x, y, w, h| {
6375                rects[n & 15] = (x, y, w, h);
6376                n += 1;
6377            });
6378        }
6379        let recording = self.edc_regions.is_some();
6380        if rw == 16 && rh == 16 {
6381            edcstat::bump(&edcstat::BSK_FULLMB, 1);
6382            if n == 1 {
6383                edcstat::bump(&edcstat::BSK_1RECT, 1);
6384                let cz = czg[0][0];
6385                let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
6386                let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
6387                let (a0, a1) = (refi0 >= 0, refi1 >= 0);
6388                if a0 && a1 && m0 == (0, 0) && m1 == (0, 0) {
6389                    edcstat::bump(&edcstat::BSK_ZBI, 1);
6390                } else if (a0 != a1) && (if a0 { m0 } else { m1 }) == (0, 0) {
6391                    edcstat::bump(&edcstat::BSK_ZUNI, 1);
6392                } else if (!a0 || (m0.0 % 4 == 0 && m0.1 % 4 == 0))
6393                    && (!a1 || (m1.0 % 4 == 0 && m1.1 % 4 == 0))
6394                {
6395                    edcstat::bump(&edcstat::BSK_FP, 1);
6396                }
6397                if self.bsk_last == Some((mb_x.wrapping_sub(1), mb_y, refi0, refi1, m0, m1)) {
6398                    edcstat::bump(&edcstat::BSK_RUNCONT, 1);
6399                }
6400                self.bsk_last = Some((mb_x, mb_y, refi0, refi1, m0, m1));
6401            } else {
6402                self.bsk_last = None;
6403            }
6404        }
6405        for &(x, y, w, h) in &rects[..n] {
6406            let cz = czg[y & 3][x & 3];
6407            let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
6408            let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
6409            let (lx, ly, lw, lh) = ((bx0 + x) * 4, (by0 + y) * 4, w * 4, h * 4);
6410            if recording {
6411                self.b_mc_or_record(
6412                    mb_x, mb_y, lx, ly, lw, lh, refi0, m0, refi1, m1, pred_y, c_pred,
6413                );
6414            } else {
6415                self.b_mc(
6416                    mb_x, mb_y, lx, ly, lw, lh, refi0, m0, refi1, m1, pred_y, c_pred,
6417                );
6418            }
6419            self.b_set_motion(mb_x, mb_y, lx, ly, lw, lh, refi0, m0, refi1, m1);
6420        }
6421    }
6422
6423    /// Temporal direct prediction for a region (spec §8.4.1.2.3): for each 4×4
6424    /// (or per-8×8 corner under `direct_8x8_inference`), take the co-located
6425    /// List-0 motion from `RefPicList1[0]`, map its reference into the current
6426    /// List-0 by POC, and scale the motion vector by the POC distances.
6427    #[allow(clippy::too_many_arguments)]
6428    fn decode_b_direct_temporal(
6429        &mut self,
6430        mb_x: usize,
6431        mb_y: usize,
6432        px: usize,
6433        py: usize,
6434        rw: usize,
6435        rh: usize,
6436        pred_y: &mut [u8; 256],
6437        c_pred: &mut [[u8; 64]; 2],
6438    ) {
6439        let poc1 = self.refs1.first().map_or(0, |f| f.pic_poc());
6440        let infer = self.direct_8x8_inference;
6441        // Under direct_8x8_inference every 4×4 in an 8×8 takes the same MB-corner
6442        // co-located motion, so motion-compensate the whole 8×8 in one call — this
6443        // hits the width-8 MC asm and pays the per-call tile/blend setup 4× less.
6444        // Without inference, motion is genuinely per-4×4. Bit-identical either way
6445        // (MC of an 8×8 with one MV == four 4×4 MCs with that same MV).
6446        let step = if infer { 8 } else { 4 };
6447        let mut sy = py;
6448        while sy < py + rh {
6449            let mut sx = px;
6450            while sx < px + rw {
6451                // Co-located 4×4 (the 8×8's MB-corner under inference) — shared with
6452                // the spatial path's colZeroFlag, which must map identically.
6453                let (colx, coly) = self.col_block(sx / 4, sy / 4);
6454                let (mvcol, refpoc) = {
6455                    let Some(col) = self.refs1.first() else {
6456                        return;
6457                    };
6458                    if let Some(live) = col.live.as_ref() {
6459                        col.wait_motion_ready();
6460                        let meta = live.meta.read().unwrap();
6461                        let idx = (mb_y * 4 + coly) * meta.w4 + (mb_x * 4 + colx);
6462                        // `mv` and `ref_poc` are PARALLEL Vecs: the `mv.len()`
6463                        // guard said nothing about `ref_poc`.
6464                        match (meta.mv.get(idx), meta.ref_poc.get(idx)) {
6465                            (Some(&m), Some(&pc)) if meta.w4 != 0 && pc != i32::MIN => (m, pc),
6466                            _ => ((0, 0), i32::MIN),
6467                        }
6468                    } else {
6469                        let idx = (mb_y * 4 + coly) * col.w4 + (mb_x * 4 + colx);
6470                        // intra co-located → zero motion, refIdxL0 = 0
6471                        match (col.mv.get(idx), col.ref_poc.get(idx)) {
6472                            (Some(&m), Some(&pc)) if col.w4 != 0 && pc != i32::MIN => (m, pc),
6473                            _ => ((0, 0), i32::MIN),
6474                        }
6475                    }
6476                };
6477                // MapColToList0: the current-list index of the co-located reference.
6478                let (refi0, mvc) = if refpoc == i32::MIN {
6479                    (0, (0, 0))
6480                } else {
6481                    let r = self
6482                        .refs
6483                        .iter()
6484                        .position(|f| f.pic_poc() == refpoc)
6485                        .unwrap_or(0) as i32;
6486                    (r, mvcol)
6487                };
6488                let Some(r0) = self.refs.get(refi0 as usize) else {
6489                    return;
6490                };
6491                let poc0 = r0.pic_poc();
6492                let td = (poc1 - poc0).clamp(-128, 127);
6493                let tb = (self.cur_poc - poc0).clamp(-128, 127);
6494                let (mv0, mv1) = if td == 0 || r0.long_term {
6495                    (mvc, (0, 0))
6496                } else {
6497                    let tx = tx_for_td(td);
6498                    let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
6499                    let m0 = ((dsf * mvc.0 + 128) >> 8, (dsf * mvc.1 + 128) >> 8);
6500                    (m0, (m0.0 - mvc.0, m0.1 - mvc.1))
6501                };
6502                self.b_mc_or_record(
6503                    mb_x, mb_y, sx, sy, step, step, refi0, mv0, 0, mv1, pred_y, c_pred,
6504                );
6505                self.b_set_motion(mb_x, mb_y, sx, sy, step, step, refi0, mv0, 0, mv1);
6506                sx += step;
6507            }
6508            sy += step;
6509        }
6510    }
6511
6512    /// Reads `ref_idx_lX` for a B partition (te(v)/ue(v) by the list's active
6513    /// count), bounds-checked against the available reference count.
6514    fn read_b_ref(&self, r: &mut BitReader, list: usize) -> Result<i32, MbError> {
6515        let (active, avail) = if list == 0 {
6516            (self.num_ref_active, self.refs.len())
6517        } else {
6518            (self.num_ref_active1, self.refs1.len())
6519        };
6520        let v = if active > 1 {
6521            read_ref_idx(r, active)?
6522        } else {
6523            0
6524        };
6525        if v as usize >= avail {
6526            return Err(MbError::Truncated);
6527        }
6528        Ok(v)
6529    }
6530
6531    /// Reconstructs a `B_Skip` macroblock: spatial-direct prediction, no residual.
6532    /// Defer one fast B_Skip's grid commit + recon into the pending row span.
6533    /// Continuation requires position adjacency AND kind equality.
6534    #[inline]
6535    fn bz_push(&mut self, mb_x: usize, mb_y: usize, kind: BzKind) {
6536        match self.bzspan {
6537            Some((row, x0, ref mut n, k)) if row == mb_y && x0 + *n == mb_x && k == kind => *n += 1,
6538            _ => {
6539                self.bz_flush();
6540                self.bzspan = Some((mb_y, mb_x, 1, kind));
6541            }
6542        }
6543    }
6544
6545    /// Range-fill the pending span's grids. MUST run before anything reads
6546    /// the motion/coded/mode/nnz grids of a deferred MB: decode_b_mb entry,
6547    /// decode_b_skip's b_direct_nbrs, bS derivation (derive_bs_row callers
6548    /// via row_hook + deblock), and slice end (edc_flush covers it).
6549    #[inline(always)]
6550    fn bz_flush(&mut self) {
6551        if self.bzspan.is_some() {
6552            self.bz_flush_slow();
6553        }
6554    }
6555
6556    fn bz_flush_slow(&mut self) {
6557        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BSpanRecon);
6558        let Some((row, x0, n, kind)) = self.bzspan.take() else {
6559            return;
6560        };
6561        edcstat::bump(&edcstat::BZ_SPANS, 1);
6562        edcstat::bump(&edcstat::BZ_SPAN_MBS, n as u64);
6563        // Grid values per kind (mirrors b_set_motion's inactive-list zeroing).
6564        let (g_r0, g_r1, g_m0, g_m1): (i32, i32, (i32, i32), (i32, i32)) = match kind {
6565            BzKind::ZeroBi => (0, 0, (0, 0), (0, 0)),
6566            BzKind::ZeroUni(0, ri) => (ri as i32, -1, (0, 0), (0, 0)),
6567            BzKind::ZeroUni(_, ri) => (-1, ri as i32, (0, 0), (0, 0)),
6568            BzKind::Fp { r0, r1, m0, m1 } => {
6569                let mm0 = if r0 >= 0 {
6570                    (m0.0 as i32, m0.1 as i32)
6571                } else {
6572                    (0, 0)
6573                };
6574                let mm1 = if r1 >= 0 {
6575                    (m1.0 as i32, m1.1 as i32)
6576                } else {
6577                    (0, 0)
6578                };
6579                (r0 as i32, r1 as i32, mm0, mm1)
6580            }
6581        };
6582        let w4 = self.mb_w * 4;
6583        let (b0, len) = (x0 * 4, n * 4);
6584        for dy in 0..4 {
6585            let a = (row * 4 + dy) * w4 + b0;
6586            self.ref_idx_y[a..a + len].fill(g_r0);
6587            self.mv_y[a..a + len].fill(g_m0);
6588            self.ref_idx1[a..a + len].fill(g_r1);
6589            self.mv1[a..a + len].fill(g_m1);
6590            self.inter_y[a..a + len].fill(true);
6591            self.coded_y[a..a + len].fill(true);
6592            self.modes_y[a..a + len].fill(2);
6593            self.nnz_y[a..a + len].fill(0);
6594        }
6595        match kind {
6596            BzKind::ZeroBi => self.bz_recon_band_bi(row, x0, n),
6597            BzKind::ZeroUni(list, ri) => {
6598                self.bz_recon_band_copy(row, x0, n, list as usize, ri as usize, (0, 0))
6599            }
6600            BzKind::Fp { r0, r1, m0, m1 } => {
6601                let mv0 = (m0.0 as i32, m0.1 as i32);
6602                let mv1 = (m1.0 as i32, m1.1 as i32);
6603                match (r0 >= 0, r1 >= 0) {
6604                    (true, true) => {
6605                        self.bz_recon_band_fp_bi(row, x0, n, r0 as usize, r1 as usize, mv0, mv1)
6606                    }
6607                    (true, false) => self.bz_recon_band_copy(row, x0, n, 0, r0 as usize, mv0),
6608                    _ => self.bz_recon_band_copy(row, x0, n, 1, r1 as usize, mv1),
6609                }
6610            }
6611        }
6612    }
6613
6614    /// ZeroBi band: rows of 16n averaged from both padded refs (index 0).
6615    fn bz_recon_band_bi(&mut self, row: usize, x0: usize, n: usize) {
6616        // Byte-identical to n recon_b_skip_zero_bi(_, _, 0, 0) calls: each
6617        // output byte is the same (a + b + 1) >> 1 of the same source bytes.
6618        let w = n * 16;
6619        let Some(rf0) = self.refs.first() else { return };
6620        let Some(rf1) = self.refs1.first() else {
6621            return;
6622        };
6623        let (l0st, l1st) = (rf0.lstride(), rf1.lstride());
6624        {
6625            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
6626            let (ly0, ly1) = (rf0.luma_guard(rf0.ch), rf1.luma_guard(rf1.ch));
6627            for dy in 0..16 {
6628                let y = row * 16 + dy;
6629                let s0 = (y + crate::LPAD) * l0st + crate::LPAD + x0 * 16;
6630                let s1 = (y + crate::LPAD) * l1st + crate::LPAD + x0 * 16;
6631                let d = y * self.cw + x0 * 16;
6632                // WIN: this bi-average was a scalar byte loop; `pixel_avg` computes
6633                // (a + b + 1) >> 1 exactly with pavgb, and the row helper walks the
6634                // span in kernel-legal widths.
6635                rusty_h264_common::inter::avg_row_into(
6636                    &ly0[s0..s0 + w],
6637                    &ly1[s1..s1 + w],
6638                    w,
6639                    &mut self.rec_y[d..d + w],
6640                );
6641            }
6642        }
6643        let (c0st, c1st) = (rf0.cstride(), rf1.cstride());
6644        let wc = n * 8;
6645        for c in 0..2 {
6646            let rc0 = rf0.chroma_guard(c, rf0.ch);
6647            let rc1 = rf1.chroma_guard(c, rf1.ch);
6648            let plane = if c == 0 {
6649                &mut self.rec_u
6650            } else {
6651                &mut self.rec_v
6652            };
6653            for dy in 0..8 {
6654                let y = row * 8 + dy;
6655                let s0 = (y + crate::CPAD) * c0st + crate::CPAD + x0 * 8;
6656                let s1 = (y + crate::CPAD) * c1st + crate::CPAD + x0 * 8;
6657                let d = y * self.ccw + x0 * 8;
6658                // WIN: this bi-average was a scalar byte loop; `pixel_avg` computes
6659                // (a + b + 1) >> 1 exactly with pavgb, and the row helper walks the
6660                // span in kernel-legal widths.
6661                rusty_h264_common::inter::avg_row_into(
6662                    &rc0[s0..s0 + wc],
6663                    &rc1[s1..s1 + wc],
6664                    wc,
6665                    &mut plane[d..d + wc],
6666                );
6667            }
6668        }
6669    }
6670
6671    /// Full-pel offset copy band from ONE list's padded planes. The caller
6672    /// prevalidated (per MB, at push): mv%8 == 0 both components and luma +
6673    /// chroma windows inside the padded planes — contiguous MB windows tile,
6674    /// so the span's union window is valid by induction.
6675    #[allow(clippy::too_many_arguments)]
6676    fn bz_recon_band_copy(
6677        &mut self,
6678        row: usize,
6679        x0: usize,
6680        n: usize,
6681        list: usize,
6682        ri: usize,
6683        mv: (i32, i32),
6684    ) {
6685        let Some(rf) = (if list == 0 {
6686            self.refs.get(ri)
6687        } else {
6688            self.refs1.get(ri)
6689        }) else {
6690            return;
6691        };
6692        let lst = rf.lstride();
6693        let (dx, dy) = ((mv.0 / 4) as isize, (mv.1 / 4) as isize);
6694        let w = n * 16;
6695        {
6696            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
6697            let ly = rf.luma_guard(rf.ch);
6698            for r in 0..16 {
6699                let y = (row * 16 + r) as isize + dy;
6700                let src = ((y + crate::LPAD as isize) * lst as isize
6701                    + crate::LPAD as isize
6702                    + x0 as isize * 16
6703                    + dx) as usize;
6704                let d = (row * 16 + r) * self.cw + x0 * 16;
6705                self.rec_y[d..d + w].copy_from_slice(&ly[src..src + w]);
6706            }
6707        }
6708        let cst = rf.cstride();
6709        let (cdx, cdy) = ((mv.0 / 8) as isize, (mv.1 / 8) as isize);
6710        let wc = n * 8;
6711        for c in 0..2 {
6712            let rc = rf.chroma_guard(c, rf.ch);
6713            let plane = if c == 0 {
6714                &mut self.rec_u
6715            } else {
6716                &mut self.rec_v
6717            };
6718            for r in 0..8 {
6719                let y = (row * 8 + r) as isize + cdy;
6720                let src = ((y + crate::CPAD as isize) * cst as isize
6721                    + crate::CPAD as isize
6722                    + x0 as isize * 8
6723                    + cdx) as usize;
6724                let d = (row * 8 + r) * self.ccw + x0 * 8;
6725                plane[d..d + wc].copy_from_slice(&rc[src..src + wc]);
6726            }
6727        }
6728    }
6729
6730    /// Full-pel bi band: rows of 16n averaged from two offset windows
6731    /// (prevalidated as above; implicit weights None/(32,32) guaranteed by
6732    /// the pushing arm).
6733    #[allow(clippy::too_many_arguments)]
6734    fn bz_recon_band_fp_bi(
6735        &mut self,
6736        row: usize,
6737        x0: usize,
6738        n: usize,
6739        r0: usize,
6740        r1: usize,
6741        mv0: (i32, i32),
6742        mv1: (i32, i32),
6743    ) {
6744        let (Some(rf0), Some(rf1)) = (self.refs.get(r0), self.refs1.get(r1)) else {
6745            return;
6746        };
6747        let (l0, l1) = (rf0.lstride(), rf1.lstride());
6748        let off = |st: usize, pad: isize, yy: isize, xx: isize| {
6749            (((yy + pad) * st as isize) + pad + xx) as usize
6750        };
6751        let w = n * 16;
6752        {
6753            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
6754            let (ly0, ly1) = (rf0.luma_guard(rf0.ch), rf1.luma_guard(rf1.ch));
6755            for r in 0..16 {
6756                let y = (row * 16 + r) as isize;
6757                let s0 = off(
6758                    l0,
6759                    crate::LPAD as isize,
6760                    y + (mv0.1 / 4) as isize,
6761                    x0 as isize * 16 + (mv0.0 / 4) as isize,
6762                );
6763                let s1 = off(
6764                    l1,
6765                    crate::LPAD as isize,
6766                    y + (mv1.1 / 4) as isize,
6767                    x0 as isize * 16 + (mv1.0 / 4) as isize,
6768                );
6769                let d = (row * 16 + r) * self.cw + x0 * 16;
6770                // WIN: this bi-average was a scalar byte loop; `pixel_avg` computes
6771                // (a + b + 1) >> 1 exactly with pavgb, and the row helper walks the
6772                // span in kernel-legal widths.
6773                rusty_h264_common::inter::avg_row_into(
6774                    &ly0[s0..s0 + w],
6775                    &ly1[s1..s1 + w],
6776                    w,
6777                    &mut self.rec_y[d..d + w],
6778                );
6779            }
6780        }
6781        let (c0, c1) = (rf0.cstride(), rf1.cstride());
6782        let wc = n * 8;
6783        for c in 0..2 {
6784            let rc0 = rf0.chroma_guard(c, rf0.ch);
6785            let rc1 = rf1.chroma_guard(c, rf1.ch);
6786            let plane = if c == 0 {
6787                &mut self.rec_u
6788            } else {
6789                &mut self.rec_v
6790            };
6791            for r in 0..8 {
6792                let y = (row * 8 + r) as isize;
6793                let s0 = off(
6794                    c0,
6795                    crate::CPAD as isize,
6796                    y + (mv0.1 / 8) as isize,
6797                    x0 as isize * 8 + (mv0.0 / 8) as isize,
6798                );
6799                let s1 = off(
6800                    c1,
6801                    crate::CPAD as isize,
6802                    y + (mv1.1 / 8) as isize,
6803                    x0 as isize * 8 + (mv1.0 / 8) as isize,
6804                );
6805                let d = (row * 8 + r) * self.ccw + x0 * 8;
6806                // WIN: this bi-average was a scalar byte loop; `pixel_avg` computes
6807                // (a + b + 1) >> 1 exactly with pavgb, and the row helper walks the
6808                // span in kernel-legal widths.
6809                rusty_h264_common::inter::avg_row_into(
6810                    &rc0[s0..s0 + wc],
6811                    &rc1[s1..s1 + wc],
6812                    wc,
6813                    &mut plane[d..d + wc],
6814                );
6815            }
6816        }
6817    }
6818
6819    /// Per-MB prevalidation for an Fp span push: mv%8 both components (chroma
6820    /// copies at mv/8) and luma + chroma windows inside the padded planes for
6821    /// every active list. False ⇒ the arm recons immediately instead.
6822    fn bz_fp_valid(
6823        &self,
6824        mbx: usize,
6825        mby: usize,
6826        r0: Option<usize>,
6827        r1: Option<usize>,
6828        mv0: (i32, i32),
6829        mv1: (i32, i32),
6830    ) -> bool {
6831        let chk = |rf: &crate::Ref, mv: (i32, i32)| -> bool {
6832            if mv.0 % 8 != 0 || mv.1 % 8 != 0 {
6833                return false;
6834            }
6835            let lst = rf.lstride();
6836            let lpad = crate::LPAD as isize;
6837            let cx = mbx as isize * 16 + (mv.0 / 4) as isize + lpad;
6838            let cy = mby as isize * 16 + (mv.1 / 4) as isize + lpad;
6839            let lrows = rf.lrows() as isize;
6840            if cx < 0 || cx + 16 > lst as isize || cy < 0 || cy + 16 > lrows {
6841                return false;
6842            }
6843            let cst = rf.cstride();
6844            let cpad = crate::CPAD as isize;
6845            let ccx = mbx as isize * 8 + (mv.0 / 8) as isize + cpad;
6846            let ccy = mby as isize * 8 + (mv.1 / 8) as isize + cpad;
6847            let crows = rf.crows() as isize;
6848            ccx >= 0 && ccx + 8 <= cst as isize && ccy >= 0 && ccy + 8 <= crows
6849        };
6850        r0.is_none_or(|i| self.refs.get(i).is_some_and(|r| chk(r, mv0)))
6851            && r1.is_none_or(|i| self.refs1.get(i).is_some_and(|r| chk(r, mv1)))
6852    }
6853
6854    /// Defer one (0,0) P_Skip's grid commit into the pending P span.
6855    #[inline]
6856    fn pz_push(&mut self, mb_x: usize, mb_y: usize, recon: bool) {
6857        match self.pzspan {
6858            Some((row, x0, ref mut n, r)) if row == mb_y && x0 + *n == mb_x && r == recon => {
6859                *n += 1
6860            }
6861            _ => {
6862                self.pz_flush();
6863                self.pzspan = Some((mb_y, mb_x, 1, recon));
6864            }
6865        }
6866    }
6867
6868    /// Range-fill the pending P span's grids (the deferral constants of
6869    /// decode_p_skip's commit: list-0 ref 0, mv (0,0), inter, coded, DC mode,
6870    /// deblock kind SKIP). Same flush points as the B span (span_flush).
6871    #[inline(always)]
6872    fn pz_flush(&mut self) {
6873        if self.pzspan.is_some() {
6874            self.pz_flush_slow();
6875        }
6876    }
6877
6878    fn pz_flush_slow(&mut self) {
6879        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BSpanRecon);
6880        let Some((row, x0, n, recon)) = self.pzspan.take() else {
6881            return;
6882        };
6883        edcstat::bump(&edcstat::PZ_SPANS, 1);
6884        edcstat::bump(&edcstat::PZ_SPAN_MBS, n as u64);
6885        let w4 = self.mb_w * 4;
6886        let (b0, len) = (x0 * 4, n * 4);
6887        for dy in 0..4 {
6888            let a = (row * 4 + dy) * w4 + b0;
6889            self.mv_y[a..a + len].fill((0, 0));
6890            self.inter_y[a..a + len].fill(true);
6891            self.ref_idx_y[a..a + len].fill(0);
6892            self.coded_y[a..a + len].fill(true);
6893            self.modes_y[a..a + len].fill(2);
6894        }
6895        self.mb_kind[row * self.mb_w + x0..row * self.mb_w + x0 + n]
6896            .fill(rusty_h264_common::deblock::MB_KIND_SKIP);
6897        if recon {
6898            // The recon deferred with the commit: one band copy from ref[0]
6899            // replaces n queued EdcJob::Skip pushes AND the flush-time
6900            // run-coalescing scan that used to rediscover this very run.
6901            self.recon_p_skip_band(x0, row, n);
6902        }
6903    }
6904
6905    /// Flush BOTH pending grid spans — the one call every grid reader makes.
6906    #[inline]
6907    fn span_flush(&mut self) {
6908        self.bz_flush();
6909        self.pz_flush();
6910    }
6911
6912    /// Hot prefix of decode_b_skip: the FORCED zero-bi run continuation,
6913    /// small enough to inline into the slice loops. Returns true when the MB
6914    /// was fully handled (deferred into the span); false falls to the cold
6915    /// body. Exactly the forced arm's conditions — no behavior change.
6916    #[inline(always)]
6917    fn b_skip_hot(&mut self, mb_x: usize, mb_y: usize) -> bool {
6918        if !self.direct_spatial || self.edc_tx.is_some() || no_bskipfast() {
6919            return false;
6920        }
6921        let mbw = self.mb_w;
6922        let addr = mb_y * mbw + mb_x;
6923        // ONE slice that ENDS at `addr` proves all three neighbour reads. Each
6924        // was a separate check against the whole `bzero` grid even though every
6925        // index is strictly below `addr` (the guards above establish that), and
6926        // the trailing `bzero[addr] = true` a fourth. `get(..=addr)` states the
6927        // bound once, in the form the neighbour indexes are already inside.
6928        // The `get(..=addr)` slice did NOT fold these: it bounds the reads from
6929        // above, but LLVM still has to chain `mb_x > 0 => addr >= 1` and
6930        // `mb_y > 0 => addr >= mbw` through the `&&` sequence, and it will not.
6931        // Fallible reads state each one where it is made; a missing neighbour is
6932        // "not zero", which is the conservative answer this guard wants.
6933        let up = addr.wrapping_sub(mbw);
6934        if !(mb_x > 0
6935            && mb_y > 0
6936            && mb_x + 1 < mbw
6937            && up >= self.slice_first_mb
6938            && self.bzero.get(addr - 1).copied().unwrap_or(false)
6939            && self.bzero.get(up).copied().unwrap_or(false)
6940            && self.bzero.get(up + 1).copied().unwrap_or(false))
6941        {
6942            return false;
6943        }
6944        let wgt = self.iw00();
6945        if !(wgt.is_none() || wgt == Some((32, 32))) {
6946            return false;
6947        }
6948        self.route_skip_mbs += 1;
6949        edcstat::bump(&edcstat::BSKB_FORCED, 1);
6950        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BSkipHot);
6951        edcstat::bump(&edcstat::BSKB_FAST, 1);
6952        self.bz_push(mb_x, mb_y, BzKind::ZeroBi);
6953        if let Some(b) = self.bzero.get_mut(addr) {
6954            *b = true;
6955        }
6956        true
6957    }
6958
6959    /// Zero this macroblock's 16 luma nnz entries, walking rows by adding `w4`
6960    /// instead of recomputing `(mb_y * 4 + dy) * w4 + mb_x * 4` four times.
6961    /// Shared by the three sites in decode_b_skip that had it open-coded.
6962    #[inline]
6963    fn clear_mb_nnz(&mut self, mb_x: usize, mb_y: usize, w4: usize) {
6964        let mut a = (mb_y * 4) * w4 + mb_x * 4;
6965        for _ in 0..4 {
6966            self.nnz_y[a..a + 4].fill(0);
6967            a += w4;
6968        }
6969    }
6970
6971    /// The B_Skip SLOW half: full spatial-direct recon plus the zero-residual
6972    /// plane copy. Split out so `pred_y`/`c_pred` (384 B) are built ONLY when a
6973    /// macroblock actually reaches it — the zero-bi / zero-uni / full-pel fast
6974    /// paths in decode_b_skip all return before this, and on LIGHT streams they
6975    /// take 90%+ of the calls.
6976    fn b_skip_slow(
6977        &mut self,
6978        mb_x: usize,
6979        mb_y: usize,
6980        nbrs: Option<([MvNeighbor; 3], [MvNeighbor; 3])>,
6981        w4: usize,
6982    ) -> Result<(), MbError> {
6983        let mut pred_y = [0u8; 256];
6984        let mut c_pred = [[0u8; 64]; 2];
6985        match nbrs {
6986            // Reuses the already-probed neighbours — the grid walk is the
6987            // expensive half of the derivation and paying it twice leaned on
6988            // fall-through-heavy streams (stockholm).
6989            Some((n0, n1)) => {
6990                self.decode_b_direct_n(mb_x, mb_y, 0, 0, 16, 16, &mut pred_y, &mut c_pred, n0, n1)
6991            }
6992            None => self.decode_b_direct(mb_x, mb_y, 0, 0, 16, 16, &mut pred_y, &mut c_pred),
6993        }
6994        if let Some(regions) = self.edc_regions.take() {
6995            // nnz clears are PARSE state; the pixel copy is the worker's.
6996            self.clear_mb_nnz(mb_x, mb_y, w4);
6997            self.edc_giveback();
6998            self.edc_send_job(EdcJob::BSkip {
6999                mbx: mb_x,
7000                mby: mb_y,
7001                regions,
7002            });
7003            return Ok(());
7004        }
7005        // Zero residual: the prediction IS the reconstruction — copy it row-wise,
7006        // stepping the destination by the stride rather than multiplying per row.
7007        let mut d = (mb_y * 16) * self.cw + mb_x * 16;
7008        for dy in 0..16 {
7009            self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
7010            d += self.cw;
7011        }
7012        let (ccw, cx0) = (self.ccw, mb_x * 8);
7013        for c in 0..2 {
7014            let plane = if c == 0 {
7015                &mut self.rec_u
7016            } else {
7017                &mut self.rec_v
7018            };
7019            let mut d = (mb_y * 8) * ccw + cx0;
7020            for dy in 0..8 {
7021                plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
7022                d += ccw;
7023            }
7024        }
7025        // nnz stays 0 (no residual) — clear the grids for neighbour context.
7026        self.clear_mb_nnz(mb_x, mb_y, w4);
7027        Ok(())
7028    }
7029
7030    fn decode_b_skip(&mut self, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
7031        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::BSkipCold);
7032        self.route_skip_mbs += 1;
7033        self.wait_refs_for_mb(mb_y);
7034        // Each list length read ONCE: the emptiness test and every later
7035        // `len() - 1` clamp (six reads in all) come from these two.
7036        let (nref0, nref1) = (self.refs.len(), self.refs1.len());
7037        if nref0 == 0 || nref1 == 0 {
7038            return Err(MbError::Unsupported("B without references"));
7039        }
7040        let (lref0, lref1) = (nref0 - 1, nref1 - 1);
7041        let mbw = self.mb_w;
7042        let addr = mb_y * mbw + mb_x;
7043        let w4 = mbw * 4;
7044        // ZERO-BI FAST PATH: when spatial direct derives (0,0)/(0,0) bi motion,
7045        // colZeroFlag is IRRELEVANT (it only zeroes MVs that are already zero),
7046        // so the colocated probing, region split, b_mc staging and the pred
7047        // round-trip all collapse into one fused row-average from the two
7048        // padded refs straight into the recon planes. b_mc's bi blend applies
7049        // only IMPLICIT weights, so `None` or (32,32) (both exactly
7050        // (a+b+1)>>1) is the whole identity condition. FourPeople-class LIGHT
7051        // streams put 90%+ of their B_Skips here (BSK_ZBI counter).
7052        // pred_y / c_pred now live in `b_skip_slow` — see there.
7053        if self.direct_spatial && self.edc_tx.is_none() && !no_bskipfast() {
7054            // (The FORCED zero-bi run continuation lives in b_skip_hot, inlined
7055            // at the loop call sites — this cold body only sees non-forced MBs.)
7056            // A pending span can only occupy the LEFT gather position, and the
7057            // left member's committed grid values are fully determined by the
7058            // span KIND — synthesize them instead of flushing, for EVERY kind
7059            // (the same mapping bz_flush writes).
7060            let patch = match self.bzspan {
7061                Some((r, x0, n, k)) if r == mb_y && x0 + n == mb_x => Some(k),
7062                _ => None,
7063            };
7064            if patch.is_none() {
7065                self.bz_flush();
7066            }
7067            let (mut n0, mut n1) = self.b_direct_nbrs(mb_x, mb_y);
7068            if let Some(k) = patch {
7069                let (r0, r1, m0, m1): (i32, i32, (i32, i32), (i32, i32)) = match k {
7070                    BzKind::ZeroBi => (0, 0, (0, 0), (0, 0)),
7071                    BzKind::ZeroUni(0, ri) => (ri as i32, -1, (0, 0), (0, 0)),
7072                    BzKind::ZeroUni(_, ri) => (-1, ri as i32, (0, 0), (0, 0)),
7073                    BzKind::Fp { r0, r1, m0, m1 } => {
7074                        let mm0 = if r0 >= 0 {
7075                            (m0.0 as i32, m0.1 as i32)
7076                        } else {
7077                            (0, 0)
7078                        };
7079                        let mm1 = if r1 >= 0 {
7080                            (m1.0 as i32, m1.1 as i32)
7081                        } else {
7082                            (0, 0)
7083                        };
7084                        (r0 as i32, r1 as i32, mm0, mm1)
7085                    }
7086                };
7087                n0[0] = MvNeighbor {
7088                    available: true,
7089                    mv: m0,
7090                    ref_idx: r0,
7091                };
7092                n1[0] = MvNeighbor {
7093                    available: true,
7094                    mv: m1,
7095                    ref_idx: r1,
7096                };
7097            }
7098            let (refi0, refi1, mv0, mv1, _dz) = Self::b_direct_refs_mvs(&n0, &n1);
7099            let (a0, a1) = (refi0 >= 0, refi1 >= 0);
7100            let fast = if a0 && a1 && mv0 == (0, 0) && mv1 == (0, 0) {
7101                // Same malformed-stream ref clamp as b_mc.
7102                let r0 = (refi0 as usize).min(lref0);
7103                let r1 = (refi1 as usize).min(lref1);
7104                // (0,0) dominates here and is already cached slice-wide by iw00.
7105                let wgt = if r0 == 0 && r1 == 0 {
7106                    self.iw00()
7107                } else {
7108                    self.implicit_weights(r0 as i32, r1 as i32)
7109                };
7110                if wgt.is_none() || wgt == Some((32, 32)) {
7111                    if refi0 == 0 && refi1 == 0 {
7112                        if let Some(b) = self.bzero.get_mut(addr) {
7113                            *b = true;
7114                        }
7115                        // Same constants as the forced arm: recon AND commit
7116                        // both defer into the span.
7117                        self.bz_push(mb_x, mb_y, BzKind::ZeroBi);
7118                        edcstat::bump(&edcstat::BSKB_FAST, 1);
7119                        return Ok(());
7120                    }
7121                    self.recon_b_skip_zero_bi(mb_x, mb_y, r0, r1);
7122                    true
7123                } else {
7124                    false
7125                }
7126            } else if a0 != a1 && (if a0 { mv0 } else { mv1 }) == (0, 0) {
7127                // ZERO-UNI: one active list at (0,0) — b_mc's uni arms are plain
7128                // unweighted copies. Defer as a memcpy-band span.
7129                let (list, ri) = if a0 {
7130                    (0u8, (refi0 as usize).min(lref0) as u8)
7131                } else {
7132                    (1u8, (refi1 as usize).min(lref1) as u8)
7133                };
7134                self.bz_push(mb_x, mb_y, BzKind::ZeroUni(list, ri));
7135                edcstat::bump(&edcstat::BSKB_FAST, 1);
7136                return Ok(());
7137            } else if (!a0 || (mv0.0 % 4 == 0 && mv0.1 % 4 == 0))
7138                && (!a1 || (mv1.0 % 4 == 0 && mv1.1 % 4 == 0))
7139                && self.direct_8x8_inference
7140            {
7141                // FULL-PEL arm (the pan case — shields): nonzero MVs make
7142                // colZeroFlag matter, so probe the four 8x8 corners; a UNIFORM
7143                // czg keeps the MB one region and the MC is an offset read.
7144                // Non-uniform czg or an out-of-pad window falls through.
7145                let mut cz_ok = true;
7146                let mut czv = false;
7147                // Same cz-relevance gate as decode_b_direct_n: only a ref-0
7148                // list with a nonzero MV can be changed by colZeroFlag (fp-arm
7149                // MVs are nonzero by construction, so refs alone decide).
7150                let cz_need = refi0 == 0 || refi1 == 0;
7151                // LOOP-INVARIANT: this gated a `break` INSIDE the probe loop, so
7152                // a fact known before the loop was re-tested on every probe.
7153                if cz_need {
7154                    // Absolute block base, hoisted out of the four probes.
7155                    let (bx4, by4) = (mb_x * 4, mb_y * 4);
7156                    for (k, &(ox, oy)) in [(0usize, 0usize), (2, 0), (0, 2), (2, 2)]
7157                        .iter()
7158                        .enumerate()
7159                    {
7160                        // col_block takes MB-LOCAL block coords (the whole MB is
7161                        // the region here); col_zero takes absolute — same
7162                        // convention as decode_b_direct_n's probe loop.
7163                        let (colx, coly) = self.col_block(ox, oy);
7164                        let cz = self.col_zero(bx4 + colx, by4 + coly);
7165                        if k == 0 {
7166                            czv = cz;
7167                        } else if cz != czv {
7168                            cz_ok = false;
7169                            break;
7170                        }
7171                    }
7172                }
7173                if cz_ok {
7174                    let m0 = if refi0 == 0 && czv { (0, 0) } else { mv0 };
7175                    let m1 = if refi1 == 0 && czv { (0, 0) } else { mv1 };
7176                    let r0c = (refi0 as usize).min(lref0);
7177                    let r1c = (refi1 as usize).min(lref1);
7178                    let (or0, or1) = (a0.then_some(r0c), a1.then_some(r1c));
7179                    let wgt_ok = !(a0 && a1) || {
7180                        let wgt = self.implicit_weights(r0c as i32, r1c as i32);
7181                        wgt.is_none() || wgt == Some((32, 32))
7182                    };
7183                    // SPAN path: prevalidated windows + %8 chroma ⇒ defer as
7184                    // an offset copy/avg band; otherwise the immediate per-MB
7185                    // recon below (which handles interpolating chroma).
7186                    if wgt_ok && self.bz_fp_valid(mb_x, mb_y, or0, or1, m0, m1) {
7187                        let kind = BzKind::Fp {
7188                            r0: if a0 { r0c as i8 } else { -1 },
7189                            r1: if a1 { r1c as i8 } else { -1 },
7190                            m0: (m0.0 as i16, m0.1 as i16),
7191                            m1: (m1.0 as i16, m1.1 as i16),
7192                        };
7193                        self.bz_push(mb_x, mb_y, kind);
7194                        edcstat::bump(&edcstat::BSKB_FP, 1);
7195                        return Ok(());
7196                    }
7197                    let ok = wgt_ok && self.recon_b_skip_fp(mb_x, mb_y, or0, or1, m0, m1);
7198                    if ok {
7199                        edcstat::bump(&edcstat::BSKB_FP, 1);
7200                        self.b_set_motion(mb_x, mb_y, 0, 0, 16, 16, refi0, m0, refi1, m1);
7201                        self.clear_mb_nnz(mb_x, mb_y, w4);
7202                        return Ok(());
7203                    }
7204                }
7205                false
7206            } else {
7207                false
7208            };
7209            if fast {
7210                edcstat::bump(&edcstat::BSKB_FAST, 1);
7211                self.b_set_motion(mb_x, mb_y, 0, 0, 16, 16, refi0, (0, 0), refi1, (0, 0));
7212                self.clear_mb_nnz(mb_x, mb_y, w4);
7213                return Ok(());
7214            }
7215            // Fall-through hands the ALREADY-probed neighbours to the slow
7216            // half so the grid walk is not paid twice.
7217            return self.b_skip_slow(mb_x, mb_y, Some((n0, n1)), w4);
7218        } else {
7219            if self.edc_tx.is_some() {
7220                self.edc_regions = Some(Vec::with_capacity(4));
7221            }
7222            return self.b_skip_slow(mb_x, mb_y, None, w4);
7223        }
7224    }
7225
7226    /// Reconstructs a B macroblock (spec Table 7-14): direct, L0/L1/Bi partitions,
7227    /// `B_8x8`, or intra.
7228    fn decode_b_mb(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
7229        // Non-skip B MBs gather neighbor motion/modes — flush any deferred
7230        // spans before those grids are read.
7231        self.span_flush();
7232        self.wait_refs_for_mb(mb_y);
7233        let mb_type = r.read_ue()?;
7234        if mb_type >= 23 {
7235            return self.decode_intra_mb(r, mb_x, mb_y, mb_type - 23);
7236        }
7237        if self.refs.is_empty() || self.refs1.is_empty() {
7238            return Err(MbError::Unsupported("B without references"));
7239        }
7240        let mut pred_y = [0u8; 256];
7241        let mut c_pred = [[0u8; 64]; 2];
7242
7243        if mb_type == 0 {
7244            // B_Direct_16x16 — 8×8 transform allowed only with direct_8x8_inference.
7245            // FORCED derivation via the zero-bi bitmap (CAVLC parity with the
7246            // CABAC direct arm): gather + rid/median skipped, chains extended.
7247            let addr_f = mb_y * self.mb_w + mb_x;
7248            let forced = self.direct_spatial
7249                && self.edc_tx.is_none()
7250                && mb_x > 0
7251                && mb_y > 0
7252                && mb_x + 1 < self.mb_w
7253                && addr_f - self.mb_w >= self.slice_first_mb
7254                && self.bzero.get(addr_f - 1).copied().unwrap_or(false)
7255                && self
7256                    .bzero
7257                    .get(addr_f.wrapping_sub(self.mb_w))
7258                    .copied()
7259                    .unwrap_or(false)
7260                && self
7261                    .bzero
7262                    .get(addr_f.wrapping_sub(self.mb_w) + 1)
7263                    .copied()
7264                    .unwrap_or(false);
7265            if forced {
7266                self.b_direct_region(
7267                    mb_x,
7268                    mb_y,
7269                    0,
7270                    0,
7271                    16,
7272                    16,
7273                    &mut pred_y,
7274                    &mut c_pred,
7275                    (0, 0, (0, 0), (0, 0), false),
7276                );
7277                if let Some(b) = self.bzero.get_mut(addr_f) {
7278                    *b = true;
7279                }
7280            } else {
7281                self.decode_b_direct(mb_x, mb_y, 0, 0, 16, 16, &mut pred_y, &mut c_pred);
7282            }
7283            return self.inter_finish(
7284                r,
7285                mb_x,
7286                mb_y,
7287                &pred_y,
7288                &c_pred,
7289                self.direct_8x8_inference,
7290                false,
7291            );
7292        }
7293        if mb_type == 22 {
7294            return self.decode_b_8x8(r, mb_x, mb_y);
7295        }
7296
7297        // 16x16 / 16x8 / 8x16 partitions with per-partition L0/L1/Bi.
7298        let (layout, mvmode, preds) = b_inter_layout(mb_type);
7299        // mb_pred order: ref_idx_l0 (all L0 parts), ref_idx_l1, mvd_l0, mvd_l1.
7300        let mut refi = [[-1i32; 2]; 2]; // [part][list]
7301        for (p, &(_, _, _, _)) in layout.iter().enumerate() {
7302            if preds[p & 1].uses(0) {
7303                refi[p][0] = self.read_b_ref(r, 0)?;
7304            }
7305        }
7306        for (p, _) in layout.iter().enumerate() {
7307            if preds[p & 1].uses(1) {
7308                refi[p][1] = self.read_b_ref(r, 1)?;
7309            }
7310        }
7311        let mut mvd = [[(0i32, 0i32); 2]; 2];
7312        for (p, _) in layout.iter().enumerate() {
7313            if preds[p & 1].uses(0) {
7314                mvd[p][0] = (read_mvd(r)?, read_mvd(r)?);
7315            }
7316        }
7317        for (p, _) in layout.iter().enumerate() {
7318            if preds[p & 1].uses(1) {
7319                mvd[p][1] = (read_mvd(r)?, read_mvd(r)?);
7320            }
7321        }
7322        // Per partition: predict + commit each list's MV, then motion-compensate.
7323        for (p, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
7324            let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
7325            let pwb = (rw / 4) as isize;
7326            let mut mv = [(0i32, 0i32); 2];
7327            for list in 0..2 {
7328                if refi[p][list] >= 0 {
7329                    let n = self.mv_neighbors_list(pbx, pby, pwb, list);
7330                    let pmv = predict_partition_mv(mvmode, p, n[0], n[1], n[2], refi[p][list]);
7331                    mv[list] = (pmv.0 + mvd[p][list].0, pmv.1 + mvd[p][list].1);
7332                }
7333            }
7334            self.b_set_motion(
7335                mb_x,
7336                mb_y,
7337                rx,
7338                ry,
7339                rw,
7340                rh,
7341                refi[p & 3][0],
7342                mv[0],
7343                refi[p & 3][1],
7344                mv[1],
7345            );
7346            // Spec-correct bi-prediction (average of L0 and L1), matching the CABAC
7347            // path. This used to replicate an openh264 bug for a Bi 16x8/8x16
7348            // partition -- openh264 mis-handles the destination buffer there, so
7349            // partition 0 came out List-1-only and partition 1 List-0-only. That was
7350            // deliberate when openh264's h264dec WAS the conformance oracle, but the
7351            // gate is ffmpeg now and the CABAC path already went spec-correct; the
7352            // CAVLC path was simply left behind. Measured: mb_type 12..21 (every B
7353            // 16x8/8x16 with at least one Bi partition) were 100% wrong vs ffmpeg,
7354            // while 1..11 (no Bi partition) were only collaterally damaged.
7355            self.b_mc_or_record(
7356                mb_x,
7357                mb_y,
7358                rx,
7359                ry,
7360                rw,
7361                rh,
7362                refi[p & 3][0],
7363                mv[0],
7364                refi[p & 3][1],
7365                mv[1],
7366                &mut pred_y,
7367                &mut c_pred,
7368            );
7369        }
7370        self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, true, false)
7371    }
7372
7373    /// Reconstructs a `B_8x8` macroblock: four 8×8 sub-macroblock partitions, each
7374    /// direct or L0/L1/Bi with its own sub-partitioning (spec Table 7-18).
7375    fn decode_b_8x8(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
7376        let mut sub = [0u32; 4];
7377        for s in sub.iter_mut() {
7378            let v = r.read_ue()?;
7379            if v > 12 {
7380                return Err(MbError::Unsupported("invalid B sub_mb_type"));
7381            }
7382            *s = v;
7383        }
7384        let mut pred_y = [0u8; 256];
7385        let mut c_pred = [[0u8; 64]; 2];
7386        // ref_idx for all 8×8 partitions (L0 batch, then L1 batch), for the
7387        // non-direct sub-partitions.
7388        let mut refi = [[-1i32; 2]; 4];
7389        for (p, &st) in sub.iter().enumerate() {
7390            if st != 0 && b_sub_uses(st, 0) {
7391                refi[p][0] = self.read_b_ref(r, 0)?;
7392            }
7393        }
7394        for (p, &st) in sub.iter().enumerate() {
7395            if st != 0 && b_sub_uses(st, 1) {
7396                refi[p][1] = self.read_b_ref(r, 1)?;
7397            }
7398        }
7399        // mvd: all mvd_l0 (partition-major, sub-partition order), then all mvd_l1.
7400        //
7401        // FIXED ARRAYS, NOT `Vec::new()` + push. These are per-MACROBLOCK on every
7402        // B_8x8, and a growing Vec allocated (and reallocated) twice per MB — on a
7403        // B-heavy stream that is thousands of allocations per frame for data whose
7404        // maximum size is a compile-time constant: 4 partitions x at most 4
7405        // sub-partitions = 16 entries. Indexing a fixed array cannot exceed that, and
7406        // an out-of-range index would panic rather than misbehave, so the bound is
7407        // enforced either way and this crate stays forbid(unsafe).
7408        const MAX_MVD: usize = 16;
7409        let mut mvd0 = [(0i32, 0i32); MAX_MVD];
7410        let mut mvd1 = [(0i32, 0i32); MAX_MVD];
7411        let (mut n0, mut n1) = (0usize, 0usize);
7412        for &st in &sub {
7413            if st != 0 && b_sub_uses(st, 0) {
7414                for _ in b_sub_parts(st) {
7415                    mvd0[n0] = (read_mvd(r)?, read_mvd(r)?);
7416                    n0 += 1;
7417                }
7418            }
7419        }
7420        for &st in &sub {
7421            if st != 0 && b_sub_uses(st, 1) {
7422                for _ in b_sub_parts(st) {
7423                    mvd1[n1] = (read_mvd(r)?, read_mvd(r)?);
7424                    n1 += 1;
7425                }
7426            }
7427        }
7428        // Decode each 8×8 partition.
7429        // Spatial-direct A/B/C are MB-level — walk once if any sub is direct.
7430        // `dmemo=0` rewalks every direct 8×8 (A/B oracle).
7431        let hoisted = if self.direct_spatial && direct_memo_on() && sub.iter().any(|&t| t == 0) {
7432            Some(self.b_direct_nbrs(mb_x, mb_y))
7433        } else {
7434            None
7435        };
7436        let (mut i0, mut i1) = (0usize, 0usize);
7437        for (p, &st) in sub.iter().enumerate() {
7438            let (b8x, b8y) = ((p % 2) * 8, (p / 2) * 8);
7439            if st == 0 {
7440                match hoisted {
7441                    Some((n0, n1)) => self.decode_b_direct_n(
7442                        mb_x,
7443                        mb_y,
7444                        b8x,
7445                        b8y,
7446                        8,
7447                        8,
7448                        &mut pred_y,
7449                        &mut c_pred,
7450                        n0,
7451                        n1,
7452                    ),
7453                    None => {
7454                        self.decode_b_direct(mb_x, mb_y, b8x, b8y, 8, 8, &mut pred_y, &mut c_pred)
7455                    }
7456                }
7457                continue;
7458            }
7459            for &(sx, sy, sw, sh) in b_sub_parts(st) {
7460                let (px, py) = (b8x + sx, b8y + sy);
7461                let (pbx, pby) = ((mb_x * 4 + px / 4) as isize, (mb_y * 4 + py / 4) as isize);
7462                let pwb = (sw / 4) as isize;
7463                let mut mv = [(0i32, 0i32); 2];
7464                if b_sub_uses(st, 0) {
7465                    let n = self.mv_neighbors_list(pbx, pby, pwb, 0);
7466                    let pmv = predict_mv(n[0], n[1], n[2], refi[p & 3][0]);
7467                    let d = mvd0[i0 & 15];
7468                    i0 += 1;
7469                    mv[0] = (pmv.0 + d.0, pmv.1 + d.1);
7470                }
7471                if b_sub_uses(st, 1) {
7472                    let n = self.mv_neighbors_list(pbx, pby, pwb, 1);
7473                    let pmv = predict_mv(n[0], n[1], n[2], refi[p & 3][1]);
7474                    let d = mvd1[i1 & 15];
7475                    i1 += 1;
7476                    mv[1] = (pmv.0 + d.0, pmv.1 + d.1);
7477                }
7478                self.b_set_motion(
7479                    mb_x,
7480                    mb_y,
7481                    px,
7482                    py,
7483                    sw,
7484                    sh,
7485                    refi[p & 3][0],
7486                    mv[0],
7487                    refi[p & 3][1],
7488                    mv[1],
7489                );
7490                self.b_mc_or_record(
7491                    mb_x,
7492                    mb_y,
7493                    px,
7494                    py,
7495                    sw,
7496                    sh,
7497                    refi[p & 3][0],
7498                    mv[0],
7499                    refi[p & 3][1],
7500                    mv[1],
7501                    &mut pred_y,
7502                    &mut c_pred,
7503                );
7504            }
7505        }
7506        // noSubMbPartSizeLessThan8x8: each sub-partition must be ≥ 8×8 (direct
7507        // counts only with the 8×8 inference flag).
7508        let allow_8x8 = sub.iter().all(|&st| {
7509            if st == 0 {
7510                self.direct_8x8_inference
7511            } else {
7512                st <= 3
7513            }
7514        });
7515        self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, allow_8x8, false)
7516    }
7517
7518    /// Reconstructs a `P_8x8` macroblock: four 8×8 sub-macroblock partitions,
7519    /// each independently split (8×8 / 8×4 / 4×8 / 4×4) with its own motion
7520    /// vector(s). `ref0` is `P_8x8ref0` (every `ref_idx` forced to 0, not coded).
7521    fn decode_p8x8(
7522        &mut self,
7523        r: &mut BitReader,
7524        mb_x: usize,
7525        mb_y: usize,
7526        ref0: bool,
7527    ) -> Result<(), MbError> {
7528        if self.refs.is_empty() {
7529            return Err(MbError::Unsupported("inter without reference"));
7530        }
7531        let w4 = self.mb_w * 4;
7532        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
7533        let num_refs = self.refs.len();
7534
7535        // mb_pred order (spec §7.3.5.2): all sub_mb_type, then all ref_idx_l0,
7536        // then all mvd_l0 (partition-major, sub-partition order within each).
7537        let mut sub_types = [0u32; 4];
7538        for st in sub_types.iter_mut() {
7539            let v = r.read_ue()?;
7540            if v > 3 {
7541                return Err(MbError::Unsupported("B-slice / invalid sub_mb_type"));
7542            }
7543            *st = v;
7544        }
7545        let mut ref_idxs = [0i32; 4];
7546        if self.num_ref_active > 1 && !ref0 {
7547            for ri in ref_idxs.iter_mut() {
7548                *ri = read_ref_idx(r, self.num_ref_active)?;
7549                if *ri as usize >= num_refs {
7550                    return Err(MbError::Truncated); // references a non-existent picture
7551                }
7552            }
7553        }
7554
7555        // Per sub-partition (in decoding order): median MV prediction from the
7556        // committed neighbor grid, mvd, commit, then motion-compensate. Committing
7557        // before the next prediction is what lets sub-partitions chain correctly.
7558        // D14b: P_8x8 defers too. Syncing before it instead cost 9,772 pipeline
7559        // drains per stream (45 per 1000 MBs, vs CABAC's 3.3) because P_8x8 is
7560        // common in CAVLC P slices — and the seam measured 1.65-1.97x SLOWER
7561        // for it. Deferring is byte-identical for sub-partitions: the worker
7562        // motion-compensates per 4x4 from the committed grids, which is exactly
7563        // how the CABAC path already handles P_8x8, and a 6-tap filter applied
7564        // per-4x4 with the same MV gives the same pixels as one 8x8 call.
7565        let defer = self.edc_tx.is_some() || self.edc_active;
7566
7567        // ── PHASE 1: PARSE + COMMIT. Must always run to completion. ──────────
7568        //
7569        // These two are interleaved BY NECESSITY: each sub-partition's MV
7570        // prediction reads the grids the previous one committed, so they cannot
7571        // be separated from each other. But they consume BITSTREAM, so nothing
7572        // here may ever be skipped conditionally.
7573        //
7574        // This phase split is a STRUCTURAL guard, not a tidy-up. When MC lived
7575        // inside this loop, deferring it was written as a `break` — which also
7576        // skipped the `read_se` mvd reads, desynced the bitstream, and
7577        // mis-parsed as a B-slice sub_mb_type on a Baseline stream. It happened
7578        // to crash; a desync that stayed IN RANGE would have produced plausible
7579        // garbage instead. With MC in its own pass below, skipping pixel work
7580        // cannot reach the bitstream at all — the mistake is unavailable.
7581        let mut regions: [(usize, usize, usize, usize, i32, (i32, i32)); 16] =
7582            [(0, 0, 0, 0, 0, (0, 0)); 16];
7583        let mut nreg = 0usize;
7584        for part in 0..4usize {
7585            let refi = ref_idxs[part];
7586            let (b8x, b8y) = ((part % 2) * 8, (part / 2) * 8);
7587            for &(srx, sry, srw, srh) in sub_mb_partitions(sub_types[part]) {
7588                let (px, py) = (b8x + srx, b8y + sry);
7589                let (pbx, pby) = ((mb_x * 4 + px / 4) as isize, (mb_y * 4 + py / 4) as isize);
7590                let [a, b, c] = self.mv_neighbors_block(pbx, pby, (srw / 4) as isize);
7591                let pmv = predict_mv(a, b, c, refi);
7592                let mvd_x = read_mvd(r)?;
7593                let mvd_y = read_mvd(r)?;
7594                let mv = (pmv.0 + mvd_x, pmv.1 + mvd_y);
7595                for by in py / 4..py / 4 + srh / 4 {
7596                    for bx in px / 4..px / 4 + srw / 4 {
7597                        let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
7598                        if let (Some(m), Some(it), Some(rf), Some(cd)) = (
7599                            self.mv_y.get_mut(idx),
7600                            self.inter_y.get_mut(idx),
7601                            self.ref_idx_y.get_mut(idx),
7602                            self.coded_y.get_mut(idx),
7603                        ) {
7604                            (*m, *it, *rf, *cd) = (mv, true, refi, true);
7605                        }
7606                    }
7607                }
7608                regions[nreg & 15] = (px, py, srw, srh, refi, mv);
7609                nreg += 1;
7610            }
7611        }
7612
7613        // ── PHASE 2: PIXEL WORK ONLY. Reads no bitstream; safe to skip. ──────
7614        let mut pred_y = [0u8; 256];
7615        let mut c_pred = [[0u8; 64]; 2];
7616        if !defer {
7617            for &(px, py, srw, srh, refi, mv) in &regions[..nreg] {
7618                let Some(reference) = self.refs.get(refi as usize) else {
7619                    continue;
7620                };
7621                let mut tmp = [0u8; 256];
7622                mc_luma_padded(
7623                    &*reference.luma_guard(reference.ch),
7624                    reference.lstride(),
7625                    crate::LPAD,
7626                    self.cw,
7627                    ch,
7628                    mb_x * 16 + px,
7629                    mb_y * 16 + py,
7630                    srw,
7631                    srh,
7632                    mv.0,
7633                    mv.1,
7634                    &mut tmp,
7635                );
7636                restride(&mut pred_y, 16, px, py, &tmp, srw, srh);
7637                let (crx, cry, crw, crh) = (px / 2, py / 2, srw / 2, srh / 2);
7638                for cc in 0..2 {
7639                    let rc = if cc == 0 {
7640                        &*reference.chroma_guard(0, reference.ch)
7641                    } else {
7642                        &*reference.chroma_guard(1, reference.ch)
7643                    };
7644                    let mut tc = [0u8; 64];
7645                    mc_chroma_padded(
7646                        rc,
7647                        reference.cstride(),
7648                        crate::CPAD,
7649                        self.ccw,
7650                        cch,
7651                        mb_x * 8 + crx,
7652                        mb_y * 8 + cry,
7653                        crw,
7654                        crh,
7655                        mv.0,
7656                        mv.1,
7657                        &mut tc,
7658                    );
7659                    restride(&mut c_pred[cc], 8, crx, cry, &tc, crw, crh);
7660                }
7661                self.weight_partition(&mut pred_y, &mut c_pred, 0, refi as usize, px, py, srw, srh);
7662            }
7663        }
7664
7665        // P_8x8 allows the 8×8 transform only when every sub-partition is 8×8.
7666        let allow_8x8 = sub_types.iter().all(|&t| t == 0);
7667        self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, allow_8x8, defer)
7668    }
7669
7670    /// Reconstructs a `P_Skip` macroblock: motion-compensate from the reference
7671    /// at the skip MV, with no residual.
7672    /// Records a B MC region (threaded mode) or executes it inline — the SAME
7673    /// arguments as `b_mc`; the recorded arm resolves the implicit weights at
7674    /// parse time (identical function, parse-side data).
7675    #[allow(clippy::too_many_arguments)]
7676    fn b_mc_or_record(
7677        &mut self,
7678        mb_x: usize,
7679        mb_y: usize,
7680        px: usize,
7681        py: usize,
7682        rw: usize,
7683        rh: usize,
7684        refi0: i32,
7685        mv0: (i32, i32),
7686        refi1: i32,
7687        mv1: (i32, i32),
7688        pred_y: &mut [u8; 256],
7689        c_pred: &mut [[u8; 64]; 2],
7690    ) {
7691        if self.edc_regions.is_some() {
7692            // Mirror `b_mc`'s malformed-stream armor EXACTLY before touching the
7693            // ref lists: the inline path clamps the indices and bails on empty
7694            // lists BEFORE computing weights; calling `implicit_weights` with the
7695            // raw indices re-introduced the panic the armor exists to prevent
7696            // (found by the fuzzer, via the unwind-guard that turned the
7697            // resulting worker deadlock back into a diagnosable failure).
7698            let cr0 = if refi0 >= 0 {
7699                (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32
7700            } else {
7701                -1
7702            };
7703            let cr1 = if refi1 >= 0 {
7704                (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32
7705            } else {
7706                -1
7707            };
7708            let w = if (cr0 >= 0 && self.refs.is_empty()) || (cr1 >= 0 && self.refs1.is_empty()) {
7709                None // the worker's port returns before reading the weights
7710            } else {
7711                self.implicit_weights(cr0, cr1)
7712            };
7713            // Cannot bind above: `implicit_weights` needs `&mut self` between the
7714            // guard and here. The guard already proved this is `Some`, so the
7715            // `else` is unreachable -- but an unreachable no-op is the correct
7716            // shape for armor code, not an `unwrap` that can abort a decode.
7717            if let Some(regions) = self.edc_regions.as_mut() {
7718                regions.push(BRegion {
7719                    px,
7720                    py,
7721                    rw,
7722                    rh,
7723                    refi0,
7724                    refi1,
7725                    mv0,
7726                    mv1,
7727                    w,
7728                });
7729            }
7730            return;
7731        }
7732        self.b_mc(
7733            mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, pred_y, c_pred,
7734        );
7735    }
7736
7737    /// Builds the worker's owned pixel context from `self` (planes MOVED out,
7738    /// shared read-only state cloned, filter inputs snapshotted).
7739    fn edc_take_ctx(&mut self) -> PixelCtx {
7740        PixelCtx {
7741            rec_y: core::mem::take(&mut self.rec_y),
7742            rec_u: core::mem::take(&mut self.rec_u),
7743            rec_v: core::mem::take(&mut self.rec_v),
7744            bak_y: core::mem::take(&mut self.bak_y),
7745            bak_u: core::mem::take(&mut self.bak_u),
7746            bak_v: core::mem::take(&mut self.bak_v),
7747            refs: self.refs.clone(),
7748            refs1: self.refs1.clone(),
7749            weights: self.weights.clone(),
7750            weights_l0id: self.weights_l0id,
7751            scaling: self.scaling,
7752            scaling8: self.scaling8,
7753            cw: self.cw,
7754            ccw: self.ccw,
7755            mb_w: self.mb_w,
7756            mb_h: self.mb_h,
7757            chroma_qp_offset: self.chroma_qp_offset,
7758            flt_rows: self.flt_rows,
7759            db_ena: self.db_ena,
7760            db_oa: self.db_oa,
7761            db_ob: self.db_ob,
7762            cur_qp: self.cur_qp,
7763            qp_grid: self.mb_qp.clone(),
7764            t8_grid: self.mb_t8x8.clone(),
7765            bs_store: self.bs_frame.clone(),
7766            progress: self.progress.clone(),
7767        }
7768    }
7769
7770    /// Restores the planes (and the filter watermark) from a returned context.
7771    fn edc_restore_ctx(&mut self, ctx: PixelCtx) {
7772        self.rec_y = ctx.rec_y;
7773        self.rec_u = ctx.rec_u;
7774        self.rec_v = ctx.rec_v;
7775        self.bak_y = ctx.bak_y;
7776        self.bak_u = ctx.bak_u;
7777        self.bak_v = ctx.bak_v;
7778        self.flt_rows = ctx.flt_rows;
7779    }
7780
7781    /// Intra macroblocks read neighbour PIXELS: fetch the context from the
7782    /// worker (which drains all prior jobs first — the channel is FIFO) and
7783    /// install the planes so the inline intra path runs unchanged. The context
7784    /// is given back lazily at the next job/row/slice-end (`edc_giveback`), so
7785    /// consecutive intra macroblocks pay ONE round-trip.
7786    /// Queue a pixel job for the worker (D10). Batched per row; see `EdcMsg::Batch`.
7787    #[inline]
7788    fn edc_send_job(&mut self, job: EdcJob) {
7789        edcstat::bump(&edcstat::JOBS, 1);
7790        if !batch_on() {
7791            self.edc_tx
7792                .as_ref()
7793                .unwrap()
7794                .send(EdcMsg::Job(job))
7795                .expect("worker alive");
7796            return;
7797        }
7798        self.edc_batch.push(job);
7799    }
7800
7801    /// Ship the accumulated row batch. MUST be called before anything that
7802    /// depends on those jobs having been applied: the row's `Row` filter
7803    /// message, a `NeedCtx` handover, and slice end.
7804    fn edc_flush_batch(&mut self) {
7805        if self.edc_batch.is_empty() {
7806            return;
7807        }
7808        // Replace with a PRE-RESERVED buffer rather than the empty Vec
7809        // `mem::take` would leave: otherwise each row reallocs and regrows from
7810        // zero, trading 208k channel sends for ~7 reallocs x 3k rows.
7811        let cap = self.edc_batch.capacity().max(self.mb_w);
7812        let jobs = core::mem::replace(&mut self.edc_batch, Vec::with_capacity(cap));
7813        edcstat::bump(&edcstat::BATCHES, 1);
7814        self.edc_tx
7815            .as_ref()
7816            .unwrap()
7817            .send(EdcMsg::Batch(jobs))
7818            .expect("worker alive");
7819    }
7820
7821    fn edc_intra_sync(&mut self) {
7822        if self.edc_tx.is_none() {
7823            self.edc_flush();
7824            return;
7825        }
7826        if self.edc_parked.is_some() {
7827            return; // already holding
7828        }
7829        // ORDER: drain the batch before taking the planes, or those jobs would
7830        // be applied to a context the parse thread is concurrently holding.
7831        self.edc_flush_batch();
7832        edcstat::bump(&edcstat::NEEDCTX, 1);
7833        self.edc_tx
7834            .as_ref()
7835            .unwrap()
7836            .send(EdcMsg::NeedCtx)
7837            .expect("worker alive");
7838        let mut ctx = self
7839            .edc_ctx_rx
7840            .as_ref()
7841            .unwrap()
7842            .recv()
7843            .expect("worker ctx");
7844        self.rec_y = core::mem::take(&mut ctx.rec_y);
7845        self.rec_u = core::mem::take(&mut ctx.rec_u);
7846        self.rec_v = core::mem::take(&mut ctx.rec_v);
7847        self.bak_y = core::mem::take(&mut ctx.bak_y);
7848        self.bak_u = core::mem::take(&mut ctx.bak_u);
7849        self.bak_v = core::mem::take(&mut ctx.bak_v);
7850        self.flt_rows = ctx.flt_rows;
7851        self.edc_parked = Some(ctx);
7852    }
7853
7854    /// Returns a held context to the worker (inverse of `edc_intra_sync`).
7855    /// NOTE (D10): this is called per-macroblock on the job paths, so it must
7856    /// NOT flush the batch — that would undo the batching. It is safe: the
7857    /// parked state is only ever entered through `edc_intra_sync`, which
7858    /// flushes before taking the planes, so nothing can be queued-but-unsent
7859    /// while the parse thread holds them.
7860    fn edc_giveback(&mut self) {
7861        if let Some(mut parked) = self.edc_parked.take() {
7862            parked.rec_y = core::mem::take(&mut self.rec_y);
7863            parked.rec_u = core::mem::take(&mut self.rec_u);
7864            parked.rec_v = core::mem::take(&mut self.rec_v);
7865            parked.bak_y = core::mem::take(&mut self.bak_y);
7866            parked.bak_u = core::mem::take(&mut self.bak_u);
7867            parked.bak_v = core::mem::take(&mut self.bak_v);
7868            parked.flt_rows = self.flt_rows;
7869            // Fail-soft: on the unwind path the worker may already be gone;
7870            // dropping the parked context is acceptable there (the planes are
7871            // lost, but the panic is being propagated anyway).
7872            let _ = self.edc_back_tx.as_ref().unwrap().send(parked);
7873        }
7874    }
7875
7876    /// Parse-side twin of the nnz/coded grid writes `add_inter_residual` does
7877    /// inline — the worker's port omits them (they are PARSE state: deblock
7878    /// derivation and the CAVLC nC contexts read them), so the threaded path
7879    /// commits them here, from the same parsed counts (their equality with the
7880    /// recon-side recount is the Part 8 nnz-threading brick's own invariant).
7881    fn edc_commit_nnz(
7882        &mut self,
7883        mbx: usize,
7884        mby: usize,
7885        t8: bool,
7886        nnzs: &[u8; 24],
7887        cbp_chroma: u32,
7888    ) {
7889        // BUILD THE RASTER LOCALLY, THEN COPY ROW-WISE. Both luma arms scattered
7890        // sixteen individually bounds-checked stores into the frame grid (the t8
7891        // arm through a 2x2-of-2x2 nest, the other through the z-order scan
7892        // table); chroma scattered eight more. Assembling the macroblock's own
7893        // 4x4 raster on the stack first turns that into four contiguous row
7894        // copies per plane.
7895        let (w4r, w2r) = (self.mb_w * 4, self.mb_w * 2);
7896        let mut raster = [0u8; 16];
7897        if t8 {
7898            for b8 in 0..4usize {
7899                let (b8x, b8y) = (b8 % 2, b8 / 2);
7900                let n = nnzs[b8 * 4];
7901                for sy in 0..2 {
7902                    for sx in 0..2 {
7903                        raster[(b8y * 2 + sy) * 4 + b8x * 2 + sx] = n;
7904                    }
7905                }
7906            }
7907        } else {
7908            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
7909                raster[lby * 4 + lbx] = nnzs[blk];
7910            }
7911        }
7912        for by in 0..4usize {
7913            let a = (mby * 4 + by) * w4r + mbx * 4;
7914            self.nnz_y[a..a + 4].copy_from_slice(&raster[by * 4..by * 4 + 4]);
7915        }
7916        if cbp_chroma == 2 {
7917            for c in 0..2usize {
7918                let mut cr = [0u8; 4];
7919                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
7920                    cr[by * 2 + bx] = nnzs[(16 + c * 4 + by * 2 + bx).min(23)];
7921                }
7922                for by in 0..2usize {
7923                    let a = (mby * 2 + by) * w2r + mbx * 2;
7924                    self.nnz_c[c][a..a + 2].copy_from_slice(&cr[by * 2..by * 2 + 2]);
7925                }
7926            }
7927        }
7928    }
7929
7930    /// Flush the entropy-decouple job queue: replay every deferred pixel job
7931    /// in parse order. Called before any intra macroblock (its reconstruction
7932    /// reads neighbour PIXELS), before row filtering, at B-branch entry, at
7933    /// slice end, and at `deblock()` as a backstop.
7934    /// Per-macroblock guard: the B arm calls this once per B MB — the empty
7935    /// test inlines, the drain body stays outlined.
7936    /// A job box from the pool (or a fresh one when the pool is empty -- the
7937    /// first row of a picture, or MT mode where boxes cross to the worker).
7938    #[inline]
7939    fn take_pinter_job(&mut self) -> Box<PInterJob> {
7940        self.edc_job_pool
7941            .pop()
7942            .unwrap_or_else(|| Box::new(PInterJob::blank()))
7943    }
7944
7945    #[inline]
7946    fn take_nores_job(&mut self, pj: PInterNoResJob) -> Box<PInterNoResJob> {
7947        match self.edc_nores_pool.pop() {
7948            Some(mut b) => {
7949                *b = pj;
7950                b
7951            }
7952            None => Box::new(pj),
7953        }
7954    }
7955
7956    #[inline(always)]
7957    fn edc_flush(&mut self) {
7958        if self.edc_jobs.is_empty() {
7959            return;
7960        }
7961        self.edc_flush_slow();
7962    }
7963
7964    fn edc_flush_slow(&mut self) {
7965        let mut jobs = core::mem::take(&mut self.edc_jobs);
7966        // Band identity holds unweighted OR under identity weights (the x264
7967        // weightp=2 common case: a table in every P slice, identity outside fades).
7968        let band_ok = self.weights.is_none() || self.weights_id0;
7969        // Measurement: nonzero-MV skips sitting in same-row runs of >=2 EQUAL
7970        // MVs (the next band candidate). counted_until stops double-counting.
7971        let mut counted_until = 0usize;
7972        let mut i = 0usize;
7973        while i < jobs.len() {
7974            let j = &jobs[i];
7975            match j {
7976                EdcJob::Skip { mbx, mby, mv } => {
7977                    // mb_skip_run band coalescing: gather the maximal run of
7978                    // consecutive same-row (0,0)-skips and recon it as ONE
7979                    // band copy (weighted-P falls back — the copy identity
7980                    // only holds unweighted).
7981                    if *mv == (0, 0) && band_ok && !no_skipband() {
7982                        let (x0, y0) = (*mbx, *mby);
7983                        let mut n = 1usize;
7984                        while let Some(EdcJob::Skip {
7985                            mbx: nx,
7986                            mby: ny,
7987                            mv: nmv,
7988                        }) = jobs.get(i + n)
7989                        {
7990                            if *ny == y0 && *nx == x0 + n && *nmv == (0, 0) {
7991                                n += 1;
7992                            } else {
7993                                break;
7994                            }
7995                        }
7996                        self.recon_p_skip_band(x0, y0, n);
7997                        if double_recon() {
7998                            edcstat::bump(&edcstat::DOUBLED, n as u64);
7999                            self.recon_p_skip_band(x0, y0, n);
8000                        }
8001                        i += n;
8002                        continue;
8003                    }
8004                    edcstat::bump(&edcstat::SKIP_SINGLES, 1);
8005                    if *mv != (0, 0) && i >= counted_until && edcstat::on() {
8006                        let (x0, y0) = (*mbx, *mby);
8007                        let mut n2 = 1usize;
8008                        while let Some(EdcJob::Skip {
8009                            mbx: nx,
8010                            mby: ny,
8011                            mv: nmv,
8012                        }) = jobs.get(i + n2)
8013                        {
8014                            if *ny == y0 && *nx == x0 + n2 && nmv == mv {
8015                                n2 += 1;
8016                            } else {
8017                                break;
8018                            }
8019                        }
8020                        if n2 >= 2 {
8021                            let fp = mv.0 % 4 == 0 && mv.1 % 4 == 0;
8022                            edcstat::bump(
8023                                if fp {
8024                                    &edcstat::PEQ_FP
8025                                } else {
8026                                    &edcstat::PEQ_FRAC
8027                                },
8028                                n2 as u64,
8029                            );
8030                        }
8031                        counted_until = i + n2;
8032                    }
8033                    self.recon_p_skip(*mbx, *mby, *mv);
8034                    if double_recon() {
8035                        edcstat::bump(&edcstat::DOUBLED, 1);
8036                        self.recon_p_skip(*mbx, *mby, *mv);
8037                    }
8038                }
8039                EdcJob::Inter(job) => {
8040                    self.recon_p_inter(job);
8041                    if double_recon() {
8042                        edcstat::bump(&edcstat::DOUBLED, 1);
8043                        self.recon_p_inter(job);
8044                    }
8045                }
8046                EdcJob::InterNoRes(job) => {
8047                    // D9b: do NOT to_full() — that memset ~2.5 KB of zeros per MB
8048                    // then walked add_inter_residual's all-zero path. MC + plane copy.
8049                    self.recon_p_inter_nores(job);
8050                    if double_recon() {
8051                        edcstat::bump(&edcstat::DOUBLED, 1);
8052                        self.recon_p_inter_nores(job);
8053                    }
8054                }
8055                // B jobs exist only in worker (MT) mode and are never queued
8056                // here — the single-thread seam keeps B inline. Not reachable
8057                // from any input (the push sites are gated on `edc_tx`).
8058                EdcJob::B(_) | EdcJob::BSkip { .. } => unreachable!("B jobs are worker-only"),
8059            }
8060            i += 1;
8061        }
8062        // RECYCLE the job boxes (was: drop = one `free` per coded inter MB).
8063        for j in jobs.drain(..) {
8064            match j {
8065                EdcJob::Inter(b) => self.edc_job_pool.push(b),
8066                EdcJob::InterNoRes(b) => self.edc_nores_pool.push(b),
8067                _ => {}
8068            }
8069        }
8070        // Hand the (now empty) Vec back so its allocation is reused.
8071        self.edc_jobs = jobs;
8072    }
8073
8074    /// Reconstructs one CABAC P inter macroblock from its parse job — the
8075    /// pixel half of the entropy-decouple seam (docs/entropy-decouple-plan.md
8076    /// E1). Reads NOTHING from parse state except the frame grids this MB's
8077    /// parse already committed (its own block MVs/refs, re-gathered below —
8078    /// stable after commit) and the immutable DPB; called either inline
8079    /// (seam off / flush disabled) or in-order at a flush point. Byte-
8080    /// identical to the former inline block by construction: replay order
8081    /// equals inline order at every pixel-observable point (intra reads, row
8082    /// filtering) because flushes precede both.
8083    /// The P-inter reconstruction body, taking its inputs directly rather than
8084    /// through a `PInterJob`. Split out so the residual planes can be passed as
8085    /// `Option`s (absent == nothing was parsed) instead of forcing every caller
8086    /// to own a zeroed copy. NOTE: it re-gathers `gmv`/`gref` from the frame
8087    /// grids rather than reading the job's copies — those are for the worker,
8088    /// which must not touch the parse thread's grids.
8089    #[allow(clippy::too_many_arguments)]
8090    fn recon_p_inter_parts(
8091        &mut self,
8092        mbx: usize,
8093        mby: usize,
8094        qp: u8,
8095        cbp_chroma: u32,
8096        luma_scan: Option<&[[i32; 16]; 16]>,
8097        luma8: Option<&[[i32; 64]; 4]>,
8098        cdc: &[[i32; 4]; 2],
8099        cac: Option<&[[[i32; 16]; 4]; 2]>,
8100        nnzs: &[u8; 24],
8101    ) {
8102        let mbw = self.mb_w;
8103        // `add_inter_residual` (and anything under it) reads `self.cur_qp`,
8104        // which at FLUSH time belongs to a later macroblock — replay must
8105        // restore this MB's qp. The x264 corpus (near-constant QP) could not
8106        // see this; the encoder's delta-QP roundtrip stream caught it.
8107        let saved_qp = self.cur_qp;
8108        self.cur_qp = qp;
8109        // ---- Recon: motion-comp (per 4×4 luma / co-located 2×2 chroma using the
8110        // committed grid MV — the 6-tap/bilinear filter is per-output-pixel, so
8111        // per-block MC is bit-identical to per-partition MC) + residual add via the
8112        // SAME reconstruct_4x4 as intra, with the MC output as the prediction.
8113        let w4r = mbw * 4;
8114        let mut pred_y = [0u8; 256];
8115        let mut c_pred = [[0u8; 64]; 2];
8116        {
8117            // MC-CALL COALESCING (side-by-side descent, dec target #2): the old
8118            // loop paid 16 mc_luma(4×4) + 32 mc_chroma(2×2) per MB regardless of
8119            // partitioning — 48 calls even for a single-MV 16×16 MB, and the
8120            // per-call glue around 2.4M calls was ~40% of decoding real-world
8121            // (x264) streams. The 6-tap/bilinear filters are per-output-pixel,
8122            // so merging blocks with equal (mv, ref) into one wider MC call is
8123            // BIT-IDENTICAL; the rect ladder mirrors the partition shapes.
8124            let _ms = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMcStage);
8125            let (rh16, cch) = (self.mb_h * 16, self.mb_h * 8);
8126            let mut gmv = [(0i32, 0i32); 16];
8127            let mut gref = [0usize; 16];
8128            let _gg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
8129            let nrefs = self.refs.len() - 1;
8130            for by in 0..4usize {
8131                // Row-contiguous: one slice copy for the MVs.
8132                let row = (mby * 4 + by) * w4r + mbx * 4;
8133                gmv[by * 4..by * 4 + 4].copy_from_slice(&self.mv_y[row..row + 4]);
8134                let ridx = &self.ref_idx_y[row..row + 4];
8135                for bx in 0..4usize {
8136                    // Per-block reference (multi-ref P): ref_idx_l0 committed to the
8137                    // grid. Clamp — a corrupt stream can over-range it (never panic).
8138                    gref[by * 4 + bx] = (ridx[bx].max(0) as usize).min(nrefs);
8139                }
8140            }
8141            drop(_gg);
8142            // All blocks of the rect (in 4×4-block units) match its top-left?
8143            let rect_eq = |x4: usize, y4: usize, w4: usize, h4: usize| -> bool {
8144                let t = y4 * 4 + x4;
8145                (0..h4).all(|dy| {
8146                    (0..w4).all(|dx| {
8147                        let b = ((y4 + dy) * 4 + (x4 + dx)) & 15;
8148                        gmv[b] == gmv[t] && gref[b] == gref[t]
8149                    })
8150                })
8151            };
8152            let refs = &self.refs;
8153            let (cw, ccw) = (self.cw, self.ccw);
8154            let mc_rect = |x4: usize,
8155                           y4: usize,
8156                           w4: usize,
8157                           h4: usize,
8158                           pred_y: &mut [u8; 256],
8159                           c_pred: &mut [[u8; 64]; 2]| {
8160                let b = y4 * 4 + x4;
8161                let (mv, gr) = (gmv[b & 15], gref[b & 15]);
8162                // `gref` holds a reference-list slot; a slot the list
8163                // does not have means there is nothing to predict from,
8164                // so the rect is left as it stands.
8165                let Some(reference) = refs.get(gr) else {
8166                    return;
8167                };
8168                let (w, h) = (w4 * 4, h4 * 4);
8169                // A FULL-WIDTH rect (w == 16, so x4 == 0) occupies contiguous
8170                // whole rows of `pred_y` — the MC output layout and the
8171                // destination layout coincide, so MC writes the prediction
8172                // buffer DIRECTLY. The staging copy exists only for narrow
8173                // rects, whose rows really are strided in `pred_y`. This is
8174                // the diagnosis's "stage-boundary materialization" tax paid
8175                // by the dominant 16×16/16×8 shapes: 256 B of `t` zeroing
8176                // plus a 256 B copy per rect, for nothing.
8177                if w == 16 {
8178                    rusty_h264_common::inter::with_mc_scratch(|scr| {
8179                        rusty_h264_common::inter::mc_luma_padded_pre(
8180                            scr,
8181                            &*reference.luma_guard(reference.ch),
8182                            reference.lstride(),
8183                            crate::LPAD,
8184                            cw,
8185                            rh16,
8186                            mbx * 16,
8187                            mby * 16 + y4 * 4,
8188                            w,
8189                            h,
8190                            mv.0,
8191                            mv.1,
8192                            &mut pred_y[y4 * 64..y4 * 64 + w * h],
8193                        )
8194                    });
8195                } else {
8196                    let mut t = [0u8; 256];
8197                    rusty_h264_common::inter::with_mc_scratch(|scr| {
8198                        rusty_h264_common::inter::mc_luma_padded_pre(
8199                            scr,
8200                            &*reference.luma_guard(reference.ch),
8201                            reference.lstride(),
8202                            crate::LPAD,
8203                            cw,
8204                            rh16,
8205                            mbx * 16 + x4 * 4,
8206                            mby * 16 + y4 * 4,
8207                            w,
8208                            h,
8209                            mv.0,
8210                            mv.1,
8211                            &mut t[..w * h],
8212                        )
8213                    });
8214                    let _pb =
8215                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
8216                    for dy in 0..h {
8217                        pred_y[(y4 * 4 + dy) * 16 + x4 * 4..][..w]
8218                            .copy_from_slice(&t[dy * w..dy * w + w]);
8219                    }
8220                }
8221                let (cw4, ch4) = (w4 * 2, h4 * 2);
8222                let nc = cw4 * ch4;
8223                let (gu, gv) = (
8224                    reference.chroma_guard(0, reference.ch),
8225                    reference.chroma_guard(1, reference.ch),
8226                );
8227                // U+V paired: one setup serves both planes (see
8228                // mc_chroma_padded_pair). Full-width coincidence:
8229                // cw4 == 8 rows are contiguous in the 8-wide plane.
8230                if cw4 == 8 {
8231                    let [cu, cv] = &mut *c_pred;
8232                    rusty_h264_common::inter::mc_chroma_padded_pair(
8233                        &gu,
8234                        &gv,
8235                        reference.cstride(),
8236                        crate::CPAD,
8237                        ccw,
8238                        cch,
8239                        mbx * 8,
8240                        mby * 8 + y4 * 2,
8241                        cw4,
8242                        ch4,
8243                        mv.0,
8244                        mv.1,
8245                        &mut cu[y4 * 16..y4 * 16 + nc],
8246                        &mut cv[y4 * 16..y4 * 16 + nc],
8247                    );
8248                } else {
8249                    let (mut tu, mut tv) = ([0u8; 64], [0u8; 64]);
8250                    rusty_h264_common::inter::mc_chroma_padded_pair(
8251                        &gu,
8252                        &gv,
8253                        reference.cstride(),
8254                        crate::CPAD,
8255                        ccw,
8256                        cch,
8257                        mbx * 8 + x4 * 2,
8258                        mby * 8 + y4 * 2,
8259                        cw4,
8260                        ch4,
8261                        mv.0,
8262                        mv.1,
8263                        &mut tu[..nc],
8264                        &mut tv[..nc],
8265                    );
8266                    let _pb =
8267                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
8268                    for (cc, tc) in [(0usize, &tu), (1, &tv)] {
8269                        for dy in 0..ch4 {
8270                            c_pred[cc][(y4 * 2 + dy) * 8 + x4 * 2..][..cw4]
8271                                .copy_from_slice(&tc[dy * cw4..dy * cw4 + cw4]);
8272                        }
8273                    }
8274                }
8275            };
8276            if rect_eq(0, 0, 4, 4) {
8277                mc_rect(0, 0, 4, 4, &mut pred_y, &mut c_pred);
8278            } else if rect_eq(0, 0, 4, 2) && rect_eq(0, 2, 4, 2) {
8279                mc_rect(0, 0, 4, 2, &mut pred_y, &mut c_pred);
8280                mc_rect(0, 2, 4, 2, &mut pred_y, &mut c_pred);
8281            } else if rect_eq(0, 0, 2, 4) && rect_eq(2, 0, 2, 4) {
8282                mc_rect(0, 0, 2, 4, &mut pred_y, &mut c_pred);
8283                mc_rect(2, 0, 2, 4, &mut pred_y, &mut c_pred);
8284            } else {
8285                for q in 0..4usize {
8286                    let (qx, qy) = ((q % 2) * 2, (q / 2) * 2);
8287                    if rect_eq(qx, qy, 2, 2) {
8288                        mc_rect(qx, qy, 2, 2, &mut pred_y, &mut c_pred);
8289                    } else if rect_eq(qx, qy, 2, 1) && rect_eq(qx, qy + 1, 2, 1) {
8290                        mc_rect(qx, qy, 2, 1, &mut pred_y, &mut c_pred);
8291                        mc_rect(qx, qy + 1, 2, 1, &mut pred_y, &mut c_pred);
8292                    } else if rect_eq(qx, qy, 1, 2) && rect_eq(qx + 1, qy, 1, 2) {
8293                        mc_rect(qx, qy, 1, 2, &mut pred_y, &mut c_pred);
8294                        mc_rect(qx + 1, qy, 1, 2, &mut pred_y, &mut c_pred);
8295                    } else {
8296                        for j in 0..4usize {
8297                            mc_rect(qx + (j % 2), qy + (j / 2), 1, 1, &mut pred_y, &mut c_pred);
8298                        }
8299                    }
8300                }
8301            }
8302            // EXPLICIT WEIGHTED PREDICTION (spec 8.4.2.3). The CAVLC inter
8303            // path weights each partition after MC; the MC-call-coalescing
8304            // rewrite of this CABAC path lost it, and nothing caught that
8305            // because the effect is invisible unless a stream actually
8306            // carries non-default weights. x264's `weightp` DUPLICATES a
8307            // reference and distinguishes the copy ONLY by its weights, so
8308            // every macroblock picking the weighted index decoded unweighted
8309            // -- a silent, accumulating luma drift.
8310            //
8311            // Applied per 4x4 block rather than per partition: the weight
8312            // depends solely on the block's reference index, so the two are
8313            // equivalent, and `gref` already holds it for every block
8314            // regardless of which rect ladder rung ran.
8315            if self.weights.is_some() {
8316                for by in 0..4usize {
8317                    for bx in 0..4usize {
8318                        let refi = gref[by * 4 + bx];
8319                        self.weight_partition(
8320                            &mut pred_y,
8321                            &mut c_pred,
8322                            0,
8323                            refi,
8324                            bx * 4,
8325                            by * 4,
8326                            4,
8327                            4,
8328                        );
8329                    }
8330                }
8331            }
8332        }
8333        // Residual add — the SAME helper the B path uses (this inline
8334        // copy was a duplicate; deduped when the zero-block fast path
8335        // landed so both paths share it).
8336        self.add_inter_residual(
8337            mbx, mby, &pred_y, &c_pred, luma_scan, luma8, cdc, cac, cbp_chroma, nnzs,
8338        );
8339        self.cur_qp = saved_qp;
8340    }
8341
8342    /// Job-shaped entry point (EDC replay + the `double_recon` A/B knob).
8343    fn recon_p_inter(&mut self, j: &PInterJob) {
8344        self.recon_p_inter_parts(
8345            j.mbx,
8346            j.mby,
8347            j.qp,
8348            j.cbp_chroma,
8349            Some(&j.luma_scan),
8350            j.t8.then_some(&j.luma8),
8351            &j.cdc,
8352            Some(&j.cac),
8353            &j.nnzs,
8354        );
8355    }
8356
8357    /// D9b: P inter with `cbp == 0` — MC + plane copy, no coeff memset / residual walk.
8358    /// Byte-identical to `recon_p_inter` on a zero-residual job: prediction is the recon.
8359    fn recon_p_inter_nores(&mut self, j: &PInterNoResJob) {
8360        let w4r = self.mb_w * 4;
8361        // Parse-side nnz for single-thread EDC flush (MT commits earlier via edc_commit_nnz).
8362        // ROW FILLS. Both arms touch the same sixteen cells - four contiguous
8363        // per macroblock row - and wrote them one indexed store at a time (the
8364        // t8 arm through a 2x2-of-2x2 nest, the other through the z-order scan).
8365        for by in 0..4usize {
8366            let r = (j.mby * 4 + by) * w4r + j.mbx * 4;
8367            self.nnz_y[r..r + 4].fill(0);
8368            if j.t8 {
8369                self.coded_y[r..r + 4].fill(true);
8370            }
8371        }
8372        let mut pred_y = [0u8; 256];
8373        let mut c_pred = [[0u8; 64]; 2];
8374        // `self.refs.len() - 1` was re-loaded on every one of the sixteen
8375        // iterations; it is a per-macroblock invariant. Written once, not
8376        // zeroed-then-filled.
8377        let nrefs = self.refs.len() - 1;
8378        let gref: [usize; 16] = core::array::from_fn(|k| (j.gref[k] as usize).min(nrefs));
8379        coalesce_p_inter_mc(
8380            &self.refs,
8381            self.cw,
8382            self.ccw,
8383            self.mb_h,
8384            j.mbx,
8385            j.mby,
8386            &j.gmv,
8387            &gref,
8388            &mut pred_y,
8389            &mut c_pred,
8390        );
8391        // The identity early-out lives INSIDE `weight_partition`, so an x264
8392        // stream - which carries a pred_weight_table in EVERY P slice, identity
8393        // outside fades - still paid sixteen calls per macroblock to be told
8394        // there was nothing to do. Hoisted to one test.
8395        if self.weights.is_some() && !self.weights_l0id {
8396            for by in 0..4usize {
8397                for bx in 0..4usize {
8398                    let refi = gref[by * 4 + bx];
8399                    self.weight_partition(&mut pred_y, &mut c_pred, 0, refi, bx * 4, by * 4, 4, 4);
8400                }
8401            }
8402        }
8403        // ONE span per plane: the destination is a stack of contiguous runs a
8404        // fixed stride apart, so the rows share a single bounds check instead of
8405        // one each (16 + 8 + 8 of them).
8406        let (cw, ccw) = (self.cw, self.ccw);
8407        let ybase = (j.mby * 16) * cw + j.mbx * 16;
8408        let ywin = &mut self.rec_y[ybase..ybase + 15 * cw + 16];
8409        for dy in 0..16 {
8410            ywin[dy * cw..dy * cw + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
8411        }
8412        let cbase = (j.mby * 8) * ccw + j.mbx * 8;
8413        for c in 0..2 {
8414            let plane = if c == 0 {
8415                &mut self.rec_u
8416            } else {
8417                &mut self.rec_v
8418            };
8419            let cwin = &mut plane[cbase..cbase + 7 * ccw + 8];
8420            for dy in 0..8 {
8421                cwin[dy * ccw..dy * ccw + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
8422            }
8423        }
8424    }
8425
8426    fn decode_p_skip(&mut self, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
8427        self.route_skip_mbs += 1;
8428        self.wait_refs_for_mb(mb_y);
8429        // DEBLOCK CLASS: a P_Skip macroblock carries no coefficients and one
8430        // (ref, mv) for all 16 blocks, so every internal boundary strength is 0 by
8431        // §8.7.2.1 and the loop filter needs 9 block loads instead of 24. This is
8432        // the single highest-value classification: the MB-kind census measures Skip
8433        // at 36.4% (CAVLC) / 65.0% (main) / 57.8% (high) of real x264 corpora.
8434        // Written HERE because both the CAVLC and the CABAC slice loops funnel
8435        // through this one function.
8436        //
8437        // Deliberately NOT done for `B_Skip` — its motion is direct-derived and can
8438        // differ per 4×4 sub-block, so its internal edges can legally reach
8439        // strength 1. B_Skip stays UNSET and takes the blind path.
8440        // (kind commit moved into the span/immediate arms below.)
8441        // P_Skip always references index 0 (the most recent picture). Borrow it —
8442        // a full-frame `.cloned()` here was ~86% of total decode time (one ~3 MB
8443        // plane copy per skip MB, thousands per frame).
8444        if self.refs.is_empty() {
8445            return Err(MbError::Unsupported("P_Skip without reference"));
8446        }
8447        let addr = mb_y * self.mb_w + mb_x;
8448        // mb_x == 0: the left neighbor is off-frame, so §8.4.1.1's
8449        // unavailability rule forces (0,0) with no gather at all.
8450        let mv = if (mb_x == 0 || self.skip_zero_next == addr) && !no_runmv() {
8451            edcstat::bump(&edcstat::SKIPMV_FORCED, 1);
8452            (0, 0)
8453        } else {
8454            // The gather reads the grids; the pending spans cannot contain
8455            // this MB's left (a non-forced skip means the previous MB was not
8456            // a (0,0) committer) — flush both before deriving.
8457            self.span_flush();
8458            edcstat::bump(&edcstat::SKIPMV_DERIVED, 1);
8459            self.skip_mv(mb_x, mb_y)
8460        };
8461        self.skip_zero_next = if mv == (0, 0) { addr + 1 } else { usize::MAX };
8462        // Grid commits are PARSE state — (0,0) skips defer them into the P
8463        // span (the deferral constants); nonzero-MV skips commit immediately.
8464        if mv == (0, 0) {
8465            // Recon defers too when the band identity holds (1T, no/identity
8466            // weights) — the span flush band-copies instead of queueing jobs.
8467            let recon = self.edc_tx.is_none() && (self.weights.is_none() || self.weights_id0);
8468            self.pz_push(mb_x, mb_y, recon);
8469            if recon {
8470                return Ok(());
8471            }
8472        } else {
8473            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8474            if let Some(p) = self.mb_kind.get_mut(mb_y * self.mb_w + mb_x) {
8475                *p = rusty_h264_common::deblock::MB_KIND_SKIP;
8476            }
8477            self.set_mb_mv(mb_x, mb_y, mv, true, 0);
8478            let w4 = self.mb_w * 4;
8479            for dy in 0..4 {
8480                let a = (mb_y * 4 + dy) * w4 + mb_x * 4;
8481                self.coded_y[a..a + 4].fill(true);
8482                self.modes_y[a..a + 4].fill(2);
8483            }
8484        }
8485        if self.edc_tx.is_some() {
8486            self.edc_giveback();
8487            self.edc_send_job(EdcJob::Skip {
8488                mbx: mb_x,
8489                mby: mb_y,
8490                mv,
8491            });
8492            return Ok(());
8493        }
8494        if self.edc_active {
8495            self.edc_jobs.push(EdcJob::Skip {
8496                mbx: mb_x,
8497                mby: mb_y,
8498                mv,
8499            });
8500            return Ok(());
8501        }
8502        self.recon_p_skip(mb_x, mb_y, mv);
8503        if double_recon() {
8504            self.recon_p_skip(mb_x, mb_y, mv);
8505        }
8506        Ok(())
8507    }
8508
8509    /// Pixel half of P_Skip (see the E1 seam note on `recon_p_inter`).
8510    /// Run-coalesced P_Skip reconstruction: `n` consecutive skip MBs on one
8511    /// row, all with `mv == (0,0)` and NO weighted prediction. At full-pel
8512    /// zero motion, MC is the identity read of `refs[0]` — so the recon is a
8513    /// straight band copy from the padded reference plane, no staging, no MC
8514    /// calls. Byte-identical to `n` calls of `recon_p_skip` by construction
8515    /// (mc_luma_padded/mc_chroma_padded at mv 0 return exactly these bytes).
8516    ///
8517    /// The (0,0) run theorem that makes ONE derivation cover the run: once a
8518    /// skip MB commits (ref 0, mv (0,0)), every later MB of the run derives
8519    /// (0,0) too — its left neighbour (or a row-start's missing neighbour)
8520    /// triggers §8.4.1.1's zero-MV rule in `skip_mv`.
8521    fn recon_p_skip_band(&mut self, mbx0: usize, mby: usize, n: usize) {
8522        let w = n * 16;
8523        let Some(rf0) = self.refs.first() else { return };
8524        let lst = rf0.lstride();
8525        {
8526            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8527            let ly = rf0.luma_guard(rf0.ch);
8528            for dy in 0..16 {
8529                let y = mby * 16 + dy;
8530                let src = (y + crate::LPAD) * lst + crate::LPAD + mbx0 * 16;
8531                let dst = y * self.cw + mbx0 * 16;
8532                // ARMOR (fuzz find, inline-execution.md 11.11): on a MALFORMED
8533                // stream a reference can carry geometry that does not match the
8534                // open picture (mutated parameter sets / truncated ref planes),
8535                // so the in-frame band's ref window can leave the guard slice.
8536                // The band REFUSES rather than panics; a conformant stream
8537                // cannot take this arm (repro: scratchpad repro.264, plane 6400
8538                // vs index 6416). Output on malformed input is unspecified —
8539                // absence of panic is the contract the fuzz gate enforces.
8540                let (Some(d), Some(sr)) = (self.rec_y.get_mut(dst..dst + w), ly.get(src..src + w))
8541                else {
8542                    return;
8543                };
8544                d.copy_from_slice(sr);
8545            }
8546        }
8547        let cst = rf0.cstride();
8548        let wc = n * 8;
8549        for c in 0..2 {
8550            let rc = rf0.chroma_guard(c, rf0.ch);
8551            let plane = if c == 0 {
8552                &mut self.rec_u
8553            } else {
8554                &mut self.rec_v
8555            };
8556            for dy in 0..8 {
8557                let y = mby * 8 + dy;
8558                let src = (y + crate::CPAD) * cst + crate::CPAD + mbx0 * 8;
8559                let dst = y * self.ccw + mbx0 * 8;
8560                // Same armor as the luma band above.
8561                let (Some(d), Some(sr)) = (plane.get_mut(dst..dst + wc), rc.get(src..src + wc))
8562                else {
8563                    return;
8564                };
8565                d.copy_from_slice(sr);
8566            }
8567        }
8568        edcstat::bump(&edcstat::SKIPBAND_MBS, n as u64);
8569        edcstat::bump(&edcstat::SKIPBAND_RUNS, 1);
8570    }
8571
8572    /// B_Skip zero-bi recon: rec = avg(ref0, ref1') row-wise, straight from the
8573    /// padded planes. Byte-identical to b_mc at mv (0,0)/(0,0): full-pel MC is
8574    /// an identity read and the unweighted bi blend is exactly (a+b+1)>>1 —
8575    /// which the compiler emits as pavgb over the sliced zips, same as b_mc's
8576    /// blend site.
8577    fn recon_b_skip_zero_bi(&mut self, mbx: usize, mby: usize, r0i: usize, r1i: usize) {
8578        let (Some(rf0), Some(rf1)) = (self.refs.get(r0i), self.refs1.get(r1i)) else {
8579            return;
8580        };
8581        let (l0st, l1st) = (rf0.lstride(), rf1.lstride());
8582        {
8583            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8584            let (ly0, ly1) = (rf0.luma_guard(rf0.ch), rf1.luma_guard(rf1.ch));
8585            for dy in 0..16 {
8586                let y = mby * 16 + dy;
8587                let s0 = (y + crate::LPAD) * l0st + crate::LPAD + mbx * 16;
8588                let s1 = (y + crate::LPAD) * l1st + crate::LPAD + mbx * 16;
8589                let d = y * self.cw + mbx * 16;
8590                // WIN: STRIDED destination, so rustc cannot do what it does for the
8591                // contiguous pred_y averages (which are already ideal vpavgb and are
8592                // left alone). Route the row through the pixel_avg kernel instead.
8593                rusty_h264_common::inter::avg_row_into(
8594                    &ly0[s0..s0 + 16],
8595                    &ly1[s1..s1 + 16],
8596                    16,
8597                    &mut self.rec_y[d..d + 16],
8598                );
8599            }
8600        }
8601        let (c0st, c1st) = (rf0.cstride(), rf1.cstride());
8602        for c in 0..2 {
8603            let rc0 = rf0.chroma_guard(c, rf0.ch);
8604            let rc1 = rf1.chroma_guard(c, rf1.ch);
8605            let plane = if c == 0 {
8606                &mut self.rec_u
8607            } else {
8608                &mut self.rec_v
8609            };
8610            for dy in 0..8 {
8611                let y = mby * 8 + dy;
8612                let s0 = (y + crate::CPAD) * c0st + crate::CPAD + mbx * 8;
8613                let s1 = (y + crate::CPAD) * c1st + crate::CPAD + mbx * 8;
8614                let d = y * self.ccw + mbx * 8;
8615                // WIN: STRIDED destination, so rustc cannot do what it does for the
8616                // contiguous pred_y averages (which are already ideal vpavgb and are
8617                // left alone). Route the row through the pixel_avg kernel instead.
8618                rusty_h264_common::inter::avg_row_into(
8619                    &rc0[s0..s0 + 8],
8620                    &rc1[s1..s1 + 8],
8621                    8,
8622                    &mut plane[d..d + 8],
8623                );
8624            }
8625        }
8626    }
8627
8628    /// Full-pel P_Skip single: rec window = ref\[0\] window at an integer offset.
8629    /// Returns false (touching nothing) when any window leaves the padded
8630    /// plane — mc's edge clamping takes over there. Luma needs mv%4==0;
8631    /// chroma needs mv%8==0 (its frac is mv&7), so odd full-pel MVs copy luma
8632    /// and interpolate chroma.
8633    fn recon_p_skip_fullpel(&mut self, mbx: usize, mby: usize, mv: (i32, i32)) -> bool {
8634        let (dx, dy) = ((mv.0 / 4) as isize, (mv.1 / 4) as isize);
8635        let Some(rf0) = self.refs.first() else {
8636            return false;
8637        };
8638        let lst = rf0.lstride();
8639        let lpad = crate::LPAD as isize;
8640        let cx = mbx as isize * 16 + dx + lpad;
8641        let cy = mby as isize * 16 + dy + lpad;
8642        {
8643            let ly = rf0.luma_guard(rf0.ch);
8644            let lrows = rf0.lrows() as isize;
8645            if cx < 0 || cx + 16 > lst as isize || cy < 0 || cy + 16 > lrows {
8646                return false;
8647            }
8648            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8649            // ONE span each side: sixteen rows a fixed stride apart, so the
8650            // source and destination checks are paid once instead of per row.
8651            let (cw, sb) = (self.cw, cy as usize * lst + cx as usize);
8652            let db = (mby * 16) * cw + mbx * 16;
8653            let sw = &ly[sb..sb + 15 * lst + 16];
8654            let dw = &mut self.rec_y[db..db + 15 * cw + 16];
8655            for r in 0..16 {
8656                dw[r * cw..r * cw + 16].copy_from_slice(&sw[r * lst..r * lst + 16]);
8657            }
8658        }
8659        let cst = rf0.cstride();
8660        let cpad = crate::CPAD as isize;
8661        if mv.0 % 8 == 0 && mv.1 % 8 == 0 {
8662            let ccx = mbx as isize * 8 + (mv.0 / 8) as isize + cpad;
8663            let ccy = mby as isize * 8 + (mv.1 / 8) as isize + cpad;
8664            let crows = rf0.crows() as isize;
8665            if ccx >= 0 && ccx + 8 <= cst as isize && ccy >= 0 && ccy + 8 <= crows {
8666                for c in 0..2 {
8667                    let rc = rf0.chroma_guard(c, rf0.ch);
8668                    let plane = if c == 0 {
8669                        &mut self.rec_u
8670                    } else {
8671                        &mut self.rec_v
8672                    };
8673                    let (ccw, sb) = (self.ccw, ccy as usize * cst + ccx as usize);
8674                    let db = (mby * 8) * ccw + mbx * 8;
8675                    let sw = &rc[sb..sb + 7 * cst + 8];
8676                    let dw = &mut plane[db..db + 7 * ccw + 8];
8677                    for r in 0..8 {
8678                        dw[r * ccw..r * ccw + 8].copy_from_slice(&sw[r * cst..r * cst + 8]);
8679                    }
8680                }
8681                edcstat::bump(&edcstat::SKIP_FP_FULL, 1);
8682                return true;
8683            }
8684        }
8685        // Odd full-pel (or chroma window out of pad): luma is already copied,
8686        // chroma still interpolates — identical to the mc path's chroma half.
8687        let cch = self.mb_h * 8;
8688        for c in 0..2 {
8689            let mut pc = [0u8; 64];
8690            let rc = if c == 0 {
8691                &*rf0.chroma_guard(0, rf0.ch)
8692            } else {
8693                &*rf0.chroma_guard(1, rf0.ch)
8694            };
8695            mc_chroma_padded(
8696                rc,
8697                cst,
8698                crate::CPAD,
8699                self.ccw,
8700                cch,
8701                mbx * 8,
8702                mby * 8,
8703                8,
8704                8,
8705                mv.0,
8706                mv.1,
8707                &mut pc,
8708            );
8709            let plane = if c == 0 {
8710                &mut self.rec_u
8711            } else {
8712                &mut self.rec_v
8713            };
8714            for r in 0..8 {
8715                let d = (mby * 8 + r) * self.ccw + mbx * 8;
8716                plane[d..d + 8].copy_from_slice(&pc[r * 8..r * 8 + 8]);
8717            }
8718        }
8719        edcstat::bump(&edcstat::SKIP_FP_LUMA, 1);
8720        true
8721    }
8722
8723    /// B_Skip full-pel recon with per-list integer offsets: uni = offset row
8724    /// copy, bi = offset row average ((a+b+1)>>1). Luma always copies here
8725    /// (caller guarantees mv%4==0); chroma copies at mv%8==0 and otherwise
8726    /// interpolates via b_mc_chroma — identical to the b_mc path's chroma
8727    /// half with no implicit weights. Returns false untouched when a luma
8728    /// window leaves the padded plane.
8729    fn recon_b_skip_fp(
8730        &mut self,
8731        mbx: usize,
8732        mby: usize,
8733        r0: Option<usize>,
8734        r1: Option<usize>,
8735        mv0: (i32, i32),
8736        mv1: (i32, i32),
8737    ) -> bool {
8738        let lpad = crate::LPAD as isize;
8739        // (start-row, start-col, stride, rows) of each active list's luma window.
8740        let win = |rf: &crate::RefFrame, mv: (i32, i32)| -> Option<(usize, usize)> {
8741            let lst = rf.lstride();
8742            let cx = mbx as isize * 16 + (mv.0 / 4) as isize + lpad;
8743            let cy = mby as isize * 16 + (mv.1 / 4) as isize + lpad;
8744            let lrows = rf.lrows() as isize;
8745            if cx < 0 || cx + 16 > lst as isize || cy < 0 || cy + 16 > lrows {
8746                None
8747            } else {
8748                Some((cy as usize * lst + cx as usize, lst))
8749            }
8750        };
8751        let w0 = match r0 {
8752            Some(i) => match self.refs.get(i).and_then(|rr| win(rr, mv0)) {
8753                Some(w) => Some(w),
8754                None => return false,
8755            },
8756            None => None,
8757        };
8758        let w1 = match r1 {
8759            Some(i) => match self.refs1.get(i).and_then(|rr| win(rr, mv1)) {
8760                Some(w) => Some(w),
8761                None => return false,
8762            },
8763            None => None,
8764        };
8765        {
8766            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8767            match (w0, w1) {
8768                (Some((s0, l0)), Some((s1, l1))) => {
8769                    let Some(rf0) = r0.and_then(|i| self.refs.get(i)) else {
8770                        return false;
8771                    };
8772                    // Same shape as the `rf0` line above: `w1` is `Some` only when
8773                    // `r1` is `Some(i)` AND `refs1.get(i)` returned a frame, so the
8774                    // `else` is unreachable -- but it is a REFUSAL, not a panic, and
8775                    // `recon_b_skip_fp` already answers `false` for every case it
8776                    // cannot fast-path. Indexing a `Vec` with an unwrapped `Option`
8777                    // spent a discriminant test, an `unwrap_failed` call and a bounds
8778                    // check to restate an invariant the caller had already proven.
8779                    let Some(rf1) = r1.and_then(|i| self.refs1.get(i)) else {
8780                        return false;
8781                    };
8782                    let (ly0, ly1) = (rf0.luma_guard(rf0.ch), rf1.luma_guard(rf1.ch));
8783                    // WIN: this was 256 scalar (p + q + 1) >> 1 per macroblock, three
8784                    // modules from the  kernel that computes exactly that
8785                    // with  and already takes both source strides. One kernel
8786                    // call over the 16x16, then sixteen row copies out to the strided
8787                    // recon plane.
8788                    for r in 0..16 {
8789                        let d = (mby * 16 + r) * self.cw + mbx * 16;
8790                        rusty_h264_common::inter::avg_row_into(
8791                            &ly0[s0 + r * l0..],
8792                            &ly1[s1 + r * l1..],
8793                            16,
8794                            &mut self.rec_y[d..d + 16],
8795                        );
8796                    }
8797                }
8798                (Some((s0, l0)), None) => {
8799                    let Some(rf0) = r0.and_then(|i| self.refs.get(i)) else {
8800                        return false;
8801                    };
8802                    let ly = rf0.luma_guard(rf0.ch);
8803                    for r in 0..16 {
8804                        let d = (mby * 16 + r) * self.cw + mbx * 16;
8805                        self.rec_y[d..d + 16].copy_from_slice(&ly[s0 + r * l0..s0 + r * l0 + 16]);
8806                    }
8807                }
8808                (None, Some((s1, l1))) => {
8809                    // Same shape as the `rf0` line above: `w1` is `Some` only when
8810                    // `r1` is `Some(i)` AND `refs1.get(i)` returned a frame, so the
8811                    // `else` is unreachable -- but it is a REFUSAL, not a panic, and
8812                    // `recon_b_skip_fp` already answers `false` for every case it
8813                    // cannot fast-path. Indexing a `Vec` with an unwrapped `Option`
8814                    // spent a discriminant test, an `unwrap_failed` call and a bounds
8815                    // check to restate an invariant the caller had already proven.
8816                    let Some(rf1) = r1.and_then(|i| self.refs1.get(i)) else {
8817                        return false;
8818                    };
8819                    let ly = rf1.luma_guard(rf1.ch);
8820                    for r in 0..16 {
8821                        let d = (mby * 16 + r) * self.cw + mbx * 16;
8822                        self.rec_y[d..d + 16].copy_from_slice(&ly[s1 + r * l1..s1 + r * l1 + 16]);
8823                    }
8824                }
8825                (None, None) => unreachable!("caller guarantees an active list"),
8826            }
8827        }
8828        // Chroma: interpolating half — route through b_mc_chroma into a stage
8829        // (weights None: the caller only enters at implicit None/(32,32), and
8830        // (32,32) IS the plain average b_mc_chroma computes for None).
8831        let cch = self.mb_h * 8;
8832        let mut c_pred = [[0u8; 64]; 2];
8833        let (ri0, ri1) = (r0.map_or(-1, |i| i as i32), r1.map_or(-1, |i| i as i32));
8834        self.b_mc_chroma(
8835            mbx,
8836            mby,
8837            0,
8838            0,
8839            16,
8840            16,
8841            ri0,
8842            mv0,
8843            ri1,
8844            mv1,
8845            &mut c_pred,
8846            None,
8847            cch,
8848        );
8849        for c in 0..2 {
8850            let plane = if c == 0 {
8851                &mut self.rec_u
8852            } else {
8853                &mut self.rec_v
8854            };
8855            for r in 0..8 {
8856                let d = (mby * 8 + r) * self.ccw + mbx * 8;
8857                plane[d..d + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
8858            }
8859        }
8860        true
8861    }
8862
8863    fn recon_p_skip(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32)) {
8864        // Full-pel + identity/no weights: MC is a pure offset read, so copy the
8865        // ref window straight into rec — no pred staging, no weight pass. The
8866        // window must sit inside the PADDED plane (outside it, mc's clamping
8867        // differs from a raw offset read, so fall through).
8868        if (self.weights.is_none() || self.weights_id0)
8869            && mv.0 % 4 == 0
8870            && mv.1 % 4 == 0
8871            && !no_skipfp()
8872            && self.recon_p_skip_fullpel(mb_x, mb_y, mv)
8873        {
8874            return;
8875        }
8876        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
8877
8878        let mut pred = [0u8; 256];
8879        let Some(rf0) = self.refs.first() else { return };
8880        mc_luma_padded(
8881            &*rf0.luma_guard(rf0.ch),
8882            rf0.lstride(),
8883            crate::LPAD,
8884            self.cw,
8885            ch,
8886            mb_x * 16,
8887            mb_y * 16,
8888            16,
8889            16,
8890            mv.0,
8891            mv.1,
8892            &mut pred,
8893        );
8894        if let Some(wt) = &self.weights {
8895            if !self.weights_id0 || no_skipfp() {
8896                for p in pred.iter_mut() {
8897                    *p = wt.apply_luma(*p, 0, 0);
8898                }
8899            }
8900        }
8901        {
8902            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
8903            // ONE span for the sixteen rows (see `recon_p_inter_nores`).
8904            let (cw, base) = (self.cw, (mb_y * 16) * self.cw + mb_x * 16);
8905            let win = &mut self.rec_y[base..base + 15 * cw + 16];
8906            for dy in 0..16 {
8907                win[dy * cw..dy * cw + 16].copy_from_slice(&pred[dy * 16..dy * 16 + 16]);
8908            }
8909        }
8910        let (mut pu, mut pv) = ([0u8; 64], [0u8; 64]);
8911        {
8912            let Some(rf0) = self.refs.first() else { return };
8913            let (gu, gv) = (rf0.chroma_guard(0, rf0.ch), rf0.chroma_guard(1, rf0.ch));
8914            rusty_h264_common::inter::mc_chroma_padded_pair(
8915                &gu,
8916                &gv,
8917                rf0.cstride(),
8918                crate::CPAD,
8919                self.ccw,
8920                cch,
8921                mb_x * 8,
8922                mb_y * 8,
8923                8,
8924                8,
8925                mv.0,
8926                mv.1,
8927                &mut pu,
8928                &mut pv,
8929            );
8930        }
8931        if let Some(wt) = &self.weights {
8932            if !self.weights_id0 || no_skipfp() {
8933                for p in pu.iter_mut() {
8934                    *p = wt.apply_chroma(*p, 0, 0, 0);
8935                }
8936                for p in pv.iter_mut() {
8937                    *p = wt.apply_chroma(*p, 0, 0, 1);
8938                }
8939            }
8940        }
8941        for (pc, plane) in [(&pu, &mut self.rec_u), (&pv, &mut self.rec_v)] {
8942            for dy in 0..8 {
8943                let d = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
8944                plane[d..d + 8].copy_from_slice(&pc[dy * 8..dy * 8 + 8]);
8945            }
8946        }
8947    }
8948
8949    /// Predicted `Intra_4x4` mode for the block at absolute coords `(bx, by)`.
8950    /// If either the left or top neighbor is outside the frame or in another
8951    /// slice, the prediction is DC (mode 2) (spec §8.3.1.1).
8952    /// `predict_i4_mode` with the MACROBLOCK-level availability already
8953    /// resolved by the caller (`top_ok` / `left_ok`).
8954    ///
8955    /// Twelve of a macroblock's sixteen blocks have BOTH neighbours inside the
8956    /// same macroblock, so their slice test is a foregone conclusion - yet the
8957    /// general form recomputed two `nbr_in_slice` products and two
8958    /// `intra_nbr_ok` tests for every one of them, ~1M times on all-intra
8959    /// content.
8960    ///
8961    /// `constrained_intra` is excluded deliberately: there the general form
8962    /// tests THIS macroblock's own `inter_y` cells for interior blocks, which
8963    /// the macroblock-level flags do not model, so that case defers unchanged.
8964    #[inline]
8965    fn predict_i4_mode_fast(
8966        &self,
8967        bx: usize,
8968        by: usize,
8969        lbx: usize,
8970        lby: usize,
8971        top_ok: bool,
8972        left_ok: bool,
8973    ) -> u8 {
8974        if self.constrained_intra {
8975            return self.predict_i4_mode(bx, by);
8976        }
8977        if !((lbx > 0 || left_ok) && (lby > 0 || top_ok)) {
8978            return 2;
8979        }
8980        let w4 = self.mb_w * 4;
8981        // Fallible reads: two checks on the same grid at two indexes. `2` is DC,
8982        // which is exactly what every unavailable-neighbour path above already
8983        // returns, so the fallback is the function's own existing semantics.
8984        let l = self.modes_y.get(by * w4 + (bx - 1)).copied().unwrap_or(2);
8985        let t = self.modes_y.get((by - 1) * w4 + bx).copied().unwrap_or(2);
8986        l.min(t)
8987    }
8988
8989    fn predict_i4_mode(&self, bx: usize, by: usize) -> u8 {
8990        if bx == 0 || by == 0 {
8991            return 2;
8992        }
8993        // Left neighbor block (bx-1,by); top neighbor block (bx,by-1). A neighbor
8994        // in another slice — or, under constrained_intra, an inter neighbor — is
8995        // unavailable, forcing the predicted mode to DC.
8996        if !self.nbr_in_slice((bx - 1) / 4, by / 4)
8997            || !self.nbr_in_slice(bx / 4, (by - 1) / 4)
8998            || !self.intra_nbr_ok(bx - 1, by)
8999            || !self.intra_nbr_ok(bx, by - 1)
9000        {
9001            return 2;
9002        }
9003        let w4 = self.mb_w * 4;
9004        // Fallible reads: two checks on the same grid at two indexes. `2` is DC,
9005        // which is exactly what every unavailable-neighbour path above already
9006        // returns, so the fallback is the function's own existing semantics.
9007        let l = self.modes_y.get(by * w4 + (bx - 1)).copied().unwrap_or(2);
9008        let t = self.modes_y.get((by - 1) * w4 + bx).copied().unwrap_or(2);
9009        l.min(t)
9010    }
9011
9012    /// Gathers 4×4 luma intra neighbors at pixel `(px, py)` from `rec_y`.
9013    fn gather_i4(
9014        &self,
9015        px: usize,
9016        py: usize,
9017        avail_top: bool,
9018        avail_left: bool,
9019        bx: usize,
9020        by: usize,
9021    ) -> ([u8; 8], [u8; 4], u8) {
9022        let (cw, w4) = (self.cw, self.mb_w * 4);
9023        let mut top = [0u8; 8];
9024        let mut left = [0u8; 4];
9025        let mut corner = 0;
9026        let (lbx, lby) = (bx & 3, by & 3);
9027        if avail_top {
9028            // Row-slice loads: the bak-vs-rec source branch runs once per row
9029            // segment instead of once per PIXEL (top_y_px paid it 8 times).
9030            top[..4].copy_from_slice(self.top_y_row(py, px, 4));
9031            // ROUTED BY POSITION (routing round): below the macroblock's top row the
9032            // top-right neighbour is inside this macroblock (or the undecoded right
9033            // neighbour), so its availability is the z-order constant I4_TR_IN_MB --
9034            // no grid load, no slice test, no constrained-intra call.
9035            let tr_avail = if lby > 0 {
9036                lbx < 3 && (I4_TR_IN_MB >> I4_Z_OF_XY[lby][lbx]) & 1 == 1
9037            } else {
9038                bx + 1 < w4
9039                    && self
9040                        .coded_y
9041                        .get((by - 1) * w4 + (bx + 1))
9042                        .copied()
9043                        .unwrap_or(false)
9044                    && self.nbr_in_slice((bx + 1) / 4, (by - 1) / 4)
9045                    && self.intra_nbr_ok(bx + 1, by - 1)
9046            };
9047            if tr_avail {
9048                top[4..8].copy_from_slice(self.top_y_row(py, px + 4, 4));
9049            } else {
9050                let t3 = top[3];
9051                top[4..8].fill(t3);
9052            }
9053        }
9054        if avail_left {
9055            // STRIDED WALK. The span form this replaces claimed `i * cw <= 3 * cw`
9056            // was provable against the span's length; it is not — the same shape
9057            // left five live checks on `recon_i16_luma`'s sixteen-sample column.
9058            // `step_by` does the stride with no index, and `zip` bounds the count.
9059            let base = py * cw + px - 1;
9060            for (s, &v) in left.iter_mut().zip(self.rec_y[base..].iter().step_by(cw)) {
9061                *s = v;
9062            }
9063        }
9064        // The above-left corner has its own availability (block D); under
9065        // constrained_intra it is gone if that block is inter.
9066        // Interior corner is this macroblock's own (intra) sample: no constrained-intra call.
9067        if avail_top && avail_left && ((lbx > 0 && lby > 0) || self.intra_nbr_ok(bx - 1, by - 1)) {
9068            corner = self.top_y_px(py, px - 1);
9069        }
9070        (top, left, corner)
9071    }
9072
9073    /// Reconstructs an `I_PCM` macroblock: byte-aligned raw 8-bit samples, no
9074    /// prediction/transform/quant (spec §7.3.5, §8.3.5).
9075    fn decode_ipcm(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
9076        r.align_to_byte()?;
9077        // ROW SLICES throughout. A PCM macroblock wrote 384 samples one at a
9078        // time, each a separately bounds-checked index into a whole plane, and
9079        // then 104 more scattered context stores below. Every extent here is a
9080        // literal (16, 8, 4, 2), so one slice per row makes the inner index
9081        // provable and the stores become a straight walk.
9082        let (lx, ly) = (mb_x * 16, mb_y * 16);
9083        for dy in 0..16 {
9084            let row = &mut self.rec_y[(ly + dy) * self.cw + lx..][..16];
9085            for px in row.iter_mut() {
9086                *px = r.read_bits(8)? as u8;
9087            }
9088        }
9089        let (cx, cy) = (mb_x * 8, mb_y * 8);
9090        let ccw = self.ccw;
9091        for plane in [&mut self.rec_u, &mut self.rec_v] {
9092            for dy in 0..8 {
9093                let row = &mut plane[(cy + dy) * ccw + cx..][..8];
9094                for px in row.iter_mut() {
9095                    *px = r.read_bits(8)? as u8;
9096                }
9097            }
9098        }
9099        // Neighbor context: an I_PCM block contributes TotalCoeff = 16, counts as
9100        // intra with DC mode for prediction, and has no motion (§9.2.1, §8.3.1.2.2).
9101        let (w4, w2) = (self.mb_w * 4, self.mb_w * 2);
9102        // The scan order is irrelevant when every written value is a constant:
9103        // the sixteen `LUMA_4X4_SCAN_XY` positions are exactly the macroblock's
9104        // 4x4 rectangle, so four row fills per array cover them.
9105        for ry in 0..4 {
9106            let base = (mb_y * 4 + ry) * w4 + mb_x * 4;
9107            self.nnz_y[base..][..4].fill(16);
9108            self.modes_y[base..][..4].fill(2);
9109            self.coded_y[base..][..4].fill(true);
9110            self.inter_y[base..][..4].fill(false);
9111            self.ref_idx_y[base..][..4].fill(-1);
9112            self.mv_y[base..][..4].fill((0, 0));
9113        }
9114        for c in 0..2 {
9115            for by in 0..2 {
9116                self.nnz_c[c][(mb_y * 2 + by) * w2 + mb_x * 2..][..2].fill(16);
9117            }
9118        }
9119        Ok(())
9120    }
9121
9122    fn decode_i4x4(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
9123        let w4 = self.mb_w * 4;
9124
9125        // intra4x4 mode signalling
9126        let mut modes = [2u8; 16]; // raster [lby*4+lbx]
9127        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
9128            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
9129            let predicted = self.predict_i4_mode(bx, by);
9130            let actual = if r.read_bit()? {
9131                predicted
9132            } else {
9133                let rem = r.read_bits(3)? as u8;
9134                if rem < predicted {
9135                    rem
9136                } else {
9137                    rem + 1
9138                }
9139            };
9140            if let Some(p) = self.modes_y.get_mut(by * w4 + bx) {
9141                *p = actual;
9142            }
9143            modes[(lby & 3) * 4 + (lbx & 3)] = actual;
9144        }
9145
9146        let chroma_mode = r.read_ue()? as u8;
9147        let cbp = read_cbp_intra(r)?;
9148        let cbp_luma = cbp & 15;
9149        let cbp_chroma = cbp >> 4;
9150        if cbp != 0 {
9151            self.step_qp(r.read_se()?)?;
9152        }
9153        let qp = self.cur_qp;
9154
9155        // luma residuals + serial reconstruction. Cross-MB neighbors are only
9156        // available when the adjacent macroblock is in this slice (and, under
9157        // constrained_intra_pred, is itself intra-coded).
9158        let top_mb_avail = mb_y > 0
9159            && self.nbr_in_slice(mb_x, mb_y - 1)
9160            && self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
9161        let left_mb_avail = mb_x > 0
9162            && self.nbr_in_slice(mb_x - 1, mb_y)
9163            && self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
9164        self.nnz_cache_load(mb_x, mb_y);
9165        let mut nnz_raster = [0u8; 16];
9166        // PARSE all sixteen blocks first (nC prediction needs only the counts), so
9167        // the residual transforms run as ONE batched IDCT before the serial
9168        // predict+add walk. Parse and recon were interleaved per block before.
9169        let mut scans = [[0i32; 16]; 16];
9170        let mut totals = [0u8; 16];
9171        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
9172            let total = if cbp_luma & (1 << (blk / 4)) != 0 {
9173                let nc = self.nc_pred(lbx, lby);
9174                decode_residual_block_into::<16, 16>(r, nc, &mut scans[blk & 15])?
9175            } else {
9176                0
9177            };
9178            self.nnz_cache_set(lbx, lby, total);
9179            nnz_raster[(lby & 3) * 4 + (lbx & 3)] = total;
9180            totals[blk & 15] = total;
9181        }
9182        let kinds = self.i4_prepare(&scans, &totals, qp);
9183        let dq0 = self.dq_const(qp, 0);
9184        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
9185            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
9186            let avail_top = lby > 0 || top_mb_avail;
9187            let avail_left = lbx > 0 || left_mb_avail;
9188            self.recon_i4_block_res(
9189                bx,
9190                by,
9191                modes[(lby & 3) * 4 + (lbx & 3)],
9192                avail_top,
9193                avail_left,
9194                kinds[blk & 15],
9195                &scans,
9196                &dq0,
9197            );
9198        }
9199        for ry in 0..4usize {
9200            self.coded_y[(mb_y * 4 + ry) * w4 + mb_x * 4..][..4].fill(true);
9201        }
9202        // Deferred: `recon_i4_block` gathers from `coded_y` / `rec_y`, never
9203        // from `nnz_y`, so one contiguous copy per row replaces sixteen stores.
9204        for by in 0..4usize {
9205            let a = (mb_y * 4 + by) * w4 + mb_x * 4;
9206            self.nnz_y[a..a + 4].copy_from_slice(&nnz_raster[by * 4..by * 4 + 4]);
9207        }
9208
9209        self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
9210    }
9211
9212    /// Decodes an `I_8x8` macroblock (High profile): four 8×8 luma blocks, each
9213    /// with its own intra mode, 8×8 transform residual (CAVLC = four interleaved
9214    /// 4×4 blocks), and 8×8 intra prediction.
9215    fn decode_i8x8(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
9216        let w4 = self.mb_w * 4;
9217        if let Some(p) = self.mb_t8x8.get_mut(mb_y * self.mb_w + mb_x) {
9218            *p = true;
9219        }
9220        self.any_t8 = true;
9221
9222        // intra8x8 mode signalling — one mode per 8×8 block (raster 0..3),
9223        // stored into all four of its 4×4 cells so neighbors can read it.
9224        let mut modes8 = [2u8; 4];
9225        for (b8, mode) in modes8.iter_mut().enumerate() {
9226            let (b8x, b8y) = (b8 % 2, b8 / 2);
9227            let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
9228            let predicted = self.predict_i4_mode(bx, by);
9229            let actual = if r.read_bit()? {
9230                predicted
9231            } else {
9232                let rem = r.read_bits(3)? as u8;
9233                if rem < predicted {
9234                    rem
9235                } else {
9236                    rem + 1
9237                }
9238            };
9239            *mode = actual;
9240            for sy in 0..2 {
9241                // Row fill: the 2x2 cell block is two contiguous PAIRS.
9242                self.modes_y[(by + sy) * w4 + bx..][..2].fill(actual);
9243            }
9244        }
9245
9246        let chroma_mode = r.read_ue()? as u8;
9247        let cbp = read_cbp_intra(r)?;
9248        let cbp_luma = cbp & 15;
9249        let cbp_chroma = cbp >> 4;
9250        if cbp != 0 {
9251            self.step_qp(r.read_se()?)?;
9252        }
9253        let qp = self.cur_qp;
9254
9255        let top_mb_avail = mb_y > 0
9256            && self.nbr_in_slice(mb_x, mb_y - 1)
9257            && self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
9258        let left_mb_avail = mb_x > 0
9259            && self.nbr_in_slice(mb_x - 1, mb_y)
9260            && self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
9261        self.nnz_cache_load(mb_x, mb_y);
9262
9263        let mut nnz_raster = [0u8; 16];
9264        for b8 in 0..4 {
9265            let (b8x, b8y) = (b8 % 2, b8 / 2);
9266            let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
9267
9268            // residual: 8×8 CAVLC = four 4×4 sub-blocks, coeff k of sub-block s
9269            // mapping to 8×8 scan position 4·k + s (spec §7.3.5.3.2).
9270            let mut scan8 = [0i32; 64];
9271            let coded8 = cbp_luma & (1 << b8) != 0;
9272            if coded8 {
9273                for sub in 0..4 {
9274                    let (sx, sy) = (sub % 2, sub / 2);
9275                    let (cx, cy) = (b8x * 2 + sx, b8y * 2 + sy);
9276                    let nc = self.nc_pred(cx, cy);
9277                    let mut blk = [0i32; 16];
9278                    let total = decode_residual_block_into::<16, 16>(r, nc, &mut blk)?;
9279                    self.nnz_cache_set(cx, cy, total);
9280                    // Deferred to one row copy per MB row after the loop (`recon_i8_block`
9281                    // gathers from `coded_y` / `rec_y`, never `nnz_y`) -- was a checked store per sub.
9282                    nnz_raster[(cy & 3) * 4 + (cx & 3)] = total;
9283                    for k in 0..16 {
9284                        scan8[4 * k + sub] = blk[k];
9285                    }
9286                }
9287            } else {
9288                for sub in 0..4 {
9289                    let (sx, sy) = (sub % 2, sub / 2);
9290                    self.nnz_cache_set(b8x * 2 + sx, b8y * 2 + sy, 0);
9291                    // `nnz_raster` is already zero here.
9292                }
9293            }
9294
9295            let avail_top = b8y > 0 || top_mb_avail;
9296            let avail_left = b8x > 0 || left_mb_avail;
9297            self.recon_i8_block(
9298                bx,
9299                by,
9300                modes8[b8],
9301                avail_top,
9302                avail_left,
9303                coded8.then_some(&scan8),
9304                qp,
9305            );
9306            for sy in 0..2 {
9307                // Row fill: the 2x2 cell block is two contiguous PAIRS.
9308                self.coded_y[(by + sy) * w4 + bx..][..2].fill(true);
9309            }
9310        }
9311
9312        // ONE contiguous copy per macroblock row (see the I4x4 arm).
9313        for ry in 0..4usize {
9314            let a = (mb_y * 4 + ry) * w4 + mb_x * 4;
9315            self.nnz_y[a..a + 4].copy_from_slice(&nnz_raster[ry * 4..ry * 4 + 4]);
9316        }
9317
9318        self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
9319    }
9320
9321    /// Dequantizes + inverse-transforms an 8×8 luma block, applying the scaling
9322    /// matrix `list` (0 = intra, 1 = inter) or flat weights.
9323    fn inv_quant8(&self, raster: &[i32; 64], qp: u8, list: usize) -> [i32; 64] {
9324        // Prebuilt per-(qp, list) constants: the flat table at zero cost, or one
9325        // 64-entry build per macroblock for scaling lists (was 64 x two lookups +
9326        // a multiply per coefficient on every block).
9327        match &self.scaling8 {
9328            Some(s) => inverse_quant_8x8_dq(raster, &Dequant8Qp::build(qp, &s[list])),
9329            None => inverse_quant_8x8_dq(raster, &DQ8_FLAT[(qp as usize).min(51)]),
9330        }
9331    }
9332
9333    /// Gathers the 8×8 luma intra reference samples at pixel `(px, py)`: the 16
9334    /// top samples (8..15 substituted from the last when no top-right), 8 left
9335    /// samples, the above-left corner, and whether the corner is available.
9336    #[allow(clippy::too_many_arguments)]
9337    fn gather_i8(
9338        &self,
9339        px: usize,
9340        py: usize,
9341        avail_top: bool,
9342        avail_left: bool,
9343        bx: usize,
9344        by: usize,
9345    ) -> ([u8; 16], [u8; 8], u8, bool) {
9346        let (cw, w4) = (self.cw, self.mb_w * 4);
9347        let mut top = [0u8; 16];
9348        let mut left = [0u8; 8];
9349        let mut corner = 0;
9350        if avail_top {
9351            // Row-slice loads: one source branch per segment, not per pixel.
9352            top[..8].copy_from_slice(self.top_y_row(py, px, 8));
9353            let tr_avail = bx + 2 < w4
9354                && self
9355                    .coded_y
9356                    .get((by - 1) * w4 + (bx + 2))
9357                    .copied()
9358                    .unwrap_or(false)
9359                && self.nbr_in_slice((bx + 2) / 4, (by - 1) / 4)
9360                && self.intra_nbr_ok(bx + 2, by - 1);
9361            if tr_avail {
9362                top[8..16].copy_from_slice(self.top_y_row(py, px + 8, 8));
9363            } else {
9364                let t7 = top[7];
9365                top[8..16].fill(t7);
9366            }
9367        }
9368        if avail_left {
9369            // STRIDED WALK, NOT STRIDED INDEXING. A span plus `col[i * cw]` still
9370            // checks, because `i * cw <= 7 * cw` is not something LLVM derives
9371            // against the span's own length; `step_by` on the iterator does the
9372            // stride with no index at all, and `zip` bounds the count.
9373            let base = py * cw + px - 1;
9374            for (s, &v) in left.iter_mut().zip(self.rec_y[base..].iter().step_by(cw)) {
9375                *s = v;
9376            }
9377        }
9378        let avail_corner = avail_top && avail_left && self.intra_nbr_ok(bx - 1, by - 1);
9379        if avail_corner {
9380            corner = self.top_y_px(py, px - 1);
9381        }
9382        (top, left, corner, avail_corner)
9383    }
9384
9385    fn decode_i16(
9386        &mut self,
9387        r: &mut BitReader,
9388        mb_x: usize,
9389        mb_y: usize,
9390        mt: u32,
9391    ) -> Result<(), MbError> {
9392        let pred_mode = I16Mode::from_id(mt % 4);
9393        let cbp_chroma = (mt % 12) / 4;
9394        let cbp_luma_15 = mt / 12 == 1;
9395        let chroma_mode = r.read_ue()? as u8;
9396        self.step_qp(r.read_se()?)?;
9397        let qp = self.cur_qp;
9398        let w4 = self.mb_w * 4;
9399
9400        // luma DC
9401        self.nnz_cache_load(mb_x, mb_y);
9402        let nc_dc = self.nc_pred(0, 0);
9403        let mut dc_scan = [0i32; 16];
9404        decode_residual_block_into::<16, 16>(r, nc_dc, &mut dc_scan)?;
9405        let recon_dc =
9406            inverse_quant_luma_dc_scan(&dc_scan, qp, self.scaling.as_ref().map(|s| s[0][0]));
9407
9408        // luma AC (nnz set for all 16 blocks: 0 when DC-only, matching the encoder)
9409        let mut nnz_raster = [0u8; 16];
9410        let mut q_blocks = [[0i32; 16]; 16];
9411        for &(bx, by) in &LUMA_4X4_SCAN_XY {
9412            let total = if cbp_luma_15 {
9413                let nc = self.nc_pred(bx, by);
9414                let mut ac = [0i32; 16];
9415                let t = decode_residual_block_into::<15, 16>(r, nc, &mut ac)?;
9416                // Zero-skip: an empty AC block leaves the fresh-zero raster
9417                // block untouched (un-scanning 16 zeros wrote zeros on zeros).
9418                if t != 0 {
9419                    q_blocks[(by & 3) * 4 + (bx & 3)] = ac; // SCAN order (fused kernel)
9420                }
9421                t
9422            } else {
9423                0
9424            };
9425            self.nnz_cache_set(bx, by, total);
9426            nnz_raster[(by & 3) * 4 + (bx & 3)] = total;
9427        }
9428        let coded16 = nnz_raster
9429            .iter()
9430            .enumerate()
9431            .fold(0u16, |m, (i, &n)| m | (((n != 0) as u16) << i));
9432        // ONE contiguous copy per row (nothing reads `nnz_y` for this
9433        // macroblock in between - `nc_pred` predicts from `nnz_cache`).
9434        for by in 0..4usize {
9435            let a = (mb_y * 4 + by) * w4 + mb_x * 4;
9436            self.nnz_y[a..a + 4].copy_from_slice(&nnz_raster[by * 4..by * 4 + 4]);
9437        }
9438
9439        // prediction + reconstruction
9440        let avail_top = mb_y > 0
9441            && self.nbr_in_slice(mb_x, mb_y - 1)
9442            && self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
9443        let avail_left = mb_x > 0
9444            && self.nbr_in_slice(mb_x - 1, mb_y)
9445            && self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
9446        self.recon_i16_luma(
9447            mb_x,
9448            mb_y,
9449            pred_mode,
9450            avail_top,
9451            avail_left,
9452            Some(&q_blocks),
9453            coded16,
9454            &recon_dc,
9455            qp,
9456        );
9457        // I_16x16 blocks are treated as DC for neighbor mode prediction.
9458        for lby in 0..4usize {
9459            let a = (mb_y * 4 + lby) * w4 + mb_x * 4;
9460            self.modes_y[a..a + 4].fill(2);
9461        }
9462
9463        self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
9464    }
9465
9466    /// Reads and reconstructs the chroma residual (shared by both luma types).
9467    fn decode_chroma(
9468        &mut self,
9469        r: &mut BitReader,
9470        mb_x: usize,
9471        mb_y: usize,
9472        cbp_chroma: u32,
9473        chroma_mode: u8,
9474    ) -> Result<(), MbError> {
9475        let qpc = self.chroma_qp_for(self.cur_qp);
9476        let avail_top = mb_y > 0
9477            && self.nbr_in_slice(mb_x, mb_y - 1)
9478            && self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
9479        let avail_left = mb_x > 0
9480            && self.nbr_in_slice(mb_x - 1, mb_y)
9481            && self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
9482
9483        let mut c_recon_dc = [[0i32; 4]; 2];
9484        if cbp_chroma != 0 {
9485            for (c, slot) in c_recon_dc.iter_mut().enumerate() {
9486                let mut dc = [0i32; 4];
9487                decode_residual_block_into::<4, 4>(r, -1, &mut dc)?;
9488                *slot = self.dequant_chroma_dc(&dc, qpc, 1 + c);
9489            }
9490        }
9491        let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
9492        let mut ccoded = [0u8; 2];
9493        if cbp_chroma == 2 {
9494            self.chroma_cache_load(mb_x, mb_y);
9495            let w2 = self.mb_w * 2;
9496            for c in 0..2 {
9497                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
9498                    let nc = self.chroma_nc_pred(c, bx, by);
9499                    let mut ac = [0i32; 16];
9500                    let total = decode_residual_block_into::<15, 16>(r, nc, &mut ac)?;
9501                    self.chroma_nnz_cache_set(c, bx, by, total);
9502                    if let Some(n) =
9503                        self.nnz_c[c & 1].get_mut((mb_y * 2 + by) * w2 + (mb_x * 2 + bx))
9504                    {
9505                        *n = total;
9506                    }
9507                    // Zero-skip: empty AC leaves the fresh-zero raster block.
9508                    if total != 0 {
9509                        ccoded[c & 1] |= 1u8 << ((by * 2 + bx) & 3);
9510                        // WIN: c < 2, by < 2, bx < 2, so both indexes are in range and
9511                        // the masks PROVE it -- the ccoded[c & 1] |= 1 << ((by*2+bx) & 3)
9512                        // on the line above already uses exactly these. This was the last
9513                        // panic_bounds_check on the decoder hot path.
9514                        c_q_blocks[c & 1][(by * 2 + bx) & 3] = ac; // SCAN order (fused kernel)
9515                    }
9516                }
9517            }
9518        }
9519        self.recon_chroma_blocks(
9520            mb_x,
9521            mb_y,
9522            chroma_mode,
9523            avail_top,
9524            avail_left,
9525            &c_q_blocks,
9526            ccoded,
9527            &c_recon_dc,
9528            qpc,
9529        );
9530        Ok(())
9531    }
9532
9533    /// Applies the in-loop deblocking filter to the reconstructed frame, with
9534    /// the slice's `FilterOffsetA`/`FilterOffsetB` (each = the coded `*_div2`
9535    /// value × 2).
9536    /// Per-frame per-MB dump for conformance bisection, keyed on `RH264_DUMP_MB`.
9537    /// Prints one char per macroblock: `i` = intra, otherwise the List-0 reference
9538    /// index of the MB's top-left 4x4 block. Directly comparable with ffmpeg's
9539    /// `-debug mb_type` map, which is the only per-MB ground truth we can get out
9540    /// of the reference decoder.
9541    fn dump_mb_map(&self) {
9542        if !dump_mb_on() {
9543            return;
9544        }
9545        let w4 = self.mb_w * 4;
9546        let mut hist = [0usize; 4];
9547        eprintln!("--- frame poc {} ---", self.cur_poc);
9548        for mb_y in 0..self.mb_h {
9549            let mut row = String::new();
9550            for mb_x in 0..self.mb_w {
9551                let b = (mb_y * 4) * w4 + mb_x * 4;
9552                let Some(&r) = self.ref_idx_y.get(b) else {
9553                    continue;
9554                };
9555                if r < 0 {
9556                    row.push('i');
9557                } else {
9558                    if (r as usize) < 4 {
9559                        hist[r as usize] += 1;
9560                    }
9561                    row.push((b'0' + (r as u8).min(9)) as char);
9562                }
9563            }
9564            eprintln!("{row}");
9565        }
9566        eprintln!(
9567            "ref histogram: {hist:?}   num_ref_active={} refs.len()={}   OUT-OF-RANGE={}",
9568            self.num_ref_active,
9569            self.refs.len(),
9570            hist.iter().skip(self.refs.len()).sum::<usize>()
9571        );
9572        let list: Vec<String> = self
9573            .refs
9574            .iter()
9575            .enumerate()
9576            .map(|(i, f)| {
9577                // A synthesized frame_num-gap frame is uniform grey with w4 == 0;
9578                // flag it, because it silently displaces real pictures in the list.
9579                let synth = if f.w4 == 0 { " SYNTH-GREY" } else { "" };
9580                format!("[{i}] poc={} fn={}{synth}", f.poc, f.frame_num)
9581            })
9582            .collect();
9583        eprintln!("  RefPicList0: {}", list.join("  "));
9584    }
9585
9586    pub fn deblock(&mut self, offset_a: i32, offset_b: i32) {
9587        self.span_flush(); // deferred grid spans feed bS derivation below
9588        self.edc_flush(); // backstop: no pixel job may survive to filtering
9589        self.dump_mb_map();
9590        // ROW MODE: finish any rows not derived during decode (mid-row slice
9591        // ends, error paths) FIRST, while `self` is still mutably borrowable.
9592        if rowdb_on() {
9593            while self.bs_rows < self.mb_h {
9594                let r = self.bs_rows;
9595                self.derive_bs_row(r);
9596                self.bs_rows += 1;
9597            }
9598        }
9599        // Deblock boundary strength uses the *transform block's* coded status. For
9600        // an 8×8-transform macroblock the unit is the whole 8×8, so every 4×4 cell
9601        // shares the 8×8's coefficient presence (OR of its four sub-block counts)
9602        // — distinct from the per-sub-block `nnz_y` used for the CAVLC nC context.
9603        // Only differs from `nnz_y` when some MB uses the 8×8 transform (High
9604        // profile). On Baseline (no 8×8) it's identical — skip the clone + rewrite.
9605        // Row-interleave already derived bS into `bs_frame`; the filter then
9606        // reads only `bs`+`t8x8`+qp — do not clone nnz or rebuild POC maps.
9607        let rowdb = rowdb_on();
9608        let nnz_db_storage;
9609        let nnz_db: &[u8] = if rowdb {
9610            &[]
9611        } else if self.mb_t8x8.iter().any(|&t| t) {
9612            let mut n = self.nnz_y.clone();
9613            let w4 = self.mb_w * 4;
9614            for mb_y in 0..self.mb_h {
9615                for mb_x in 0..self.mb_w {
9616                    if !self
9617                        .mb_t8x8
9618                        .get(mb_y * self.mb_w + mb_x)
9619                        .copied()
9620                        .unwrap_or(false)
9621                    {
9622                        continue;
9623                    }
9624                    for b8 in 0..4 {
9625                        let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
9626                        let any = (0..2).any(|sy| {
9627                            self.nnz_y[(by + sy) * w4 + bx..][..2]
9628                                .iter()
9629                                .any(|&v| v > 0)
9630                        });
9631                        for sy in 0..2 {
9632                            // Row fill: the 2x2 cell block is two contiguous PAIRS.
9633                            n[(by + sy) * w4 + bx..][..2].fill(u8::from(any));
9634                        }
9635                    }
9636                }
9637            }
9638            nnz_db_storage = n;
9639            &nnz_db_storage
9640        } else {
9641            &self.nnz_y
9642        };
9643        let mut info = rusty_h264_common::deblock::BlockInfo {
9644            inter: if rowdb { &[] } else { &self.inter_y },
9645            nnz: nnz_db,
9646            mv: if rowdb { &[] } else { &self.mv_y },
9647            ref_id: if rowdb { &[] } else { &self.ref_idx_y },
9648            mv1: if rowdb { &[] } else { &self.mv1 },
9649            ref_id1: if rowdb || self.ref_poc1.is_empty() {
9650                &[]
9651            } else {
9652                &self.ref_idx1
9653            },
9654            w4: self.mb_w * 4,
9655            t8x8: &self.mb_t8x8,
9656            bs: &[],
9657            poc0: if rowdb { &[] } else { &self.ref_poc0 },
9658            poc1: if rowdb { &[] } else { &self.ref_poc1 },
9659            kind: &self.mb_kind,
9660        };
9661        // ROW MODE (R2): rows were derived during decode; the remainder was
9662        // finished above (before `info` borrowed the grids). Fallback: the
9663        // Part 16/17 picture-end precompute; `RS_H264_BS_PRE=0` further falls
9664        // back to the pack-then-derive-in-loop pipeline.
9665        let bs_store;
9666        if rowdb {
9667            bs_store = core::mem::take(&mut self.bs_frame);
9668            info.bs = &bs_store;
9669        } else if bs_pre_on() {
9670            let mut buf = Vec::new();
9671            rusty_h264_common::deblock::precompute_bs_frame(&info, self.mb_w, self.mb_h, &mut buf);
9672            bs_store = buf;
9673            info.bs = &bs_store;
9674        } else {
9675            bs_store = Vec::new();
9676        }
9677        let first_row = if rowdb { self.flt_rows } else { 0 };
9678        rusty_h264_common::deblock::filter_frame_rows_pre(
9679            &mut self.rec_y,
9680            &mut self.rec_u,
9681            &mut self.rec_v,
9682            self.mb_w,
9683            self.mb_h,
9684            first_row..self.mb_h,
9685            &self.mb_qp,
9686            self.chroma_qp_offset,
9687            offset_a,
9688            offset_b,
9689            &info,
9690        );
9691        drop(info);
9692        if rowdb {
9693            self.bs_frame = bs_store;
9694        }
9695    }
9696
9697    /// Crops the reconstructed coded-size planes to the display window.
9698    /// `into_frame`, additionally handing the per-picture grids back for reuse by
9699    /// the next picture. See `GridPool` for why this is worth doing.
9700    pub fn into_frame_recycle(mut self, crop_r: usize, crop_b: usize) -> (YuvFrame, GridPool) {
9701        let [c0, c1] = core::mem::take(&mut self.nnz_c);
9702        let pool = GridPool {
9703            job_pool: core::mem::take(&mut self.edc_job_pool),
9704            nores_pool: core::mem::take(&mut self.edc_nores_pool),
9705            bits_per_mb: self.bits_per_mb,
9706            mb_qp: core::mem::take(&mut self.mb_qp),
9707            bs_frame: core::mem::take(&mut self.bs_frame),
9708            pk_prev: core::mem::take(&mut self.pk_prev),
9709            pk_cur: core::mem::take(&mut self.pk_cur),
9710            nnz_dbr: core::mem::take(&mut self.nnz_dbr),
9711            bak_y: core::mem::take(&mut self.bak_y),
9712            bak_u: core::mem::take(&mut self.bak_u),
9713            bak_v: core::mem::take(&mut self.bak_v),
9714            nnz_y: core::mem::take(&mut self.nnz_y),
9715            nnz_c0: c0,
9716            nnz_c1: c1,
9717            modes_y: core::mem::take(&mut self.modes_y),
9718            coded_y: core::mem::take(&mut self.coded_y),
9719            mv_y: core::mem::take(&mut self.mv_y),
9720            inter_y: core::mem::take(&mut self.inter_y),
9721            ref_idx_y: core::mem::take(&mut self.ref_idx_y),
9722            mv1: core::mem::take(&mut self.mv1),
9723            ref_idx1: core::mem::take(&mut self.ref_idx1),
9724            mb_t8x8: core::mem::take(&mut self.mb_t8x8),
9725            mb_kind: core::mem::take(&mut self.mb_kind),
9726            bzero: core::mem::take(&mut self.bzero),
9727            sc_cat: core::mem::take(&mut self.sc_cat),
9728            sc_cbp: core::mem::take(&mut self.sc_cbp),
9729            sc_cmode: core::mem::take(&mut self.sc_cmode),
9730            sc_nzc: core::mem::take(&mut self.sc_nzc),
9731            sc_cbfdc: core::mem::take(&mut self.sc_cbfdc),
9732            sc_skip: core::mem::take(&mut self.sc_skip),
9733            sc_ref: core::mem::take(&mut self.sc_ref),
9734            sc_mvd: core::mem::take(&mut self.sc_mvd),
9735            sc_ref1: core::mem::take(&mut self.sc_ref1),
9736            sc_mvd1: core::mem::take(&mut self.sc_mvd1),
9737            sc_direct: core::mem::take(&mut self.sc_direct),
9738            ref_poc0: core::mem::take(&mut self.ref_poc0),
9739            ref_poc1: core::mem::take(&mut self.ref_poc1),
9740        };
9741        (self.into_frame(crop_r, crop_b), pool)
9742    }
9743
9744    pub fn into_frame(self, crop_r: usize, crop_b: usize) -> YuvFrame {
9745        // No cropping (the common case): the reconstruction planes ARE the output —
9746        // move them out instead of allocating + copying three full planes per frame.
9747        if crop_r == 0 && crop_b == 0 {
9748            return YuvFrame {
9749                width: self.cw,
9750                height: self.ch,
9751                y: self.rec_y,
9752                u: self.rec_u,
9753                v: self.rec_v,
9754            };
9755        }
9756        let dw = self.cw - 2 * crop_r;
9757        let dh = self.ch - 2 * crop_b;
9758        let mut y = vec![0u8; dw * dh];
9759        for row in 0..dh {
9760            y[row * dw..row * dw + dw]
9761                .copy_from_slice(&self.rec_y[row * self.cw..row * self.cw + dw]);
9762        }
9763        let (cdw, cdh) = (dw / 2, dh / 2);
9764        let mut u = vec![0u8; cdw * cdh];
9765        let mut v = vec![0u8; cdw * cdh];
9766        for row in 0..cdh {
9767            u[row * cdw..row * cdw + cdw]
9768                .copy_from_slice(&self.rec_u[row * self.ccw..row * self.ccw + cdw]);
9769            v[row * cdw..row * cdw + cdw]
9770                .copy_from_slice(&self.rec_v[row * self.ccw..row * self.ccw + cdw]);
9771        }
9772        let _ = self.cch;
9773        YuvFrame {
9774            width: dw,
9775            height: dh,
9776            y,
9777            u,
9778            v,
9779        }
9780    }
9781}
9782
9783/// Reads `ref_idx_l0` as `te(v)` with range `num_ref_active - 1`: a single flag
9784/// when exactly two references are active (cMax == 1), else `ue(v)`.
9785// ---- CABAC binarization engine helpers (openh264 cabac_decoder.cpp) ----
9786
9787/// Unary bin (`DecodeUnaryBinCabac`): bin0 at `ctx`; if 1, count bins at `ctx+off`
9788/// (including the terminating 0) until a 0.
9789/// CAVLC `mvd_lX` with a sanity bound. `se(v)` can legally code ±2^31-1, but
9790/// every profile/level caps |MV| far below ±2^17 quarter-pel units; beyond
9791/// that is a corrupt stream, and the unchecked `pmv + mvd` addition would
9792/// overflow i32 (debug panic / release wrap into an absurd vector).
9793fn read_mvd(r: &mut BitReader) -> Result<i32, MbError> {
9794    let v = r.read_se()?;
9795    if v.unsigned_abs() > (1 << 17) {
9796        return Err(MbError::Truncated);
9797    }
9798    Ok(v)
9799}
9800
9801type Eng = crate::cabac::Engine;
9802type Ctx = crate::cabac::Ctx;
9803
9804fn cabac_unary(e: &mut Eng, data: &[u8], ctx: &mut Ctx, c: usize, off: usize) -> u32 {
9805    if e.decode_decision(data, ctx, c) == 0 {
9806        return 0;
9807    }
9808    let mut sym = 0;
9809    loop {
9810        let bin = e.decode_decision(data, ctx, c + off);
9811        sym += 1;
9812        // Cap the unary run: no valid H.264 element coded through this helper
9813        // (mb_qp_delta) exceeds a few dozen bins, but on malformed / buffer-exhausted
9814        // input the arithmetic engine keeps yielding 1s (it zero-fills past the end),
9815        // which would loop forever. 512 is far beyond any legal value.
9816        if bin == 0 || sym >= 512 {
9817            break;
9818        }
9819    }
9820    sym
9821}
9822
9823/// k-th order Exp-Golomb in bypass (`DecodeExpBypassCabac`). Out of line (it is the
9824/// rare escape arm of levels and mvds), so it takes the engine BY VALUE and hands
9825/// it back: a `&mut Engine` here would be the one address escape in the hot
9826/// callers and LLVM would then home their engine copy on the stack for EVERY bin.
9827#[inline(never)]
9828fn cabac_exp_bypass(mut e: Eng, data: &[u8], mut count: i32) -> (u32, Eng) {
9829    let mut sym = 0u32;
9830    loop {
9831        let c = e.decode_bypass(data);
9832        if c == 1 {
9833            sym += 1 << count;
9834            count += 1;
9835        }
9836        if c == 0 || count == 16 {
9837            break;
9838        }
9839    }
9840    let mut sym2 = 0u32;
9841    while count > 0 {
9842        count -= 1;
9843        if e.decode_bypass(data) != 0 {
9844            sym2 |= 1 << count;
9845        }
9846    }
9847    (sym + sym2, e)
9848}
9849
9850/// UEG0 coeff-level suffix (`DecodeUEGLevelCabac`): TU prefix at `c` (<=13) then an
9851/// EG0 bypass suffix. INLINED ALWAYS into the four residual-parser instantiations:
9852/// out of line it took `&mut Engine`, which put the engine back in memory for
9853/// every level >= 2 and spilled the caller around the call.
9854#[inline(always)]
9855fn cabac_ueg_level(e: &mut Eng, data: &[u8], ctx: &mut Ctx, c: usize) -> u32 {
9856    if e.decode_decision(data, ctx, c) == 0 {
9857        return 0;
9858    }
9859    let mut code = 0u32;
9860    let mut tmp;
9861    loop {
9862        tmp = e.decode_decision(data, ctx, c);
9863        code += 1;
9864        if tmp == 0 || code == 12 {
9865            break;
9866        }
9867    }
9868    if tmp != 0 {
9869        let (v, e2) = cabac_exp_bypass(*e, data, 0);
9870        *e = e2;
9871        code += v + 1;
9872    }
9873    code
9874}
9875
9876/// `mb_qp_delta` CABAC (`ParseDeltaQpCabac`): ctxIdxOffset 60, ctxInc = (prev delta != 0).
9877pub fn parse_mb_qp_delta_cabac(cab: &mut crate::cabac::Cabac, last_delta_qp: &mut i32) -> i32 {
9878    const O: usize = 60;
9879    let ctx_inc = (*last_delta_qp != 0) as usize;
9880    let mut qp_delta = 0;
9881    let (data, mut e, ctx) = cab.view();
9882    if e.decode_decision(data, ctx, O + ctx_inc) != 0 {
9883        let code = cabac_unary(&mut e, data, ctx, O + 2, 1) + 1;
9884        qp_delta = ((code + 1) >> 1) as i32;
9885        if code & 1 == 0 {
9886            qp_delta = -qp_delta;
9887        }
9888    }
9889    cab.commit(e);
9890    *last_delta_qp = qp_delta;
9891    qp_delta
9892}
9893
9894// Shared CABAC residual glue tables (NZC_CACHE, RES_*) now live in
9895// `cabac_tables` — both coders read the ONE copy.
9896use rusty_h264_common::cabac_tables::{
9897    NZC_CACHE, RES_CBF, RES_MAP, RES_MAXC2, RES_MAXPOS, RES_ONE,
9898};
9899// res-property values (post GetMbResProperty, CABAC): the ctx-table index.
9900const RP_I16_DC: usize = 1;
9901const RP_I16_AC: usize = 2;
9902const RP_LUMA_4X4: usize = 3;
9903const RP_CHROMA_DC: usize = 7; // U (V=8, same offsets)
9904const RP_CHROMA_AC: usize = 9; // U (V=10, same offsets)
9905/// Luma 8×8 (ctxBlockCat 5). Its RES_MAP/RES_CBF entries stay 0: cat 5 does NOT
9906/// share the `105 + off` / `166 + off` context bases the 4×4 categories use — it
9907/// has its own absolute bases (402 sig, 417 last) and its own per-position
9908/// ctxIdxInc maps below. RES_ONE[6] = 199 IS used, because 227 + 199 = 426 and
9909/// 232 + 199 = 431 reproduce the spec's coeff_abs_level_minus1 base exactly, so
9910/// the level loop needs no special case at all.
9911const RP_LUMA_8X8: usize = 6;
9912
9913// SIG8X8 / LAST8X8 moved to `rusty_h264_common::cabac_tables` (R6-1) so the encoder's
9914// ctxBlockCat 5 writer shares the exact spec data this reader is validated against.
9915use rusty_h264_common::cabac_tables::{LAST8X8, SIG8X8};
9916
9917/// One residual block (openh264 `ParseResidualBlockCabac`), generic over the 5 CABAC
9918/// block categories. `rp` selects the context offsets. DC categories (I16 luma DC,
9919/// chroma DC) take the cbf context from the per-MB `cbf_dc` bitmask + neighbour MB DC
9920/// cbf; AC categories from the padded nzc cache. Returns totalCoeffNum.
9921#[allow(clippy::too_many_arguments)]
9922#[inline(always)]
9923fn residual_block_eng<const RP: usize, const N: usize>(
9924    e: &mut Eng,
9925    data: &[u8],
9926    ctx: &mut Ctx,
9927    nzc: &mut [u8; 48],
9928    cbf_dc: &mut u16,
9929    iz: usize,
9930    plane: usize,
9931    is_intra: bool,
9932    ndc: (u16, u16),    // resolved neighbour DC cbf words (see `resolve_ndc`)
9933    out: &mut [i32; N], // scan-order coefficients written here (fresh-zero on entry)
9934) -> u32 {
9935    // CONST-GENERIC over the block category (`RP`, one of the RP_* literals every
9936    // call site already passed) and the block length (`N` = 4 / 16 / 64): the
9937    // category tests, the five `RES_*` table reads, the 8x8 map selects and the
9938    // output bound all fold at compile time. Chroma U/V share every table
9939    // entry (RES_*[7] == RES_*[8], RES_*[9] == RES_*[10]); only the cbf_dc bit
9940    // differs, so the plane rides in `plane` (0 for luma categories).
9941    const { assert!(N == 4 || N == 16 || N == 64) };
9942    // The CABAC residual parse IS the entropy stage of the decoder on Main-profile
9943    // streams -- it was invisible (a ~47% residue) until this scope named it.
9944    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Entropy);
9945    // ---- coded_block_flag ----
9946    // ctxBlockCat 5 is the ONLY category with no coded_block_flag: its presence is
9947    // inferred from CodedBlockPatternLuma, so parsing one here would desync.
9948    let is8 = RP == RP_LUMA_8X8;
9949    let is_dc = RP == RP_I16_DC || RP == RP_CHROMA_DC;
9950    let bit = RP + plane;
9951    let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
9952    // `nzc` is [u8; 48] and every `NZC_CACHE` entry lies in [9, 47], so this
9953    // clamp is a semantic no-op that hands LLVM BOTH bounds - which is what
9954    // makes `nzc[scan]`, `nzc[scan - 8]` and `nzc[scan - 1]` below provable
9955    // instead of three bounds checks. Same trick as `parse_mvd_partition`.
9956    let scan = NZC_CACHE[iz.min(23)].clamp(8, 47);
9957    if is_dc {
9958        nb = ((ndc.0 >> bit) & 1) as u8;
9959        na = ((ndc.1 >> bit) & 1) as u8;
9960    } else {
9961        let (nbc, nac) = (nzc[scan - 8], nzc[scan - 1]);
9962        if nbc != 0xff {
9963            nb = (nbc != 0) as u8;
9964        }
9965        if nac != 0xff {
9966            na = (nac != 0) as u8;
9967        }
9968    }
9969    if !is8 {
9970        let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntCbf);
9971        let cbf = e.decode_decision(data, ctx, 85 + RES_CBF[RP] + (na + (nb << 1)) as usize);
9972        if cbf == 0 {
9973            if !is_dc {
9974                nzc[scan] = 0;
9975            }
9976            return 0;
9977        }
9978        if is_dc {
9979            *cbf_dc |= 1 << bit;
9980        }
9981    }
9982    // ---- significance map ----
9983    let maxpos = RES_MAXPOS[RP] as usize;
9984    debug_assert!(maxpos < N);
9985    // cat 5 uses its own absolute bases; the 4x4 categories share 105/166 + offset.
9986    let (map, last) = if is8 {
9987        (402, 417)
9988    } else {
9989        let m = RES_MAP[RP];
9990        (105 + m, 166 + m)
9991    };
9992    // SPARSE significance map: record each significant POSITION in `pos[..n]`.
9993    // Bin ORDER is unchanged: levels are decoded at descending significant
9994    // positions, which is exactly `pos[..n]` reversed. `pos` is sized to the
9995    // block (N), not 64: a 4x4 block zeroes 16 bytes, not 64.
9996    //
9997    // CONTRACT with the callers (all 10 sites): `out` is freshly zeroed, so
9998    // writing only the significant entries leaves the same contents a dense
9999    // copy would produce. A reused non-zero `out` would be a correctness bug.
10000    // Significant positions as a BITMASK (bit i = scan position i): one OR per
10001    // significant coefficient instead of a store + count increment into a
10002    // zero-initialised array; the level loop walks the set bits from the top
10003    // (descending positions = the spec order).
10004    let mut sig = 0u64;
10005    let mut last_hit = false;
10006    let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntSig);
10007    // 4x4: ctxIdxInc IS the scan position. 8x8: it comes from the folded maps.
10008    // NOTE (4:2:2 landmine): `(i, i)` is correct for every 4:2:0 category
10009    // only because chroma-DC (cat 3) has NumC8x8 == 1 here; spec 9.3.3.1.3
10010    // wants `Min(i / NumC8x8, 2)` for its sig/last ctxIdxInc, which
10011    // diverges the day 4:2:2 (NumC8x8 == 2) is admitted.
10012    for i in 0..maxpos {
10013        // Both maps are [u8; 64] and `i < maxpos <= 63`: `& 63` folds the checks.
10014        let (mi, li) = if is8 {
10015            (
10016                (SIG8X8[i & 63] & 15) as usize,
10017                (LAST8X8[i & 63] & 15) as usize,
10018            )
10019        } else {
10020            (i, i)
10021        };
10022        if e.decode_decision(data, ctx, map + mi) != 0 {
10023            // `n <= maxpos < N` at every write: `& (N - 1)` is the own bound of `pos`.
10024            sig |= 1u64 << i;
10025            if e.decode_decision(data, ctx, last + li) != 0 {
10026                last_hit = true;
10027                break;
10028            }
10029        }
10030    }
10031    if !last_hit {
10032        sig |= 1u64 << maxpos;
10033    }
10034    let coeff_num = sig.count_ones();
10035    // ---- levels ----
10036    let one = 227 + RES_ONE[RP];
10037    let abs = one + 5;
10038    // `usize` with visible `min` bounds: as i32 the casts hid the ranges and every
10039    // `one + c1` / `abs + c2` context index kept its `& 511` (one op per bin).
10040    let maxc2 = RES_MAXC2[RP] as usize;
10041    let (mut c1, mut c2) = (1usize, 0usize);
10042    drop(_sg);
10043    let _lg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntLvl);
10044    let mut m = sig;
10045    while m != 0 {
10046        let i = 63 - m.leading_zeros() as usize;
10047        m &= !(1u64 << i);
10048        let mut level = 1 + e.decode_decision(data, ctx, one + c1) as i32;
10049        if level == 2 {
10050            level += cabac_ueg_level(e, data, ctx, abs + c2) as i32;
10051            c2 = (c2 + 1).min(maxc2);
10052            c1 = 0;
10053        } else if c1 != 0 {
10054            c1 = (c1 + 1).min(4);
10055        }
10056        if e.decode_bypass_sign() != 0 {
10057            level = -level;
10058        }
10059        // `out` is `[i32; N]` and every recorded position is <= maxpos < N, so
10060        // `& (N - 1)` is the own bound of the array -- a proof, not a relocation
10061        // (the runtime-length-slice form this replaces needed `.get_mut`).
10062        out[i & (N - 1)] = level;
10063    }
10064    if is8 {
10065        // One 8x8 covers four consecutive z-order 4x4 cells. Every later
10066        // coded_block_flag ctxIdxInc reads this cache, so all four must carry the
10067        // count -- writing only `scan` would corrupt the contexts of the NEXT
10068        // macroblock.
10069        let cn = coeff_num as u8;
10070        for k in 0..4 {
10071            nzc[NZC_CACHE[(iz + k).min(23)].min(47)] = cn;
10072        }
10073    } else if !is_dc {
10074        nzc[scan] = coeff_num as u8;
10075    }
10076    coeff_num
10077}
10078
10079/// Where a macroblock residual parse writes: plain arrays whose UNCODED blocks
10080/// are stale by contract (every consumer reads a block only when its `nnzs`
10081/// entry is nonzero, or copies the prediction). The parse zeroes exactly the
10082/// blocks it is about to write.
10083struct ResidualOut<'a> {
10084    luma: &'a mut [[i32; 16]; 16],
10085    luma8: &'a mut [[i32; 64]; 4],
10086    cdc: &'a mut [[i32; 4]; 2],
10087    cac: &'a mut [[[i32; 16]; 4]; 2],
10088    /// Per-block totalCoeff, `iz`-indexed; must be zero on entry.
10089    nnzs: &'a mut [u8; 24],
10090}
10091
10092/// The neighbour-record form of a macroblock per-block coefficient counts:
10093/// luma in RASTER order (from the z-order `nnzs`, via G_SCAN4, which is its own
10094/// inverse) and chroma Cb/Cr in the interleaved slot order the readers expect.
10095/// Uncoded blocks are already 0 in `nnzs`, so the 0xff->0 scrub of the old
10096/// cache-based export is gone with the 24 scattered cache reads.
10097/// Explicit-weighted-prediction sample: `((s*w + round) >> denom) + o`, clipped.
10098#[inline(always)]
10099fn weight_sample(sample: u8, w: i32, o: i32, denom: i32) -> u8 {
10100    let v = if denom >= 1 {
10101        ((sample as i32 * w + (1 << (denom - 1))) >> denom) + o
10102    } else {
10103        sample as i32 * w + o
10104    };
10105    v.clamp(0, 255) as u8
10106}
10107
10108/// CONST-WIDTH rows of the weighted-prediction loop (SIMD census 2026-09-05,
10109/// finding #9). The two recon twins carried the IDENTICAL runtime-width loop
10110/// (`for dx in 0..rw` over a masked index) and LLVM vectorised it to 500 packed
10111/// ops in one inlining context and 53 in the other. A partition width is one
10112/// of 16/8/4 (chroma 8/4/2); handing LLVM the trip count makes both contexts
10113/// vectorise the same way. One row slice per row instead of a masked index per
10114/// sample; the sample formula is unchanged.
10115#[inline(always)]
10116fn weight_rows<const W: usize>(
10117    plane: &mut [u8],
10118    stride: usize,
10119    rx: usize,
10120    ry: usize,
10121    rh: usize,
10122    w: i32,
10123    o: i32,
10124    denom: i32,
10125) {
10126    for dy in 0..rh {
10127        let row = &mut plane[(ry + dy) * stride + rx..][..W];
10128        for s in row.iter_mut() {
10129            *s = weight_sample(*s, w, o, denom);
10130        }
10131    }
10132}
10133
10134#[allow(clippy::too_many_arguments)]
10135#[inline]
10136fn weight_block(
10137    plane: &mut [u8],
10138    stride: usize,
10139    rx: usize,
10140    ry: usize,
10141    rw: usize,
10142    rh: usize,
10143    w: i32,
10144    o: i32,
10145    denom: i32,
10146) {
10147    match rw {
10148        16 => weight_rows::<16>(plane, stride, rx, ry, rh, w, o, denom),
10149        8 => weight_rows::<8>(plane, stride, rx, ry, rh, w, o, denom),
10150        4 => weight_rows::<4>(plane, stride, rx, ry, rh, w, o, denom),
10151        2 => weight_rows::<2>(plane, stride, rx, ry, rh, w, o, denom),
10152        _ => {
10153            for dy in 0..rh {
10154                for dx in 0..rw {
10155                    let i = (ry + dy) * stride + rx + dx;
10156                    if let Some(s) = plane.get_mut(i) {
10157                        *s = weight_sample(*s, w, o, denom);
10158                    }
10159                }
10160            }
10161        }
10162    }
10163}
10164
10165/// Residual class of an intra 4x4 block (see `FrameDecoder::i4_prepare`).
10166#[derive(Clone, Copy)]
10167enum I4Res {
10168    Zero,
10169    Flat(i32),
10170    Idx(u8),
10171}
10172
10173/// z-order index of the 4x4 block at raster (x, y) -- inverse of `LUMA_4X4_SCAN_XY`.
10174const I4_Z_OF_XY: [[usize; 4]; 4] = {
10175    let mut t = [[0usize; 4]; 4];
10176    let mut i = 0;
10177    while i < 16 {
10178        let (x, y) = LUMA_4X4_SCAN_XY[i];
10179        t[y][x] = i;
10180        i += 1;
10181    }
10182    t
10183};
10184/// Bit i set iff the top-right 4x4 neighbour of z-order block i lies INSIDE the
10185/// macroblock and is already reconstructed when block i is (spec 6.4.11.4 via the
10186/// decode order): the classic i4x4 top-right availability table, here derived from
10187/// the scan table at compile time instead of hand-typed.
10188const I4_TR_IN_MB: u16 = {
10189    let mut m = 0u16;
10190    let mut i = 0;
10191    while i < 16 {
10192        let (x, y) = LUMA_4X4_SCAN_XY[i];
10193        if y > 0 && x < 3 && I4_Z_OF_XY[y - 1][x + 1] < i {
10194            m |= 1 << i;
10195        }
10196        i += 1;
10197    }
10198    m
10199};
10200
10201// SPARSE-vs-DENSE DEQUANT ROUTE (routing round C, 2026-09-05): the DQROUTE census
10202// (scatter = 37 + 6L + 9nnz vs dense-AVX2 ~60; crowd 134.8M -> 80.3M instrs / 60 f)
10203// retired the scan-walking scatter; every coded block now takes the FUSED
10204// scan-order kernel (`reconstruct_4x4_scan_into`: un-scan + dequant + IDCT + add).
10205// The (nnz, L) histogram tap `edcstat::dq_note` stays for the next route question.
10206
10207/// Neighbour DC coded_block_flags as plain bit words: an unavailable neighbour
10208/// contributes the intra default for EVERY category, so the per-block
10209/// `Option` test becomes one shift. Resolved once per macroblock.
10210#[inline(always)]
10211fn resolve_ndc(ndc: (Option<u16>, Option<u16>), is_intra: bool) -> (u16, u16) {
10212    let d = if is_intra { 0xFFFF } else { 0 };
10213    (ndc.0.unwrap_or(d), ndc.1.unwrap_or(d))
10214}
10215
10216/// The WHOLE residual of one non-I16 macroblock (luma 4x4 or 8x8 by cbp bit,
10217/// chroma DC, chroma AC) on ONE engine view. The block body is inlined here,
10218/// so a macroblock costs one call and one engine load/store, where each block
10219/// used to pay a call, eight arguments (five on the stack), an eight-register
10220/// prologue/epilogue and its own view/commit round trip -- ~40 instructions
10221/// per block beside the bins, on 7-24 blocks per coded macroblock.
10222#[allow(clippy::too_many_arguments)]
10223fn parse_mb_residual_cabac<const INTRA: bool>(
10224    cab: &mut crate::cabac::Cabac,
10225    nzc: &mut [u8; 48],
10226    cbf_dc: &mut u16,
10227    ndc: (Option<u16>, Option<u16>),
10228    cbp_luma: u32,
10229    cbp_chroma: u32,
10230    t8: bool,
10231    o: ResidualOut,
10232) {
10233    // INTRA is a const generic: the cbf defaults and the neighbour-word resolve
10234    // fold per instantiation (every call site passes a literal).
10235    let is_intra = INTRA;
10236    let nd = resolve_ndc(ndc, INTRA);
10237    let (data, mut e, ctx) = cab.view();
10238    for id8 in 0..4usize {
10239        if cbp_luma & (1 << id8) != 0 {
10240            if t8 {
10241                // ctxBlockCat 5: one 64-coefficient block per 8x8, no cbf. All
10242                // four nnzs slots carry the 8x8 total (the recon reads one per cell).
10243                o.luma8[id8] = [0i32; 64];
10244                let n = residual_block_eng::<RP_LUMA_8X8, 64>(
10245                    &mut e,
10246                    data,
10247                    ctx,
10248                    nzc,
10249                    cbf_dc,
10250                    id8 * 4,
10251                    0,
10252                    is_intra,
10253                    nd,
10254                    &mut o.luma8[id8],
10255                ) as u8;
10256                o.nnzs[id8 * 4..id8 * 4 + 4].fill(n);
10257            } else {
10258                o.luma[id8 * 4..id8 * 4 + 4].fill([0i32; 16]);
10259                for id4 in 0..4usize {
10260                    let iz = id8 * 4 + id4;
10261                    o.nnzs[iz] = residual_block_eng::<RP_LUMA_4X4, 16>(
10262                        &mut e,
10263                        data,
10264                        ctx,
10265                        nzc,
10266                        cbf_dc,
10267                        iz,
10268                        0,
10269                        is_intra,
10270                        nd,
10271                        &mut o.luma[iz],
10272                    ) as u8;
10273                }
10274            }
10275        } else {
10276            for k in 0..4 {
10277                nzc[NZC_CACHE[(id8 * 4 + k).min(23)].min(47)] = 0;
10278            }
10279        }
10280    }
10281    if cbp_chroma >= 1 {
10282        *o.cdc = [[0i32; 4]; 2];
10283        for i in 0..2usize {
10284            residual_block_eng::<RP_CHROMA_DC, 4>(
10285                &mut e,
10286                data,
10287                ctx,
10288                nzc,
10289                cbf_dc,
10290                16 + i * 4,
10291                i,
10292                is_intra,
10293                nd,
10294                &mut o.cdc[i],
10295            );
10296        }
10297    }
10298    if cbp_chroma == 2 {
10299        *o.cac = [[[0i32; 16]; 4]; 2];
10300        for i in 0..2usize {
10301            for id4 in 0..4usize {
10302                o.nnzs[16 + i * 4 + id4] = residual_block_eng::<RP_CHROMA_AC, 16>(
10303                    &mut e,
10304                    data,
10305                    ctx,
10306                    nzc,
10307                    cbf_dc,
10308                    16 + i * 4 + id4,
10309                    i,
10310                    is_intra,
10311                    nd,
10312                    &mut o.cac[i][id4],
10313                ) as u8;
10314            }
10315        }
10316    }
10317    cab.commit(e);
10318}
10319
10320// ============================================================================
10321// Entropy-decouple E2: the OWNED pixel context that crosses the thread
10322// boundary (docs/entropy-decouple-plan.md). The worker owns the planes, the
10323// backup rows, the DPB Arcs and its own qp/t8/bs grids (fed by Row messages);
10324// the parse thread keeps every syntax grid. The methods below are ports of
10325// the FrameDecoder pixel halves — grid writes removed (parse commits its own
10326// grids), motion carried in the job instead of re-gathered.
10327// ============================================================================
10328
10329/// The committed-value class of a deferred B_Skip span. Continuation
10330/// requires kind EQUALITY: the flush range-fills grids and recons the band
10331/// from these values alone.
10332#[derive(Clone, Copy, PartialEq, Eq)]
10333enum BzKind {
10334    /// ref 0 both lists, (0,0)/(0,0) — band = avg of both padded refs.
10335    ZeroBi,
10336    /// One active list at (0,0): (list, clamped ref index) — band = memcpy.
10337    ZeroUni(u8, u8),
10338    /// Uniform full-pel direct: clamped refs (-1 = inactive) + adjusted MVs,
10339    /// windows PREVALIDATED per MB at push (contiguous tiles ⇒ the union
10340    /// window is valid) — band = offset copy/avg. Chroma requires mv%8==0
10341    /// (also prevalidated).
10342    Fp {
10343        r0: i8,
10344        r1: i8,
10345        m0: (i16, i16),
10346        m1: (i16, i16),
10347    },
10348}
10349
10350/// Messages from the parse thread to the pixel worker.
10351enum EdcMsg {
10352    Job(EdcJob),
10353    /// A ROW's worth of pixel jobs in one message (D10).
10354    ///
10355    /// The seam sent ONE message per macroblock: 208k sends per 60-frame pass
10356    /// against 2,596 rows. The overhead is per-JOB (channel lock, park/unpark)
10357    /// while the prize is proportional to pixel WORK, so the per-job send was
10358    /// dividing the payoff by ~80 for nothing. Batching per row cuts the
10359    /// synchronisation events by that factor and moves not one byte of pixel
10360    /// work off the worker.
10361    ///
10362    /// ORDER IS THE CORRECTNESS CONDITION: the worker must see a row's jobs
10363    /// before that row's `Row` filter message, so the batch is flushed at every
10364    /// row boundary, before `NeedCtx`, and at slice end.
10365    Batch(Vec<EdcJob>),
10366    /// A macroblock row finished parsing: install its qp/t8/bs and filter it.
10367    Row {
10368        r: usize,
10369        bs: Vec<rusty_h264_common::deblock::MbBs>,
10370        qp: Vec<u8>,
10371        t8: Vec<bool>,
10372    },
10373    /// An intra macroblock needs the planes on the parse thread: send the
10374    /// context over and wait for it to come back.
10375    NeedCtx,
10376}
10377
10378pub(crate) struct PixelCtx {
10379    rec_y: Vec<u8>,
10380    rec_u: Vec<u8>,
10381    rec_v: Vec<u8>,
10382    bak_y: Vec<u8>,
10383    bak_u: Vec<u8>,
10384    bak_v: Vec<u8>,
10385    refs: Vec<crate::Ref>,
10386    refs1: Vec<crate::Ref>,
10387    weights: Option<WeightTable>,
10388    /// `weights.list_identity(0)`, cached - mirrors `FrameDecoder::weights_l0id`
10389    /// so the worker twin does not have to walk the table per macroblock.
10390    weights_l0id: bool,
10391    scaling: Option<[[i32; 16]; 6]>,
10392    scaling8: Option<[[i32; 64]; 2]>,
10393    cw: usize,
10394    ccw: usize,
10395    mb_w: usize,
10396    mb_h: usize,
10397    chroma_qp_offset: i32,
10398    flt_rows: usize,
10399    db_ena: bool,
10400    db_oa: i32,
10401    db_ob: i32,
10402    cur_qp: u8,
10403    qp_grid: Vec<u8>,
10404    t8_grid: Vec<bool>,
10405    bs_store: Vec<rusty_h264_common::deblock::MbBs>,
10406    /// Frame-MT Phase B progress Arc (row publish from the EDC worker).
10407    progress: Option<crate::Ref>,
10408}
10409
10410impl PixelCtx {
10411    /// Frame-MT Phase B: copy filtered MB rows into the shared progress Arc.
10412    fn publish_progress_rows(&self) {
10413        let Some(slot) = &self.progress else {
10414            return;
10415        };
10416        if !self.db_ena || self.flt_rows == 0 {
10417            return;
10418        }
10419        publish_filtered_rows_to_slot(
10420            slot,
10421            &self.rec_y,
10422            &self.rec_u,
10423            &self.rec_v,
10424            self.cw,
10425            self.ccw,
10426            self.mb_h * 16,
10427            self.flt_rows,
10428        );
10429    }
10430
10431    fn chroma_qp_for(&self, qp: u8) -> u8 {
10432        rusty_h264_common::predict::chroma_qp(
10433            ((qp as i32 + self.chroma_qp_offset).clamp(0, 51)) as u8,
10434        )
10435    }
10436
10437    /// Per-(qp, list) dequant constants for the fused scan-order kernel (worker twin).
10438    #[inline]
10439    fn dq_const(&self, qp: u8, list: usize) -> DequantQp {
10440        match &self.scaling {
10441            Some(s) => DequantQp::weighted(qp, &s[list]),
10442            None => DQ_FLAT[(qp as usize).min(51)],
10443        }
10444    }
10445
10446    fn dequant_dc4(&self, level: i32, qp: u8, list: usize) -> i32 {
10447        rusty_h264_common::transform::dequantize_dc4(
10448            level,
10449            qp,
10450            self.scaling.as_ref().map(|sc| sc[list][0]),
10451        )
10452    }
10453
10454    fn inv_quant8(&self, raster: &[i32; 64], qp: u8, list: usize) -> [i32; 64] {
10455        // Prebuilt per-(qp, list) constants: the flat table at zero cost, or one
10456        // 64-entry build per macroblock for scaling lists (was 64 x two lookups +
10457        // a multiply per coefficient on every block).
10458        match &self.scaling8 {
10459            Some(s) => inverse_quant_8x8_dq(raster, &Dequant8Qp::build(qp, &s[list])),
10460            None => inverse_quant_8x8_dq(raster, &DQ8_FLAT[(qp as usize).min(51)]),
10461        }
10462    }
10463
10464    fn dequant_chroma_dc(&self, levels: &[i32; 4], qp: u8, list: usize) -> [i32; 4] {
10465        match &self.scaling {
10466            Some(sc) => inverse_quant_chroma_dc_weighted(levels, qp, sc[list][0]),
10467            None => inverse_quant_chroma_dc(levels, qp),
10468        }
10469    }
10470
10471    fn weight_partition(
10472        &self,
10473        pred_y: &mut [u8; 256],
10474        c_pred: &mut [[u8; 64]; 2],
10475        list: usize,
10476        refi: usize,
10477        rx: usize,
10478        ry: usize,
10479        rw: usize,
10480        rh: usize,
10481    ) {
10482        let Some(wt) = &self.weights else { return };
10483        // REFUTED, reverted: row-slicing these loops measured +28% instructions
10484        // on `weight_partition` (the extents are runtime values, so the slice
10485        // bounds cost more than the per-sample checks they replaced). The real
10486        // win for this function was hoisting the identity test to its CALLERS,
10487        // which stands.
10488        // RESOLVE ONCE, APPLY MANY. `apply_luma` re-read `self.luma[list][refi]`
10489        // — an array index, a Vec deref and an element index, two of them bounds
10490        // checked — on EVERY sample, up to 256 luma plus 128 chroma per call.
10491        // The weight is a property of the partition, not of the pixel. (The loop
10492        // SHAPE is untouched: row-slicing it is refuted above.)
10493        // MASKED, not row-sliced. `pred_y` is a fixed `[u8; 256]` and `c_pred` a
10494        // `[[u8; 64]; 2]`, so `& 255` / `& 63` are no-ops that prove the index
10495        // outright — where SLICING these loops cost +28% (the extents are runtime
10496        // values, so the slice bounds outweigh the checks). Same lesson as
10497        // `luma_centre`: the refutation was of one SHAPE, not of the goal.
10498        let (lw, lo) = wt.luma_wo(list, refi);
10499        weight_block(pred_y, 16, rx, ry, rw, rh, lw, lo, wt.luma_log2_denom);
10500        let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
10501        for cc in 0..2 {
10502            let (cw, co) = wt.chroma_wo(list, refi, cc);
10503            weight_block(
10504                &mut c_pred[cc & 1],
10505                8,
10506                crx,
10507                cry,
10508                crw,
10509                crh,
10510                cw,
10511                co,
10512                wt.chroma_log2_denom,
10513            );
10514        }
10515    }
10516
10517    fn recon_p_inter(&mut self, j: &PInterJob) {
10518        crate::RefFrame::set_mc_row_need(j.mby, self.mb_h * 16);
10519        // `add_inter_residual` reads `self.cur_qp`; unlike the FrameDecoder copy
10520        // (which interleaves with parsing and must save/restore), every PixelCtx
10521        // job sets it from the job before any reader, so no restore is needed.
10522        self.cur_qp = j.qp;
10523        // ---- Recon: motion-comp (per 4×4 luma / co-located 2×2 chroma using the
10524        // committed grid MV — the 6-tap/bilinear filter is per-output-pixel, so
10525        // per-block MC is bit-identical to per-partition MC) + residual add via the
10526        // SAME reconstruct_4x4 as intra, with the MC output as the prediction.
10527        let mut pred_y = [0u8; 256];
10528        let mut c_pred = [[0u8; 64]; 2];
10529        {
10530            // MC-CALL COALESCING (side-by-side descent, dec target #2): the old
10531            // loop paid 16 mc_luma(4×4) + 32 mc_chroma(2×2) per MB regardless of
10532            // partitioning — 48 calls even for a single-MV 16×16 MB, and the
10533            // per-call glue around 2.4M calls was ~40% of decoding real-world
10534            // (x264) streams. The 6-tap/bilinear filters are per-output-pixel,
10535            // so merging blocks with equal (mv, ref) into one wider MC call is
10536            // BIT-IDENTICAL; the rect ladder mirrors the partition shapes.
10537            let _ms = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMcStage);
10538            let (rh16, cch) = (self.mb_h * 16, self.mb_h * 8);
10539            // E2: the worker owns no syntax grids — the job carries the
10540            // committed per-block motion (filled at parse time).
10541            let gmv = j.gmv;
10542            let mut gref = [0usize; 16];
10543            for k in 0..16 {
10544                gref[k] = (j.gref[k] as usize).min(self.refs.len() - 1);
10545            }
10546            // All blocks of the rect (in 4×4-block units) match its top-left?
10547            let rect_eq = |x4: usize, y4: usize, w4: usize, h4: usize| -> bool {
10548                let t = y4 * 4 + x4;
10549                (0..h4).all(|dy| {
10550                    (0..w4).all(|dx| {
10551                        let b = ((y4 + dy) * 4 + (x4 + dx)) & 15;
10552                        gmv[b] == gmv[t] && gref[b] == gref[t]
10553                    })
10554                })
10555            };
10556            let refs = &self.refs;
10557            let (cw, ccw) = (self.cw, self.ccw);
10558            let mc_rect = |x4: usize,
10559                           y4: usize,
10560                           w4: usize,
10561                           h4: usize,
10562                           pred_y: &mut [u8; 256],
10563                           c_pred: &mut [[u8; 64]; 2]| {
10564                let b = y4 * 4 + x4;
10565                let (mv, gr) = (gmv[b & 15], gref[b & 15]);
10566                // `gref` holds a reference-list slot; a slot the list
10567                // does not have means there is nothing to predict from,
10568                // so the rect is left as it stands.
10569                let Some(reference) = refs.get(gr) else {
10570                    return;
10571                };
10572                let (w, h) = (w4 * 4, h4 * 4);
10573                // A FULL-WIDTH rect (w == 16, so x4 == 0) occupies contiguous
10574                // whole rows of `pred_y` — the MC output layout and the
10575                // destination layout coincide, so MC writes the prediction
10576                // buffer DIRECTLY. The staging copy exists only for narrow
10577                // rects, whose rows really are strided in `pred_y`. This is
10578                // the diagnosis's "stage-boundary materialization" tax paid
10579                // by the dominant 16×16/16×8 shapes: 256 B of `t` zeroing
10580                // plus a 256 B copy per rect, for nothing.
10581                if w == 16 {
10582                    rusty_h264_common::inter::with_mc_scratch(|scr| {
10583                        rusty_h264_common::inter::mc_luma_padded_pre(
10584                            scr,
10585                            &*reference.luma_guard(reference.ch),
10586                            reference.lstride(),
10587                            crate::LPAD,
10588                            cw,
10589                            rh16,
10590                            j.mbx * 16,
10591                            j.mby * 16 + y4 * 4,
10592                            w,
10593                            h,
10594                            mv.0,
10595                            mv.1,
10596                            &mut pred_y[y4 * 64..y4 * 64 + w * h],
10597                        )
10598                    });
10599                } else {
10600                    let mut t = [0u8; 256];
10601                    rusty_h264_common::inter::with_mc_scratch(|scr| {
10602                        rusty_h264_common::inter::mc_luma_padded_pre(
10603                            scr,
10604                            &*reference.luma_guard(reference.ch),
10605                            reference.lstride(),
10606                            crate::LPAD,
10607                            cw,
10608                            rh16,
10609                            j.mbx * 16 + x4 * 4,
10610                            j.mby * 16 + y4 * 4,
10611                            w,
10612                            h,
10613                            mv.0,
10614                            mv.1,
10615                            &mut t[..w * h],
10616                        )
10617                    });
10618                    let _pb =
10619                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
10620                    for dy in 0..h {
10621                        pred_y[(y4 * 4 + dy) * 16 + x4 * 4..][..w]
10622                            .copy_from_slice(&t[dy * w..dy * w + w]);
10623                    }
10624                }
10625                let (cw4, ch4) = (w4 * 2, h4 * 2);
10626                let nc = cw4 * ch4;
10627                let (gu, gv) = (
10628                    reference.chroma_guard(0, reference.ch),
10629                    reference.chroma_guard(1, reference.ch),
10630                );
10631                // U+V paired: one setup serves both planes (see
10632                // mc_chroma_padded_pair). Full-width coincidence:
10633                // cw4 == 8 rows are contiguous in the 8-wide plane.
10634                if cw4 == 8 {
10635                    let [cu, cv] = &mut *c_pred;
10636                    rusty_h264_common::inter::mc_chroma_padded_pair(
10637                        &gu,
10638                        &gv,
10639                        reference.cstride(),
10640                        crate::CPAD,
10641                        ccw,
10642                        cch,
10643                        j.mbx * 8,
10644                        j.mby * 8 + y4 * 2,
10645                        cw4,
10646                        ch4,
10647                        mv.0,
10648                        mv.1,
10649                        &mut cu[y4 * 16..y4 * 16 + nc],
10650                        &mut cv[y4 * 16..y4 * 16 + nc],
10651                    );
10652                } else {
10653                    let (mut tu, mut tv) = ([0u8; 64], [0u8; 64]);
10654                    rusty_h264_common::inter::mc_chroma_padded_pair(
10655                        &gu,
10656                        &gv,
10657                        reference.cstride(),
10658                        crate::CPAD,
10659                        ccw,
10660                        cch,
10661                        j.mbx * 8 + x4 * 2,
10662                        j.mby * 8 + y4 * 2,
10663                        cw4,
10664                        ch4,
10665                        mv.0,
10666                        mv.1,
10667                        &mut tu[..nc],
10668                        &mut tv[..nc],
10669                    );
10670                    let _pb =
10671                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
10672                    for (cc, tc) in [(0usize, &tu), (1, &tv)] {
10673                        for dy in 0..ch4 {
10674                            c_pred[cc][(y4 * 2 + dy) * 8 + x4 * 2..][..cw4]
10675                                .copy_from_slice(&tc[dy * cw4..dy * cw4 + cw4]);
10676                        }
10677                    }
10678                }
10679            };
10680            if rect_eq(0, 0, 4, 4) {
10681                mc_rect(0, 0, 4, 4, &mut pred_y, &mut c_pred);
10682            } else if rect_eq(0, 0, 4, 2) && rect_eq(0, 2, 4, 2) {
10683                mc_rect(0, 0, 4, 2, &mut pred_y, &mut c_pred);
10684                mc_rect(0, 2, 4, 2, &mut pred_y, &mut c_pred);
10685            } else if rect_eq(0, 0, 2, 4) && rect_eq(2, 0, 2, 4) {
10686                mc_rect(0, 0, 2, 4, &mut pred_y, &mut c_pred);
10687                mc_rect(2, 0, 2, 4, &mut pred_y, &mut c_pred);
10688            } else {
10689                for q in 0..4usize {
10690                    let (qx, qy) = ((q % 2) * 2, (q / 2) * 2);
10691                    if rect_eq(qx, qy, 2, 2) {
10692                        mc_rect(qx, qy, 2, 2, &mut pred_y, &mut c_pred);
10693                    } else if rect_eq(qx, qy, 2, 1) && rect_eq(qx, qy + 1, 2, 1) {
10694                        mc_rect(qx, qy, 2, 1, &mut pred_y, &mut c_pred);
10695                        mc_rect(qx, qy + 1, 2, 1, &mut pred_y, &mut c_pred);
10696                    } else if rect_eq(qx, qy, 1, 2) && rect_eq(qx + 1, qy, 1, 2) {
10697                        mc_rect(qx, qy, 1, 2, &mut pred_y, &mut c_pred);
10698                        mc_rect(qx + 1, qy, 1, 2, &mut pred_y, &mut c_pred);
10699                    } else {
10700                        for j in 0..4usize {
10701                            mc_rect(qx + (j % 2), qy + (j / 2), 1, 1, &mut pred_y, &mut c_pred);
10702                        }
10703                    }
10704                }
10705            }
10706            // EXPLICIT WEIGHTED PREDICTION (spec 8.4.2.3). The CAVLC inter
10707            // path weights each partition after MC; the MC-call-coalescing
10708            // rewrite of this CABAC path lost it, and nothing caught that
10709            // because the effect is invisible unless a stream actually
10710            // carries non-default weights. x264's `weightp` DUPLICATES a
10711            // reference and distinguishes the copy ONLY by its weights, so
10712            // every macroblock picking the weighted index decoded unweighted
10713            // -- a silent, accumulating luma drift.
10714            //
10715            // Applied per 4x4 block rather than per partition: the weight
10716            // depends solely on the block's reference index, so the two are
10717            // equivalent, and `gref` already holds it for every block
10718            // regardless of which rect ladder rung ran.
10719            if self.weights.is_some() {
10720                for by in 0..4usize {
10721                    for bx in 0..4usize {
10722                        let refi = gref[by * 4 + bx];
10723                        self.weight_partition(
10724                            &mut pred_y,
10725                            &mut c_pred,
10726                            0,
10727                            refi,
10728                            bx * 4,
10729                            by * 4,
10730                            4,
10731                            4,
10732                        );
10733                    }
10734                }
10735            }
10736        }
10737        // Residual add — the SAME helper the B path uses (this inline
10738        // copy was a duplicate; deduped when the zero-block fast path
10739        // landed so both paths share it).
10740        self.add_inter_residual(
10741            j.mbx,
10742            j.mby,
10743            &pred_y,
10744            &c_pred,
10745            Some(&j.luma_scan),
10746            j.t8.then_some(&j.luma8),
10747            &j.cdc,
10748            Some(&j.cac),
10749            j.cbp_chroma,
10750            &j.nnzs,
10751        );
10752    }
10753
10754    /// D9b: P inter with `cbp == 0` — MC + plane copy (worker twin of FrameDecoder).
10755    fn recon_p_inter_nores(&mut self, j: &PInterNoResJob) {
10756        let mut pred_y = [0u8; 256];
10757        let mut c_pred = [[0u8; 64]; 2];
10758        // `self.refs.len() - 1` was re-loaded on every one of the sixteen
10759        // iterations; it is a per-macroblock invariant. Written once, not
10760        // zeroed-then-filled.
10761        let nrefs = self.refs.len() - 1;
10762        let gref: [usize; 16] = core::array::from_fn(|k| (j.gref[k] as usize).min(nrefs));
10763        coalesce_p_inter_mc(
10764            &self.refs,
10765            self.cw,
10766            self.ccw,
10767            self.mb_h,
10768            j.mbx,
10769            j.mby,
10770            &j.gmv,
10771            &gref,
10772            &mut pred_y,
10773            &mut c_pred,
10774        );
10775        // The identity early-out lives INSIDE `weight_partition`, so an x264
10776        // stream - which carries a pred_weight_table in EVERY P slice, identity
10777        // outside fades - still paid sixteen calls per macroblock to be told
10778        // there was nothing to do. Hoisted to one test.
10779        if self.weights.is_some() && !self.weights_l0id {
10780            for by in 0..4usize {
10781                for bx in 0..4usize {
10782                    let refi = gref[by * 4 + bx];
10783                    self.weight_partition(&mut pred_y, &mut c_pred, 0, refi, bx * 4, by * 4, 4, 4);
10784                }
10785            }
10786        }
10787        // ONE span per plane: the destination is a stack of contiguous runs a
10788        // fixed stride apart, so the rows share a single bounds check instead of
10789        // one each (16 + 8 + 8 of them).
10790        let (cw, ccw) = (self.cw, self.ccw);
10791        let ybase = (j.mby * 16) * cw + j.mbx * 16;
10792        let ywin = &mut self.rec_y[ybase..ybase + 15 * cw + 16];
10793        for dy in 0..16 {
10794            ywin[dy * cw..dy * cw + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
10795        }
10796        let cbase = (j.mby * 8) * ccw + j.mbx * 8;
10797        for c in 0..2 {
10798            let plane = if c == 0 {
10799                &mut self.rec_u
10800            } else {
10801                &mut self.rec_v
10802            };
10803            let cwin = &mut plane[cbase..cbase + 7 * ccw + 8];
10804            for dy in 0..8 {
10805                cwin[dy * ccw..dy * ccw + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
10806            }
10807        }
10808    }
10809
10810    fn recon_p_skip(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32)) {
10811        crate::RefFrame::set_mc_row_need(mb_y, self.mb_h * 16);
10812        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
10813
10814        let mut pred = [0u8; 256];
10815        let Some(rf0) = self.refs.first() else { return };
10816        mc_luma_padded(
10817            &*rf0.luma_guard(rf0.ch),
10818            rf0.lstride(),
10819            crate::LPAD,
10820            self.cw,
10821            ch,
10822            mb_x * 16,
10823            mb_y * 16,
10824            16,
10825            16,
10826            mv.0,
10827            mv.1,
10828            &mut pred,
10829        );
10830        if let Some(wt) = &self.weights {
10831            for p in pred.iter_mut() {
10832                *p = wt.apply_luma(*p, 0, 0);
10833            }
10834        }
10835        {
10836            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
10837            // ONE span for the sixteen rows (see `recon_p_inter_nores`).
10838            let (cw, base) = (self.cw, (mb_y * 16) * self.cw + mb_x * 16);
10839            let win = &mut self.rec_y[base..base + 15 * cw + 16];
10840            for dy in 0..16 {
10841                win[dy * cw..dy * cw + 16].copy_from_slice(&pred[dy * 16..dy * 16 + 16]);
10842            }
10843        }
10844        for c in 0..2 {
10845            let mut pc = [0u8; 64];
10846            let Some(rf0) = self.refs.first() else { return };
10847            let rc = if c == 0 {
10848                &*rf0.chroma_guard(0, rf0.ch)
10849            } else {
10850                &*rf0.chroma_guard(1, rf0.ch)
10851            };
10852            mc_chroma_padded(
10853                rc,
10854                rf0.cstride(),
10855                crate::CPAD,
10856                self.ccw,
10857                cch,
10858                mb_x * 8,
10859                mb_y * 8,
10860                8,
10861                8,
10862                mv.0,
10863                mv.1,
10864                &mut pc,
10865            );
10866            if let Some(wt) = &self.weights {
10867                for p in pc.iter_mut() {
10868                    *p = wt.apply_chroma(*p, 0, 0, c);
10869                }
10870            }
10871            let plane = if c == 0 {
10872                &mut self.rec_u
10873            } else {
10874                &mut self.rec_v
10875            };
10876            for dy in 0..8 {
10877                let d = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
10878                plane[d..d + 8].copy_from_slice(&pc[dy * 8..dy * 8 + 8]);
10879            }
10880        }
10881    }
10882
10883    fn add_inter_residual(
10884        &mut self,
10885        mb_x: usize,
10886        mb_y: usize,
10887        pred_y: &[u8; 256],
10888        c_pred: &[[u8; 64]; 2],
10889        luma_scan: Option<&[[i32; 16]; 16]>,
10890        // `Some` when the macroblock carries transform_size_8x8_flag: four 8x8
10891        // blocks in 8x8 scan order, replacing the sixteen 4x4 luma blocks.
10892        luma8: Option<&[[i32; 64]; 4]>,
10893        cdc: &[[i32; 4]; 2],
10894        cac: Option<&[[[i32; 16]; 4]; 2]>,
10895        cbp_chroma: u32,
10896        // Parsed totalCoeff per block, indexed exactly as the parse's `iz`:
10897        // [0..16] luma 4x4 z-order (for t8, the 8x8 count sits at `id8*4`),
10898        // [16..24] chroma AC as `16 + c*4 + id4`. The parser already counted
10899        // every significant coefficient; re-deriving the counts here scanned
10900        // 16-64 array elements per block (~400 loads/MB) for information the
10901        // caller was holding — the diagnosis's stage-boundary re-derivation tax.
10902        nnzs: &[u8; 24],
10903    ) {
10904        // A `None` field means the parse wrote nothing there; every read below
10905        // is guarded by a zero-count test, so the shared zero plane is
10906        // read-equivalent to the per-macroblock zeroed stack array it replaces.
10907        let luma_scan = luma_scan.unwrap_or(&ZERO_LUMA_SCAN);
10908        let cac = cac.unwrap_or(&ZERO_CAC);
10909        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecResidAdd);
10910        let qp = self.cur_qp;
10911        let qpc = self.chroma_qp_for(qp);
10912        if let Some(l8) = luma8 {
10913            // INTER 8x8 luma: same primitives the I_8x8 and CAVLC paths use.
10914            for b8 in 0..4usize {
10915                let (b8x, b8y) = (b8 % 2, b8 / 2);
10916                // Summed, not slot 0: with per-4x4 counts in the CAVLC case, slot 0
10917                // is only the first sub-block and can be 0 while the block is coded.
10918                let nnz: u32 = (0..4).map(|k| nnzs[b8 * 4 + k] as u32).sum();
10919                let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
10920                if nnz == 0 {
10921                    // Zero residual: recon == pred — same shortcut as the
10922                    // FrameDecoder twin.
10923                    edcstat::bump(&edcstat::T8_ZERO, 1);
10924                    for dy in 0..8 {
10925                        let d = (py + dy) * self.cw + px;
10926                        let po = (b8y * 8 + dy) * 16 + b8x * 8;
10927                        self.rec_y[d..d + 8].copy_from_slice(&pred_y[po..po + 8]);
10928                    }
10929                } else {
10930                    let raster = un_scan_8x8(&l8[b8]);
10931                    // list 1 = INTER 8x8 luma scaling list (0 is the intra one).
10932                    let res8 = self.inv_quant8(&raster, qp, 1);
10933                    let predb: [i32; 64] = core::array::from_fn(|i| {
10934                        pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
10935                    });
10936                    let recon = add_residual_8x8(&res8, &predb);
10937                    // Eight row copies. This wrote SIXTY-FOUR individually
10938                    // bounds-checked samples into the luma plane per coded 8x8
10939                    // block — the same shape whose fix carried `recon_i8_block`.
10940                    // Present in BOTH the main path and the EDC worker twin.
10941                    for dy in 0..8 {
10942                        let d = (py + dy) * self.cw + px;
10943                        self.rec_y[d..][..8].copy_from_slice(&recon[dy * 8..][..8]);
10944                    }
10945                }
10946            }
10947        }
10948        // RESIDUAL LADDER, then the SIMD IDCT+add kernel per parked block (SIMD census
10949        // 2026-09-05). The zero and DC-only ladders write the plane in the loop;
10950        // every other coded block parks its dequantised coefficients here and is
10951        // reconstructed by `reconstruct_4x4_into` = accel `idct4x4_add` (in-register
10952        // transposes, exact on i32). A lane-per-block BATCHED form was tried first
10953        // and lost on all-intra content: its scalar gathers cost more than the
10954        // butterflies they vectorised.
10955        let dq3 = self.dq_const(qp, 3);
10956        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
10957            if luma8.is_some() {
10958                break;
10959            }
10960            let nnz = nnzs[blk];
10961            let cw = self.cw;
10962            let p_off = (lby * 4) * 16 + lbx * 4;
10963            let r_off = (mb_y * 4 + lby) * 4 * cw + (mb_x * 4 + lbx) * 4;
10964            if nnz == 0 {
10965                // Zero residual → recon == prediction EXACTLY (the integer IDCT is
10966                // linear so zeros map to zeros, and pred is already 0..=255) — copy
10967                // the pred rows straight into the plane. On real (sparse-cbp)
10968                // streams this is MOST of the 4×4 blocks.
10969                for r in 0..4 {
10970                    self.rec_y[r_off + r * cw..r_off + r * cw + 4]
10971                        .copy_from_slice(&pred_y[p_off + r * 16..p_off + r * 16 + 4]);
10972                }
10973                continue;
10974            }
10975            // DC-ONLY: the sole significant coefficient is scan position 0 (the
10976            // zig-zag starts at DC, and un_scan keeps it at raster 0), so the
10977            // whole dequant + IDCT collapses to one multiply and a flat add.
10978            if nnz == 1 && luma_scan[blk][0] != 0 {
10979                let f = self.dequant_dc4(luma_scan[blk][0], qp, 3);
10980                reconstruct_4x4_dc_into(
10981                    (f + 32) >> 6,
10982                    pred_y,
10983                    p_off,
10984                    16,
10985                    &mut self.rec_y,
10986                    r_off,
10987                    cw,
10988                );
10989            } else {
10990                // Fused un-scan + dequant over ONLY the significant coefficients,
10991                // then IDCT + add + clip straight into the plane — no `qb`, no
10992                // `deq`-from-dense, no `predb` gather, no `s`, no `store` call.
10993                //
10994                // HYBRID: the scatter walks scan positions with a data-dependent
10995                // branch per slot, which beats the branchless dense 16-multiply
10996                // loop only while the block is SPARSE. The DC/zero fast paths
10997                // already removed the sparsest blocks, so the population here
10998                // skews denser — above ~6 coefficients the dense loop wins.
10999                // FUSED: un-scan + dequant + IDCT + add in one kernel call (dense-over-scatter round).
11000                reconstruct_4x4_scan_into::<false>(
11001                    &luma_scan[blk],
11002                    &dq3,
11003                    0,
11004                    pred_y,
11005                    p_off,
11006                    16,
11007                    &mut self.rec_y,
11008                    r_off,
11009                    cw,
11010                );
11011            }
11012        }
11013        let mut c_dc = [[0i32; 4]; 2];
11014        if cbp_chroma != 0 {
11015            for c in 0..2 {
11016                c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 4 + c);
11017            }
11018        }
11019        let dqc = [self.dq_const(qpc, 4), self.dq_const(qpc, 5)];
11020        let ccw = self.ccw;
11021        for c in 0..2 {
11022            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
11023                let mut ac_nz = false;
11024                if cbp_chroma == 2 {
11025                    let n = nnzs[(16 + c * 4 + by * 2 + bx).min(23)];
11026                    ac_nz = n != 0;
11027                }
11028                let dc = c_dc[c & 1][(by * 2 + bx) & 3];
11029                let p_off = (by * 4) * 8 + bx * 4;
11030                let r_off = (mb_y * 2 + by) * 4 * ccw + (mb_x * 2 + bx) * 4;
11031                let plane = if c == 0 {
11032                    &mut self.rec_u
11033                } else {
11034                    &mut self.rec_v
11035                };
11036                if dc == 0 && !ac_nz {
11037                    // Zero residual (no AC, zero DC) → recon == prediction exactly.
11038                    for r in 0..4 {
11039                        plane[r_off + r * ccw..r_off + r * ccw + 4]
11040                            .copy_from_slice(&c_pred[c][p_off + r * 8..p_off + r * 8 + 4]);
11041                    }
11042                    continue;
11043                }
11044                // DC-ONLY (no coded AC — covers every cbp_chroma==1 block and the
11045                // AC-empty blocks of cbp_chroma==2): the chroma DC arrives ALREADY
11046                // dequantized, so the residual is `(dc + 32) >> 6` flat.
11047                if !ac_nz {
11048                    reconstruct_4x4_dc_into(
11049                        (dc + 32) >> 6,
11050                        &c_pred[c],
11051                        p_off,
11052                        8,
11053                        plane,
11054                        r_off,
11055                        ccw,
11056                    );
11057                    continue;
11058                }
11059                // AC-only scan: index i is overall scan position i+1 (ac_shift=1).
11060                // Same sparse/dense hybrid as luma.
11061                reconstruct_4x4_scan_into::<true>(
11062                    &cac[c & 1][(by * 2 + bx) & 3],
11063                    &dqc[c & 1],
11064                    dc,
11065                    &c_pred[c],
11066                    p_off,
11067                    8,
11068                    plane,
11069                    r_off,
11070                    ccw,
11071                );
11072            }
11073        }
11074    }
11075
11076    fn filter_row(&mut self, r: usize) {
11077        // The precomputed consumer path reads ONLY `bs` + `t8x8` (+ the qp grid
11078        // passed as a parameter) — verified when the path landed (WHYS Part 15).
11079        let info = rusty_h264_common::deblock::BlockInfo {
11080            inter: &[],
11081            nnz: &[],
11082            mv: &[],
11083            ref_id: &[],
11084            mv1: &[],
11085            ref_id1: &[],
11086            w4: self.mb_w * 4,
11087            t8x8: &self.t8_grid,
11088            bs: &self.bs_store,
11089            poc0: &[],
11090            poc1: &[],
11091            kind: &[],
11092        };
11093        rusty_h264_common::deblock::filter_frame_rows_pre(
11094            &mut self.rec_y,
11095            &mut self.rec_u,
11096            &mut self.rec_v,
11097            self.mb_w,
11098            self.mb_h,
11099            r..r + 1,
11100            &self.qp_grid,
11101            self.chroma_qp_offset,
11102            self.db_oa,
11103            self.db_ob,
11104            &info,
11105        );
11106    }
11107
11108    fn save_bak(&mut self, r: usize) {
11109        let y0 = (r * 16 + 15) * self.cw;
11110        self.bak_y.copy_from_slice(&self.rec_y[y0..y0 + self.cw]);
11111        let c0 = (r * 8 + 7) * self.ccw;
11112        self.bak_u.copy_from_slice(&self.rec_u[c0..c0 + self.ccw]);
11113        self.bak_v.copy_from_slice(&self.rec_v[c0..c0 + self.ccw]);
11114    }
11115}
11116
11117/// The pixel worker: replays jobs in parse order, filters rows as their
11118/// messages arrive, and hands the whole context to the parse thread (and
11119/// back) around intra macroblocks. Returns the context at slice end.
11120/// E2 SEAM COUNTERS (D7). Deterministic — one run is the verdict, no pinning.
11121/// `RS_H264_EDC_STATS=1` prints at decode end. Counts, not clocks: the question
11122/// "does one intra macroblock drain the pipeline" is a COUNT question.
11123pub(crate) mod edcstat {
11124    use core::sync::atomic::Ordering::Relaxed;
11125    use rusty_h264_common::atomic::AtomicU64;
11126    pub static NEEDCTX: AtomicU64 = AtomicU64::new(0);
11127    pub static JOBS: AtomicU64 = AtomicU64::new(0);
11128    pub static ROWS: AtomicU64 = AtomicU64::new(0);
11129    pub static ROWBYTES: AtomicU64 = AtomicU64::new(0);
11130    pub static MBS: AtomicU64 = AtomicU64::new(0);
11131    /// Sparse-dequant routing histogram: [nnz class 1-2/3-4/5-6/7+][L class 1-4/5-8/9-12/13-16],
11132    /// L = highest coded scan position + 1 (the scatter loop's trip count).
11133    pub static DQ: [AtomicU64; 16] = [const { AtomicU64::new(0) }; 16];
11134    #[inline(always)]
11135    #[allow(dead_code)]
11136    pub fn dq_note(nnz: u8, scan: &[i32; 16]) {
11137        #[cfg(feature = "profile")]
11138        if on() {
11139            let l = 16 - scan.iter().rev().take_while(|&&v| v == 0).count();
11140            let nc = ((nnz.max(1) as usize - 1) / 2).min(3);
11141            let lc = ((l.max(1) - 1) / 4).min(3);
11142            DQ[nc * 4 + lc].fetch_add(1, Relaxed);
11143        }
11144        #[cfg(not(feature = "profile"))]
11145        let _ = (nnz, scan);
11146    }
11147    pub static J_INTER: AtomicU64 = AtomicU64::new(0);
11148    pub static DOUBLED: AtomicU64 = AtomicU64::new(0);
11149    pub static J_NORES_SENT: AtomicU64 = AtomicU64::new(0);
11150    pub static BATCHES: AtomicU64 = AtomicU64::new(0);
11151    pub static DISPATCH_ON: AtomicU64 = AtomicU64::new(0);
11152    pub static DISPATCH_SEEN: AtomicU64 = AtomicU64::new(0);
11153    /// mb_skip_run band path (big-oppy-decoder §6 KEY glue): P_Skip MBs
11154    /// reconstructed by run-coalesced band copies, and the runs themselves.
11155    /// DETERMINISTIC WIN counters — each banded MB removed 1 luma MC call,
11156    /// 2 chroma MC calls and the 256+128-byte pred staging round-trip.
11157    pub static SKIPBAND_MBS: AtomicU64 = AtomicU64::new(0);
11158    pub static SKIPBAND_RUNS: AtomicU64 = AtomicU64::new(0);
11159    pub static SKIP_SINGLES: AtomicU64 = AtomicU64::new(0);
11160    // MEASUREMENT counters sizing the next skip-band extensions (no behavior).
11161    // P side: singles that sit in a same-row run of >=2 EQUAL nonzero MVs,
11162    // split by full-pel (band-copy-with-offset eligible) vs fractional.
11163    pub static PEQ_FP: AtomicU64 = AtomicU64::new(0);
11164    pub static PEQ_FRAC: AtomicU64 = AtomicU64::new(0);
11165    // B side: full-16x16 spatial-direct calls (B_Skip + direct-16), how many
11166    // collapse to ONE uniform rect, and of those the zero-motion bi/uni and
11167    // full-pel populations + run adjacency (previous MB same params, same row).
11168    pub static BSK_FULLMB: AtomicU64 = AtomicU64::new(0);
11169    pub static BSK_1RECT: AtomicU64 = AtomicU64::new(0);
11170    pub static BSK_ZBI: AtomicU64 = AtomicU64::new(0);
11171    pub static BSK_ZUNI: AtomicU64 = AtomicU64::new(0);
11172    pub static BSK_FP: AtomicU64 = AtomicU64::new(0);
11173    pub static BSK_RUNCONT: AtomicU64 = AtomicU64::new(0);
11174    /// DETERMINISTIC WIN counter — B_Skip MBs taken by the zero-bi fast path.
11175    pub static BSKB_FAST: AtomicU64 = AtomicU64::new(0);
11176    /// P_Skip singles taken by the full-pel direct-copy path (both planes /
11177    /// luma only, chroma still interpolating at mv%8!=0).
11178    pub static SKIP_FP_FULL: AtomicU64 = AtomicU64::new(0);
11179    pub static SKIP_FP_LUMA: AtomicU64 = AtomicU64::new(0);
11180    /// B_Skip full-pel nonzero-MV MBs taken by the offset copy/avg path.
11181    pub static BSKB_FP: AtomicU64 = AtomicU64::new(0);
11182    /// P_Skip parse side: MBs whose skip MV was FORCED (0,0) by the run
11183    /// theorem (left = just-committed (0,0) skip, or out-of-slice) vs MBs
11184    /// that ran the 3-neighbor gather + rule.
11185    pub static SKIPMV_FORCED: AtomicU64 = AtomicU64::new(0);
11186    pub static SKIPMV_DERIVED: AtomicU64 = AtomicU64::new(0);
11187    /// B_Skips whose zero-bi derivation was FORCED by the known-zero bitmap
11188    /// (left+top+topright all recorded ref0/(0,0)-both-lists) — the 6-gather
11189    /// b_direct_nbrs walk skipped entirely.
11190    pub static BSKB_FORCED: AtomicU64 = AtomicU64::new(0);
11191    /// Zero-bi fast B_Skip grid commits batched into row spans: MBs covered
11192    /// and spans flushed (fills of 4N replace N MBs x 28 per-MB fills).
11193    pub static BZ_SPAN_MBS: AtomicU64 = AtomicU64::new(0);
11194    pub static BZ_SPANS: AtomicU64 = AtomicU64::new(0);
11195    /// P_Skip (0,0) grid-commit spans (parse-side mirror of BZ_*).
11196    pub static PZ_SPAN_MBS: AtomicU64 = AtomicU64::new(0);
11197    pub static PZ_SPANS: AtomicU64 = AtomicU64::new(0);
11198    /// Census: b_mc bi-pred regions where BOTH MVs are full-pel (fusable to a
11199    /// direct offset average — sizing only, no behavior).
11200    pub static BMC_BI_FP: AtomicU64 = AtomicU64::new(0);
11201    /// weight_partition passes skipped because the whole list-0 table is
11202    /// identity (384 apply ops avoided per full-MB pass).
11203    pub static WP_SKIPPED: AtomicU64 = AtomicU64::new(0);
11204    /// Intra I_4x4 luma residual dispatch (the inter ladder, ported): how many
11205    /// blocks took the zero / DC-only / sparse-scatter / dense arms.
11206    pub static I4_ZERO: AtomicU64 = AtomicU64::new(0);
11207    pub static I4_DC: AtomicU64 = AtomicU64::new(0);
11208    pub static I4_SPARSE: AtomicU64 = AtomicU64::new(0);
11209    pub static I4_DENSE: AtomicU64 = AtomicU64::new(0);
11210    /// 8x8 residual blocks (intra + inter t8) whose residual is entirely zero:
11211    /// recon == pred, the 64-px widen + add + clip skipped.
11212    pub static T8_ZERO: AtomicU64 = AtomicU64::new(0);
11213    /// I_16x16 4x4 sub-blocks with zero AC: the residual is the (already
11214    /// Hadamard-transformed) DC alone, so the dense dequant + IDCT collapse
11215    /// to one flat add.
11216    pub static I16_DCONLY: AtomicU64 = AtomicU64::new(0);
11217    pub static J_INTER_NORES: AtomicU64 = AtomicU64::new(0);
11218    // ---- deb:derive census (derive_bs_row) — sizing the bS derivation arms.
11219    /// Rows derived, and macroblocks derived across them.
11220    pub static DBS_ROWS: AtomicU64 = AtomicU64::new(0);
11221    pub static DBS_MB: AtomicU64 = AtomicU64::new(0);
11222    /// Arm split: Intra kind-arm / Skip|InterUniform kind-arm (only under
11223    /// RS_H264_KIND_LOADS=1) / the packed `derive_mb_records` default.
11224    pub static DBS_INTRA: AtomicU64 = AtomicU64::new(0);
11225    pub static DBS_KINDARM: AtomicU64 = AtomicU64::new(0);
11226    /// Macroblocks whose kind is Skip|InterUniform: the population that used
11227    /// to evaluate the `kind_loads()` OnceLock guard once EACH.
11228    pub static DBS_KINDGUARD: AtomicU64 = AtomicU64::new(0);
11229    pub static DBS_PACKED: AtomicU64 = AtomicU64::new(0);
11230    /// Of the packed arm: macroblocks whose derivation returned `flat_inter`
11231    /// (internal strengths 0 by construction — the early-return class).
11232    pub static DBS_FLAT: AtomicU64 = AtomicU64::new(0);
11233    /// Macroblocks whose FINAL stored MbBs is all-zero: the population the
11234    /// consumer's two-16-byte-compare early-out serves, and the denominator
11235    /// for the per-MB 128-byte bs_v/bs_h zero-init that precedes it.
11236    pub static DBS_ALLZERO: AtomicU64 = AtomicU64::new(0);
11237    /// nnz_dbr maintenance: rows copied wholesale vs rows that actually
11238    /// carried a transform-8x8 macroblock needing the 8x8 OR fixup.
11239    pub static DBS_NNZ_ROWCOPY: AtomicU64 = AtomicU64::new(0);
11240    pub static DBS_T8ROW: AtomicU64 = AtomicU64::new(0);
11241    pub static DBS_T8MB: AtomicU64 = AtomicU64::new(0);
11242    /// Rows that ran the disable_deblocking_filter_idc==2 crossing-edge pass.
11243    pub static DBS_IDC2ROW: AtomicU64 = AtomicU64::new(0);
11244    #[inline]
11245    pub fn bump(c: &AtomicU64, n: u64) {
11246        #[cfg(feature = "profile")]
11247        if on() {
11248            c.fetch_add(n, Relaxed);
11249        }
11250        #[cfg(not(feature = "profile"))]
11251        let _ = (c, n);
11252    }
11253    /// Off by default, yet read on EVERY bump — up to four times per B_Skip.
11254    /// `OnceLock::get_or_init` is an acquire load plus an initialised-state
11255    /// branch behind a non-inlined call; this is the relaxed AtomicU8 tri-state
11256    /// the other knobs in this file already use, and it inlines to one load.
11257    #[inline]
11258    pub fn on() -> bool {
11259        #[cfg(not(feature = "profile"))]
11260        {
11261            return false;
11262        }
11263        #[cfg(feature = "profile")]
11264        {
11265            use core::sync::atomic::AtomicU8;
11266            static V: AtomicU8 = AtomicU8::new(0);
11267            match V.load(Relaxed) {
11268                1 => true,
11269                2 => false,
11270                _ => {
11271                    let b = rusty_h264_common::knob("RS_H264_EDC_STATS").is_some();
11272                    V.store(if b { 1 } else { 2 }, Relaxed);
11273                    b
11274                }
11275            }
11276        }
11277    }
11278    pub fn report() {
11279        if !on() {
11280            return;
11281        }
11282        {
11283            // Routing model (census, 2026-09-05): scatter = 37 + 6*L + 9*nnz instrs;
11284            // dense = unscan 32 + dequantize ~63 (scalar) / ~28 (AVX2 twin).
11285            let (nm, lm) = ([1.5f64, 3.5, 5.5, 9.0], [2.5f64, 6.5, 10.5, 14.5]);
11286            let (mut tot, mut sc, mut d95, mut d60, mut best) =
11287                (0u64, 0.0f64, 0.0f64, 0.0f64, 0.0f64);
11288            eprintln!(
11289                "DQROUTE sparse-arm blocks by [nnz class][L class] (L = last coded position + 1):"
11290            );
11291            for nc in 0..4 {
11292                let row: Vec<u64> = (0..4).map(|lc| DQ[nc * 4 + lc].load(Relaxed)).collect();
11293                eprintln!(
11294                    "  nnz {:<5} L1-4={:>9} L5-8={:>9} L9-12={:>9} L13-16={:>9}",
11295                    ["1-2", "3-4", "5-6", "7+"][nc],
11296                    row[0],
11297                    row[1],
11298                    row[2],
11299                    row[3]
11300                );
11301                for lc in 0..4 {
11302                    let k = row[lc] as f64;
11303                    let s = 37.0 + 6.0 * lm[lc] + 9.0 * nm[nc];
11304                    tot += row[lc];
11305                    sc += k * s;
11306                    d95 += k * 95.0;
11307                    d60 += k * 60.0;
11308                    best += k * s.min(60.0);
11309                }
11310            }
11311            if tot > 0 {
11312                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);
11313            }
11314        }
11315        eprintln!(
11316            "EDCDISPATCH threaded_slices={} eligible_slices={}",
11317            DISPATCH_ON.load(Relaxed),
11318            DISPATCH_SEEN.load(Relaxed)
11319        );
11320        eprintln!(
11321            "EDCSIZE EdcMsg={} EdcJob={} PInterJob={} BJob={}",
11322            core::mem::size_of::<super::EdcMsg>(),
11323            core::mem::size_of::<super::EdcJob>(),
11324            core::mem::size_of::<super::PInterJob>(),
11325            core::mem::size_of::<super::BJob>(),
11326        );
11327        let (n, j, r, b, m) = (
11328            NEEDCTX.load(Relaxed),
11329            JOBS.load(Relaxed),
11330            ROWS.load(Relaxed),
11331            ROWBYTES.load(Relaxed),
11332            MBS.load(Relaxed),
11333        );
11334        eprintln!(
11335            "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}",
11336            BATCHES.load(Relaxed),
11337            j as f64 / BATCHES.load(Relaxed).max(1) as f64,
11338            1000.0 * n as f64 / m.max(1) as f64,
11339            j as f64 / n.max(1) as f64
11340        );
11341        eprintln!(
11342            "SKIPRUN band_mbs={} band_runs={} mbs_per_run={:.1} singles={} banded_pct={:.1}",
11343            SKIPBAND_MBS.load(Relaxed),
11344            SKIPBAND_RUNS.load(Relaxed),
11345            SKIPBAND_MBS.load(Relaxed) as f64 / SKIPBAND_RUNS.load(Relaxed).max(1) as f64,
11346            SKIP_SINGLES.load(Relaxed),
11347            100.0 * SKIPBAND_MBS.load(Relaxed) as f64
11348                / (SKIPBAND_MBS.load(Relaxed) + SKIP_SINGLES.load(Relaxed)).max(1) as f64,
11349        );
11350        eprintln!(
11351            "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={}",
11352            PEQ_FP.load(Relaxed), PEQ_FRAC.load(Relaxed),
11353            BSK_FULLMB.load(Relaxed), BSK_1RECT.load(Relaxed),
11354            BSK_ZBI.load(Relaxed), BSK_ZUNI.load(Relaxed),
11355            BSK_FP.load(Relaxed), BSK_RUNCONT.load(Relaxed),
11356            BSKB_FAST.load(Relaxed),
11357            SKIP_FP_FULL.load(Relaxed), SKIP_FP_LUMA.load(Relaxed),
11358            BSKB_FP.load(Relaxed),
11359            SKIPMV_FORCED.load(Relaxed), SKIPMV_DERIVED.load(Relaxed),
11360            BSKB_FORCED.load(Relaxed),
11361            BZ_SPANS.load(Relaxed), BZ_SPAN_MBS.load(Relaxed),
11362            PZ_SPANS.load(Relaxed), PZ_SPAN_MBS.load(Relaxed),
11363            BMC_BI_FP.load(Relaxed),
11364            WP_SKIPPED.load(Relaxed),
11365            I4_ZERO.load(Relaxed), I4_DC.load(Relaxed),
11366            I4_SPARSE.load(Relaxed), I4_DENSE.load(Relaxed),
11367            T8_ZERO.load(Relaxed), I16_DCONLY.load(Relaxed),
11368        );
11369        let dmb = DBS_MB.load(Relaxed).max(1);
11370        eprintln!(
11371            "DBSDERIVE rows={} mb={} intra={} kindarm={} kindguard={} packed={} flat={} ({:.1}%) allzero={} ({:.1}%) nnz_rowcopy={} t8row={} t8mb={} idc2row={}",
11372            DBS_ROWS.load(Relaxed), DBS_MB.load(Relaxed),
11373            DBS_INTRA.load(Relaxed), DBS_KINDARM.load(Relaxed),
11374            DBS_KINDGUARD.load(Relaxed), DBS_PACKED.load(Relaxed),
11375            DBS_FLAT.load(Relaxed), 100.0 * DBS_FLAT.load(Relaxed) as f64 / dmb as f64,
11376            DBS_ALLZERO.load(Relaxed), 100.0 * DBS_ALLZERO.load(Relaxed) as f64 / dmb as f64,
11377            DBS_NNZ_ROWCOPY.load(Relaxed), DBS_T8ROW.load(Relaxed),
11378            DBS_T8MB.load(Relaxed), DBS_IDC2ROW.load(Relaxed),
11379        );
11380        let (ji, jn) = (J_INTER.load(Relaxed), J_INTER_NORES.load(Relaxed));
11381        eprintln!(
11382            "EDCMIX doubled={} nores_sent={} inter={ji} inter_no_residual={jn} ({:.1}% of inter) wasted_bytes={:.1} MB of {:.1} MB total inter payload",
11383            DOUBLED.load(Relaxed),
11384            J_NORES_SENT.load(Relaxed),
11385            100.0 * jn as f64 / ji.max(1) as f64,
11386            (jn * 2784) as f64 / 1.048576e6,
11387            (ji * 2784) as f64 / 1.048576e6,
11388        );
11389    }
11390}
11391
11392#[cfg(feature = "std")]
11393fn edc_worker(
11394    mut ctx: PixelCtx,
11395    rx: crate::sync::mpsc::Receiver<EdcMsg>,
11396    ctx_tx: crate::sync::mpsc::Sender<PixelCtx>,
11397    back_rx: crate::sync::mpsc::Receiver<PixelCtx>,
11398) -> PixelCtx {
11399    while let Ok(msg) = rx.recv() {
11400        match msg {
11401            EdcMsg::Batch(jobs) => {
11402                for j in jobs {
11403                    match j {
11404                        EdcJob::Skip { mbx, mby, mv } => ctx.recon_p_skip(mbx, mby, mv),
11405                        EdcJob::Inter(j) => ctx.recon_p_inter(&j),
11406                        EdcJob::InterNoRes(j) => ctx.recon_p_inter_nores(&j),
11407                        EdcJob::B(j) => ctx.recon_b(&j),
11408                        EdcJob::BSkip { mbx, mby, regions } => ctx.recon_b_skip(mbx, mby, &regions),
11409                    }
11410                }
11411            }
11412            EdcMsg::Job(EdcJob::Skip { mbx, mby, mv }) => ctx.recon_p_skip(mbx, mby, mv),
11413            EdcMsg::Job(EdcJob::Inter(j)) => ctx.recon_p_inter(&j),
11414            EdcMsg::Job(EdcJob::InterNoRes(j)) => ctx.recon_p_inter_nores(&j),
11415            EdcMsg::Job(EdcJob::B(j)) => ctx.recon_b(&j),
11416            EdcMsg::Job(EdcJob::BSkip { mbx, mby, regions }) => {
11417                ctx.recon_b_skip(mbx, mby, &regions)
11418            }
11419            EdcMsg::Row { r, bs, qp, t8 } => {
11420                let (w, base) = (ctx.mb_w, r * ctx.mb_w);
11421                ctx.bs_store[base..base + w].copy_from_slice(&bs);
11422                ctx.qp_grid[base..base + w].copy_from_slice(&qp);
11423                ctx.t8_grid[base..base + w].copy_from_slice(&t8);
11424                if ctx.db_ena {
11425                    ctx.save_bak(r);
11426                    ctx.filter_row(r);
11427                    ctx.flt_rows = r + 1;
11428                    ctx.publish_progress_rows();
11429                }
11430            }
11431            EdcMsg::NeedCtx => {
11432                ctx_tx.send(ctx).expect("parse thread alive");
11433                ctx = back_rx.recv().expect("ctx returned after intra");
11434            }
11435        }
11436    }
11437    ctx
11438}
11439
11440/// One motion-compensation region of a B macroblock, recorded at parse time
11441/// (E3). Weights are the RESOLVED implicit pair — computing them needs the
11442/// ref lists' POCs, which are parse-side state.
11443pub(crate) struct BRegion {
11444    px: usize,
11445    py: usize,
11446    rw: usize,
11447    rh: usize,
11448    refi0: i32,
11449    refi1: i32,
11450    mv0: (i32, i32),
11451    mv1: (i32, i32),
11452    w: Option<(i32, i32)>,
11453}
11454
11455/// A B macroblock's deferred pixel work: replay the regions into fresh
11456/// prediction buffers, then either copy them out (skip/direct, no residual)
11457/// or run the residual add.
11458pub(crate) struct BJob {
11459    mbx: usize,
11460    mby: usize,
11461    qp: u8,
11462    cbp_chroma: u32,
11463    skip: bool,
11464    regions: Vec<BRegion>,
11465    /// `None` when no 4x4 luma block was coded — t8 macroblocks included,
11466    /// since those carry `luma8` instead.
11467    luma_scan: Option<[[i32; 16]; 16]>,
11468    /// `Some` only under transform_size_8x8_flag.
11469    luma8: Option<[[i32; 64]; 4]>,
11470    cdc: [[i32; 4]; 2],
11471    /// `None` unless cbp_chroma == 2 (chroma AC coded).
11472    cac: Option<[[[i32; 16]; 4]; 2]>,
11473    nnzs: [u8; 24],
11474}
11475
11476impl PixelCtx {
11477    fn b_mc(
11478        &self,
11479        mb_x: usize,
11480        mb_y: usize,
11481        px: usize,
11482        py: usize,
11483        rw: usize,
11484        rh: usize,
11485        refi0: i32,
11486        mv0: (i32, i32),
11487        refi1: i32,
11488        mv1: (i32, i32),
11489        pred_y: &mut [u8; 256],
11490        c_pred: &mut [[u8; 64]; 2],
11491        wparam: Option<(i32, i32)>,
11492    ) {
11493        let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBMc);
11494        // Malformed-stream armor, mirroring the P path: now that B slices actually
11495        // PARSE ref_idx (they used to be hardcoded to 0), a mutated stream can hand
11496        // us an index past the end of either list. Clamp rather than panic — the
11497        // crate is `forbid(unsafe_code)` and fuzz-gated to never panic, and a
11498        // wrong picture on garbage input carries no conformance duty.
11499        let refi0 = if refi0 >= 0 {
11500            (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32
11501        } else {
11502            -1
11503        };
11504        let refi1 = if refi1 >= 0 {
11505            (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32
11506        } else {
11507            -1
11508        };
11509        if (refi0 >= 0 && self.refs.is_empty()) || (refi1 >= 0 && self.refs1.is_empty()) {
11510            return;
11511        }
11512        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
11513        // E3: implicit weights are PARSE-side (they read the ref lists' POCs);
11514        // the region carries the resolved pair.
11515        let weights = wparam;
11516        // Bi-prediction blend: the weights decision is LOOP-INVARIANT, so every
11517        // blend site below matches on `weights` ONCE and runs a branch-free
11518        // pixel loop — the unweighted `(p+q+1)>>1` average then autovectorizes
11519        // (the per-pixel closure this replaces hid the invariant behind a
11520        // capture, and its chroma form was a &dyn call PER PIXEL).
11521        // FULL-WIDTH regions (px == 0, rw == 16 — every 16×16/16×8 partition and
11522        // most direct regions) occupy contiguous rows of `pred_y`, so MC writes
11523        // the destination DIRECTLY: uni-pred needs no staging at all, bi-pred
11524        // stages only the second list and blends in place. The staging arrays
11525        // (512 B zeroed per call before this) now exist only on the branches
11526        // that read them. Same fusion as the P path's mc_rect (WHYS Part 8).
11527        let full = px == 0 && rw == 16;
11528        let _gl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBLuma);
11529        // One scratch borrow for the whole region — both bi-pred passes included.
11530        // The closure yields whether the arm already ran the chroma half (the
11531        // bi-pred full-width arm does, to keep its staging alive) — a plain
11532        // `return` inside would exit the CLOSURE only and chroma would run twice.
11533        let chroma_done =
11534            rusty_h264_common::inter::with_mc_scratch(|scr| match (refi0 >= 0, refi1 >= 0, full) {
11535                (true, false, true) => {
11536                    let Some(rf) = self.refs.get(refi0 as usize) else {
11537                        return false;
11538                    };
11539                    rusty_h264_common::inter::mc_luma_padded_pre(
11540                        scr,
11541                        &*rf.luma_guard(rf.ch),
11542                        rf.lstride(),
11543                        crate::LPAD,
11544                        self.cw,
11545                        ch,
11546                        mb_x * 16,
11547                        mb_y * 16 + py,
11548                        rw,
11549                        rh,
11550                        mv0.0,
11551                        mv0.1,
11552                        &mut pred_y[py * 16..py * 16 + rw * rh],
11553                    );
11554                    false
11555                }
11556                (false, true, true) => {
11557                    let Some(rf) = self.refs1.get(refi1 as usize) else {
11558                        return false;
11559                    };
11560                    rusty_h264_common::inter::mc_luma_padded_pre(
11561                        scr,
11562                        &*rf.luma_guard(rf.ch),
11563                        rf.lstride(),
11564                        crate::LPAD,
11565                        self.cw,
11566                        ch,
11567                        mb_x * 16,
11568                        mb_y * 16 + py,
11569                        rw,
11570                        rh,
11571                        mv1.0,
11572                        mv1.1,
11573                        &mut pred_y[py * 16..py * 16 + rw * rh],
11574                    );
11575                    false
11576                }
11577                (true, true, true) => {
11578                    let Some(rf) = self.refs.get(refi0 as usize) else {
11579                        return false;
11580                    };
11581                    rusty_h264_common::inter::mc_luma_padded_pre(
11582                        scr,
11583                        &*rf.luma_guard(rf.ch),
11584                        rf.lstride(),
11585                        crate::LPAD,
11586                        self.cw,
11587                        ch,
11588                        mb_x * 16,
11589                        mb_y * 16 + py,
11590                        rw,
11591                        rh,
11592                        mv0.0,
11593                        mv0.1,
11594                        &mut pred_y[py * 16..py * 16 + rw * rh],
11595                    );
11596                    let mut b = [0u8; 256];
11597                    let Some(rf) = self.refs1.get(refi1 as usize) else {
11598                        return false;
11599                    };
11600                    rusty_h264_common::inter::mc_luma_padded_pre(
11601                        scr,
11602                        &*rf.luma_guard(rf.ch),
11603                        rf.lstride(),
11604                        crate::LPAD,
11605                        self.cw,
11606                        ch,
11607                        mb_x * 16,
11608                        mb_y * 16 + py,
11609                        rw,
11610                        rh,
11611                        mv1.0,
11612                        mv1.1,
11613                        &mut b[..rw * rh],
11614                    );
11615                    drop(_gl);
11616                    let _gbl =
11617                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
11618                    // SLICE-then-zip: proving the bounds ONCE lets rustc emit the whole
11619                    // 256-byte average as 8 straight-line vpavgb ops (verified in
11620                    // isolation, x86-64-v3); the indexed form kept a per-iteration
11621                    // bounds check and a loop. A hand AVX2 kernel is refuted — the
11622                    // compiler already emits the ideal instruction.
11623                    let dst = &mut pred_y[py * 16..py * 16 + rw * rh];
11624                    match weights {
11625                        None => {
11626                            for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
11627                                *d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
11628                            }
11629                        }
11630                        Some((w0, w1)) => {
11631                            for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
11632                                *d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255)
11633                                    as u8;
11634                            }
11635                        }
11636                    }
11637                    let _gc =
11638                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
11639                    self.b_mc_chroma(
11640                        mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch,
11641                    );
11642                    true
11643                }
11644                _ => {
11645                    // Narrow region — rows are strided in `pred_y`; stage and copy.
11646                    let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
11647                    if refi0 >= 0 {
11648                        let Some(rf) = self.refs.get(refi0 as usize) else {
11649                            return false;
11650                        };
11651                        rusty_h264_common::inter::mc_luma_padded_pre(
11652                            scr,
11653                            &*rf.luma_guard(rf.ch),
11654                            rf.lstride(),
11655                            crate::LPAD,
11656                            self.cw,
11657                            ch,
11658                            mb_x * 16 + px,
11659                            mb_y * 16 + py,
11660                            rw,
11661                            rh,
11662                            mv0.0,
11663                            mv0.1,
11664                            &mut a[..rw * rh],
11665                        );
11666                    }
11667                    if refi1 >= 0 {
11668                        let Some(rf) = self.refs1.get(refi1 as usize) else {
11669                            return false;
11670                        };
11671                        rusty_h264_common::inter::mc_luma_padded_pre(
11672                            scr,
11673                            &*rf.luma_guard(rf.ch),
11674                            rf.lstride(),
11675                            crate::LPAD,
11676                            self.cw,
11677                            ch,
11678                            mb_x * 16 + px,
11679                            mb_y * 16 + py,
11680                            rw,
11681                            rh,
11682                            mv1.0,
11683                            mv1.1,
11684                            &mut b[..rw * rh],
11685                        );
11686                    }
11687                    drop(_gl);
11688                    let _gbl =
11689                        rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
11690                    match (refi0 >= 0, refi1 >= 0) {
11691                        (true, true) => {
11692                            for dy in 0..rh {
11693                                let (ar, br) =
11694                                    (&a[dy * rw..dy * rw + rw], &b[dy * rw..dy * rw + rw]);
11695                                let base = (py + dy) * 16 + px;
11696                                let dst = &mut pred_y[base..base + rw];
11697                                match weights {
11698                                    None => {
11699                                        for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
11700                                            *d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
11701                                        }
11702                                    }
11703                                    Some((w0, w1)) => {
11704                                        for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
11705                                            *d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6)
11706                                                .clamp(0, 255)
11707                                                as u8;
11708                                        }
11709                                    }
11710                                }
11711                            }
11712                        }
11713                        (true, false) => {
11714                            for dy in 0..rh {
11715                                let d = (py + dy) * 16 + px;
11716                                pred_y[d..d + rw].copy_from_slice(&a[dy * rw..dy * rw + rw]);
11717                            }
11718                        }
11719                        _ => {
11720                            for dy in 0..rh {
11721                                let d = (py + dy) * 16 + px;
11722                                pred_y[d..d + rw].copy_from_slice(&b[dy * rw..dy * rw + rw]);
11723                            }
11724                        }
11725                    }
11726                    false
11727                }
11728            });
11729        if chroma_done {
11730            return;
11731        }
11732        let _gc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
11733        self.b_mc_chroma(
11734            mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch,
11735        );
11736    }
11737
11738    fn b_mc_chroma(
11739        &self,
11740        mb_x: usize,
11741        mb_y: usize,
11742        px: usize,
11743        py: usize,
11744        rw: usize,
11745        rh: usize,
11746        refi0: i32,
11747        mv0: (i32, i32),
11748        refi1: i32,
11749        mv1: (i32, i32),
11750        c_pred: &mut [[u8; 64]; 2],
11751        weights: Option<(i32, i32)>,
11752        cch: usize,
11753    ) {
11754        let (crx, cry, crw, crh) = (px / 2, py / 2, rw / 2, rh / 2);
11755        let full = crx == 0 && crw == 8;
11756        for c in 0..2 {
11757            match (refi0 >= 0, refi1 >= 0, full) {
11758                (true, false, true) => {
11759                    let Some(rf) = self.refs.get(refi0 as usize) else {
11760                        return;
11761                    };
11762                    let pl = if c == 0 {
11763                        &*rf.chroma_guard(0, rf.ch)
11764                    } else {
11765                        &*rf.chroma_guard(1, rf.ch)
11766                    };
11767                    mc_chroma_padded(
11768                        pl,
11769                        rf.cstride(),
11770                        crate::CPAD,
11771                        self.ccw,
11772                        cch,
11773                        mb_x * 8,
11774                        mb_y * 8 + cry,
11775                        crw,
11776                        crh,
11777                        mv0.0,
11778                        mv0.1,
11779                        &mut c_pred[c][cry * 8..cry * 8 + crw * crh],
11780                    );
11781                }
11782                (false, true, true) => {
11783                    let Some(rf) = self.refs1.get(refi1 as usize) else {
11784                        return;
11785                    };
11786                    let pl = if c == 0 {
11787                        &*rf.chroma_guard(0, rf.ch)
11788                    } else {
11789                        &*rf.chroma_guard(1, rf.ch)
11790                    };
11791                    mc_chroma_padded(
11792                        pl,
11793                        rf.cstride(),
11794                        crate::CPAD,
11795                        self.ccw,
11796                        cch,
11797                        mb_x * 8,
11798                        mb_y * 8 + cry,
11799                        crw,
11800                        crh,
11801                        mv1.0,
11802                        mv1.1,
11803                        &mut c_pred[c][cry * 8..cry * 8 + crw * crh],
11804                    );
11805                }
11806                (true, true, true) => {
11807                    let Some(rf) = self.refs.get(refi0 as usize) else {
11808                        return;
11809                    };
11810                    let pl = if c == 0 {
11811                        &*rf.chroma_guard(0, rf.ch)
11812                    } else {
11813                        &*rf.chroma_guard(1, rf.ch)
11814                    };
11815                    mc_chroma_padded(
11816                        pl,
11817                        rf.cstride(),
11818                        crate::CPAD,
11819                        self.ccw,
11820                        cch,
11821                        mb_x * 8,
11822                        mb_y * 8 + cry,
11823                        crw,
11824                        crh,
11825                        mv0.0,
11826                        mv0.1,
11827                        &mut c_pred[c][cry * 8..cry * 8 + crw * crh],
11828                    );
11829                    let mut cb = [0u8; 64];
11830                    let Some(rf) = self.refs1.get(refi1 as usize) else {
11831                        return;
11832                    };
11833                    let pl = if c == 0 {
11834                        &*rf.chroma_guard(0, rf.ch)
11835                    } else {
11836                        &*rf.chroma_guard(1, rf.ch)
11837                    };
11838                    mc_chroma_padded(
11839                        pl,
11840                        rf.cstride(),
11841                        crate::CPAD,
11842                        self.ccw,
11843                        cch,
11844                        mb_x * 8,
11845                        mb_y * 8 + cry,
11846                        crw,
11847                        crh,
11848                        mv1.0,
11849                        mv1.1,
11850                        &mut cb[..crw * crh],
11851                    );
11852                    let dst = &mut c_pred[c][cry * 8..cry * 8 + crw * crh];
11853                    match weights {
11854                        None => {
11855                            for (d, s) in dst.iter_mut().zip(&cb[..crw * crh]) {
11856                                *d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
11857                            }
11858                        }
11859                        Some((w0, w1)) => {
11860                            for (d, s) in dst.iter_mut().zip(&cb[..crw * crh]) {
11861                                *d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255)
11862                                    as u8;
11863                            }
11864                        }
11865                    }
11866                }
11867                _ => {
11868                    let (mut ca, mut cb) = ([0u8; 64], [0u8; 64]);
11869                    if refi0 >= 0 {
11870                        let Some(rf) = self.refs.get(refi0 as usize) else {
11871                            return;
11872                        };
11873                        let pl = if c == 0 {
11874                            &*rf.chroma_guard(0, rf.ch)
11875                        } else {
11876                            &*rf.chroma_guard(1, rf.ch)
11877                        };
11878                        mc_chroma_padded(
11879                            pl,
11880                            rf.cstride(),
11881                            crate::CPAD,
11882                            self.ccw,
11883                            cch,
11884                            mb_x * 8 + crx,
11885                            mb_y * 8 + cry,
11886                            crw,
11887                            crh,
11888                            mv0.0,
11889                            mv0.1,
11890                            &mut ca[..crw * crh],
11891                        );
11892                    }
11893                    if refi1 >= 0 {
11894                        let Some(rf) = self.refs1.get(refi1 as usize) else {
11895                            return;
11896                        };
11897                        let pl = if c == 0 {
11898                            &*rf.chroma_guard(0, rf.ch)
11899                        } else {
11900                            &*rf.chroma_guard(1, rf.ch)
11901                        };
11902                        mc_chroma_padded(
11903                            pl,
11904                            rf.cstride(),
11905                            crate::CPAD,
11906                            self.ccw,
11907                            cch,
11908                            mb_x * 8 + crx,
11909                            mb_y * 8 + cry,
11910                            crw,
11911                            crh,
11912                            mv1.0,
11913                            mv1.1,
11914                            &mut cb[..crw * crh],
11915                        );
11916                    }
11917                    match (refi0 >= 0, refi1 >= 0) {
11918                        (true, true) => {
11919                            for dy in 0..crh {
11920                                let (pr, qr) =
11921                                    (&ca[dy * crw..dy * crw + crw], &cb[dy * crw..dy * crw + crw]);
11922                                let base = (cry + dy) * 8 + crx;
11923                                let dst = &mut c_pred[c][base..base + crw];
11924                                match weights {
11925                                    None => {
11926                                        for ((d, p), q) in dst.iter_mut().zip(pr).zip(qr) {
11927                                            *d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
11928                                        }
11929                                    }
11930                                    Some((w0, w1)) => {
11931                                        for ((d, p), q) in dst.iter_mut().zip(pr).zip(qr) {
11932                                            *d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6)
11933                                                .clamp(0, 255)
11934                                                as u8;
11935                                        }
11936                                    }
11937                                }
11938                            }
11939                        }
11940                        (true, false) => {
11941                            for dy in 0..crh {
11942                                let d = (cry + dy) * 8 + crx;
11943                                c_pred[c][d..d + crw]
11944                                    .copy_from_slice(&ca[dy * crw..dy * crw + crw]);
11945                            }
11946                        }
11947                        _ => {
11948                            for dy in 0..crh {
11949                                let d = (cry + dy) * 8 + crx;
11950                                c_pred[c][d..d + crw]
11951                                    .copy_from_slice(&cb[dy * crw..dy * crw + crw]);
11952                            }
11953                        }
11954                    }
11955                }
11956            }
11957        }
11958    }
11959
11960    /// B_Skip replay: regions into fresh prediction buffers, then the plane
11961    /// copy — no residual, no coefficient arrays.
11962    fn recon_b_skip(&mut self, mbx: usize, mby: usize, regions: &[BRegion]) {
11963        crate::RefFrame::set_mc_row_need(mby, self.mb_h * 16);
11964        let mut pred_y = [0u8; 256];
11965        let mut c_pred = [[0u8; 64]; 2];
11966        for r in regions {
11967            self.b_mc(
11968                mbx,
11969                mby,
11970                r.px,
11971                r.py,
11972                r.rw,
11973                r.rh,
11974                r.refi0,
11975                r.mv0,
11976                r.refi1,
11977                r.mv1,
11978                &mut pred_y,
11979                &mut c_pred,
11980                r.w,
11981            );
11982        }
11983        for dy in 0..16 {
11984            let d = (mby * 16 + dy) * self.cw + mbx * 16;
11985            self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
11986        }
11987        for c in 0..2 {
11988            let plane = if c == 0 {
11989                &mut self.rec_u
11990            } else {
11991                &mut self.rec_v
11992            };
11993            for dy in 0..8 {
11994                let d = (mby * 8 + dy) * self.ccw + mbx * 8;
11995                plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
11996            }
11997        }
11998    }
11999
12000    /// Replays one B macroblock's regions + residual (the worker half of the
12001    /// E3 seam). Mirrors the inline order exactly: MC regions in parse order
12002    /// into the prediction buffers, then the residual add (or the skip copy).
12003    fn recon_b(&mut self, j: &BJob) {
12004        crate::RefFrame::set_mc_row_need(j.mby, self.mb_h * 16);
12005        let mut pred_y = [0u8; 256];
12006        let mut c_pred = [[0u8; 64]; 2];
12007        for r in &j.regions {
12008            self.b_mc(
12009                j.mbx,
12010                j.mby,
12011                r.px,
12012                r.py,
12013                r.rw,
12014                r.rh,
12015                r.refi0,
12016                r.mv0,
12017                r.refi1,
12018                r.mv1,
12019                &mut pred_y,
12020                &mut c_pred,
12021                r.w,
12022            );
12023        }
12024        if j.skip {
12025            for dy in 0..16 {
12026                let d = (j.mby * 16 + dy) * self.cw + j.mbx * 16;
12027                self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
12028            }
12029            for c in 0..2 {
12030                let plane = if c == 0 {
12031                    &mut self.rec_u
12032                } else {
12033                    &mut self.rec_v
12034                };
12035                for dy in 0..8 {
12036                    let d = (j.mby * 8 + dy) * self.ccw + j.mbx * 8;
12037                    plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
12038                }
12039            }
12040        } else {
12041            self.cur_qp = j.qp;
12042            self.add_inter_residual(
12043                j.mbx,
12044                j.mby,
12045                &pred_y,
12046                &c_pred,
12047                j.luma_scan.as_ref(),
12048                j.luma8.as_ref(),
12049                &j.cdc,
12050                j.cac.as_ref(),
12051                j.cbp_chroma,
12052                &j.nnzs,
12053            );
12054        }
12055    }
12056}
12057
12058/// D9b: MC-call coalescing for a P inter MB from committed per-block (mv, ref).
12059/// Shared by residual and no-residual recon — filters are per-output-pixel so
12060/// wider rects are bit-identical to sixteen 4×4 calls.
12061fn coalesce_p_inter_mc(
12062    refs: &[crate::Ref],
12063    cw: usize,
12064    ccw: usize,
12065    mb_h: usize,
12066    mbx: usize,
12067    mby: usize,
12068    gmv: &[(i32, i32); 16],
12069    gref: &[usize; 16],
12070    pred_y: &mut [u8; 256],
12071    c_pred: &mut [[u8; 64]; 2],
12072) {
12073    let _ms = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMcStage);
12074    let (rh16, cch) = (mb_h * 16, mb_h * 8);
12075    let rect_eq = |x4: usize, y4: usize, w4: usize, h4: usize| -> bool {
12076        let t = y4 * 4 + x4;
12077        (0..h4).all(|dy| {
12078            (0..w4).all(|dx| {
12079                // 4x4 block grid: the index is always < 16, but LLVM cannot
12080                // infer it from the nested ranges. `& 15` states it.
12081                let b = ((y4 + dy) * 4 + (x4 + dx)) & 15;
12082                gmv[b] == gmv[t] && gref[b] == gref[t]
12083            })
12084        })
12085    };
12086    let mut mc_rect = |x4: usize, y4: usize, w4: usize, h4: usize| {
12087        let b = y4 * 4 + x4;
12088        let (mv, gr) = (gmv[b & 15], gref[b & 15]);
12089        let Some(reference) = refs.get(gr) else {
12090            return;
12091        };
12092        let (w, h) = (w4 * 4, h4 * 4);
12093        if w == 16 {
12094            rusty_h264_common::inter::with_mc_scratch(|scr| {
12095                rusty_h264_common::inter::mc_luma_padded_pre(
12096                    scr,
12097                    &*reference.luma_guard(reference.ch),
12098                    reference.lstride(),
12099                    crate::LPAD,
12100                    cw,
12101                    rh16,
12102                    mbx * 16,
12103                    mby * 16 + y4 * 4,
12104                    w,
12105                    h,
12106                    mv.0,
12107                    mv.1,
12108                    &mut pred_y[y4 * 64..y4 * 64 + w * h],
12109                )
12110            });
12111        } else {
12112            let mut t = [0u8; 256];
12113            rusty_h264_common::inter::with_mc_scratch(|scr| {
12114                rusty_h264_common::inter::mc_luma_padded_pre(
12115                    scr,
12116                    &*reference.luma_guard(reference.ch),
12117                    reference.lstride(),
12118                    crate::LPAD,
12119                    cw,
12120                    rh16,
12121                    mbx * 16 + x4 * 4,
12122                    mby * 16 + y4 * 4,
12123                    w,
12124                    h,
12125                    mv.0,
12126                    mv.1,
12127                    &mut t[..w * h],
12128                )
12129            });
12130            let _pb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
12131            for dy in 0..h {
12132                pred_y[(y4 * 4 + dy) * 16 + x4 * 4..][..w].copy_from_slice(&t[dy * w..dy * w + w]);
12133            }
12134        }
12135        let (cw4, ch4) = (w4 * 2, h4 * 2);
12136        let nc = cw4 * ch4;
12137        let (gu, gv) = (
12138            reference.chroma_guard(0, reference.ch),
12139            reference.chroma_guard(1, reference.ch),
12140        );
12141        // U+V paired: one setup serves both planes (see mc_chroma_padded_pair).
12142        if cw4 == 8 {
12143            let [cu, cv] = &mut *c_pred;
12144            rusty_h264_common::inter::mc_chroma_padded_pair(
12145                &gu,
12146                &gv,
12147                reference.cstride(),
12148                crate::CPAD,
12149                ccw,
12150                cch,
12151                mbx * 8,
12152                mby * 8 + y4 * 2,
12153                cw4,
12154                ch4,
12155                mv.0,
12156                mv.1,
12157                &mut cu[y4 * 16..y4 * 16 + nc],
12158                &mut cv[y4 * 16..y4 * 16 + nc],
12159            );
12160        } else {
12161            let (mut tu, mut tv) = ([0u8; 64], [0u8; 64]);
12162            rusty_h264_common::inter::mc_chroma_padded_pair(
12163                &gu,
12164                &gv,
12165                reference.cstride(),
12166                crate::CPAD,
12167                ccw,
12168                cch,
12169                mbx * 8 + x4 * 2,
12170                mby * 8 + y4 * 2,
12171                cw4,
12172                ch4,
12173                mv.0,
12174                mv.1,
12175                &mut tu[..nc],
12176                &mut tv[..nc],
12177            );
12178            let _pb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
12179            for (cc, tc) in [(0usize, &tu), (1, &tv)] {
12180                for dy in 0..ch4 {
12181                    c_pred[cc][(y4 * 2 + dy) * 8 + x4 * 2..][..cw4]
12182                        .copy_from_slice(&tc[dy * cw4..dy * cw4 + cw4]);
12183                }
12184            }
12185        }
12186    };
12187    if rect_eq(0, 0, 4, 4) {
12188        mc_rect(0, 0, 4, 4);
12189    } else if rect_eq(0, 0, 4, 2) && rect_eq(0, 2, 4, 2) {
12190        mc_rect(0, 0, 4, 2);
12191        mc_rect(0, 2, 4, 2);
12192    } else if rect_eq(0, 0, 2, 4) && rect_eq(2, 0, 2, 4) {
12193        mc_rect(0, 0, 2, 4);
12194        mc_rect(2, 0, 2, 4);
12195    } else {
12196        for q in 0..4usize {
12197            let (qx, qy) = ((q % 2) * 2, (q / 2) * 2);
12198            if rect_eq(qx, qy, 2, 2) {
12199                mc_rect(qx, qy, 2, 2);
12200            } else if rect_eq(qx, qy, 2, 1) && rect_eq(qx, qy + 1, 2, 1) {
12201                mc_rect(qx, qy, 2, 1);
12202                mc_rect(qx, qy + 1, 2, 1);
12203            } else if rect_eq(qx, qy, 1, 2) && rect_eq(qx + 1, qy, 1, 2) {
12204                mc_rect(qx, qy, 1, 2);
12205                mc_rect(qx + 1, qy, 1, 2);
12206            } else {
12207                for j in 0..4usize {
12208                    mc_rect(qx + (j % 2), qy + (j / 2), 1, 1);
12209                }
12210            }
12211        }
12212    }
12213}
12214
12215/// One deferred pixel-reconstruction job (entropy-decouple E1 seam).
12216enum EdcJob {
12217    Skip {
12218        mbx: usize,
12219        mby: usize,
12220        mv: (i32, i32),
12221    },
12222    Inter(Box<PInterJob>),
12223    B(Box<BJob>),
12224    /// B_Skip / no-residual direct: regions only. The full `BJob` carried
12225    /// 2.6 KB of ZEROED coefficient arrays for ~60% of B macroblocks — the
12226    /// wall-time regression's main CPU tax (B-heavy MT arm measured +45% CPU).
12227    BSkip {
12228        mbx: usize,
12229        mby: usize,
12230        regions: Vec<BRegion>,
12231    },
12232    /// P inter with `cbp == 0` — the P-side twin of `BSkip` (D9).
12233    InterNoRes(Box<PInterNoResJob>),
12234}
12235
12236/// A P inter macroblock with NO residual (`cbp == 0`) — motion only.
12237///
12238/// D9. `PInterJob` is 2,784 bytes and **93% of that is coefficient arrays**
12239/// (`luma_scan` 1024 + `luma8` 1024 + `cac` 512 + `cdc` 32 = 2,592). When
12240/// `cbp == 0` every one of them is ZERO, and the seam was heap-allocating,
12241/// filling, channel-passing and freeing all 2,592 bytes of nothing —
12242/// 12.8-37.5% of inter jobs on the x264 corpus, 15.8-44.1 MB per 60-frame pass.
12243///
12244/// This is the same pathology `EdcJob::BSkip` was introduced to fix on the B
12245/// side ("2.6 KB of ZEROED coefficient arrays for ~60% of B macroblocks — the
12246/// wall-time regression's main CPU tax"). It was never ported to P.
12247///
12248/// Consumer uses `recon_p_inter_nores` (MC + plane copy) — bit-identical to
12249/// `recon_p_inter` on zero residuals, without memset of 2.5 KB coeff arrays.
12250/// (`RS_H264_NORES=0` keeps the old full-job path for A/B — routed at the
12251/// CONSTRUCTOR, so this job carries only what nores recon reads.)
12252struct PInterNoResJob {
12253    mbx: usize,
12254    mby: usize,
12255    t8: bool,
12256    gmv: [(i32, i32); 16],
12257    gref: [u8; 16],
12258}
12259
12260/// The compact inputs of one CABAC P inter macroblock's reconstruction.
12261/// Shared all-zero residual planes. An Option-shaped residual field is `None`
12262/// exactly when the parse wrote nothing into it, and every consumer read is
12263/// already guarded by a zero-count test — so a `None` reader would have seen
12264/// zeros anyway. Pointing at one static beats zero-initialising 1 KB (luma) or
12265/// 512 B (chroma AC) of stack per macroblock.
12266static ZERO_LUMA_SCAN: [[i32; 16]; 16] = [[0; 16]; 16];
12267/// `(16384 + |td|/2) / td` for every `td` the spec can present. `td` is
12268/// `.clamp(-128, 127)` at both call sites (spec 8.4.1.2.3), so the divide has
12269/// exactly 256 possible inputs and a table replaces it EXACTLY -- this is not an
12270/// approximation, it is the same arithmetic evaluated ahead of time. `td == 0`
12271/// is guarded by the caller and maps to 0 here so the table has no hole.
12272static TX_FOR_TD: [i32; 256] = {
12273    let mut t = [0i32; 256];
12274    let mut i = 0usize;
12275    while i < 256 {
12276        let td = i as i32 - 128; // index 0 => -128 .. index 255 => 127
12277        t[i] = if td == 0 {
12278            0
12279        } else {
12280            (16384 + td.abs() / 2) / td
12281        };
12282        i += 1;
12283    }
12284    t
12285};
12286
12287#[inline(always)]
12288fn tx_for_td(td: i32) -> i32 {
12289    debug_assert!((-128..=127).contains(&td), "td must be clamped before this");
12290    TX_FOR_TD[(td.clamp(-128, 127) + 128) as usize]
12291}
12292
12293/// The "no coefficients" neighbour record. `mb_nzc` reads are reached only
12294/// through `if let Some(..) = top/left`, so an out-of-range index is
12295/// unreachable; this is what the unavailable branches already imply.
12296static ZERO_NZC: [u8; 24] = [0u8; 24];
12297static ZERO_CAC: [[[i32; 16]; 4]; 2] = [[[0; 16]; 4]; 2];
12298
12299struct PInterJob {
12300    mbx: usize,
12301    mby: usize,
12302    qp: u8,
12303    cbp_chroma: u32,
12304    /// transform_size_8x8_flag: the residual is `luma8`, not `luma_scan`.
12305    t8: bool,
12306    /// The committed per-block motion, copied at parse time so the worker
12307    /// never reads the parse thread's grids (E2). Ref indices clamped by the
12308    /// consumer, kept u8 (spec max 15).
12309    gmv: [(i32, i32); 16],
12310    gref: [u8; 16],
12311    /// `None` when no 4x4 luma block was coded — t8 macroblocks included,
12312    /// since those carry `luma8` instead.
12313    /// POOLED and reused across macroblocks (see `take_pinter_job`): only the
12314    /// blocks whose `nnzs` entry is nonzero hold the data of this macroblock; every
12315    /// other block is STALE and the consumer never reads it (it copies the
12316    /// prediction when nnz == 0). The parse zeroes exactly the blocks it is
12317    /// about to write.
12318    luma_scan: [[i32; 16]; 16],
12319    luma8: [[i32; 64]; 4],
12320    cdc: [[i32; 4]; 2],
12321    cac: [[[i32; 16]; 4]; 2],
12322    nnzs: [u8; 24],
12323}
12324
12325impl PInterJob {
12326    fn blank() -> Self {
12327        PInterJob {
12328            mbx: 0,
12329            mby: 0,
12330            qp: 0,
12331            cbp_chroma: 0,
12332            t8: false,
12333            gmv: [(0, 0); 16],
12334            gref: [0; 16],
12335            luma_scan: [[0; 16]; 16],
12336            luma8: [[0; 64]; 4],
12337            cdc: [[0; 4]; 2],
12338            cac: [[[0; 16]; 4]; 2],
12339            nnzs: [0; 24],
12340        }
12341    }
12342}
12343
12344/// Entropy-decouple master knob — DEFAULT ON since 2026-08-05 (`RS_H264_EDC=0`
12345/// opts out). E1 was expected to be cost-neutral scaffolding for the E2
12346/// thread; it BANKED on its own: 13/15 pairs, z=2.84, median +4.0% (pooled
12347/// 19/24, z=2.86). Mechanism: LOOP FISSION — batching a row's parsing and
12348/// then a row's reconstruction keeps each large code path's I-cache and
12349/// branch state hot, instead of alternating two giant bodies per macroblock.
12350fn edc_on() -> bool {
12351    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12352    // constant below; the env A/B arm exists only under `--features knobs`.
12353    #[cfg(not(feature = "knobs"))]
12354    {
12355        return true;
12356    }
12357    #[cfg(feature = "knobs")]
12358    {
12359        use core::sync::atomic::{AtomicU8, Ordering};
12360        static ON: AtomicU8 = AtomicU8::new(0);
12361        match ON.load(Ordering::Relaxed) {
12362            0 => {
12363                let v = !rusty_h264_common::knob("RS_H264_EDC").is_some_and(|v| v == "0");
12364                ON.store(if v { 1 } else { 2 }, Ordering::Relaxed);
12365                v
12366            }
12367            n => n == 1,
12368        }
12369    }
12370}
12371
12372/// E2/E3 worker-thread knob.
12373///
12374/// **Default OFF (2026-08-11).** ffmpeg's parallel unit is the PICTURE
12375/// (`decode_slice` + `hl_decode_mb` on the same thread). `edc_worker` is a
12376/// nested recon thread — the wrong function:
12377///   * 1T (`ffmpeg -threads 1`, pinvs pin): two threads thrash one core
12378///   * frame-MT (`ffmpeg -threads N`): each picture worker would spawn another
12379/// Frame-MT (`RS_H264_FRAME_THREADS=N`) is the ffmpeg-shaped pool. This
12380/// pipeline stays as an explicit oracle (`RS_H264_EDC_MT=1`) / old auto-gate
12381/// (`=auto`). `=0` forces inline.
12382/// D8: run every pixel reconstruction TWICE (the second pass is discarded work,
12383/// not discarded output -- recon is idempotent, so the bytes are unchanged).
12384/// `t(double) - t(single)` IS the pixel half's cost, which is the parallel
12385/// fraction the E2 seam can address. Byte-identity is the proof the ablation
12386/// did not change the program (unlike removing the stage, which cascades).
12387/// D9 compact no-residual P inter jobs. `RS_H264_NORES=0` restores the old
12388/// always-full-payload path for A/B (the arm must PIN the value, never inherit
12389/// a default -- an "off" arm that only omits an override measures
12390/// default-vs-default and prints all zeros).
12391/// D10 row batching — **DEFAULT ON. A DELIBERATE THROUGHPUT-OVER-LATENCY TRADE.**
12392///
12393/// Ships a row's pixel jobs in one message instead of one per macroblock:
12394/// 207,949 sends -> 3,086 (**67-70x fewer**), and **~3% less CPU**.
12395///
12396/// ⚠ IT COSTS 2-4% WALL on a single stream — 0.963x / 0.980x, **11/11 pairs on
12397/// two clips**, A/B'd against itself at a fixed queue bound. That is measured,
12398/// reproducible, and ACCEPTED, not an oversight. Do not "fix" it by flipping
12399/// the default; read this first.
12400///
12401/// WHY IT COSTS WALL: batching trades pipelining for synchronisation.
12402/// Per-macroblock sends let the worker start on job 1 immediately; a row batch
12403/// makes it idle until ~70 macroblocks are parsed, then hands it a burst.
12404///
12405/// WHY IT IS STILL THE RIGHT DEFAULT: wall time here is SINGLE-STREAM LATENCY;
12406/// CPU is THROUGHPUT. A host decoding many streams concurrently is CPU-bound,
12407/// not latency-bound, so 3% less CPU is ~3% more capacity while the 2-4% wall
12408/// cost falls on a dimension that is not the constraint. The 67x drop in
12409/// channel operations also removes a park/unpark storm that scales with the
12410/// number of runnable threads — it gets better, not worse, as the box fills.
12411///
12412/// `RS_H264_BATCH=0` restores per-macroblock sends for the latency-sensitive
12413/// single-stream case (playback, seek preview, anything where first-frame time
12414/// dominates).
12415fn batch_on() -> bool {
12416    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12417    // constant below; the env A/B arm exists only under `--features knobs`.
12418    #[cfg(not(feature = "knobs"))]
12419    {
12420        return true;
12421    }
12422    #[cfg(feature = "knobs")]
12423    {
12424        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12425        *V.get_or_init(|| !rusty_h264_common::knob("RS_H264_BATCH").is_some_and(|v| v == "0"))
12426    }
12427}
12428
12429fn nores_on() -> bool {
12430    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12431    // constant below; the env A/B arm exists only under `--features knobs`.
12432    #[cfg(not(feature = "knobs"))]
12433    {
12434        return true;
12435    }
12436    #[cfg(feature = "knobs")]
12437    {
12438        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12439        *V.get_or_init(|| !rusty_h264_common::knob("RS_H264_NORES").is_some_and(|v| v == "0"))
12440    }
12441}
12442
12443/// D13 A/B: always allocate B-only CABAC neighbour grids even on P/I slices.
12444fn fat_slice_on() -> bool {
12445    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12446    // constant below; the env A/B arm exists only under `--features knobs`.
12447    #[cfg(not(feature = "knobs"))]
12448    {
12449        return false;
12450    }
12451    #[cfg(feature = "knobs")]
12452    {
12453        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12454        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_FAT_SLICE").is_some_and(|v| v == "1"))
12455    }
12456}
12457
12458/// MEASUREMENT KNOB — `RS_H264_NO_SKIPBAND=1` forces the per-MB skip path so the
12459/// mb_skip_run band coalescer can be A/B'd paired on ONE binary. Inert when unset.
12460fn no_skipband() -> bool {
12461    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12462    // constant below; the env A/B arm exists only under `--features knobs`.
12463    #[cfg(not(feature = "knobs"))]
12464    {
12465        return false;
12466    }
12467    #[cfg(feature = "knobs")]
12468    {
12469        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12470        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_NO_SKIPBAND").is_some_and(|v| v == "1"))
12471    }
12472}
12473
12474/// MEASUREMENT KNOB — `RS_H264_NO_RUNMV=1` forces the full skip_mv derivation
12475/// on every P_Skip (disables the run-theorem forced-(0,0) branch) for paired A/B.
12476fn no_runmv() -> bool {
12477    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12478    // constant below; the env A/B arm exists only under `--features knobs`.
12479    #[cfg(not(feature = "knobs"))]
12480    {
12481        return false;
12482    }
12483    #[cfg(feature = "knobs")]
12484    {
12485        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12486        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_NO_RUNMV").is_some_and(|v| v == "1"))
12487    }
12488}
12489
12490/// MEASUREMENT KNOB — `RS_H264_NO_SKIPFP=1` disables the P_Skip single fast
12491/// paths (full-pel direct copy + identity-weight-pass skip) for paired A/B.
12492fn no_skipfp() -> bool {
12493    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12494    // constant below; the env A/B arm exists only under `--features knobs`.
12495    #[cfg(not(feature = "knobs"))]
12496    {
12497        return false;
12498    }
12499    #[cfg(feature = "knobs")]
12500    {
12501        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12502        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_NO_SKIPFP").is_some_and(|v| v == "1"))
12503    }
12504}
12505
12506/// MEASUREMENT KNOB — `RS_H264_NO_BSKIPFAST=1` forces the full decode_b_direct
12507/// path for B_Skip so the zero-bi fast path can be A/B'd paired on ONE binary.
12508fn no_bskipfast() -> bool {
12509    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12510    // constant below; the env A/B arm exists only under `--features knobs`.
12511    #[cfg(not(feature = "knobs"))]
12512    {
12513        return false;
12514    }
12515    #[cfg(feature = "knobs")]
12516    {
12517        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12518        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_NO_BSKIPFAST").is_some_and(|v| v == "1"))
12519    }
12520}
12521
12522fn double_recon() -> bool {
12523    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12524    // constant below; the env A/B arm exists only under `--features knobs`.
12525    #[cfg(not(feature = "knobs"))]
12526    {
12527        return false;
12528    }
12529    #[cfg(feature = "knobs")]
12530    {
12531        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12532        // `== "1"`, not `is_some()`: the old presence test meant even
12533        // `RS_H264_DOUBLE_RECON=0` DOUBLED the recon work — the one knob in the
12534        // inventory whose "off" spelling turned it on (2026-08-27 audit, site 8).
12535        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_DOUBLE_RECON").is_some_and(|v| v == "1"))
12536    }
12537}
12538
12539fn edc_bound() -> usize {
12540    static V: rusty_h264_common::once::OnceLock<usize> = rusty_h264_common::once::OnceLock::new();
12541    *V.get_or_init(|| {
12542        rusty_h264_common::knob("RS_H264_EDC_BOUND")
12543            .and_then(|v| v.parse().ok())
12544            .unwrap_or(256)
12545    })
12546}
12547
12548/// D12 — E2 THREADING DISPATCH. Fires on `720p-or-smaller AND bits/MB > 38.4`.
12549///
12550/// The seam threads unconditionally before this, and that shipped a REGRESSION:
12551/// 8-10% slower wall on main profile for 38-56% more CPU. It cannot pay in
12552/// general — the pixel half is only ~15.6% of decode, so Amdahl caps two
12553/// threads at 1.085x — but it DOES win on some streams, so the answer is a
12554/// dispatch, not abandonment.
12555///
12556/// Fitted with `bench/examples/gate_optimizer.rs` over **28 interleaved
12557/// configurations** (12 clips x core counts 4-8, `bench/pinmtx.ps1`):
12558/// **net +29.40 of +30.70 perfect**, train +25.10 AND holdout +4.30, worst
12559/// fired class **+2.94**, precision 0.80. It forgoes +0.40 of wins to avoid
12560/// **169.90** of losses. Calibration: depth-2 **2/300** rules passed (0.67%),
12561/// so the separation carries information.
12562///
12563/// Per clause, both load-bearing (dropping either: -20.50 / -50.60, 6-7 big
12564/// losers):
12565/// * `bits/MB > 38.4` — coefficient density is the runtime proxy for PIXEL
12566///   SHARE: more coefficients means more residual work on the far side of the
12567///   seam. Threshold sits in an open gap (highest excluded 35.4, lowest firing
12568///   41.4). Note this is the OPPOSITE direction to an earlier hand-fitted
12569///   `bits < 65`, which was fitted to one clip and falsified.
12570/// * frame <= 720p — every 1080p configuration measured loses, including a
12571///   low-density one, so density alone is not sufficient.
12572/// * `cabac` — ADDED 2026-08-07 after the CAVLC arm made those units
12573///   measurable. bits/MB DOES NOT TRANSFER ACROSS ENTROPY CODERS: CAVLC needs
12574///   more bits for the SAME coefficients, so its density (62-65) reads deep
12575///   inside the firing region while its pixel work is unchanged. Without this
12576///   clause the rule routed CAVLC into threading, where it measured 1.29-1.49x
12577///   SLOWER — net -52.30, worst class -40.85. With it, +29.40 and worst class
12578///   +2.94. `gate_optimizer` could not find this: the rule needs THREE clauses
12579///   and the search is depth-2 (both depth-2 pairs fail, -20.50 / -50.60).
12580///
12581/// The estimate comes from ALREADY-DECODED slices, so the first slice of a
12582/// stream runs INLINE (the safe arm) until a measurement exists. Both arms are
12583/// byte-identical, so the choice can never affect output.
12584/// Invoked only by `RS_H264_EDC_MT=auto` (the pre-2026-08-11 default).
12585fn edc_dispatch(mb_w: usize, mb_h: usize, bits_per_mb: f64, cabac: bool) -> bool {
12586    const BITS_MIN: f64 = 38.4;
12587    const MAX_MBS: usize = 5000; // 720p = 3600, 1080p = 8160
12588                                 // The `cabac` clause is NOT cosmetic — see the header note. Without it this
12589                                 // rule scores net -52.30 with worst class -40.85 once CAVLC units are in the
12590                                 // corpus, because CAVLC's bits/MB is inflated by a less efficient entropy
12591                                 // coder rather than by more pixel work.
12592    cabac && bits_per_mb > BITS_MIN && mb_w * mb_h <= MAX_MBS
12593}
12594
12595fn edc_mt() -> Option<bool> {
12596    static V: rusty_h264_common::once::OnceLock<Option<bool>> =
12597        rusty_h264_common::once::OnceLock::new();
12598    *V.get_or_init(
12599        || match rusty_h264_common::knob("RS_H264_EDC_MT").as_deref() {
12600            Some("0") => Some(false),
12601            Some("1") => Some(true),
12602            Some("auto") => None,
12603            _ => Some(false),
12604        },
12605    )
12606}
12607
12608/// Spawn `edc_worker`? ffmpeg never does this: the picture thread owns recon.
12609/// Frame-MT workers (`FRAME_THREADS>1`) always inline. Otherwise honor the knob
12610/// (`1` / `0` / `auto`→[`edc_dispatch`]; unset = inline).
12611fn edc_spawn_worker(mb_w: usize, mb_h: usize, bits_per_mb: f64, cabac: bool) -> bool {
12612    #[cfg(not(feature = "std"))]
12613    {
12614        let _ = (mb_w, mb_h, bits_per_mb, cabac);
12615        false
12616    }
12617    #[cfg(feature = "std")]
12618    {
12619        if crate::frame_threads() > 1 {
12620            return false;
12621        }
12622        edc_mt().unwrap_or_else(|| edc_dispatch(mb_w, mb_h, bits_per_mb, cabac))
12623    }
12624}
12625
12626/// Picture-end bS precompute (rowdb-off fallback). `RS_H264_BS_PRE=0` opts out.
12627fn bs_pre_on() -> bool {
12628    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12629    // constant below; the env A/B arm exists only under `--features knobs`.
12630    #[cfg(not(feature = "knobs"))]
12631    {
12632        return true;
12633    }
12634    #[cfg(feature = "knobs")]
12635    {
12636        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12637        *V.get_or_init(|| !rusty_h264_common::knob("RS_H264_BS_PRE").is_some_and(|v| v == "0"))
12638    }
12639}
12640
12641/// Row-interleaved deblocking master knob: `RS_H264_ROWDB=0` opts out,
12642/// restoring the picture-end pipeline (WHYS Part 17) as the A/B comparator.
12643/// MEASUREMENT KNOB — `RS_H264_KIND_LOADS=1` restores the Blk::load-based
12644/// kind arms in derive_bs_row (see the routing comment there) for paired A/B.
12645fn kind_loads() -> bool {
12646    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12647    // constant below; the env A/B arm exists only under `--features knobs`.
12648    #[cfg(not(feature = "knobs"))]
12649    {
12650        return false;
12651    }
12652    #[cfg(feature = "knobs")]
12653    {
12654        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12655        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_KIND_LOADS").is_some_and(|v| v == "1"))
12656    }
12657}
12658
12659fn rowdb_on() -> bool {
12660    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12661    // constant below; the env A/B arm exists only under `--features knobs`.
12662    #[cfg(not(feature = "knobs"))]
12663    {
12664        return true;
12665    }
12666    #[cfg(feature = "knobs")]
12667    {
12668        use core::sync::atomic::{AtomicU8, Ordering};
12669        static ON: AtomicU8 = AtomicU8::new(0);
12670        match ON.load(Ordering::Relaxed) {
12671            0 => {
12672                let v = !rusty_h264_common::knob("RS_H264_ROWDB").is_some_and(|v| v == "0");
12673                ON.store(if v { 1 } else { 2 }, Ordering::Relaxed);
12674                v
12675            }
12676            n => n == 1,
12677        }
12678    }
12679}
12680
12681/// A/B: per-MB `row_hook` body even when no row has completed (old behaviour).
12682fn rowhook_eager() -> bool {
12683    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12684    // constant below; the env A/B arm exists only under `--features knobs`.
12685    #[cfg(not(feature = "knobs"))]
12686    {
12687        return false;
12688    }
12689    #[cfg(feature = "knobs")]
12690    {
12691        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12692        *V.get_or_init(|| rusty_h264_common::knob("RS_H264_ROWHOOK_EAGER").is_some_and(|v| v == "1"))
12693    }
12694}
12695
12696/// A/B: `RS_H264_DIRECT_MEMO=0` rewalks spatial-direct neighbours every 8×8.
12697#[inline]
12698fn direct_memo_on() -> bool {
12699    // ROUTED AT BUILD TIME (routing round 2026-09-05): the shipped arm is the
12700    // constant below; the env A/B arm exists only under `--features knobs`.
12701    #[cfg(not(feature = "knobs"))]
12702    {
12703        return true;
12704    }
12705    #[cfg(feature = "knobs")]
12706    {
12707        static V: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
12708        *V.get_or_init(|| !rusty_h264_common::knob("RS_H264_DIRECT_MEMO").is_some_and(|v| v == "0"))
12709    }
12710}
12711
12712/// 4×4-block (z-order) → 30-entry (6-stride) mv/ref/mvd cache index (openh264
12713/// g_kCache30ScanIdx). Top neighbour = cache[idx-6], left = cache[idx-1].
12714use rusty_h264_common::cabac_tables::CACHE30;
12715
12716/// z-order 4×4-block → raster index (openh264 g_kuiScan4). Per-MB mvd/ref state is
12717/// stored raster-indexed (matching how neighbour blocks 3/7/11/15 and 12..15 are read).
12718use rusty_h264_common::cabac_tables::G_SCAN4;
12719
12720/// P `sub_mb_type` CABAC (openh264 `ParseSubMBTypeCabac`, ctx 21). 0=8×8, 1=8×4, 2=4×8, 3=4×4.
12721fn parse_sub_mb_type_p_cabac(cab: &mut crate::cabac::Cabac) -> u32 {
12722    const S: usize = 21;
12723    let (data, mut e, ctx) = cab.view();
12724    let v = if e.decode_decision(data, ctx, S) != 0 {
12725        0
12726    } else if e.decode_decision(data, ctx, S + 1) != 0 {
12727        3 - e.decode_decision(data, ctx, S + 2)
12728    } else {
12729        1
12730    };
12731    cab.commit(e);
12732    v
12733}
12734
12735/// Intra `mb_type` sub-parse for P/B slices (openh264 `DecodeCabacIntraMbType`, `base`=32
12736/// for B), on a live engine view. Returns 0 = I_4x4, 1..=24 = I_16x16, 25 = I_PCM (in
12737/// the intra numbering).
12738#[inline(always)]
12739fn intra_mb_type_eng(e: &mut Eng, data: &[u8], ctx: &mut Ctx, base: usize) -> u32 {
12740    if e.decode_decision(data, ctx, base) == 0 {
12741        return 0; // I_4x4
12742    }
12743    if e.decode_terminate(data) {
12744        return 25; // I_PCM
12745    }
12746    let mut t = 1 + 12 * e.decode_decision(data, ctx, base + 1) as u32; // cbp_luma != 0
12747    if e.decode_decision(data, ctx, base + 2) != 0 {
12748        t += 4 + 4 * e.decode_decision(data, ctx, base + 2) as u32;
12749    }
12750    t += 2 * e.decode_decision(data, ctx, base + 3) as u32;
12751    t += e.decode_decision(data, ctx, base + 3) as u32;
12752    t
12753}
12754
12755/// B `mb_type` CABAC (openh264 `ParseMBTypeBSliceCabac`, ctx base 27). `ctx_inc` = (left
12756/// avail & !direct) + (top avail & !direct). Returns 0 = B_Direct_16x16, 1..=21 = the
12757/// L0/L1/Bi 16x16/16x8/8x16 shapes, 22 = B_8x8, 23.. = intra (mb_type - 23).
12758/// Test-only alias so the ENCODER crate can gate `cb_mb_type_b` against this
12759/// parser directly -- they are exact inverses, so a round-trip is a complete gate.
12760#[doc(hidden)]
12761pub fn parse_mb_type_b(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
12762    parse_mb_type_b_cabac(cab, ctx_inc)
12763}
12764
12765#[inline]
12766fn parse_mb_type_b_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
12767    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
12768    const B: usize = 27;
12769    let (data, mut e, ctx) = cab.view();
12770    let v = mb_type_b_eng(&mut e, data, ctx, B, ctx_inc);
12771    cab.commit(e);
12772    v
12773}
12774
12775#[inline(always)]
12776fn mb_type_b_eng(e: &mut Eng, data: &[u8], ctx: &mut Ctx, b: usize, ctx_inc: usize) -> u32 {
12777    if e.decode_decision(data, ctx, b + ctx_inc) == 0 {
12778        return 0; // B_Direct_16x16
12779    }
12780    if e.decode_decision(data, ctx, b + 3) == 0 {
12781        return 1 + e.decode_decision(data, ctx, b + 5); // 16x16 L0 / L1
12782    }
12783    let mut m = e.decode_decision(data, ctx, b + 4) << 3;
12784    m |= e.decode_decision(data, ctx, b + 5) << 2;
12785    m |= e.decode_decision(data, ctx, b + 5) << 1;
12786    m |= e.decode_decision(data, ctx, b + 5);
12787    if m < 8 {
12788        return m + 3;
12789    }
12790    if m == 13 {
12791        return intra_mb_type_eng(e, data, ctx, 32) + 23;
12792    }
12793    if m == 14 {
12794        return 11; // B_Bi_8x16
12795    }
12796    if m == 15 {
12797        return 22; // B_8x8
12798    }
12799    m = (m << 1) | e.decode_decision(data, ctx, b + 5);
12800    m - 4
12801}
12802
12803/// B `sub_mb_type` CABAC (openh264 `ParseBSubMBTypeCabac`, ctx base 36). Returns 0..=12
12804/// per spec Table 7-18 (0 = B_Direct_8x8, 1 = B_L0_8x8, ..., 12 = B_Bi_4x4).
12805fn parse_sub_mb_type_b_cabac(cab: &mut crate::cabac::Cabac) -> u32 {
12806    const B: usize = 36;
12807    let (data, mut e, ctx) = cab.view();
12808    let v = 'v: {
12809        if e.decode_decision(data, ctx, B) == 0 {
12810            break 'v 0; // B_Direct_8x8
12811        }
12812        if e.decode_decision(data, ctx, B + 1) == 0 {
12813            break 'v 1 + e.decode_decision(data, ctx, B + 3); // B_L0_8x8 / B_L1_8x8
12814        }
12815        let mut st = 3u32;
12816        if e.decode_decision(data, ctx, B + 2) != 0 {
12817            if e.decode_decision(data, ctx, B + 3) != 0 {
12818                break 'v 11 + e.decode_decision(data, ctx, B + 3); // B_L1_4x4 / B_Bi_4x4
12819            }
12820            st += 4;
12821        }
12822        st += 2 * e.decode_decision(data, ctx, B + 3);
12823        st += e.decode_decision(data, ctx, B + 3);
12824        st
12825    };
12826    cab.commit(e);
12827    v
12828}
12829
12830/// Parse one motion partition's `mvd` (x,y) and splat it into the 30-entry cache + the
12831/// per-MB raster mvd/ref state. `part_idx` = the partition's top-left z-order block (for
12832/// the ctxInc neighbour lookup); `zblocks` = every z-order 4×4 block the partition covers.
12833fn parse_mvd_partition(
12834    cab: &mut crate::cabac::Cabac,
12835    part_idx: usize,
12836    zblocks: &[usize],
12837    mvdc: &mut [[i16; 2]; 30],
12838    refc: &mut [i8; 30],
12839    mmvd: &mut [[i16; 2]; 16],
12840    mref: &mut [i8; 16],
12841    ref_idx: i8,
12842) -> (i32, i32) {
12843    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
12844    // `CACHE30` is [usize; 16] and every entry lies in [7, 28]; the mask and
12845    // clamp are semantic no-ops that hand LLVM the ranges it cannot infer, so
12846    // the `refc[s - 6]` / `mvdc[s - 1]` indexes into the 30-entry caches become
12847    // provable instead of bounds-checked.
12848    let s = CACHE30[part_idx & 15].clamp(6, 29);
12849    // Neighbour AVAILABILITY is per-neighbour, not per-component: the old closure
12850    // re-tested both refc slots for each of x and y, four loads to answer two
12851    // questions. Resolve once, then sum each component.
12852    let (av_b, av_a) = (refc[s - 6] >= 0, refc[s - 1] >= 0);
12853    let (nb, na) = (mvdc[s - 6], mvdc[s - 1]);
12854    let ctx = |comp: usize| -> usize {
12855        let mut a = 0i32;
12856        if av_b {
12857            a += nb[comp].unsigned_abs() as i32;
12858        }
12859        if av_a {
12860            a += na[comp].unsigned_abs() as i32;
12861        }
12862        if a >= 3 {
12863            1 + (a > 32) as usize
12864        } else {
12865            0
12866        }
12867    };
12868    let (cx, cy) = (ctx(0), ctx(1));
12869    // Both components on ONE engine view (see `Cabac::view`).
12870    let (data, mut e, ctx) = cab.view();
12871    let mvx = mvd_component(&mut e, data, ctx, 0, cx);
12872    let mvy = mvd_component(&mut e, data, ctx, 1, cy);
12873    cab.commit(e);
12874    let mvd = [mvx, mvy];
12875    if zblocks.len() == 16 {
12876        // Whole-macroblock partition (the dominant B shape): every raster slot
12877        // takes the same value — one fill each instead of 16 indexed stores.
12878        mmvd.fill(mvd);
12879        mref.fill(ref_idx);
12880        // The 16 z-blocks land on FOUR contiguous 4-entry runs of the 6-stride
12881        // cache (rows 7.., 13.., 19.., 25..): eight fills instead of 32 stores.
12882        for r in [7usize, 13, 19, 25] {
12883            mvdc[r..r + 4].fill(mvd);
12884            refc[r..r + 4].fill(ref_idx);
12885        }
12886    } else {
12887        for &zb in zblocks {
12888            // Each table was being read twice per block.
12889            let (c, g) = (CACHE30[zb & 15], G_SCAN4[zb & 15]);
12890            mvdc[c] = mvd;
12891            refc[c] = ref_idx;
12892            mmvd[g] = mvd;
12893            mref[g] = ref_idx;
12894        }
12895    }
12896    (mvx as i32, mvy as i32)
12897}
12898
12899/// `ref_idx_l0` (P) CABAC — mirror of the encoder `cb_ref_idx`. Unary, ctxIdxOffset
12900/// 54: binIdx 0 → `ctx0` (condTermFlagA + 2·condTermFlagB), binIdx 1 → 4, binIdx ≥2 → 5.
12901pub fn parse_ref_idx_cabac(cab: &mut crate::cabac::Cabac, ctx0: usize) -> i8 {
12902    const B: usize = 54;
12903    let (data, mut e, ctx) = cab.view();
12904    let mut r = 0i8;
12905    let mut bin_idx = 0u32;
12906    // Cap the unary length: valid ref_idx <= 15 (16 refs max); the cap keeps a corrupt
12907    // stream from looping unboundedly. The MC clamps the index, so an over-range value
12908    // is decoded as garbage (never a panic) -- the robustness contract, not correctness.
12909    while bin_idx < 32 {
12910        let c = match bin_idx {
12911            0 => ctx0,
12912            1 => 4,
12913            _ => 5,
12914        };
12915        if e.decode_decision(data, ctx, B + c) == 0 {
12916            break;
12917        }
12918        r += 1;
12919        bin_idx += 1;
12920    }
12921    cab.commit(e);
12922    r
12923}
12924
12925/// UEG3 mvd suffix (openh264 `DecodeUEGMvCabac`): TU prefix at `base + {0,1,2,3,3,..}`
12926/// (<=7), then EG3 bypass. Inlined into `parse_mvd_partition` for the same
12927/// register-residency reason as `cabac_ueg_level`.
12928#[inline(always)]
12929fn decode_ueg_mv(e: &mut Eng, data: &[u8], ctx: &mut Ctx, base: usize) -> u32 {
12930    const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
12931    if e.decode_decision(data, ctx, base) == 0 {
12932        return 0;
12933    }
12934    let mut code = 0u32;
12935    let mut count = 1usize;
12936    let mut tmp;
12937    loop {
12938        // `count` is 1..=7 here; `& 7` is the own bound of the 8-entry table.
12939        tmp = e.decode_decision(data, ctx, base + P2C[count & 7]);
12940        code += 1;
12941        count += 1;
12942        if tmp == 0 || count == 8 {
12943            break;
12944        }
12945    }
12946    if tmp != 0 {
12947        let (v, e2) = cabac_exp_bypass(*e, data, 3);
12948        *e = e2;
12949        code += v + 1;
12950    }
12951    code
12952}
12953
12954/// One `mvd` component (openh264 `ParseMvdInfoCabac`) on a live engine view.
12955/// `ctx_inc` (0/1/2) from the neighbour |mvd| sum. ctxIdxOffset 40 (x) / 47 (y).
12956#[inline(always)]
12957fn mvd_component(e: &mut Eng, data: &[u8], ctx: &mut Ctx, comp: usize, ctx_inc: usize) -> i16 {
12958    let base = 40 + comp * 7; // NEW_CTX_OFFSET_MVD + comp*CTX_NUM_MVD
12959    if e.decode_decision(data, ctx, base + ctx_inc) == 0 {
12960        return 0;
12961    }
12962    let mag = (decode_ueg_mv(e, data, ctx, base + 3) + 1) as i16;
12963    if e.decode_bypass(data) != 0 {
12964        -mag
12965    } else {
12966        mag
12967    }
12968}
12969
12970/// P-slice `mb_type` CABAC (openh264 `ParseMBTypePSliceCabac`). Returns 0..3 = inter
12971/// (P_L0_16x16 / P_16x8 / P_8x16 / P_8x8), 5 = I_4x4, 6..29 = I_16x16, 30 = I_PCM.
12972#[inline(always)]
12973fn mb_type_p_eng(e: &mut Eng, data: &[u8], ctx: &mut Ctx) -> u32 {
12974    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
12975    const S: usize = 11; // NEW_CTX_OFFSET_SKIP; P mb_type contexts hang off it
12976    let v = 'v: {
12977        if e.decode_decision(data, ctx, S + 3) == 0 {
12978            // inter
12979            break 'v if e.decode_decision(data, ctx, S + 4) != 0 {
12980                if e.decode_decision(data, ctx, S + 6) != 0 {
12981                    1
12982                } else {
12983                    2
12984                }
12985            } else if e.decode_decision(data, ctx, S + 5) != 0 {
12986                3
12987            } else {
12988                0
12989            };
12990        }
12991        // intra (prefix bit was 1)
12992        if e.decode_decision(data, ctx, S + 6) == 0 {
12993            break 'v 5; // I_4x4
12994        }
12995        if e.decode_terminate(data) {
12996            break 'v 30; // I_PCM
12997        }
12998        let mut t = 6 + e.decode_decision(data, ctx, S + 7) * 12;
12999        if e.decode_decision(data, ctx, S + 8) != 0 {
13000            t += 4;
13001            if e.decode_decision(data, ctx, S + 8) != 0 {
13002                t += 4;
13003            }
13004        }
13005        t += e.decode_decision(data, ctx, S + 9) << 1;
13006        t += e.decode_decision(data, ctx, S + 9);
13007        t
13008    };
13009    v
13010}
13011
13012/// I-slice `mb_type` CABAC parse (spec §9.3.2.5 / openh264 `ParseMBTypeISliceCabac`).
13013/// `ctx_inc` = (left MB is I_16x16/non-intra) + (top MB is …), i.e. 0..2; the corner
13014/// MB has no neighbours so `ctx_inc = 0`. Returns the raw mb_type: 0 = I_NxN (I_4x4/
13015/// I_8x8), 1..24 = I_16x16 (pred-mode/cbp packed), 25 = I_PCM.
13016fn parse_mb_type_i_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
13017    const O: usize = 3; // ctxIdxOffset for I-slice mb_type
13018    let (data, mut e, ctx) = cab.view();
13019    let v = 'v: {
13020        if e.decode_decision(data, ctx, O + ctx_inc) == 0 {
13021            break 'v 0; // I_NxN
13022        }
13023        if e.decode_terminate(data) {
13024            break 'v 25; // I_PCM
13025        }
13026        let mut t = 1 + e.decode_decision(data, ctx, O + 3) * 12; // CBP luma: 0 or 12
13027        if e.decode_decision(data, ctx, O + 4) != 0 {
13028            t += 4; // CBP chroma 1 or 2
13029            if e.decode_decision(data, ctx, O + 5) != 0 {
13030                t += 4;
13031            }
13032        }
13033        t += e.decode_decision(data, ctx, O + 6) << 1; // I_16x16 pred mode (2 bins)
13034        t += e.decode_decision(data, ctx, O + 7);
13035        t
13036    };
13037    cab.commit(e);
13038    v
13039}
13040
13041/// One `Intra_4x4` (or `8x8`) pred-mode parse on a live engine view (openh264
13042/// `ParseIntraPredModeLumaCabac`): `prev_intra4x4_pred_mode_flag` (ctx 68) then, if 0,
13043/// `rem_intra4x4_pred_mode` (3 bins at ctx 69). Returns `-1` for "use predicted
13044/// mode", else the 0..7 remainder. The 16 (or 4) reads of a macroblock share ONE view.
13045#[inline(always)]
13046fn intra4x4_pred_mode_eng(e: &mut Eng, data: &[u8], ctx: &mut Ctx) -> i32 {
13047    const IPR: usize = 68;
13048    if e.decode_decision(data, ctx, IPR) == 1 {
13049        return -1; // prev_intra4x4_pred_mode_flag = 1
13050    }
13051    let mut m = e.decode_decision(data, ctx, IPR + 1) as i32;
13052    m |= (e.decode_decision(data, ctx, IPR + 1) as i32) << 1;
13053    m |= (e.decode_decision(data, ctx, IPR + 1) as i32) << 2;
13054    m
13055}
13056
13057/// One `Intra_4x4` (or `8x8`) pred-mode CABAC parse (openh264 `ParseIntraPredModeLuma
13058/// Cabac`): `prev_intra4x4_pred_mode_flag` (ctx 68) then, if 0, `rem_intra4x4_pred_mode`
13059/// (3 bins at ctx 69). Returns `-1` for "use predicted mode", else the 0..7 remainder.
13060
13061/// `intra_chroma_pred_mode` CABAC parse (openh264 `ParseIntraPredModeChromaCabac`):
13062/// TU(cMax=3) — bin0 at ctx `64 + ctx_inc` (ctx_inc from neighbour chroma modes, 0 for
13063/// the corner MB), the rest at ctx 67. Returns the mode 0..3.
13064fn parse_intra_chroma_pred_mode_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
13065    const CIPR: usize = 64;
13066    if cab.decode_decision(CIPR + ctx_inc) == 0 {
13067        return 0;
13068    }
13069    if cab.decode_decision(CIPR + 3) == 0 {
13070        return 1;
13071    }
13072    if cab.decode_decision(CIPR + 3) == 0 {
13073        return 2;
13074    }
13075    3
13076}
13077
13078/// `coded_block_pattern` CABAC parse (openh264 `ParseCbpInfoCabac`), corner-MB variant
13079/// (top/left neighbours unavailable → their terms are 0). ctxIdxOffset 73 (luma) with 4
13080/// z-order 8×8 bins whose ctxInc uses the EARLIER-decoded bits within this MB, then
13081/// chroma bits at 77/81. Returns cbp: bits 0-3 = luma 8×8, bits 4-5 = chroma pattern.
13082pub fn parse_cbp_cabac(cab: &mut crate::cabac::Cabac, top: Option<u8>, left: Option<u8>) -> u32 {
13083    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
13084    const CBP: usize = 73;
13085    // Neighbour terms folded ONCE into two 4-bit masks: bit k of `tz` / `lz` is
13086    // the condTermFlag "the 8x8 block k of that neighbour is NOT coded"
13087    // (unavailable => 0). The six per-bin `Option::map_or` closures this
13088    // replaces re-matched the same two Options for every luma bin.
13089    let tz = top.map_or(0u32, |c| !(c as u32) & 0xF);
13090    let lz = left.map_or(0u32, |c| !(c as u32) & 0xF);
13091    let tc = top.map_or(0u32, |c| (c >> 4) as u32);
13092    let lc = left.map_or(0u32, |c| (c >> 4) as u32);
13093    let (data, mut e, ctx) = cab.view();
13094    // Luma, 4 8x8 blocks in z-order. Top uses cbp bits 2/3, left uses 1/3; a
13095    // block already decoded in THIS macroblock contributes `bin ^ 1`.
13096    let b0 = e.decode_decision(
13097        data,
13098        ctx,
13099        CBP + (((lz >> 1) & 1) + (((tz >> 2) & 1) << 1)) as usize,
13100    );
13101    let b1 = e.decode_decision(
13102        data,
13103        ctx,
13104        CBP + ((b0 ^ 1) + (((tz >> 3) & 1) << 1)) as usize,
13105    );
13106    let b2 = e.decode_decision(
13107        data,
13108        ctx,
13109        CBP + (((lz >> 3) & 1) + ((b0 ^ 1) << 1)) as usize,
13110    );
13111    let b3 = e.decode_decision(data, ctx, CBP + ((b2 ^ 1) + ((b1 ^ 1) << 1)) as usize);
13112    let mut cbp = b0 | (b1 << 1) | (b2 << 2) | (b3 << 3);
13113    // Chroma (4:2:0). ctxInc from neighbour chroma cbp (>>4).
13114    let (ct, cl) = ((tc != 0) as u32, (lc != 0) as u32);
13115    if e.decode_decision(data, ctx, CBP + 4 + (cl + (ct << 1)) as usize) != 0 {
13116        let (ct2, cl2) = ((tc == 2) as u32, (lc == 2) as u32);
13117        let c1 = e.decode_decision(data, ctx, CBP + 8 + (cl2 + (ct2 << 1)) as usize);
13118        cbp |= 1 << (4 + c1);
13119    }
13120    cab.commit(e);
13121    cbp
13122}
13123
13124/// Inlined: `num_ref_active` is loop-invariant at every partition site, so the
13125/// te(v) branch folds and the bit/ue read lands in the caller with no call.
13126#[inline]
13127fn read_ref_idx(r: &mut BitReader, num_ref_active: usize) -> Result<i32, OutOfData> {
13128    if num_ref_active == 2 {
13129        Ok(if r.read_bit()? { 0 } else { 1 }) // te(v): value = !bit
13130    } else {
13131        Ok(r.read_ue()? as i32)
13132    }
13133}
13134
13135/// B-partition prediction direction.
13136#[derive(Clone, Copy, PartialEq)]
13137enum BPred {
13138    L0,
13139    L1,
13140    Bi,
13141}
13142impl BPred {
13143    /// Whether this direction uses reference list `list` (0 or 1).
13144    fn uses(self, list: usize) -> bool {
13145        matches!(
13146            (self, list),
13147            (BPred::L0, 0) | (BPred::L1, 1) | (BPred::Bi, 0) | (BPred::Bi, 1)
13148        )
13149    }
13150}
13151
13152const B16X16: &[(usize, usize, usize, usize)] = &[(0, 0, 16, 16)];
13153const B16X8: &[(usize, usize, usize, usize)] = &[(0, 0, 16, 8), (0, 8, 16, 8)];
13154const B8X16: &[(usize, usize, usize, usize)] = &[(0, 0, 8, 16), (8, 0, 8, 16)];
13155
13156/// A partition region `(x, y, w, h)` in samples.
13157type Region = (usize, usize, usize, usize);
13158
13159/// B `mb_type` 1..=21 → (partition layout, MV-prediction mode 0/1/2 for 16×16/
13160/// 16×8/8×16, per-partition prediction direction) (spec Table 7-14).
13161/// Test-only view of [`b_inter_layout`] for the ENCODER crate: `(mvmode, p0, p1)`
13162/// with pred coded 1 = L0, 2 = L1, 3 = Bi — the encoder's `b_part_mb_type` is the
13163/// exact inverse, so a round-trip over 4..=21 gates the two tables against drift.
13164pub fn b_inter_shape(mb_type: u32) -> (u8, u8, u8) {
13165    let (_, mvmode, preds) = b_inter_layout(mb_type);
13166    let code = |p: BPred| match (p.uses(0), p.uses(1)) {
13167        (true, true) => 3,
13168        (true, false) => 1,
13169        _ => 2,
13170    };
13171    (mvmode, code(preds[0]), code(preds[1]))
13172}
13173
13174fn b_inter_layout(mb_type: u32) -> (&'static [Region], u8, [BPred; 2]) {
13175    use BPred::*;
13176    match mb_type {
13177        1 => (B16X16, 0, [L0, L0]),
13178        2 => (B16X16, 0, [L1, L1]),
13179        3 => (B16X16, 0, [Bi, Bi]),
13180        4 => (B16X8, 1, [L0, L0]),
13181        5 => (B8X16, 2, [L0, L0]),
13182        6 => (B16X8, 1, [L1, L1]),
13183        7 => (B8X16, 2, [L1, L1]),
13184        8 => (B16X8, 1, [L0, L1]),
13185        9 => (B8X16, 2, [L0, L1]),
13186        10 => (B16X8, 1, [L1, L0]),
13187        11 => (B8X16, 2, [L1, L0]),
13188        12 => (B16X8, 1, [L0, Bi]),
13189        13 => (B8X16, 2, [L0, Bi]),
13190        14 => (B16X8, 1, [L1, Bi]),
13191        15 => (B8X16, 2, [L1, Bi]),
13192        16 => (B16X8, 1, [Bi, L0]),
13193        17 => (B8X16, 2, [Bi, L0]),
13194        18 => (B16X8, 1, [Bi, L1]),
13195        19 => (B8X16, 2, [Bi, L1]),
13196        20 => (B16X8, 1, [Bi, Bi]),
13197        _ => (B8X16, 2, [Bi, Bi]), // 21
13198    }
13199}
13200
13201/// Whether a B `sub_mb_type` (1..=12) uses reference list `list`.
13202fn b_sub_uses(st: u32, list: usize) -> bool {
13203    let pred = match st {
13204        1 | 4 | 5 | 10 => 0, // L0
13205        2 | 6 | 7 | 11 => 1, // L1
13206        _ => 2,              // Bi (3, 8, 9, 12)
13207    };
13208    (list == 0 && pred != 1) || (list == 1 && pred != 0)
13209}
13210
13211/// Sub-partition shapes within an 8×8 for a B `sub_mb_type` (1..=12).
13212fn b_sub_parts(st: u32) -> &'static [(usize, usize, usize, usize)] {
13213    match st {
13214        1..=3 => &[(0, 0, 8, 8)],
13215        4 | 6 | 8 => &[(0, 0, 8, 4), (0, 4, 8, 4)],
13216        5 | 7 | 9 => &[(0, 0, 4, 8), (4, 0, 4, 8)],
13217        _ => &[(0, 0, 4, 4), (4, 0, 4, 4), (0, 4, 4, 4), (4, 4, 4, 4)], // 10/11/12
13218    }
13219}
13220
13221/// Sub-macroblock partition layout `(x, y, w, h)` in samples within an 8×8, for
13222/// a P-slice `sub_mb_type` (0 = 8×8, 1 = 8×4, 2 = 4×8, 3 = 4×4).
13223fn sub_mb_partitions(sub_type: u32) -> &'static [(usize, usize, usize, usize)] {
13224    match sub_type {
13225        0 => &[(0, 0, 8, 8)],
13226        1 => &[(0, 0, 8, 4), (0, 4, 8, 4)],
13227        2 => &[(0, 0, 4, 8), (4, 0, 4, 8)],
13228        _ => &[(0, 0, 4, 4), (4, 0, 4, 4), (0, 4, 4, 4), (4, 4, 4, 4)],
13229    }
13230}
13231
13232/// Copy a contiguous `w`x`h` block into a strided destination at `(x0, y0)`.
13233///
13234/// The width is SPECIALISED. Written as a per-pixel loop bounded by a runtime `w`,
13235/// this lowers to a bounds-checked store per pixel — and where it is a row copy of
13236/// runtime length, to a variable-length `memcpy` CALL per row. Both are the same
13237/// codegen trap the ENCODER fixed long ago ("H-17"); the decoder's copy of it was
13238/// never fixed, and it costs the most on exactly the streams a real encoder emits,
13239/// because x264's sub-16x16 partitions call it far more often than our own
13240/// 16x16-dominated bitstreams ever did. Byte-identical to the scalar form.
13241#[inline]
13242fn restride(
13243    dst: &mut [u8],
13244    dst_stride: usize,
13245    x0: usize,
13246    y0: usize,
13247    src: &[u8],
13248    w: usize,
13249    h: usize,
13250) {
13251    macro_rules! rows {
13252        ($n:expr) => {{
13253            for dy in 0..h {
13254                dst[(y0 + dy) * dst_stride + x0..][..$n].copy_from_slice(&src[dy * $n..][..$n]);
13255            }
13256        }};
13257    }
13258    match w {
13259        16 => rows!(16),
13260        8 => rows!(8),
13261        4 => rows!(4),
13262        2 => rows!(2),
13263        _ => {
13264            for dy in 0..h {
13265                dst[(y0 + dy) * dst_stride + x0..][..w].copy_from_slice(&src[dy * w..][..w]);
13266            }
13267        }
13268    }
13269}
13270
13271/// Un-scans an 8×8 block from frame zig-zag scan order to raster (spec Table 8-12).
13272fn un_scan_8x8(scan: &[i32; 64]) -> [i32; 64] {
13273    const ZZ8: [usize; 64] = [
13274        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,
13275        20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
13276        58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
13277    ];
13278    let mut out = [0i32; 64];
13279    for k in 0..64 {
13280        out[ZZ8[k]] = scan[k];
13281    }
13282    out
13283}
13284
13285#[cfg(test)]
13286mod tests {
13287    use super::*;
13288
13289    fn fd(qp: u8, offset: i32) -> FrameDecoder {
13290        FrameDecoder::new(1, 1, qp, offset, Vec::new(), 1, false, false, true)
13291    }
13292
13293    #[test]
13294    fn mb_qp_delta_accumulates_mod_52() {
13295        let mut d = fd(26, 0);
13296        assert_eq!(d.cur_qp, 26, "QPy starts at the slice QP");
13297        d.step_qp(4).unwrap();
13298        assert_eq!(d.cur_qp, 30); // 26 + 4
13299        d.step_qp(-10).unwrap();
13300        assert_eq!(d.cur_qp, 20); // carries from the previous MB, not the slice
13301                                  // Wrap-around: (20 + 40 + 52) % 52 = 112 % 52 = 8.
13302        d.step_qp(40).unwrap();
13303        assert_eq!(d.cur_qp, 8);
13304        // Negative wrap: (8 - 20 + 52) % 52 = 40.
13305        d.step_qp(-20).unwrap();
13306        assert_eq!(d.cur_qp, 40);
13307    }
13308
13309    #[test]
13310    fn chroma_qp_index_offset_applied_and_clamped() {
13311        // Offset 0 reproduces the bare luma->chroma table (QP30 -> 29).
13312        assert_eq!(fd(0, 0).chroma_qp_for(30), 29);
13313        // Positive offset shifts the table lookup (QP30 + 2 -> table[2] = 31).
13314        assert_eq!(fd(0, 2).chroma_qp_for(30), 31);
13315        // The qPi index is clamped into 0..=51 before the lookup.
13316        assert_eq!(fd(0, -12).chroma_qp_for(5), chroma_qp(0));
13317        assert_eq!(fd(0, 99).chroma_qp_for(40), chroma_qp(51));
13318    }
13319}