Skip to main content

rusty_h264_encoder/
mb16.rs

1//! I_16x16 macroblock encoding (DC prediction) — the compressing intra path.
2//!
3//! Index-based loops below drive pixel/block-position arithmetic and read
4//! clearer than iterator adapters for this raster math.
5#![allow(clippy::needless_range_loop)]
6//!
7//! Each macroblock is DC-predicted from already-reconstructed neighbors, the
8//! residual is transformed/quantized (luma DC via the secondary Hadamard), the
9//! coefficients are CAVLC-coded, and the macroblock is reconstructed so the next
10//! one can predict from it. `nnz` grids feed the CAVLC `nC` context exactly as a
11//! conforming decoder derives it.
12
13use crate::cabac::CabacEncoder;
14use crate::config::EncoderConfig;
15use rusty_h264_common::cavlc::{
16    encode_residual_block, scan_4x4_ac, scan_4x4_dcac, write_cbp_inter, write_cbp_intra,
17};
18use rusty_h264_common::inter::{
19    inter_partitions, mc_chroma, mc_luma, predict_mv, predict_partition_mv, MvNeighbor,
20};
21use rusty_h264_common::predict::{
22    add_residual_4x4, add_residual_8x8, chroma8x8_pred, chroma_mode_available, chroma_qp,
23    intra4x4_pred, intra8x8_pred, luma16x16_pred, reconstruct_4x4, I16Mode, CHROMA_4X4_SCAN_XY,
24    LUMA_4X4_SCAN_XY,
25};
26use rusty_h264_common::transform::{
27    dequantize, forward_core, forward_core_8x8, forward_dct_blocks, forward_quant_chroma_dc,
28    forward_quant_luma_dc, inverse_dct_blocks, inverse_quant_8x8, inverse_quant_chroma_dc,
29    inverse_quant_luma_dc, quantize, quantize_8x8, satd_4x4_sum,
30};
31use rusty_h264_common::aligned::AlignedBytes;
32use rusty_h264_common::{BitWriter, YuvFrame};
33
34/// A/B switch for the batched full-pel rescue grid (`RFF_ME_BATCH=0` disables).
35///
36/// Read ONCE per process, not per call: this sits inside the motion-search rescue
37/// path, and `std::env::var` allocates a `String` and takes the process-wide
38/// environment lock every time. A runtime switch inside a hot loop is its own
39/// measurable tax — cache it.
40
41
42/// λ-normalised threshold for the partition-split search gate (U2).
43///
44/// The existing `split_gate` is a function of qstep ALONE, so it does not scale with
45/// the rate/distortion trade the search is actually making. Normalising the null arm
46/// by λ — the king feature for any search-skip gate — makes one constant transfer
47/// across content AND the whole QP ladder, and in the SAFE direction: the feature is
48/// small exactly where the 16×16 null arm is already good, so easy content skips more.
49///
50/// Harvested over 36 k gated macroblocks (4 clips): at T = 400 the split search is
51/// skipped on 2.9–22.5% of them while keeping **100.00%** of the achievable cost gain
52/// on every clip; T = 600 skips 11–79% for 93–99% kept. `RFF_SPLIT_T=0` disables.
53pub(crate) static DEFER_SUBPEL: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
54
55pub(crate) static SPLIT_T: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
56
57fn split_t() -> f64 {
58    let v = SPLIT_T.load(std::sync::atomic::Ordering::Relaxed);
59    if v != u32::MAX {
60        return v as f64;
61    }
62    let d: u32 = std::env::var("RFF_SPLIT_T").ok().and_then(|s| s.parse().ok()).unwrap_or(0);
63    SPLIT_T.store(d, std::sync::atomic::Ordering::Relaxed);
64    d as f64
65}
66
67/// Observe-only HARVEST for the PARTITION-split gate (U2/U5).
68///
69/// The 16×16 search is the null arm and runs first; the 2-way splits and P_8x8 are
70/// the expensive arm (7 further `best_part` calls, each with its own full-pel search
71/// AND sub-pel refinement). Today they are gated by a fixed `split_gate` formula.
72/// Records the null-arm cost, the best split cost, and which won, so the
73/// skip-rate-vs-gain-kept ceiling can be swept before any threshold is touched.
74mod split_harvest {
75    use std::fs::File;
76    use std::io::Write;
77    use std::sync::{Mutex, OnceLock};
78
79    fn sink() -> &'static Option<Mutex<File>> {
80        static S: OnceLock<Option<Mutex<File>>> = OnceLock::new();
81        S.get_or_init(|| {
82            std::env::var("RFF_SPLIT_HARVEST").ok().and_then(|p| {
83                let mut f = File::create(p).ok()?;
84                let _ = writeln!(f, "c16,best,lambda,gate,won");
85                Some(Mutex::new(f))
86            })
87        })
88    }
89
90    #[inline]
91    pub fn enabled() -> bool {
92        sink().is_some()
93    }
94
95    pub fn record(c16: i64, best: i64, lambda: f64, gate: i64, won: u8) {
96        if let Some(m) = sink() {
97            if let Ok(mut f) = m.lock() {
98                let _ = writeln!(f, "{c16},{best},{lambda:.4},{gate},{won}");
99            }
100        }
101    }
102}
103
104/// Descent C escape hatch: `RFF_HPEL_REF=0` restores the copy-then-SATD half-pel path
105/// (byte-identical to it either way — this exists as a bisection anchor).
106fn hpel_ref_enabled() -> bool {
107    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
108    *E.get_or_init(|| std::env::var("RFF_HPEL_REF").map(|v| v != "0").unwrap_or(true))
109}
110
111/// Challenge-1 A3 escape hatch: `RFF_SATD_AVG=0` restores the materialize-then-SATD
112/// quarter-pel cost path (byte-identical either way — a bisection anchor, like
113/// `RFF_HPEL_REF`).
114fn satd_avg_enabled() -> bool {
115    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
116    *E.get_or_init(|| std::env::var("RFF_SATD_AVG").map(|v| v != "0").unwrap_or(true))
117}
118
119/// Track-B B2 (docs/lets-win-optimize.md): run the FULL-PEL phase of the non-fast
120/// motion search in the SAD domain (`psadbw`-class, ~3-4× cheaper per candidate) and
121/// reprice the winner in SATD before the rescue/sub-pel phases — the cost split
122/// every x264 preset uses (SAD fpel, SATD from subme≥2). ⚠ BITSTREAM-CHANGING (a
123/// different full-pel winner can emerge), so it ships opt-in until the per-clip
124/// 4-QP BD gate clears it. `set_me_sadfp` overrides; unset → `RFF_ME_SADFP` env,
125/// default OFF (off = byte-identical to the pre-B2 encoder).
126/// Modes: 0 = off (byte-identical), 1 = DISPATCHED per frame by the `b2_mgain`
127/// probe (the shipping shape), 2 = force-on everywhere (the truth-table A/B arm).
128static ME_SADFP: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
129pub fn set_me_sadfp(on: bool) {
130    // Harness semantics preserved: `true` = the force-on arm truth tables measure.
131    ME_SADFP.store(if on { 2 } else { 0 }, core::sync::atomic::Ordering::Relaxed)
132}
133pub fn set_me_sadfp_mode(m: u32) {
134    ME_SADFP.store(m.min(2), core::sync::atomic::Ordering::Relaxed)
135}
136fn me_sadfp_mode() -> u32 {
137    match ME_SADFP.load(core::sync::atomic::Ordering::Relaxed) {
138        u32::MAX => {
139            static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
140            // DEFAULT = 1 (dispatched) since the H-3 gate: 16-clip corpus mean
141            // −0.26% BD, wins bus −1.71 / football −1.84 / foreman −0.44 /
142            // shields −0.22, every former loss 0.00; residual tail (soccer +0.09,
143            // harbour +0.06) is BD-fit noise — it responds NON-monotonically to
144            // threshold changes (less B2 made soccer read WORSE, +0.18).
145            // `RFF_ME_SADFP=0` is the escape hatch reproducing the pre-B2 bytes.
146            *INIT.get_or_init(|| {
147                std::env::var("RFF_ME_SADFP").ok().and_then(|v| v.parse().ok()).unwrap_or(1)
148            })
149        }
150        m => m,
151    }
152}
153
154/// B2 dispatch threshold on the per-frame `b2_mgain` probe (`RFF_ME_SADT`).
155/// Calibrated on the DEPLOYED estimator (recon reference, sampled MBs), not the
156/// offline source-frame probe — the recurring R6 law.
157fn me_sadt() -> f64 {
158    static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
159    *T.get_or_init(|| std::env::var("RFF_ME_SADT").ok().and_then(|s| s.parse().ok()).unwrap_or(0.13))
160}
161fn me_sadt_dbg() -> bool {
162    static D: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
163    *D.get_or_init(|| std::env::var_os("RFF_ME_SADT_DBG").is_some())
164}
165
166/// Fixed-centre batched diamond passes on SAD-routed frames (`RFF_ME_FC=0` falls
167/// back to the cascading scalar walk — the bisection anchor). Fixed-centre differs
168/// from the cascade only when 2+ points improve in one pass, so it rides B2's BD
169/// gate; dispatched-OFF frames never take this path and stay byte-identical.
170/// ③ Sub-pel ring FC: fixed-centre argmin passes for the HALF-PEL step, batched
171/// through `satd_16x16_x4p` (two calls cover the 8-ring; candidates resolve to
172/// h/h/v/v and c/c/c/c plane reads from an integer centre). Quarter-step and any
173/// declined pass keep the cascading walk. Bitstream-changing → own gate
174/// (`AB_SPFC`), `RFF_SP_FC=0` anchor.
175static SP_FC: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
176pub fn set_sp_fc(on: bool) {
177    SP_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
178}
179fn sp_fc_enabled() -> bool {
180    match SP_FC.load(core::sync::atomic::Ordering::Relaxed) {
181        0 => false,
182        1 => true,
183        _ => {
184            static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
185            *E.get_or_init(|| std::env::var("RFF_SP_FC").map(|v| v != "0").unwrap_or(false))
186        }
187    }
188}
189
190static ME_FC: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
191pub fn set_me_fc(on: bool) {
192    ME_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
193}
194fn me_fc_enabled() -> bool {
195    match ME_FC.load(core::sync::atomic::Ordering::Relaxed) {
196        0 => false,
197        1 => true,
198        _ => {
199            static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
200            *E.get_or_init(|| std::env::var("RFF_ME_FC").map(|v| v != "0").unwrap_or(true))
201        }
202    }
203}
204
205/// The flash veto: frames whose zero-MV residual is DC-shift-dominated beyond this
206/// fraction route OFF even at high mgain (`RFF_ME_SADDC`). Calibrated on the
207/// DEPLOYED per-frame values: crew's harmful ON-frames read dc 0.843–0.859 (the
208/// camera flashes) while every good ON-frame on bus/football/foreman reads ≤ 0.478
209/// — a 1.76× natural gap; 0.6 sits mid-gap with margin both ways.
210fn me_sad_dcmax() -> f64 {
211    static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
212    *T.get_or_init(|| std::env::var("RFF_ME_SADDC").ok().and_then(|s| s.parse().ok()).unwrap_or(0.6))
213}
214
215/// The B2 dispatch signal: mean over ~24 sampled interior MBs of
216/// `(SAD@zeroMV − bestSAD over a ±8 step-4 full-pel grid) / SAD@zeroMV` — how much
217/// a plain TRANSLATIONAL full-pel search improves on zero motion, i.e. exactly the
218/// surface B2's SAD diamond exploits. Offline (b2_signals, 16-clip truth table) it
219/// separates every B2 loss (crew flash 0.070, city 0.110, tempete 0.008) from
220/// every meaningful win (bus 0.323, football/foreman 0.164/0.165, shields 0.361);
221/// notably `me_wide_headroom` CANNOT be reused here — crew's headroom is high (20)
222/// but B2 loses there, because SAD overprices the DC shifts of its camera flashes.
223/// Returns `(mgain, dcfrac)`. `dcfrac` — mean `|Σcur − Σref| / SAD0` per sampled
224/// block — is the FLASH detector: under an illumination change the zero-MV residual
225/// is mostly a DC shift, which SAD prices fully but the Hadamard largely discounts,
226/// so SAD misranks candidates exactly there. Justified by the one clip the
227/// single-term gate got wrong (crew: high mgain on its motion frames, +0.54 BD).
228fn b2_mgain(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> (f64, f64) {
229    const WIDE: isize = 8;
230    const STEP: isize = 4;
231    const TARGET: usize = 24;
232    let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
233        if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
234            return None;
235        }
236        let (rx, ry) = (rx as usize, ry as usize);
237        let mut s = 0u32;
238        for dy in 0..16 {
239            let a = &sy[(by + dy) * cw + bx..][..16];
240            let b = &ref_y[(ry + dy) * cw + rx..][..16];
241            s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
242        }
243        Some(s)
244    };
245    let (mbw, mbh) = (cw / 16, ch / 16);
246    if mbw < 6 || mbh < 6 {
247        return (0.0, 0.0);
248    }
249    let inner = (mbw - 4) * (mbh - 4);
250    let stride = (inner / TARGET).max(1);
251    let (mut acc, mut dc, mut n) = (0.0f64, 0.0f64, 0u32);
252    let mut i = 0usize;
253    while i < inner {
254        let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
255        let (bx, by) = (mx * 16, my * 16);
256        if let Some(s0) = sad16(bx, by, bx as isize, by as isize) {
257            let (mut ms, mut mr) = (0u32, 0u32);
258            for dy in 0..16 {
259                ms += sy[(by + dy) * cw + bx..][..16].iter().map(|&v| v as u32).sum::<u32>();
260                mr += ref_y[(by + dy) * cw + bx..][..16].iter().map(|&v| v as u32).sum::<u32>();
261            }
262            dc += ms.abs_diff(mr) as f64 / (s0 + 1) as f64;
263            let mut best = s0;
264            let mut dy = -WIDE;
265            while dy <= WIDE {
266                let mut dx = -WIDE;
267                while dx <= WIDE {
268                    if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
269                        best = best.min(s);
270                    }
271                    dx += STEP;
272                }
273                dy += STEP;
274            }
275            acc += (s0 - best) as f64 / (s0 + 1) as f64;
276            n += 1;
277        }
278        i += stride;
279    }
280    if n == 0 { (0.0, 0.0) } else { (acc / n as f64, dc / n as f64) }
281}
282
283/// Track-B B3: cap on sub-pel ring ITERATIONS per step (`RFF_SP_MAXIT` /
284/// `set_sp_maxit`). 0 = unlimited (the default — byte-identical to the walk-to-
285/// convergence encoder); N caps each step's walk at N passes, the bounded budget
286/// x264's subme levels have always had. Bitstream-changing when set → BD-gated.
287static SP_MAXIT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
288pub fn set_sp_maxit(n: u32) {
289    SP_MAXIT.store(n, core::sync::atomic::Ordering::Relaxed)
290}
291fn sp_maxit() -> u32 {
292    match SP_MAXIT.load(core::sync::atomic::Ordering::Relaxed) {
293        u32::MAX => {
294            static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
295            *INIT.get_or_init(|| {
296                std::env::var("RFF_SP_MAXIT").ok().and_then(|v| v.parse().ok()).unwrap_or(0)
297            })
298        }
299        n => n,
300    }
301}
302
303/// B2 calibration: λ multiplier for the SAD-domain full-pel phase (`RFF_ME_SADL`,
304/// default 1.0). SATD distortion runs ~2× SAD's scale, so λ tuned for SATD weighs
305/// the rate term ~2× heavier in the SAD domain — 0.5 restores the SATD-era
306/// rate/distortion balance. Read once per process (hoisted per search).
307fn me_sadfp_lambda() -> f64 {
308    static E: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
309    // 0.5 = the calibrated default (SATD ≈ 2× SAD's scale; at 1.0 the rate term
310    // weighs double and foreman flips to a BD loss). Rides with the mode-1 default.
311    *E.get_or_init(|| {
312        std::env::var("RFF_ME_SADL").ok().and_then(|v| v.parse().ok()).unwrap_or(0.5)
313    })
314}
315
316/// Descent D: sub-pel ring census — evals/improvements by (step, ring position) and
317/// by loop ITERATION, so a position or an iteration that never pays is visible rather
318/// than assumed.
319#[cfg(feature = "profile")]
320pub mod spstats {
321    use core::sync::atomic::{AtomicU64, Ordering};
322    /// [step 0=half,1=quarter][position 0..8][0=evals,1=improvements]
323    pub static POS: [AtomicU64; 2 * 8 * 2] = [const { AtomicU64::new(0) }; 32];
324    /// [step][iteration 1..=6 clamped][0=evals,1=improvements]
325    pub static IT: [AtomicU64; 2 * 6 * 2] = [const { AtomicU64::new(0) }; 24];
326    #[inline]
327    pub fn ev(st: usize, pos: usize, it: u32) {
328        POS[(st * 8 + pos.min(7)) * 2].fetch_add(1, Ordering::Relaxed);
329        IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2].fetch_add(1, Ordering::Relaxed);
330    }
331    #[inline]
332    pub fn imp(st: usize, pos: usize, it: u32) {
333        POS[(st * 8 + pos.min(7)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
334        IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
335    }
336    /// Sub-pel evaluations that re-price an MV already evaluated in the SAME refinement.
337    pub static REDUNDANT: AtomicU64 = AtomicU64::new(0);
338    #[inline]
339    pub fn redundant() { REDUNDANT.fetch_add(1, Ordering::Relaxed); }
340    pub fn reset() {
341        for c in POS.iter() { c.store(0, Ordering::Relaxed); }
342        for c in IT.iter() { c.store(0, Ordering::Relaxed); }
343        REDUNDANT.store(0, Ordering::Relaxed);
344    }
345    pub fn snapshot() -> (Vec<u64>, Vec<u64>) {
346        (POS.iter().map(|c| c.load(Ordering::Relaxed)).collect(),
347         IT.iter().map(|c| c.load(Ordering::Relaxed)).collect())
348    }
349    pub fn redundant_count() -> u64 { REDUNDANT.load(Ordering::Relaxed) }
350}
351
352/// Descent B: which path does each ME cost evaluation actually take?
353#[cfg(feature = "profile")]
354pub mod satdpath {
355    use core::sync::atomic::{AtomicU64, Ordering};
356    pub static C: [AtomicU64; 3] = [const { AtomicU64::new(0) }; 3];
357    #[inline]
358    pub fn bump(i: usize) { C[i].fetch_add(1, Ordering::Relaxed); }
359    pub fn reset() { for c in C.iter() { c.store(0, Ordering::Relaxed); } }
360    pub fn snapshot() -> Vec<u64> { C.iter().map(|c| c.load(Ordering::Relaxed)).collect() }
361}
362
363/// The coarse-to-fine step ladder. DEFAULT `[16,8,4]` — the 64 and 32 rungs were
364/// REMOVED after the per-rung census showed they are ~39% of full-pel evaluations at a
365/// 0.05-0.84% hit rate, and the 20-clip 4-QP BD curve showed those rare hits are actively
366/// HARMFUL: a coarse jump finds a distant MV with marginally lower SATD, but it costs
367/// more mvd bits AND breaks the spatial coherence of the MV field, degrading every
368/// downstream neighbour's predictor. `lambda*mvbits` prices the first effect and is blind
369/// to the second. Dropping them is mean -0.93% BD-PSNR / -1.09% BD-SSIM with a WORST clip
370/// of +0.00%/+0.00% over 20 clips, and 1.15-1.57x fewer ME cost evaluations.
371///
372/// The 8 rung is load-bearing: `[16,4]` reads marginally better BD but makes football_cif
373/// do 1.55x MORE work, because the step-4 walk then has to crawl the distance the 8 rung
374/// covered in one hop. Reach and stride both matter; only the useless TOP is removed.
375///
376/// Bit i of the mask enables rung i of [64,32,16,8,4]. `RFF_DIA_LADDER=64,32,16,8,4`
377/// restores the pre-change ladder byte-for-byte; `set_dia_mask` overrides at runtime so a
378/// single process can measure several ladders.
379pub const DIA_RUNGS: [i32; 5] = [64, 32, 16, 8, 4];
380/// Rungs walked by default: `[16,8,4]`.
381pub const DIA_DEFAULT: u32 = 0b11100;
382pub static DIA_MASK: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
383pub fn set_dia_mask(m: u32) { DIA_MASK.store(m, core::sync::atomic::Ordering::Relaxed) }
384fn dia_mask() -> u32 {
385    let m = DIA_MASK.load(core::sync::atomic::Ordering::Relaxed);
386    if m != u32::MAX { return m; }
387    static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
388    *INIT.get_or_init(|| match std::env::var("RFF_DIA_LADDER") {
389        Ok(v) => {
390            let want: Vec<i32> = v.split(',').filter_map(|t| t.trim().parse().ok()).collect();
391            let mut m = 0u32;
392            for (i, r) in DIA_RUNGS.iter().enumerate() {
393                if want.contains(r) { m |= 1 << i; }
394            }
395            if m == 0 { DIA_DEFAULT } else { m }
396        }
397        Err(_) => DIA_DEFAULT,
398    })
399}
400
401/// Descent A: per-STEP-SIZE census of the coarse-to-fine diamond. The ladder is
402/// [64,32,16,8,4] quarter-pel (i.e. 16,8,4,2,1 full-pel) and each step walks until it
403/// stops improving. Counts evaluations AND improvements per step so a step that never
404/// pays can be identified rather than assumed.
405#[cfg(feature = "profile")]
406pub mod diastats {
407    use core::sync::atomic::{AtomicU64, Ordering};
408    /// [step_index][0]=evals, [1]=improvements
409    pub static C: [AtomicU64; 12] = [const { AtomicU64::new(0) }; 12];
410    #[inline]
411    pub fn ev(i: usize) { C[i * 2].fetch_add(1, Ordering::Relaxed); }
412    #[inline]
413    pub fn imp(i: usize) { C[i * 2 + 1].fetch_add(1, Ordering::Relaxed); }
414    pub fn reset() { for c in C.iter() { c.store(0, Ordering::Relaxed); } }
415    pub fn snapshot() -> Vec<(u64, u64)> {
416        (0..6).map(|i| (C[i * 2].load(Ordering::Relaxed), C[i * 2 + 1].load(Ordering::Relaxed))).collect()
417    }
418}
419
420/// Sub-pel refinement PATTERN (U1). Bit 0 = 4-point diamond ring instead of the
421/// 8-point square; bit 1 = single pass instead of walking to convergence.
422///
423/// Harvested from 280 k real refinements: ~29 evaluations each, but the LAST
424/// improvement lands at eval ~14–15 — **half of every refinement is spent confirming
425/// an answer already found** — and the first ring alone captures 64–72% of the total
426/// gain. An 8-point ring pays 8 evaluations for that confirmation; a 4-point diamond
427/// (what x264's subme uses) pays 4.
428///
429/// `RFF_SUBPEL_PAT`: 0 = 8-point + iterate (the pre-U1 default), 1 = 4-point +
430/// iterate, 2 = 8-point single pass, 3 = 4-point single pass.
431pub(crate) static SUBPEL_PAT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
432
433/// Learning-window size and ring-1 threshold (percent) for the U1 online dispatcher.
434/// `RFF_SUBPEL_DISPATCH=0` disables it (pure `RFF_SUBPEL_PAT` behaviour).
435pub(crate) static SP_DISPATCH: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
436
437fn sp_dispatch_cfg() -> (u32, i64) {
438    use std::sync::OnceLock;
439    let forced = SP_DISPATCH.load(std::sync::atomic::Ordering::Relaxed);
440    if forced == 0 {
441        return (0, 0);
442    }
443    static C: OnceLock<(u32, i64)> = OnceLock::new();
444    *C.get_or_init(|| {
445        // DEFAULT OFF — measured and refuted (see the U1 entry in
446        // docs/WHYS-speed-gap.md). It only delivers speed where a blanket pattern
447        // change already would (bus 1.47x) while costing BD where it delivers none
448        // (foreman +0.97% for 1.04x, mobile +0.33% for 0.98x), and mixing refinement
449        // quality across frames measured WORSE than a uniform cut (bus +0.81%
450        // dispatched vs +0.30% pat2-always) — the refinement feeds the reference
451        // chain, so per-frame inconsistency propagates.
452        let on = std::env::var("RFF_SUBPEL_DISPATCH").map(|s| s != "0").unwrap_or(false);
453        if !on {
454            return (0, 0);
455        }
456        let k = std::env::var("RFF_SUBPEL_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(200);
457        let t = std::env::var("RFF_SUBPEL_T").ok().and_then(|s| s.parse().ok()).unwrap_or(67);
458        (k, t)
459    })
460}
461
462/// Explicit override only; `None` means "use the preset's default".
463fn subpel_pattern_override() -> Option<u32> {
464    let v = SUBPEL_PAT.load(std::sync::atomic::Ordering::Relaxed);
465    if v != u32::MAX {
466        return Some(v);
467    }
468    if let Some(e) = std::env::var("RFF_SUBPEL_PAT").ok().and_then(|s| s.parse::<u32>().ok()) {
469        SUBPEL_PAT.store(e, std::sync::atomic::Ordering::Relaxed);
470        return Some(e);
471    }
472    None
473}
474
475fn subpel_pattern() -> u32 {
476    let v = SUBPEL_PAT.load(std::sync::atomic::Ordering::Relaxed);
477    if v != u32::MAX {
478        return v;
479    }
480    // Unset -> take the env default once and latch it, so the hot path stays a
481    // relaxed load rather than an env lookup.
482    let d = std::env::var("RFF_SUBPEL_PAT").ok().and_then(|s| s.parse().ok()).unwrap_or(0);
483    SUBPEL_PAT.store(d, std::sync::atomic::Ordering::Relaxed);
484    d
485}
486
487/// Observe-only HARVEST for the sub-pel refinement skip-gate (U1).
488///
489/// `me-subpel` is 141 ms of a 320 ms quality encode — 44% — at 241 candidate
490/// evaluations per macroblock. This tap records, per refinement, the NULL-ARM cost
491/// (the full-pel winner, i.e. what we would keep if we skipped) against the cost the
492/// refinement actually reached, so the skip-rate-vs-gain-kept ceiling can be swept
493/// offline before any gate is written. Writes nothing unless `RFF_SUBPEL_HARVEST`
494/// names a file.
495mod subpel_harvest {
496    use std::fs::File;
497    use std::io::Write;
498    use std::sync::{Mutex, OnceLock};
499
500    fn sink() -> &'static Option<Mutex<File>> {
501        static S: OnceLock<Option<Mutex<File>>> = OnceLock::new();
502        S.get_or_init(|| {
503            std::env::var("RFF_SUBPEL_HARVEST").ok().and_then(|p| {
504                let mut f = File::create(p).ok()?;
505                let _ = writeln!(f, "pre,post,lambda,w,h,evals,to_best,ring1");
506                Some(Mutex::new(f))
507            })
508        })
509    }
510
511    #[inline]
512    pub fn enabled() -> bool {
513        sink().is_some()
514    }
515
516    #[allow(clippy::too_many_arguments)]
517    pub fn record(pre: i64, post: i64, lambda: f64, w: usize, h: usize, evals: u32, to_best: u32, ring1: i64) {
518        if let Some(m) = sink() {
519            if let Ok(mut f) = m.lock() {
520                let _ = writeln!(f, "{pre},{post},{lambda:.4},{w},{h},{evals},{to_best},{ring1}");
521            }
522        }
523    }
524}
525
526/// A/B switch for serving B-direct 4×4 MC from the cached half-pel planes
527/// (`RFF_BDIRECT_PLANES=0` restores the direct `mc_luma` 6-tap). Byte-identical
528/// either way; the knob exists so the arm can be measured in one binary.
529fn bdirect_planes_enabled() -> bool {
530    use std::sync::OnceLock;
531    static ON: OnceLock<bool> = OnceLock::new();
532    *ON.get_or_init(|| std::env::var("RFF_BDIRECT_PLANES").map(|s| s != "0").unwrap_or(true))
533}
534
535fn me_batch_enabled() -> bool {
536    use std::sync::OnceLock;
537    static ON: OnceLock<bool> = OnceLock::new();
538    *ON.get_or_init(|| std::env::var("RFF_ME_BATCH").map(|s| s != "0").unwrap_or(true))
539}
540
541/// A 16-byte-aligned 16×16 luma block — the aligned `op1` openh264's SSE2 SAD/SATD
542/// kernels require (`movdqa`). Safe to construct (`forbid(unsafe)` holds); the asm
543/// FFI that consumes it lives in `rusty_h264-accel`. Only used on the `asm` feature.
544#[cfg(accel)]
545#[repr(align(16))]
546struct AlignedMb([u8; 256]);
547
548/// A B-slice 16×16 inter-coding spec: the prediction direction and the motion it
549/// uses. `dir` 1 = `B_L0_16x16`, 2 = `B_L1_16x16`, 3 = `B_Bi_16x16` (spec Table
550/// 7-14). List-0 is `refs[0]` (nearest past anchor); `l1` is List-1 (nearest
551/// future anchor). `mv0`/`mv1` are the List-0/List-1 motion vectors (quarter-pel).
552#[derive(Clone, Copy)]
553struct BInter<'a> {
554    dir: u8,
555    l1: &'a crate::RefFrame,
556    mv0: (i32, i32),
557    mv1: (i32, i32),
558}
559
560/// 16-byte-aligned 256-`i16` DCT/coefficient buffer — the in-place `movdqa` quant
561/// kernel (`WelsQuantFour4x4_sse2`) requires aligned coefficients. `asm`-feature only.
562#[cfg(accel)]
563#[repr(align(16))]
564struct AlignedDct([i16; 256]);
565
566/// Luma variance of the 16×16 source MB at (mb_x, mb_y) — the content signal for
567/// the adaptive SAD↔SATD cost dispatch (high variance = detail = SAD misprices).
568/// `256·variance` scale (the /256 of the mean-square is kept integer); only the
569/// RELATIVE ordering matters for the per-frame percentile, so the constant drops.
570fn mb_variance(sy: &[u8], cw: usize, mb_x: usize, mb_y: usize) -> i64 {
571    let base = mb_y * 16 * cw + mb_x * 16;
572    // Accumulate in u32, not i64: the sum of 256 bytes maxes at 65280 and the sum
573    // of squares at 16.6M, so 64-bit accumulators (and a 64-bit multiply per
574    // pixel) were pure width — and they stop LLVM vectorising what is otherwise a
575    // textbook pair of reductions over 16 contiguous bytes.
576    let (mut s, mut ss) = (0u32, 0u32);
577    for r in 0..16 {
578        let row = &sy[base + r * cw..base + r * cw + 16];
579        for &p in row {
580            let v = p as u32;
581            s += v;
582            ss += v * v;
583        }
584    }
585    // Widen once at the end: s*s reaches 4.26e9, which only just fits u32.
586    ss as i64 - (s as i64) * (s as i64) / 256 // 256·variance, monotone in variance
587}
588
589/// Adaptive-Quantization per-MB QP map: flat (low-variance) macroblocks get a FINER
590/// QP (where blocking/banding is visible), busy ones a COARSER QP (where the eye
591/// masks error) — moving bits to where they're seen. The shift is `strength ·
592/// (log2 var − frame mean log2 var)`, so it's relative to THIS frame's texture
593/// distribution (content-invariant), rounded to an integer QP step and clamped.
594/// `strength == 0` → uniform base QP (byte-identical: every `mb_qp_delta` is 0).
595fn aq_qp_map(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize, base_qp: u8, strength: f64) -> Vec<u8> {
596    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncAq);
597    const AQ_DQP_MAX: i32 = 4;
598    let n = mb_w * mb_h;
599    if strength == 0.0 || n == 0 {
600        return vec![base_qp; n];
601    }
602    // Per-MB variance (the bit-cost weight) and its log2 (+1 avoids log2(0) on a flat
603    // MB → reads as maximally flat → finest QP).
604    let mut var = Vec::with_capacity(n);
605    let mut lv = Vec::with_capacity(n);
606    for my in 0..mb_h {
607        for mx in 0..mb_w {
608            let v = (mb_variance(sy, cw, mx, my) + 1) as f64;
609            var.push(v);
610            lv.push(v.log2());
611        }
612    }
613    let mean_lv = lv.iter().sum::<f64>() / n as f64;
614    // CONTENT-ADAPTIVE STRENGTH: back off where the log-variance SPREAD is high. A
615    // wide/bimodal spread means synthetic-ish content (flat regions beside detailed
616    // patterns) where "busy = maskable" FAILS and the patterns are salient — full AQ
617    // there costs PSNR. Natural content's spread is ~1 (keeps full strength); a
618    // synthetic pan's is ~6 (heavily reduced). Ramp 1.0→`AQ_SPREAD_MIN` over
619    // [`AQ_SPREAD_LO`, `AQ_SPREAD_HI`].
620    const AQ_SPREAD_LO: f64 = 1.5;
621    const AQ_SPREAD_HI: f64 = 5.0;
622    const AQ_SPREAD_MIN: f64 = 0.0; // extreme spread (pathological synthetic) → AQ OFF
623    let std_lv = (lv.iter().map(|&l| (l - mean_lv).powi(2)).sum::<f64>() / n as f64).sqrt();
624    let factor = (1.0 - (std_lv - AQ_SPREAD_LO) / (AQ_SPREAD_HI - AQ_SPREAD_LO)).clamp(AQ_SPREAD_MIN, 1.0);
625    let eff_strength = strength * factor;
626    // Per-MB QP shift (clamped): busy (log-var above mean) coarser, flat finer.
627    let dqp: Vec<i32> = lv
628        .iter()
629        .map(|&l| (eff_strength * (l - mean_lv)).round() as i32)
630        .map(|d| d.clamp(-AQ_DQP_MAX, AQ_DQP_MAX))
631        .collect();
632    // RATE COMPENSATION: AQ nets a rate change (coarsening a busy MB saves more bits
633    // than fining a flat one adds), so shift the whole frame's QP by `c` to restore
634    // the un-AQ rate — keeping `qp` meaningful. Bit model `bits_i ∝ var_i·2^(−qp_i/6)`
635    // (variance as the per-MB cost proxy): `c = 6·log2(Σ var·2^(−dqp/6) / Σ var)`.
636    let sum_v: f64 = var.iter().sum();
637    // `dqp` is clamped to [-AQ_DQP_MAX, AQ_DQP_MAX], so 2^(-d/6) has only nine
638    // possible values — but it was being recomputed with a `powf` for every
639    // macroblock of every frame. Same expression, evaluated once per offset:
640    // bit-identical, and it retires a transcendental from a per-macroblock loop.
641    let qstep: [f64; (2 * AQ_DQP_MAX + 1) as usize] =
642        std::array::from_fn(|i| 2f64.powf(-((i as i32 - AQ_DQP_MAX) as f64) / 6.0));
643    let sum_vs: f64 = var
644        .iter()
645        .zip(&dqp)
646        .map(|(&v, &d)| v * qstep[(d + AQ_DQP_MAX) as usize])
647        .sum();
648    let c = (6.0 * (sum_vs / sum_v).log2()).round() as i32;
649    dqp.iter()
650        .map(|&d| (base_qp as i32 + c + d).clamp(0, 51) as u8)
651        .collect()
652}
653
654/// Mean per-sampled-pixel residual after GLOBAL-motion compensation of `sy` from
655/// `ref_y` (coarse ±12 global ME + ±3 refine, subsampled interior). ~0 on a PURE pan
656/// (a single MV predicts the whole frame) — precisely the content where the local ME
657/// diamond never genuinely STALLS (its seed = the median = the pan MV is already
658/// right), so the `me_wide` rescue can only find SPURIOUS MVs that wreck the B-frame
659/// spatial-direct predictors. Gates `me_wide` off there — non-uniform content
660/// (real stalls, where me_wide wins) reads well above 0.
661/// Per-frame HEAD-ROOM probe for the `me_wide` rescue: on a small subsample of
662/// blocks, how much does a WIDE full-pel search beat a PREDICTOR-LOCAL one?
663///
664/// This measures what the rescue actually buys, before the macroblock loop and
665/// without committing any vector — unlike the online payoff gate, which scores its
666/// own SATD cost-cut *after* committing MVs and so only ever separated static
667/// content. Returns the mean relative SAD improvement, in percent.
668///
669/// Calibrated against the 20-clip per-clip BD truth table (docs/WHYS-speed-gap.md
670/// R5): me_wide earns its 1.4–5.1× on high-head-room content (bus +4.57, blue_sky
671/// +4.70, football +1.51, park_joy +0.91) and REGRESSES on low-head-room content
672/// (foreman_qcif −1.08, foreman_cif −0.16, tempete −0.12, mobile −0.03).
673///
674/// Deliberately PER-FRAME, not per-clip: cross-frame adaptive state is
675/// nondeterministic under the GOP-parallel encode path (a lesson already paid for
676/// by the rescue's own learning window).
677/// Head-room threshold (percent) for the `me_wide` frame gate. DEFAULT-ON at 16.
678///
679/// Calibrated on the DEPLOYED estimator (not the offline probe — they differ) and
680/// gated on the full 20-clip `video-tests` corpus plus four synthesized boundary
681/// clips, 4-QP BD-rate on PSNR and SSIM:
682///
683/// | | me_wide always-on | gated at 16 |
684/// |---|---|---|
685/// | real-corpus mean | +0.62% | +0.547% (88% retained) |
686/// | **worst clip** | **−1.08%** (foreman_qcif) | **0.00%** |
687/// | clips paying 1.1–3.6× for ~nothing | 13 | 0 |
688///
689/// Wins preserved: blue_sky +4.70, bus +4.37, park_joy +0.94, football +0.64,
690/// shields +0.20; synthesized fast-pan +6.73, rotation +1.72, zoom +1.11.
691/// Monotone non-regression — no clip is negative — which is what promotes this from
692/// a speed trade to a default.
693///
694/// `RFF_ME_HR=0` disables the gate and reproduces the pre-gate bytes exactly (the
695/// escape hatch / bisection anchor). Thresholds 13 and 16 both clear the boundary
696/// clip (foreman_cif +0.07 / +0.03); 10 does NOT (−0.23) — the threshold is
697/// calibrated on a narrow boundary pair, so treat it as re-tunable, not settled.
698fn me_wide_hr_thresh() -> f64 {
699    use std::sync::OnceLock;
700    static T: OnceLock<f64> = OnceLock::new();
701    *T.get_or_init(|| std::env::var("RFF_ME_HR").ok().and_then(|s| s.parse().ok()).unwrap_or(16.0))
702}
703
704/// Cached, because it is read per frame — an `env::var` there is its own tax.
705fn me_wide_hr_dbg() -> bool {
706    use std::sync::OnceLock;
707    static D: OnceLock<bool> = OnceLock::new();
708    *D.get_or_init(|| std::env::var_os("RFF_ME_HR_DBG").is_some())
709}
710
711fn me_wide_headroom(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
712    const LOCAL: isize = 2; // a well-seeded diamond's effective reach
713    const WIDE: isize = 24; // the rescue grid's half-extent
714    const STEP: isize = 4; // coarse: this is a frame-level statistic, not a search
715    const TARGET: usize = 24; // samples per frame — keep the probe ~0.5% of a frame
716    let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
717        if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
718            return None;
719        }
720        let (rx, ry) = (rx as usize, ry as usize);
721        let mut s = 0u32;
722        for dy in 0..16 {
723            let a = &sy[(by + dy) * cw + bx..][..16];
724            let b = &ref_y[(ry + dy) * cw + rx..][..16];
725            s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
726        }
727        Some(s)
728    };
729    // Interior blocks only (the probe must not measure edge clamping), spread over
730    // the frame so one moving object cannot dominate.
731    let (mbw, mbh) = (cw / 16, ch / 16);
732    if mbw < 6 || mbh < 6 {
733        return 0.0;
734    }
735    let inner = (mbw - 4) * (mbh - 4);
736    let stride = (inner / TARGET).max(1);
737    let (mut acc, mut n) = (0.0f64, 0u32);
738    let mut i = 0usize;
739    while i < inner {
740        let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
741        let (bx, by) = (mx * 16, my * 16);
742        let mut best_local = u32::MAX;
743        for dy in -LOCAL..=LOCAL {
744            for dx in -LOCAL..=LOCAL {
745                if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
746                    best_local = best_local.min(s);
747                }
748            }
749        }
750        let mut best_wide = best_local;
751        let mut dy = -WIDE;
752        while dy <= WIDE {
753            let mut dx = -WIDE;
754            while dx <= WIDE {
755                if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
756                    best_wide = best_wide.min(s);
757                }
758                dx += STEP;
759            }
760            dy += STEP;
761        }
762        if best_local > 0 {
763            acc += (best_local - best_wide) as f64 / best_local as f64;
764            n += 1;
765        }
766        i += stride;
767    }
768    if n == 0 {
769        0.0
770    } else {
771        100.0 * acc / n as f64
772    }
773}
774
775fn global_mc_residual(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
776    if cw < 48 || ch < 48 {
777        return f64::INFINITY;
778    }
779    let sad = |dx: isize, dy: isize| -> u64 {
780        let mut s = 0u64;
781        let mut y = 16;
782        while y < ch - 16 {
783            let cbase = (y * cw) as isize;
784            let rbase = (y as isize + dy) * cw as isize + dx;
785            let mut x = 16isize;
786            while x < (cw - 16) as isize {
787                let c = sy[(cbase + x) as usize] as i32;
788                let r = ref_y[(rbase + x) as usize] as i32;
789                s += (c - r).unsigned_abs() as u64;
790                x += 8;
791            }
792            y += 8;
793        }
794        s
795    };
796    let (mut best, mut bc) = ((0isize, 0isize), u64::MAX);
797    let mut dy = -12;
798    while dy <= 12 {
799        let mut dx = -12;
800        while dx <= 12 {
801            let c = sad(dx, dy);
802            if c < bc {
803                bc = c;
804                best = (dx, dy);
805            }
806            dx += 4;
807        }
808        dy += 4;
809    }
810    for dy in best.1 - 3..=best.1 + 3 {
811        for dx in best.0 - 3..=best.0 + 3 {
812            let c = sad(dx, dy);
813            if c < bc {
814                bc = c;
815            }
816        }
817    }
818    let nx = (16..cw - 16).step_by(8).count();
819    let ny = (16..ch - 16).step_by(8).count();
820    bc as f64 / (nx * ny).max(1) as f64
821}
822
823/// Adds the mb-tree per-MB QP offset (TEMPORAL AQ — [`crate::mbtree`]) to the
824/// spatial-AQ `aq_qp` map in place. An empty `qpo` (mb-tree off) or a length
825/// mismatch is a no-op → byte-identical. Shared by the CAVLC and CABAC slice paths.
826fn apply_mbtree_qpo(aq_qp: &mut [u8], qpo: &[i32]) {
827    if qpo.len() == aq_qp.len() {
828        for (q, &o) in aq_qp.iter_mut().zip(qpo) {
829            *q = (*q as i32 + o).clamp(0, 51) as u8;
830        }
831    }
832}
833
834/// IMPLICIT bi-prediction weights `(w0, w1)` from POC distances (spec §8.4.2.3.2,
835/// `weighted_bipred_idc == 2`), IDENTICAL to the decoder's `implicit_weights`. The
836/// closer anchor gets more weight; an equidistant B (`bframes == 1`) yields 32:32,
837/// i.e. the plain average. `(32, 32)` fallback for the degenerate/out-of-range cases
838/// the decoder also averages (no long-term refs here).
839fn implicit_bi_weights(cur_poc: i32, l0_poc: i32, l1_poc: i32) -> (i32, i32) {
840    let td = (l1_poc - l0_poc).clamp(-128, 127);
841    let tb = (cur_poc - l0_poc).clamp(-128, 127);
842    if td == 0 {
843        return (32, 32);
844    }
845    let tx = (16384 + td.abs() / 2) / td;
846    let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
847    let w1 = dsf >> 2;
848    if !(-64..=128).contains(&w1) {
849        return (32, 32);
850    }
851    (64 - w1, w1)
852}
853
854/// Bi-prediction blend of two motion-compensated samples `p` (List-0) and `q`
855/// (List-1) under weights `(w0, w1)` — the decoder's `b_mc` blend. `(32, 32)` is the
856/// plain `(p+q+1)>>1` average.
857#[inline(always)]
858fn bi_blend(p: i32, q: i32, w: (i32, i32)) -> u8 {
859    ((p * w.0 + q * w.1 + 32) >> 6).clamp(0, 255) as u8
860}
861
862/// Zig-zag scan of a raster i16 4×4 block into scan-order i32 — the fused-path
863/// twin of `scan_4x4_dcac(&q_blocks[..])`, reading quantized levels straight from
864/// the hot i16 DCT buffer. Byte-identical: the i16→i32 widening of a quant level
865/// is exact (levels always fit i16, being the input to the i16 idct kernel).
866#[cfg(accel)]
867#[inline]
868fn scan_4x4_dcac_i16(d: &[i16]) -> [i32; 16] {
869    [
870        d[0] as i32, d[1] as i32, d[4] as i32, d[8] as i32, d[5] as i32, d[2] as i32,
871        d[3] as i32, d[6] as i32, d[9] as i32, d[12] as i32, d[13] as i32, d[10] as i32,
872        d[7] as i32, d[11] as i32, d[14] as i32, d[15] as i32,
873    ]
874}
875
876
877/// Per-frame intra encoder state: reconstructed planes (coded size) and the
878/// per-4×4-block non-zero-coefficient counts used for CAVLC context.
879pub struct FrameEncoder {
880    mb_w: usize,
881    mb_h: usize,
882    qp: u8,  // the CURRENT macroblock's target QPy (AQ varies it per MB)
883    qpc: u8, // chroma QP for `qp`
884    /// Running QPy of the last macroblock that coded an `mb_qp_delta` (spec QPY_PREV).
885    /// `mb_qp_delta = qp − cur_qp`; a skip / cbp==0 MB codes no delta and inherits it.
886    cur_qp: u8,
887    /// Implicit bi-prediction weights `(w0, w1)` for the current B-frame (from its
888    /// L0/L1 anchor POC distances). `(32, 32)` = plain average (P/I frames, `bframes
889    /// == 1`); unequal for `bframes > 1`.
890    bi_w: (i32, i32),
891    cw: usize, // coded luma width
892    ccw: usize, // coded chroma width
893    // 16-byte aligned (the openh264 deblock/MC/intra asm load aligned row chunks).
894    rec_y: AlignedBytes,
895    rec_u: AlignedBytes,
896    rec_v: AlignedBytes,
897    nnz_y: Vec<u8>,    // (mb_w*4) x (mb_h*4)
898    nnz_c: [Vec<u8>; 2], // each (mb_w*2) x (mb_h*2)
899    modes_y: Vec<u8>,  // intra4x4 mode per 4×4 block (2=DC for I_16x16 blocks)
900    coded_y: Vec<bool>, // whether each 4×4 block is reconstructed (top-right avail)
901    mv_y: Vec<(i32, i32)>, // motion vector per 4×4 block (quarter-pel) — List-0
902    inter_y: Vec<bool>, // whether each 4×4 block is inter-coded
903    ref_idx_y: Vec<i32>, // reference index per 4×4 block (-1 = intra/uncoded) — List-0
904    // B-slice List-1 motion field (empty for P/I). B_L1/B_Bi commit here so a later
905    // partition's List-1 median predictor sees it, mirroring the decoder's
906    // `mv_neighbors_list(.., 1)` over `mv1`/`ref_idx1`.
907    mv1_y: Vec<(i32, i32)>,
908    ref_idx1_y: Vec<i32>,
909    idz: i64, // intra dead-zone divisor: 2 for all-intra, 3 when frames reference each other
910    rdoq_strength: f64, // CABAC trellis (RDOQ) strength; 0 = off (hard quantize, CAVLC path)
911    transform_8x8: bool, // High-profile 8x8 transform enabled (transform_8x8_mode_flag)
912    sub8x8: bool, // P_8x8 sub-partition motion (four 8x8 MVs per MB)
913    me_wide: bool, // adaptive wide ME grid search rescue (diamond stalls on flat surfaces)
914    /// Track-B B2 for THIS frame: SAD-domain full-pel phase. Set at construction
915    /// (force mode), or per frame by the `b2_mgain` dispatcher (mode 1).
916    sadfp: bool,
917    me_wide_var: u64, // per-pixel source variance below which a block is "flat"
918    me_rescue: i64, // per-pixel residual SATD (on a flat block) that flags a diamond stall
919    me_wide_coh: f64, // gate me_wide off when the frame's global-MC residual is below this (pure pan)
920    me_range: i32, // rescue grid half-range in px (16 = ±16; wider reaches FAST motion the diamond misses)
921    me_fast: bool, // also fire the rescue on HIGH-VARIANCE high-residual blocks (fast-motion stalls, not just flat)
922    // ONLINE per-frame rescue-payoff gate (adaptive; WITHIN-frame so it stays
923    // deterministic under the frame-parallel encode). Run the real rescue on the
924    // first `me_learn` stalls of a frame, count how many the fine grid improves by
925    // ≥6.25%, and if that fraction is below `me_payoff_pct`% disable the rescue for
926    // the rest of the frame. This separates genuine diamond stalls (tsrc/zoom fine
927    // grid improves ~33% of fires) from IRREDUCIBLE residual (rotation/fractal ~5-8%)
928    // using the ACTUAL neighbour-seeded diamond — the only faithful signal (a cheap
929    // SAD proxy from (0,0) inverts it: rot reads as highest-payoff). Frame-level, so
930    // no per-block selection concentrates the B-direct-poisoning spurious MVs.
931    me_learn: u32,
932    me_payoff_pct: u32,
933    /// U1 online sub-pel dispatcher (within-frame, so it stays deterministic under
934    /// GOP-parallel encode). For the first `SP_LEARN` refinements of a frame we run
935    /// the full 8-point+iterate pattern and accumulate how much of the total gain the
936    /// FIRST ring captured; once the window fills, a frame whose gain is concentrated
937    /// in ring 1 switches to the single-pass pattern for the rest of the frame.
938    ///
939    /// Harvested justification: ring-1 captures 63.7% of the gain on foreman (which
940    /// loses +2.34% BD to a blanket single-pass) against 69.9–71.9% on bus/mobile
941    /// (which lose only +0.30/+0.74% and gain 1.08–1.31×). The fraction separates the
942    /// content that can afford the cut from the content that cannot.
943    sp_single_pass: bool,
944    /// U5-struct: when set, `motion_search` returns its FULL-PEL winner and skips
945    /// sub-pel refinement entirely. The partition driver uses this to search all
946    /// candidate shapes cheaply, pick one, and refine ONLY the winner's sub-blocks.
947    /// Measured ceiling: 3.4–6.4× less sub-pel work (the losing shapes' refinements
948    /// are pure waste), i.e. ~1.42× whole-encode at 44% sub-pel share.
949    sp_defer: std::cell::Cell<bool>,
950    sp_learn_n: std::cell::Cell<u32>,
951    sp_ring1: std::cell::Cell<i64>,
952    sp_total: std::cell::Cell<i64>,
953    sp_1pass: std::cell::Cell<bool>,
954    resc_n: std::cell::Cell<u32>,   // stalls the fine grid ran on this frame (learning phase)
955    resc_big: std::cell::Cell<u32>, // of those, how many it improved ≥6.25%
956    resc_off: std::cell::Cell<bool>, // rescue disabled for the rest of this frame
957    inter8x8: u8, // inter 8x8-transform dispatch: 0=off, 1=always-RD, 2=content-adaptive
958    inter8_pen: i64, // extra rate charge (nonzero-equiv) on the inter 8x8 candidate
959    fast: bool, // Preset::Fast — SATD mode decision (no RDO), 16×16/I_16x16 only
960    skip_accel_check: bool, // A/B knob: whole-MB psadbw gate in the P_Skip free-check
961    coded_path_v2: bool,    // A/B knob: route inter coding through encode_inter_mb_v2
962    tune_lambda_scale: f64, // tuning knob: scale on the RD λ (1.0 = standard)
963    tune_intra_penalty: f64,
964    satd_q: f64,               // adaptive: fraction of high-variance MBs routed to SATD cost
965    subpel_force: bool,        // force sub-pel refinement even in the fast preset
966    me_snap: bool,             // snap the diamond centre to integer-pel (see config)
967    me_subpel_iter: bool,      // walk the sub-pel refine to convergence
968    greedy_skip: bool,         // quality preset's SAD-thresholded P_Skip (PredictSadSkip)
969    greedy_min_free: u32,      // online free-skip % gating greedy_skip on this frame
970    rd_skip: bool,             // decide P_Skip by J = SSD + lambda*bits, not exact-zero residual
971    rd_skip_min_free: u32,     // online free-skip % gating rd_skip on this frame
972    rd_skip_fast_t: f64,       // skip-gate on SSD(skip)/lambda; <= 0 prices every candidate
973    satd_var_thresh: i64,      // per-frame variance threshold for the routing (set in a pre-pass)
974    aq_strength: f64,          // adaptive quantization: per-MB QP modulation strength (0 = off)
975    mb_use_satd: bool,         // per-MB: this MB uses the SATD cost this decision
976    // Per-MB luma nnz prediction cache (openh264 scan8 style): a padded 5×5 grid,
977    // block (lbx,lby) at (lby+1)*5+(lbx+1); row 0 = top neighbours, col 0 = left.
978    // Unavailable edges hold the sentinel 0x80, so the nnz predict is branchless.
979    nnz_l_cache: [u8; 25],
980    // Same, per chroma plane: a padded 3×3 grid for the 2×2 chroma blocks.
981    nnz_c_cache: [[u8; 9]; 2],
982    // openh264 predicted-SAD skip apparatus (per MB, mb_w×mb_h): the P_Skip
983    // prediction's luma SAD, and whether the MB was actually skipped. The greedy
984    // skip threshold for an MB is the median of its skip *neighbours'* skip SADs
985    // (`PredictSadSkip`) — so skip propagates only from already-skip regions
986    // (seeded by free skips) and self-limits, instead of a fixed bound that drifts.
987    mb_skip_sad: Vec<u32>,
988    mb_was_skip: Vec<bool>,
989}
990
991/// A chosen inter coding for a macroblock: `mb_type` and, per partition, the
992/// reference index and motion vector.
993type InterChoice = (u8, Vec<(i32, (i32, i32))>);
994
995/// Approximate marginal rate (bits) of one `P_Skip` — it only lengthens the
996/// surrounding `mb_skip_run` Exp-Golomb code slightly.
997const SKIP_RATE_BITS: f64 = 1.0;
998
999
1000
1001/// EXTERNAL MV SCORING (`RFF_MV_CMP=1`). Holds another encoder's motion field
1002/// (per frame, 4x4-block raster) so our own coder can price ITS vectors against
1003/// ours under REAL coded bits instead of SATD — the only way to tell a bad search
1004/// from a bad cost function.
1005pub static EXT_MV: std::sync::Mutex<Vec<Vec<(i32, i32)>>> = std::sync::Mutex::new(Vec::new());
1006/// [n, our bits, ext bits, our SSD, ext SSD, ext won on J, MVs differing]
1007pub static MVCMP: [std::sync::atomic::AtomicU64; 7] = {
1008    const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1009    [Z; 7]
1010};
1011pub static MVCMP_FRAME: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1012/// Replace our chosen vector with the external field's, for EVERY macroblock where
1013/// that field used a single 16x16 partition. Transplanting one vector in isolation
1014/// is meaningless — `mvd` is coded against the NEIGHBOURS' vectors, so a lone
1015/// foreign vector prices against the wrong predictor. Only a whole coherent field
1016/// can be compared fairly.
1017fn mv_force_on() -> bool {
1018    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1019    *ON.get_or_init(|| std::env::var("RFF_MV_FORCE").map_or(false, |v| v != "0"))
1020}
1021fn mv_cmp_on() -> bool {
1022    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1023    *ON.get_or_init(|| std::env::var("RFF_MV_CMP").map_or(false, |v| v != "0"))
1024}
1025
1026/// [full-pel SATD evals, INTERPOLATED SATD evals] — `RFF_MC_COUNT=1`.
1027/// x264 precomputes half-pel planes once per frame; we run the 6-tap filter per
1028/// candidate, so this ratio prices that difference.
1029pub static MC_COUNT: [std::sync::atomic::AtomicU64; 2] = [
1030    std::sync::atomic::AtomicU64::new(0),
1031    std::sync::atomic::AtomicU64::new(0),
1032];
1033fn mc_count_on() -> bool {
1034    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1035    *ON.get_or_init(|| std::env::var("RFF_MC_COUNT").map_or(false, |v| v != "0"))
1036}
1037
1038/// [n, sum our cost, sum oracle cost, blocks the oracle beat us on, cost() evals]
1039pub static ME_PROBE: [std::sync::atomic::AtomicU64; 7] = {
1040    const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1041    [Z; 7]
1042};
1043
1044/// Cached — an `env::var` inside the ME loop inflated it 4x when probed naively.
1045fn me_oracle_on() -> bool {
1046    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1047    *ON.get_or_init(|| std::env::var("RFF_ME_ORACLE").map_or(false, |v| v != "0"))
1048}
1049
1050/// RDO early-termination gate. Sub-partitions (16×8 / 8×16) only help at motion
1051/// boundaries, which show up as a heavy 16×16 residual; below this many coded bits
1052/// the 16×16 already fits, so skip their motion search and trials. (Intra is *not*
1053/// gated — it can win even against a cheap inter prediction, so gating it on inter
1054/// cost regresses compression badly on textured content.)
1055const SPLIT_GATE_BITS: f64 = 60.0;
1056
1057/// Fast preset: signalling-cost penalty (in bits, SATD-weighted by √λ) charged to
1058/// the intra candidate so it only wins a P-macroblock when its prediction is
1059/// clearly better than inter — intra's `mb_type` + modes cost more to signal.
1060const FAST_INTRA_PENALTY_BITS: f64 = 24.0;
1061
1062/// A snapshot of one macroblock's per-block grids and reconstruction region,
1063/// used to roll back a trial encode during RD mode decision.
1064///
1065/// Every field is a `Vec`, so building one from scratch is ten heap allocations.
1066/// The RD skip decision snapshots on EVERY candidate macroblock, which made that
1067/// allocation traffic the decision's dominant cost — hence
1068/// [`save_mb_into`](FrameEncoder::save_mb_into), which refills a reused buffer.
1069#[derive(Default)]
1070struct MbState {
1071    rec_y: Vec<u8>,
1072    rec_u: Vec<u8>,
1073    rec_v: Vec<u8>,
1074    nnz_y: Vec<u8>,
1075    nnz_c: [Vec<u8>; 2],
1076    mv_y: Vec<(i32, i32)>,
1077    inter_y: Vec<bool>,
1078    ref_idx_y: Vec<i32>,
1079    coded_y: Vec<bool>,
1080    modes_y: Vec<u8>,
1081    /// QPY_PREV. `qp_delta()` MUTATES this as a side effect of coding
1082    /// `mb_qp_delta`, so a trial encode advances it; without restoring it the
1083    /// real encode then codes its delta against the wrong predecessor and the
1084    /// decoder's QP diverges from the encoder's — a silent stream corruption,
1085    /// not a quality tweak.
1086    cur_qp: u8,
1087}
1088
1089/// Edge-clamped, coded-size source planes (luma, Cb, Cr).
1090/// Fast-preset pruned I4x4 mode search ({MPM, DC, V, H} instead of all 9 — the
1091/// x264-ultrafast-style candidate set). DEFAULT ON for the fast preset (gated:
1092/// +0.5% size at +0.02 dB on all-intra, +17% all-intra speed); RUSTY_FAST_INTRA=0
1093/// restores the exhaustive 9-mode search (the pre-flip bitstream).
1094fn fast_intra_enabled() -> bool {
1095    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1096    *ON.get_or_init(|| std::env::var("RUSTY_FAST_INTRA").map_or(true, |v| v != "0"))
1097}
1098
1099fn coded_source(cfg: &EncoderConfig, frame: &YuvFrame) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
1100    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSource);
1101    let cw = cfg.mb_width() * 16;
1102    let ch = cfg.mb_height() * 16;
1103    // MB-aligned frame: the clamp is the identity — a plane memcpy (clone) replaces
1104    // the per-pixel clamp loop (bit-exact: same bytes).
1105    if frame.width == cw && frame.height == ch {
1106        return (frame.y.clone(), frame.u.clone(), frame.v.clone());
1107    }
1108    let y = clamp_plane(&frame.y, frame.width, frame.height, cw, ch);
1109    let u = clamp_plane(&frame.u, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1110    let v = clamp_plane(&frame.v, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1111    (y, u, v)
1112}
1113
1114/// Edge-extends `plane` from `w`×`h` to the coded `ow`×`oh`, replicating the last
1115/// row/column — the source form the MB grid needs.
1116///
1117/// Row-wise, because the per-pixel form is O(pixels) of scalar `min`+multiply and is
1118/// the DOMINANT cost of `enc-source-copy`: every frame whose height is not a multiple
1119/// of 16 takes this path, which includes all 1080p content (1080/16 = 67.5 → coded
1120/// height 1088). The stage measured 579 ms over the corpus while the three plane
1121/// clones on the MB-aligned fast path account for only ~135 ms of it.
1122///
1123/// Byte-identical to the per-pixel form (`clamp_plane_per_pixel`, kept as the test
1124/// oracle): `x.min(w-1)` is the identity below `w` and pins to the last column above
1125/// it, so a row is a `copy_from_slice` plus a `fill`; `y.min(h-1)` makes the
1126/// overhanging rows copies of the final row. Both lower to memcpy/memset.
1127fn clamp_plane(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1128    let mut out = vec![0u8; ow * oh];
1129    for y in 0..oh {
1130        let sy = y.min(h - 1);
1131        let src = &plane[sy * w..sy * w + w];
1132        let dst = &mut out[y * ow..y * ow + ow];
1133        if ow <= w {
1134            dst.copy_from_slice(&src[..ow]);
1135        } else {
1136            dst[..w].copy_from_slice(src);
1137            dst[w..].fill(src[w - 1]);
1138        }
1139    }
1140    out
1141}
1142
1143/// The original per-pixel edge extension — kept as the correctness oracle for
1144/// [`clamp_plane`], per the scalar-twin discipline.
1145#[cfg(test)]
1146fn clamp_plane_per_pixel(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1147    let mut out = vec![0u8; ow * oh];
1148    for y in 0..oh {
1149        for x in 0..ow {
1150            out[y * ow + x] = plane[y.min(h - 1) * w + x.min(w - 1)];
1151        }
1152    }
1153    out
1154}
1155
1156#[cfg(test)]
1157mod source_tests {
1158    use super::*;
1159
1160    #[test]
1161    fn clamp_plane_matches_per_pixel_oracle() {
1162        let mut s: u32 = 0xDEAD_BEEF;
1163        let mut rnd = || {
1164            s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
1165            (s >> 24) as u8
1166        };
1167        // Real coded geometries plus adversarial ones: width-only overhang,
1168        // height-only overhang (the 1080p case), both, and neither.
1169        let cases = [
1170            (1920usize, 1080usize, 1920usize, 1088usize), // 1080p luma
1171            (960, 540, 960, 544),                         // 1080p chroma
1172            (352, 288, 352, 288),                         // exactly aligned
1173            (100, 100, 112, 112),                         // both axes overhang
1174            (37, 5, 48, 16),                              // tiny + ragged
1175            (16, 1, 16, 16),                              // single source row
1176            (1, 1, 16, 16),                               // single sample
1177        ];
1178        for (w, h, ow, oh) in cases {
1179            let plane: Vec<u8> = (0..w * h).map(|_| rnd()).collect();
1180            assert_eq!(
1181                clamp_plane(&plane, w, h, ow, oh),
1182                clamp_plane_per_pixel(&plane, w, h, ow, oh),
1183                "clamp mismatch for {w}x{h} -> {ow}x{oh}"
1184            );
1185        }
1186    }
1187}
1188
1189impl FrameEncoder {
1190    fn new(cfg: &EncoderConfig) -> Self {
1191        let (mb_w, mb_h) = (cfg.mb_width(), cfg.mb_height());
1192        let (cw, ch) = (mb_w * 16, mb_h * 16);
1193        let (ccw, cch) = (cw / 2, ch / 2);
1194        Self {
1195            mb_w,
1196            mb_h,
1197            qp: cfg.qp,
1198            qpc: chroma_qp(cfg.qp),
1199            cur_qp: cfg.qp,
1200            bi_w: (32, 32),
1201            cw,
1202            ccw,
1203            rec_y: AlignedBytes::zeroed(cw * ch),
1204            rec_u: AlignedBytes::zeroed(ccw * cch),
1205            rec_v: AlignedBytes::zeroed(ccw * cch),
1206            nnz_y: vec![0; (mb_w * 4) * (mb_h * 4)],
1207            nnz_c: [vec![0; (mb_w * 2) * (mb_h * 2)], vec![0; (mb_w * 2) * (mb_h * 2)]],
1208            modes_y: vec![2; (mb_w * 4) * (mb_h * 4)],
1209            coded_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1210            mv_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1211            inter_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1212            ref_idx_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1213            mv1_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1214            ref_idx1_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1215            // All-intra (no inter references) tolerates the larger dead-zone; in
1216            // an I+P stream the IDR is a reference, so keep the standard offset.
1217            idz: if cfg.gop_size <= 1 { 2 } else { 3 },
1218            rdoq_strength: 0.0, // set >0 only in the CABAC slice coders
1219            transform_8x8: cfg.transform_8x8,
1220            // sub8x8 stays OPT-IN: the four P_8x8 sub-MVs feed the B-frames'
1221            // spatial-direct predictor, so on DIVERGENT motion (rotation/zoom/mixed)
1222            // it regresses with B-frames (mixed +0.24%, rot +0.42%, zoom +0.40%) —
1223            // a global effect its local RD gate can't see, and no clean dispatch
1224            // signal separates it yet (unlike me_wide's pure-pan coherence gate).
1225            // DEFAULT-ON for Quality (net real-content win; a 6-channel discovery
1226            // harvest proved no cheap gate beats always-on). Quality-only (Fast never
1227            // runs it). env RFF_SUB8X8 (0/1) > cfg.sub_8x8 (Some) > preset default.
1228            sub8x8: std::env::var("RFF_SUB8X8").ok().map(|s| s == "1")
1229                .or(cfg.sub_8x8)
1230                .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1231            // me_wide is DEFAULT-ON for the Quality preset. VALIDATED 2026-07-27 on the
1232            // full 20-clip `video-tests` Derf corpus (4-QP BD-rate, PSNR+SSIM, anchor =
1233            // me_wide ON): **mean +0.62% BD-PSNR / +0.69% BD-SSIM**, i.e. turning it off
1234            // costs that much. Biggest wins blue_sky +4.70, bus +4.57, football +1.51,
1235            // park_joy +0.91; synthesized boundary content (smooth fast-pan / rotation /
1236            // zoom) reaches +2.6..+6.7%. The static clips (akiyo, FourPeople) sit at
1237            // exactly 0.00 at ~1.0x — the online payoff gate correctly disables it there.
1238            //
1239            // ⚠ UNFINISHED DISPATCH — the per-clip BD SIGN-FLIPS (+4.70 blue_sky ..
1240            // -1.08 foreman_qcif), and the cost when it fires is 1.0-5.1x. Worst value:
1241            // soccer_4cif 1.70x for +0.00, park_joy 5.08x for +0.91. `me_range` is NOT
1242            // the separating axis — it is a compromise dial (foreman_qcif loses at EVERY
1243            // range 24/16/8/4 = -1.08/-0.55/-0.50/-0.19 while blue_sky wins at every one
1244            // = +4.70/+3.10/+0.73), so shrinking it just trades the win away. The real
1245            // fix is a content signal that predicts the sign; the truth table for it is
1246            // in docs/WHYS-speed-gap.md.
1247            //
1248            // Quality-only (Fast never runs it). Precedence:
1249            // env RFF_ME_WIDE (0/1, for A/B) > cfg.me_wide (Some) > preset default.
1250            sadfp: me_sadfp_mode() == 2,
1251            me_wide: std::env::var("RFF_ME_WIDE").ok().map(|s| s == "1")
1252                .or(cfg.me_wide)
1253                .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1254            me_wide_var: std::env::var("RFF_ME_WIDE_VAR").ok().and_then(|s| s.parse().ok()).unwrap_or(800),
1255            me_rescue: std::env::var("RFF_ME_RESCUE").ok().and_then(|s| s.parse().ok()).unwrap_or(3),
1256            me_wide_coh: std::env::var("RFF_ME_COH").ok().and_then(|s| s.parse().ok()).unwrap_or(4.0),
1257            me_range: std::env::var("RFF_ME_RANGE").ok().and_then(|s| s.parse().ok()).unwrap_or(24),
1258            me_fast: std::env::var("RFF_ME_FASTMO").map(|s| s != "0").unwrap_or(true),
1259            me_learn: std::env::var("RFF_ME_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(40),
1260            me_payoff_pct: std::env::var("RFF_ME_PAYOFF").ok().and_then(|s| s.parse().ok()).unwrap_or(15),
1261            // U3: `balanced` runs SINGLE-PASS sub-pel. Measured on the 4-QP corpus,
1262            // a single pass captures 95.5–99.4% of the full refinement's BD benefit
1263            // (foreman −38.14 vs −39.94, mobile −49.38 vs −49.66, akiyo −26.10 vs
1264            // −26.43) for 1.03–1.31× less time — a straight Pareto improvement on the
1265            // preset. `RFF_SUBPEL_PAT=0` restores the full walk-to-convergence.
1266            sp_single_pass: cfg.preset == crate::config::Preset::Balanced,
1267            sp_defer: std::cell::Cell::new({
1268                let a = DEFER_SUBPEL.load(std::sync::atomic::Ordering::Relaxed) != 0
1269                    || std::env::var("RFF_DEFER_SUBPEL").map(|v| v != "0").unwrap_or(false);
1270                // ONLY the Quality preset runs the multi-shape partition driver. On the
1271                // fast/balanced path there is a single 16×16 candidate, so there is no
1272                // losing shape to skip — deferring there does not save the refinement,
1273                // it DELETES it (measured +91..+145% BD before this guard).
1274                a && cfg.preset == crate::config::Preset::Quality
1275            }),
1276            sp_learn_n: std::cell::Cell::new(0),
1277            sp_ring1: std::cell::Cell::new(0),
1278            sp_total: std::cell::Cell::new(0),
1279            sp_1pass: std::cell::Cell::new(false),
1280            resc_n: std::cell::Cell::new(0),
1281            resc_big: std::cell::Cell::new(0),
1282            resc_off: std::cell::Cell::new(false),
1283            inter8x8: std::env::var("RFF_INTER8")
1284                .ok()
1285                .and_then(|s| s.parse().ok())
1286                .unwrap_or(1),
1287            // ~2 bits per 8x8 luma block (×4) of CAVLC-8x8 overhead the level-aware
1288            // rate still under-charges (no native 8x8 entropy model in CAVLC). Keeps
1289            // the per-MB transform RD from over-picking 8x8 on fine-texture MBs where
1290            // it doesn't compact — content-adaptive: only decisively-favorable MBs win.
1291            inter8_pen: std::env::var("RFF_INTER8_PEN")
1292                .ok()
1293                .and_then(|s| s.parse().ok())
1294                .unwrap_or(8),
1295            // Balanced shares Fast's decision path; only sub-pel differs.
1296            fast: cfg.preset != crate::config::Preset::Quality,
1297            skip_accel_check: cfg.tune_skip_accel_check,
1298            coded_path_v2: cfg.coded_path_v2,
1299            aq_strength: cfg.aq_strength,
1300            tune_lambda_scale: cfg.tune_lambda_scale,
1301            tune_intra_penalty: cfg.tune_intra_penalty,
1302            satd_q: cfg.tune_satd_q,
1303            subpel_force: cfg.tune_subpel || cfg.preset == crate::config::Preset::Balanced,
1304            me_snap: cfg.tune_me_snap,
1305            me_subpel_iter: cfg.tune_me_subpel_iter,
1306            greedy_skip: cfg.tune_greedy_skip,
1307            greedy_min_free: cfg.tune_greedy_skip_min_free.unwrap_or(85),
1308            rd_skip: cfg.tune_rd_skip,
1309            rd_skip_fast_t: cfg.tune_rd_skip_fast_t.unwrap_or(0.0),
1310            rd_skip_min_free: cfg.tune_rd_skip_min_free.unwrap_or(
1311                if cfg.preset == crate::config::Preset::Fast { 60 } else { 90 },
1312            ),
1313            satd_var_thresh: i64::MAX,
1314            mb_use_satd: false,
1315            nnz_l_cache: [0x80; 25],
1316            nnz_c_cache: [[0x80; 9]; 2],
1317            mb_skip_sad: vec![0; mb_w * mb_h],
1318            mb_was_skip: vec![false; mb_w * mb_h],
1319        }
1320    }
1321
1322    /// openh264 `PredictSadSkip`: the greedy P_Skip threshold = the median of the
1323    /// skip SADs of the *skip* neighbours (left A, top B, top-right C, top-left
1324    /// fallback for C). Non-skip neighbours contribute 0, so with no skip neighbour
1325    /// the threshold is 0 (no greedy skip). This makes the skip self-calibrating —
1326    /// it only spreads where a neighbour already skipped at a comparable SAD.
1327    fn pred_skip_sad(&self, mb_x: usize, mb_y: usize) -> u32 {
1328        let mbw = self.mb_w;
1329        let at = |x: isize, y: isize| -> Option<(bool, u32)> {
1330            if x < 0 || y < 0 || x >= mbw as isize {
1331                return None;
1332            }
1333            let i = y as usize * mbw + x as usize;
1334            Some((self.mb_was_skip[i], self.mb_skip_sad[i]))
1335        };
1336        let a = at(mb_x as isize - 1, mb_y as isize); // left
1337        let b = at(mb_x as isize, mb_y as isize - 1); // top
1338        let c = at(mb_x as isize + 1, mb_y as isize - 1) // top-right
1339            .or_else(|| at(mb_x as isize - 1, mb_y as isize - 1)); // top-left fallback
1340        let sad = |n: Option<(bool, u32)>| n.filter(|&(s, _)| s).map_or(0, |(_, v)| v);
1341        let (sa, sb, sc) = (sad(a), sad(b), sad(c));
1342        // B and C unavailable but A available → A only.
1343        if b.is_none() && c.is_none() && a.is_some() {
1344            return sa;
1345        }
1346        match (
1347            a.is_some_and(|(s, _)| s),
1348            b.is_some_and(|(s, _)| s),
1349            c.is_some_and(|(s, _)| s),
1350        ) {
1351            (true, false, false) => sa,
1352            (false, true, false) => sb,
1353            (false, false, true) => sc,
1354            _ => sb.max(sa.min(sc)).min(sa.max(sc)), // median(sa, sb, sc)
1355        }
1356    }
1357
1358    /// The `mb_qp_delta` for the current macroblock (`qp − cur_qp`) and commits the
1359    /// running QPy — called ONLY where the syntax actually codes a delta (I_16x16
1360    /// always; inter / I_4x4 when `cbp != 0`), so a skip / cbp==0 MB leaves `cur_qp`
1361    /// unchanged and inherits it, exactly as the decoder's `step_qp` does.
1362    fn qp_delta(&mut self) -> i32 {
1363        let d = self.qp as i32 - self.cur_qp as i32;
1364        self.cur_qp = self.qp;
1365        d
1366    }
1367
1368    /// MV-predictor neighbors (left, above, above-right) for the 16×16 partition
1369    /// of macroblock `(mb_x, mb_y)`, read from the per-4×4-block grids.
1370    fn mv_neighbors(&self, mb_x: usize, mb_y: usize) -> [MvNeighbor; 3] {
1371        let w4 = self.mb_w * 4;
1372        let get = |avail: bool, bx: isize, by: isize| {
1373            if avail {
1374                let idx = by as usize * w4 + bx as usize;
1375                MvNeighbor {
1376                    available: true,
1377                    mv: self.mv_y[idx],
1378                    ref_idx: self.ref_idx_y[idx],
1379                }
1380            } else {
1381                MvNeighbor::NONE
1382            }
1383        };
1384        let (bx, by) = (mb_x as isize * 4, mb_y as isize * 4);
1385        let a = get(mb_x > 0, bx - 1, by);
1386        let b = get(mb_y > 0, bx, by - 1);
1387        // C = above-right; if unavailable, fall back to D = above-left.
1388        let c = if mb_y > 0 && mb_x + 1 < self.mb_w {
1389            get(true, bx + 4, by - 1)
1390        } else {
1391            get(mb_x > 0 && mb_y > 0, bx - 1, by - 1)
1392        };
1393        [a, b, c]
1394    }
1395
1396    /// The `P_Skip` motion vector (spec §8.4.1.1). P_Skip always references
1397    /// index 0 (the most recent picture).
1398    fn skip_mv(&self, mb_x: usize, mb_y: usize) -> (i32, i32) {
1399        let [a, b, c] = self.mv_neighbors(mb_x, mb_y);
1400        if !a.available
1401            || !b.available
1402            || (a.ref_idx == 0 && a.mv == (0, 0))
1403            || (b.ref_idx == 0 && b.mv == (0, 0))
1404        {
1405            (0, 0)
1406        } else {
1407            predict_mv(a, b, c, 0)
1408        }
1409    }
1410
1411    /// Records a macroblock's per-4×4-block motion state (`ref` = reference index
1412    /// for inter, ignored for intra where `inter` is false).
1413    fn set_mb_mv(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32), inter: bool, refi: i32) {
1414        let w4 = self.mb_w * 4;
1415        for dy in 0..4 {
1416            for dx in 0..4 {
1417                let idx = (mb_y * 4 + dy) * w4 + (mb_x * 4 + dx);
1418                self.mv_y[idx] = mv;
1419                self.inter_y[idx] = inter;
1420                self.ref_idx_y[idx] = if inter { refi } else { -1 };
1421            }
1422        }
1423    }
1424
1425    /// Block-level MV-predictor neighbors for a partition whose top-left 4×4
1426    /// block is `(pbx, pby)` and which is `pwb` blocks wide. Availability uses
1427    /// the decoded-block grid, so in-macroblock partitions see earlier ones.
1428    fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
1429        let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1430        let get = |bx: isize, by: isize| -> MvNeighbor {
1431            if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1432                MvNeighbor::NONE
1433            } else {
1434                let idx = (by * w4 + bx) as usize;
1435                MvNeighbor { available: true, mv: self.mv_y[idx], ref_idx: self.ref_idx_y[idx] }
1436            }
1437        };
1438        let a = get(pbx - 1, pby);
1439        let b = get(pbx, pby - 1);
1440        let mut c = get(pbx + pwb, pby - 1);
1441        if !c.available {
1442            c = get(pbx - 1, pby - 1); // D fallback
1443        }
1444        [a, b, c]
1445    }
1446
1447    /// List-aware block MV-predictor neighbors (`list` 0 or 1), for the B-slice
1448    /// per-list `mvd` predictor. Identical geometry to [`Self::mv_neighbors_block`]
1449    /// but reads the List-1 motion grid when `list == 1`, matching the decoder's
1450    /// `mv_neighbors_list`. A neighbor not coded in this list reads `ref_idx = -1`
1451    /// (so `predict_partition_mv` treats it as non-matching, exactly as the decoder).
1452    fn mv_neighbors_block_list(&self, pbx: isize, pby: isize, pwb: isize, list: usize) -> [MvNeighbor; 3] {
1453        let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1454        let (mvg, refg): (&[(i32, i32)], &[i32]) = if list == 0 {
1455            (&self.mv_y, &self.ref_idx_y)
1456        } else {
1457            (&self.mv1_y, &self.ref_idx1_y)
1458        };
1459        let get = |bx: isize, by: isize| -> MvNeighbor {
1460            if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1461                MvNeighbor::NONE
1462            } else {
1463                let idx = (by * w4 + bx) as usize;
1464                MvNeighbor { available: true, mv: mvg[idx], ref_idx: refg[idx] }
1465            }
1466        };
1467        let a = get(pbx - 1, pby);
1468        let b = get(pbx, pby - 1);
1469        let mut c = get(pbx + pwb, pby - 1);
1470        if !c.available {
1471            c = get(pbx - 1, pby - 1); // D fallback
1472        }
1473        [a, b, c]
1474    }
1475
1476    /// SATD cost of a motion-compensated `rw`×`rh` luma region against the source —
1477    /// THE per-candidate ME cost function (Challenge-1 A2 shape: the per-search
1478    /// invariants arrive as parameters instead of being re-derived per candidate).
1479    /// `hp` is the already-resolved plane cache (`None` ⇔ the fast preset, whose
1480    /// SATD path never reads planes), `hr_on` the hoisted `RFF_HPEL_REF` knob,
1481    /// `src_row` the hoisted source slice base. Dispatch order (interior full-pel →
1482    /// in-place plane read → fused avg+SATD → materialize → `mc_luma` fallback) is
1483    /// the historical `mc_satd` order, so the accepted candidate set — and the
1484    /// bitstream — are byte-identical to it.
1485    #[allow(clippy::too_many_arguments)]
1486    #[inline]
1487    fn mc_satd_hp(
1488        &self,
1489        reference: &crate::RefFrame,
1490        hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1491        hr_on: bool,
1492        // `hr_on && RFF_SATD_AVG` (and accel compiled in) — hoisted per search like
1493        // `hr_on`, so the fused-kernel gate costs zero OnceLock loads per candidate.
1494        // Unused (and always false) on non-accel builds.
1495        sa_on: bool,
1496        src_row: &[u8],
1497        lx: usize,
1498        ly: usize,
1499        rw: usize,
1500        rh: usize,
1501        mv: (i32, i32),
1502    ) -> i64 {
1503        #[cfg(not(accel))]
1504        let _ = sa_on;
1505        #[cfg(feature = "profile")]
1506        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1507        let ch = self.mb_h * 16;
1508        let cw = self.cw;
1509        let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1510        let interior_fullpel = mv.0 & 3 == 0
1511            && mv.1 & 3 == 0
1512            && ix0 >= 0
1513            && iy0 >= 0
1514            && ix0 + rw as isize <= cw as isize
1515            && iy0 + rh as isize <= ch as isize;
1516        #[cfg(feature = "profile")]
1517        {
1518            let fullpel = mv.0 & 3 == 0 && mv.1 & 3 == 0;
1519            satdpath::bump(if interior_fullpel { 0 } else if fullpel { 1 } else { 2 });
1520        }
1521        if interior_fullpel {
1522            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1523            let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1524            return satd_px(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1525        }
1526        if let Some(hp) = hp {
1527            if hr_on {
1528                if let Some((plane, base, stride)) =
1529                    rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1530                {
1531                    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1532                    return satd_px(src_row, cw, &plane[base..], stride, rw, rh);
1533                }
1534            }
1535            // A3: QUARTER-pel — fuse the two-plane (a+b+1)>>1 average into the
1536            // SATD kernel itself (no 256-byte materialize + reload, no FFI hop).
1537            // `satd_avg` returns the exact `Σ|H·d|` that `satd_px` computes on
1538            // the materialized average, so the cost value — and the bitstream —
1539            // are byte-identical; on non-AVX2 (or a declined size) it returns
1540            // `None` and the old materialize path below runs unchanged.
1541            #[cfg(accel)]
1542            if sa_on {
1543                if let Some((pa, ba, pb, bb, stride)) =
1544                    rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1545                {
1546                    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1547                    if let Some(v) = rusty_h264_accel::satd_avg(
1548                        src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh,
1549                    ) {
1550                        return v as i64;
1551                    }
1552                }
1553            }
1554            let mut pred = [0u8; 256];
1555            if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1556                return satd_px(src_row, cw, &pred, rw, rw, rh);
1557            }
1558            mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1559            return satd_px(src_row, cw, &pred, rw, rw, rh);
1560        }
1561        let mut pred = [0u8; 256];
1562        mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1563        satd_px(src_row, cw, &pred, rw, rw, rh)
1564    }
1565
1566    /// Track-B B2.1: the SAD twin of `mc_satd_hp` — the SAME dispatch ladder
1567    /// (interior full-pel → in-place plane read → fused avg → materialize →
1568    /// `mc_luma`), with SAD (`psadbw`-class) distortion. `mc_sad` (the fast
1569    /// preset's function) had NONE of the SATD path's accumulated wins, so the
1570    /// first B2 cut measured 61% MORE `mc_luma` fallbacks; this is the parity fix.
1571    /// Every arm reads the same samples the materializing path would, so the SAD
1572    /// value — and therefore the B2-on bitstream — is unchanged by this function.
1573    #[allow(clippy::too_many_arguments)]
1574    #[inline]
1575    fn mc_sad_hp(
1576        &self,
1577        reference: &crate::RefFrame,
1578        hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1579        hr_on: bool,
1580        src_row: &[u8],
1581        lx: usize,
1582        ly: usize,
1583        rw: usize,
1584        rh: usize,
1585        mv: (i32, i32),
1586        _asrc: Option<&[u8; 256]>,
1587    ) -> i64 {
1588        #[cfg(feature = "profile")]
1589        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1590        let ch = self.mb_h * 16;
1591        let cw = self.cw;
1592        let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1593        let interior_fullpel = mv.0 & 3 == 0
1594            && mv.1 & 3 == 0
1595            && ix0 >= 0
1596            && iy0 >= 0
1597            && ix0 + rw as isize <= cw as isize
1598            && iy0 + rh as isize <= ch as isize;
1599        if interior_fullpel {
1600            let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1601            #[cfg(accel)]
1602            if rw == 16 && rh == 16 {
1603                if let Some(src) = _asrc {
1604                    return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1605                        as i64;
1606                }
1607            }
1608            return sad_strided(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1609        }
1610        if let Some(hp) = hp {
1611            if hr_on {
1612                // Single-plane phases (h/v/c half-pel AND edge full-pel via the
1613                // padded `f` plane — the E-3 move, which `mc_sad` never had).
1614                if let Some((plane, base, stride)) =
1615                    rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1616                {
1617                    return sad_strided(src_row, cw, &plane[base..], stride, rw, rh);
1618                }
1619                // Quarter-pel: fused (a+b+1)>>1 + SAD, no materialize.
1620                if let Some((pa, ba, pb, bb, stride)) =
1621                    rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1622                {
1623                    return sad_avg_strided(src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh);
1624                }
1625            }
1626            let mut pred = [0u8; 256];
1627            if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1628                return sad_strided(src_row, cw, &pred, rw, rw, rh);
1629            }
1630            mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1631            return sad_strided(src_row, cw, &pred, rw, rw, rh);
1632        }
1633        let mut pred = [0u8; 256];
1634        mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1635        sad_strided(src_row, cw, &pred, rw, rw, rh)
1636    }
1637
1638    /// SAD (sum of absolute differences) of a motion-compensated `rw`×`rh` luma
1639    /// region against the source — the **fast** preset's motion-search cost.
1640    ///
1641    /// SAD is far cheaper than SATD (no Hadamard transform), and the inner loop is
1642    /// written as `Σ a.abs_diff(b)` over `u8` slices, the exact pattern LLVM
1643    /// auto-vectorizes to the `psadbw` SAD instruction — the same instruction
1644    /// x264's hand-written assembly uses, but reached without any `unsafe`. (x264's
1645    /// fast presets use SAD for the full-pel search for precisely this reason.)
1646    #[allow(clippy::too_many_arguments)]
1647    fn mc_sad(
1648        &self,
1649        reference: &crate::RefFrame,
1650        sy: &[u8],
1651        lx: usize,
1652        ly: usize,
1653        rw: usize,
1654        rh: usize,
1655        mv: (i32, i32),
1656        // 16-aligned source MB (built once per search) for the asm SAD; `None`
1657        // (and unused) on the scalar build.
1658        _asrc: Option<&[u8; 256]>,
1659    ) -> i64 {
1660        // Descent E depth-6: tag WHO is calling mc_luma. The search's edge fallback and
1661        // reconstruction land in the same `inter-mc` bucket; pricing a recon-side lever
1662        // against the merged total is pricing the wrong population.
1663        #[cfg(feature = "profile")]
1664        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1665        let ch = self.mb_h * 16;
1666        let cw = self.cw;
1667        let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1668        let interior_fullpel = mv.0 & 3 == 0
1669            && mv.1 & 3 == 0
1670            && ix0 >= 0
1671            && iy0 >= 0
1672            && ix0 + rw as isize <= cw as isize
1673            && iy0 + rh as isize <= ch as isize;
1674        // Full-pel interior 16×16: openh264's `psadbw` SAD of the aligned source vs
1675        // the (movdqu) reference block. SAD is exact, so this is byte-identical to the
1676        // scalar path — a pure ME speedup (~2.4× the kernel).
1677        #[cfg(accel)]
1678        if interior_fullpel && rw == 16 && rh == 16 {
1679            if let Some(src) = _asrc {
1680                let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1681                return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1682                    as i64;
1683            }
1684        }
1685        let mut sad = 0u32;
1686        if interior_fullpel {
1687            // Direct from the reference (a copy at full-pel) — no interpolation.
1688            let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1689            let refy = &reference.y;
1690            for dy in 0..rh {
1691                let s = &sy[(ly + dy) * cw + lx..][..rw];
1692                let r = &refy[(ry0 + dy) * cw + rx0..][..rw];
1693                sad += s.iter().zip(r).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1694            }
1695        } else {
1696            let mut pred = [0u8; 256];
1697            // Same plane-cache read (and same preset gate) as `mc_satd`.
1698            let from_planes = !self.fast
1699                && rusty_h264_common::inter::hpel_block(
1700                    reference.hpel(cw, ch),
1701                    lx,
1702                    ly,
1703                    rw,
1704                    rh,
1705                    mv.0,
1706                    mv.1,
1707                    &mut pred,
1708                );
1709            if !from_planes {
1710                mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1711            }
1712            for dy in 0..rh {
1713                let s = &sy[(ly + dy) * cw + lx..][..rw];
1714                let p = &pred[dy * rw..][..rw];
1715                sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1716            }
1717        }
1718        sad as i64
1719    }
1720
1721    /// Luma distortion of a `B_Bi` 16×16 prediction: motion-compensate `l0`/`l1`,
1722    /// average `(p+q+1)>>1` (the decoder's `b_mc` blend at `weighted_bipred_idc=0`),
1723    /// and score vs the source with the SAME metric the per-list searches used —
1724    /// SAD on the fast path, SATD when this MB is SATD-routed — so `J_bi` compares
1725    /// directly against `J0`/`J1`.
1726    fn bi_dist(
1727        &self,
1728        l0: &crate::RefFrame,
1729        l1: &crate::RefFrame,
1730        sy: &[u8],
1731        lx: usize,
1732        ly: usize,
1733        mv0: (i32, i32),
1734        mv1: (i32, i32),
1735    ) -> i64 {
1736        // Descent E/F: identify this mc_luma population by call site.
1737        #[cfg(feature = "profile")]
1738        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1739        let ch = self.mb_h * 16;
1740        let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
1741        mc_luma(&l0.y, self.cw, ch, lx, ly, 16, 16, mv0.0, mv0.1, &mut a);
1742        mc_luma(&l1.y, self.cw, ch, lx, ly, 16, 16, mv1.0, mv1.1, &mut b);
1743        let mut avg = [0u8; 256];
1744        for i in 0..256 {
1745            avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
1746        }
1747        if self.fast && !self.mb_use_satd {
1748            let mut sad = 0u32;
1749            for dy in 0..16 {
1750                let s = &sy[(ly + dy) * self.cw + lx..][..16];
1751                let p = &avg[dy * 16..][..16];
1752                sad += s.iter().zip(p).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
1753            }
1754            sad as i64
1755        } else {
1756            satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, 16, 16, 16)
1757        }
1758    }
1759
1760    /// Distortion of a pre-formed 16×16 luma prediction vs the source (SAD on the
1761    /// fast path, SATD when SATD-routed) — the mode-decision cost for `B_Direct`,
1762    /// on the same scale as the per-list search J so they compare directly.
1763    fn pred_dist(&self, sy: &[u8], lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
1764        if self.fast && !self.mb_use_satd {
1765            let mut sad = 0u32;
1766            for dy in 0..16 {
1767                let s = &sy[(ly + dy) * self.cw + lx..][..16];
1768                let p = &pred[dy * 16..][..16];
1769                sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1770            }
1771            sad as i64
1772        } else {
1773            satd_px(&sy[ly * self.cw + lx..], self.cw, pred, 16, 16, 16)
1774        }
1775    }
1776
1777    /// `colZeroFlag` for absolute 4×4 block `(bx, by)` (spec §8.4.1.2.2): true when
1778    /// the co-located picture `RefPicList1[0]` (`l1`) is short-term (always, here —
1779    /// we use no long-term refs) and its co-located block uses List-0 reference 0
1780    /// with a near-zero (|·| ≤ 1) motion vector. Must match the decoder's `col_zero`.
1781    fn col_zero(&self, l1: &crate::RefFrame, bx: usize, by: usize) -> bool {
1782        if l1.w4 == 0 {
1783            return false;
1784        }
1785        let idx = by * l1.w4 + bx;
1786        if idx >= l1.ref_idx.len() {
1787            return false;
1788        }
1789        l1.ref_idx[idx] == 0 && l1.mv[idx].0.abs() <= 1 && l1.mv[idx].1.abs() <= 1
1790    }
1791
1792    /// Bi-predictive MC of one small region into `pred_y`/`c_pred` at MB-relative
1793    /// offset `(dx, dy)` — the per-4×4 primitive the spatial-direct derivation uses.
1794    /// Mirrors the decoder's `b_mc` (average `(p+q+1)>>1` for bi, copy for uni).
1795    #[allow(clippy::too_many_arguments)]
1796    fn b_mc_block(
1797        &self,
1798        l0: &crate::RefFrame,
1799        l1: &crate::RefFrame,
1800        mb_x: usize,
1801        mb_y: usize,
1802        dx: usize,
1803        dy: usize,
1804        refi0: i32,
1805        m0: (i32, i32),
1806        refi1: i32,
1807        m1: (i32, i32),
1808        pred_y: &mut [u8; 256],
1809        c_pred: &mut [[u8; 64]; 2],
1810    ) {
1811        // Descent E/F: identify this mc_luma population by call site.
1812        #[cfg(feature = "profile")]
1813        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1814        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
1815        let (px, py) = (mb_x * 16 + dx, mb_y * 16 + dy);
1816        let (mut a, mut b) = ([0u8; 16], [0u8; 16]);
1817        // 4-wide luma exists ONLY here: P partitions bottom out at 8×8, so B-frame
1818        // spatial-direct is the encoder's only 4×4 MC. With B-frames on it is ~8% of
1819        // all MC calls and HALF of all sub-pel ones — and `luma_h`/`luma_v` dispatch
1820        // to asm only at width 16/8, so 4-wide otherwise runs the scalar 6-tap.
1821        // Serving it from the cached half-pel planes (bit-identical, and they are
1822        // already built for this reference by the motion search) is strictly better
1823        // than adding a 4-wide asm kernel.
1824        let mc4 = |r: &crate::RefFrame, mv: (i32, i32), out: &mut [u8; 16]| {
1825            if !self.fast
1826                && bdirect_planes_enabled()
1827                && rusty_h264_common::inter::hpel_block(
1828                    r.hpel(self.cw, ch), px, py, 4, 4, mv.0, mv.1, out,
1829                )
1830            {
1831                return;
1832            }
1833            mc_luma(&r.y, self.cw, ch, px, py, 4, 4, mv.0, mv.1, out);
1834        };
1835        if refi0 >= 0 {
1836            mc4(l0, m0, &mut a);
1837        }
1838        if refi1 >= 0 {
1839            mc4(l1, m1, &mut b);
1840        }
1841        for yy in 0..4 {
1842            for xx in 0..4 {
1843                let i = yy * 4 + xx;
1844                let v = match (refi0 >= 0, refi1 >= 0) {
1845                    (true, true) => bi_blend(a[i] as i32, b[i] as i32, self.bi_w),
1846                    (true, false) => a[i],
1847                    _ => b[i],
1848                };
1849                pred_y[(dy + yy) * 16 + (dx + xx)] = v;
1850            }
1851        }
1852        // Chroma: the co-located 2×2 block at half resolution.
1853        let (cpx, cpy) = (mb_x * 8 + dx / 2, mb_y * 8 + dy / 2);
1854        for c in 0..2 {
1855            let (r0, r1) = if c == 0 { (&l0.u, &l1.u) } else { (&l0.v, &l1.v) };
1856            let (mut ca, mut cb) = ([0u8; 4], [0u8; 4]);
1857            if refi0 >= 0 {
1858                mc_chroma(r0, self.ccw, cch, cpx, cpy, 2, 2, m0.0, m0.1, &mut ca);
1859            }
1860            if refi1 >= 0 {
1861                mc_chroma(r1, self.ccw, cch, cpx, cpy, 2, 2, m1.0, m1.1, &mut cb);
1862            }
1863            for yy in 0..2 {
1864                for xx in 0..2 {
1865                    let i = yy * 2 + xx;
1866                    let v = match (refi0 >= 0, refi1 >= 0) {
1867                        (true, true) => bi_blend(ca[i] as i32, cb[i] as i32, self.bi_w),
1868                        (true, false) => ca[i],
1869                        _ => cb[i],
1870                    };
1871                    c_pred[c][(dy / 2 + yy) * 8 + (dx / 2 + xx)] = v;
1872                }
1873            }
1874        }
1875    }
1876
1877    /// Spatial-direct (`direct_spatial_mv_pred_flag == 1`) prediction for a 16×16 B
1878    /// macroblock — the shared basis of `B_Skip` and `B_Direct_16x16`. Returns the
1879    /// prediction and the per-4×4 `(refIdxL0, mvL0, refIdxL1, mvL1)` motion the
1880    /// decoder's `decode_b_direct` derives (so the caller commits identical motion).
1881    fn b_direct(
1882        &self,
1883        l0: &crate::RefFrame,
1884        l1: &crate::RefFrame,
1885        mb_x: usize,
1886        mb_y: usize,
1887    ) -> ([u8; 256], [[u8; 64]; 2], [(i32, (i32, i32), i32, (i32, i32)); 16]) {
1888        let (nbx, nby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
1889        let n0 = self.mv_neighbors_block_list(nbx, nby, 4, 0);
1890        let n1 = self.mv_neighbors_block_list(nbx, nby, 4, 1);
1891        let min_pos = |a: i32, b: i32| if a < 0 { b } else if b < 0 { a } else { a.min(b) };
1892        let rid = |n: &[MvNeighbor; 3]| min_pos(min_pos(n[0].ref_idx, n[1].ref_idx), n[2].ref_idx);
1893        let (mut refi0, mut refi1) = (rid(&n0), rid(&n1));
1894        let direct_zero = refi0 < 0 && refi1 < 0;
1895        if direct_zero {
1896            refi0 = 0;
1897            refi1 = 0;
1898        }
1899        let mv0 = if refi0 >= 0 && !direct_zero { predict_mv(n0[0], n0[1], n0[2], refi0) } else { (0, 0) };
1900        let mv1 = if refi1 >= 0 && !direct_zero { predict_mv(n1[0], n1[1], n1[2], refi1) } else { (0, 0) };
1901        let mut pred_y = [0u8; 256];
1902        let mut c_pred = [[0u8; 64]; 2];
1903        let mut motion = [(0i32, (0i32, 0i32), 0i32, (0i32, 0i32)); 16];
1904        for sby in 0..4 {
1905            for sbx in 0..4 {
1906                let cz = !direct_zero && self.col_zero(l1, mb_x * 4 + sbx, mb_y * 4 + sby);
1907                let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
1908                let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
1909                motion[sby * 4 + sbx] = (refi0, m0, refi1, m1);
1910                self.b_mc_block(l0, l1, mb_x, mb_y, sbx * 4, sby * 4, refi0, m0, refi1, m1, &mut pred_y, &mut c_pred);
1911            }
1912        }
1913        (pred_y, c_pred, motion)
1914    }
1915
1916    /// Commits a spatial-direct MB's per-4×4 motion into the List-0/List-1 grids so
1917    /// later MBs' neighbor predictors see it (mirrors the decoder's `b_set_motion`).
1918    fn commit_direct_motion(&mut self, mb_x: usize, mb_y: usize, motion: &[(i32, (i32, i32), i32, (i32, i32)); 16]) {
1919        let w4 = self.mb_w * 4;
1920        for sby in 0..4 {
1921            for sbx in 0..4 {
1922                let (refi0, m0, refi1, m1) = motion[sby * 4 + sbx];
1923                let idx = (mb_y * 4 + sby) * w4 + (mb_x * 4 + sbx);
1924                self.inter_y[idx] = true;
1925                self.coded_y[idx] = true;
1926                self.mv_y[idx] = m0;
1927                self.ref_idx_y[idx] = refi0;
1928                self.mv1_y[idx] = m1;
1929                self.ref_idx1_y[idx] = refi1;
1930            }
1931        }
1932    }
1933
1934    /// Rate-aware motion search for a luma region: full-pel diamond + half/
1935    /// quarter-pel refinement minimizing `J = SATD + λ·bits(mvd)`, where the
1936    /// motion cost is measured against `predictors[0]` (the MV predictor the
1937    /// `mvd` will actually be coded against). The search is seeded from every
1938    /// entry in `predictors` plus `(0,0)`. Returns the best MV and its `J`.
1939    ///
1940    /// The rate term is only a *search heuristic* — whatever MV it picks is still
1941    /// coded as a correct `mvd`, so this never affects decodability.
1942    #[allow(clippy::too_many_arguments)]
1943    /// ME ORACLE PROBE (`RFF_ME_ORACLE=1`): does our search actually FIND the best
1944    /// motion vector available to it? Accumulates our chosen cost against an
1945    /// exhaustive +-24 full-pel search refined by the identical sub-pel pass, so a
1946    /// gap is attributable to the SEARCH, not to the cost function or precision.
1947    /// [n, sum(our cost), sum(oracle cost), blocks where oracle won, cost() evals]
1948    fn motion_search(
1949        &self,
1950        reference: &crate::RefFrame,
1951        sy: &[u8],
1952        lx: usize,
1953        ly: usize,
1954        rw: usize,
1955        rh: usize,
1956        predictors: &[(i32, i32)],
1957        lambda_me: f64,
1958        // Some(mv) => skip the full-pel search entirely and refine THIS vector. The
1959        // starting COST is recomputed here rather than passed in, so the baseline the
1960        // refinement must beat is priced by the same closure as every candidate.
1961        start: Option<(i32, i32)>,
1962    ) -> ((i32, i32), i64) {
1963        // Bit length of `se(d)` (Exp-Golomb), i.e. what an `mvd` component costs.
1964        // Branchless closed form of the old `while n > 1 { n >>= 1; len += 2 }` loop:
1965        // that loop yields `len = 1 + 2·floor(log2(codenum+1))`, and for x ≥ 1
1966        // `floor(log2(x)) == 31 - x.leading_zeros()`. Removes a data-dependent branch
1967        // from the innermost ME cost — bit-identical (verified over the d range).
1968        #[inline(always)]
1969        fn mvbits(d: i32) -> u32 {
1970            let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
1971            1 + 2 * (31 - (codenum + 1).leading_zeros())
1972        }
1973        let center = predictors[0];
1974        let probe = me_oracle_on();
1975        // Track-B B2: the full-pel phase (seeds/snap/diamond) prices candidates in
1976        // the SAD domain; the winner is repriced in SATD before rescue/sub-pel.
1977        // Refine-only searches have no full-pel phase, so B2 does not apply there.
1978        // `self.sadfp` is force-mode at construction or the per-frame `b2_mgain`
1979        // dispatcher's routing (mode 1).
1980        let sadfp = !self.fast && start.is_none() && self.sadfp;
1981        // Build the 16-aligned source MB ONCE per search for the asm SAD path (fast
1982        // preset — and B2's SAD full-pel phase — full 16×16). Amortized over every
1983        // candidate's SAD; the reference block stays unaligned (movdqu). Scalar
1984        // build does no copy.
1985        #[cfg(accel)]
1986        let asrc_buf = if (self.fast || sadfp) && rw == 16 && rh == 16 {
1987            let mut a = AlignedMb([0u8; 256]);
1988            for dy in 0..16 {
1989                a.0[dy * 16..dy * 16 + 16].copy_from_slice(&sy[(ly + dy) * self.cw + lx..][..16]);
1990            }
1991            Some(a)
1992        } else {
1993            None
1994        };
1995        #[cfg(accel)]
1996        let asrc: Option<&[u8; 256]> = asrc_buf.as_ref().map(|a| &a.0);
1997        #[cfg(not(accel))]
1998        let asrc: Option<&[u8; 256]> = None;
1999        // Challenge-1 A2: hoist the SATD path's per-search invariants OUT of the
2000        // per-candidate closure. `mc_satd` re-derived, for EVERY candidate: the
2001        // plane-cache OnceLock (an acquire load + branch, twice on the quarter-pel
2002        // arm), the `RFF_HPEL_REF` OnceLock, and the source-row slice base (a bounds
2003        // check). All are constant across the ~20-50 evaluations of one search.
2004        // `mc_satd_hp` is the same dispatch with those values passed in — the same
2005        // arms in the same order, so the accepted candidate set is byte-identical.
2006        let use_sad = self.fast && !self.mb_use_satd;
2007        let cw = self.cw;
2008        // Every non-fast search sub-pel-refines at the end, so the planes are built
2009        // for any reference a search touches — hoisting the get_or_init here does not
2010        // build planes a lazy path would have avoided.
2011        let hp: Option<&rusty_h264_common::inter::HpelPlanes> =
2012            if !self.fast { Some(reference.hpel(cw, self.mb_h * 16)) } else { None };
2013        let hr_on = hpel_ref_enabled();
2014        // A3 gate, hoisted with the rest (`RFF_HPEL_REF=0` restores the FULL pre-C/A3
2015        // copy path, so the fused kernel rides the same master anchor).
2016        let sa_on = cfg!(accel) && hr_on && satd_avg_enabled();
2017        let src_row = &sy[ly * cw + lx..];
2018        let cost = |mv: (i32, i32)| -> i64 {
2019            let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2020            // Fast preset: SAD (psadbw — asm kernel on `--features asm`, else auto-vec)
2021            // — far cheaper than SATD, the single biggest reason x264 fast out-runs us.
2022            let dist = if use_sad {
2023                self.mc_sad(reference, sy, lx, ly, rw, rh, mv, asrc)
2024            } else {
2025                self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
2026            };
2027            dist + (lambda_me * rate as f64) as i64
2028        };
2029        // B2's full-pel-phase cost: SAD distortion, λ scaled to the SAD domain
2030        // (`RFF_ME_SADL`, hoisted). Falls through to `cost` (SATD) whenever B2 is
2031        // off, so every pre-B2 path is untouched.
2032        let lam_fp = lambda_me * if sadfp { me_sadfp_lambda() } else { 1.0 };
2033        let cost_fp = |mv: (i32, i32)| -> i64 {
2034            if !sadfp {
2035                return cost(mv);
2036            }
2037            let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2038            self.mc_sad_hp(reference, hp, hr_on, src_row, lx, ly, rw, rh, mv, asrc)
2039                + (lam_fp * rate as f64) as i64
2040        };
2041        // Seed from (0,0) and each predictor; keep the cheapest.
2042        let refine_only = start.is_some();
2043        let (mut best, mut best_c) = match start {
2044            Some(mv) => (mv, cost(mv)),
2045            None => {
2046                let mut b = (0, 0);
2047                let mut bc = cost_fp(b);
2048                for &p in predictors {
2049                    let pc = cost_fp(p);
2050                    if pc < bc {
2051                        bc = pc;
2052                        b = p;
2053                    }
2054                }
2055                (b, bc)
2056            }
2057        };
2058        // SNAP THE DIAMOND CENTRE TO INTEGER-PEL. The diamond below steps by whole
2059        // pels, so a fractional centre makes EVERY candidate fractional and forces
2060        // all of them through `mc_luma`'s 6-tap filter — measured at 84-90% of all
2061        // SATD evaluations. Snapping puts the whole full-pel phase on the direct
2062        // (no-interpolation) SATD path. The pre-snap seed is kept and re-compared
2063        // after refinement, so this can only change WHERE we search, never make the
2064        // returned vector worse than the seed we started from.
2065        let (seed_mv, mut seed_c) = (best, best_c);
2066        if !refine_only && self.me_snap && (best.0 & 3 != 0 || best.1 & 3 != 0) {
2067            let snapped = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2068            best_c = cost_fp(snapped);
2069            best = snapped;
2070        }
2071        // Coarse-to-fine full-pel search: a 4-point diamond walked at each step
2072        // size from 16 px down to 1 px (steps in quarter-pel units: 64,32,…,4).
2073        // The larger initial steps reach fast motion the predictor missed; the
2074        // diamond stays orthogonal (no diagonals) — diagonal probes were found to
2075        // chase equally-good far matches on ambiguous motion, wrecking MV-field
2076        // coherence and the neighbor predictors.
2077        // The fast preset trusts the neighbour MV predictor and refines locally
2078        // (one coarse reach + fine), like x264's `me=dia`; quality sweeps the full
2079        // coarse-to-fine range. Each step's diamond still walks until no
2080        // improvement, so even fast reaches far motion — just in smaller hops.
2081        // Descent A: the coarse rungs are ~76-80% of full-pel evals at a 0.05-1.0% hit
2082        // rate (near-equal eval counts per rung = the walk almost never walks, so each
2083        // rung is a flat ~4-eval toll). RFF_DIA_LADDER selects which rungs to pay for.
2084        let mut ladder = [0i32; 5];
2085        let mut nladder = 0usize;
2086        let steps: &[i32] = if self.fast {
2087            &[16, 4]
2088        } else {
2089            let m = dia_mask();
2090            for (i, r) in DIA_RUNGS.iter().enumerate() {
2091                if m & (1 << i) != 0 {
2092                    ladder[nladder] = *r;
2093                    nladder += 1;
2094                }
2095            }
2096            &ladder[..nladder]
2097        };
2098        let _gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeDiamond);
2099        // FC: batch a fixed-centre diamond pass through the x4 kernels when every
2100        // candidate is an interior full-pel 16×16 read — one source band covers all
2101        // four candidates. Applies to BOTH cost domains (`sad_16x16_x4` on
2102        // SAD-routed frames, `satd_16x16_x4` otherwise); the fast preset keeps its
2103        // own untouched path. Argmin-of-4 replaces the first-improver cascade —
2104        // measured BD-POSITIVE on the SAD domain (bus −1.71→−2.61) and gated on the
2105        // corpus for the SATD domain the same way. `RFF_ME_FC=0` restores cascade.
2106        let fc = !self.fast && rw == 16 && rh == 16 && cfg!(accel) && me_fc_enabled();
2107        let ch_px = self.mb_h as isize * 16;
2108        for (_si, &step) in steps.iter().enumerate() {
2109            if refine_only {
2110                break;
2111            }
2112            loop {
2113                #[cfg(accel)]
2114                if fc && best.0 & 3 == 0 && best.1 & 3 == 0 {
2115                    // All four candidates full-pel; interior iff the ±step box is.
2116                    let s = (step >> 2) as isize;
2117                    let (bx, by) = (lx as isize + (best.0 >> 2) as isize, ly as isize + (best.1 >> 2) as isize);
2118                    if bx - s >= 0 && by - s >= 0 && bx + s + 16 <= cw as isize && by + s + 16 <= ch_px {
2119                        let offs = [
2120                            (by * cw as isize + bx + s) as usize,
2121                            (by * cw as isize + bx - s) as usize,
2122                            ((by + s) * cw as isize + bx) as usize,
2123                            ((by - s) * cw as isize + bx) as usize,
2124                        ];
2125                        let batch = if sadfp {
2126                            rusty_h264_accel::sad_16x16_x4(src_row, cw, &reference.y, offs, cw)
2127                        } else {
2128                            rusty_h264_accel::satd_16x16_x4(src_row, cw, &reference.y, offs, cw)
2129                        };
2130                        if let Some(sads) = batch {
2131                            let ring = [(step, 0), (-step, 0), (0, step), (0, -step)];
2132                            let (mut bi, mut bc) = (usize::MAX, best_c);
2133                            for (i, &(dx, dy)) in ring.iter().enumerate() {
2134                                let mv = (best.0 + dx, best.1 + dy);
2135                                let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2136                                let cc = sads[i] as i64 + (lam_fp * rate as f64) as i64;
2137                                #[cfg(feature = "profile")]
2138                                diastats::ev(_si);
2139                                if cc < bc {
2140                                    bc = cc;
2141                                    bi = i;
2142                                }
2143                            }
2144                            if bi == usize::MAX {
2145                                break;
2146                            }
2147                            best_c = bc;
2148                            best = (best.0 + ring[bi].0, best.1 + ring[bi].1);
2149                            #[cfg(feature = "profile")]
2150                            diastats::imp(_si);
2151                            continue;
2152                        }
2153                    }
2154                }
2155                let mut improved = false;
2156                for &(dx, dy) in &[(step, 0), (-step, 0), (0, step), (0, -step)] {
2157                    let c = (best.0 + dx, best.1 + dy);
2158                    let cc = cost_fp(c);
2159                    #[cfg(feature = "profile")]
2160                    diastats::ev(_si);
2161                    if cc < best_c {
2162                        best_c = cc;
2163                        best = c;
2164                        improved = true;
2165                        #[cfg(feature = "profile")]
2166                        diastats::imp(_si);
2167                    }
2168                }
2169                if !improved {
2170                    break;
2171                }
2172            }
2173        }
2174        // DIAMOND-STALLED RESCUE (content-adaptive: fires on the FAILURE, not a proxy).
2175        // The gradient-descent diamond stalls at a plateau on FLAT cost surfaces and
2176        // never reaches the far-but-better MV that exists within ±16 (measured: ~+22%
2177        // BD-rate vs x264's simple dia on smooth content). The precise stall signal is
2178        // the CONJUNCTION: a FLAT source block (low variance) whose diamond match STILL
2179        // has a high residual — because on a flat surface the RIGHT MV predicts near-
2180        // perfectly, so a high residual there means the diamond missed it (a stall).
2181        // (Residual alone fires on busy blocks where a high residual is inherent — that
2182        // was 3.3× slower on mand for nothing; variance alone fires on flat-but-well-
2183        // predicted blocks. The AND targets exactly the stalls.) Then a FINE ±16 step-2
2184        // grid reaches the true minimum. Fires on a fraction of blocks → affordable.
2185        // Quality preset only.
2186        drop(_gd);
2187        // B2: the full-pel phase priced in the SAD domain — reprice the winner AND
2188        // the pre-snap seed into the SATD domain the rescue + sub-pel phases (and the
2189        // final seed-vs-refined comparison) trade in. Two SATD evaluations per
2190        // search, against the ~20-50 candidate evaluations the SAD domain cheapened.
2191        if sadfp {
2192            best_c = cost(best);
2193            seed_c = cost(seed_mv);
2194        }
2195        let _gr = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeRescue);
2196        let flat = !refine_only && self.me_wide && !self.fast && {
2197            let (mut s, mut ss) = (0u64, 0u64);
2198            for dy in 0..rh {
2199                for dx in 0..rw {
2200                    let v = sy[(ly + dy) * self.cw + lx + dx] as u64;
2201                    s += v;
2202                    ss += v * v;
2203                }
2204            }
2205            let n = (rw * rh) as u64;
2206            (ss - s * s / n) / n < self.me_wide_var
2207        };
2208        // The online payoff gate may have disabled the rescue for the rest of this
2209        // frame (irreducible-residual content — rotation/fractal — where the fine grid
2210        // fixes almost nothing; measured 2.25× on rot for a ~0% BD gain). A gated-off
2211        // frame runs exactly the diamond → identical to me_wide-off → never worse.
2212        // FAST-MOTION extension: the flat gate targets smooth-surface stalls, but the
2213        // diamond ALSO stalls on FAST motion (bus/football: an exhaustive ±24 search
2214        // recovers 6-15% BD) — those blocks are high-VARIANCE (detail) so `flat` misses
2215        // them. `me_fast` also fires on any high-residual block; the online payoff gate
2216        // then keeps it only where a wider search actually pays off (fast motion), and
2217        // disables it on irreducible-residual detail — the same self-tuning as flat.
2218        if self.me_wide && !self.fast && (flat || self.me_fast) && !self.resc_off.get() {
2219            let dist = self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, best);
2220            if dist / (rw * rh).max(1) as i64 > self.me_rescue {
2221                // FINE ±16 step-2 grid + ±1 refine — recover the true minimum the
2222                // diamond missed. Fires only on flat-block stalls, so it is affordable.
2223                // SNAP THE GRID CENTRE TO INTEGER-PEL: the diamond seed can be sub-pel
2224                // (sub-pel neighbour predictors), and since every grid point shares
2225                // cx&3, a sub-pel centre forces the WHOLE ±16 grid through mc_luma
2226                // interpolation — measured 89% of zoom's rescue cost. The rescue only
2227                // needs the right REGION (a far MV the diamond missed); the sub-pel
2228                // refine that follows recovers the fraction. Integer centre → the grid
2229                // hits the fast full-pel SATD path (no interpolation).
2230                let pre_c = best_c;
2231                let (cx, cy) = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2232                let mut gb = best;
2233                // BATCHED FULL-PEL GRID (accel): now that the grid centre is integer-pel
2234                // (all points interior full-pel), hoist the interior/bounds check out of
2235                // the loop and call the AVX2 SATD directly — skipping mc_satd's per-point
2236                // interior test + satd_px dispatch. BYTE-IDENTICAL to the cost() path
2237                // (same 2·satd_16x16 + rate), so it is default-on (RFF_ME_BATCH=0 to A/B
2238                // it off). ~+7% zoom / +4% tsrc on top of the snap; the SATD kernel itself
2239                // is already AVX2 and its transform can't amortise across the grid, so
2240                // this per-call-overhead trim is the ceiling for an "asm grid kernel".
2241                let cw = self.cw;
2242                let r = self.me_range;
2243                let batched = rw == 16 && rh == 16 && cfg!(accel) && {
2244                    let (icdx, icdy) = (cx >> 2, cy >> 2);
2245                    lx as i32 + icdx >= r
2246                        && lx as i32 + icdx + r + 16 <= cw as i32
2247                        && ly as i32 + icdy >= r
2248                        && ly as i32 + icdy + r + 16 <= (self.mb_h * 16) as i32
2249                        && me_batch_enabled()
2250                };
2251                #[cfg(accel)]
2252                if batched {
2253                    let (icdx, icdy) = ((cx >> 2), (cy >> 2));
2254                    let src = &sy[ly * cw + lx..];
2255                    let mut dy = -r;
2256                    while dy <= r {
2257                        let rby = (ly as i32 + icdy + dy) as usize;
2258                        let mut dx = -r;
2259                        while dx <= r {
2260                            let rbx = (lx as i32 + icdx + dx) as usize;
2261                            let satd =
2262                                2 * rusty_h264_accel::satd_16x16(src, cw, &reference.y[rby * cw + rbx..], cw) as i64;
2263                            let mv = (cx + dx * 4, cy + dy * 4);
2264                            let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2265                            let cc = satd + (lambda_me * rate as f64) as i64;
2266                            if cc < best_c {
2267                                best_c = cc;
2268                                gb = mv;
2269                            }
2270                            dx += 2;
2271                        }
2272                        dy += 2;
2273                    }
2274                }
2275                if !batched {
2276                    let mut dy = -r;
2277                    while dy <= r {
2278                        let mut dx = -r;
2279                        while dx <= r {
2280                            let cc = cost((cx + dx * 4, cy + dy * 4));
2281                            if cc < best_c {
2282                                best_c = cc;
2283                                gb = (cx + dx * 4, cy + dy * 4);
2284                            }
2285                            dx += 2;
2286                        }
2287                        dy += 2;
2288                    }
2289                }
2290                best = gb;
2291                for dy in -1..=1 {
2292                    for dx in -1..=1 {
2293                        let c = (best.0 + dx * 4, best.1 + dy * 4);
2294                        let cc = cost(c);
2295                        if cc < best_c {
2296                            best_c = cc;
2297                            best = c;
2298                        }
2299                    }
2300                }
2301                // LEARNING PHASE: for the first `me_learn` stalls of the frame, tally
2302                // whether the grid actually paid off (≥6.25% cost cut). Once the window
2303                // fills, if too few paid off the residual is irreducible on this content
2304                // → disable the rescue for the rest of the frame. The window's own MVs
2305                // are committed, but they're a small spatially-clustered set (not
2306                // improvement-selected), so on net-neutral content (rot) they can't
2307                // regress — only frame-level on/off avoids the per-block B-direct
2308                // selection effect.
2309                let n = self.resc_n.get();
2310                if n < self.me_learn {
2311                    self.resc_n.set(n + 1);
2312                    if best_c * 16 <= pre_c * 15 {
2313                        self.resc_big.set(self.resc_big.get() + 1);
2314                    }
2315                    if n + 1 == self.me_learn
2316                        && self.resc_big.get() * 100 < self.me_learn * self.me_payoff_pct
2317                    {
2318                        self.resc_off.set(true);
2319                    }
2320                }
2321            }
2322        }
2323        drop(_gr);
2324        let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeSubpel);
2325        // Sub-pel refinement uses the 6-tap/bilinear interpolation — the expensive
2326        // per-pixel `mc_luma` path that profiling pinned at ~55% of the entire
2327        // encode. The fast preset skips it (integer-pel only, like x264's fastest
2328        // presets `subme=0`): ~3× faster, trading a little quality on sub-pixel
2329        // motion. The quality preset does the full half-pel + quarter-pel rings.
2330        if probe {
2331            // Exhaustive +-24 full-pel around the same centre, then the SAME sub-pel
2332            // pass, so only the full-pel search strategy differs.
2333            let mut ob = center;
2334            let mut oc = i64::MAX;
2335            for gy in -24i32..=24 {
2336                for gx in -24i32..=24 {
2337                    let c = (center.0 + gx * 4, center.1 + gy * 4);
2338                    let cc = cost(c);
2339                    if cc < oc {
2340                        oc = cc;
2341                        ob = c;
2342                    }
2343                }
2344            }
2345            let fullpel_best = ob;
2346            for &st in &[2i32, 1] {
2347                for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2348                    let c = (ob.0 + dx, ob.1 + dy);
2349                    let cc = cost(c);
2350                    if cc < oc {
2351                        oc = cc;
2352                        ob = c;
2353                    }
2354                }
2355            }
2356            // EXHAUSTIVE sub-pel: every quarter-pel offset in +-3 around the full-pel
2357            // winner. Our own pass is a single 4-point probe at half then quarter, so
2358            // this is what separates a sub-pel deficiency from a full-pel one.
2359            let mut oc_sp = oc;
2360            for dy in -3i32..=3 {
2361                for dx in -3i32..=3 {
2362                    let c = (fullpel_best.0 + dx, fullpel_best.1 + dy);
2363                    let cc = cost(c);
2364                    if cc < oc_sp {
2365                        oc_sp = cc;
2366                    }
2367                }
2368            }
2369            // our own sub-pel pass has not run yet; replicate it for a fair compare
2370            let (mut mb_, mut mc_) = (best, best_c);
2371            for &st in &[2i32, 1] {
2372                for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2373                    let c = (mb_.0 + dx, mb_.1 + dy);
2374                    let cc = cost(c);
2375                    if cc < mc_ {
2376                        mc_ = cc;
2377                        mb_ = c;
2378                    }
2379                }
2380            }
2381            use std::sync::atomic::Ordering::Relaxed;
2382            ME_PROBE[0].fetch_add(1, Relaxed);
2383            ME_PROBE[1].fetch_add(mc_.max(0) as u64, Relaxed);
2384            ME_PROBE[2].fetch_add(oc.max(0) as u64, Relaxed);
2385            ME_PROBE[3].fetch_add((mc_ > oc) as u64, Relaxed);
2386            ME_PROBE[5].fetch_add(oc_sp.max(0) as u64, Relaxed);
2387            ME_PROBE[6].fetch_add((mc_ > oc_sp) as u64, Relaxed);
2388        }
2389        let subpel: &[i32] = if (self.fast && !self.subpel_force) || (self.sp_defer.get() && !refine_only) {
2390            &[]
2391        } else {
2392            &[2, 1]
2393        };
2394        // U1 harvest: the null arm is the full-pel winner we would keep on a skip.
2395        let (hv_pre, mut hv_evals) = (best_c, 0u32);
2396        // `to_best` = eval index of the LAST improvement; `ring1` = the cost after the
2397        // first 8-point half-pel ring. Together they answer "how many of these 29
2398        // evaluations actually matter", which is the ceiling for any cheaper pattern.
2399        let (mut hv_to_best, mut hv_ring1) = (0u32, i64::MIN);
2400        let mut pat = subpel_pattern_override()
2401            .unwrap_or(if self.sp_single_pass { 2 } else { 0 });
2402        let (sp_learn, sp_t) = sp_dispatch_cfg();
2403        // Only dispatch when the caller has not pinned a pattern (pat 0 = default).
2404        let sp_dispatching = sp_learn > 0 && pat == 0 && !subpel.is_empty();
2405        if sp_dispatching && self.sp_learn_n.get() >= sp_learn && self.sp_1pass.get() {
2406            pat = 2;
2407        }
2408        // Descent D-2 MEMO. The ring walks around a MOVING centre, so iteration N+1's
2409        // ring necessarily re-contains the previous centre and several previous ring
2410        // points: 27-44% of sub-pel evaluations re-price an MV this refinement already
2411        // priced. `cost()` is PURE in `mv` (rate from mv-centre; distortion from the
2412        // fixed reference/source/block captures), so memoizing is EXACT -- identical
2413        // costs, identical comparisons, identical chosen MV, byte-identical output.
2414        // A miss simply recomputes, so the table's hit rate is a SPEED property only.
2415        //
2416        // 64-entry direct-mapped on the low bits of the MV, tagged with the full MV so
2417        // a collision is a miss rather than a wrong answer. Stack-resident (1 KiB) and
2418        // re-initialized per refinement: measured cheaper than a thread-local + RefCell
2419        // borrow on every evaluation, since ~60% of lookups miss.
2420        const SP_MEMO_N: usize = 64;
2421        #[inline(always)]
2422        fn sp_slot(mv: (i32, i32)) -> usize {
2423            ((mv.0 & 7) as usize) | (((mv.1 & 7) as usize) << 3)
2424        }
2425        let mut memo_mv = [(i32::MIN, i32::MIN); SP_MEMO_N];
2426        let mut memo_c = [0i64; SP_MEMO_N];
2427        if !subpel.is_empty() {
2428            let s0 = sp_slot(best);
2429            memo_mv[s0] = best;
2430            memo_c[s0] = best_c;
2431        }
2432        // Descent D-2 census: the ring walks around a MOVING centre, so iteration N+1's ring
2433        // necessarily re-contains the previous centre and several previous ring points.
2434        // Count how many sub-pel evaluations price an MV this refinement ALREADY priced
2435        // -- redundant recompute is byte-identically removable, unlike dropping work.
2436        #[cfg(feature = "profile")]
2437        let mut seen: Vec<(i32, i32)> = Vec::with_capacity(64);
2438        #[cfg(feature = "profile")]
2439        {
2440            seen.push(best);
2441        }
2442        // Track-B B3: the sub-pel iteration BUDGET. The ring walks until no
2443        // improvement; Descent D's census says iteration 1 carries 55% of evals at
2444        // an 11-13% hit rate, iteration 2 another 35-40% at 1.5-2.5%, and the tail
2445        // past that almost never pays — but under B2's SAD-chosen starts the tail
2446        // GROWS (+27% ns/search), eating the SAD savings. A cap bounds the walk the
2447        // way x264's fixed subme budget does. 0 (default) = unlimited =
2448        // byte-identical; bitstream-changing otherwise → BD-gated, opt-in.
2449        let sp_cap = sp_maxit();
2450        // ③: batched fixed-centre half-pel ring (see `sp_fc_enabled`).
2451        let sp_fc = sp_fc_enabled() && !self.fast && rw == 16 && rh == 16 && cfg!(accel);
2452        for &step in subpel {
2453            // Snapping starts this refine from an integer centre instead of the
2454            // seed's own fractional lattice, so a single 8-point pass can leave
2455            // precision behind. Walk it until it stops improving to compensate —
2456            // the snap is what pays for the extra probes.
2457            let ring8 = [
2458                (step, 0), (-step, 0), (0, step), (0, -step),
2459                (step, step), (-step, -step), (step, -step), (-step, step),
2460            ];
2461            let ring4 = [(step, 0), (-step, 0), (0, step), (0, -step)];
2462            let ring: &[(i32, i32)] = if pat & 1 != 0 { &ring4 } else { &ring8 };
2463            let mut _iter = 0u32;
2464            loop {
2465                // ③: from an INTEGER centre at step 2, all 8 ring candidates are
2466                // single-plane reads (h/h/v/v axes, c/c/c/c diagonals) — batch them
2467                // as two x4 kernel calls and take the argmin (first-wins in ring
2468                // order). Any decline (edge, half-pel centre, ring4 pattern) falls
2469                // through to the cascading walk for this pass.
2470                // ③b: the QUARTER step — every ±1 offset makes a component odd, so
2471                // all 8 candidates are two-plane average pairs regardless of the
2472                // centre's phase; two `satd_avg_x4` calls cover the ring.
2473                #[cfg(accel)]
2474                if sp_fc && step == 1 && pat & 1 == 0 {
2475                    _iter += 1;
2476                    let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2477                    let ring8 = [
2478                        (1, 0), (-1, 0), (0, 1), (0, -1),
2479                        (1, 1), (-1, -1), (1, -1), (-1, 1),
2480                    ];
2481                    let mut prs: [Option<(&[u8], usize, &[u8], usize, usize)>; 8] = [None; 8];
2482                    let mut all = true;
2483                    for (i, &(dx, dy)) in ring8.iter().enumerate() {
2484                        prs[i] = rusty_h264_common::inter::hpel_qpel_refs(
2485                            hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2486                        );
2487                        all &= prs[i].is_some();
2488                    }
2489                    if all {
2490                        let stride = prs[0].unwrap().4;
2491                        let pack = |a: usize, b: usize, c2: usize, d: usize| {
2492                            let g = |i: usize| {
2493                                let (pa, oa, pb, ob, _) = prs[i].unwrap();
2494                                (pa, oa, pb, ob)
2495                            };
2496                            rusty_h264_accel::satd_avg_16x16_x4(
2497                                src_row, cw, [g(a), g(b), g(c2), g(d)], stride,
2498                            )
2499                        };
2500                        if let (Some(ax), Some(di)) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7)) {
2501                            let (mut bi, mut bc) = (usize::MAX, best_c);
2502                            for i in 0..8 {
2503                                let (dx, dy) = ring8[i];
2504                                let mv = (best.0 + dx, best.1 + dy);
2505                                let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2506                                let d = if i < 4 { ax[i] } else { di[i - 4] } as i64;
2507                                let cc = d + (lambda_me * rate as f64) as i64;
2508                                hv_evals += 1;
2509                                if cc < bc {
2510                                    bc = cc;
2511                                    bi = i;
2512                                }
2513                            }
2514                            if hv_ring1 == i64::MIN {
2515                                hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2516                            }
2517                            if bi == usize::MAX
2518                                || !self.me_subpel_iter
2519                                || pat & 2 != 0
2520                                || (sp_cap != 0 && _iter >= sp_cap)
2521                            {
2522                                if bi != usize::MAX {
2523                                    best_c = bc;
2524                                    best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2525                                    hv_to_best = hv_evals;
2526                                }
2527                                break;
2528                            }
2529                            best_c = bc;
2530                            best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2531                            hv_to_best = hv_evals;
2532                            continue;
2533                        }
2534                    }
2535                    _iter -= 1;
2536                }
2537                #[cfg(accel)]
2538                if sp_fc && step == 2 && best.0 & 3 == 0 && best.1 & 3 == 0 && pat & 1 == 0 {
2539                    _iter += 1;
2540                    let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2541                    let ring8 = [
2542                        (step, 0), (-step, 0), (0, step), (0, -step),
2543                        (step, step), (-step, -step), (step, -step), (-step, step),
2544                    ];
2545                    let mut refs8: [Option<(&[u8], usize, usize)>; 8] = [None; 8];
2546                    let mut all = true;
2547                    for (i, &(dx, dy)) in ring8.iter().enumerate() {
2548                        refs8[i] = rusty_h264_common::inter::hpel_ref(
2549                            hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2550                        );
2551                        all &= refs8[i].is_some();
2552                    }
2553                    if all {
2554                        let stride = refs8[0].unwrap().2;
2555                        let pack = |a: usize, b: usize, c2: usize, d: usize| {
2556                            let g = |i: usize| {
2557                                let (p, o, _) = refs8[i].unwrap();
2558                                (p, o)
2559                            };
2560                            rusty_h264_accel::satd_16x16_x4p(
2561                                src_row, cw, [g(a), g(b), g(c2), g(d)], stride,
2562                            )
2563                        };
2564                        if let (Some(ax), Some(di)) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7)) {
2565                            let (mut bi, mut bc) = (usize::MAX, best_c);
2566                            for i in 0..8 {
2567                                let (dx, dy) = ring8[i];
2568                                let mv = (best.0 + dx, best.1 + dy);
2569                                let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2570                                let d = if i < 4 { ax[i] } else { di[i - 4] } as i64;
2571                                let cc = d + (lambda_me * rate as f64) as i64;
2572                                hv_evals += 1;
2573                                if cc < bc {
2574                                    bc = cc;
2575                                    bi = i;
2576                                }
2577                            }
2578                            if hv_ring1 == i64::MIN {
2579                                hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2580                            }
2581                            if bi == usize::MAX
2582                                || !self.me_subpel_iter
2583                                || pat & 2 != 0
2584                                || (sp_cap != 0 && _iter >= sp_cap)
2585                            {
2586                                if bi != usize::MAX {
2587                                    best_c = bc;
2588                                    best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2589                                    hv_to_best = hv_evals;
2590                                }
2591                                break;
2592                            }
2593                            best_c = bc;
2594                            best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2595                            hv_to_best = hv_evals;
2596                            continue;
2597                        }
2598                    }
2599                    _iter -= 1; // declined — the cascade pass below re-counts it
2600                }
2601                let mut improved = false;
2602                _iter += 1;
2603                for (_pi, &(dx, dy)) in ring.iter().enumerate() {
2604                    let c = (best.0 + dx, best.1 + dy);
2605                    let slot = sp_slot(c);
2606                    let cc = if memo_mv[slot] == c {
2607                        memo_c[slot]
2608                    } else {
2609                        let v = cost(c);
2610                        memo_mv[slot] = c;
2611                        memo_c[slot] = v;
2612                        v
2613                    };
2614                    hv_evals += 1;
2615                    // Descent D: which ring POSITION and which ITERATION actually pay?
2616                    // Same census that showed the diamond's coarse rungs were noise,
2617                    // aimed at the stage that is now 41% of encode.
2618                    #[cfg(feature = "profile")]
2619                    {
2620                        spstats::ev(if step == 2 { 0 } else { 1 }, _pi, _iter);
2621                        if seen.contains(&c) {
2622                            spstats::redundant();
2623                        } else {
2624                            seen.push(c);
2625                        }
2626                    }
2627                    if cc < best_c {
2628                        best_c = cc;
2629                        best = c;
2630                        improved = true;
2631                        hv_to_best = hv_evals;
2632                        #[cfg(feature = "profile")]
2633                        spstats::imp(if step == 2 { 0 } else { 1 }, _pi, _iter);
2634                    }
2635                }
2636                if hv_ring1 == i64::MIN {
2637                    hv_ring1 = best_c;
2638                }
2639                if !improved
2640                    || !self.me_subpel_iter
2641                    || pat & 2 != 0
2642                    || (sp_cap != 0 && _iter >= sp_cap)
2643                {
2644                    break;
2645                }
2646            }
2647        }
2648        if sp_dispatching {
2649            let n = self.sp_learn_n.get();
2650            if n < sp_learn {
2651                self.sp_learn_n.set(n + 1);
2652                if hv_ring1 != i64::MIN {
2653                    self.sp_ring1.set(self.sp_ring1.get() + (hv_pre - hv_ring1).max(0));
2654                    self.sp_total.set(self.sp_total.get() + (hv_pre - best_c).max(0));
2655                }
2656                if n + 1 == sp_learn {
2657                    let tot = self.sp_total.get();
2658                    // Concentrated in ring 1 -> the later rings are affordable to drop.
2659                    self.sp_1pass.set(tot > 0 && self.sp_ring1.get() * 100 >= tot * sp_t);
2660                }
2661            }
2662        }
2663        if !subpel.is_empty() && subpel_harvest::enabled() {
2664            subpel_harvest::record(hv_pre, best_c, lambda_me, rw, rh, hv_evals, hv_to_best, hv_ring1);
2665        }
2666        // The snap moved the search off the seed; if the seed was better after all,
2667        // keep it. This is what makes the snap safe by construction.
2668        if self.me_snap && seed_c < best_c {
2669            best = seed_mv;
2670            best_c = seed_c;
2671        }
2672        (best, best_c)
2673    }
2674
2675    /// Encodes macroblock `(mb_x, mb_y)` as an inter macroblock of the given
2676    /// `mode` (0 = P_L0_16x16, 1 = P_16x8, 2 = P_8x16) with one motion vector
2677    /// per partition: motion-compensate each partition, code the macroblock
2678    /// residual, and reconstruct.
2679    #[allow(clippy::too_many_arguments)]
2680    /// Dispatch to the current coded path (`_v1`) or the isolated fused path
2681    /// (`_v2`), selected by the hidden `coded_path_v2` A/B knob. Both produce
2682    /// byte-identical bitstreams (gated by the `coded_path_ab` test); the split
2683    /// exists so the two run side-by-side in one binary for honest timing.
2684    #[allow(clippy::too_many_arguments)]
2685    fn encode_inter_mb(
2686        &mut self,
2687        w: &mut BitWriter,
2688        refs: &[crate::RefFrame],
2689        sy: &[u8],
2690        su: &[u8],
2691        sv: &[u8],
2692        mb_x: usize,
2693        mb_y: usize,
2694        mode: u8,
2695        parts: &[(i32, (i32, i32))],
2696    ) {
2697        if self.coded_path_v2 {
2698            self.encode_inter_mb_v2(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2699        } else {
2700            self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2701        }
2702    }
2703
2704    /// Isolated, coefficient-fused inter coding path (A/B twin of `_v1`). The
2705    /// quantized luma levels stay in the hot 16-byte-aligned i16 DCT buffer for the
2706    /// whole MB; the i32 form is materialized on demand only for *coded* blocks
2707    /// (CAVLC scan + recon dequant), so uncoded quads never pay the conversion and
2708    /// there is no 256-word i32 `q_blocks` round-trip. Byte-identical to `_v1`
2709    /// (gated by `coded_path_ab`). Accel-only optimization; the scalar build reuses
2710    /// `_v1` unchanged.
2711    #[allow(clippy::too_many_arguments)]
2712    fn encode_inter_mb_v2(
2713        &mut self,
2714        w: &mut BitWriter,
2715        refs: &[crate::RefFrame],
2716        sy: &[u8],
2717        su: &[u8],
2718        sv: &[u8],
2719        mb_x: usize,
2720        mb_y: usize,
2721        mode: u8,
2722        parts: &[(i32, (i32, i32))],
2723    ) {
2724        // Descent E/F: identify this mc_luma population by call site.
2725        #[cfg(feature = "profile")]
2726        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
2727        #[cfg(not(accel))]
2728        {
2729            self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2730        }
2731        #[cfg(accel)]
2732        {
2733            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
2734            let (qp, qpc) = (self.qp, self.qpc);
2735            let w4 = self.mb_w * 4;
2736            let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
2737
2738            // ---- per-partition motion compensation + MV prediction (== v1) ----
2739            let mut pred_y = [0u8; 256];
2740            let mut c_pred = [[0u8; 64]; 2];
2741            let mut mvds = [(0i32, 0i32); 4];
2742            let mut n_mvd = 0;
2743            let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
2744            for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
2745                let (refi, mv) = parts[part];
2746                let reference = &refs[refi as usize];
2747                let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
2748                let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
2749                let pmv = predict_partition_mv(mode, part, a, b, c, refi);
2750                mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
2751                n_mvd += 1;
2752                for by in ry / 4..ry / 4 + rh / 4 {
2753                    for bx in rx / 4..rx / 4 + rw / 4 {
2754                        let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
2755                        self.mv_y[idx] = mv;
2756                        self.inter_y[idx] = true;
2757                        self.ref_idx_y[idx] = refi;
2758                        self.coded_y[idx] = true;
2759                    }
2760                }
2761                if rw == 16 && rh == 16 {
2762                    self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
2763                } else {
2764                    let mut tmp = [0u8; 256];
2765                    self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
2766                    for dy in 0..rh {
2767                        for dx in 0..rw {
2768                            pred_y[(ry + dy) * 16 + (rx + dx)] = tmp[dy * rw + dx];
2769                        }
2770                    }
2771                }
2772                let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
2773                for cc in 0..2 {
2774                    let rc = if cc == 0 { &reference.u } else { &reference.v };
2775                    if crw == 8 && crh == 8 {
2776                        mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
2777                    } else {
2778                        let mut tc = [0u8; 64];
2779                        mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
2780                        for dy in 0..crh {
2781                            for dx in 0..crw {
2782                                c_pred[cc][(cry + dy) * 8 + (crx + dx)] = tc[dy * crw + dx];
2783                            }
2784                        }
2785                    }
2786                }
2787            }
2788
2789            // ---- luma residual + quantization: keep levels in the i16 buffer ----
2790            let mut dctw = AlignedDct([0i16; 256]);
2791            let dct = &mut dctw.0;
2792            let mut cbp_luma = 0u32;
2793            drop(_g_mc);
2794            let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
2795            let base = mb_y * 16 * self.cw + mb_x * 16;
2796            for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
2797                rusty_h264_accel::dct_four_t4(
2798                    &mut dct[qi * 64..qi * 64 + 64],
2799                    &sy[base + qy * self.cw + qx..],
2800                    self.cw,
2801                    &pred_y[qy * 16 + qx..],
2802                    16,
2803                );
2804            }
2805            let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
2806            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
2807            for qi in 0..4 {
2808                rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
2809            }
2810            // cbp per quad straight from the i16 levels (no i32 q_blocks copy).
2811            for blk in 0..16 {
2812                if dct[blk * 16..blk * 16 + 16].iter().any(|&v| v != 0) {
2813                    cbp_luma |= 1 << (blk / 4);
2814                }
2815            }
2816
2817            // ---- chroma residual (identical to v1: c_q stays i32) ----
2818            let mut c_dc_levels = [[0i32; 4]; 2];
2819            let mut c_recon_dc = [[0i32; 4]; 2];
2820            let mut c_q = [[[0i32; 16]; 4]; 2];
2821            let (mut any_ac, mut any_dc) = (false, false);
2822            for c in 0..2 {
2823                let src = if c == 0 { su } else { sv };
2824                let dc2x2 = {
2825                    #[repr(align(16))]
2826                    struct A([i16; 64]);
2827                    let mut cdct = A([0i16; 64]);
2828                    rusty_h264_accel::dct_four_t4(
2829                        &mut cdct.0,
2830                        &src[(mb_y * 8) * self.ccw + mb_x * 8..],
2831                        self.ccw,
2832                        &c_pred[c],
2833                        8,
2834                    );
2835                    let dc = [cdct.0[0] as i32, cdct.0[16] as i32, cdct.0[32] as i32, cdct.0[48] as i32];
2836                    let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
2837                    let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
2838                    rusty_h264_accel::quant_four_4x4(&mut cdct.0, &ffc, mfc);
2839                    for i in 0..4 {
2840                        let q = &mut c_q[c][i];
2841                        q[0] = 0;
2842                        for j in 1..16 {
2843                            let v = cdct.0[i * 16 + j] as i32;
2844                            q[j] = v;
2845                            if v != 0 {
2846                                any_ac = true;
2847                            }
2848                        }
2849                    }
2850                    dc
2851                };
2852                let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
2853                if dl.iter().any(|&v| v != 0) {
2854                    any_dc = true;
2855                }
2856                c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
2857                c_dc_levels[c] = dl;
2858            }
2859            let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
2860            let cbp = cbp_luma | (cbp_chroma << 4);
2861
2862            // ---- emit syntax (== v1) ----
2863            drop(_g_tq);
2864            let _g_syn = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
2865            w.write_ue(mode as u32);
2866            let num_refs = refs.len();
2867            if num_refs > 1 {
2868                for &(refi, _) in parts {
2869                    write_ref_idx(w, refi, num_refs);
2870                }
2871            }
2872            for &(mvdx, mvdy) in &mvds[..n_mvd] {
2873                w.write_se(mvdx);
2874                w.write_se(mvdy);
2875            }
2876            write_cbp_inter(w, cbp);
2877            if cbp != 0 {
2878                w.write_se(self.qp_delta()); // mb_qp_delta (AQ per-MB QPy)
2879            }
2880            self.nnz_cache_load(mb_x, mb_y);
2881            drop(_g_syn);
2882
2883            // ---- CAVLC: scan straight from the i16 levels for coded blocks ----
2884            let _g_scan = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Scatter);
2885            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
2886                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
2887                let total = if cbp_luma & (1 << (blk / 4)) != 0 {
2888                    let nc = self.nc_pred(lbx, lby);
2889                    let scan16 = scan_4x4_dcac_i16(&dct[blk * 16..blk * 16 + 16]);
2890                    encode_residual_block(w, &scan16, 16, nc) as u8
2891                } else {
2892                    0
2893                };
2894                self.nnz_cache_set(lbx, lby, total);
2895                self.nnz_y[by * w4 + bx] = total;
2896            }
2897            if cbp_chroma != 0 {
2898                for c in 0..2 {
2899                    encode_residual_block(w, &c_dc_levels[c], 4, -1);
2900                }
2901            }
2902            if cbp_chroma == 2 {
2903                self.chroma_cache_load(mb_x, mb_y);
2904                let w2 = self.mb_w * 2;
2905                for c in 0..2 {
2906                    for &(bx, by) in &CHROMA_4X4_SCAN_XY {
2907                        let nc = self.chroma_nc_pred(c, bx, by);
2908                        let ac = scan_4x4_ac(&c_q[c][by * 2 + bx]);
2909                        let total = encode_residual_block(w, &ac, 15, nc) as u8;
2910                        self.chroma_nnz_cache_set(c, bx, by, total);
2911                        self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
2912                    }
2913                }
2914            }
2915            drop(_g_scan);
2916
2917            // ---- reconstruction: dequantize luma straight from the i16 levels ----
2918            let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
2919            #[repr(align(16))]
2920            struct Align16([i16; 64]);
2921            let mut dct_in = Align16([0i16; 64]);
2922            for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
2923                let rec_off = base + qy * self.cw + qx;
2924                if cbp_luma & (1 << qi) == 0 {
2925                    for r in 0..8 {
2926                        let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
2927                        self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
2928                    }
2929                    continue;
2930                }
2931                for k in 0..4 {
2932                    let blk = qi * 4 + k;
2933                    let mut lvl = [0i32; 16];
2934                    for i in 0..16 {
2935                        lvl[i] = dct[blk * 16 + i] as i32;
2936                    }
2937                    let deq = dequantize(&lvl, qp);
2938                    for i in 0..16 {
2939                        dct_in.0[k * 16 + i] = deq[i] as i16;
2940                    }
2941                }
2942                rusty_h264_accel::idct_four_t4_rec(
2943                    &mut self.rec_y[rec_off..],
2944                    self.cw,
2945                    &pred_y[qy * 16 + qx..],
2946                    16,
2947                    &dct_in.0,
2948                );
2949            }
2950            // chroma recon (identical to v1)
2951            for c in 0..2 {
2952                let base_c = (mb_y * 8) * self.ccw + mb_x * 8;
2953                let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
2954                if cbp_chroma == 0 {
2955                    for r in 0..8 {
2956                        let dsti = base_c + r * self.ccw;
2957                        plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
2958                    }
2959                } else {
2960                    #[repr(align(16))]
2961                    struct A([i16; 64]);
2962                    let mut d = A([0i16; 64]);
2963                    for i in 0..4 {
2964                        let deq = dequantize(&c_q[c][i], qpc);
2965                        for j in 0..16 {
2966                            d.0[i * 16 + j] = deq[j] as i16;
2967                        }
2968                        d.0[i * 16] = c_recon_dc[c][i] as i16;
2969                    }
2970                    rusty_h264_accel::idct_four_t4_rec(&mut plane[base_c..], self.ccw, &c_pred[c], 8, &d.0);
2971                }
2972            }
2973            for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
2974                self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
2975            }
2976        }
2977    }
2978
2979    #[allow(clippy::too_many_arguments)]
2980    fn encode_inter_mb_v1(
2981        &mut self,
2982        w: &mut BitWriter,
2983        refs: &[crate::RefFrame],
2984        sy: &[u8],
2985        su: &[u8],
2986        sv: &[u8],
2987        mb_x: usize,
2988        mb_y: usize,
2989        mode: u8,
2990        parts: &[(i32, (i32, i32))],
2991    ) {
2992        self.encode_inter_mb_v1_b(w, refs, sy, su, sv, mb_x, mb_y, mode, parts, None);
2993    }
2994
2995    /// As [`Self::encode_inter_mb_v1`], but `b_mode` selects B-slice framing: the
2996    /// macroblock is coded as `B_L0_16x16` (`mb_type == 1`) instead of the P-slice
2997    /// `mb_type == mode`. Everything else — the single List-0 partition, the median
2998    /// `mvd_l0` predictor, the residual, and the reconstruction — is byte-identical
2999    /// to `P_L0_16x16`, so the caller passes `mode == 0`, `refs == &[L0_anchor]`
3000    /// (length 1 ⇒ no `ref_idx` coded), and `parts == &[(0, mv)]`.
3001    /// Decide + reconstruct one inter macroblock (motion compensation, residual,
3002    /// quantize, reconstruct, commit motion grids) — everything except entropy
3003    /// coding. Returns an [`InterPlan`] coded by either backend, so CAVLC and CABAC
3004    /// share this whole path bit-for-bit (the P/B analogue of [`plan_mb`]).
3005    #[allow(clippy::too_many_arguments)]
3006    fn plan_inter_mb(
3007        &mut self,
3008        refs: &[crate::RefFrame],
3009        sy: &[u8],
3010        su: &[u8],
3011        sv: &[u8],
3012        mb_x: usize,
3013        mb_y: usize,
3014        mode: u8,
3015        parts: &[(i32, (i32, i32))],
3016        bspec: Option<BInter>,
3017    ) -> InterPlan {
3018        // Descent E/F: identify this mc_luma population by call site.
3019        #[cfg(feature = "profile")]
3020        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
3021        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
3022        let (qp, qpc) = (self.qp, self.qpc);
3023        let w4 = self.mb_w * 4;
3024        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3025
3026        // ---- per-partition motion compensation + MV prediction ----
3027        let mut pred_y = [0u8; 256];
3028        let mut c_pred = [[0u8; 64]; 2];
3029        let mut mvds = [(0i32, 0i32); 4]; // ≤4 partitions; no per-MB Vec alloc
3030        let mut plan_refs = [0i32; 4]; // per-partition ref_idx_l0 (0 for B / 1-ref)
3031        let mut n_mvd = 0;
3032        let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
3033        if let Some(b) = bspec.filter(|b| b.dir == 0) {
3034            // ---- B_Direct_16x16 (mb_type 0): spatial-direct prediction, no mvd ----
3035            let (dp, dc, motion) = self.b_direct(&refs[0], b.l1, mb_x, mb_y);
3036            pred_y = dp;
3037            c_pred = dc;
3038            self.commit_direct_motion(mb_x, mb_y, &motion);
3039        } else if let Some(b) = bspec {
3040            // ---- B 16×16 prediction: List-0 / List-1 / Bi ----
3041            let use0 = b.dir == 1 || b.dir == 3;
3042            let use1 = b.dir == 2 || b.dir == 3;
3043            let (lx, ly) = (mb_x * 16, mb_y * 16);
3044            let (cx, cy) = (mb_x * 8, mb_y * 8);
3045            let (pbx, pby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
3046            // Per-list `mvd` against the median predictor over that list's neighbors.
3047            if use0 {
3048                let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 0);
3049                let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3050                mvds[n_mvd] = (b.mv0.0 - p.0, b.mv0.1 - p.1);
3051                n_mvd += 1;
3052            }
3053            if use1 {
3054                let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 1);
3055                let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3056                mvds[n_mvd] = (b.mv1.0 - p.0, b.mv1.1 - p.1);
3057                n_mvd += 1;
3058            }
3059            // Motion compensation. L0/L1 write straight into pred; Bi averages
3060            // (p+q+1)>>1 — the decoder's `b_mc` blend with weighted_bipred_idc=0.
3061            let mut a_y = [0u8; 256];
3062            let mut b_y = [0u8; 256];
3063            let mut a_c = [[0u8; 64]; 2];
3064            let mut b_c = [[0u8; 64]; 2];
3065            if use0 {
3066                mc_luma(&refs[0].y, self.cw, ch, lx, ly, 16, 16, b.mv0.0, b.mv0.1, &mut a_y);
3067                mc_chroma(&refs[0].u, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[0]);
3068                mc_chroma(&refs[0].v, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[1]);
3069            }
3070            if use1 {
3071                mc_luma(&b.l1.y, self.cw, ch, lx, ly, 16, 16, b.mv1.0, b.mv1.1, &mut b_y);
3072                mc_chroma(&b.l1.u, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[0]);
3073                mc_chroma(&b.l1.v, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[1]);
3074            }
3075            match (use0, use1) {
3076                (true, true) => {
3077                    for i in 0..256 {
3078                        pred_y[i] = bi_blend(a_y[i] as i32, b_y[i] as i32, self.bi_w);
3079                    }
3080                    for c in 0..2 {
3081                        for i in 0..64 {
3082                            c_pred[c][i] = bi_blend(a_c[c][i] as i32, b_c[c][i] as i32, self.bi_w);
3083                        }
3084                    }
3085                }
3086                (true, false) => {
3087                    pred_y = a_y;
3088                    c_pred = a_c;
3089                }
3090                _ => {
3091                    pred_y = b_y;
3092                    c_pred = b_c;
3093                }
3094            }
3095            // Commit per-list motion so later MBs' per-list predictors see it.
3096            for by in 0..4 {
3097                for bx in 0..4 {
3098                    let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3099                    self.inter_y[idx] = true;
3100                    self.coded_y[idx] = true;
3101                    self.mv_y[idx] = if use0 { b.mv0 } else { (0, 0) };
3102                    self.ref_idx_y[idx] = if use0 { 0 } else { -1 };
3103                    self.mv1_y[idx] = if use1 { b.mv1 } else { (0, 0) };
3104                    self.ref_idx1_y[idx] = if use1 { 0 } else { -1 };
3105                }
3106            }
3107        } else {
3108        for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
3109            let (refi, mv) = parts[part];
3110            plan_refs[part] = refi; // per-partition ref_idx_l0 → carried to the CABAC emit
3111            let reference = &refs[refi as usize];
3112            let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3113            let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
3114            let pmv = predict_partition_mv(mode, part, a, b, c, refi);
3115            mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
3116            n_mvd += 1;
3117            // Commit this partition's motion so later partitions can predict from it.
3118            for by in ry / 4..ry / 4 + rh / 4 {
3119                for bx in rx / 4..rx / 4 + rw / 4 {
3120                    let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3121                    self.mv_y[idx] = mv;
3122                    self.inter_y[idx] = true;
3123                    self.ref_idx_y[idx] = refi;
3124                    self.coded_y[idx] = true;
3125                }
3126            }
3127            // Luma MC into the partition's sub-region. A full-MB (16×16) partition is
3128            // the whole `pred_y`, so MC straight into it — no scratch + repack copy.
3129            if rw == 16 && rh == 16 {
3130                self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3131            } else {
3132                let mut tmp = [0u8; 256];
3133                self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
3134                for dy in 0..rh {
3135                    for dx in 0..rw {
3136                        pred_y[(ry + dy) * 16 + (rx + dx)] = tmp[dy * rw + dx];
3137                    }
3138                }
3139            }
3140            // Chroma MC (half-resolution region); 8×8 = the whole plane prediction.
3141            let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
3142            for cc in 0..2 {
3143                let rc = if cc == 0 { &reference.u } else { &reference.v };
3144                if crw == 8 && crh == 8 {
3145                    mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
3146                } else {
3147                    let mut tc = [0u8; 64];
3148                    mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
3149                    for dy in 0..crh {
3150                        for dx in 0..crw {
3151                            c_pred[cc][(cry + dy) * 8 + (crx + dx)] = tc[dy * crw + dx];
3152                        }
3153                    }
3154                }
3155            }
3156        }
3157        } // end P per-partition formation (else of the B branch)
3158
3159        // ---- luma residual + quantization ----
3160        let mut q_blocks = [[0i32; 16]; 16]; // raster, levels
3161        let mut cbp_luma = 0u32;
3162        // Inter 8x8-transform candidate (High profile, scalar path). Filled by the
3163        // per-MB 4x4-vs-8x8 RD below; false/zero means the 4x4 residual is used.
3164        #[allow(unused_mut)]
3165        let mut t8x8 = false;
3166        #[allow(unused_mut)]
3167        let mut q8 = [[0i32; 64]; 4];
3168        drop(_g_mc);
3169        let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
3170        #[cfg(accel)]
3171        {
3172            // openh264 `WelsDctFourT4_sse2` (fused residual+DCT) → i16, then
3173            // `WelsQuantFour4x4_sse2` in place — the whole DCT→quant chain stays in i16,
3174            // no i32 round-trip. Quant is openh264's structure carrying OUR deadzone
3175            // (`quant_dz_ff` + `QUANT_MF_OH`), so levels are bit-identical to `quantize`.
3176            let mut dctw = AlignedDct([0i16; 256]);
3177            let dct = &mut dctw.0;
3178            let base = mb_y * 16 * self.cw + mb_x * 16;
3179            for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3180                rusty_h264_accel::dct_four_t4(
3181                    &mut dct[qi * 64..qi * 64 + 64],
3182                    &sy[base + qy * self.cw + qx..],
3183                    self.cw,
3184                    &pred_y[qy * 16 + qx..],
3185                    16,
3186                );
3187            }
3188            let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3189            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3190            for qi in 0..4 {
3191                rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
3192            }
3193            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3194                let mut nz = false;
3195                for i in 0..16 {
3196                    let v = dct[blk * 16 + i] as i32;
3197                    q_blocks[lby * 4 + lbx][i] = v;
3198                    nz |= v != 0;
3199                }
3200                if nz {
3201                    cbp_luma |= 1 << (blk / 4);
3202                }
3203            }
3204        }
3205        #[cfg(not(accel))]
3206        {
3207            // Scalar/`wide`: gather all 16 residual blocks, batched forward-DCT, quantize.
3208            let mut res_blocks = [[0i32; 16]; 16]; // raster
3209            for lby in 0..4 {
3210                for lbx in 0..4 {
3211                    let b = &mut res_blocks[lby * 4 + lbx];
3212                    for dy in 0..4 {
3213                        for dx in 0..4 {
3214                            let sx = mb_x * 16 + lbx * 4 + dx;
3215                            let syy = mb_y * 16 + lby * 4 + dy;
3216                            b[dy * 4 + dx] = sy[syy * self.cw + sx] as i32
3217                                - pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3218                        }
3219                    }
3220                }
3221            }
3222            let mut coeffs = [[0i32; 16]; 16];
3223            forward_dct_blocks(&res_blocks, &mut coeffs);
3224            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3225                let q = rdoq(&coeffs[lby * 4 + lbx], qp, 6, self.rdoq_strength, 0);
3226                if q.iter().any(|&v| v != 0) {
3227                    cbp_luma |= 1 << (blk / 4);
3228                }
3229                q_blocks[lby * 4 + lbx] = q;
3230            }
3231        }
3232
3233        // Per-MB transform-size RD (runs in scalar AND accel builds — q_blocks +
3234        // cbp_luma are filled by whichever quant path ran; the 8x8 candidate + its
3235        // recon are pure Rust). One 8x8 DCT per 8x8 block vs four 4x4s. Every inter
3236        // partition here is >= 8x8, so transform_size_8x8_flag is always allowed.
3237        // Content-adaptive by construction — the winner is chosen per MB.
3238        {
3239            if self.transform_8x8 && self.inter8x8 != 0 {
3240                let lambda =
3241                    0.85 * self.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
3242                let mut ssd4 = 0i64;
3243                let mut rate4 = 0f64;
3244                for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3245                    let mut predb = [0i32; 16];
3246                    for dy in 0..4 {
3247                        for dx in 0..4 {
3248                            predb[dy * 4 + dx] =
3249                                pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3250                        }
3251                    }
3252                    let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3253                    let s = reconstruct_4x4(&deq, &predb);
3254                    for dy in 0..4 {
3255                        for dx in 0..4 {
3256                            let sx = mb_x * 16 + lbx * 4 + dx;
3257                            let syy = mb_y * 16 + lby * 4 + dy;
3258                            let d = s[dy * 4 + dx] as i64 - sy[syy * self.cw + sx] as i64;
3259                            ssd4 += d * d;
3260                        }
3261                    }
3262                    for &l in &q_blocks[lby * 4 + lbx] {
3263                        if l != 0 {
3264                            rate4 += rdoq_rate((l as i64).abs());
3265                        }
3266                    }
3267                }
3268                let (q8c, cbp8, rate8, _rec8, ssd8) =
3269                    plan_inter8_luma(sy, self.cw, mb_x, mb_y, &pred_y, qp);
3270                // Both candidates priced with the SAME level-aware rate (Σ rdoq_rate);
3271                // `inter8_pen` is an optional extra bias (default 0) on the 8x8 flag.
3272                let j4 = ssd4 as f64 + lambda * (rate4 + 16.0);
3273                let j8 = ssd8 as f64 + lambda * (rate8 + 16.0 + self.inter8_pen as f64);
3274                if cbp8 > 0 && j8 < j4 {
3275                    t8x8 = true;
3276                    cbp_luma = cbp8;
3277                    q8 = q8c;
3278                }
3279            }
3280        }
3281
3282        // ---- chroma residual (prediction already built per partition) ----
3283        let mut c_dc_levels = [[0i32; 4]; 2];
3284        let mut c_recon_dc = [[0i32; 4]; 2];
3285        let mut c_q = [[[0i32; 16]; 4]; 2];
3286        let (mut any_ac, mut any_dc) = (false, false);
3287        for c in 0..2 {
3288            let src = if c == 0 { su } else { sv };
3289            // Fast path: one dct_four_t4 covers the whole 8x8 chroma region (all 4
3290            // blocks, residual+DCT fused straight from the planes); block b's pre-quant
3291            // DC is dct[b*16] (quad z-scan == 2x2 raster); quant_four_4x4 with our
3292            // FF/MF is bit-identical to scalar `quantize`. Same pairing the P_Skip
3293            // free-check proved byte-identical over the corpus.
3294            #[cfg(accel)]
3295            let (mut dc2x2, applied) = {
3296                #[repr(align(16))]
3297                struct A([i16; 64]);
3298                let mut dct = A([0i16; 64]);
3299                rusty_h264_accel::dct_four_t4(
3300                    &mut dct.0,
3301                    &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3302                    self.ccw,
3303                    &c_pred[c],
3304                    8,
3305                );
3306                let dc = [
3307                    dct.0[0] as i32,
3308                    dct.0[16] as i32,
3309                    dct.0[32] as i32,
3310                    dct.0[48] as i32,
3311                ];
3312                let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3313                let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3314                rusty_h264_accel::quant_four_4x4(&mut dct.0, &ffc, mfc);
3315                for i in 0..4 {
3316                    let q = &mut c_q[c][i];
3317                    q[0] = 0;
3318                    for j in 1..16 {
3319                        let v = dct.0[i * 16 + j] as i32;
3320                        q[j] = v;
3321                        if v != 0 {
3322                            any_ac = true;
3323                        }
3324                    }
3325                }
3326                (dc, true)
3327            };
3328            #[cfg(not(accel))]
3329            let (mut dc2x2, applied) = ([0i32; 4], false);
3330            if !applied {
3331                // Scalar/`wide` twin: gather, batch forward DCT, quantize per block.
3332                let mut res_blocks = [[0i32; 16]; 4];
3333                for by in 0..2 {
3334                    for bx in 0..2 {
3335                        let b = &mut res_blocks[by * 2 + bx];
3336                        for dy in 0..4 {
3337                            for dx in 0..4 {
3338                                let sx = mb_x * 8 + bx * 4 + dx;
3339                                let syy = mb_y * 8 + by * 4 + dy;
3340                                b[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
3341                                    - c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3342                            }
3343                        }
3344                    }
3345                }
3346                let mut coeffs = [[0i32; 16]; 4];
3347                forward_dct_blocks(&res_blocks, &mut coeffs);
3348                for i in 0..4 {
3349                    dc2x2[i] = coeffs[i][0];
3350                    let mut q = rdoq(&coeffs[i], qpc, 6, self.rdoq_strength, 1);
3351                    q[0] = 0;
3352                    if q[1..].iter().any(|&v| v != 0) {
3353                        any_ac = true;
3354                    }
3355                    c_q[c][i] = q;
3356                }
3357            }
3358            let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
3359            if dl.iter().any(|&v| v != 0) {
3360                any_dc = true;
3361            }
3362            c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
3363            c_dc_levels[c] = dl;
3364        }
3365        let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
3366        let cbp = cbp_luma | (cbp_chroma << 4);
3367
3368        drop(_g_tq);
3369        let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
3370        // ---- reconstruction (luma) ----
3371        #[cfg(accel)]
3372        if t8x8 {
3373            // 8x8-transform recon is pure Rust (no asm 8x8 kernels yet); inverse of
3374            // the decoder's t8x8 inter path. Same code as the scalar branch below.
3375            let weight = [16i32; 64];
3376            for b8 in 0..4usize {
3377                let (b8x, b8y) = (b8 % 2, b8 / 2);
3378                let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3379                let predb: [i32; 64] = std::array::from_fn(|i| {
3380                    pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3381                });
3382                let recon = add_residual_8x8(&res_r, &predb);
3383                for dy in 0..8 {
3384                    for dx in 0..8 {
3385                        let px = mb_x * 16 + b8x * 8 + dx;
3386                        let py = mb_y * 16 + b8y * 8 + dy;
3387                        self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3388                    }
3389                }
3390            }
3391        } else {
3392            // Dequantize all 16 blocks into the 4-quadrant int16 layout (16-byte
3393            // aligned — the kernel uses movdqa coeff loads), then inverse-DCT + add
3394            // prediction + clip per quadrant via openh264. The inverse butterfly +
3395            // (x+32)>>6 is bit-identical to reconstruct_4x4 (verified in accel).
3396            // An 8x8 quad whose cbp bit is clear has ZERO residual: reconstruction
3397            // IS the prediction (the decoder's own uncoded-region fast path) — a row
3398            // copy replaces dequant + convert + idct for that quad. Byte-identical:
3399            // idct of an all-zero block adds (0+32)>>6 = 0 to pred, clip is identity.
3400            #[repr(align(16))]
3401            struct Align16([i16; 64]);
3402            let mut dct_in = Align16([0i16; 64]);
3403            let base = mb_y * 16 * self.cw + mb_x * 16;
3404            for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3405                let rec_off = base + qy * self.cw + qx;
3406                if cbp_luma & (1 << qi) == 0 {
3407                    for r in 0..8 {
3408                        let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
3409                        self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
3410                    }
3411                    continue;
3412                }
3413                for k in 0..4 {
3414                    let blk = qi * 4 + k;
3415                    let (lbx, lby) = LUMA_4X4_SCAN_XY[blk];
3416                    let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3417                    for i in 0..16 {
3418                        dct_in.0[k * 16 + i] = deq[i] as i16;
3419                    }
3420                }
3421                rusty_h264_accel::idct_four_t4_rec(
3422                    &mut self.rec_y[rec_off..],
3423                    self.cw,
3424                    &pred_y[qy * 16 + qx..],
3425                    16,
3426                    &dct_in.0,
3427                );
3428            }
3429        }
3430        #[cfg(not(accel))]
3431        if t8x8 {
3432            // 8x8-transform reconstruction (inverse of the decoder's t8x8 inter path).
3433            let weight = [16i32; 64];
3434            for b8 in 0..4usize {
3435                let (b8x, b8y) = (b8 % 2, b8 / 2);
3436                let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3437                let predb: [i32; 64] = std::array::from_fn(|i| {
3438                    pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3439                });
3440                let recon = add_residual_8x8(&res_r, &predb);
3441                for dy in 0..8 {
3442                    for dx in 0..8 {
3443                        let px = mb_x * 16 + b8x * 8 + dx;
3444                        let py = mb_y * 16 + b8y * 8 + dy;
3445                        self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3446                    }
3447                }
3448            }
3449        } else {
3450            for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3451                let mut predb = [0i32; 16];
3452                for dy in 0..4 {
3453                    for dx in 0..4 {
3454                        predb[dy * 4 + dx] = pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3455                    }
3456                }
3457                let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3458                let s = reconstruct_4x4(&deq, &predb);
3459                store(&mut self.rec_y, self.cw, mb_x * 16 + lbx * 4, mb_y * 16 + lby * 4, &s);
3460            }
3461        }
3462        for c in 0..2 {
3463            // Fast path: dequantize into the quad i16 layout (raster == the kernel's
3464            // z-order for a 2x2) with the Hadamard DC injected, then ONE
3465            // idct+add-pred+clip kernel writes the 8x8 straight into the plane —
3466            // bit-identical to the scalar tail below (verified kernel pairing).
3467            #[cfg(accel)]
3468            {
3469                let base = (mb_y * 8) * self.ccw + mb_x * 8;
3470                let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3471                if cbp_chroma == 0 {
3472                    // No chroma residual at all: recon = prediction (row copies).
3473                    for r in 0..8 {
3474                        let dsti = base + r * self.ccw;
3475                        plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
3476                    }
3477                } else {
3478                    #[repr(align(16))]
3479                    struct A([i16; 64]);
3480                    let mut d = A([0i16; 64]);
3481                    for i in 0..4 {
3482                        let deq = dequantize(&c_q[c][i], qpc);
3483                        for j in 0..16 {
3484                            d.0[i * 16 + j] = deq[j] as i16;
3485                        }
3486                        d.0[i * 16] = c_recon_dc[c][i] as i16;
3487                    }
3488                    rusty_h264_accel::idct_four_t4_rec(&mut plane[base..], self.ccw, &c_pred[c], 8, &d.0);
3489                }
3490            }
3491            #[cfg(not(accel))]
3492            {
3493                // Dequantize the 4 blocks (raster, DC overridden by the 2×2-Hadamard
3494                // recon), then batch the inverse DCT and share the add+clip tail.
3495                let mut deq_blocks = [[0i32; 16]; 4];
3496                for i in 0..4 {
3497                    deq_blocks[i] = dequantize(&c_q[c][i], qpc);
3498                    deq_blocks[i][0] = c_recon_dc[c][i];
3499                }
3500                let mut res = [[0i32; 16]; 4];
3501                inverse_dct_blocks(&deq_blocks, &mut res);
3502                let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3503                for by in 0..2 {
3504                    for bx in 0..2 {
3505                        let mut predb = [0i32; 16];
3506                        for dy in 0..4 {
3507                            for dx in 0..4 {
3508                                predb[dy * 4 + dx] = c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3509                            }
3510                        }
3511                        let s = add_residual_4x4(&res[by * 2 + bx], &predb);
3512                        store(plane, self.ccw, mb_x * 8 + bx * 4, mb_y * 8 + by * 4, &s);
3513                    }
3514                }
3515            }
3516        }
3517        // MV grid + coded flags were set per partition; mark modes as DC.
3518        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3519            self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
3520        }
3521        InterPlan { mvds, plan_refs, n_mvd, cbp, q_blocks, c_dc_levels, c_q, t8x8, q8 }
3522    }
3523
3524    /// Code one planned inter macroblock as CAVLC (the original `encode_inter_mb_v1_b`
3525    /// tail). `plan_inter_mb` already committed the reconstruction + motion grids.
3526    #[allow(clippy::too_many_arguments)]
3527    fn encode_inter_mb_v1_b(
3528        &mut self,
3529        w: &mut BitWriter,
3530        refs: &[crate::RefFrame],
3531        sy: &[u8],
3532        su: &[u8],
3533        sv: &[u8],
3534        mb_x: usize,
3535        mb_y: usize,
3536        mode: u8,
3537        parts: &[(i32, (i32, i32))],
3538        bspec: Option<BInter>,
3539    ) {
3540        let plan = self.plan_inter_mb(refs, sy, su, sv, mb_x, mb_y, mode, parts, bspec);
3541        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
3542        self.emit_inter_cavlc(w, refs.len(), mb_x, mb_y, mode, parts, bspec, &plan);
3543    }
3544
3545    /// CAVLC entropy coding for a planned inter macroblock.
3546    #[allow(clippy::too_many_arguments)]
3547    fn emit_inter_cavlc(
3548        &mut self,
3549        w: &mut BitWriter,
3550        num_refs: usize,
3551        mb_x: usize,
3552        mb_y: usize,
3553        mode: u8,
3554        parts: &[(i32, (i32, i32))],
3555        bspec: Option<BInter>,
3556        plan: &InterPlan,
3557    ) {
3558        let w4 = self.mb_w * 4;
3559        let (cbp, cbp_luma, cbp_chroma) = (plan.cbp, plan.cbp & 15, plan.cbp >> 4);
3560        // mb_pred order (spec 7.3.5.1): mb_type, then all ref_idx_l0, then all mvd_l0.
3561        // B-slice mb_type = the B direction 1/2/3; P-slice uses `mode`. ref_idx coded
3562        // only when >1 reference is active.
3563        w.write_ue(bspec.map_or(mode as u32, |b| b.dir as u32)); // inter mb_type
3564        // P_8x8 (mb_type 3): sub_mb_type per 8×8 (spec 7.3.5.2, before ref_idx/mvd).
3565        // 0 = P_L0_8x8 (one MV) — the only shape emitted for now.
3566        if mode == 3 {
3567            for _ in 0..4 {
3568                w.write_ue(0);
3569            }
3570        }
3571        if num_refs > 1 {
3572            for &(refi, _) in parts {
3573                write_ref_idx(w, refi, num_refs);
3574            }
3575        }
3576        for &(mvdx, mvdy) in &plan.mvds[..plan.n_mvd] {
3577            w.write_se(mvdx);
3578            w.write_se(mvdy);
3579        }
3580        write_cbp_inter(w, cbp);
3581        // transform_size_8x8_flag: after cbp, before mb_qp_delta, present only when
3582        // luma has coefficients and the 8x8 transform is enabled. Every inter partition
3583        // here is >= 8x8, so the spec's allow_8x8 (all partitions >= 8x8) always holds.
3584        if cbp_luma > 0 && self.transform_8x8 {
3585            w.write_bit(plan.t8x8);
3586        }
3587        if cbp != 0 {
3588            w.write_se(self.qp_delta()); // mb_qp_delta (AQ per-MB QPy)
3589        }
3590        self.nnz_cache_load(mb_x, mb_y);
3591        if plan.t8x8 {
3592            // 8x8 residual: four interleaved 4x4 CAVLC sub-blocks per 8x8 block
3593            // (coeff k of sub s -> 8x8 scan position 4k+s), the inverse of the
3594            // decoder's t8x8 inter luma read. nnz set per 4x4 sub-block.
3595            for b8 in 0..4usize {
3596                let (b8x, b8y) = (b8 % 2, b8 / 2);
3597                let scan8 = scan_8x8_fwd(&plan.q8[b8]);
3598                for sub in 0..4usize {
3599                    let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
3600                    let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
3601                    let total = if cbp_luma & (1 << b8) != 0 {
3602                        let nc = self.nc_pred(cx, cy);
3603                        let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
3604                        encode_residual_block(w, &blk, 16, nc) as u8
3605                    } else {
3606                        0
3607                    };
3608                    self.nnz_cache_set(cx, cy, total);
3609                    self.nnz_y[by * w4 + bx] = total;
3610                }
3611            }
3612        } else {
3613            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3614                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
3615                let total = if cbp_luma & (1 << (blk / 4)) != 0 {
3616                    let nc = self.nc_pred(lbx, lby);
3617                    let scan16 = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
3618                    encode_residual_block(w, &scan16, 16, nc) as u8
3619                } else {
3620                    0
3621                };
3622                self.nnz_cache_set(lbx, lby, total);
3623                self.nnz_y[by * w4 + bx] = total;
3624            }
3625        }
3626        if cbp_chroma != 0 {
3627            for c in 0..2 {
3628                encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
3629            }
3630        }
3631        if cbp_chroma == 2 {
3632            self.chroma_cache_load(mb_x, mb_y);
3633            let w2 = self.mb_w * 2;
3634            for c in 0..2 {
3635                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
3636                    let nc = self.chroma_nc_pred(c, bx, by);
3637                    let ac = scan_4x4_ac(&plan.c_q[c][by * 2 + bx]);
3638                    let total = encode_residual_block(w, &ac, 15, nc) as u8;
3639                    self.chroma_nnz_cache_set(c, bx, by, total);
3640                    self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
3641                }
3642            }
3643        }
3644    }
3645
3646    /// Descent F: reconstruction / skip-check MC through the cached half-pel planes
3647    /// instead of the per-pixel 6-tap. `hpel_block` is proven bit-identical to `mc_luma`
3648    /// (`hpel_block_matches_mc_luma_exactly`) and the `f` plane is the padded,
3649    /// edge-replicated reference, so both paths are BYTE-IDENTICAL; anything outside the
3650    /// padded plane still falls back to `mc_luma`.
3651    ///
3652    /// Census that motivated it: with the search's edge fallback fixed, `mc_luma` is
3653    /// 3.8-5.2% of encode and splits recon ~56-67% / skip-check ~24-35%, the latter at a
3654    /// content-independent one call per macroblock.
3655    #[inline]
3656    fn mc_luma_cached(
3657        &self,
3658        reference: &crate::RefFrame,
3659        x0: usize,
3660        y0: usize,
3661        bw: usize,
3662        bh: usize,
3663        mvx: i32,
3664        mvy: i32,
3665        out: &mut [u8],
3666    ) {
3667        let ch = self.mb_h * 16;
3668        let cw = self.cw;
3669        if !self.fast {
3670            let p = reference.hpel(cw, ch);
3671            if rusty_h264_common::inter::hpel_block(p, x0, y0, bw, bh, mvx, mvy, out) {
3672                return;
3673            }
3674            if let Some((plane, base, stride)) =
3675                rusty_h264_common::inter::hpel_ref(p, x0, y0, bw, bh, mvx, mvy)
3676            {
3677                for r in 0..bh {
3678                    out[r * bw..r * bw + bw].copy_from_slice(&plane[base + r * stride..][..bw]);
3679                }
3680                return;
3681            }
3682        }
3683        mc_luma(&reference.y, cw, ch, x0, y0, bw, bh, mvx, mvy, out);
3684    }
3685
3686    /// Motion-compensates the `P_Skip` prediction (luma + both chroma) from
3687    /// reference 0 at the skip MV.
3688    /// Luma half of the P_Skip prediction. Split out so the fast path can test the
3689    /// luma residual first and only motion-compensate chroma when luma is free —
3690    /// for the majority of (non-free) macroblocks the chroma MC is never needed.
3691    fn skip_predict_luma(
3692        &self,
3693        refs: &[crate::RefFrame],
3694        mb_x: usize,
3695        mb_y: usize,
3696        mv: (i32, i32),
3697    ) -> [u8; 256] {
3698        // Descent E/F: identify this mc_luma population by call site.
3699        #[cfg(feature = "profile")]
3700        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(3);
3701        let reference = &refs[0]; // P_Skip always references index 0
3702        let ch = self.mb_h * 16;
3703        let mut pred_y = [0u8; 256];
3704        self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3705        pred_y
3706    }
3707
3708    /// Chroma half of the P_Skip prediction (see [`Self::skip_predict_luma`]).
3709    fn skip_predict_chroma(
3710        &self,
3711        refs: &[crate::RefFrame],
3712        mb_x: usize,
3713        mb_y: usize,
3714        mv: (i32, i32),
3715    ) -> [[u8; 64]; 2] {
3716        let reference = &refs[0];
3717        let cch = self.mb_h * 8;
3718        let mut pred_c = [[0u8; 64]; 2];
3719        for c in 0..2 {
3720            let rc = if c == 0 { &reference.u } else { &reference.v };
3721            mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut pred_c[c]);
3722        }
3723        pred_c
3724    }
3725
3726    /// Whether the luma half of the P_Skip prediction has an all-zero quantized
3727    /// residual. Tested first and independently so the caller can defer the chroma
3728    /// MC + test for the common case where luma already disqualifies the skip (a
3729    /// "free", exact P_Skip costs no bits and is strictly beneficial).
3730    fn skip_luma_is_free(&self, sy: &[u8], mb_x: usize, mb_y: usize, pred_y: &[u8; 256]) -> bool {
3731        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
3732        let qp = self.qp;
3733        // Fast path (deployment): the SAME asm kernels the coding path uses —
3734        // `dct_four_t4` computes the 4x4 DCTs of (src - pred) for an 8x8 quad
3735        // STRAIGHT FROM THE PLANES (no scalar gather), `quant_four_4x4` quantizes
3736        // with the identical FF/MF math as scalar `quantize` (bit-identical), and
3737        // "free" = all 64 levels zero, which is order-independent. Per-quad early
3738        // exit. The knob interleaves this against the scalar twin for A/B.
3739        #[cfg(accel)]
3740        if self.skip_accel_check {
3741            #[repr(align(16))]
3742            struct Align16([i16; 64]);
3743            let mut dct = Align16([0i16; 64]);
3744            let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3745            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3746            for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
3747                rusty_h264_accel::dct_four_t4(
3748                    &mut dct.0,
3749                    &sy[(mb_y * 16 + qy) * self.cw + mb_x * 16 + qx..],
3750                    self.cw,
3751                    &pred_y[qy * 16 + qx..],
3752                    16,
3753                );
3754                rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
3755                if dct.0.iter().any(|&v| v != 0) {
3756                    return false;
3757                }
3758            }
3759            return true;
3760        }
3761        // Exact quantize-to-zero bounds (mirrors `quantize`: level != 0 iff
3762        // (|c| + ff[p])·mf_oh[p] >= 2^16). With |C_ij| <= 4·SAD (max |H| entry = 2)
3763        // and C_DC = Σres, most blocks are decided by one SAD/sum pass — the full
3764        // scalar DCT+quant proof only runs for the rare undecided middle band.
3765        // BIT-EXACT: both shortcuts are sufficient conditions of the exact check.
3766        let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3767        let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3768        let mut t_min = i32::MAX;
3769        for p in 0..8 {
3770            let t = (65536 + mf[p] as i32 - 1) / mf[p] as i32 - ff[p] as i32;
3771            t_min = t_min.min(t);
3772        }
3773        let t_dc = (65536 + mf[0] as i32 - 1) / mf[0] as i32 - ff[0] as i32;
3774        // Whole-MB gate: SAD(any 4x4) <= SAD(MB), so 4*SAD_MB < T_min proves all 16
3775        // blocks quantize to zero from ONE (psadbw) SAD. On skip-heavy content most
3776        // free MBs are exact/near-exact copies (SAD_MB ~ 0) - they skip the whole
3777        // per-block walk. Not-free MBs pay one extra SAD (~2% of their check).
3778        for by in 0..4 {
3779            for bx in 0..4 {
3780                let mut res = [0i32; 16];
3781                let (mut sad, mut dc) = (0i32, 0i32);
3782                for dy in 0..4 {
3783                    for dx in 0..4 {
3784                        let sx = mb_x * 16 + bx * 4 + dx;
3785                        let syy = mb_y * 16 + by * 4 + dy;
3786                        let d = sy[syy * self.cw + sx] as i32
3787                            - pred_y[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
3788                        res[dy * 4 + dx] = d;
3789                        sad += d.abs();
3790                        dc += d;
3791                    }
3792                }
3793                if 4 * sad < t_min {
3794                    continue; // every |C| <= 4·SAD < T_min → all levels zero
3795                }
3796                if dc.abs() >= t_dc {
3797                    return false; // DC level provably nonzero
3798                }
3799                if quantize(&forward_core(&res), qp, 6).iter().any(|&v| v != 0) {
3800                    return false;
3801                }
3802            }
3803        }
3804        true
3805    }
3806
3807    /// Chroma half of [`Self::skip_is_free`].
3808    fn skip_chroma_is_free(
3809        &self,
3810        su: &[u8],
3811        sv: &[u8],
3812        mb_x: usize,
3813        mb_y: usize,
3814        pred_c: &[[u8; 64]; 2],
3815    ) -> bool {
3816        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
3817        let qpc = self.qpc;
3818        // Fast path: one dct_four_t4 covers the whole 8x8 chroma plane region (all 4
3819        // blocks, residual+DCT fused, no scalar gather). Block order is the quad's
3820        // z-scan == raster for 2x2, so block b's DC (pre-quant) sits at dct[b*16] —
3821        // exactly the dc2x2 the Hadamard check needs. quant_four_4x4 with our FF/MF
3822        // is bit-identical to scalar `quantize`; AC-free = positions 1..16 all zero.
3823        #[cfg(accel)]
3824        if self.skip_accel_check {
3825            #[repr(align(16))]
3826            struct Align16C([i16; 64]);
3827            let mut dct = Align16C([0i16; 64]);
3828            let ff = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3829            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3830            for c in 0..2 {
3831                let src = if c == 0 { su } else { sv };
3832                rusty_h264_accel::dct_four_t4(
3833                    &mut dct.0,
3834                    &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3835                    self.ccw,
3836                    &pred_c[c],
3837                    8,
3838                );
3839                let dc2x2 = [
3840                    dct.0[0] as i32,
3841                    dct.0[16] as i32,
3842                    dct.0[32] as i32,
3843                    dct.0[48] as i32,
3844                ];
3845                rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
3846                for b in 0..4 {
3847                    if dct.0[b * 16 + 1..b * 16 + 16].iter().any(|&v| v != 0) {
3848                        return false;
3849                    }
3850                }
3851                if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
3852                    return false;
3853                }
3854            }
3855            return true;
3856        }
3857        for c in 0..2 {
3858            let src = if c == 0 { su } else { sv };
3859            let mut dc2x2 = [0i32; 4];
3860            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
3861                let mut res = [0i32; 16];
3862                for dy in 0..4 {
3863                    for dx in 0..4 {
3864                        let sx = mb_x * 8 + bx * 4 + dx;
3865                        let syy = mb_y * 8 + by * 4 + dy;
3866                        res[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
3867                            - pred_c[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3868                    }
3869                }
3870                let coeffs = forward_core(&res);
3871                dc2x2[by * 2 + bx] = coeffs[0];
3872                if quantize(&coeffs, qpc, 6)[1..].iter().any(|&v| v != 0) {
3873                    return false;
3874                }
3875            }
3876            if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
3877                return false;
3878            }
3879        }
3880        true
3881    }
3882
3883    /// SSD between the source and a macroblock prediction (luma + chroma).
3884    #[allow(clippy::too_many_arguments)]
3885    fn pred_ssd(
3886        &self,
3887        sy: &[u8],
3888        su: &[u8],
3889        sv: &[u8],
3890        mb_x: usize,
3891        mb_y: usize,
3892        pred_y: &[u8; 256],
3893        pred_c: &[[u8; 64]; 2],
3894    ) -> i64 {
3895        let mut ssd = 0i64;
3896        for dy in 0..16 {
3897            for dx in 0..16 {
3898                let d = sy[(mb_y * 16 + dy) * self.cw + mb_x * 16 + dx] as i64
3899                    - pred_y[dy * 16 + dx] as i64;
3900                ssd += d * d;
3901            }
3902        }
3903        for c in 0..2 {
3904            let src = if c == 0 { su } else { sv };
3905            for dy in 0..8 {
3906                for dx in 0..8 {
3907                    let d = src[(mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx] as i64
3908                        - pred_c[c][dy * 8 + dx] as i64;
3909                    ssd += d * d;
3910                }
3911            }
3912        }
3913        ssd
3914    }
3915
3916    /// SSD between the *reconstructed* macroblock and the source.
3917    fn mb_ssd(&self, sy: &[u8], su: &[u8], sv: &[u8], mb_x: usize, mb_y: usize) -> i64 {
3918        let mut ssd = 0i64;
3919        for dy in 0..16 {
3920            for dx in 0..16 {
3921                let i = (mb_y * 16 + dy) * self.cw + mb_x * 16 + dx;
3922                let d = sy[i] as i64 - self.rec_y[i] as i64;
3923                ssd += d * d;
3924            }
3925        }
3926        for c in 0..2 {
3927            let (src, rec) = if c == 0 { (su, &self.rec_u) } else { (sv, &self.rec_v) };
3928            for dy in 0..8 {
3929                for dx in 0..8 {
3930                    let i = (mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx;
3931                    let d = src[i] as i64 - rec[i] as i64;
3932                    ssd += d * d;
3933                }
3934            }
3935        }
3936        ssd
3937    }
3938
3939    /// Reconstructs a `P_Skip` macroblock (reconstruction *is* the prediction —
3940    /// no residual coded) and records its motion state.
3941    #[allow(clippy::too_many_arguments)]
3942    fn commit_skip_probe_marker(&self) {}
3943    fn commit_skip(
3944        &mut self,
3945        mb_x: usize,
3946        mb_y: usize,
3947        mv: (i32, i32),
3948        pred_y: &[u8; 256],
3949        pred_c: &[[u8; 64]; 2],
3950    ) {
3951        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
3952        // Skip recon = the prediction verbatim: straight row copies (byte-identical
3953        // to the old per-4x4 gather + store scatter, ~5x fewer ops).
3954        let base = mb_y * 16 * self.cw + mb_x * 16;
3955        for r in 0..16 {
3956            let d = base + r * self.cw;
3957            self.rec_y[d..d + 16].copy_from_slice(&pred_y[r * 16..r * 16 + 16]);
3958        }
3959        let cbase = mb_y * 8 * self.ccw + mb_x * 8;
3960        for c in 0..2 {
3961            let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3962            for r in 0..8 {
3963                let d = cbase + r * self.ccw;
3964                plane[d..d + 8].copy_from_slice(&pred_c[c][r * 8..r * 8 + 8]);
3965            }
3966        }
3967        self.set_mb_mv(mb_x, mb_y, mv, true, 0);
3968        let w4 = self.mb_w * 4;
3969        for row in 0..4 {
3970            let st = (mb_y * 4 + row) * w4 + mb_x * 4;
3971            self.modes_y[st..st + 4].fill(2);
3972            self.coded_y[st..st + 4].fill(true);
3973        }
3974    }
3975
3976    /// Trial-encodes an inter macroblock to measure its rate-distortion cost
3977    /// `(SSD, bits)` without committing: snapshot the macroblock's grid + recon
3978    /// region, run the real `encode_inter_mb` into a scratch writer, read the
3979    /// bit count and reconstruction SSD, then restore. Neighbor CAVLC context is
3980    /// read (not mutated), so the bit count is accurate.
3981    #[allow(clippy::too_many_arguments)]
3982    fn trial_inter(
3983        &mut self,
3984        refs: &[crate::RefFrame],
3985        sy: &[u8],
3986        su: &[u8],
3987        sv: &[u8],
3988        mb_x: usize,
3989        mb_y: usize,
3990        mode: u8,
3991        parts: &[(i32, (i32, i32))],
3992    ) -> (i64, usize) {
3993        let snap = self.save_mb(mb_x, mb_y);
3994        let mut scratch = BitWriter::new();
3995        self.encode_inter_mb(&mut scratch, refs, sy, su, sv, mb_x, mb_y, mode, parts);
3996        let bits = scratch.bit_len();
3997        let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
3998        self.load_mb(mb_x, mb_y, &snap);
3999        (ssd, bits)
4000    }
4001
4002    /// Trial-encodes the macroblock as **intra** (`encode_mb` runs its own
4003    /// I_16x16-vs-I_4x4 decision), measuring `(SSD, bits)` without committing —
4004    /// the intra candidate for the RD mode decision.
4005    fn trial_intra(
4006        &mut self,
4007        sy: &[u8],
4008        su: &[u8],
4009        sv: &[u8],
4010        mb_x: usize,
4011        mb_y: usize,
4012        is_p: bool,
4013    ) -> (i64, usize) {
4014        let snap = self.save_mb(mb_x, mb_y);
4015        let mut scratch = BitWriter::new();
4016        encode_mb(self, &mut scratch, mb_x, mb_y, sy, su, sv, is_p);
4017        let bits = scratch.bit_len();
4018        let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
4019        self.load_mb(mb_x, mb_y, &snap);
4020        (ssd, bits)
4021    }
4022
4023    /// Best `(ref_idx, mv, cost)` for one partition by `SATD + λ·bits`, searched
4024    /// across every reference (`cost` is that SATD-domain rate-distortion cost).
4025    /// `extra` seeds the search with already-found MVs (e.g. the 16×16 result when
4026    /// refining a sub-partition).
4027    #[allow(clippy::too_many_arguments)]
4028    fn best_part(
4029        &self,
4030        refs: &[crate::RefFrame],
4031        sy: &[u8],
4032        nb: &[MvNeighbor; 3],
4033        num_refs: usize,
4034        rx: usize,
4035        ry: usize,
4036        rw: usize,
4037        rh: usize,
4038        extra: &[(i32, i32)],
4039        lme: f64,
4040    ) -> (i32, (i32, i32), i64) {
4041        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4042        let [a, b, c] = *nb;
4043        let (mut br, mut bmv, mut bc) = (0i32, (0, 0), i64::MAX);
4044        for r in 0..num_refs {
4045            let mut seeds = vec![predict_mv(a, b, c, r as i32)];
4046            seeds.extend_from_slice(extra);
4047            let (mv, cost) = self.motion_search(&refs[r], sy, rx, ry, rw, rh, &seeds, lme, None);
4048            let cost = cost + (lme * ref_bits(r, num_refs) as f64) as i64;
4049            if cost < bc {
4050                bc = cost;
4051                br = r as i32;
4052                bmv = mv;
4053            }
4054        }
4055        (br, bmv, bc)
4056    }
4057
4058    /// Sub-pel-refines ONE already-chosen partition, reusing `motion_search`'s cost
4059    /// closure via its `start` hook so the rate term and predictor centre are exactly
4060    /// the ones the full search used. Companion to `best_part` under `sp_defer`.
4061    #[allow(clippy::too_many_arguments)]
4062    fn refine_part(
4063        &self,
4064        refs: &[crate::RefFrame],
4065        sy: &[u8],
4066        nb: &[MvNeighbor; 3],
4067        num_refs: usize,
4068        rx: usize,
4069        ry: usize,
4070        rw: usize,
4071        rh: usize,
4072        lme: f64,
4073        r: i32,
4074        mv: (i32, i32),
4075    ) -> ((i32, i32), i64) {
4076        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4077        let [a, b, c] = *nb;
4078        let rb = (lme * ref_bits(r as usize, num_refs) as f64) as i64;
4079        let seeds = [predict_mv(a, b, c, r)];
4080        let (m, cc) = self.motion_search(&refs[r as usize], sy, rx, ry, rw, rh, &seeds, lme, Some(mv));
4081        (m, cc + rb)
4082    }
4083
4084    /// Cheapest `I_16x16` prediction's SAD over the four whole-block modes, using
4085    /// the already-reconstructed top/left neighbours — the intra candidate's cost
4086    /// in the fast (SAD) mode decision, without the full `I_4x4` search.
4087    fn best_i16_sad(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4088        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4089        let (lx, ly) = (mb_x * 16, mb_y * 16);
4090        let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4091        let mut top = [0u8; 16];
4092        let mut left = [0u8; 16];
4093        if avail_top {
4094            for i in 0..16 {
4095                top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4096            }
4097        }
4098        if avail_left {
4099            for i in 0..16 {
4100                left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4101            }
4102        }
4103        let corner = if avail_top && avail_left {
4104            self.rec_y[(ly - 1) * self.cw + lx - 1]
4105        } else {
4106            0
4107        };
4108        let mut best = i64::MAX;
4109        for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4110            if !mode.available(avail_top, avail_left) {
4111                continue;
4112            }
4113            let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4114            best = best.min(sad_16x16(sy, self.cw, lx, ly, &pred));
4115        }
4116        best
4117    }
4118
4119    /// SATD sibling of [`Self::best_i16_sad`] — the intra candidate's cost in the
4120    /// quality preset's SATD mode decision (openh264's `WelsMdI16x16`).
4121    fn best_i16_satd(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4122        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4123        let (lx, ly) = (mb_x * 16, mb_y * 16);
4124        let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4125        let mut top = [0u8; 16];
4126        let mut left = [0u8; 16];
4127        if avail_top {
4128            for i in 0..16 {
4129                top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4130            }
4131        }
4132        if avail_left {
4133            for i in 0..16 {
4134                left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4135            }
4136        }
4137        let corner = if avail_top && avail_left {
4138            self.rec_y[(ly - 1) * self.cw + lx - 1]
4139        } else {
4140            0
4141        };
4142        let mut best = i64::MAX;
4143        for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4144            if !mode.available(avail_top, avail_left) {
4145                continue;
4146            }
4147            let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4148            best = best.min(satd_16x16(sy, self.cw, lx, ly, &pred));
4149        }
4150        best
4151    }
4152
4153    /// Snapshots the per-block grids and reconstruction for one macroblock, so a
4154    /// trial encode can be rolled back.
4155    fn save_mb(&self, mb_x: usize, mb_y: usize) -> MbState {
4156        let mut d = MbState::default();
4157        self.save_mb_into(mb_x, mb_y, &mut d);
4158        d
4159    }
4160
4161    /// [`save_mb`](Self::save_mb) into an existing buffer, reusing its allocations.
4162    /// The per-macroblock region is a fixed size, so after the first call every
4163    /// `Vec` already has the capacity it needs and refilling is a pure copy.
4164    fn save_mb_into(&self, mb_x: usize, mb_y: usize, d: &mut MbState) {
4165        let w4 = self.mb_w * 4;
4166        let w2 = self.mb_w * 2;
4167        macro_rules! reg4 {
4168            ($v:expr, $o:expr) => {{
4169                $o.clear();
4170                for dy in 0..4 {
4171                    for dx in 0..4 {
4172                        $o.push($v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx]);
4173                    }
4174                }
4175            }};
4176        }
4177        macro_rules! regn {
4178            ($v:expr, $o:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {{
4179                $o.clear();
4180                for dy in 0..$n {
4181                    for dx in 0..$n {
4182                        $o.push($v[($oy + dy) * $stride + $ox + dx]);
4183                    }
4184                }
4185            }};
4186        }
4187        regn!(self.rec_y, d.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4188        regn!(self.rec_u, d.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4189        regn!(self.rec_v, d.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4190        reg4!(self.nnz_y, d.nnz_y);
4191        regn!(self.nnz_c[0], d.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4192        regn!(self.nnz_c[1], d.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4193        reg4!(self.mv_y, d.mv_y);
4194        reg4!(self.inter_y, d.inter_y);
4195        reg4!(self.ref_idx_y, d.ref_idx_y);
4196        reg4!(self.coded_y, d.coded_y);
4197        reg4!(self.modes_y, d.modes_y);
4198        d.cur_qp = self.cur_qp;
4199    }
4200
4201    /// Restores a macroblock's grids + reconstruction from a [`save_mb`] snapshot.
4202    fn load_mb(&mut self, mb_x: usize, mb_y: usize, s: &MbState) {
4203        let w4 = self.mb_w * 4;
4204        let w2 = self.mb_w * 2;
4205        macro_rules! put4 {
4206            ($v:expr, $src:expr) => {
4207                for dy in 0..4 {
4208                    for dx in 0..4 {
4209                        $v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx] = $src[dy * 4 + dx];
4210                    }
4211                }
4212            };
4213        }
4214        macro_rules! putn {
4215            ($v:expr, $src:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {
4216                for dy in 0..$n {
4217                    for dx in 0..$n {
4218                        $v[($oy + dy) * $stride + $ox + dx] = $src[dy * $n + dx];
4219                    }
4220                }
4221            };
4222        }
4223        putn!(self.rec_y, s.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4224        putn!(self.rec_u, s.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4225        putn!(self.rec_v, s.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4226        put4!(self.nnz_y, s.nnz_y);
4227        putn!(self.nnz_c[0], s.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4228        putn!(self.nnz_c[1], s.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4229        put4!(self.mv_y, s.mv_y);
4230        put4!(self.inter_y, s.inter_y);
4231        put4!(self.ref_idx_y, s.ref_idx_y);
4232        put4!(self.coded_y, s.coded_y);
4233        put4!(self.modes_y, s.modes_y);
4234        self.cur_qp = s.cur_qp;
4235    }
4236
4237    /// Loads the per-MB luma nnz prediction cache (openh264 `scan8` style): the top
4238    /// row from the macroblock above and the left column from the macroblock to the
4239    /// left (both already in `nnz_y`), with `0x80` at the picture edges. After this,
4240    /// neighbour nnz reads are branchless cache indexing — no bounds-checked `Option`.
4241    fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4242        let w4 = self.mb_w * 4;
4243        for lbx in 0..4 {
4244            self.nnz_l_cache[1 + lbx] = if mb_y == 0 {
4245                0x80
4246            } else {
4247                self.nnz_y[(mb_y * 4 - 1) * w4 + (mb_x * 4 + lbx)]
4248            };
4249        }
4250        for lby in 0..4 {
4251            self.nnz_l_cache[(lby + 1) * 5] = if mb_x == 0 {
4252                0x80
4253            } else {
4254                self.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 - 1)]
4255            };
4256        }
4257    }
4258
4259    /// Branchless nnz prediction (`nC`) for luma block `(lbx,lby)` from the cache —
4260    /// the `0x80` sentinel + `& 0x7f` mask collapse the four availability cases
4261    /// (matches the scalar nnz predict). Call after the block's left/top are cached.
4262    #[inline]
4263    fn nc_pred(&self, lbx: usize, lby: usize) -> i32 {
4264        let left = self.nnz_l_cache[(lby + 1) * 5 + lbx] as i32; // (lbx-1)+1
4265        let top = self.nnz_l_cache[lby * 5 + (lbx + 1)] as i32; // (lby-1)+1
4266        let r = left + top;
4267        if r < 0x80 {
4268            (r + 1) >> 1
4269        } else {
4270            r & 0x7f
4271        }
4272    }
4273
4274    /// Records a luma block's nnz into the per-MB cache (for later neighbour reads).
4275    #[inline]
4276    fn nnz_cache_set(&mut self, lbx: usize, lby: usize, total: u8) {
4277        self.nnz_l_cache[(lby + 1) * 5 + (lbx + 1)] = total;
4278    }
4279
4280    /// Loads the per-MB chroma nnz prediction cache (both planes) from the chroma
4281    /// blocks above/left, `0x80` at the picture edges — the chroma analogue of
4282    /// [`Self::nnz_cache_load`] (2×2 blocks → padded 3×3 grid).
4283    fn chroma_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4284        let w2 = self.mb_w * 2;
4285        for c in 0..2 {
4286            for bx in 0..2 {
4287                self.nnz_c_cache[c][1 + bx] = if mb_y == 0 {
4288                    0x80
4289                } else {
4290                    self.nnz_c[c][(mb_y * 2 - 1) * w2 + (mb_x * 2 + bx)]
4291                };
4292            }
4293            for by in 0..2 {
4294                self.nnz_c_cache[c][(by + 1) * 3] = if mb_x == 0 {
4295                    0x80
4296                } else {
4297                    self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 - 1)]
4298                };
4299            }
4300        }
4301    }
4302
4303    /// Branchless chroma nnz prediction (`nC`) for plane `c`, block `(bx,by)`.
4304    #[inline]
4305    fn chroma_nc_pred(&self, c: usize, bx: usize, by: usize) -> i32 {
4306        let left = self.nnz_c_cache[c][(by + 1) * 3 + bx] as i32;
4307        let top = self.nnz_c_cache[c][by * 3 + (bx + 1)] as i32;
4308        let r = left + top;
4309        if r < 0x80 {
4310            (r + 1) >> 1
4311        } else {
4312            r & 0x7f
4313        }
4314    }
4315
4316    /// Records a chroma block's nnz into the per-MB cache.
4317    #[inline]
4318    fn chroma_nnz_cache_set(&mut self, c: usize, bx: usize, by: usize, total: u8) {
4319        self.nnz_c_cache[c][(by + 1) * 3 + (bx + 1)] = total;
4320    }
4321}
4322
4323/// Encodes a slice's macroblocks then RBSP trailing bits, returning the
4324/// **deblocked** reconstruction to serve as the next frame's reference.
4325///
4326/// `is_p` selects P-slice framing (`mb_skip_run` prefix + intra `mb_type` +5
4327/// offset). In phase 4a every macroblock is still coded intra; motion-compensated
4328/// macroblocks arrive in 4b (using `reference`).
4329/// Boundary strengths for one macroblock, derived from the encoder's own grids
4330/// the moment it finishes coding.
4331///
4332/// `ref_idx_y` holds raw indices (-1 for intra) rather than the deblocker's
4333/// `NO_REF` sentinel; safe because reference identity is only compared between
4334/// two INTER blocks, which always carry a valid index.
4335// NOT inlined: this sits at three exits of the hottest loop in the encoder, and
4336// inlining it there costs more in I-cache and register pressure on the
4337// surrounding code than the call saves (measured: the loop grew ~2x the
4338// derivation's own cost).
4339#[inline(never)]
4340fn derive_mb_bs_from(
4341    fe: &FrameEncoder,
4342    mb_x: usize,
4343    mb_y: usize,
4344    kind: rusty_h264_common::deblock::MbKind,
4345) -> rusty_h264_common::deblock::MbBs {
4346    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncBs);
4347    let view = rusty_h264_common::deblock::BlockInfo {
4348        inter: &fe.inter_y,
4349        nnz: &fe.nnz_y,
4350        mv: &fe.mv_y,
4351        ref_id: &fe.ref_idx_y,
4352        mv1: &[],
4353        ref_id1: &[],
4354        w4: fe.mb_w * 4,
4355        t8x8: &[],
4356        bs: &[],
4357    };
4358    rusty_h264_common::deblock::derive_mb_kind(&view, mb_x, mb_y, kind)
4359}
4360
4361pub fn encode_slice_data(
4362    w: &mut BitWriter,
4363    cfg: &EncoderConfig,
4364    frame: &YuvFrame,
4365    qp: u8,
4366    is_p: bool,
4367    refs: &[crate::RefFrame],
4368    qpo: &[i32],
4369) -> crate::RefFrame {
4370    let _g_prep = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncPrep);
4371    let mut fe = FrameEncoder::new(cfg);
4372    let precomp = rusty_h264_common::deblock::precomputed_bs_enabled();
4373    let mut bs_grid =
4374        vec![rusty_h264_common::deblock::MbBs::UNSET; if precomp { fe.mb_w * fe.mb_h } else { 0 }];
4375    fe.qp = qp;
4376    fe.qpc = chroma_qp(qp);
4377    fe.cur_qp = qp;
4378    if cfg.cabac_dz_div > 0 {
4379        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
4380    } // QPY_PREV starts at the slice QP so the first mb_qp_delta is 0
4381    let (sy, su, sv) = coded_source(cfg, frame);
4382    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
4383    let num_refs = refs.len();
4384    // me_wide CONTENT GATE: on a pure PAN the global-MC residual ≈ 0, so the diamond's
4385    // seed (median = pan MV) is already right and the wide rescue only over-fits
4386    // (spurious MVs that hurt the B-frames' spatial-direct — the panc regression).
4387    // Gate it off there; non-uniform content (real stalls) reads well above 0.
4388    if is_p && fe.me_wide && !refs.is_empty()
4389        && global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y) < fe.me_wide_coh
4390    {
4391        fe.me_wide = false;
4392    }
4393    // me_wide HEAD-ROOM GATE (the dispatcher the truth table asked for). The rescue
4394    // only pays where a wide search actually beats a predictor-local one; measure
4395    // that directly per frame and route the frame. `RFF_ME_HR` sets the threshold
4396    // (percent); 0 disables the gate and restores the always-on behaviour.
4397    // Skip the probe entirely when the gate is disabled: it must not tax the
4398    // default path (`RFF_ME_HR=0`), which stays byte-identical to pre-gate output.
4399    if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
4400        let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4401        if me_wide_hr_dbg() {
4402            eprintln!("ME_HR qp{qp} headroom={hr:.2}");
4403        }
4404        if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
4405            fe.me_wide = false;
4406        }
4407    }
4408    // Track-B B2 DISPATCH (WHYS H-2): SAD full-pel wins where a plain full-pel
4409    // translational search actually improves on zero motion (`b2_mgain`) and loses
4410    // on flash/fine-detail content. Probe per frame, route the frame — per-frame,
4411    // not cross-frame, so it stays deterministic under GOP-parallel encode.
4412    if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
4413        let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4414        if me_sadt_dbg() {
4415            eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
4416        }
4417        fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
4418    }
4419    // Content-adaptive cost-function dispatch (codec-content-adaptive-dispatch): the
4420    // fast preset prices modes by cheap SAD, which is rate-blind on detailed MBs;
4421    // route the top `satd_q` fraction of highest-VARIANCE MBs to the rate-faithful
4422    // SATD cost. A per-frame PERCENTILE threshold makes the routed fraction — hence
4423    // the speed/quality split — content-invariant (same q → same fraction on any
4424    // clip). `satd_q == 0` leaves the threshold at MAX (pure SAD, byte-identical).
4425    if is_p && fe.satd_q > 0.0 {
4426        let mut vars: Vec<i64> = (0..fe.mb_h)
4427            .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
4428            .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
4429            .collect();
4430        vars.sort_unstable();
4431        let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
4432        fe.satd_var_thresh = vars[idx];
4433    }
4434    // Adaptive Quantization: per-MB target QPy from content (finer on flat MBs,
4435    // coarser on busy ones). `mb_qpy` records each MB's ACTUAL QPy (a skip / cbp==0
4436    // MB inherits `cur_qp`), for the deblock filter. `strength 0` → uniform → the
4437    // mb_qp_delta stays 0, byte-identical.
4438    let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
4439    apply_mbtree_qpo(&mut aq_qp, qpo); // mb-tree temporal AQ (empty = byte-identical)
4440    fe.cur_qp = qp;
4441    let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
4442    let mut skip_run = 0u32;
4443    // ---- adaptive RD-skip gate -------------------------------------------
4444    // RD P_Skip is a large win on temporally redundant content and a large LOSS
4445    // on detailed content (SSIM: akiyo -13.1%, FourPeople -5.6% vs in_to_tree
4446    // +34.0%, stockholm +95.7%). The separating signal is the content's own
4447    // FREE-skip rate — how much of it is already exactly redundant — and the gap
4448    // is wide (winners >=58.7%, losers <=6.4%). Measure it ONLINE over the first
4449    // slice of the frame and enable RD skip for the remainder only if it clears
4450    // the bar. Within-frame, so it stays deterministic under GOP-parallel encode.
4451    if is_p && mv_cmp_on() {
4452        MVCMP_FRAME.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4453    }
4454    // Reused across every RD-skip candidate — see `MbState`.
4455    let mut rdskip_snap = MbState::default();
4456    let mut rdskip_free = 0usize;
4457    let mut rdskip_seen = 0usize;
4458    let mut rdskip_on = false;
4459    let mut greedy_on = fe.greedy_min_free == 0; // 0 = ungated (historic behaviour)
4460    let rdskip_learn = (fe.mb_w * fe.mb_h / 8).max(64);
4461    let rdskip_min_free = fe.rd_skip_min_free as usize;
4462
4463    drop(_g_prep);
4464    let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
4465    for mb_y in 0..fe.mb_h {
4466        for mb_x in 0..fe.mb_w {
4467            let mb_idx = mb_y * fe.mb_w + mb_x;
4468            fe.qp = aq_qp[mb_idx];
4469            fe.qpc = chroma_qp(aq_qp[mb_idx]);
4470            // P_Skip: motion-compensate from the most-recent reference; accept if free.
4471            // Chosen inter coding: (mb_type, per-partition (ref_idx, mv)).
4472            let mut inter: Option<InterChoice> = None;
4473            // Bits of an inter macroblock already encoded by the skip decision
4474            // below. When present the emit path splices them instead of encoding
4475            // the same macroblock a second time.
4476            let mut coded: Option<BitWriter> = None;
4477            if is_p {
4478                if num_refs > 0 {
4479                    // P_Skip prediction (reference 0). A free skip (zero residual) is
4480                    // taken immediately; the quality preset also takes a greedy P_Skip
4481                    // when its SAD is below the neighbour-predicted bound (below).
4482                    let _g_skip = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSkip);
4483                    let _g_smc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
4484                    rdskip_seen += 1;
4485                    if rdskip_seen >= rdskip_learn {
4486                        rdskip_on = rdskip_free * 100 >= rdskip_seen * rdskip_min_free;
4487                        greedy_on = fe.greedy_min_free == 0
4488                            || rdskip_free * 100 >= rdskip_seen * fe.greedy_min_free as usize;
4489                    }
4490                    let mv_skip = fe.skip_mv(mb_x, mb_y);
4491                    let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
4492                    drop(_g_smc);
4493                    let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
4494                    // Chroma MC only when it can matter: luma already free (so the
4495                    // skip might be taken) or the quality path needs it below.
4496                    let skip_c = if luma_free || !fe.fast {
4497                        fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
4498                    } else {
4499                        [[0u8; 64]; 2]
4500                    };
4501                    let is_free =
4502                        luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
4503                    // Skip-prediction luma SAD (the quality preset's predicted-SAD apparatus).
4504                    let skip_sad = if fe.fast {
4505                        0
4506                    } else {
4507                        let (lx, ly) = (mb_x * 16, mb_y * 16);
4508                        let mut s = 0u32;
4509                        for dy in 0..16 {
4510                            let src = &sy[(ly + dy) * fe.cw + lx..][..16];
4511                            let p = &skip_y[dy * 16..][..16];
4512                            s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
4513                        }
4514                        s
4515                    };
4516                    if is_free {
4517                        fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4518                        if !fe.fast {
4519                            fe.mb_was_skip[mb_idx] = true;
4520                            fe.mb_skip_sad[mb_idx] = skip_sad;
4521                        }
4522                        mb_qpy[mb_idx] = fe.cur_qp; // skip inherits QPy
4523                        rdskip_free += 1;
4524                        if precomp {
4525                            bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4526                        }
4527                        skip_run += 1;
4528                        continue;
4529                    }
4530                    drop(_g_skip);
4531                    let (lx, ly) = (mb_x * 16, mb_y * 16);
4532                    let nb = {
4533                        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
4534                        fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
4535                    };
4536                    let lme = lambda.sqrt();
4537
4538                    if fe.fast {
4539                        // Fast preset: pick the cheapest *prediction* by SATD (no
4540                        // trial-encoding), then always code its residual — P_16x16 vs
4541                        // I_16x16 only, no sub-partitions. Crucially it does NOT make a
4542                        // SATD skip-vs-code decision: P_Skip is taken only for a truly
4543                        // free (zero-residual) macroblock, handled above. Pricing skip
4544                        // by SATD would drop residual the QP wants coded and tank PSNR;
4545                        // like x264's fast presets, fast trades *efficiency* (more bits)
4546                        // for speed, not quality. The faster ME is what makes it fast.
4547                        // Adaptive dispatch: high-variance MBs price by SATD (both
4548                        // inter — via `mb_use_satd` in `best_part` — and intra), the
4549                        // rest by cheap SAD. Set the per-MB flag before best_part.
4550                        fe.mb_use_satd = fe.satd_q > 0.0
4551                            && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
4552                        let (r16, mv16, cost_inter) =
4553                            fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4554                        let cost_intra = if fe.mb_use_satd {
4555                            fe.best_i16_satd(&sy, mb_x, mb_y)
4556                        } else {
4557                            fe.best_i16_sad(&sy, mb_x, mb_y)
4558                        } + (lme * fe.tune_intra_penalty) as i64;
4559                        inter = if cost_intra < cost_inter {
4560                            None // intra wins → encode_mb below
4561                        } else {
4562                            Some((0, vec![(r16, mv16)]))
4563                        };
4564                    } else {
4565                        // Quality preset: openh264's mode-decision model — SATD + λ·mvbits
4566                        // cost ESTIMATE (no per-candidate trial-encode); modes are ranked
4567                        // by that cost and only the winner is encoded (once) below. This
4568                        // removes ~the 93%-of-quality re-encode cost.
4569
4570                        // Greedy P_Skip (openh264 `PredictSadSkip`): take the skip when its
4571                        // luma SAD is below the neighbour-predicted skip SAD. The threshold
4572                        // is what skip neighbours achieved, so the skip propagates from the
4573                        // free skips and self-limits — no fixed bound, no inter-chain drift.
4574                        if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
4575                            fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4576                            fe.mb_was_skip[mb_idx] = true;
4577                            fe.mb_skip_sad[mb_idx] = skip_sad;
4578                            mb_qpy[mb_idx] = fe.cur_qp; // skip inherits QPy
4579                            if precomp {
4580                                bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4581                            }
4582                            skip_run += 1;
4583                            continue;
4584                        }
4585
4586                        // 16×16 baseline (SATD + λ·bits, with sub-pel refinement).
4587                        let (r16, mv16, c16) =
4588                            fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4589                        let mut best_c = c16;
4590                        let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
4591
4592                        // Sub-partitions, ranked by SATD, gated on a heavy 16×16 (a likely
4593                        // motion boundary — the 4 sub-pel searches are the expensive part).
4594                        const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
4595                        let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
4596                        let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
4597                        let split_t = split_t();
4598                        if c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
4599                            let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
4600                            let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
4601                            let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
4602                            let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
4603                            if ct + cb < best_c {
4604                                best_c = ct + cb;
4605                                pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
4606                            }
4607                            if cl + cr < best_c {
4608                                best_c = cl + cr;
4609                                pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
4610                            }
4611
4612                            // P_8x8: four independent 8×8 sub-partitions (finer motion
4613                            // granularity — the win on complex/boundary motion). Each 8×8
4614                            // seeded by the 16×16 MV; the exact chained MVD is computed in
4615                            // plan_inter_mb. Same heavy-16×16 gate as the 2-way splits.
4616                            if fe.sub8x8 {
4617                                let mut c8 = (lme * 4.0) as i64; // ~4 sub_mb_type bits
4618                                let mut p8 = Vec::with_capacity(4);
4619                                for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
4620                                    let (r, mv, c) = fe.best_part(
4621                                        refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
4622                                    );
4623                                    c8 += c;
4624                                    p8.push((r, mv));
4625                                }
4626                                if c8 < best_c {
4627                                    best_c = c8;
4628                                    pick = Some((3u8, p8));
4629                                }
4630                            }
4631                        }
4632
4633                        // U5-struct: everything above searched FULL-PEL only when
4634                        // `sp_defer` is set. Now that a shape has won, refine just its
4635                        // sub-blocks — the losing shapes' refinements were the waste
4636                        // (measured 3.4–6.4× more refinement than necessary).
4637                        if fe.sp_defer.get() {
4638                            if let Some((mode, parts)) = pick.as_mut() {
4639                                let regions: &[(usize, usize, usize, usize)] = match mode {
4640                                    1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
4641                                    2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
4642                                    3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
4643                                    _ => &[(0, 0, 16, 16)],
4644                                };
4645                                let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
4646                                for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
4647                                    let (r, mv) = parts[i];
4648                                    let (m2, c2) = fe.refine_part(
4649                                        refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
4650                                    );
4651                                    parts[i] = (r, m2);
4652                                    tot += c2;
4653                                }
4654                                best_c = tot;
4655                            }
4656                        }
4657                        if split_harvest::enabled() {
4658                            let won = match pick.as_ref().map(|p| p.0) {
4659                                Some(0) | None => 0u8,
4660                                Some(m) => m,
4661                            };
4662                            split_harvest::record(c16, best_c, lme, split_gate, won);
4663                        }
4664                        // Intra is ALWAYS a candidate (textured / occluded content):
4665                        // I_16x16 SATD + λ·mode bits.
4666                        let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
4667                            + (lme * fe.tune_intra_penalty) as i64;
4668                        inter = if c_intra < best_c { None } else { pick };
4669                        fe.mb_was_skip[mb_idx] = false;
4670                        fe.mb_skip_sad[mb_idx] = skip_sad;
4671                    }
4672
4673                    // ---- RD P_Skip ----------------------------------------
4674                    // The default criterion skips only when the residual quantizes
4675                    // to EXACTLY zero. That matches x264 at both extremes (akiyo
4676                    // 72.5% vs 73.6%, mobile 1.0% vs 1.4%) but falls 17-23 points
4677                    // short in the middle (foreman 6.4% vs 23.6%), because x264
4678                    // also skips macroblocks whose residual is small-but-nonzero.
4679                    // Decide it properly: trial-encode the chosen mode for real
4680                    // bits + reconstruction SSD, and compare J = SSD + lambda*R
4681                    // against the skip. Raw-SAD versions of this comparison fail
4682                    // badly (coding REPAIRS the residual, skipping keeps it), so
4683                    // the distortion term has to come from the reconstruction.
4684                    if fe.rd_skip && rdskip_on && inter.is_some() {
4685                        let skip_cp = fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip);
4686                        // A P_Skip carries no residual, so its RECONSTRUCTION *is*
4687                        // its prediction — the skip SSD needs no state mutation at
4688                        // all. The commit / mb_ssd / restore round trip this
4689                        // replaces cost a full macroblock save+restore on every
4690                        // candidate, including the ones that go on to code.
4691                        let ssd_s = fe.pred_ssd(&sy, &su, &sv, mb_x, mb_y, &skip_y, &skip_cp);
4692                        debug_assert_eq!(ssd_s, {
4693                            let snap = fe.save_mb(mb_x, mb_y);
4694                            fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
4695                            let v = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
4696                            fe.load_mb(mb_x, mb_y, &snap);
4697                            v
4698                        }, "skip prediction SSD must equal the committed-skip reconstruction SSD");
4699                        // A skip inside a run costs ~1 bit of mb_skip_run.
4700                        let j_skip = ssd_s as f64 + lambda;
4701                        // Search-skip gate: when the null arm is this cheap it
4702                        // almost always wins, so take it without pricing the coded
4703                        // arm at all. This is where the decision's remaining cost
4704                        // lives — the coded arm is encoded and then discarded on
4705                        // 55-80% of candidates.
4706                        let take_skip = if fe.rd_skip_fast_t > 0.0
4707                            && (ssd_s as f64) <= lambda * fe.rd_skip_fast_t
4708                        {
4709                            true
4710                        } else {
4711                        // Otherwise encode ONCE, into scratch, and KEEP the state.
4712                        // If the skip loses, those are the real bits and they splice
4713                        // straight into the slice. The previous shape trial-encoded,
4714                        // threw the result away, and then encoded again — paying
4715                        // twice on the path that actually codes.
4716                            fe.save_mb_into(mb_x, mb_y, &mut rdskip_snap);
4717                            let mut scratch = BitWriter::new();
4718                            {
4719                                let (m, p) = inter.as_ref().unwrap();
4720                                fe.encode_inter_mb(
4721                                    &mut scratch, refs, &sy, &su, &sv, mb_x, mb_y, *m, p,
4722                                );
4723                            }
4724                            let bits_c = scratch.bit_len();
4725                            let ssd_c = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
4726                            let won = j_skip <= ssd_c as f64 + lambda * bits_c as f64;
4727                            if won {
4728                                fe.load_mb(mb_x, mb_y, &rdskip_snap); // undo it; take the skip
4729                                true
4730                            } else {
4731                                coded = Some(scratch); // keep it — no second encode
4732                                false
4733                            }
4734                        };
4735                        if take_skip {
4736                            fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
4737                            if !fe.fast {
4738                                fe.mb_was_skip[mb_idx] = true;
4739                                fe.mb_skip_sad[mb_idx] = skip_sad;
4740                            }
4741                            mb_qpy[mb_idx] = fe.cur_qp;
4742                            if precomp {
4743                                bs_grid[mb_idx] = derive_mb_bs_from(
4744                                    &fe, mb_x, mb_y,
4745                                    rusty_h264_common::deblock::MbKind::Skip,
4746                                );
4747                            }
4748                            skip_run += 1;
4749                            continue;
4750                        }
4751                    }
4752                }
4753                w.write_ue(skip_run); // run of skipped macroblocks before this one
4754                skip_run = 0;
4755            }
4756            if mv_force_on() && is_p && inter.is_some() {
4757                let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
4758                let ext = EXT_MV.lock().unwrap();
4759                if let Some(field) = ext.get(fi) {
4760                    let w4 = fe.mb_w * 4;
4761                    let b0 = (mb_y * 4) * w4 + mb_x * 4;
4762                    // uniform 16x16 only: a sub-partitioned macroblock has no single
4763                    // vector to transplant, so leave those to our own decision
4764                    let uniform = (0..4).all(|r| {
4765                        (0..4).all(|c| field.get(b0 + r * w4 + c) == field.get(b0))
4766                    });
4767                    if uniform {
4768                        if let Some(&emv) = field.get(b0) {
4769                            inter = Some((0, vec![(0, emv)]));
4770                            MVCMP[6].fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4771                        }
4772                    }
4773                }
4774            }
4775            if mv_cmp_on() && is_p {
4776                if let Some((mode, parts)) = inter.as_ref() {
4777                    let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
4778                    let ext = EXT_MV.lock().unwrap();
4779                    if let Some(field) = ext.get(fi) {
4780                        let bidx = (mb_y * 4) * (fe.mb_w * 4) + mb_x * 4;
4781                        if let Some(&emv) = field.get(bidx) {
4782                            let (mode, parts) = (*mode, parts.clone());
4783                            drop(ext);
4784                            // Both priced through the SAME pipeline: MC, transform,
4785                            // quantize, CAVLC. Real bits, real reconstruction SSD.
4786                            let (so, bo) =
4787                                fe.trial_inter(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts);
4788                            let (se, be) = fe.trial_inter(
4789                                refs, &sy, &su, &sv, mb_x, mb_y, 0, &[(0, emv)],
4790                            );
4791                            let jo = so as f64 + lambda * bo as f64;
4792                            let je = se as f64 + lambda * be as f64;
4793                            use std::sync::atomic::Ordering::Relaxed;
4794                            MVCMP[0].fetch_add(1, Relaxed);
4795                            MVCMP[1].fetch_add(bo as u64, Relaxed);
4796                            MVCMP[2].fetch_add(be as u64, Relaxed);
4797                            MVCMP[3].fetch_add(so.max(0) as u64, Relaxed);
4798                            MVCMP[4].fetch_add(se.max(0) as u64, Relaxed);
4799                            MVCMP[5].fetch_add((je < jo) as u64, Relaxed);
4800                            MVCMP[6].fetch_add((parts[0].1 != emv) as u64, Relaxed);
4801                        }
4802                    }
4803                }
4804            }
4805            // Capture the kind before `inter` is consumed: the deblocking
4806            // strengths of an intra macroblock are pure constants.
4807            let mb_kind = match &inter {
4808                // A single partition covers the whole macroblock with one
4809                // (ref, mv), which collapses the internal derivation to nnz.
4810                Some((_, parts)) if parts.len() == 1 => {
4811                    rusty_h264_common::deblock::MbKind::InterUniform
4812                }
4813                Some(_) => rusty_h264_common::deblock::MbKind::Inter,
4814                None => rusty_h264_common::deblock::MbKind::Intra,
4815            };
4816            match inter {
4817                Some((mode, parts)) => match coded {
4818                    // Encoded already, during the skip decision — splice the bits in
4819                    // rather than encoding this macroblock for a second time.
4820                    Some(sc) => w.append(&sc),
4821                    None => {
4822                        fe.encode_inter_mb(w, refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts)
4823                    }
4824                },
4825                None => encode_mb(&mut fe, w, mb_x, mb_y, &sy, &su, &sv, is_p),
4826            }
4827            mb_qpy[mb_idx] = fe.cur_qp; // ACTUAL QPy (updated iff an mb_qp_delta was coded)
4828            if precomp {
4829                bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, mb_kind);
4830            }
4831        }
4832    }
4833    debug_assert!(
4834        !precomp || bs_grid.iter().all(|b| *b != rusty_h264_common::deblock::MbBs::UNSET),
4835        "a macroblock loop exit failed to store its boundary strengths"
4836    );
4837    if is_p && skip_run > 0 {
4838        w.write_ue(skip_run); // trailing skipped macroblocks
4839    }
4840    w.rbsp_trailing_bits();
4841
4842    // Deblock the reconstruction; the result is the inter reference. Baseline: the
4843    // intra mask is `!inter_y` (passed directly, no alloc); no B (List-1 empty); no
4844    // 8×8 transform (t8x8 empty). ref_id is each block's List-0 ref index.
4845    drop(_g_loop);
4846    let _g_fin = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFinal);
4847    // No NO_REF-mapping collect: it ran over every 4x4 block every frame (~1.9 MB
4848    // of allocation + map at 1080p) to produce a grid that is only ever read for
4849    // INTER-vs-INTER comparisons, where the encoder's raw indices are already
4850    // equivalent. Intra blocks short-circuit before reference identity is touched.
4851    let info = rusty_h264_common::deblock::BlockInfo {
4852        inter: &fe.inter_y,
4853        nnz: &fe.nnz_y,
4854        mv: &fe.mv_y,
4855        ref_id: &fe.ref_idx_y,
4856        mv1: &[],
4857        ref_id1: &[],
4858        w4: fe.mb_w * 4,
4859        t8x8: &[],
4860        bs: &bs_grid,
4861    };
4862    // Per-MB actual QPy (AQ varies it; `mb_qp_delta`-driven). With `aq_strength 0`
4863    // this is uniform, reproducing the old scalar-QP filtering exactly.
4864    drop(_g_fin);
4865    rusty_h264_common::deblock::filter_frame(
4866        &mut fe.rec_y,
4867        &mut fe.rec_u,
4868        &mut fe.rec_v,
4869        fe.mb_w,
4870        fe.mb_h,
4871        &mb_qpy,
4872        0, // chroma_qp_index_offset — the encoder emits 0
4873        0, // slice_alpha_c0_offset — the encoder always signals zero offsets
4874        0, // slice_beta_offset
4875        &info,
4876    );
4877    let w4 = fe.mb_w * 4;
4878    crate::RefFrame {
4879        y: fe.rec_y,
4880        u: fe.rec_u,
4881        v: fe.rec_v,
4882        poc: 0,       // set by the caller (it knows the display order)
4883        frame_num: 0, // set by the caller
4884        // List-0 motion field, for a later B-frame's spatial-direct colZeroFlag.
4885        mv: fe.mv_y,
4886        ref_idx: fe.ref_idx_y,
4887        w4,
4888        // Filtered lazily on first sub-pel search use (see `RefFrame::hpel`).
4889        hpel: std::sync::OnceLock::new(),
4890    }
4891}
4892
4893/// Codes a B-slice's macroblock layer. B-frames are **non-reference**, so the
4894/// reconstruction is computed (the CAVLC nnz predictor needs it) but discarded.
4895///
4896/// This brick: every MB is coded `B_L0_16x16` (`mb_type == 1`) — a real
4897/// motion-compensated prediction from `l0` (the nearest PAST anchor, List-0 index
4898/// 0) plus a coded residual. Because every MB is List-0-only with `ref_idx`
4899/// inferred 0, the per-4×4 List-0 motion field and its median `mvd` predictor are
4900/// byte-identical to the P-slice `P_L0_16x16` path — so this reuses
4901/// [`FrameEncoder::encode_inter_mb_v1_b`] verbatim, differing from P only in the
4902/// `mb_type` value. `l1` (nearest future anchor) is unused until `B_Bi` lands.
4903#[allow(clippy::too_many_arguments)]
4904#[allow(clippy::too_many_arguments)]
4905pub fn encode_slice_data_b(
4906    w: &mut BitWriter,
4907    cfg: &EncoderConfig,
4908    frame: &YuvFrame,
4909    qp: u8,
4910    poc: i32,
4911    l0: &crate::RefFrame,
4912    l1: &crate::RefFrame,
4913    qpo: &[i32],
4914) {
4915    let mut fe = FrameEncoder::new(cfg);
4916    fe.qp = qp;
4917    fe.qpc = chroma_qp(qp);
4918    fe.cur_qp = qp;
4919    if cfg.cabac_dz_div > 0 {
4920        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
4921    } // QPY_PREV starts at the slice QP so the first mb_qp_delta is 0
4922    // Implicit bi-prediction weights from the anchor POC distances (matches the
4923    // decoder). Equidistant B (bframes==1) → 32:32 (plain average); unequal → weighted.
4924    fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
4925    let (sy, su, sv) = coded_source(cfg, frame);
4926    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
4927    let lme = lambda.sqrt();
4928    let refs = std::slice::from_ref(l0); // List-0 = [nearest past anchor]
4929    // Same content-adaptive SAD→SATD dispatch as the P path (codec-content-adaptive-
4930    // dispatch): the top `satd_q` fraction of highest-variance MBs price by SATD.
4931    if fe.satd_q > 0.0 {
4932        let mut vars: Vec<i64> = (0..fe.mb_h)
4933            .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
4934            .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
4935            .collect();
4936        vars.sort_unstable();
4937        let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
4938        fe.satd_var_thresh = vars[idx];
4939    }
4940    let mut skip_run = 0u32; // run of consecutive B_Skip MBs pending a coded MB
4941    for mb_y in 0..fe.mb_h {
4942        for mb_x in 0..fe.mb_w {
4943            let (lx, ly) = (mb_x * 16, mb_y * 16);
4944            let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
4945            fe.mb_use_satd =
4946                fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
4947            // Per-list median MV predictors — the search-rate center AND the actual
4948            // `mvd` predictor (identical to the decoder's `predict_partition_mv`).
4949            let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
4950            let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
4951            let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
4952            let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
4953            // Independent List-0 / List-1 motion searches (their J already includes
4954            // the mvd rate against the matching predictor, so J0/J1 compare directly).
4955            // Spatial-direct prediction (basis of B_Skip and B_Direct_16x16).
4956            let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
4957            // B_Skip: take the direct prediction with NO coded residual (~1 bit in
4958            // the mb_skip_run) only when it is truly FREE — its residual quantizes to
4959            // zero at the B QP, so skipping loses nothing. (A looser SATD-threshold
4960            // skip was measured strictly WORSE: on B's derived prediction the SATD
4961            // proxy over-values the skip, dropping residual the quantizer wanted —
4962            // the same proxy-vs-quantization gap seen on sub-pel. So skip only when
4963            // provably free; the rest goes through the L0/L1/Bi/Direct RD decision.)
4964            if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
4965                && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
4966            {
4967                fe.commit_direct_motion(mb_x, mb_y, &dmotion);
4968                skip_run += 1;
4969                continue;
4970            }
4971            let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
4972            let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
4973            let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
4974            // Bi: average the two winners' predictions; rate = both mvds.
4975            let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
4976            let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
4977                + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
4978            let j_bi = d_bi + (lme * r_bi as f64) as i64;
4979            // B_Direct (mb_type 0): spatial-direct prediction, NO coded MV — so its
4980            // J (d_direct, computed above) carries zero mvd rate and it wins wherever
4981            // the derived motion predicts as well as an explicit vector.
4982            // Pick the cheapest of {0=Direct, 1=L0, 2=L1, 3=Bi}; Direct wins ties.
4983            let (mut dir, mut best) = (0u8, d_direct);
4984            if j0 < best { dir = 1; best = j0; }
4985            if j1 < best { dir = 2; best = j1; }
4986            if j_bi < best { dir = 3; best = j_bi; }
4987            let _ = best;
4988            w.write_ue(skip_run); // run of B_Skips preceding this coded MB
4989            skip_run = 0;
4990            let bspec = BInter { dir, l1, mv0, mv1 };
4991            fe.encode_inter_mb_v1_b(w, refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
4992        }
4993    }
4994    if skip_run > 0 {
4995        w.write_ue(skip_run); // trailing B_Skip run
4996    }
4997    w.rbsp_trailing_bits();
4998}
4999
5000/// `se(d)` Exp-Golomb bit length — the `mvd`-component rate for the B mode
5001/// decision. Same closed form as `motion_search`'s private `mvbits` (kept separate
5002/// so the P search's heuristic — and thus P output — is untouched).
5003#[inline(always)]
5004fn mvd_bits(d: i32) -> u32 {
5005    let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
5006    1 + 2 * (31 - (codenum + 1).leading_zeros())
5007}
5008
5009/// Reads a 4×4 residual block (source minus a raster prediction block).
5010/// Writes `ref_idx_l0` (spec: `te(v)` when two references are active — a single
5011/// flag — else `ue(v)`). Only called when more than one reference is active.
5012fn write_ref_idx(w: &mut BitWriter, refi: i32, num_refs: usize) {
5013    if num_refs == 2 {
5014        w.write_bit(refi == 0); // te(v): value = !bit
5015    } else {
5016        w.write_ue(refi as u32);
5017    }
5018}
5019
5020/// Approximate bit cost of coding `ref_idx = r` with `num_refs` active, for the
5021/// motion-estimation rate term. Zero with a single reference (no `ref_idx` coded).
5022fn ref_bits(r: usize, num_refs: usize) -> u32 {
5023    if num_refs <= 1 {
5024        0
5025    } else if num_refs == 2 {
5026        1
5027    } else {
5028        let mut n = r as u32 + 1;
5029        let mut len = 1;
5030        while n > 1 {
5031            n >>= 1;
5032            len += 2;
5033        }
5034        len
5035    }
5036}
5037
5038fn residual(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[i32; 16]) -> [i32; 16] {
5039    let mut r = [0i32; 16];
5040    for dy in 0..4 {
5041        for dx in 0..4 {
5042            r[dy * 4 + dx] = src[(y0 + dy) * stride + (x0 + dx)] as i32 - pred[dy * 4 + dx];
5043        }
5044    }
5045    r
5046}
5047
5048/// Writes reconstructed samples back into a plane.
5049fn store(plane: &mut [u8], stride: usize, x0: usize, y0: usize, s: &[u8; 16]) {
5050    for dy in 0..4 {
5051        for dx in 0..4 {
5052            plane[(y0 + dy) * stride + (x0 + dx)] = s[dy * 4 + dx];
5053        }
5054    }
5055}
5056
5057/// Extracts the 4×4 raster prediction block at `(bx, by)` from a 16×16 (256-sample)
5058/// luma prediction.
5059fn pred_block(pred: &[u8; 256], bx: usize, by: usize) -> [i32; 16] {
5060    let mut p = [0i32; 16];
5061    for dy in 0..4 {
5062        for dx in 0..4 {
5063            p[dy * 4 + dx] = pred[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
5064        }
5065    }
5066    p
5067}
5068
5069/// Sum of absolute transformed differences over a 16×16 luma macroblock — the
5070/// mode-decision cost (correlates with coded bits better than plain SAD).
5071/// SATD of a `w`×`h` luma block: `src` (stride `ss`) vs `pred` (stride `ps`).
5072///
5073/// With `--features asm` and a supported size this is `2 · WelsSampleSatd_sse2`, which
5074/// is **byte-identical** to the scalar `Σ|H·d|` Hadamard: the openh264 kernel returns
5075/// `(Σ+1)>>1`, and `Σ` is always even (every 4×4 Hadamard coefficient shares the block
5076/// sum's parity, so 16 of them sum even), so `×2` recovers `Σ` exactly — proven over
5077/// 20 k random blocks at 4×4/8×8/16×16 in `tests/satd_asm_compare.rs`. Without asm (or
5078/// for an unsupported size) it falls back to the scalar Hadamard — the original path.
5079#[inline]
5080fn satd_px(src: &[u8], ss: usize, pred: &[u8], ps: usize, w: usize, h: usize) -> i64 {
5081    #[cfg(accel)]
5082    {
5083        let asm = match (w, h) {
5084            (16, 16) => Some(rusty_h264_accel::satd_16x16(src, ss, pred, ps)),
5085            (16, 8) => Some(rusty_h264_accel::satd_16x8(src, ss, pred, ps)),
5086            (8, 16) => Some(rusty_h264_accel::satd_8x16(src, ss, pred, ps)),
5087            (8, 8) => Some(rusty_h264_accel::satd_8x8(src, ss, pred, ps)),
5088            (4, 4) => Some(rusty_h264_accel::satd_4x4(src, ss, pred, ps)),
5089            _ => None,
5090        };
5091        if let Some(v) = asm {
5092            return 2 * v as i64;
5093        }
5094    }
5095    // Scalar Hadamard (also the no-asm path): Σ over the 4×4 sub-blocks.
5096    let (nbx, nby) = (w / 4, h / 4);
5097    let mut blocks = [[0i32; 16]; 16];
5098    let mut bi = 0;
5099    for by in 0..nby {
5100        for bx in 0..nbx {
5101            let blk = &mut blocks[bi];
5102            for dy in 0..4 {
5103                for dx in 0..4 {
5104                    blk[dy * 4 + dx] =
5105                        src[(by * 4 + dy) * ss + bx * 4 + dx] as i32 - pred[(by * 4 + dy) * ps + bx * 4 + dx] as i32;
5106                }
5107            }
5108            bi += 1;
5109        }
5110    }
5111    satd_4x4_sum(&blocks[..nbx * nby])
5112}
5113
5114/// SAD of a `w`×`h` block: `src` (stride `ss`) vs a strided region `r` (stride `rs`)
5115/// — the openh264 `psadbw` kernels for the shapes that ship them (they take strides,
5116/// so in-place plane reads need NO materialize), scalar `Σ abs_diff` rows otherwise
5117/// (LLVM lowers the idiom to `psadbw` for contiguous rows).
5118#[inline]
5119fn sad_strided(src: &[u8], ss: usize, r: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5120    #[cfg(accel)]
5121    {
5122        match (w, h) {
5123            (16, 16) => return rusty_h264_accel::sad_16x16(src, ss, r, rs) as i64,
5124            (16, 8) => return rusty_h264_accel::sad_16x8(src, ss, r, rs) as i64,
5125            (8, 16) => return rusty_h264_accel::sad_8x16(src, ss, r, rs) as i64,
5126            _ => {}
5127        }
5128    }
5129    let mut sad = 0u32;
5130    for dy in 0..h {
5131        let a = &src[dy * ss..][..w];
5132        let b = &r[dy * rs..][..w];
5133        sad += a.iter().zip(b).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
5134    }
5135    sad as i64
5136}
5137
5138/// Fused `SAD(src, (a+b+1)>>1)` — the quarter-pel SAD without materializing the
5139/// average (the B2 sibling of the A3 `satd_avg` kernel, scalar because the avg+SAD
5140/// idiom auto-vectorizes and quarter-phase SAD evals are seed-frequency only).
5141#[inline]
5142fn sad_avg_strided(src: &[u8], ss: usize, a: &[u8], b: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5143    let mut sad = 0u32;
5144    for dy in 0..h {
5145        let s = &src[dy * ss..][..w];
5146        let pa = &a[dy * rs..][..w];
5147        let pb = &b[dy * rs..][..w];
5148        for i in 0..w {
5149            let p = ((pa[i] as u16 + pb[i] as u16 + 1) >> 1) as u8;
5150            sad += s[i].abs_diff(p) as u32;
5151        }
5152    }
5153    sad as i64
5154}
5155
5156fn satd_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5157    satd_px(&src[ly * stride + lx..], stride, pred, 16, 16, 16)
5158}
5159
5160/// SAD over a 16×16 luma macroblock against a prediction — the fast preset's
5161/// intra cost, kept in the same (SAD) domain as its inter cost. `Σ a.abs_diff(b)`
5162/// over `u8` slices auto-vectorizes to `psadbw`.
5163fn sad_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5164    let mut sad = 0u32;
5165    for dy in 0..16 {
5166        let s = &src[(ly + dy) * stride + lx..][..16];
5167        let p = &pred[dy * 16..][..16];
5168        sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
5169    }
5170    sad as i64
5171}
5172
5173/// SATD over an 8×8 chroma block (four 4×4 sub-blocks) against a prediction.
5174fn satd_8x8(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[u8; 64]) -> i64 {
5175    satd_px(&src[y0 * stride + x0..], stride, pred, 8, 8, 8)
5176}
5177
5178/// SATD of one 4×4 luma block against a prediction.
5179fn satd_4x4(src: &[u8], stride: usize, px: usize, py: usize, pred: &[u8; 16]) -> i64 {
5180    satd_px(&src[py * stride + px..], stride, pred, 4, 4, 4)
5181}
5182
5183/// Whether an `Intra_4x4` mode is usable given top/left neighbor availability.
5184fn i4_mode_available(mode: u8, top: bool, left: bool) -> bool {
5185    match mode {
5186        0 | 3 | 7 => top,        // vertical, diag-down-left, vertical-left
5187        1 | 8 => left,           // horizontal, horizontal-up
5188        2 => true,               // DC
5189        _ => top && left,        // diag-down-right, vertical-right, horizontal-down
5190    }
5191}
5192
5193/// Result of planning an I_4x4 macroblock (luma). Reconstruction has already
5194/// been written into the frame's `rec_y` and `coded_y` by [`plan_i4x4`].
5195struct I4Plan {
5196    modes: [u8; 16],       // per-block intra4x4 mode, raster [lby*4+lbx]
5197    q: [[i32; 16]; 16],    // per-block quantized coefficients (full, raster)
5198    cbp_luma: u32,         // 4-bit coded-block-pattern (one bit per 8×8 region)
5199    nonzero: i64,          // total non-zero coefficients (rate proxy)
5200}
5201
5202/// A fully-decided intra macroblock: the mode decision, the quantized coefficients,
5203/// and the committed reconstruction. Produced by [`plan_mb`] (which reuses the
5204/// entire mode-decision + transform + reconstruct path), then consumed by an
5205/// entropy backend — `emit_mb_cavlc` or `emit_mb_cabac` — so the two coders share
5206/// every non-entropy decision bit-for-bit (the bringup-encoder reuse guarantee).
5207struct MbPlan {
5208    use_i4: bool,
5209    // I_16x16 (when !use_i4): prediction mode, whether any AC is coded (cbp_luma=15),
5210    // luma DC levels (block order), per-4×4 quantized AC (raster).
5211    i16_mode: I16Mode,
5212    i16_cbp15: bool,
5213    i16_dc_levels: [i32; 16],
5214    i16_q: [[i32; 16]; 16],
5215    // I_4x4 (when use_i4 && i8 is None): the sub-plan, already reconstructed.
5216    i4: Option<I4Plan>,
5217    // I_8x8 (High profile; when use_i4 && i8 is Some): the sub-plan, already
5218    // reconstructed. use_i4 means "I_NxN"; i8 present disambiguates 8x8 from 4x4.
5219    i8: Option<I8Plan>,
5220    // Chroma (shared by both luma types).
5221    chroma_mode: u8,
5222    cbp_chroma: u32,
5223    c_dc_levels: [[i32; 4]; 2],
5224    c_q_blocks: [[[i32; 16]; 4]; 2],
5225}
5226
5227/// A fully-decided inter macroblock: the per-partition motion residuals, coded
5228/// block pattern, and quantized residual, with the reconstruction + motion grids
5229/// already committed. Produced by [`FrameEncoder::plan_inter_mb`] (which reuses the
5230/// whole MC + residual + reconstruct path), then coded by `emit_inter_cavlc` or
5231/// `emit_inter_cabac` — so the two entropy backends share every non-entropy
5232/// decision bit-for-bit (the P/B analogue of [`MbPlan`]).
5233struct InterPlan {
5234    mvds: [(i32, i32); 4], // per-partition mvd (P: mvd_l0; B: mvd_l0 then mvd_l1)
5235    plan_refs: [i32; 4],   // per-partition ref_idx_l0 (multi-ref P; 0 for B / single-ref)
5236    n_mvd: usize,
5237    cbp: u32,
5238    q_blocks: [[i32; 16]; 16], // luma quantized levels (raster) — used when !t8x8
5239    c_dc_levels: [[i32; 4]; 2],
5240    c_q: [[[i32; 16]; 4]; 2],
5241    t8x8: bool,           // transform_size_8x8_flag (High profile, 8x8 luma residual)
5242    q8: [[i32; 64]; 4],   // per-8x8-block quantized levels (raster) — used when t8x8
5243}
5244
5245/// Gathers the 4×4 luma intra neighbors at pixel `(px, py)` from `rec_y`.
5246fn gather_i4(
5247    fe: &FrameEncoder,
5248    px: usize,
5249    py: usize,
5250    avail_top: bool,
5251    avail_left: bool,
5252    bx: usize,
5253    by: usize,
5254) -> ([u8; 8], [u8; 4], u8) {
5255    let (cw, w4) = (fe.cw, fe.mb_w * 4);
5256    let mut top = [0u8; 8];
5257    let mut left = [0u8; 4];
5258    let mut corner = 0;
5259    if avail_top {
5260        for i in 0..4 {
5261            top[i] = fe.rec_y[(py - 1) * cw + px + i];
5262        }
5263        let tr_avail = bx + 1 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 1)];
5264        for i in 0..4 {
5265            top[4 + i] = if tr_avail {
5266                fe.rec_y[(py - 1) * cw + px + 4 + i]
5267            } else {
5268                top[3]
5269            };
5270        }
5271    }
5272    if avail_left {
5273        for i in 0..4 {
5274            left[i] = fe.rec_y[(py + i) * cw + px - 1];
5275        }
5276    }
5277    if avail_top && avail_left {
5278        corner = fe.rec_y[(py - 1) * cw + px - 1];
5279    }
5280    (top, left, corner)
5281}
5282
5283/// Plans an I_4x4 macroblock: picks a mode per 4×4 block (lowest-SATD available
5284/// mode), quantizes, and reconstructs serially into `rec_y` so each block can
5285/// predict from the previous one.
5286/// Neighbour 4x4 block intra mode for the MPM candidate: in-MB blocks read the
5287/// in-progress local `modes`; blocks in earlier MBs read `fe.modes_y`. (bx, by)
5288/// are the current block's absolute 4x4 grid coords.
5289#[inline]
5290fn modes_at(fe: &FrameEncoder, modes: &[u8; 16], lbx: usize, lby: usize, dx: isize, dy: isize, bx: usize, by: usize) -> u8 {
5291    let (nx, ny) = (lbx as isize + dx, lby as isize + dy);
5292    if (0..4).contains(&nx) && (0..4).contains(&ny) {
5293        modes[ny as usize * 4 + nx as usize]
5294    } else {
5295        let w4 = fe.mb_w * 4;
5296        let gx = (bx as isize + dx) as usize;
5297        let gy = (by as isize + dy) as usize;
5298        fe.modes_y[gy * w4 + gx]
5299    }
5300}
5301
5302fn plan_i4x4(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I4Plan {
5303    let w4 = fe.mb_w * 4;
5304    let mut modes = [2u8; 16];
5305    let mut q = [[0i32; 16]; 16];
5306    let mut cbp_luma = 0u32;
5307    let mut nonzero = 0i64;
5308
5309    for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
5310        let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
5311        let (px, py) = (bx * 4, by * 4);
5312        let avail_top = by > 0;
5313        let avail_left = bx > 0;
5314        let (top, left, corner) = gather_i4(fe, px, py, avail_top, avail_left, bx, by);
5315
5316        // Pick the lowest-SATD available mode. RUSTY_FAST_INTRA prunes the
5317        // candidate set to {MPM, DC, V, H} (x264-ultrafast-style); the H.264
5318        // predicted mode (min of left/top block modes, DC on the edge) keeps the
5319        // 1-bit prev_intra4x4_pred_mode signalling cheap for the common winner.
5320        let mut best_m = 2u8;
5321        let mut best_cost = i64::MAX;
5322        if fe.fast && fast_intra_enabled() {
5323            let lm = if bx > 0 { modes_at(fe, &modes, lbx, lby, -1, 0, bx, by) } else { 2 };
5324            let tm = if by > 0 { modes_at(fe, &modes, lbx, lby, 0, -1, bx, by) } else { 2 };
5325            let mpm = lm.min(tm);
5326            let mut cands = [mpm, 2u8, 0, 1];
5327            for i in 1..4 {
5328                for j in 0..i {
5329                    if cands[i] == cands[j] {
5330                        cands[i] = 255;
5331                    }
5332                }
5333            }
5334            for &m in cands.iter() {
5335                if m == 255 || !i4_mode_available(m, avail_top, avail_left) {
5336                    continue;
5337                }
5338                let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5339                let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5340                if cost < best_cost {
5341                    best_cost = cost;
5342                    best_m = m;
5343                }
5344            }
5345        } else {
5346            for m in 0..9u8 {
5347                if !i4_mode_available(m, avail_top, avail_left) {
5348                    continue;
5349                }
5350                let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5351                let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5352                if cost < best_cost {
5353                    best_cost = cost;
5354                    best_m = m;
5355                }
5356            }
5357        }
5358
5359        // Quantize + reconstruct with the chosen mode.
5360        let pred = intra4x4_pred(best_m, avail_top, avail_left, &top, &left, corner);
5361        let mut predb = [0i32; 16];
5362        for i in 0..16 {
5363            predb[i] = pred[i] as i32;
5364        }
5365        let res = residual(sy, fe.cw, px, py, &predb);
5366        let qb = rdoq(&forward_core(&res), qp, fe.idz, fe.rdoq_strength, 0); // full 16 incl DC
5367        let s = reconstruct_4x4(&dequantize(&qb, qp), &predb);
5368        store(&mut fe.rec_y, fe.cw, px, py, &s);
5369        fe.coded_y[by * w4 + bx] = true;
5370
5371        let nz = qb.iter().filter(|&&v| v != 0).count();
5372        if nz > 0 {
5373            cbp_luma |= 1 << ((lby / 2) * 2 + (lbx / 2));
5374        }
5375        nonzero += nz as i64;
5376        modes[lby * 4 + lbx] = best_m;
5377        q[lby * 4 + lbx] = qb;
5378    }
5379    I4Plan {
5380        modes,
5381        q,
5382        cbp_luma,
5383        nonzero,
5384    }
5385}
5386
5387/// A planned I_8x8 macroblock (High profile): one intra8x8 mode + one 8x8 DCT per
5388/// 8x8 block. Reconstructed serially into `rec_y` (each block predicts from the
5389/// previous), and `modes_y` written per block so later blocks' MPM sees earlier.
5390struct I8Plan {
5391    modes: [u8; 4],     // per-8x8-block intra8x8 mode (raster b8 0..3)
5392    q: [[i32; 64]; 4],  // per-8x8-block quantized levels (raster)
5393    cbp_luma: u32,      // 4-bit coded-block-pattern (one bit per 8x8 block)
5394    nonzero: i64,       // rate proxy
5395}
5396
5397/// Forward zig-zag scan of a raster 8x8 block: `scan[i] = raster[ZIGZAG_8X8[i]]`
5398/// (the inverse of the decoder's `un_scan_8x8`).
5399const ZIGZAG_8X8: [usize; 64] = [
5400    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, 20,
5401    13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
5402    52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
5403];
5404
5405#[inline]
5406fn scan_8x8_fwd(raster: &[i32; 64]) -> [i32; 64] {
5407    std::array::from_fn(|i| raster[ZIGZAG_8X8[i]])
5408}
5409
5410/// Gather the 8x8 intra reference samples (top[16] incl top-right, left[8], corner)
5411/// from `rec_y` — the encoder counterpart of the decoder's `gather_i8`.
5412fn gather_i8_enc(
5413    fe: &FrameEncoder,
5414    px: usize,
5415    py: usize,
5416    avail_top: bool,
5417    avail_left: bool,
5418    bx: usize,
5419    by: usize,
5420) -> ([u8; 16], [u8; 8], u8, bool) {
5421    let (cw, w4) = (fe.cw, fe.mb_w * 4);
5422    let mut top = [0u8; 16];
5423    let mut left = [0u8; 8];
5424    let mut corner = 0;
5425    if avail_top {
5426        for i in 0..8 {
5427            top[i] = fe.rec_y[(py - 1) * cw + px + i];
5428        }
5429        let tr_avail = bx + 2 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 2)];
5430        for i in 0..8 {
5431            top[8 + i] = if tr_avail {
5432                fe.rec_y[(py - 1) * cw + px + 8 + i]
5433            } else {
5434                top[7]
5435            };
5436        }
5437    }
5438    if avail_left {
5439        for i in 0..8 {
5440            left[i] = fe.rec_y[(py + i) * cw + px - 1];
5441        }
5442    }
5443    let avail_corner = avail_top && avail_left;
5444    if avail_corner {
5445        corner = fe.rec_y[(py - 1) * cw + px - 1];
5446    }
5447    (top, left, corner, avail_corner)
5448}
5449
5450/// Plans an I_8x8 macroblock: per 8x8 block, picks the lowest-SATD intra8x8 mode,
5451/// 8x8-forward-transforms + quantizes, and reconstructs serially into `rec_y`.
5452fn plan_i8x8(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I8Plan {
5453    let w4 = fe.mb_w * 4;
5454    let mut modes = [2u8; 4];
5455    let mut q = [[0i32; 64]; 4];
5456    let mut cbp_luma = 0u32;
5457    let mut nonzero = 0i64;
5458    let weight = [16i32; 64];
5459
5460    for b8 in 0..4usize {
5461        let (b8x, b8y) = (b8 % 2, b8 / 2);
5462        let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
5463        let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2); // top-left 4x4 cell
5464        let avail_top = b8y > 0 || mb_y > 0;
5465        let avail_left = b8x > 0 || mb_x > 0;
5466        let (top, left, corner, avail_corner) =
5467            gather_i8_enc(fe, px, py, avail_top, avail_left, bx, by);
5468
5469        // Mode decision: lowest-SATD available intra8x8 mode (same 9 modes / avail
5470        // rules as intra4x4). The MPM (predict_i4_mode on the top-left 4x4) keeps the
5471        // 1-bit prev-mode signalling cheap; a small penalty biases toward it.
5472        let predicted = predict_i4_mode(fe, bx, by);
5473        let mut best_m = 2u8;
5474        let mut best_cost = i64::MAX;
5475        for m in 0..9u8 {
5476            if !i4_mode_available(m, avail_top, avail_left) {
5477                continue;
5478            }
5479            let pred = intra8x8_pred(m, avail_top, avail_left, avail_corner, &top, &left, corner);
5480            let mut cost = satd_8x8(sy, fe.cw, px, py, &pred);
5481            if m != predicted {
5482                cost += 4 * fe.qp as i64; // ~mode-signal penalty (rem vs prev flag)
5483            }
5484            if cost < best_cost {
5485                best_cost = cost;
5486                best_m = m;
5487            }
5488        }
5489        modes[b8] = best_m;
5490
5491        // Forward 8x8 transform + quantize + reconstruct (shared decoder primitives).
5492        let pred = intra8x8_pred(best_m, avail_top, avail_left, avail_corner, &top, &left, corner);
5493        let mut res = [0i32; 64];
5494        for dy in 0..8 {
5495            for dx in 0..8 {
5496                res[dy * 8 + dx] =
5497                    sy[(py + dy) * fe.cw + (px + dx)] as i32 - pred[dy * 8 + dx] as i32;
5498            }
5499        }
5500        let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, fe.idz);
5501        let nz = levels.iter().filter(|&&v| v != 0).count();
5502        if nz > 0 {
5503            cbp_luma |= 1 << b8;
5504        }
5505        nonzero += nz as i64;
5506        q[b8] = levels;
5507
5508        let res_r = inverse_quant_8x8(&levels, qp, &weight);
5509        let predb: [i32; 64] = std::array::from_fn(|i| pred[i] as i32);
5510        let recon = add_residual_8x8(&res_r, &predb);
5511        for dy in 0..8 {
5512            for dx in 0..8 {
5513                fe.rec_y[(py + dy) * fe.cw + (px + dx)] = recon[dy * 8 + dx];
5514            }
5515        }
5516        // Publish the mode into all four 4x4 cells + mark coded — so the next 8x8
5517        // block's MPM (and later MBs' neighbours) see it, exactly as the decoder does.
5518        for sry in 0..2 {
5519            for srx in 0..2 {
5520                fe.modes_y[(by + sry) * w4 + (bx + srx)] = best_m;
5521                fe.coded_y[(by + sry) * w4 + (bx + srx)] = true;
5522            }
5523        }
5524    }
5525    I8Plan {
5526        modes,
5527        q,
5528        cbp_luma,
5529        nonzero,
5530    }
5531}
5532
5533/// Inter 8×8-transform luma candidate. Forward-8×8 + quantize + reconstruct each of
5534/// the four 8×8 blocks of the motion-compensated residual `(source − pred_y)`, the
5535/// pure inverse of the decoder's t8x8 inter luma path (`inv_quant8` ∘ `un_scan_8x8`
5536/// ∘ `add_residual_8x8`). Returns the quantized levels, `cbp_luma`, a LEVEL-AWARE rate
5537/// estimate (Σ `rdoq_rate(|level|)` — charges the 8×8's fewer-but-larger coeffs at
5538/// their true bit cost, not a blind count), the 256-sample reconstruction, and its
5539/// SSD vs source. Inter deadzone `dz_div = 6`; scaling list flat (16).
5540#[allow(clippy::too_many_arguments)]
5541fn plan_inter8_luma(
5542    sy: &[u8],
5543    cw: usize,
5544    mb_x: usize,
5545    mb_y: usize,
5546    pred_y: &[u8; 256],
5547    qp: u8,
5548) -> ([[i32; 64]; 4], u32, f64, [u8; 256], i64) {
5549    let weight = [16i32; 64];
5550    let mut q8 = [[0i32; 64]; 4];
5551    let mut cbp = 0u32;
5552    let mut rate = 0f64;
5553    let mut rec = [0u8; 256];
5554    let mut ssd = 0i64;
5555    for b8 in 0..4usize {
5556        let (b8x, b8y) = (b8 % 2, b8 / 2);
5557        let mut res = [0i32; 64];
5558        for dy in 0..8 {
5559            for dx in 0..8 {
5560                let sx = mb_x * 16 + b8x * 8 + dx;
5561                let syy = mb_y * 16 + b8y * 8 + dy;
5562                let p = pred_y[(b8y * 8 + dy) * 16 + (b8x * 8 + dx)] as i32;
5563                res[dy * 8 + dx] = sy[syy * cw + sx] as i32 - p;
5564            }
5565        }
5566        let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, 6);
5567        let mut nz = false;
5568        for &l in &levels {
5569            if l != 0 {
5570                nz = true;
5571                rate += rdoq_rate((l as i64).abs());
5572            }
5573        }
5574        if nz {
5575            cbp |= 1 << b8;
5576        }
5577        q8[b8] = levels;
5578
5579        let res_r = inverse_quant_8x8(&levels, qp, &weight);
5580        let predb: [i32; 64] =
5581            std::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
5582        let recon = add_residual_8x8(&res_r, &predb);
5583        for dy in 0..8 {
5584            for dx in 0..8 {
5585                let ri = (b8y * 8 + dy) * 16 + (b8x * 8 + dx);
5586                rec[ri] = recon[dy * 8 + dx];
5587                let sx = mb_x * 16 + b8x * 8 + dx;
5588                let syy = mb_y * 16 + b8y * 8 + dy;
5589                let d = recon[dy * 8 + dx] as i64 - sy[syy * cw + sx] as i64;
5590                ssd += d * d;
5591            }
5592        }
5593    }
5594    (q8, cbp, rate, rec, ssd)
5595}
5596
5597/// 16×16 luma intra prediction. For interior MBs (both neighbors available) this
5598/// dispatches to openh264's `WelsI16x16LumaPred*_sse2` (bit-identical to the spec
5599/// predictor); edge MBs (partial availability → C-only DC variants) use the scalar
5600/// path. The scalar `top`/`left`/`corner` are gathered by the caller regardless.
5601#[inline]
5602fn i16_pred(
5603    fe: &FrameEncoder,
5604    mode: I16Mode,
5605    avail_top: bool,
5606    avail_left: bool,
5607    top: &[u8; 16],
5608    left: &[u8; 16],
5609    corner: u8,
5610    lx: usize,
5611    ly: usize,
5612) -> [u8; 256] {
5613    #[cfg(accel)]
5614    if avail_top && avail_left {
5615        let mode_n = match mode {
5616            I16Mode::Vertical => 0,
5617            I16Mode::Horizontal => 1,
5618            I16Mode::Dc => 2,
5619            I16Mode::Plane => 3,
5620        };
5621        let mut p = AlignedMb([0; 256]);
5622        rusty_h264_accel::i16x16_luma_pred(mode_n, &mut p.0, &fe.rec_y[..], ly * fe.cw + lx, fe.cw);
5623        return p.0;
5624    }
5625    let _ = (fe, lx, ly);
5626    luma16x16_pred(mode, avail_top, avail_left, top, left, corner)
5627}
5628
5629/// 8×8 chroma intra prediction. Interior MBs use openh264's `WelsIChromaPred{V,Plane}_sse2`
5630/// for the V/Plane modes (bit-identical); DC/Horizontal (C-only in openh264) and edge MBs
5631/// use the scalar path.
5632#[inline]
5633#[allow(clippy::too_many_arguments)]
5634fn chroma_pred(
5635    fe: &FrameEncoder,
5636    mode: u8,
5637    avail_top: bool,
5638    avail_left: bool,
5639    c: usize,
5640    top: &[u8; 8],
5641    left: &[u8; 8],
5642    corner: u8,
5643    cx: usize,
5644    cy: usize,
5645) -> [u8; 64] {
5646    #[cfg(accel)]
5647    if avail_top && avail_left && (mode == 2 || mode == 3) {
5648        let plane = if c == 0 { &fe.rec_u } else { &fe.rec_v };
5649        let mut p = AlignedMb([0; 256]);
5650        rusty_h264_accel::chroma8x8_pred(mode, &mut p.0[..64], &plane[..], cy * fe.ccw + cx, fe.ccw);
5651        let mut out = [0u8; 64];
5652        out.copy_from_slice(&p.0[..64]);
5653        return out;
5654    }
5655    let _ = (fe, c, cx, cy);
5656    chroma8x8_pred(mode, avail_top, avail_left, top, left, corner)
5657}
5658
5659/// Predicted `Intra_4x4` mode for the block at absolute coords `(bx, by)` —
5660/// `min` of the left/top neighbor modes, or DC if either is unavailable.
5661fn predict_i4_mode(fe: &FrameEncoder, bx: usize, by: usize) -> u8 {
5662    if bx == 0 || by == 0 {
5663        return 2;
5664    }
5665    let w4 = fe.mb_w * 4;
5666    fe.modes_y[by * w4 + (bx - 1)].min(fe.modes_y[(by - 1) * w4 + bx])
5667}
5668
5669#[allow(clippy::too_many_arguments)]
5670/// Zig-zag scan: block (raster 4×4) index at scan position i.
5671const RDOQ_ZZ: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
5672
5673/// Approximate CABAC bit cost of coding one residual coefficient at magnitude
5674/// `level`: significant_coeff_flag (~1) + coeff_abs_level_minus1 bins (gt1 + UEG0)
5675/// + sign (~1); `level == 0` is significant_coeff_flag = 0 (~1). A coarse model —
5676/// the transform-norm/bin-to-bit scaling is absorbed into the calibrated strength.
5677#[inline]
5678fn rdoq_rate(level: i64) -> f64 {
5679    if level == 0 {
5680        1.0
5681    } else if level == 1 {
5682        3.0 // sig(1) + gt1=0 (1) + sign(1)
5683    } else {
5684        // sig(1) + gt1=1 (1) + UEG0(level-2) prefix (~level-1, capped) + sign(1)
5685        3.0 + (level - 1).min(13) as f64
5686    }
5687}
5688
5689/// Rate-distortion optimized quantization (CABAC trellis, RDOQ) for one 4×4 residual
5690/// block. Refines the hard-decision levels toward min over {|q|, |q|-1} of
5691/// `SSD_coef + λ·R_cabac` per coefficient (coefficient-domain distortion
5692/// `(|coeff| - level·deq_step)²`; `λ = strength·2^((qp-12)/3)`). `strength == 0`
5693/// returns the hard quantization unchanged (the CAVLC path). `first` = 1 skips the
5694/// DC (AC-only categories: I_16x16 AC, chroma AC), else 0.
5695fn rdoq(coeffs: &[i32; 16], qp: u8, dz_div: i64, strength: f64, first: usize) -> [i32; 16] {
5696    let mut q = quantize(coeffs, qp, dz_div);
5697    if strength <= 0.0 {
5698        return q;
5699    }
5700    let lambda = strength * 2f64.powf((qp as f64 - 12.0) / 3.0);
5701    // Distortion is measured in the QUANTIZER-INPUT (forward-transform) domain, where
5702    // level L reconstructs to L·qstep, qstep = 2^16 / MF (the inverse of the forward
5703    // quant scale). The transform norm (forward↔pixel) folds into `strength`.
5704    let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
5705    const POS: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7];
5706    let dist = |p: usize, level: i64| -> f64 {
5707        let e = coeffs[p].unsigned_abs() as f64 - level as f64 * (65536.0 / mf[POS[p]] as f64);
5708        e * e
5709    };
5710    // Pass 1: per-coefficient level lowering (|q| → |q|-1) minimizing D + λ·R.
5711    for i in first..16 {
5712        let p = RDOQ_ZZ[i];
5713        let m = q[p].unsigned_abs() as i64;
5714        if m == 0 {
5715            continue;
5716        }
5717        let j_keep = dist(p, m) + lambda * rdoq_rate(m);
5718        let j_down = dist(p, m - 1) + lambda * rdoq_rate(m - 1);
5719        if j_down < j_keep {
5720            let nl = (m - 1) as i32;
5721            q[p] = if q[p] < 0 { -nl } else { nl };
5722        }
5723    }
5724    // Pass 2: last-significant-position trimming. Zeroing the trailing significant
5725    // coefficient frees its own bits AND the last_significant flag + every sig=0 flag
5726    // between it and the previous significant coefficient (positions past the new last
5727    // aren't coded at all) — the dominant RDOQ gain on sparse (inter) residuals.
5728    loop {
5729        let Some(li) = (first..16).rev().find(|&i| q[RDOQ_ZZ[i]] != 0) else {
5730            break;
5731        };
5732        let p = RDOQ_ZZ[li];
5733        let m = q[p].unsigned_abs() as i64;
5734        let prev = (first..li).rev().find(|&i| q[RDOQ_ZZ[i]] != 0);
5735        let base = prev.map_or(first, |j| j + 1);
5736        let bits = rdoq_rate(m) + 1.0 + (li - base) as f64; // coeff + last-flag + freed sig=0
5737        let d_add = dist(p, 0) - dist(p, m);
5738        if d_add < lambda * bits {
5739            q[p] = 0;
5740        } else {
5741            break;
5742        }
5743    }
5744    q
5745}
5746
5747/// Decide one intra macroblock (I_16x16 vs I_4x4, prediction modes, chroma),
5748/// forward-transform + quantize, and commit the reconstruction + neighbour mode
5749/// state — everything except entropy coding. The returned [`MbPlan`] is coded by
5750/// either entropy backend, so CAVLC and CABAC share this whole path bit-for-bit.
5751fn plan_mb(
5752    fe: &mut FrameEncoder,
5753    mb_x: usize,
5754    mb_y: usize,
5755    sy: &[u8],
5756    su: &[u8],
5757    sv: &[u8],
5758) -> MbPlan {
5759    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCode);
5760    let qp = fe.qp;
5761    let qpc = fe.qpc;
5762    // Lagrangian λ for rate-distortion decisions (standard H.264 form).
5763    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
5764
5765    // ---------------- luma ----------------
5766    let (lx, ly) = (mb_x * 16, mb_y * 16);
5767    let avail_top = mb_y > 0;
5768    let avail_left = mb_x > 0;
5769    let mut top = [0u8; 16];
5770    let mut left = [0u8; 16];
5771    if avail_top {
5772        for i in 0..16 {
5773            top[i] = fe.rec_y[(ly - 1) * fe.cw + lx + i];
5774        }
5775    }
5776    if avail_left {
5777        for i in 0..16 {
5778            left[i] = fe.rec_y[(ly + i) * fe.cw + lx - 1];
5779        }
5780    }
5781    let corner = if avail_top && avail_left {
5782        fe.rec_y[(ly - 1) * fe.cw + lx - 1]
5783    } else {
5784        0
5785    };
5786
5787    let w4 = fe.mb_w * 4;
5788
5789    // ============ I_16x16 plan (reconstruct into a local buffer) ============
5790    let mut i16_mode = I16Mode::Dc;
5791    let mut best_pred = i16_pred(fe, I16Mode::Dc, avail_top, avail_left, &top, &left, corner, lx, ly);
5792    let mut best_cost = satd_16x16(sy, fe.cw, lx, ly, &best_pred);
5793    for mode in [I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
5794        if !mode.available(avail_top, avail_left) {
5795            continue;
5796        }
5797        let pred = i16_pred(fe, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
5798        let cost = satd_16x16(sy, fe.cw, lx, ly, &pred);
5799        if cost < best_cost {
5800            best_cost = cost;
5801            i16_mode = mode;
5802            best_pred = pred;
5803        }
5804    }
5805    // I_16x16 blocks are independent (one fixed whole-MB prediction), so batch the
5806    // forward DCT (`forward_dct_blocks` → SIMD), bit-identical to `forward_core`.
5807    let mut dc4x4 = [0i32; 16];
5808    let mut i16_q = [[0i32; 16]; 16];
5809    // Fast path: forward DCT of (src - pred) straight from the planes per 8x8 quad,
5810    // quantize with the identical FF/MF math (deadzone = fe.idz), recon via the
5811    // bit-identical idct+add+clip kernel — the same pairing encode_inter_mb and the
5812    // P_Skip free-check already use, byte-identical to the scalar twin below.
5813    #[cfg(accel)]
5814    let (i16_dc_levels, _i16_recon_dc, recon16) = {
5815        #[repr(align(16))]
5816        struct A([i16; 256]);
5817        let mut dct = A([0i16; 256]);
5818        let base = ly * fe.cw + lx;
5819        for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
5820            rusty_h264_accel::dct_four_t4(
5821                &mut dct.0[qi * 64..qi * 64 + 64],
5822                &sy[base + qy * fe.cw + qx..],
5823                fe.cw,
5824                &best_pred[qy * 16 + qx..],
5825                16,
5826            );
5827        }
5828        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5829            dc4x4[lby * 4 + lbx] = dct.0[blk * 16] as i32;
5830        }
5831        if fe.rdoq_strength > 0.0 {
5832            // Trellis (all-intra only): scalar RDOQ from the asm DCT output instead of
5833            // the asm hard quantizer. dct.0 keeps the raw DCT here; the recon loop below
5834            // overwrites it with the dequantized RDOQ levels.
5835            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5836                let coeffs: [i32; 16] = std::array::from_fn(|i| dct.0[blk * 16 + i] as i32);
5837                let mut q = rdoq(&coeffs, qp, fe.idz, fe.rdoq_strength, 1);
5838                q[0] = 0;
5839                i16_q[lby * 4 + lbx] = q;
5840            }
5841        } else {
5842            let ff = rusty_h264_common::transform::quant_dz_ff(qp, fe.idz);
5843            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
5844            for qi in 0..4 {
5845                rusty_h264_accel::quant_four_4x4(&mut dct.0[qi * 64..qi * 64 + 64], &ff, mf);
5846            }
5847            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5848                let q = &mut i16_q[lby * 4 + lbx];
5849                q[0] = 0;
5850                for i in 1..16 {
5851                    q[i] = dct.0[blk * 16 + i] as i32;
5852                }
5853            }
5854        }
5855        let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
5856        let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
5857        // Recon: dequantize (DC injected from the Hadamard) back into quad layout,
5858        // then idct+add-pred+clip into the trial buffer.
5859        let mut recon16 = [0u8; 256];
5860        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5861            let mut deq = dequantize(&i16_q[lby * 4 + lbx], qp);
5862            deq[0] = i16_recon_dc[lby * 4 + lbx];
5863            for i in 0..16 {
5864                dct.0[blk * 16 + i] = deq[i] as i16;
5865            }
5866        }
5867        for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
5868            rusty_h264_accel::idct_four_t4_rec(
5869                &mut recon16[qy * 16 + qx..],
5870                16,
5871                &best_pred[qy * 16 + qx..],
5872                16,
5873                &dct.0[qi * 64..qi * 64 + 64],
5874            );
5875        }
5876        (i16_dc_levels, i16_recon_dc, recon16)
5877    };
5878    #[cfg(not(accel))]
5879    let (i16_dc_levels, _i16_recon_dc, recon16) = {
5880        let mut res_blocks = [[0i32; 16]; 16];
5881        for by in 0..4 {
5882            for bx in 0..4 {
5883                let predb = pred_block(&best_pred, bx, by);
5884                res_blocks[by * 4 + bx] = residual(sy, fe.cw, lx + bx * 4, ly + by * 4, &predb);
5885            }
5886        }
5887        let mut coeffs = [[0i32; 16]; 16];
5888        forward_dct_blocks(&res_blocks, &mut coeffs);
5889        for i in 0..16 {
5890            dc4x4[i] = coeffs[i][0];
5891            let mut q = rdoq(&coeffs[i], qp, fe.idz, fe.rdoq_strength, 1);
5892            q[0] = 0;
5893            i16_q[i] = q;
5894        }
5895        let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
5896        let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
5897        let mut recon16 = [0u8; 256];
5898        let mut deq_blocks = [[0i32; 16]; 16];
5899        for i in 0..16 {
5900            deq_blocks[i] = dequantize(&i16_q[i], qp);
5901            deq_blocks[i][0] = i16_recon_dc[i];
5902        }
5903        let mut idct = [[0i32; 16]; 16];
5904        inverse_dct_blocks(&deq_blocks, &mut idct);
5905        for by in 0..4 {
5906            for bx in 0..4 {
5907                let s = add_residual_4x4(&idct[by * 4 + bx], &pred_block(&best_pred, bx, by));
5908                for dy in 0..4 {
5909                    for dx in 0..4 {
5910                        recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)] = s[dy * 4 + dx];
5911                    }
5912                }
5913            }
5914        }
5915        (i16_dc_levels, i16_recon_dc, recon16)
5916    };
5917    let i16_cbp15 = i16_q.iter().any(|b| b[1..].iter().any(|&c| c != 0));
5918    let i16_dc_nz = i16_dc_levels.iter().filter(|&&v| v != 0).count() as i64;
5919    let i16_ac_nz: i64 = i16_q
5920        .iter()
5921        .map(|b| b[1..].iter().filter(|&&v| v != 0).count() as i64)
5922        .sum();
5923    // I_16x16 AC is all-or-nothing: any AC ⇒ all 16 blocks pay a coeff_token.
5924    let i16_rate = i16_dc_nz + i16_ac_nz + if i16_cbp15 { 16 } else { 0 };
5925    // Reconstruction distortion (SSD) for the rate-distortion decision.
5926    let mut ssd16 = 0i64;
5927    for dy in 0..16 {
5928        for dx in 0..16 {
5929            let d = recon16[dy * 16 + dx] as i64 - sy[(ly + dy) * fe.cw + (lx + dx)] as i64;
5930            ssd16 += d * d;
5931        }
5932    }
5933
5934    // ============ chroma (shared by both luma types; commit immediately) ============
5935    let (cx, cy) = (mb_x * 8, mb_y * 8);
5936    // Gather both components' neighbors, then pick a chroma mode by combined SATD.
5937    let mut ntop = [[0u8; 8]; 2];
5938    let mut nleft = [[0u8; 8]; 2];
5939    let mut ncorner = [0u8; 2];
5940    for c in 0..2 {
5941        let rec_c = if c == 0 { &fe.rec_u } else { &fe.rec_v };
5942        if avail_top {
5943            for i in 0..8 {
5944                ntop[c][i] = rec_c[(cy - 1) * fe.ccw + cx + i];
5945            }
5946        }
5947        if avail_left {
5948            for i in 0..8 {
5949                nleft[c][i] = rec_c[(cy + i) * fe.ccw + cx - 1];
5950            }
5951        }
5952        if avail_top && avail_left {
5953            ncorner[c] = rec_c[(cy - 1) * fe.ccw + cx - 1];
5954        }
5955    }
5956    let mut chroma_mode = 0u8;
5957    let mut best_c_cost = i64::MAX;
5958    for m in 0..4u8 {
5959        if !chroma_mode_available(m, avail_top, avail_left) {
5960            continue;
5961        }
5962        let mut cost = 0i64;
5963        for c in 0..2 {
5964            let src = if c == 0 { su } else { sv };
5965            let pred8 = chroma_pred(fe, m, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
5966            cost += satd_8x8(src, fe.ccw, cx, cy, &pred8);
5967        }
5968        if cost < best_c_cost {
5969            best_c_cost = cost;
5970            chroma_mode = m;
5971        }
5972    }
5973
5974    let mut c_dc_levels = [[0i32; 4]; 2];
5975    let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
5976    let mut any_chroma_ac = false;
5977    let mut any_chroma_dc = false;
5978    for c in 0..2 {
5979        let src = if c == 0 { su } else { sv };
5980        let pred8 =
5981            chroma_pred(fe, chroma_mode, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
5982        let pblk = |bx: usize, by: usize| -> [i32; 16] {
5983            let mut predb = [0i32; 16];
5984            for dy in 0..4 {
5985                for dx in 0..4 {
5986                    predb[dy * 4 + dx] = pred8[(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
5987                }
5988            }
5989            predb
5990        };
5991        // Fast path: forward DCT of (src - pred8) straight from the planes, quantize
5992        // with identical FF/MF (idz deadzone), recon via one idct+add+clip kernel —
5993        // bit-identical to the scalar twin below (proven kernel pairings).
5994        let mut dc2x2 = [0i32; 4];
5995        let mut qbs = [[0i32; 16]; 4];
5996        #[cfg(accel)]
5997        let recon_dc = {
5998            #[repr(align(16))]
5999            struct A([i16; 64]);
6000            let mut d = A([0i16; 64]);
6001            rusty_h264_accel::dct_four_t4(&mut d.0, &src[cy * fe.ccw + cx..], fe.ccw, &pred8, 8);
6002            for i in 0..4 {
6003                dc2x2[i] = d.0[i * 16] as i32;
6004            }
6005            if fe.rdoq_strength > 0.0 {
6006                // Trellis (all-intra only): scalar RDOQ from the asm chroma DCT.
6007                for i in 0..4 {
6008                    let coeffs: [i32; 16] = std::array::from_fn(|j| d.0[i * 16 + j] as i32);
6009                    let mut q = rdoq(&coeffs, qpc, fe.idz, fe.rdoq_strength, 1);
6010                    q[0] = 0;
6011                    if q[1..].iter().any(|&v| v != 0) {
6012                        any_chroma_ac = true;
6013                    }
6014                    qbs[i] = q;
6015                }
6016            } else {
6017                let ff = rusty_h264_common::transform::quant_dz_ff(qpc, fe.idz);
6018                let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
6019                rusty_h264_accel::quant_four_4x4(&mut d.0, &ff, mf);
6020                for i in 0..4 {
6021                    let q = &mut qbs[i];
6022                    q[0] = 0;
6023                    for j in 1..16 {
6024                        let v = d.0[i * 16 + j] as i32;
6025                        q[j] = v;
6026                        if v != 0 {
6027                            any_chroma_ac = true;
6028                        }
6029                    }
6030                }
6031            }
6032            let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6033            if dl.iter().any(|&v| v != 0) {
6034                any_chroma_dc = true;
6035            }
6036            let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6037            for i in 0..4 {
6038                let deq = dequantize(&qbs[i], qpc);
6039                for j in 0..16 {
6040                    d.0[i * 16 + j] = deq[j] as i16;
6041                }
6042                d.0[i * 16] = recon_dc[i] as i16;
6043            }
6044            let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6045            rusty_h264_accel::idct_four_t4_rec(&mut plane[cy * fe.ccw + cx..], fe.ccw, &pred8, 8, &d.0);
6046            c_dc_levels[c] = dl;
6047            recon_dc
6048        };
6049        #[cfg(not(accel))]
6050        let recon_dc = {
6051            let mut res_blocks = [[0i32; 16]; 4];
6052            for by in 0..2 {
6053                for bx in 0..2 {
6054                    res_blocks[by * 2 + bx] =
6055                        residual(src, fe.ccw, cx + bx * 4, cy + by * 4, &pblk(bx, by));
6056                }
6057            }
6058            let mut coeffs = [[0i32; 16]; 4];
6059            forward_dct_blocks(&res_blocks, &mut coeffs);
6060            for i in 0..4 {
6061                dc2x2[i] = coeffs[i][0];
6062                let mut q = rdoq(&coeffs[i], qpc, fe.idz, fe.rdoq_strength, 1);
6063                q[0] = 0;
6064                qbs[i] = q;
6065                if q[1..].iter().any(|&v| v != 0) {
6066                    any_chroma_ac = true;
6067                }
6068            }
6069            let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6070            if dl.iter().any(|&v| v != 0) {
6071                any_chroma_dc = true;
6072            }
6073            let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6074            let mut deq_blocks = [[0i32; 16]; 4];
6075            for i in 0..4 {
6076                deq_blocks[i] = dequantize(&qbs[i], qpc);
6077                deq_blocks[i][0] = recon_dc[i];
6078            }
6079            let mut idct = [[0i32; 16]; 4];
6080            inverse_dct_blocks(&deq_blocks, &mut idct);
6081            let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6082            for by in 0..2 {
6083                for bx in 0..2 {
6084                    let s = add_residual_4x4(&idct[by * 2 + bx], &pblk(bx, by));
6085                    store(plane, fe.ccw, cx + bx * 4, cy + by * 4, &s);
6086                }
6087            }
6088            c_dc_levels[c] = dl;
6089            recon_dc
6090        };
6091        let _ = recon_dc;
6092        c_q_blocks[c] = qbs;
6093    }
6094    let cbp_chroma: u32 = if any_chroma_ac {
6095        2
6096    } else if any_chroma_dc {
6097        1
6098    } else {
6099        0
6100    };
6101
6102    // ============ I_NxN plan + RD: I_16x16 vs I_4x4 vs (High profile) I_8x8 ============
6103    // I_4x4 and I_8x8 both reconstruct serially into rec_y, but each block predicts
6104    // only from NEIGHBOURS + earlier blocks it fills itself — never the stale MB
6105    // content — so running I_8x8 after I_4x4 needs no restore. J = SSD + λ·R picks the
6106    // per-MB transform (the content-adaptive win: 8x8 on smooth, 4x4 on detail).
6107    let base = ly * fe.cw + lx;
6108    let i4 = if i16_rate > 2 {
6109        Some(plan_i4x4(fe, sy, mb_x, mb_y, qp))
6110    } else {
6111        None
6112    };
6113    let (j4, i4_recon) = match &i4 {
6114        Some(p) => {
6115            let mut ssd = 0i64;
6116            let mut rec = [0u8; 256];
6117            for i in 0..256 {
6118                let v = fe.rec_y[base + (i / 16) * fe.cw + i % 16];
6119                rec[i] = v;
6120                let d = v as i64 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6121                ssd += d * d;
6122            }
6123            (ssd as f64 + lambda * (p.nonzero + 16) as f64, Some(rec))
6124        }
6125        None => (f64::INFINITY, None),
6126    };
6127    let i8 = if fe.transform_8x8 {
6128        Some(plan_i8x8(fe, sy, mb_x, mb_y, qp))
6129    } else {
6130        None
6131    };
6132    let j8 = match &i8 {
6133        Some(p) => {
6134            let mut ssd = 0i64;
6135            for i in 0..256 {
6136                let d = fe.rec_y[base + (i / 16) * fe.cw + i % 16] as i64
6137                    - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6138                ssd += d * d;
6139            }
6140            ssd as f64 + lambda * (p.nonzero + 16) as f64
6141        }
6142        None => f64::INFINITY,
6143    };
6144    let j16 = ssd16 as f64 + lambda * i16_rate as f64;
6145
6146    // ============ commit the RD winner's reconstruction + neighbour modes ============
6147    let (use_i4, i4, i8) = if i8.is_some() && j8 <= j4 && j8 <= j16 {
6148        // I_8x8: plan_i8x8 already committed rec_y AND modes_y (per 8x8 block).
6149        (true, None, i8)
6150    } else if i4.is_some() && j4 < j16 {
6151        // I_4x4: restore its reconstruction (I_8x8 may have overwritten rec_y), publish modes.
6152        let rec = i4_recon.unwrap();
6153        for i in 0..256 {
6154            fe.rec_y[base + (i / 16) * fe.cw + i % 16] = rec[i];
6155        }
6156        let modes = i4.as_ref().unwrap().modes;
6157        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6158            fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = modes[lby * 4 + lbx];
6159        }
6160        (true, i4, None)
6161    } else {
6162        // I_16x16: commit its reconstruction, mark modes DC.
6163        for by in 0..4 {
6164            for bx in 0..4 {
6165                for dy in 0..4 {
6166                    for dx in 0..4 {
6167                        fe.rec_y[(ly + by * 4 + dy) * fe.cw + (lx + bx * 4 + dx)] =
6168                            recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)];
6169                    }
6170                }
6171            }
6172        }
6173        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6174            fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
6175        }
6176        (false, None, None)
6177    };
6178    // Mark all luma blocks coded for the next macroblock's top-right availability.
6179    for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6180        fe.coded_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = true;
6181    }
6182
6183    MbPlan {
6184        use_i4,
6185        i16_mode,
6186        i16_cbp15,
6187        i16_dc_levels,
6188        i16_q,
6189        i4,
6190        i8,
6191        chroma_mode,
6192        cbp_chroma,
6193        c_dc_levels,
6194        c_q_blocks,
6195    }
6196}
6197
6198/// Emit one planned intra macroblock as CAVLC (the original `encode_mb` tail). Reads
6199/// only the decided values from `plan`; `plan_mb` already committed recon + modes.
6200fn encode_mb(
6201    fe: &mut FrameEncoder,
6202    w: &mut BitWriter,
6203    mb_x: usize,
6204    mb_y: usize,
6205    sy: &[u8],
6206    su: &[u8],
6207    sv: &[u8],
6208    is_p: bool,
6209) {
6210    let plan = plan_mb(fe, mb_x, mb_y, sy, su, sv);
6211    // In a P-slice, intra macroblock types are offset by 5 (0..4 are inter).
6212    let mb_type_offset = if is_p { 5 } else { 0 };
6213    let w4 = fe.mb_w * 4;
6214    let cbp_chroma = plan.cbp_chroma;
6215
6216    // ============ emit luma ============
6217    if let Some(i8) = plan.i8.as_ref().filter(|_| plan.use_i4) {
6218        // ---- I_8x8 (High profile): mb_type = I_NxN, transform_size_8x8_flag = 1, then
6219        // one intra8x8 mode per 8x8 block, cbp, mb_qp_delta, and the 8x8 residual as
6220        // four interleaved 4x4 CAVLC sub-blocks (coeff k of sub s -> 8x8 scan 4k+s). ----
6221        let cbp = i8.cbp_luma | (cbp_chroma << 4);
6222        w.write_ue(mb_type_offset); // mb_type = I_NxN
6223        w.write_bit(true); // transform_size_8x8_flag = 1
6224        for b8 in 0..4usize {
6225            let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
6226            let predicted = predict_i4_mode(fe, bx, by);
6227            let actual = i8.modes[b8];
6228            if actual == predicted {
6229                w.write_bit(true);
6230            } else {
6231                w.write_bit(false);
6232                let rem = if actual < predicted { actual } else { actual - 1 };
6233                w.write_bits(rem as u32, 3);
6234            }
6235        }
6236        w.write_ue(plan.chroma_mode as u32); // intra_chroma_pred_mode
6237        write_cbp_intra(w, cbp);
6238        if cbp != 0 {
6239            w.write_se(fe.qp_delta());
6240        }
6241        fe.nnz_cache_load(mb_x, mb_y);
6242        for b8 in 0..4usize {
6243            let (b8x, b8y) = (b8 % 2, b8 / 2);
6244            let scan8 = scan_8x8_fwd(&i8.q[b8]);
6245            for sub in 0..4usize {
6246                let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
6247                let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
6248                let total = if i8.cbp_luma & (1 << b8) != 0 {
6249                    let nc = fe.nc_pred(cx, cy);
6250                    let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
6251                    encode_residual_block(w, &blk, 16, nc) as u8
6252                } else {
6253                    0
6254                };
6255                fe.nnz_cache_set(cx, cy, total);
6256                fe.nnz_y[by * w4 + bx] = total;
6257            }
6258        }
6259    } else if plan.use_i4 {
6260        let i4 = plan.i4.as_ref().unwrap();
6261        let cbp = i4.cbp_luma | (cbp_chroma << 4);
6262        w.write_ue(mb_type_offset); // mb_type = I_4x4 (+5 in P-slices)
6263        if fe.transform_8x8 {
6264            w.write_bit(false); // transform_size_8x8_flag = 0 (this I_NxN is 4x4)
6265        }
6266        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6267            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6268            let predicted = predict_i4_mode(fe, bx, by);
6269            let actual = i4.modes[lby * 4 + lbx];
6270            if actual == predicted {
6271                w.write_bit(true);
6272            } else {
6273                w.write_bit(false);
6274                let rem = if actual < predicted { actual } else { actual - 1 };
6275                w.write_bits(rem as u32, 3);
6276            }
6277        }
6278        w.write_ue(plan.chroma_mode as u32); // intra_chroma_pred_mode
6279        write_cbp_intra(w, cbp);
6280        if cbp != 0 {
6281            w.write_se(fe.qp_delta()); // mb_qp_delta (AQ per-MB QPy)
6282        }
6283        fe.nnz_cache_load(mb_x, mb_y);
6284        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6285            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6286            let total = if i4.cbp_luma & (1 << (blk / 4)) != 0 {
6287                let nc = fe.nc_pred(lbx, lby);
6288                let scan16 = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
6289                encode_residual_block(w, &scan16, 16, nc) as u8
6290            } else {
6291                0
6292            };
6293            fe.nnz_cache_set(lbx, lby, total);
6294            fe.nnz_y[by * w4 + bx] = total;
6295        }
6296    } else {
6297        let mb_type = 1 + plan.i16_mode as u32 + 4 * cbp_chroma + if plan.i16_cbp15 { 12 } else { 0 };
6298        w.write_ue(mb_type + mb_type_offset);
6299        w.write_ue(plan.chroma_mode as u32); // intra_chroma_pred_mode
6300        w.write_se(fe.qp_delta()); // mb_qp_delta (I_16x16 always codes it; AQ per-MB QPy)
6301        fe.nnz_cache_load(mb_x, mb_y);
6302        let nc_dc = fe.nc_pred(0, 0);
6303        let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
6304        encode_residual_block(w, &dc_scan, 16, nc_dc);
6305        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6306            fe.nnz_cache_set(lbx, lby, 0);
6307            fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
6308        }
6309        if plan.i16_cbp15 {
6310            for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6311                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6312                let nc = fe.nc_pred(lbx, lby);
6313                let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
6314                let total = encode_residual_block(w, &ac, 15, nc) as u8;
6315                fe.nnz_cache_set(lbx, lby, total);
6316                fe.nnz_y[by * w4 + bx] = total;
6317            }
6318        }
6319    }
6320
6321    // ============ emit chroma residual (shared) ============
6322    if cbp_chroma != 0 {
6323        for c in 0..2 {
6324            encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
6325        }
6326    }
6327    if cbp_chroma == 2 {
6328        fe.chroma_cache_load(mb_x, mb_y);
6329        let w2 = fe.mb_w * 2;
6330        for c in 0..2 {
6331            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
6332                let nc = fe.chroma_nc_pred(c, bx, by);
6333                let ac = scan_4x4_ac(&plan.c_q_blocks[c][by * 2 + bx]);
6334                let total = encode_residual_block(w, &ac, 15, nc) as u8;
6335                fe.chroma_nnz_cache_set(c, bx, by, total);
6336                fe.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
6337            }
6338        }
6339    }
6340}
6341
6342// ============================================================================
6343// CABAC I-slice entropy coding — the exact forward inverse of the decoder's
6344// `decode_slice_data_cabac` I-slice path (rusty_h264-decoder mb16.rs). Every
6345// binarization + context-selection here mirrors a `parse_*_cabac` there; the
6346// neighbour state (nzc cache, cbf_dc, cat, cmode, mb_cbp, last_delta_qp) is
6347// reconstructed identically so the contexts evolve bit-for-bit. Reuses `plan_mb`
6348// for the entire mode-decision/transform/recon (shared with CAVLC).
6349// ============================================================================
6350
6351// --- res-property tables (must match the decoder's mb16.rs g_kBlockCat2CtxOffset*) ---
6352const CB_NZC_CACHE: [usize; 24] = [
6353    9, 10, 17, 18, 11, 12, 19, 20, 25, 26, 33, 34, 27, 28, 35, 36, // luma
6354    14, 15, 22, 23, // Cb
6355    38, 39, 46, 47, // Cr
6356];
6357const CB_RES_MAXPOS: [i32; 11] = [0, 15, 14, 15, 3, 14, 63, 3, 3, 14, 14];
6358const CB_RES_MAXC2: [i32; 11] = [0, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4];
6359const CB_RES_CBF: [usize; 11] = [0, 0, 4, 8, 12, 16, 0, 12, 12, 16, 16];
6360const CB_RES_MAP: [usize; 11] = [0, 0, 15, 29, 44, 47, 0, 44, 44, 47, 47];
6361const CB_RES_ONE: [usize; 11] = [0, 0, 10, 20, 30, 39, 0, 30, 30, 39, 39];
6362const CB_RP_I16_DC: usize = 1;
6363const CB_RP_I16_AC: usize = 2;
6364const CB_RP_LUMA_4X4: usize = 3;
6365const CB_RP_CHROMA_DC: usize = 7;
6366const CB_RP_CHROMA_AC: usize = 9;
6367
6368/// Inverse of `cabac_unary(ctx, off)`: bin0 at `ctx`; for value >= 1, `value-1` ones
6369/// then a terminating 0, all at `ctx+off`.
6370fn cb_unary(cab: &mut CabacEncoder, ctx: usize, off: usize, value: u32) {
6371    if value == 0 {
6372        cab.encode_decision(ctx, 0);
6373        return;
6374    }
6375    cab.encode_decision(ctx, 1);
6376    for _ in 0..value - 1 {
6377        cab.encode_decision(ctx + off, 1);
6378    }
6379    cab.encode_decision(ctx + off, 0);
6380}
6381
6382/// Exp-Golomb order-`k` in bypass — inverse of `cabac_exp_bypass(k)`.
6383fn cb_exp_bypass(cab: &mut CabacEncoder, mut k: i32, mut n: u32) {
6384    while n >= (1 << k) {
6385        cab.encode_bypass(1);
6386        n -= 1 << k;
6387        k += 1;
6388    }
6389    cab.encode_bypass(0);
6390    while k > 0 {
6391        k -= 1;
6392        cab.encode_bypass((n >> k) & 1);
6393    }
6394}
6395
6396/// UEG0 coeff-level suffix — inverse of `cabac_ueg_level(ctx)` (TU prefix <=13 at
6397/// `ctx`, then an EG0 bypass suffix).
6398fn cb_ueg_level(cab: &mut CabacEncoder, ctx: usize, value: u32) {
6399    if value == 0 {
6400        cab.encode_decision(ctx, 0);
6401        return;
6402    }
6403    let ones = value.min(13);
6404    for _ in 0..ones {
6405        cab.encode_decision(ctx, 1);
6406    }
6407    if value < 13 {
6408        cab.encode_decision(ctx, 0);
6409    } else {
6410        cb_exp_bypass(cab, 0, value - 13);
6411    }
6412}
6413
6414/// `mb_qp_delta` — inverse of `parse_mb_qp_delta_cabac` (ctxIdxOffset 60).
6415fn cb_mb_qp_delta(cab: &mut CabacEncoder, last_delta_qp: &mut i32, delta: i32) {
6416    const O: usize = 60;
6417    let ctx_inc = (*last_delta_qp != 0) as usize;
6418    if delta == 0 {
6419        cab.encode_decision(O + ctx_inc, 0);
6420    } else {
6421        cab.encode_decision(O + ctx_inc, 1);
6422        // code = 2|d| - (d>0); the decode's cabac_unary sees code-1.
6423        let code = 2 * delta.unsigned_abs() - (delta > 0) as u32;
6424        cb_unary(cab, O + 2, 1, code - 1);
6425    }
6426    *last_delta_qp = delta;
6427}
6428
6429/// `intra_chroma_pred_mode` (TU cMax=3) — inverse of `parse_intra_chroma_pred_mode_cabac`.
6430fn cb_chroma_pred_mode(cab: &mut CabacEncoder, ctx_inc: usize, mode: u8) {
6431    const C: usize = 64;
6432    if mode == 0 {
6433        cab.encode_decision(C + ctx_inc, 0);
6434        return;
6435    }
6436    cab.encode_decision(C + ctx_inc, 1);
6437    if mode == 1 {
6438        cab.encode_decision(C + 3, 0);
6439    } else if mode == 2 {
6440        cab.encode_decision(C + 3, 1);
6441        cab.encode_decision(C + 3, 0);
6442    } else {
6443        cab.encode_decision(C + 3, 1);
6444        cab.encode_decision(C + 3, 1);
6445    }
6446}
6447
6448/// I-slice `mb_type` — inverse of `parse_mb_type_i_cabac` (ctxIdxOffset 3).
6449fn cb_mb_type_i(
6450    cab: &mut CabacEncoder,
6451    ctx_inc: usize,
6452    use_i4: bool,
6453    i16_mode: u32,
6454    cbp_chroma: u32,
6455    cbp_luma15: bool,
6456) {
6457    const O: usize = 3;
6458    if use_i4 {
6459        cab.encode_decision(O + ctx_inc, 0); // I_NxN
6460        return;
6461    }
6462    cab.encode_decision(O + ctx_inc, 1);
6463    cab.encode_terminate(false); // not I_PCM
6464    cab.encode_decision(O + 3, cbp_luma15 as u32);
6465    if cbp_chroma != 0 {
6466        cab.encode_decision(O + 4, 1);
6467        cab.encode_decision(O + 5, (cbp_chroma == 2) as u32);
6468    } else {
6469        cab.encode_decision(O + 4, 0);
6470    }
6471    cab.encode_decision(O + 6, (i16_mode >> 1) & 1);
6472    cab.encode_decision(O + 7, i16_mode & 1);
6473}
6474
6475/// One `Intra_4x4` pred-mode — inverse of `parse_intra4x4_pred_mode_cabac` (ctx 68).
6476fn cb_intra4x4_pred_mode(cab: &mut CabacEncoder, predicted: u8, actual: u8) {
6477    const IPR: usize = 68;
6478    if actual == predicted {
6479        cab.encode_decision(IPR, 1);
6480    } else {
6481        cab.encode_decision(IPR, 0);
6482        let rem = if actual < predicted { actual } else { actual - 1 } as u32;
6483        cab.encode_decision(IPR + 1, rem & 1);
6484        cab.encode_decision(IPR + 1, (rem >> 1) & 1);
6485        cab.encode_decision(IPR + 1, (rem >> 2) & 1);
6486    }
6487}
6488
6489/// `coded_block_pattern` — inverse of `parse_cbp_cabac` (ctxIdxOffset 73).
6490fn cb_cbp(cab: &mut CabacEncoder, top: Option<u8>, left: Option<u8>, cbp: u32) {
6491    const CBP: usize = 73;
6492    let t = |m: u32| top.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6493    let l = |m: u32| left.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6494    let nb = |x: u32| (x == 0) as u32;
6495    let b0 = cbp & 1;
6496    let b1 = (cbp >> 1) & 1;
6497    let b2 = (cbp >> 2) & 1;
6498    let b3 = (cbp >> 3) & 1;
6499    cab.encode_decision(CBP + (l(1 << 1) + (t(1 << 2) << 1)) as usize, b0);
6500    cab.encode_decision(CBP + (nb(b0) + (t(1 << 3) << 1)) as usize, b1);
6501    cab.encode_decision(CBP + (l(1 << 3) + (nb(b0) << 1)) as usize, b2);
6502    cab.encode_decision(CBP + (nb(b2) + (nb(b1) << 1)) as usize, b3);
6503    let cbp_chroma = cbp >> 4;
6504    let ct = top.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6505    let cl = left.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6506    cab.encode_decision(CBP + 4 + (cl + (ct << 1)) as usize, (cbp_chroma != 0) as u32);
6507    if cbp_chroma != 0 {
6508        let ct2 = top.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6509        let cl2 = left.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6510        cab.encode_decision(CBP + 8 + (cl2 + (ct2 << 1)) as usize, (cbp_chroma == 2) as u32);
6511    }
6512}
6513
6514/// One residual block — inverse of `parse_residual_cabac`. `coeffs` is scan-order
6515/// (len >= maxPos+1). Returns totalCoeffNum (for the nzc cache + deblock nnz).
6516#[allow(clippy::too_many_arguments)]
6517fn cb_residual(
6518    cab: &mut CabacEncoder,
6519    nzc: &mut [u8; 48],
6520    cbf_dc: &mut u16,
6521    iz: usize,
6522    rp: usize,
6523    is_intra: bool,
6524    ndc: (Option<u16>, Option<u16>),
6525    coeffs: &[i32],
6526) -> u32 {
6527    let is_dc = rp == CB_RP_I16_DC || rp == CB_RP_CHROMA_DC || rp == CB_RP_CHROMA_DC + 1;
6528    let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
6529    let scan = CB_NZC_CACHE[iz.min(23)];
6530    if is_dc {
6531        if let Some(t) = ndc.0 {
6532            nb = ((t >> rp) & 1) as u8;
6533        }
6534        if let Some(l) = ndc.1 {
6535            na = ((l >> rp) & 1) as u8;
6536        }
6537    } else {
6538        if nzc[scan - 8] != 0xff {
6539            nb = (nzc[scan - 8] != 0) as u8;
6540        }
6541        if nzc[scan - 1] != 0xff {
6542            na = (nzc[scan - 1] != 0) as u8;
6543        }
6544    }
6545    let maxpos = CB_RES_MAXPOS[rp] as usize;
6546    let coeff_num = coeffs[..=maxpos].iter().filter(|&&c| c != 0).count() as u32;
6547    let cbf = coeff_num != 0;
6548    cab.encode_decision(85 + CB_RES_CBF[rp] + (na + (nb << 1)) as usize, cbf as u32);
6549    if !cbf {
6550        if !is_dc {
6551            nzc[scan] = 0;
6552        }
6553        return 0;
6554    }
6555    if is_dc {
6556        *cbf_dc |= 1 << rp;
6557    }
6558    // significance map
6559    let map = 105 + CB_RES_MAP[rp];
6560    let last = 166 + CB_RES_MAP[rp];
6561    let lastnz = (0..=maxpos).rev().find(|&i| coeffs[i] != 0).unwrap();
6562    for i in 0..maxpos {
6563        let s = coeffs[i] != 0;
6564        cab.encode_decision(map + i, s as u32);
6565        if s {
6566            let is_last = i == lastnz;
6567            cab.encode_decision(last + i, is_last as u32);
6568            if is_last {
6569                break;
6570            }
6571        }
6572    }
6573    // levels (reverse scan)
6574    let one = 227 + CB_RES_ONE[rp];
6575    let abs = 232 + CB_RES_ONE[rp];
6576    let maxc2 = CB_RES_MAXC2[rp];
6577    let (mut c1, mut c2) = (1i32, 0i32);
6578    for i in (0..=maxpos).rev() {
6579        if coeffs[i] != 0 {
6580            let av = coeffs[i].unsigned_abs();
6581            let gt1 = av > 1;
6582            cab.encode_decision(one + c1 as usize, gt1 as u32);
6583            if gt1 {
6584                cb_ueg_level(cab, abs + c2 as usize, av - 2);
6585                c2 = (c2 + 1).min(maxc2);
6586                c1 = 0;
6587            } else if c1 != 0 {
6588                c1 = (c1 + 1).min(4);
6589            }
6590            cab.encode_bypass((coeffs[i] < 0) as u32);
6591        }
6592    }
6593    if !is_dc {
6594        nzc[scan] = coeff_num as u8;
6595    }
6596    coeff_num
6597}
6598
6599/// Build the 48-entry padded nzc cache from the top/left neighbour MB exports
6600/// (openh264 `WelsFillCacheNonZeroCount`) — identical to the decoder.
6601fn cb_build_nzc(mb_nzc: &[[u8; 24]], top: Option<usize>, left: Option<usize>) -> [u8; 48] {
6602    let mut nzc = [0xffu8; 48];
6603    if let Some(t) = top {
6604        let tn = mb_nzc[t];
6605        nzc[1..5].copy_from_slice(&tn[12..16]);
6606        (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
6607        (nzc[6], nzc[7]) = (tn[20], tn[21]);
6608        (nzc[30], nzc[31]) = (tn[22], tn[23]);
6609    }
6610    if let Some(l) = left {
6611        let ln = mb_nzc[l];
6612        (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
6613        (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
6614    }
6615    nzc
6616}
6617
6618/// Extract the 24-entry per-MB nzc (raster luma + chroma) for future neighbours.
6619fn cb_export_nzc(nzc: &[u8; 48]) -> [u8; 24] {
6620    let mut mn = [0u8; 24];
6621    for k in 0..4 {
6622        mn[k] = nzc[9 + k];
6623        mn[4 + k] = nzc[17 + k];
6624        mn[8 + k] = nzc[25 + k];
6625        mn[12 + k] = nzc[33 + k];
6626    }
6627    (mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
6628    (mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
6629    for v in mn.iter_mut() {
6630        if *v == 0xff {
6631            *v = 0;
6632        }
6633    }
6634    mn
6635}
6636
6637/// Per-frame CABAC neighbour state (I-slice): one entry per macroblock, mirroring
6638/// the arrays the decoder's `decode_slice_data_cabac` maintains.
6639struct CabacState {
6640    cat: Vec<u8>,          // 2 = I_16x16, 0 = I_NxN, 100 = inter (mb_type / skip ctxInc)
6641    cmode: Vec<i32>,       // per-MB chroma mode (chroma-pred ctxInc)
6642    mb_cbp: Vec<u8>,       // per-MB cbp byte (cbp ctxInc)
6643    cbf_dc: Vec<u16>,      // per-MB DC coded_block_flag mask (residual DC ctxInc)
6644    mb_nzc: Vec<[u8; 24]>, // per-MB nzc export (residual AC ctxInc)
6645    // Inter (P/B) neighbour state — mirrors the decoder's WelsFillCacheInterCabac.
6646    mb_mvd: Vec<[[i16; 2]; 16]>,  // per-MB per-4x4 List-0 mvd (raster), for the mvd ctxInc cache
6647    mb_ref: Vec<[i8; 16]>,        // per-MB per-4x4 List-0 ref idx (raster); -1 = unavailable
6648    mb_mvd1: Vec<[[i16; 2]; 16]>, // B: per-MB per-4x4 List-1 mvd
6649    mb_ref1: Vec<[i8; 16]>,       // B: per-MB per-4x4 List-1 ref idx
6650    mb_skip: Vec<bool>,           // per-MB mb_skip_flag (skip ctxInc)
6651    mb_direct: Vec<bool>,         // B: per-MB B_Direct/B_Skip (B mb_type ctxInc)
6652    last_delta_qp: i32,
6653}
6654
6655impl CabacState {
6656    fn new(n: usize) -> Self {
6657        CabacState {
6658            cat: vec![0; n],
6659            cmode: vec![0; n],
6660            mb_cbp: vec![0; n],
6661            cbf_dc: vec![0; n],
6662            mb_nzc: vec![[0u8; 24]; n],
6663            mb_mvd: vec![[[0i16; 2]; 16]; n],
6664            mb_ref: vec![[-1i8; 16]; n],
6665            mb_mvd1: vec![[[0i16; 2]; 16]; n],
6666            mb_ref1: vec![[-1i8; 16]; n],
6667            mb_skip: vec![false; n],
6668            mb_direct: vec![false; n],
6669            last_delta_qp: 0,
6670        }
6671    }
6672}
6673
6674/// Emit one planned intra macroblock as CABAC (I-slice). Mirrors the decoder's
6675/// I-slice MB body exactly: `mb_type`, then per luma-type the intra modes / cbp /
6676/// `mb_qp_delta` / residual in spec order, maintaining `cs` and `fe.nnz_y`.
6677fn emit_mb_cabac_i(
6678    fe: &mut FrameEncoder,
6679    cab: &mut CabacEncoder,
6680    cs: &mut CabacState,
6681    plan: &MbPlan,
6682    mb_x: usize,
6683    mb_y: usize,
6684) {
6685    let mb_w = fe.mb_w;
6686    let addr = mb_y * mb_w + mb_x;
6687    let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
6688    let left = if mb_x > 0 { Some(addr - 1) } else { None };
6689
6690    // ---- mb_type (I-slice prefix; carries I_16x16 pred-mode/cbp) ----
6691    let li = left.map_or(0, |a| (cs.cat[a] >= 2) as usize);
6692    let ti = top.map_or(0, |a| (cs.cat[a] >= 2) as usize);
6693    if plan.use_i4 {
6694        cb_mb_type_i(cab, li + ti, true, 0, 0, false);
6695    } else {
6696        cb_mb_type_i(cab, li + ti, false, plan.i16_mode as u32, plan.cbp_chroma, plan.i16_cbp15);
6697    }
6698    emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
6699}
6700
6701/// The intra macroblock body (chroma pred mode, intra modes, cbp, mb_qp_delta,
6702/// residual) shared by I-slice intra and P/B-slice intra — everything AFTER the
6703/// slice-specific `mb_type` prefix (which already carries the I_16x16 pred-mode/cbp).
6704#[allow(clippy::too_many_arguments)]
6705fn emit_intra_body_cabac(
6706    fe: &mut FrameEncoder,
6707    cab: &mut CabacEncoder,
6708    cs: &mut CabacState,
6709    plan: &MbPlan,
6710    mb_x: usize,
6711    mb_y: usize,
6712    addr: usize,
6713    top: Option<usize>,
6714    left: Option<usize>,
6715) {
6716    let w4 = fe.mb_w * 4;
6717    let cbp_chroma = plan.cbp_chroma;
6718    // chroma-pred-mode ctxInc from neighbour chroma modes (1..=3).
6719    let cci = left.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize)
6720        + top.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize);
6721
6722    let mut nzc;
6723    let mut cbfdc = 0u16;
6724    let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
6725
6726    if !plan.use_i4 {
6727        // ---- I_16x16 ----
6728        cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
6729        cs.cmode[addr] = plan.chroma_mode as i32;
6730        cs.cat[addr] = 2;
6731        cs.mb_cbp[addr] = ((cbp_chroma as u8) << 4) | if plan.i16_cbp15 { 15 } else { 0 };
6732        nzc = cb_build_nzc(&cs.mb_nzc, top, left);
6733
6734        let delta = fe.qp_delta();
6735        cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
6736
6737        // luma DC
6738        let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
6739        cb_residual(cab, &mut nzc, &mut cbfdc, 0, CB_RP_I16_DC, true, ndc, &dc_scan);
6740        // luma AC
6741        for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6742            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6743            let total = if plan.i16_cbp15 {
6744                let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
6745                cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_I16_AC, true, ndc, &ac)
6746            } else {
6747                nzc[CB_NZC_CACHE[iz]] = 0;
6748                0
6749            };
6750            fe.nnz_y[by * w4 + bx] = total as u8;
6751        }
6752        cb_emit_chroma_residual(cab, fe, &mut nzc, &mut cbfdc, ndc, true, plan.cbp_chroma, &plan.c_dc_levels, &plan.c_q_blocks, mb_x, mb_y);
6753    } else {
6754        // ---- I_NxN (I_4x4) ----
6755        let i4 = plan.i4.as_ref().unwrap();
6756        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6757            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6758            let predicted = predict_i4_mode(fe, bx, by);
6759            cb_intra4x4_pred_mode(cab, predicted, i4.modes[lby * 4 + lbx]);
6760        }
6761        cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
6762        cs.cmode[addr] = plan.chroma_mode as i32;
6763        cs.cat[addr] = 0;
6764        let cbp = i4.cbp_luma | (cbp_chroma << 4);
6765        cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
6766        cs.mb_cbp[addr] = cbp as u8;
6767        nzc = cb_build_nzc(&cs.mb_nzc, top, left);
6768
6769        if cbp == 0 {
6770            cs.last_delta_qp = 0;
6771            for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6772                fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
6773            }
6774        } else {
6775            let delta = fe.qp_delta();
6776            cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
6777            for id8 in 0..4usize {
6778                for id4 in 0..4usize {
6779                    let iz = id8 * 4 + id4;
6780                    let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
6781                    let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6782                    let total = if i4.cbp_luma & (1 << id8) != 0 {
6783                        let sc = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
6784                        cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, true, ndc, &sc)
6785                    } else {
6786                        nzc[CB_NZC_CACHE[iz]] = 0;
6787                        0
6788                    };
6789                    fe.nnz_y[by * w4 + bx] = total as u8;
6790                }
6791            }
6792            cb_emit_chroma_residual(cab, fe, &mut nzc, &mut cbfdc, ndc, true, plan.cbp_chroma, &plan.c_dc_levels, &plan.c_q_blocks, mb_x, mb_y);
6793        }
6794    }
6795
6796    cs.cbf_dc[addr] = cbfdc;
6797    cs.mb_nzc[addr] = cb_export_nzc(&nzc);
6798}
6799
6800/// Chroma DC + AC residual (shared by intra I_16x16/I_NxN and inter) — matches the
6801/// decoder's chroma residual order. `is_intra` selects the coded_block_flag default
6802/// (nA=nB default to is_intra). Populates the chroma nnz grid for deblock.
6803#[allow(clippy::too_many_arguments)]
6804fn cb_emit_chroma_residual(
6805    cab: &mut CabacEncoder,
6806    fe: &mut FrameEncoder,
6807    nzc: &mut [u8; 48],
6808    cbfdc: &mut u16,
6809    ndc: (Option<u16>, Option<u16>),
6810    is_intra: bool,
6811    cbp_chroma: u32,
6812    c_dc_levels: &[[i32; 4]; 2],
6813    c_q: &[[[i32; 16]; 4]; 2],
6814    mb_x: usize,
6815    mb_y: usize,
6816) {
6817    let w2 = fe.mb_w * 2;
6818    if cbp_chroma >= 1 {
6819        for i in 0..2usize {
6820            cb_residual(cab, nzc, cbfdc, 16 + i * 4, CB_RP_CHROMA_DC + i, is_intra, ndc, &c_dc_levels[i]);
6821        }
6822    }
6823    if cbp_chroma == 2 {
6824        for i in 0..2usize {
6825            for (id4, &(bx, by)) in CHROMA_4X4_SCAN_XY.iter().enumerate() {
6826                let ac = scan_4x4_ac(&c_q[i][by * 2 + bx]);
6827                let total = cb_residual(
6828                    cab, nzc, cbfdc, 16 + i * 4 + id4, CB_RP_CHROMA_AC + i, is_intra, ndc, &ac,
6829                );
6830                fe.nnz_c[i][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total as u8;
6831            }
6832        }
6833    }
6834}
6835
6836/// CABAC all-intra slice-data coder (IDR / I-slice). Mirrors `encode_slice_data`'s
6837/// setup + deblock + `RefFrame` construction, but codes every MB via `plan_mb` +
6838/// `emit_mb_cabac_i` into a CABAC bitstream. `w` already holds the byte-aligned
6839/// slice header; the CABAC bytes are appended after `cabac_alignment_one_bit`.
6840pub fn encode_slice_data_cabac_intra(
6841    w: &mut BitWriter,
6842    cfg: &EncoderConfig,
6843    frame: &YuvFrame,
6844    qp: u8,
6845    qpo: &[i32],
6846) -> crate::RefFrame {
6847    let mut fe = FrameEncoder::new(cfg);
6848    fe.qp = qp;
6849    fe.qpc = chroma_qp(qp);
6850    fe.cur_qp = qp;
6851    if cfg.cabac_dz_div > 0 {
6852        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
6853    }
6854    let (sy, su, sv) = coded_source(cfg, frame);
6855    let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
6856    apply_mbtree_qpo(&mut aq_qp, qpo); // mb-tree temporal AQ (empty = byte-identical)
6857    fe.cur_qp = qp;
6858    let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
6859
6860    // CABAC trellis (RDOQ): structure-adaptive. ON only for ALL-INTRA streams
6861    // (gop_size<=1), where each IDR is independent so trading a little distortion for
6862    // rate is a clean −0.5..−1.3% BD-rate win. OFF inside a GOP: there the I-frame is
6863    // a REFERENCE, and degrading it costs the dependent P-frames more than the I-frame
6864    // saves (measured ~+0.1% net) — so the safe end is a true no-op (never regresses).
6865    fe.rdoq_strength = if cfg.gop_size <= 1 { cfg.cabac_rdoq } else { 0.0 };
6866    // Contexts init from SliceQPY (the slice qp), init_idc unused for I, is_i = true.
6867    let mut cab = CabacEncoder::new(qp as i32, 0, true);
6868    let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
6869    let total = fe.mb_w * fe.mb_h;
6870
6871    for mb_y in 0..fe.mb_h {
6872        for mb_x in 0..fe.mb_w {
6873            let mb_idx = mb_y * fe.mb_w + mb_x;
6874            fe.qp = aq_qp[mb_idx];
6875            fe.qpc = chroma_qp(aq_qp[mb_idx]);
6876            let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
6877            emit_mb_cabac_i(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
6878            mb_qpy[mb_idx] = fe.cur_qp;
6879            // end_of_slice_flag (EncodeTerminate): 1 on the last MB, else 0.
6880            cab.encode_terminate(mb_idx + 1 == total);
6881        }
6882    }
6883
6884    // Append CABAC slice data after cabac_alignment_one_bit (pad header with 1-bits).
6885    while !w.is_byte_aligned() {
6886        w.write_bit(true);
6887    }
6888    for b in cab.into_bytes() {
6889        w.write_bits(b as u32, 8);
6890    }
6891
6892    // Deblock the reconstruction (all-intra: BS derives from intra-ness) -> reference.
6893    let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
6894    let info = rusty_h264_common::deblock::BlockInfo {
6895        inter: &fe.inter_y,
6896        nnz: &fe.nnz_y,
6897        mv: &fe.mv_y,
6898        ref_id: &ref_id,
6899        mv1: &[],
6900        ref_id1: &[],
6901        w4: fe.mb_w * 4,
6902        t8x8: &[],
6903        bs: &[],
6904        };
6905    rusty_h264_common::deblock::filter_frame(
6906        &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
6907    );
6908    let w4 = fe.mb_w * 4;
6909    crate::RefFrame {
6910        y: fe.rec_y,
6911        u: fe.rec_u,
6912        v: fe.rec_v,
6913        poc: 0,
6914        frame_num: 0,
6915        mv: fe.mv_y,
6916        ref_idx: fe.ref_idx_y,
6917        w4,
6918        // Filtered lazily on first sub-pel search use (see `RefFrame::hpel`).
6919        hpel: std::sync::OnceLock::new(),
6920    }
6921}
6922
6923// ============================================================================
6924// CABAC P-slice entropy coding — the forward inverse of the decoder's
6925// decode_slice_data_cabac P-slice path. mb_skip_flag / mb_type_p / mvd (UEG3) /
6926// inter residual, plus intra-in-P (the shared intra body under a P mb_type prefix).
6927// Scope: 1 reference (no ref_idx), P_16x16/16x8/8x16 (no P_8x8/sub_mb_type) — the
6928// modes the encoder's decision produces.
6929// ============================================================================
6930
6931// z-order 4x4 block -> 30-entry (6-stride) mvd/ref cache index (openh264 g_kCache30ScanIdx).
6932const CB_CACHE30: [usize; 16] = [7, 8, 13, 14, 9, 10, 15, 16, 19, 20, 25, 26, 21, 22, 27, 28];
6933// z-order 4x4 block -> raster index (openh264 g_kuiScan4): the per-MB mvd/ref grid layout.
6934const CB_G_SCAN4: [usize; 16] = [0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15];
6935
6936/// UEG3 mvd suffix — inverse of `decode_ueg_mv(base)` (TU prefix at base+{0,1,2,3,3..},
6937/// cMax 7, then EG3 bypass). `v` is the value decode_ueg_mv returns.
6938fn cb_ueg_mv(cab: &mut CabacEncoder, base: usize, v: u32) {
6939    const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
6940    if v == 0 {
6941        cab.encode_decision(base, 0);
6942        return;
6943    }
6944    cab.encode_decision(base, 1);
6945    if v <= 7 {
6946        // (v-1) ones then a terminating 0, at base+P2C[count] for count = 1..
6947        let mut count = 1;
6948        for _ in 0..v - 1 {
6949            cab.encode_decision(base + P2C[count], 1);
6950            count += 1;
6951        }
6952        cab.encode_decision(base + P2C[count], 0);
6953    } else {
6954        // prefix maxes out: 7 ones (count 1..7) then EG3(v-8).
6955        let mut count = 1;
6956        for _ in 0..7 {
6957            cab.encode_decision(base + P2C[count], 1);
6958            count += 1;
6959        }
6960        cb_exp_bypass(cab, 3, v - 8);
6961    }
6962}
6963
6964/// One `mvd` component — inverse of `parse_mvd_cabac(comp, ctx_inc)` (ctxIdxOffset
6965/// 40 for x, 47 for y).
6966fn cb_mvd(cab: &mut CabacEncoder, comp: usize, ctx_inc: usize, d: i32) {
6967    let base = 40 + comp * 7;
6968    if d == 0 {
6969        cab.encode_decision(base + ctx_inc, 0);
6970        return;
6971    }
6972    cab.encode_decision(base + ctx_inc, 1);
6973    cb_ueg_mv(cab, base + 3, d.unsigned_abs() - 1); // decode adds 1 back
6974    cab.encode_bypass((d < 0) as u32);
6975}
6976
6977/// `mb_skip_flag` — inverse of `parse_mb_skip_cabac` (ctx 11 P + neighbour-not-skip).
6978fn cb_mb_skip(cab: &mut CabacEncoder, ctx_inc: usize, skip: bool) {
6979    cab.encode_decision(ctx_inc, skip as u32);
6980}
6981
6982/// `ref_idx_l0` (P) — inverse of `parse_ref_idx_cabac`. Unary binarization,
6983/// ctxIdxOffset 54: binIdx 0 → `ctx0` (condTermFlagA + 2·condTermFlagB, condTermFlagN =
6984/// neighbour partition's ref_idx > 0), binIdx 1 → 4, binIdx ≥2 → 5 (spec 9.3.3.1.1.6).
6985fn cb_ref_idx(cab: &mut CabacEncoder, ctx0: usize, r: u32) {
6986    const B: usize = 54;
6987    let mut v = r;
6988    let mut bin_idx = 0u32;
6989    loop {
6990        let bin = (v > 0) as u32;
6991        let ctx = match bin_idx {
6992            0 => ctx0,
6993            1 => 4,
6994            _ => 5,
6995        };
6996        cab.encode_decision(B + ctx, bin);
6997        if bin == 0 {
6998            break;
6999        }
7000        v -= 1;
7001        bin_idx += 1;
7002    }
7003}
7004
7005/// P-slice inter `mb_type` (0/1/2 = P_L0_16x16 / P_16x8 / P_8x16) — inverse of the
7006/// inter branch of `parse_mb_type_p_cabac` (ctx base 11).
7007fn cb_mb_type_p_inter(cab: &mut CabacEncoder, mode: u8) {
7008    const S: usize = 11;
7009    cab.encode_decision(S + 3, 0); // inter (prefix bit 0)
7010    match mode {
7011        0 => {
7012            cab.encode_decision(S + 4, 0);
7013            cab.encode_decision(S + 5, 0);
7014        }
7015        3 => {
7016            // P_8x8 (bins "0 0 1")
7017            cab.encode_decision(S + 4, 0);
7018            cab.encode_decision(S + 5, 1);
7019        }
7020        1 => {
7021            cab.encode_decision(S + 4, 1);
7022            cab.encode_decision(S + 6, 1);
7023        }
7024        _ => {
7025            // mode == 2 (P_8x16)
7026            cab.encode_decision(S + 4, 1);
7027            cab.encode_decision(S + 6, 0);
7028        }
7029    }
7030}
7031
7032/// P `sub_mb_type` CABAC — inverse of `parse_sub_mb_type_p_cabac` (ctx base 21).
7033/// Only 0 = P_L0_8x8 (bin "1") is emitted (8×8 sub-partitions only).
7034fn cb_sub_mb_type_p(cab: &mut CabacEncoder, sub_type: u8) {
7035    const S: usize = 21;
7036    match sub_type {
7037        0 => cab.encode_decision(S, 1),
7038        _ => unreachable!("only 8x8 sub_mb_type (0) emitted"),
7039    }
7040}
7041
7042/// P-slice intra `mb_type` prefix — inverse of the intra branch of
7043/// `parse_mb_type_p_cabac` (ctx base 11). Carries the I_16x16 pred-mode/cbp exactly
7044/// like the I-slice mb_type, so the shared intra body re-emits neither.
7045fn cb_mb_type_p_intra(cab: &mut CabacEncoder, plan: &MbPlan) {
7046    const S: usize = 11;
7047    cab.encode_decision(S + 3, 1); // intra (prefix bit 1)
7048    if plan.use_i4 {
7049        cab.encode_decision(S + 6, 0); // I_4x4
7050        return;
7051    }
7052    cab.encode_decision(S + 6, 1); // I_16x16
7053    cab.encode_terminate(false); // not I_PCM
7054    cab.encode_decision(S + 7, plan.i16_cbp15 as u32);
7055    if plan.cbp_chroma != 0 {
7056        cab.encode_decision(S + 8, 1);
7057        cab.encode_decision(S + 8, (plan.cbp_chroma == 2) as u32);
7058    } else {
7059        cab.encode_decision(S + 8, 0);
7060    }
7061    cab.encode_decision(S + 9, (plan.i16_mode as u32 >> 1) & 1);
7062    cab.encode_decision(S + 9, plan.i16_mode as u32 & 1);
7063}
7064
7065/// P-slice partition layout: `(part_idx, z-blocks)` per motion partition (matches
7066/// the decoder's `part!` invocations). part_idx = the partition's top-left z-block
7067/// (its `CACHE30` slot for the mvd ctxInc); z-blocks = every 4x4 it covers.
7068fn p_partition_layout(mode: u8) -> &'static [(usize, &'static [usize])] {
7069    match mode {
7070        1 => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7]), (8, &[8, 9, 10, 11, 12, 13, 14, 15])],
7071        2 => &[(0, &[0, 1, 2, 3, 8, 9, 10, 11]), (4, &[4, 5, 6, 7, 12, 13, 14, 15])],
7072        // P_8x8: four 8×8 quads (z-order 4×4 blocks), part order == inter_partitions(3).
7073        3 => &[(0, &[0, 1, 2, 3]), (4, &[4, 5, 6, 7]), (8, &[8, 9, 10, 11]), (12, &[12, 13, 14, 15])],
7074        _ => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])],
7075    }
7076}
7077
7078/// Emit one motion partition's `mvd` (x,y) and splat it into the 30-entry cache +
7079/// per-MB raster mvd/ref grids — inverse of the decoder's `parse_mvd_partition`.
7080#[allow(clippy::too_many_arguments)]
7081fn cb_emit_mvd_partition(
7082    cab: &mut CabacEncoder,
7083    part_idx: usize,
7084    zblocks: &[usize],
7085    mvdc: &mut [[i16; 2]; 30],
7086    refc: &mut [i8; 30],
7087    mmvd: &mut [[i16; 2]; 16],
7088    mref: &mut [i8; 16],
7089    mvd: (i32, i32),
7090    ref_idx: i8, // the partition's ref_idx_l0 (0 for single-ref) — stored for neighbour context
7091) {
7092    let s = CB_CACHE30[part_idx];
7093    let ctx = |comp: usize| -> usize {
7094        let mut a = 0i32;
7095        if refc[s - 6] >= 0 {
7096            a += mvdc[s - 6][comp].unsigned_abs() as i32;
7097        }
7098        if refc[s - 1] >= 0 {
7099            a += mvdc[s - 1][comp].unsigned_abs() as i32;
7100        }
7101        if a >= 3 {
7102            1 + (a > 32) as usize
7103        } else {
7104            0
7105        }
7106    };
7107    cb_mvd(cab, 0, ctx(0), mvd.0);
7108    cb_mvd(cab, 1, ctx(1), mvd.1);
7109    let (mx, my) = (mvd.0 as i16, mvd.1 as i16);
7110    for &zb in zblocks {
7111        mvdc[CB_CACHE30[zb]] = [mx, my];
7112        refc[CB_CACHE30[zb]] = ref_idx;
7113        mmvd[CB_G_SCAN4[zb]] = [mx, my];
7114        mref[CB_G_SCAN4[zb]] = ref_idx;
7115    }
7116}
7117
7118/// Emit one planned INTER macroblock as CABAC (P-slice, mb_skip_flag already coded
7119/// as 0). `mode`/`parts` + `plan` from `plan_inter_mb`. 1-ref: no ref_idx.
7120fn emit_mb_cabac_p_inter(
7121    fe: &mut FrameEncoder,
7122    cab: &mut CabacEncoder,
7123    cs: &mut CabacState,
7124    mode: u8,
7125    plan: &InterPlan,
7126    mb_x: usize,
7127    mb_y: usize,
7128    num_refs: usize,
7129) {
7130    let mb_w = fe.mb_w;
7131    let addr = mb_y * mb_w + mb_x;
7132    let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7133    let left = if mb_x > 0 { Some(addr - 1) } else { None };
7134
7135    cb_mb_type_p_inter(cab, mode);
7136    // P_8x8: four sub_mb_type (all 0 = 8×8), spec order before ref_idx/mvd.
7137    if mode == 3 {
7138        for _ in 0..4 {
7139            cb_sub_mb_type_p(cab, 0);
7140        }
7141    }
7142
7143    // ---- mb_pred (spec 7.3.5.1): all ref_idx_l0 FIRST, then all mvd_l0 ----
7144    let mut mvdc = [[0i16; 2]; 30];
7145    let mut refc = [-1i8; 30];
7146    cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc, &mut mvdc, top, left, addr, mb_w);
7147    let mut mmvd = [[0i16; 2]; 16];
7148    let mut mref = [0i8; 16];
7149    let layout = p_partition_layout(mode);
7150    // Phase 1: ref_idx_l0 per partition, only when the slice has >1 active reference.
7151    // Update refc after each so a later partition's ref context sees the earlier one.
7152    if num_refs > 1 {
7153        for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7154            let r = plan.plan_refs[part];
7155            let s = CB_CACHE30[part_idx];
7156            let ctx0 = (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
7157            cb_ref_idx(cab, ctx0, r as u32);
7158            for &zb in zblocks {
7159                refc[CB_CACHE30[zb]] = r as i8;
7160            }
7161        }
7162    }
7163    // Phase 2: mvd per partition (carries the ref into refc/mref for neighbour context).
7164    for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7165        cb_emit_mvd_partition(
7166            cab, part_idx, zblocks, &mut mvdc, &mut refc, &mut mmvd, &mut mref, plan.mvds[part],
7167            plan.plan_refs[part] as i8,
7168        );
7169    }
7170    cs.mb_mvd[addr] = mmvd;
7171    cs.mb_ref[addr] = mref;
7172    cs.cat[addr] = 100;
7173    cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7174}
7175
7176/// Inter cbp + residual (is_intra = false) — shared by P and B inter MBs. Maintains
7177/// cs.mb_cbp/cbf_dc/mb_nzc/last_delta_qp + fe.nnz_y.
7178#[allow(clippy::too_many_arguments)]
7179fn cb_emit_inter_residual(
7180    fe: &mut FrameEncoder,
7181    cab: &mut CabacEncoder,
7182    cs: &mut CabacState,
7183    plan: &InterPlan,
7184    mb_x: usize,
7185    mb_y: usize,
7186    addr: usize,
7187    top: Option<usize>,
7188    left: Option<usize>,
7189) {
7190    let w4 = fe.mb_w * 4;
7191    let cbp = plan.cbp;
7192    let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
7193    cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7194    cs.mb_cbp[addr] = cbp as u8;
7195    let mut nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7196    let mut cbfdc = 0u16;
7197    let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7198
7199    if cbp == 0 {
7200        cs.last_delta_qp = 0;
7201        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7202            fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7203        }
7204    } else {
7205        let delta = fe.qp_delta();
7206        cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7207        for id8 in 0..4usize {
7208            for id4 in 0..4usize {
7209                let iz = id8 * 4 + id4;
7210                let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7211                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7212                let total = if cbp_luma & (1 << id8) != 0 {
7213                    let sc = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
7214                    cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, false, ndc, &sc)
7215                } else {
7216                    nzc[CB_NZC_CACHE[iz]] = 0;
7217                    0
7218                };
7219                fe.nnz_y[by * w4 + bx] = total as u8;
7220            }
7221        }
7222        cb_emit_chroma_residual(cab, fe, &mut nzc, &mut cbfdc, ndc, false, cbp_chroma, &plan.c_dc_levels, &plan.c_q, mb_x, mb_y);
7223    }
7224    cs.cbf_dc[addr] = cbfdc;
7225    cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7226}
7227
7228/// Emit one planned INTRA macroblock inside a P-slice: the P mb_type prefix (which
7229/// carries the I_16x16 pred-mode/cbp) then the shared intra body.
7230fn emit_mb_cabac_p_intra(
7231    fe: &mut FrameEncoder,
7232    cab: &mut CabacEncoder,
7233    cs: &mut CabacState,
7234    plan: &MbPlan,
7235    mb_x: usize,
7236    mb_y: usize,
7237) {
7238    let mb_w = fe.mb_w;
7239    let addr = mb_y * mb_w + mb_x;
7240    let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7241    let left = if mb_x > 0 { Some(addr - 1) } else { None };
7242    cb_mb_type_p_intra(cab, plan);
7243    emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7244}
7245
7246/// Emit a P_Skip macroblock's `mb_skip_flag = 1` and update neighbour state. The
7247/// motion grid was committed by `commit_skip`; the mvd/ref cache is LEFT at its
7248/// init (-1 ref) — matching the decoder, which does not touch mb_mvd/mb_ref for a
7249/// P_Skip (so a skip neighbour contributes nothing to a later mvd ctxInc).
7250fn emit_p_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
7251    let sctx = 11
7252        + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7253        + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7254    cb_mb_skip(cab, sctx, true);
7255    cs.mb_skip[addr] = true;
7256    cs.cat[addr] = 100;
7257    cs.last_delta_qp = 0;
7258}
7259
7260/// CABAC P-slice data coder. Mirrors `encode_slice_data`'s decision (P_Skip check +
7261/// fast/quality inter-vs-intra RD) exactly — only the emit differs (per-MB
7262/// mb_skip_flag + CABAC syntax + per-MB end_of_slice terminate).
7263pub fn encode_slice_data_cabac_p(
7264    w: &mut BitWriter,
7265    cfg: &EncoderConfig,
7266    frame: &YuvFrame,
7267    qp: u8,
7268    refs: &[crate::RefFrame],
7269    qpo: &[i32],
7270) -> crate::RefFrame {
7271    let mut fe = FrameEncoder::new(cfg);
7272    fe.qp = qp;
7273    fe.qpc = chroma_qp(qp);
7274    fe.cur_qp = qp;
7275    if cfg.cabac_dz_div > 0 {
7276        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
7277    }
7278    let (sy, su, sv) = coded_source(cfg, frame);
7279    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
7280    let num_refs = refs.len();
7281    // me_wide content gate (pure-pan → global-MC residual ≈ 0 → off; see encode_slice_data).
7282    if fe.me_wide && !refs.is_empty() {
7283        let coh = global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7284        if std::env::var("RFF_ME_COH_DBG").is_ok() {
7285            eprintln!("ME_COH qp{qp} residual={coh:.2}");
7286        }
7287        if coh < fe.me_wide_coh {
7288            fe.me_wide = false;
7289        }
7290    }
7291    // me_wide HEAD-ROOM GATE (the dispatcher the truth table asked for). The rescue
7292    // only pays where a wide search actually beats a predictor-local one; measure
7293    // that directly per frame and route the frame. `RFF_ME_HR` sets the threshold
7294    // (percent); 0 disables the gate and restores the always-on behaviour.
7295    // Skip the probe entirely when the gate is disabled: it must not tax the
7296    // default path (`RFF_ME_HR=0`), which stays byte-identical to pre-gate output.
7297    if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
7298        let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7299        if me_wide_hr_dbg() {
7300            eprintln!("ME_HR qp{qp} headroom={hr:.2}");
7301        }
7302        if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
7303            fe.me_wide = false;
7304        }
7305    }
7306    // Track-B B2 DISPATCH — same probe/route as the CAVLC driver above (the two
7307    // drivers must stay in lockstep; the U5-struct bug came from patching one).
7308    if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
7309        let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7310        if me_sadt_dbg() {
7311            eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
7312        }
7313        fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
7314    }
7315    if fe.satd_q > 0.0 {
7316        let mut vars: Vec<i64> = (0..fe.mb_h)
7317            .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
7318            .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
7319            .collect();
7320        vars.sort_unstable();
7321        let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
7322        fe.satd_var_thresh = vars[idx];
7323    }
7324    let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7325    apply_mbtree_qpo(&mut aq_qp, qpo); // mb-tree temporal AQ (empty = byte-identical)
7326    fe.cur_qp = qp;
7327    let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7328
7329    // Same online free-skip dispatch as the CAVLC path gates the greedy P_Skip on
7330    // (see `encode_slice_data`): measured over the frame so far, within-frame so it
7331    // stays deterministic under GOP-parallel encode.
7332    let mut greedy_free = 0usize;
7333    let mut greedy_seen = 0usize;
7334    let mut greedy_on = fe.greedy_min_free == 0;
7335    let greedy_learn = (fe.mb_w * fe.mb_h / 8).max(64);
7336    let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false); // P-slice
7337    let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7338    let total = fe.mb_w * fe.mb_h;
7339
7340    // ② residue naming: the CABAC driver's MB loop was untapped (the CAVLC twin
7341    // has this scope) — `EncMbLoop − Σ(per-MB stages)` is the per-MB glue.
7342    let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
7343    for mb_y in 0..fe.mb_h {
7344        for mb_x in 0..fe.mb_w {
7345            let mb_idx = mb_y * fe.mb_w + mb_x;
7346            let addr = mb_idx;
7347            let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
7348            let left = if mb_x > 0 { Some(addr - 1) } else { None };
7349            fe.qp = aq_qp[mb_idx];
7350            fe.qpc = chroma_qp(aq_qp[mb_idx]);
7351
7352            // ---- P_Skip check (identical logic to encode_slice_data) ----
7353            let mut inter: Option<InterChoice> = None;
7354            let mut did_skip = false;
7355            if num_refs > 0 {
7356                let mv_skip = fe.skip_mv(mb_x, mb_y);
7357                let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
7358                let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
7359                let skip_c = if luma_free || !fe.fast {
7360                    fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
7361                } else {
7362                    [[0u8; 64]; 2]
7363                };
7364                let is_free = luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
7365                let skip_sad = if fe.fast {
7366                    0
7367                } else {
7368                    let (lx, ly) = (mb_x * 16, mb_y * 16);
7369                    let mut s = 0u32;
7370                    for dy in 0..16 {
7371                        let src = &sy[(ly + dy) * fe.cw + lx..][..16];
7372                        let p = &skip_y[dy * 16..][..16];
7373                        s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
7374                    }
7375                    s
7376                };
7377                greedy_seen += 1;
7378                if greedy_seen >= greedy_learn {
7379                    greedy_on = fe.greedy_min_free == 0
7380                        || greedy_free * 100 >= greedy_seen * fe.greedy_min_free as usize;
7381                }
7382                if is_free {
7383                    fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7384                    if !fe.fast {
7385                        fe.mb_was_skip[mb_idx] = true;
7386                        fe.mb_skip_sad[mb_idx] = skip_sad;
7387                    }
7388                    greedy_free += 1;
7389                    did_skip = true;
7390                } else {
7391                    let (lx, ly) = (mb_x * 16, mb_y * 16);
7392                    let nb = {
7393                        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
7394                        fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
7395                    };
7396                    let lme = lambda.sqrt() * cfg.cabac_lambda_scale;
7397                    if fe.fast {
7398                        fe.mb_use_satd = fe.satd_q > 0.0
7399                            && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
7400                        let (r16, mv16, cost_inter) =
7401                            fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7402                        let cost_intra = if fe.mb_use_satd {
7403                            fe.best_i16_satd(&sy, mb_x, mb_y)
7404                        } else {
7405                            fe.best_i16_sad(&sy, mb_x, mb_y)
7406                        } + (lme * fe.tune_intra_penalty) as i64;
7407                        inter = if cost_intra < cost_inter {
7408                            None
7409                        } else {
7410                            Some((0, vec![(r16, mv16)]))
7411                        };
7412                    } else {
7413                        // Quality preset: greedy P_Skip, then 16x16 baseline + sub-partitions + intra.
7414                        if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
7415                            fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7416                            fe.mb_was_skip[mb_idx] = true;
7417                            fe.mb_skip_sad[mb_idx] = skip_sad;
7418                            did_skip = true;
7419                        } else {
7420                            let (r16, mv16, c16) =
7421                                fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7422                            let mut best_c = c16;
7423                            let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
7424                            const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
7425                            let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
7426                            let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
7427                            let split_t = split_t();
7428                        if c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
7429                                let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
7430                                let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
7431                                let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
7432                                let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
7433                                if ct + cb < best_c {
7434                                    best_c = ct + cb;
7435                                    pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
7436                                }
7437                                if cl + cr < best_c {
7438                                    best_c = cl + cr;
7439                                    pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
7440                                }
7441                                // P_8x8: four 8×8 sub-partitions (see the CAVLC path).
7442                                if fe.sub8x8 {
7443                                    let mut c8 = (lme * 4.0) as i64;
7444                                    let mut p8 = Vec::with_capacity(4);
7445                                    for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
7446                                        let (r, mv, c) = fe.best_part(
7447                                            refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
7448                                        );
7449                                        c8 += c;
7450                                        p8.push((r, mv));
7451                                    }
7452                                    if c8 < best_c {
7453                                        best_c = c8;
7454                                        pick = Some((3u8, p8));
7455                                    }
7456                                }
7457                            }
7458                            // U5-struct: refine ONLY the winning shape (see the twin
7459                            // block in the CAVLC driver). This site is the CABAC path —
7460                            // which is now the DEFAULT, so omitting it here left sub-pel
7461                            // deferred but never refined on every default encode.
7462                            if fe.sp_defer.get() {
7463                                if let Some((mode, parts)) = pick.as_mut() {
7464                                    let regions: &[(usize, usize, usize, usize)] = match mode {
7465                                        1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
7466                                        2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
7467                                        3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
7468                                        _ => &[(0, 0, 16, 16)],
7469                                    };
7470                                    let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
7471                                    for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
7472                                        let (r, mv) = parts[i];
7473                                        let (m2, c2) = fe.refine_part(
7474                                            refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
7475                                        );
7476                                        parts[i] = (r, m2);
7477                                        tot += c2;
7478                                    }
7479                                    best_c = tot;
7480                                }
7481                            }
7482                            let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
7483                                + (lme * fe.tune_intra_penalty) as i64;
7484                            inter = if c_intra < best_c { None } else { pick };
7485                            fe.mb_was_skip[mb_idx] = false;
7486                            fe.mb_skip_sad[mb_idx] = skip_sad;
7487                        }
7488                    }
7489                }
7490            }
7491
7492            // ---- emit ----
7493            if did_skip {
7494                emit_p_skip_cabac(&mut cab, &mut cs, addr, top, left);
7495                mb_qpy[mb_idx] = fe.cur_qp;
7496                cab.encode_terminate(mb_idx + 1 == total);
7497                continue;
7498            }
7499            // mb_skip_flag = 0
7500            let sctx = 11
7501                + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7502                + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7503            cb_mb_skip(&mut cab, sctx, false);
7504            cs.mb_skip[addr] = false;
7505            match inter {
7506                Some((mode, parts)) => {
7507                    let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts, None);
7508                    // ② residue naming: the CABAC entropy EMIT was untapped on the
7509                    // (default) CABAC driver — the whole encoder-side arithmetic
7510                    // coder was landing in `mgmt/other`.
7511                    let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
7512                    emit_mb_cabac_p_inter(&mut fe, &mut cab, &mut cs, mode, &plan, mb_x, mb_y, num_refs);
7513                }
7514                None => {
7515                    let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
7516                    let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
7517                    emit_mb_cabac_p_intra(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
7518                }
7519            }
7520            mb_qpy[mb_idx] = fe.cur_qp;
7521            cab.encode_terminate(mb_idx + 1 == total);
7522        }
7523    }
7524
7525    while !w.is_byte_aligned() {
7526        w.write_bit(true);
7527    }
7528    for b in cab.into_bytes() {
7529        w.write_bits(b as u32, 8);
7530    }
7531
7532    // Deblock -> inter reference (same as encode_slice_data).
7533    let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
7534    let info = rusty_h264_common::deblock::BlockInfo {
7535        inter: &fe.inter_y,
7536        nnz: &fe.nnz_y,
7537        mv: &fe.mv_y,
7538        ref_id: &ref_id,
7539        mv1: &[],
7540        ref_id1: &[],
7541        w4: fe.mb_w * 4,
7542        t8x8: &[],
7543        bs: &[],
7544        };
7545    rusty_h264_common::deblock::filter_frame(
7546        &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
7547    );
7548    let w4 = fe.mb_w * 4;
7549    crate::RefFrame {
7550        y: fe.rec_y,
7551        u: fe.rec_u,
7552        v: fe.rec_v,
7553        poc: 0,
7554        frame_num: 0,
7555        mv: fe.mv_y,
7556        ref_idx: fe.ref_idx_y,
7557        w4,
7558        // Filtered lazily on first sub-pel search use (see `RefFrame::hpel`).
7559        hpel: std::sync::OnceLock::new(),
7560    }
7561}
7562
7563// ============================================================================
7564// CABAC B-slice entropy coding — inverse of the decoder's decode_slice_data_cabac
7565// B-slice path. Scope: the modes the encoder's B decision produces — B_Skip,
7566// B_Direct_16x16 (0), B_L0/L1/Bi_16x16 (1/2/3) — no sub_mb_type, no intra-in-B.
7567// The new piece vs P is the dual-list (L0 + L1) mvd/ref neighbour cache.
7568// ============================================================================
7569
7570/// Fill one list's 30-entry mvd/ref neighbour cache from the per-MB export grids
7571/// (openh264 WelsFillCacheInterCabac). Shared by P (List-0) and B (both lists).
7572fn cb_fill_inter_cache(
7573    mb_ref: &[[i8; 16]],
7574    mb_mvd: &[[[i16; 2]; 16]],
7575    refc: &mut [i8; 30],
7576    mvdc: &mut [[i16; 2]; 30],
7577    top: Option<usize>,
7578    left: Option<usize>,
7579    addr: usize,
7580    mb_w: usize,
7581) {
7582    if let Some(l) = left {
7583        for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
7584            refc[ci] = mb_ref[l][bi];
7585            mvdc[ci] = mb_mvd[l][bi];
7586        }
7587    }
7588    if let Some(t) = top {
7589        for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
7590            refc[ci] = mb_ref[t][bi];
7591            mvdc[ci] = mb_mvd[t][bi];
7592        }
7593    }
7594    let mb_x = addr % mb_w;
7595    let mb_y = addr / mb_w;
7596    if mb_x > 0 && mb_y > 0 {
7597        let a = addr - mb_w - 1;
7598        (refc[0], mvdc[0]) = (mb_ref[a][15], mb_mvd[a][15]);
7599    }
7600    if mb_y > 0 && mb_x + 1 < mb_w {
7601        let a = addr - mb_w + 1;
7602        (refc[5], mvdc[5]) = (mb_ref[a][12], mb_mvd[a][12]);
7603    }
7604}
7605
7606/// B-slice `mb_type` for the encoder's B modes (0 = B_Direct_16x16, 1 = B_L0_16x16,
7607/// 2 = B_L1_16x16, 3 = B_Bi_16x16) — inverse of `parse_mb_type_b_cabac` (ctx 27).
7608fn cb_mb_type_b(cab: &mut CabacEncoder, ctx_inc: usize, dir: u8) {
7609    const B: usize = 27;
7610    match dir {
7611        0 => cab.encode_decision(B + ctx_inc, 0), // B_Direct_16x16
7612        1 => {
7613            cab.encode_decision(B + ctx_inc, 1);
7614            cab.encode_decision(B + 3, 0);
7615            cab.encode_decision(B + 5, 0); // L0
7616        }
7617        2 => {
7618            cab.encode_decision(B + ctx_inc, 1);
7619            cab.encode_decision(B + 3, 0);
7620            cab.encode_decision(B + 5, 1); // L1
7621        }
7622        _ => {
7623            // dir == 3 (B_Bi_16x16): m = 0 → return m+3 = 3
7624            cab.encode_decision(B + ctx_inc, 1);
7625            cab.encode_decision(B + 3, 1);
7626            cab.encode_decision(B + 4, 0);
7627            cab.encode_decision(B + 5, 0);
7628            cab.encode_decision(B + 5, 0);
7629            cab.encode_decision(B + 5, 0);
7630        }
7631    }
7632}
7633
7634const CB_ALL16: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
7635
7636/// Emit one planned INTER B macroblock (mb_skip_flag already coded 0). `dir` is the
7637/// B direction 0/1/2/3; `plan.mvds` holds mvd_l0 then mvd_l1 (per used list).
7638fn emit_mb_cabac_b(
7639    fe: &mut FrameEncoder,
7640    cab: &mut CabacEncoder,
7641    cs: &mut CabacState,
7642    dir: u8,
7643    plan: &InterPlan,
7644    mb_x: usize,
7645    mb_y: usize,
7646) {
7647    let mb_w = fe.mb_w;
7648    let addr = mb_y * mb_w + mb_x;
7649    let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7650    let left = if mb_x > 0 { Some(addr - 1) } else { None };
7651
7652    let bci = left.map_or(0, |a| (!cs.mb_direct[a]) as usize)
7653        + top.map_or(0, |a| (!cs.mb_direct[a]) as usize);
7654    cb_mb_type_b(cab, bci, dir);
7655
7656    // Dual-list mvd/ref caches (L0 = mb_ref/mb_mvd, L1 = mb_ref1/mb_mvd1).
7657    let mut mvdc0 = [[0i16; 2]; 30];
7658    let mut refc0 = [-1i8; 30];
7659    let mut mvdc1 = [[0i16; 2]; 30];
7660    let mut refc1 = [-1i8; 30];
7661    cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc0, &mut mvdc0, top, left, addr, mb_w);
7662    cb_fill_inter_cache(&cs.mb_ref1, &cs.mb_mvd1, &mut refc1, &mut mvdc1, top, left, addr, mb_w);
7663    let mut mmvd0 = [[0i16; 2]; 16];
7664    let mut mref0 = [-1i8; 16];
7665    let mut mmvd1 = [[0i16; 2]; 16];
7666    let mut mref1 = [-1i8; 16];
7667    let (use0, use1) = (dir == 1 || dir == 3, dir == 2 || dir == 3);
7668    if dir == 0 {
7669        // B_Direct_16x16: no coded motion; ref 0 in both lists (mvd stays 0) so a
7670        // later MB's mvd ctxInc sums |0|.
7671        mref0 = [0i8; 16];
7672        mref1 = [0i8; 16];
7673    } else {
7674        // mvd parse order: list-major (L0 then L1); a single 16x16 partition (idx 0).
7675        let mut k = 0;
7676        if use0 {
7677            cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
7678            k += 1;
7679        }
7680        if use1 {
7681            cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
7682        }
7683    }
7684    cs.mb_mvd[addr] = mmvd0;
7685    cs.mb_ref[addr] = mref0;
7686    cs.mb_mvd1[addr] = mmvd1;
7687    cs.mb_ref1[addr] = mref1;
7688    cs.mb_direct[addr] = dir == 0;
7689    cs.cat[addr] = 100;
7690    cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7691}
7692
7693/// Emit a B_Skip macroblock's mb_skip_flag = 1 (ctx 24 base) + neighbour state. The
7694/// direct motion was committed by `commit_direct_motion`; ref 0 in both lists, mvd 0
7695/// (matching the decoder's decode_b_skip handling).
7696fn emit_b_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
7697    let sctx = 24
7698        + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7699        + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7700    cb_mb_skip(cab, sctx, true);
7701    cs.mb_skip[addr] = true;
7702    cs.cat[addr] = 100;
7703    cs.mb_direct[addr] = true;
7704    cs.mb_ref[addr] = [0i8; 16];
7705    cs.mb_ref1[addr] = [0i8; 16];
7706    cs.last_delta_qp = 0;
7707}
7708
7709/// CABAC B-slice data coder. Mirrors `encode_slice_data_b`'s B_Skip-free check +
7710/// L0/L1/Bi/Direct RD decision verbatim; only the emit differs (per-MB
7711/// mb_skip_flag + CABAC + per-MB terminate). B is non-reference → no deblock/return.
7712#[allow(clippy::too_many_arguments)]
7713pub fn encode_slice_data_cabac_b(
7714    w: &mut BitWriter,
7715    cfg: &EncoderConfig,
7716    frame: &YuvFrame,
7717    qp: u8,
7718    poc: i32,
7719    l0: &crate::RefFrame,
7720    l1: &crate::RefFrame,
7721    qpo: &[i32],
7722) {
7723    let mut fe = FrameEncoder::new(cfg);
7724    fe.qp = qp;
7725    fe.qpc = chroma_qp(qp);
7726    fe.cur_qp = qp;
7727    if cfg.cabac_dz_div > 0 {
7728        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
7729    }
7730    fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
7731    let (sy, su, sv) = coded_source(cfg, frame);
7732    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
7733    let lme = lambda.sqrt() * cfg.cabac_lambda_scale;
7734    let refs = std::slice::from_ref(l0);
7735    if fe.satd_q > 0.0 {
7736        let mut vars: Vec<i64> = (0..fe.mb_h)
7737            .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
7738            .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
7739            .collect();
7740        vars.sort_unstable();
7741        let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
7742        fe.satd_var_thresh = vars[idx];
7743    }
7744    let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7745    apply_mbtree_qpo(&mut aq_qp, qpo); // mb-tree temporal AQ (empty = byte-identical)
7746    fe.cur_qp = qp;
7747
7748    let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
7749    let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7750    let total = fe.mb_w * fe.mb_h;
7751
7752    for mb_y in 0..fe.mb_h {
7753        for mb_x in 0..fe.mb_w {
7754            let mb_idx = mb_y * fe.mb_w + mb_x;
7755            let addr = mb_idx;
7756            let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
7757            let left = if mb_x > 0 { Some(addr - 1) } else { None };
7758            fe.qp = aq_qp[mb_idx];
7759            fe.qpc = chroma_qp(aq_qp[mb_idx]);
7760            let (lx, ly) = (mb_x * 16, mb_y * 16);
7761            let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
7762            fe.mb_use_satd =
7763                fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
7764            let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
7765            let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
7766            let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
7767            let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
7768            let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
7769            // B_Skip: free direct prediction → mb_skip_flag = 1.
7770            if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
7771                && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
7772            {
7773                fe.commit_direct_motion(mb_x, mb_y, &dmotion);
7774                emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
7775                cab.encode_terminate(mb_idx + 1 == total);
7776                continue;
7777            }
7778            let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
7779            let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
7780            let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
7781            let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
7782            let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
7783                + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
7784            let j_bi = d_bi + (lme * r_bi as f64) as i64;
7785            let (mut dir, mut best) = (0u8, d_direct);
7786            if j0 < best { dir = 1; best = j0; }
7787            if j1 < best { dir = 2; best = j1; }
7788            if j_bi < best { dir = 3; best = j_bi; }
7789            let _ = best;
7790            // mb_skip_flag = 0, then the coded B MB.
7791            let sctx = 24
7792                + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7793                + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7794            cb_mb_skip(&mut cab, sctx, false);
7795            cs.mb_skip[addr] = false;
7796            let bspec = BInter { dir, l1, mv0, mv1 };
7797            let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
7798            emit_mb_cabac_b(&mut fe, &mut cab, &mut cs, dir, &plan, mb_x, mb_y);
7799            cab.encode_terminate(mb_idx + 1 == total);
7800        }
7801    }
7802
7803    while !w.is_byte_aligned() {
7804        w.write_bit(true);
7805    }
7806    for b in cab.into_bytes() {
7807        w.write_bits(b as u32, 8);
7808    }
7809    // B is non-reference: no deblock, no RefFrame (the decoder deblocks for display).
7810}
7811
7812/// Minimal all-B_Skip CABAC B-slice (the rare no-bracketing-anchor fallback in
7813/// `code_picture`): every MB is mb_skip_flag = 1. B is non-reference so the recon
7814/// is irrelevant; this only needs to be a legal CABAC slice.
7815pub fn encode_all_skip_b_cabac(w: &mut BitWriter, cfg: &EncoderConfig, qp: u8, n: usize) {
7816    let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
7817    for i in 0..n {
7818        // ctxInc = 24 + (left avail & not-skip) + (top avail & not-skip). Every
7819        // neighbour is either a skip (contributes 0) or unavailable (0) → always 24.
7820        cab.encode_decision(24, 1);
7821        cab.encode_terminate(i + 1 == n);
7822    }
7823    while !w.is_byte_aligned() {
7824        w.write_bit(true);
7825    }
7826    for b in cab.into_bytes() {
7827        w.write_bits(b as u32, 8);
7828    }
7829}