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/// H-23: smooth (x264-shape) mvd cost table, in quarter-bit units scaled to the
112/// same magnitude as the Exp-Golomb model so λ stays calibrated. `RFF_MVCOST=1`.
113static MV_COST_TAB: std::sync::OnceLock<Vec<u16>> = std::sync::OnceLock::new();
114fn build_mv_cost() -> Vec<u16> {
115    (0..4096u32)
116        .map(|a| {
117            let c = 2.0 * ((a + 1) as f64).log2() + 0.718 + if a != 0 { 1.0 } else { 0.0 };
118            // Round to quarter-bits then express in the caller's integer "bits"
119            // domain by keeping 4× resolution — λ is rescaled to match below.
120            (c * 4.0).round() as u16
121        })
122        .collect()
123}
124/// H-24: 0 = off (Exp-Golomb step, byte-identical), 1 = DISPATCHED per frame by
125/// the `b2_mgain` motion probe, 2 = force-on. The BD sign-flip (bus −1.31 /
126/// football −0.24 vs foreman +0.23 / akiyo +0.11) tracks MOTION: the smooth
127/// curve pays where |mvd| is large enough to leave the first bracket, and only
128/// adds noise where every vector already sits inside it.
129static MV_SMOOTH: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
130pub fn set_mv_smooth(on: bool) {
131    MV_SMOOTH.store(if on { 2 } else { 0 }, core::sync::atomic::Ordering::Relaxed)
132}
133pub fn set_mv_smooth_mode(m: u32) {
134    MV_SMOOTH.store(m.min(3), core::sync::atomic::Ordering::Relaxed)
135}
136/// Dispatch threshold on the per-frame mgain probe (`RFF_MVCOST_T`, default 0.10).
137/// Calibrated on the DEPLOYED probe: bus min-frame 0.185 and football med 0.208
138/// route ON; foreman med 0.164 is the boundary case, akiyo ~0.00 routes OFF.
139/// H-26: the measured TRUE table plus a COHERENCE BIAS on every d≠0 entry —
140/// the cheap scalar form of the MV-field externality H-25 root-caused (a chosen
141/// vector that leaves the predictor degrades the neighbours' medians; truth
142/// per-vector under-prices that shared damage). `RFF_MVCOST_BIAS` in bits
143/// (default 0 = pure truth), read once at first use.
144static MV_TRUE_BIASED: std::sync::OnceLock<Vec<u16>> = std::sync::OnceLock::new();
145fn build_true_biased() -> Vec<u16> {
146    let bias_q4 = (std::env::var("RFF_MVCOST_BIAS")
147        .ok()
148        .and_then(|v| v.parse::<f64>().ok())
149        .unwrap_or(1.0)
150        * 4.0)
151        .round() as u16;
152    crate::mvd_cost_tab::MVD_TRUE_COST4
153        .iter()
154        .enumerate()
155        .map(|(d, &c)| if d == 0 { c } else { c.saturating_add(bias_q4) })
156        .collect()
157}
158
159fn mv_smooth_t() -> f64 {
160    static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
161    *T.get_or_init(|| std::env::var("RFF_MVCOST_T").ok().and_then(|v| v.parse().ok()).unwrap_or(0.10))
162}
163/// 0 = step model, 1 = smooth (this frame routed on), 2 = smooth (forced),
164/// 3 = the MEASURED true-cost table (H-25) — no dispatch needed if it wins
165/// everywhere, since it is the truth both analytic models approximate.
166/// `frame_smooth` is the per-frame probe decision, carried on the frame state
167/// (like `sadfp`) — a process-global here races under the GOP-parallel encode.
168#[inline]
169fn mv_cost_kind(frame_smooth: bool) -> u32 {
170    match mv_smooth_mode() {
171        0 => 0,
172        // H-26 verdict: smooth/truth/truth+bias shuffle within ±0.2 BD fit-noise
173        // of each other at dispatch (bus prefers truth, football prefers smooth,
174        // none dominates), so the dispatch keeps its ORIGINALLY-GATED smooth
175        // ON-model; the measured/biased tables remain as modes 2/3 for research.
176        1 => frame_smooth as u32,
177        2 => 1, // archaeology: the x264 smooth curve, forced
178        _ => 2, // the biased-truth table, forced
179    }
180}
181#[inline]
182fn mv_smooth_mode() -> u32 {
183    match MV_SMOOTH.load(core::sync::atomic::Ordering::Relaxed) {
184        m @ 0..=3 => m,
185        _ => {
186            static E: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
187            // DEFAULT 1 = DISPATCHED (H-24). Owner's call: mean −0.27% BD is
188            // taken over minimax, accepting a known, bounded +0.16-0.18% on
189            // foreman-class content. `RFF_MVCOST=0` restores the pre-H-23 bytes.
190            *E.get_or_init(|| {
191                std::env::var("RFF_MVCOST").ok().and_then(|v| v.parse().ok()).unwrap_or(1)
192            })
193        }
194    }
195}
196
197/// Challenge-1 A3 escape hatch: `RFF_SATD_AVG=0` restores the materialize-then-SATD
198/// quarter-pel cost path (byte-identical either way — a bisection anchor, like
199/// `RFF_HPEL_REF`).
200/// H-14 R3 escape hatch: `RFF_MECTX=0` restores the per-eval safe dispatch
201/// (byte-identical either way — MeCtx returns exactly the safe path's values).
202fn mectx_enabled() -> bool {
203    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
204    *E.get_or_init(|| std::env::var("RFF_MECTX").map(|v| v != "0").unwrap_or(true))
205}
206
207fn satd_avg_enabled() -> bool {
208    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
209    *E.get_or_init(|| std::env::var("RFF_SATD_AVG").map(|v| v != "0").unwrap_or(true))
210}
211
212/// Track-B B2 (docs/lets-win-optimize.md): run the FULL-PEL phase of the non-fast
213/// motion search in the SAD domain (`psadbw`-class, ~3-4× cheaper per candidate) and
214/// reprice the winner in SATD before the rescue/sub-pel phases — the cost split
215/// every x264 preset uses (SAD fpel, SATD from subme≥2). ⚠ BITSTREAM-CHANGING (a
216/// different full-pel winner can emerge), so it ships opt-in until the per-clip
217/// 4-QP BD gate clears it. `set_me_sadfp` overrides; unset → `RFF_ME_SADFP` env,
218/// default OFF (off = byte-identical to the pre-B2 encoder).
219/// Modes: 0 = off (byte-identical), 1 = DISPATCHED per frame by the `b2_mgain`
220/// probe (the shipping shape), 2 = force-on everywhere (the truth-table A/B arm).
221static ME_SADFP: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
222pub fn set_me_sadfp(on: bool) {
223    // Harness semantics preserved: `true` = the force-on arm truth tables measure.
224    ME_SADFP.store(if on { 2 } else { 0 }, core::sync::atomic::Ordering::Relaxed)
225}
226pub fn set_me_sadfp_mode(m: u32) {
227    ME_SADFP.store(m.min(2), core::sync::atomic::Ordering::Relaxed)
228}
229fn me_sadfp_mode() -> u32 {
230    match ME_SADFP.load(core::sync::atomic::Ordering::Relaxed) {
231        u32::MAX => {
232            static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
233            // DEFAULT = 1 (dispatched) since the H-3 gate: 16-clip corpus mean
234            // −0.26% BD, wins bus −1.71 / football −1.84 / foreman −0.44 /
235            // shields −0.22, every former loss 0.00; residual tail (soccer +0.09,
236            // harbour +0.06) is BD-fit noise — it responds NON-monotonically to
237            // threshold changes (less B2 made soccer read WORSE, +0.18).
238            // `RFF_ME_SADFP=0` is the escape hatch reproducing the pre-B2 bytes.
239            *INIT.get_or_init(|| {
240                std::env::var("RFF_ME_SADFP").ok().and_then(|v| v.parse().ok()).unwrap_or(1)
241            })
242        }
243        m => m,
244    }
245}
246
247/// B2 dispatch threshold on the per-frame `b2_mgain` probe (`RFF_ME_SADT`).
248/// Calibrated on the DEPLOYED estimator (recon reference, sampled MBs), not the
249/// offline source-frame probe — the recurring R6 law.
250fn me_sadt() -> f64 {
251    static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
252    *T.get_or_init(|| std::env::var("RFF_ME_SADT").ok().and_then(|s| s.parse().ok()).unwrap_or(0.13))
253}
254fn me_sadt_dbg() -> bool {
255    static D: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
256    *D.get_or_init(|| std::env::var_os("RFF_ME_SADT_DBG").is_some())
257}
258
259/// Fixed-centre batched diamond passes on SAD-routed frames (`RFF_ME_FC=0` falls
260/// back to the cascading scalar walk — the bisection anchor). Fixed-centre differs
261/// from the cascade only when 2+ points improve in one pass, so it rides B2's BD
262/// gate; dispatched-OFF frames never take this path and stay byte-identical.
263/// ③ Sub-pel ring FC: fixed-centre argmin passes for the HALF-PEL step, batched
264/// through `satd_16x16_x4p` (two calls cover the 8-ring; candidates resolve to
265/// h/h/v/v and c/c/c/c plane reads from an integer centre). Quarter-step and any
266/// declined pass keep the cascading walk. Bitstream-changing → own gate
267/// (`AB_SPFC`), `RFF_SP_FC=0` anchor.
268static SP_FC: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
269pub fn set_sp_fc(on: bool) {
270    SP_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
271}
272fn sp_fc_enabled() -> bool {
273    match SP_FC.load(core::sync::atomic::Ordering::Relaxed) {
274        0 => false,
275        1 => true,
276        _ => {
277            static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
278            *E.get_or_init(|| std::env::var("RFF_SP_FC").map(|v| v != "0").unwrap_or(false))
279        }
280    }
281}
282
283static ME_FC: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
284pub fn set_me_fc(on: bool) {
285    ME_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
286}
287fn me_fc_enabled() -> bool {
288    match ME_FC.load(core::sync::atomic::Ordering::Relaxed) {
289        0 => false,
290        1 => true,
291        _ => {
292            static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
293            *E.get_or_init(|| std::env::var("RFF_ME_FC").map(|v| v != "0").unwrap_or(true))
294        }
295    }
296}
297
298/// H-13 SPLIT DISPATCH — measured and REFUTED as a free dispatch, shipped as an
299/// OPT-IN rung (default 0 = off = byte-identical). The premise "splits buy
300/// ~nothing on near-static frames" is FALSE: at T=0.03 akiyo read +2.45% BD,
301/// akiyo_qcif +2.02%, FourPeople +2.00% for only 1.10-1.15× — partition splits
302/// EARN BD on every measured content class (the third death of the split-gate
303/// idea: U2 T=400, the sum-weighted ceiling, now the mgain axis). foreman/bus
304/// route ON at any sane T (min frame mgain 0.061/0.185) and stay byte-identical.
305/// `RFF_SPLIT_MG` (fraction) / `set_split_mg` (milli): a priced speed rung, not
306/// a free lunch.
307static SPLIT_MG: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
308pub fn set_split_mg(milli: u32) {
309    SPLIT_MG.store(milli, core::sync::atomic::Ordering::Relaxed)
310}
311fn split_mg() -> f64 {
312    match SPLIT_MG.load(core::sync::atomic::Ordering::Relaxed) {
313        u32::MAX => {
314            static E: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
315            *E.get_or_init(|| {
316                std::env::var("RFF_SPLIT_MG").ok().and_then(|v| v.parse().ok()).unwrap_or(0.0)
317            })
318        }
319        m => m as f64 / 1000.0,
320    }
321}
322
323/// The flash veto: frames whose zero-MV residual is DC-shift-dominated beyond this
324/// fraction route OFF even at high mgain (`RFF_ME_SADDC`). Calibrated on the
325/// DEPLOYED per-frame values: crew's harmful ON-frames read dc 0.843–0.859 (the
326/// camera flashes) while every good ON-frame on bus/football/foreman reads ≤ 0.478
327/// — a 1.76× natural gap; 0.6 sits mid-gap with margin both ways.
328fn me_sad_dcmax() -> f64 {
329    static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
330    *T.get_or_init(|| std::env::var("RFF_ME_SADDC").ok().and_then(|s| s.parse().ok()).unwrap_or(0.6))
331}
332
333/// The B2 dispatch signal: mean over ~24 sampled interior MBs of
334/// `(SAD@zeroMV − bestSAD over a ±8 step-4 full-pel grid) / SAD@zeroMV` — how much
335/// a plain TRANSLATIONAL full-pel search improves on zero motion, i.e. exactly the
336/// surface B2's SAD diamond exploits. Offline (b2_signals, 16-clip truth table) it
337/// separates every B2 loss (crew flash 0.070, city 0.110, tempete 0.008) from
338/// every meaningful win (bus 0.323, football/foreman 0.164/0.165, shields 0.361);
339/// notably `me_wide_headroom` CANNOT be reused here — crew's headroom is high (20)
340/// but B2 loses there, because SAD overprices the DC shifts of its camera flashes.
341/// Returns `(mgain, dcfrac)`. `dcfrac` — mean `|Σcur − Σref| / SAD0` per sampled
342/// block — is the FLASH detector: under an illumination change the zero-MV residual
343/// is mostly a DC shift, which SAD prices fully but the Hadamard largely discounts,
344/// so SAD misranks candidates exactly there. Justified by the one clip the
345/// single-term gate got wrong (crew: high mgain on its motion frames, +0.54 BD).
346fn b2_mgain(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> (f64, f64) {
347    const WIDE: isize = 8;
348    const STEP: isize = 4;
349    const TARGET: usize = 24;
350    let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
351        if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
352            return None;
353        }
354        let (rx, ry) = (rx as usize, ry as usize);
355        let mut s = 0u32;
356        for dy in 0..16 {
357            let a = &sy[(by + dy) * cw + bx..][..16];
358            let b = &ref_y[(ry + dy) * cw + rx..][..16];
359            s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
360        }
361        Some(s)
362    };
363    let (mbw, mbh) = (cw / 16, ch / 16);
364    if mbw < 6 || mbh < 6 {
365        return (0.0, 0.0);
366    }
367    let inner = (mbw - 4) * (mbh - 4);
368    let stride = (inner / TARGET).max(1);
369    let (mut acc, mut dc, mut n) = (0.0f64, 0.0f64, 0u32);
370    let mut i = 0usize;
371    while i < inner {
372        let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
373        let (bx, by) = (mx * 16, my * 16);
374        if let Some(s0) = sad16(bx, by, bx as isize, by as isize) {
375            let (mut ms, mut mr) = (0u32, 0u32);
376            for dy in 0..16 {
377                ms += sy[(by + dy) * cw + bx..][..16].iter().map(|&v| v as u32).sum::<u32>();
378                mr += ref_y[(by + dy) * cw + bx..][..16].iter().map(|&v| v as u32).sum::<u32>();
379            }
380            dc += ms.abs_diff(mr) as f64 / (s0 + 1) as f64;
381            let mut best = s0;
382            let mut dy = -WIDE;
383            while dy <= WIDE {
384                let mut dx = -WIDE;
385                while dx <= WIDE {
386                    if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
387                        best = best.min(s);
388                    }
389                    dx += STEP;
390                }
391                dy += STEP;
392            }
393            acc += (s0 - best) as f64 / (s0 + 1) as f64;
394            n += 1;
395        }
396        i += stride;
397    }
398    if n == 0 { (0.0, 0.0) } else { (acc / n as f64, dc / n as f64) }
399}
400
401/// Track-B B3: cap on sub-pel ring ITERATIONS per step (`RFF_SP_MAXIT` /
402/// `set_sp_maxit`). 0 = unlimited (the default — byte-identical to the walk-to-
403/// convergence encoder); N caps each step's walk at N passes, the bounded budget
404/// x264's subme levels have always had. Bitstream-changing when set → BD-gated.
405static SP_MAXIT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
406pub fn set_sp_maxit(n: u32) {
407    SP_MAXIT.store(n, core::sync::atomic::Ordering::Relaxed)
408}
409fn sp_maxit() -> u32 {
410    match SP_MAXIT.load(core::sync::atomic::Ordering::Relaxed) {
411        u32::MAX => {
412            static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
413            *INIT.get_or_init(|| {
414                std::env::var("RFF_SP_MAXIT").ok().and_then(|v| v.parse().ok()).unwrap_or(0)
415            })
416        }
417        n => n,
418    }
419}
420
421/// B2 calibration: λ multiplier for the SAD-domain full-pel phase (`RFF_ME_SADL`,
422/// default 1.0). SATD distortion runs ~2× SAD's scale, so λ tuned for SATD weighs
423/// the rate term ~2× heavier in the SAD domain — 0.5 restores the SATD-era
424/// rate/distortion balance. Read once per process (hoisted per search).
425fn me_sadfp_lambda() -> f64 {
426    static E: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
427    // 0.5 = the calibrated default (SATD ≈ 2× SAD's scale; at 1.0 the rate term
428    // weighs double and foreman flips to a BD loss). Rides with the mode-1 default.
429    *E.get_or_init(|| {
430        std::env::var("RFF_ME_SADL").ok().and_then(|v| v.parse().ok()).unwrap_or(0.5)
431    })
432}
433
434/// Descent D: sub-pel ring census — evals/improvements by (step, ring position) and
435/// by loop ITERATION, so a position or an iteration that never pays is visible rather
436/// than assumed.
437#[cfg(feature = "profile")]
438pub mod spstats {
439    use core::sync::atomic::{AtomicU64, Ordering};
440    /// [step 0=half,1=quarter][position 0..8][0=evals,1=improvements]
441    pub static POS: [AtomicU64; 2 * 8 * 2] = [const { AtomicU64::new(0) }; 32];
442    /// [step][iteration 1..=6 clamped][0=evals,1=improvements]
443    pub static IT: [AtomicU64; 2 * 6 * 2] = [const { AtomicU64::new(0) }; 24];
444    #[inline]
445    pub fn ev(st: usize, pos: usize, it: u32) {
446        POS[(st * 8 + pos.min(7)) * 2].fetch_add(1, Ordering::Relaxed);
447        IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2].fetch_add(1, Ordering::Relaxed);
448    }
449    #[inline]
450    pub fn imp(st: usize, pos: usize, it: u32) {
451        POS[(st * 8 + pos.min(7)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
452        IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
453    }
454    /// Sub-pel evaluations that re-price an MV already evaluated in the SAME refinement.
455    pub static REDUNDANT: AtomicU64 = AtomicU64::new(0);
456    #[inline]
457    pub fn redundant() { REDUNDANT.fetch_add(1, Ordering::Relaxed); }
458    pub fn reset() {
459        for c in POS.iter() { c.store(0, Ordering::Relaxed); }
460        for c in IT.iter() { c.store(0, Ordering::Relaxed); }
461        REDUNDANT.store(0, Ordering::Relaxed);
462    }
463    pub fn snapshot() -> (Vec<u64>, Vec<u64>) {
464        (POS.iter().map(|c| c.load(Ordering::Relaxed)).collect(),
465         IT.iter().map(|c| c.load(Ordering::Relaxed)).collect())
466    }
467    pub fn redundant_count() -> u64 { REDUNDANT.load(Ordering::Relaxed) }
468}
469
470/// Descent B: which path does each ME cost evaluation actually take?
471#[cfg(feature = "profile")]
472pub mod satdpath {
473    use core::sync::atomic::{AtomicU64, Ordering};
474    pub static C: [AtomicU64; 3] = [const { AtomicU64::new(0) }; 3];
475    #[inline]
476    pub fn bump(i: usize) { C[i].fetch_add(1, Ordering::Relaxed); }
477    pub fn reset() { for c in C.iter() { c.store(0, Ordering::Relaxed); } }
478    pub fn snapshot() -> Vec<u64> { C.iter().map(|c| c.load(Ordering::Relaxed)).collect() }
479}
480
481/// The coarse-to-fine step ladder. DEFAULT `[16,8,4]` — the 64 and 32 rungs were
482/// REMOVED after the per-rung census showed they are ~39% of full-pel evaluations at a
483/// 0.05-0.84% hit rate, and the 20-clip 4-QP BD curve showed those rare hits are actively
484/// HARMFUL: a coarse jump finds a distant MV with marginally lower SATD, but it costs
485/// more mvd bits AND breaks the spatial coherence of the MV field, degrading every
486/// downstream neighbour's predictor. `lambda*mvbits` prices the first effect and is blind
487/// to the second. Dropping them is mean -0.93% BD-PSNR / -1.09% BD-SSIM with a WORST clip
488/// of +0.00%/+0.00% over 20 clips, and 1.15-1.57x fewer ME cost evaluations.
489///
490/// The 8 rung is load-bearing: `[16,4]` reads marginally better BD but makes football_cif
491/// do 1.55x MORE work, because the step-4 walk then has to crawl the distance the 8 rung
492/// covered in one hop. Reach and stride both matter; only the useless TOP is removed.
493///
494/// Bit i of the mask enables rung i of [64,32,16,8,4]. `RFF_DIA_LADDER=64,32,16,8,4`
495/// restores the pre-change ladder byte-for-byte; `set_dia_mask` overrides at runtime so a
496/// single process can measure several ladders.
497pub const DIA_RUNGS: [i32; 5] = [64, 32, 16, 8, 4];
498/// Rungs walked by default: `[16,8,4]`.
499pub const DIA_DEFAULT: u32 = 0b11100;
500pub static DIA_MASK: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
501pub fn set_dia_mask(m: u32) { DIA_MASK.store(m, core::sync::atomic::Ordering::Relaxed) }
502fn dia_mask() -> u32 {
503    let m = DIA_MASK.load(core::sync::atomic::Ordering::Relaxed);
504    if m != u32::MAX { return m; }
505    static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
506    *INIT.get_or_init(|| match std::env::var("RFF_DIA_LADDER") {
507        Ok(v) => {
508            let want: Vec<i32> = v.split(',').filter_map(|t| t.trim().parse().ok()).collect();
509            let mut m = 0u32;
510            for (i, r) in DIA_RUNGS.iter().enumerate() {
511                if want.contains(r) { m |= 1 << i; }
512            }
513            if m == 0 { DIA_DEFAULT } else { m }
514        }
515        Err(_) => DIA_DEFAULT,
516    })
517}
518
519/// Descent A: per-STEP-SIZE census of the coarse-to-fine diamond. The ladder is
520/// [64,32,16,8,4] quarter-pel (i.e. 16,8,4,2,1 full-pel) and each step walks until it
521/// stops improving. Counts evaluations AND improvements per step so a step that never
522/// pays can be identified rather than assumed.
523#[cfg(feature = "profile")]
524pub mod diastats {
525    use core::sync::atomic::{AtomicU64, Ordering};
526    /// [step_index][0]=evals, [1]=improvements
527    pub static C: [AtomicU64; 12] = [const { AtomicU64::new(0) }; 12];
528    #[inline]
529    pub fn ev(i: usize) { C[i * 2].fetch_add(1, Ordering::Relaxed); }
530    #[inline]
531    pub fn imp(i: usize) { C[i * 2 + 1].fetch_add(1, Ordering::Relaxed); }
532    pub fn reset() { for c in C.iter() { c.store(0, Ordering::Relaxed); } }
533    pub fn snapshot() -> Vec<(u64, u64)> {
534        (0..6).map(|i| (C[i * 2].load(Ordering::Relaxed), C[i * 2 + 1].load(Ordering::Relaxed))).collect()
535    }
536}
537
538/// Sub-pel refinement PATTERN (U1). Bit 0 = 4-point diamond ring instead of the
539/// 8-point square; bit 1 = single pass instead of walking to convergence.
540///
541/// Harvested from 280 k real refinements: ~29 evaluations each, but the LAST
542/// improvement lands at eval ~14–15 — **half of every refinement is spent confirming
543/// an answer already found** — and the first ring alone captures 64–72% of the total
544/// gain. An 8-point ring pays 8 evaluations for that confirmation; a 4-point diamond
545/// (what x264's subme uses) pays 4.
546///
547/// `RFF_SUBPEL_PAT`: 0 = 8-point + iterate (the pre-U1 default), 1 = 4-point +
548/// iterate, 2 = 8-point single pass, 3 = 4-point single pass.
549pub(crate) static SUBPEL_PAT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
550
551/// Learning-window size and ring-1 threshold (percent) for the U1 online dispatcher.
552/// `RFF_SUBPEL_DISPATCH=0` disables it (pure `RFF_SUBPEL_PAT` behaviour).
553pub(crate) static SP_DISPATCH: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
554
555fn sp_dispatch_cfg() -> (u32, i64) {
556    use std::sync::OnceLock;
557    let forced = SP_DISPATCH.load(std::sync::atomic::Ordering::Relaxed);
558    if forced == 0 {
559        return (0, 0);
560    }
561    static C: OnceLock<(u32, i64)> = OnceLock::new();
562    *C.get_or_init(|| {
563        // DEFAULT OFF — measured and refuted (see the U1 entry in
564        // docs/WHYS-speed-gap.md). It only delivers speed where a blanket pattern
565        // change already would (bus 1.47x) while costing BD where it delivers none
566        // (foreman +0.97% for 1.04x, mobile +0.33% for 0.98x), and mixing refinement
567        // quality across frames measured WORSE than a uniform cut (bus +0.81%
568        // dispatched vs +0.30% pat2-always) — the refinement feeds the reference
569        // chain, so per-frame inconsistency propagates.
570        let on = std::env::var("RFF_SUBPEL_DISPATCH").map(|s| s != "0").unwrap_or(false);
571        if !on {
572            return (0, 0);
573        }
574        let k = std::env::var("RFF_SUBPEL_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(200);
575        let t = std::env::var("RFF_SUBPEL_T").ok().and_then(|s| s.parse().ok()).unwrap_or(67);
576        (k, t)
577    })
578}
579
580/// Explicit override only; `None` means "use the preset's default".
581fn subpel_pattern_override() -> Option<u32> {
582    let v = SUBPEL_PAT.load(std::sync::atomic::Ordering::Relaxed);
583    if v != u32::MAX {
584        return Some(v);
585    }
586    if let Some(e) = std::env::var("RFF_SUBPEL_PAT").ok().and_then(|s| s.parse::<u32>().ok()) {
587        SUBPEL_PAT.store(e, std::sync::atomic::Ordering::Relaxed);
588        return Some(e);
589    }
590    None
591}
592
593fn subpel_pattern() -> u32 {
594    let v = SUBPEL_PAT.load(std::sync::atomic::Ordering::Relaxed);
595    if v != u32::MAX {
596        return v;
597    }
598    // Unset -> take the env default once and latch it, so the hot path stays a
599    // relaxed load rather than an env lookup.
600    let d = std::env::var("RFF_SUBPEL_PAT").ok().and_then(|s| s.parse().ok()).unwrap_or(0);
601    SUBPEL_PAT.store(d, std::sync::atomic::Ordering::Relaxed);
602    d
603}
604
605/// Observe-only HARVEST for the sub-pel refinement skip-gate (U1).
606///
607/// `me-subpel` is 141 ms of a 320 ms quality encode — 44% — at 241 candidate
608/// evaluations per macroblock. This tap records, per refinement, the NULL-ARM cost
609/// (the full-pel winner, i.e. what we would keep if we skipped) against the cost the
610/// refinement actually reached, so the skip-rate-vs-gain-kept ceiling can be swept
611/// offline before any gate is written. Writes nothing unless `RFF_SUBPEL_HARVEST`
612/// names a file.
613mod subpel_harvest {
614    use std::fs::File;
615    use std::io::Write;
616    use std::sync::{Mutex, OnceLock};
617
618    fn sink() -> &'static Option<Mutex<File>> {
619        static S: OnceLock<Option<Mutex<File>>> = OnceLock::new();
620        S.get_or_init(|| {
621            std::env::var("RFF_SUBPEL_HARVEST").ok().and_then(|p| {
622                let mut f = File::create(p).ok()?;
623                let _ = writeln!(f, "pre,post,lambda,w,h,evals,to_best,ring1");
624                Some(Mutex::new(f))
625            })
626        })
627    }
628
629    #[inline]
630    pub fn enabled() -> bool {
631        sink().is_some()
632    }
633
634    #[allow(clippy::too_many_arguments)]
635    pub fn record(pre: i64, post: i64, lambda: f64, w: usize, h: usize, evals: u32, to_best: u32, ring1: i64) {
636        if let Some(m) = sink() {
637            if let Ok(mut f) = m.lock() {
638                let _ = writeln!(f, "{pre},{post},{lambda:.4},{w},{h},{evals},{to_best},{ring1}");
639            }
640        }
641    }
642}
643
644/// A/B switch for serving B-direct 4×4 MC from the cached half-pel planes
645/// (`RFF_BDIRECT_PLANES=0` restores the direct `mc_luma` 6-tap). Byte-identical
646/// either way; the knob exists so the arm can be measured in one binary.
647fn bdirect_planes_enabled() -> bool {
648    use std::sync::OnceLock;
649    static ON: OnceLock<bool> = OnceLock::new();
650    *ON.get_or_init(|| std::env::var("RFF_BDIRECT_PLANES").map(|s| s != "0").unwrap_or(true))
651}
652
653fn me_batch_enabled() -> bool {
654    use std::sync::OnceLock;
655    static ON: OnceLock<bool> = OnceLock::new();
656    *ON.get_or_init(|| std::env::var("RFF_ME_BATCH").map(|s| s != "0").unwrap_or(true))
657}
658
659/// A 16-byte-aligned 16×16 luma block — the aligned `op1` openh264's SSE2 SAD/SATD
660/// kernels require (`movdqa`). Safe to construct (`forbid(unsafe)` holds); the asm
661/// FFI that consumes it lives in `rusty_h264-accel`. Only used on the `asm` feature.
662#[cfg(accel)]
663#[repr(align(16))]
664struct AlignedMb([u8; 256]);
665
666/// A B-slice 16×16 inter-coding spec: the prediction direction and the motion it
667/// uses. `dir` 1 = `B_L0_16x16`, 2 = `B_L1_16x16`, 3 = `B_Bi_16x16` (spec Table
668/// 7-14). List-0 is `refs[0]` (nearest past anchor); `l1` is List-1 (nearest
669/// future anchor). `mv0`/`mv1` are the List-0/List-1 motion vectors (quarter-pel).
670#[derive(Clone, Copy)]
671struct BInter<'a> {
672    dir: u8,
673    l1: &'a crate::RefFrame,
674    mv0: (i32, i32),
675    mv1: (i32, i32),
676    /// 0 = single 16x16 (use `dir`/`mv0`/`mv1`); 1 = 16x8; 2 = 8x16. When non-zero
677    /// `parts2` carries `(pred, mv0, mv1)` per partition with pred 1=L0 / 2=L1 / 3=Bi.
678    mvmode: u8,
679    parts2: [(u8, (i32, i32), (i32, i32)); 2],
680}
681
682/// 16-byte-aligned 256-`i16` DCT/coefficient buffer — the in-place `movdqa` quant
683/// kernel (`WelsQuantFour4x4_sse2`) requires aligned coefficients. `asm`-feature only.
684#[cfg(accel)]
685#[repr(align(16))]
686struct AlignedDct([i16; 256]);
687
688/// Luma variance of the 16×16 source MB at (mb_x, mb_y) — the content signal for
689/// the adaptive SAD↔SATD cost dispatch (high variance = detail = SAD misprices).
690/// `256·variance` scale (the /256 of the mean-square is kept integer); only the
691/// RELATIVE ordering matters for the per-frame percentile, so the constant drops.
692fn mb_variance(sy: &[u8], cw: usize, mb_x: usize, mb_y: usize) -> i64 {
693    let base = mb_y * 16 * cw + mb_x * 16;
694    // Accumulate in u32, not i64: the sum of 256 bytes maxes at 65280 and the sum
695    // of squares at 16.6M, so 64-bit accumulators (and a 64-bit multiply per
696    // pixel) were pure width — and they stop LLVM vectorising what is otherwise a
697    // textbook pair of reductions over 16 contiguous bytes.
698    let (mut s, mut ss) = (0u32, 0u32);
699    for r in 0..16 {
700        let row = &sy[base + r * cw..base + r * cw + 16];
701        for &p in row {
702            let v = p as u32;
703            s += v;
704            ss += v * v;
705        }
706    }
707    // Widen once at the end: s*s reaches 4.26e9, which only just fits u32.
708    ss as i64 - (s as i64) * (s as i64) / 256 // 256·variance, monotone in variance
709}
710
711/// Adaptive-Quantization per-MB QP map: flat (low-variance) macroblocks get a FINER
712/// QP (where blocking/banding is visible), busy ones a COARSER QP (where the eye
713/// masks error) — moving bits to where they're seen. The shift is `strength ·
714/// (log2 var − frame mean log2 var)`, so it's relative to THIS frame's texture
715/// distribution (content-invariant), rounded to an integer QP step and clamped.
716/// `strength == 0` → uniform base QP (byte-identical: every `mb_qp_delta` is 0).
717fn aq_qp_map(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize, base_qp: u8, strength: f64) -> Vec<u8> {
718    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncAq);
719    const AQ_DQP_MAX: i32 = 4;
720    let n = mb_w * mb_h;
721    if strength == 0.0 || n == 0 {
722        return vec![base_qp; n];
723    }
724    // Per-MB variance (the bit-cost weight) and its log2 (+1 avoids log2(0) on a flat
725    // MB → reads as maximally flat → finest QP).
726    let mut var = Vec::with_capacity(n);
727    let mut lv = Vec::with_capacity(n);
728    for my in 0..mb_h {
729        for mx in 0..mb_w {
730            let v = (mb_variance(sy, cw, mx, my) + 1) as f64;
731            var.push(v);
732            lv.push(v.log2());
733        }
734    }
735    let mean_lv = lv.iter().sum::<f64>() / n as f64;
736    // CONTENT-ADAPTIVE STRENGTH: back off where the log-variance SPREAD is high. A
737    // wide/bimodal spread means synthetic-ish content (flat regions beside detailed
738    // patterns) where "busy = maskable" FAILS and the patterns are salient — full AQ
739    // there costs PSNR. Natural content's spread is ~1 (keeps full strength); a
740    // synthetic pan's is ~6 (heavily reduced). Ramp 1.0→`AQ_SPREAD_MIN` over
741    // [`AQ_SPREAD_LO`, `AQ_SPREAD_HI`].
742    const AQ_SPREAD_LO: f64 = 1.5;
743    const AQ_SPREAD_HI: f64 = 5.0;
744    const AQ_SPREAD_MIN: f64 = 0.0; // extreme spread (pathological synthetic) → AQ OFF
745    let std_lv = (lv.iter().map(|&l| (l - mean_lv).powi(2)).sum::<f64>() / n as f64).sqrt();
746    let factor = (1.0 - (std_lv - AQ_SPREAD_LO) / (AQ_SPREAD_HI - AQ_SPREAD_LO)).clamp(AQ_SPREAD_MIN, 1.0);
747    let eff_strength = strength * factor;
748    // Per-MB QP shift (clamped): busy (log-var above mean) coarser, flat finer.
749    let dqp: Vec<i32> = lv
750        .iter()
751        .map(|&l| (eff_strength * (l - mean_lv)).round() as i32)
752        .map(|d| d.clamp(-AQ_DQP_MAX, AQ_DQP_MAX))
753        .collect();
754    // RATE COMPENSATION: AQ nets a rate change (coarsening a busy MB saves more bits
755    // than fining a flat one adds), so shift the whole frame's QP by `c` to restore
756    // the un-AQ rate — keeping `qp` meaningful. Bit model `bits_i ∝ var_i·2^(−qp_i/6)`
757    // (variance as the per-MB cost proxy): `c = 6·log2(Σ var·2^(−dqp/6) / Σ var)`.
758    let sum_v: f64 = var.iter().sum();
759    // `dqp` is clamped to [-AQ_DQP_MAX, AQ_DQP_MAX], so 2^(-d/6) has only nine
760    // possible values — but it was being recomputed with a `powf` for every
761    // macroblock of every frame. Same expression, evaluated once per offset:
762    // bit-identical, and it retires a transcendental from a per-macroblock loop.
763    let qstep: [f64; (2 * AQ_DQP_MAX + 1) as usize] =
764        std::array::from_fn(|i| 2f64.powf(-((i as i32 - AQ_DQP_MAX) as f64) / 6.0));
765    let sum_vs: f64 = var
766        .iter()
767        .zip(&dqp)
768        .map(|(&v, &d)| v * qstep[(d + AQ_DQP_MAX) as usize])
769        .sum();
770    let c = (6.0 * (sum_vs / sum_v).log2()).round() as i32;
771    dqp.iter()
772        .map(|&d| (base_qp as i32 + c + d).clamp(0, 51) as u8)
773        .collect()
774}
775
776/// Mean per-sampled-pixel residual after GLOBAL-motion compensation of `sy` from
777/// `ref_y` (coarse ±12 global ME + ±3 refine, subsampled interior). ~0 on a PURE pan
778/// (a single MV predicts the whole frame) — precisely the content where the local ME
779/// diamond never genuinely STALLS (its seed = the median = the pan MV is already
780/// right), so the `me_wide` rescue can only find SPURIOUS MVs that wreck the B-frame
781/// spatial-direct predictors. Gates `me_wide` off there — non-uniform content
782/// (real stalls, where me_wide wins) reads well above 0.
783/// Per-frame HEAD-ROOM probe for the `me_wide` rescue: on a small subsample of
784/// blocks, how much does a WIDE full-pel search beat a PREDICTOR-LOCAL one?
785///
786/// This measures what the rescue actually buys, before the macroblock loop and
787/// without committing any vector — unlike the online payoff gate, which scores its
788/// own SATD cost-cut *after* committing MVs and so only ever separated static
789/// content. Returns the mean relative SAD improvement, in percent.
790///
791/// Calibrated against the 20-clip per-clip BD truth table (docs/WHYS-speed-gap.md
792/// R5): me_wide earns its 1.4–5.1× on high-head-room content (bus +4.57, blue_sky
793/// +4.70, football +1.51, park_joy +0.91) and REGRESSES on low-head-room content
794/// (foreman_qcif −1.08, foreman_cif −0.16, tempete −0.12, mobile −0.03).
795///
796/// Deliberately PER-FRAME, not per-clip: cross-frame adaptive state is
797/// nondeterministic under the GOP-parallel encode path (a lesson already paid for
798/// by the rescue's own learning window).
799/// Head-room threshold (percent) for the `me_wide` frame gate. DEFAULT-ON at 16.
800///
801/// Calibrated on the DEPLOYED estimator (not the offline probe — they differ) and
802/// gated on the full 20-clip `video-tests` corpus plus four synthesized boundary
803/// clips, 4-QP BD-rate on PSNR and SSIM:
804///
805/// | | me_wide always-on | gated at 16 |
806/// |---|---|---|
807/// | real-corpus mean | +0.62% | +0.547% (88% retained) |
808/// | **worst clip** | **−1.08%** (foreman_qcif) | **0.00%** |
809/// | clips paying 1.1–3.6× for ~nothing | 13 | 0 |
810///
811/// Wins preserved: blue_sky +4.70, bus +4.37, park_joy +0.94, football +0.64,
812/// shields +0.20; synthesized fast-pan +6.73, rotation +1.72, zoom +1.11.
813/// Monotone non-regression — no clip is negative — which is what promotes this from
814/// a speed trade to a default.
815///
816/// `RFF_ME_HR=0` disables the gate and reproduces the pre-gate bytes exactly (the
817/// escape hatch / bisection anchor). Thresholds 13 and 16 both clear the boundary
818/// clip (foreman_cif +0.07 / +0.03); 10 does NOT (−0.23) — the threshold is
819/// calibrated on a narrow boundary pair, so treat it as re-tunable, not settled.
820fn me_wide_hr_thresh() -> f64 {
821    use std::sync::OnceLock;
822    static T: OnceLock<f64> = OnceLock::new();
823    *T.get_or_init(|| std::env::var("RFF_ME_HR").ok().and_then(|s| s.parse().ok()).unwrap_or(16.0))
824}
825
826/// Cached, because it is read per frame — an `env::var` there is its own tax.
827fn me_wide_hr_dbg() -> bool {
828    use std::sync::OnceLock;
829    static D: OnceLock<bool> = OnceLock::new();
830    *D.get_or_init(|| std::env::var_os("RFF_ME_HR_DBG").is_some())
831}
832
833fn me_wide_headroom(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
834    const LOCAL: isize = 2; // a well-seeded diamond's effective reach
835    const WIDE: isize = 24; // the rescue grid's half-extent
836    const STEP: isize = 4; // coarse: this is a frame-level statistic, not a search
837    const TARGET: usize = 24; // samples per frame — keep the probe ~0.5% of a frame
838    let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
839        if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
840            return None;
841        }
842        let (rx, ry) = (rx as usize, ry as usize);
843        let mut s = 0u32;
844        for dy in 0..16 {
845            let a = &sy[(by + dy) * cw + bx..][..16];
846            let b = &ref_y[(ry + dy) * cw + rx..][..16];
847            s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
848        }
849        Some(s)
850    };
851    // Interior blocks only (the probe must not measure edge clamping), spread over
852    // the frame so one moving object cannot dominate.
853    let (mbw, mbh) = (cw / 16, ch / 16);
854    if mbw < 6 || mbh < 6 {
855        return 0.0;
856    }
857    let inner = (mbw - 4) * (mbh - 4);
858    let stride = (inner / TARGET).max(1);
859    let (mut acc, mut n) = (0.0f64, 0u32);
860    let mut i = 0usize;
861    while i < inner {
862        let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
863        let (bx, by) = (mx * 16, my * 16);
864        let mut best_local = u32::MAX;
865        for dy in -LOCAL..=LOCAL {
866            for dx in -LOCAL..=LOCAL {
867                if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
868                    best_local = best_local.min(s);
869                }
870            }
871        }
872        let mut best_wide = best_local;
873        let mut dy = -WIDE;
874        while dy <= WIDE {
875            let mut dx = -WIDE;
876            while dx <= WIDE {
877                if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
878                    best_wide = best_wide.min(s);
879                }
880                dx += STEP;
881            }
882            dy += STEP;
883        }
884        if best_local > 0 {
885            acc += (best_local - best_wide) as f64 / best_local as f64;
886            n += 1;
887        }
888        i += stride;
889    }
890    if n == 0 {
891        0.0
892    } else {
893        100.0 * acc / n as f64
894    }
895}
896
897fn global_mc_residual(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
898    if cw < 48 || ch < 48 {
899        return f64::INFINITY;
900    }
901    let sad = |dx: isize, dy: isize| -> u64 {
902        let mut s = 0u64;
903        let mut y = 16;
904        while y < ch - 16 {
905            let cbase = (y * cw) as isize;
906            let rbase = (y as isize + dy) * cw as isize + dx;
907            let mut x = 16isize;
908            while x < (cw - 16) as isize {
909                let c = sy[(cbase + x) as usize] as i32;
910                let r = ref_y[(rbase + x) as usize] as i32;
911                s += (c - r).unsigned_abs() as u64;
912                x += 8;
913            }
914            y += 8;
915        }
916        s
917    };
918    let (mut best, mut bc) = ((0isize, 0isize), u64::MAX);
919    let mut dy = -12;
920    while dy <= 12 {
921        let mut dx = -12;
922        while dx <= 12 {
923            let c = sad(dx, dy);
924            if c < bc {
925                bc = c;
926                best = (dx, dy);
927            }
928            dx += 4;
929        }
930        dy += 4;
931    }
932    for dy in best.1 - 3..=best.1 + 3 {
933        for dx in best.0 - 3..=best.0 + 3 {
934            let c = sad(dx, dy);
935            if c < bc {
936                bc = c;
937            }
938        }
939    }
940    let nx = (16..cw - 16).step_by(8).count();
941    let ny = (16..ch - 16).step_by(8).count();
942    bc as f64 / (nx * ny).max(1) as f64
943}
944
945/// Adds the mb-tree per-MB QP offset (TEMPORAL AQ — [`crate::mbtree`]) to the
946/// spatial-AQ `aq_qp` map in place. An empty `qpo` (mb-tree off) or a length
947/// mismatch is a no-op → byte-identical. Shared by the CAVLC and CABAC slice paths.
948fn apply_mbtree_qpo(aq_qp: &mut [u8], qpo: &[i32]) {
949    if qpo.len() == aq_qp.len() {
950        for (q, &o) in aq_qp.iter_mut().zip(qpo) {
951            *q = (*q as i32 + o).clamp(0, 51) as u8;
952        }
953    }
954}
955
956/// IMPLICIT bi-prediction weights `(w0, w1)` from POC distances (spec §8.4.2.3.2,
957/// `weighted_bipred_idc == 2`), IDENTICAL to the decoder's `implicit_weights`. The
958/// closer anchor gets more weight; an equidistant B (`bframes == 1`) yields 32:32,
959/// i.e. the plain average. `(32, 32)` fallback for the degenerate/out-of-range cases
960/// the decoder also averages (no long-term refs here).
961fn implicit_bi_weights(cur_poc: i32, l0_poc: i32, l1_poc: i32) -> (i32, i32) {
962    let td = (l1_poc - l0_poc).clamp(-128, 127);
963    let tb = (cur_poc - l0_poc).clamp(-128, 127);
964    if td == 0 {
965        return (32, 32);
966    }
967    let tx = (16384 + td.abs() / 2) / td;
968    let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
969    let w1 = dsf >> 2;
970    if !(-64..=128).contains(&w1) {
971        return (32, 32);
972    }
973    (64 - w1, w1)
974}
975
976/// Bi-prediction blend of two motion-compensated samples `p` (List-0) and `q`
977/// (List-1) under weights `(w0, w1)` — the decoder's `b_mc` blend. `(32, 32)` is the
978/// plain `(p+q+1)>>1` average.
979#[inline(always)]
980fn bi_blend(p: i32, q: i32, w: (i32, i32)) -> u8 {
981    ((p * w.0 + q * w.1 + 32) >> 6).clamp(0, 255) as u8
982}
983
984/// Zig-zag scan of a raster i16 4×4 block into scan-order i32 — the fused-path
985/// twin of `scan_4x4_dcac(&q_blocks[..])`, reading quantized levels straight from
986/// the hot i16 DCT buffer. Byte-identical: the i16→i32 widening of a quant level
987/// is exact (levels always fit i16, being the input to the i16 idct kernel).
988#[cfg(accel)]
989#[inline]
990fn scan_4x4_dcac_i16(d: &[i16]) -> [i32; 16] {
991    [
992        d[0] as i32, d[1] as i32, d[4] as i32, d[8] as i32, d[5] as i32, d[2] as i32,
993        d[3] as i32, d[6] as i32, d[9] as i32, d[12] as i32, d[13] as i32, d[10] as i32,
994        d[7] as i32, d[11] as i32, d[14] as i32, d[15] as i32,
995    ]
996}
997
998
999/// Per-frame intra encoder state: reconstructed planes (coded size) and the
1000/// per-4×4-block non-zero-coefficient counts used for CAVLC context.
1001pub struct FrameEncoder {
1002    mb_w: usize,
1003    mb_h: usize,
1004    qp: u8,  // the CURRENT macroblock's target QPy (AQ varies it per MB)
1005    qpc: u8, // chroma QP for `qp`
1006    /// Running QPy of the last macroblock that coded an `mb_qp_delta` (spec QPY_PREV).
1007    /// `mb_qp_delta = qp − cur_qp`; a skip / cbp==0 MB codes no delta and inherits it.
1008    cur_qp: u8,
1009    /// Implicit bi-prediction weights `(w0, w1)` for the current B-frame (from its
1010    /// L0/L1 anchor POC distances). `(32, 32)` = plain average (P/I frames, `bframes
1011    /// == 1`); unequal for `bframes > 1`.
1012    bi_w: (i32, i32),
1013    cw: usize, // coded luma width
1014    ccw: usize, // coded chroma width
1015    // 16-byte aligned (the openh264 deblock/MC/intra asm load aligned row chunks).
1016    rec_y: AlignedBytes,
1017    rec_u: AlignedBytes,
1018    rec_v: AlignedBytes,
1019    nnz_y: Vec<u8>,    // (mb_w*4) x (mb_h*4)
1020    nnz_c: [Vec<u8>; 2], // each (mb_w*2) x (mb_h*2)
1021    modes_y: Vec<u8>,  // intra4x4 mode per 4×4 block (2=DC for I_16x16 blocks)
1022    coded_y: Vec<bool>, // whether each 4×4 block is reconstructed (top-right avail)
1023    mv_y: Vec<(i32, i32)>, // motion vector per 4×4 block (quarter-pel) — List-0
1024    inter_y: Vec<bool>, // whether each 4×4 block is inter-coded
1025    ref_idx_y: Vec<i32>, // reference index per 4×4 block (-1 = intra/uncoded) — List-0
1026    // B-slice List-1 motion field (empty for P/I). B_L1/B_Bi commit here so a later
1027    // partition's List-1 median predictor sees it, mirroring the decoder's
1028    // `mv_neighbors_list(.., 1)` over `mv1`/`ref_idx1`.
1029    mv1_y: Vec<(i32, i32)>,
1030    ref_idx1_y: Vec<i32>,
1031    idz: i64, // intra dead-zone divisor: 2 for all-intra, 3 when frames reference each other
1032    rdoq_strength: f64, // CABAC trellis (RDOQ) strength; 0 = off (hard quantize, CAVLC path)
1033    transform_8x8: bool, // High-profile 8x8 transform enabled (transform_8x8_mode_flag)
1034    sub8x8: bool, // P_8x8 sub-partition motion (four 8x8 MVs per MB)
1035    me_wide: bool, // adaptive wide ME grid search rescue (diamond stalls on flat surfaces)
1036    /// Track-B B2 for THIS frame: SAD-domain full-pel phase. Set at construction
1037    /// (force mode), or per frame by the `b2_mgain` dispatcher (mode 1).
1038    sadfp: bool,
1039    /// H-24 mv-cost SHAPE routing for THIS frame (mv_smooth mode 1), set by the
1040    /// same `b2_mgain` probe. Per-frame state, NOT a global: the GOP-parallel
1041    /// encode runs frames concurrently and a global store races across workers.
1042    mv_smooth: bool,
1043    /// H-13: search partition splits this frame (routed off on near-static frames).
1044    do_splits: bool,
1045    me_wide_var: u64, // per-pixel source variance below which a block is "flat"
1046    me_rescue: i64, // per-pixel residual SATD (on a flat block) that flags a diamond stall
1047    me_wide_coh: f64, // gate me_wide off when the frame's global-MC residual is below this (pure pan)
1048    me_range: i32, // rescue grid half-range in px (16 = ±16; wider reaches FAST motion the diamond misses)
1049    me_fast: bool, // also fire the rescue on HIGH-VARIANCE high-residual blocks (fast-motion stalls, not just flat)
1050    // ONLINE per-frame rescue-payoff gate (adaptive; WITHIN-frame so it stays
1051    // deterministic under the frame-parallel encode). Run the real rescue on the
1052    // first `me_learn` stalls of a frame, count how many the fine grid improves by
1053    // ≥6.25%, and if that fraction is below `me_payoff_pct`% disable the rescue for
1054    // the rest of the frame. This separates genuine diamond stalls (tsrc/zoom fine
1055    // grid improves ~33% of fires) from IRREDUCIBLE residual (rotation/fractal ~5-8%)
1056    // using the ACTUAL neighbour-seeded diamond — the only faithful signal (a cheap
1057    // SAD proxy from (0,0) inverts it: rot reads as highest-payoff). Frame-level, so
1058    // no per-block selection concentrates the B-direct-poisoning spurious MVs.
1059    me_learn: u32,
1060    me_payoff_pct: u32,
1061    /// U1 online sub-pel dispatcher (within-frame, so it stays deterministic under
1062    /// GOP-parallel encode). For the first `SP_LEARN` refinements of a frame we run
1063    /// the full 8-point+iterate pattern and accumulate how much of the total gain the
1064    /// FIRST ring captured; once the window fills, a frame whose gain is concentrated
1065    /// in ring 1 switches to the single-pass pattern for the rest of the frame.
1066    ///
1067    /// Harvested justification: ring-1 captures 63.7% of the gain on foreman (which
1068    /// loses +2.34% BD to a blanket single-pass) against 69.9–71.9% on bus/mobile
1069    /// (which lose only +0.30/+0.74% and gain 1.08–1.31×). The fraction separates the
1070    /// content that can afford the cut from the content that cannot.
1071    sp_single_pass: bool,
1072    /// U5-struct: when set, `motion_search` returns its FULL-PEL winner and skips
1073    /// sub-pel refinement entirely. The partition driver uses this to search all
1074    /// candidate shapes cheaply, pick one, and refine ONLY the winner's sub-blocks.
1075    /// Measured ceiling: 3.4–6.4× less sub-pel work (the losing shapes' refinements
1076    /// are pure waste), i.e. ~1.42× whole-encode at 44% sub-pel share.
1077    sp_defer: std::cell::Cell<bool>,
1078    sp_learn_n: std::cell::Cell<u32>,
1079    sp_ring1: std::cell::Cell<i64>,
1080    sp_total: std::cell::Cell<i64>,
1081    sp_1pass: std::cell::Cell<bool>,
1082    resc_n: std::cell::Cell<u32>,   // stalls the fine grid ran on this frame (learning phase)
1083    resc_big: std::cell::Cell<u32>, // of those, how many it improved ≥6.25%
1084    resc_off: std::cell::Cell<bool>, // rescue disabled for the rest of this frame
1085    inter8x8: u8, // inter 8x8-transform dispatch: 0=off, 1=always-RD, 2=content-adaptive
1086    inter8_pen: i64, // extra rate charge (nonzero-equiv) on the inter 8x8 candidate
1087    fast: bool, // Preset::Fast — SATD mode decision (no RDO), 16×16/I_16x16 only
1088    skip_accel_check: bool, // A/B knob: whole-MB psadbw gate in the P_Skip free-check
1089    coded_path_v2: bool,    // A/B knob: route inter coding through encode_inter_mb_v2
1090    tune_lambda_scale: f64, // tuning knob: scale on the RD λ (1.0 = standard)
1091    tune_intra_penalty: f64,
1092    satd_q: f64,               // adaptive: fraction of high-variance MBs routed to SATD cost
1093    subpel_force: bool,        // force sub-pel refinement even in the fast preset
1094    me_snap: bool,             // snap the diamond centre to integer-pel (see config)
1095    me_subpel_iter: bool,      // walk the sub-pel refine to convergence
1096    greedy_skip: bool,         // quality preset's SAD-thresholded P_Skip (PredictSadSkip)
1097    greedy_min_free: u32,      // online free-skip % gating greedy_skip on this frame
1098    rd_skip: bool,             // decide P_Skip by J = SSD + lambda*bits, not exact-zero residual
1099    rd_skip_min_free: u32,     // online free-skip % gating rd_skip on this frame
1100    rd_skip_fast_t: f64,       // skip-gate on SSD(skip)/lambda; <= 0 prices every candidate
1101    satd_var_thresh: i64,      // per-frame variance threshold for the routing (set in a pre-pass)
1102    aq_strength: f64,          // adaptive quantization: per-MB QP modulation strength (0 = off)
1103    mb_use_satd: bool,         // per-MB: this MB uses the SATD cost this decision
1104    // Per-MB luma nnz prediction cache (openh264 scan8 style): a padded 5×5 grid,
1105    // block (lbx,lby) at (lby+1)*5+(lbx+1); row 0 = top neighbours, col 0 = left.
1106    // Unavailable edges hold the sentinel 0x80, so the nnz predict is branchless.
1107    nnz_l_cache: [u8; 25],
1108    // Same, per chroma plane: a padded 3×3 grid for the 2×2 chroma blocks.
1109    nnz_c_cache: [[u8; 9]; 2],
1110    // openh264 predicted-SAD skip apparatus (per MB, mb_w×mb_h): the P_Skip
1111    // prediction's luma SAD, and whether the MB was actually skipped. The greedy
1112    // skip threshold for an MB is the median of its skip *neighbours'* skip SADs
1113    // (`PredictSadSkip`) — so skip propagates only from already-skip regions
1114    // (seeded by free skips) and self-limits, instead of a fixed bound that drifts.
1115    mb_skip_sad: Vec<u32>,
1116    mb_was_skip: Vec<bool>,
1117}
1118
1119/// A chosen inter coding for a macroblock: `mb_type` and, per partition, the
1120/// reference index and motion vector.
1121type InterChoice = (u8, Vec<(i32, (i32, i32))>);
1122
1123/// Approximate marginal rate (bits) of one `P_Skip` — it only lengthens the
1124/// surrounding `mb_skip_run` Exp-Golomb code slightly.
1125const SKIP_RATE_BITS: f64 = 1.0;
1126
1127
1128
1129/// EXTERNAL MV SCORING (`RFF_MV_CMP=1`). Holds another encoder's motion field
1130/// (per frame, 4x4-block raster) so our own coder can price ITS vectors against
1131/// ours under REAL coded bits instead of SATD — the only way to tell a bad search
1132/// from a bad cost function.
1133pub static EXT_MV: std::sync::Mutex<Vec<Vec<(i32, i32)>>> = std::sync::Mutex::new(Vec::new());
1134/// [n, our bits, ext bits, our SSD, ext SSD, ext won on J, MVs differing]
1135pub static MVCMP: [std::sync::atomic::AtomicU64; 7] = {
1136    const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1137    [Z; 7]
1138};
1139pub static MVCMP_FRAME: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1140/// Replace our chosen vector with the external field's, for EVERY macroblock where
1141/// that field used a single 16x16 partition. Transplanting one vector in isolation
1142/// is meaningless — `mvd` is coded against the NEIGHBOURS' vectors, so a lone
1143/// foreign vector prices against the wrong predictor. Only a whole coherent field
1144/// can be compared fairly.
1145fn mv_force_on() -> bool {
1146    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1147    *ON.get_or_init(|| std::env::var("RFF_MV_FORCE").map_or(false, |v| v != "0"))
1148}
1149fn mv_cmp_on() -> bool {
1150    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1151    *ON.get_or_init(|| std::env::var("RFF_MV_CMP").map_or(false, |v| v != "0"))
1152}
1153
1154/// [full-pel SATD evals, INTERPOLATED SATD evals] — `RFF_MC_COUNT=1`.
1155/// x264 precomputes half-pel planes once per frame; we run the 6-tap filter per
1156/// candidate, so this ratio prices that difference.
1157pub static MC_COUNT: [std::sync::atomic::AtomicU64; 2] = [
1158    std::sync::atomic::AtomicU64::new(0),
1159    std::sync::atomic::AtomicU64::new(0),
1160];
1161fn mc_count_on() -> bool {
1162    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1163    *ON.get_or_init(|| std::env::var("RFF_MC_COUNT").map_or(false, |v| v != "0"))
1164}
1165
1166/// [n, sum our cost, sum oracle cost, blocks the oracle beat us on, cost() evals]
1167pub static ME_PROBE: [std::sync::atomic::AtomicU64; 7] = {
1168    const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1169    [Z; 7]
1170};
1171
1172/// Cached — an `env::var` inside the ME loop inflated it 4x when probed naively.
1173fn me_oracle_on() -> bool {
1174    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1175    *ON.get_or_init(|| std::env::var("RFF_ME_ORACLE").map_or(false, |v| v != "0"))
1176}
1177
1178/// RDO early-termination gate. Sub-partitions (16×8 / 8×16) only help at motion
1179/// boundaries, which show up as a heavy 16×16 residual; below this many coded bits
1180/// the 16×16 already fits, so skip their motion search and trials. (Intra is *not*
1181/// gated — it can win even against a cheap inter prediction, so gating it on inter
1182/// cost regresses compression badly on textured content.)
1183const SPLIT_GATE_BITS: f64 = 60.0;
1184
1185/// Fast preset: signalling-cost penalty (in bits, SATD-weighted by √λ) charged to
1186/// the intra candidate so it only wins a P-macroblock when its prediction is
1187/// clearly better than inter — intra's `mb_type` + modes cost more to signal.
1188const FAST_INTRA_PENALTY_BITS: f64 = 24.0;
1189
1190/// A snapshot of one macroblock's per-block grids and reconstruction region,
1191/// used to roll back a trial encode during RD mode decision.
1192///
1193/// Every field is a `Vec`, so building one from scratch is ten heap allocations.
1194/// The RD skip decision snapshots on EVERY candidate macroblock, which made that
1195/// allocation traffic the decision's dominant cost — hence
1196/// [`save_mb_into`](FrameEncoder::save_mb_into), which refills a reused buffer.
1197#[derive(Default)]
1198struct MbState {
1199    rec_y: Vec<u8>,
1200    rec_u: Vec<u8>,
1201    rec_v: Vec<u8>,
1202    nnz_y: Vec<u8>,
1203    nnz_c: [Vec<u8>; 2],
1204    mv_y: Vec<(i32, i32)>,
1205    inter_y: Vec<bool>,
1206    ref_idx_y: Vec<i32>,
1207    coded_y: Vec<bool>,
1208    modes_y: Vec<u8>,
1209    /// QPY_PREV. `qp_delta()` MUTATES this as a side effect of coding
1210    /// `mb_qp_delta`, so a trial encode advances it; without restoring it the
1211    /// real encode then codes its delta against the wrong predecessor and the
1212    /// decoder's QP diverges from the encoder's — a silent stream corruption,
1213    /// not a quality tweak.
1214    cur_qp: u8,
1215}
1216
1217/// Edge-clamped, coded-size source planes (luma, Cb, Cr).
1218/// Fast-preset pruned I4x4 mode search ({MPM, DC, V, H} instead of all 9 — the
1219/// x264-ultrafast-style candidate set). DEFAULT ON for the fast preset (gated:
1220/// +0.5% size at +0.02 dB on all-intra, +17% all-intra speed); RUSTY_FAST_INTRA=0
1221/// restores the exhaustive 9-mode search (the pre-flip bitstream).
1222fn fast_intra_enabled() -> bool {
1223    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1224    *ON.get_or_init(|| std::env::var("RUSTY_FAST_INTRA").map_or(true, |v| v != "0"))
1225}
1226
1227fn coded_source(cfg: &EncoderConfig, frame: &YuvFrame) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
1228    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSource);
1229    let cw = cfg.mb_width() * 16;
1230    let ch = cfg.mb_height() * 16;
1231    // MB-aligned frame: the clamp is the identity — a plane memcpy (clone) replaces
1232    // the per-pixel clamp loop (bit-exact: same bytes).
1233    if frame.width == cw && frame.height == ch {
1234        return (frame.y.clone(), frame.u.clone(), frame.v.clone());
1235    }
1236    let y = clamp_plane(&frame.y, frame.width, frame.height, cw, ch);
1237    let u = clamp_plane(&frame.u, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1238    let v = clamp_plane(&frame.v, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1239    (y, u, v)
1240}
1241
1242/// Edge-extends `plane` from `w`×`h` to the coded `ow`×`oh`, replicating the last
1243/// row/column — the source form the MB grid needs.
1244///
1245/// Row-wise, because the per-pixel form is O(pixels) of scalar `min`+multiply and is
1246/// the DOMINANT cost of `enc-source-copy`: every frame whose height is not a multiple
1247/// of 16 takes this path, which includes all 1080p content (1080/16 = 67.5 → coded
1248/// height 1088). The stage measured 579 ms over the corpus while the three plane
1249/// clones on the MB-aligned fast path account for only ~135 ms of it.
1250///
1251/// Byte-identical to the per-pixel form (`clamp_plane_per_pixel`, kept as the test
1252/// oracle): `x.min(w-1)` is the identity below `w` and pins to the last column above
1253/// it, so a row is a `copy_from_slice` plus a `fill`; `y.min(h-1)` makes the
1254/// overhanging rows copies of the final row. Both lower to memcpy/memset.
1255fn clamp_plane(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1256    let mut out = vec![0u8; ow * oh];
1257    for y in 0..oh {
1258        let sy = y.min(h - 1);
1259        let src = &plane[sy * w..sy * w + w];
1260        let dst = &mut out[y * ow..y * ow + ow];
1261        if ow <= w {
1262            dst.copy_from_slice(&src[..ow]);
1263        } else {
1264            dst[..w].copy_from_slice(src);
1265            dst[w..].fill(src[w - 1]);
1266        }
1267    }
1268    out
1269}
1270
1271/// The original per-pixel edge extension — kept as the correctness oracle for
1272/// [`clamp_plane`], per the scalar-twin discipline.
1273#[cfg(test)]
1274fn clamp_plane_per_pixel(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1275    let mut out = vec![0u8; ow * oh];
1276    for y in 0..oh {
1277        for x in 0..ow {
1278            out[y * ow + x] = plane[y.min(h - 1) * w + x.min(w - 1)];
1279        }
1280    }
1281    out
1282}
1283
1284#[cfg(test)]
1285mod source_tests {
1286    use super::*;
1287
1288    #[test]
1289    fn clamp_plane_matches_per_pixel_oracle() {
1290        let mut s: u32 = 0xDEAD_BEEF;
1291        let mut rnd = || {
1292            s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
1293            (s >> 24) as u8
1294        };
1295        // Real coded geometries plus adversarial ones: width-only overhang,
1296        // height-only overhang (the 1080p case), both, and neither.
1297        let cases = [
1298            (1920usize, 1080usize, 1920usize, 1088usize), // 1080p luma
1299            (960, 540, 960, 544),                         // 1080p chroma
1300            (352, 288, 352, 288),                         // exactly aligned
1301            (100, 100, 112, 112),                         // both axes overhang
1302            (37, 5, 48, 16),                              // tiny + ragged
1303            (16, 1, 16, 16),                              // single source row
1304            (1, 1, 16, 16),                               // single sample
1305        ];
1306        for (w, h, ow, oh) in cases {
1307            let plane: Vec<u8> = (0..w * h).map(|_| rnd()).collect();
1308            assert_eq!(
1309                clamp_plane(&plane, w, h, ow, oh),
1310                clamp_plane_per_pixel(&plane, w, h, ow, oh),
1311                "clamp mismatch for {w}x{h} -> {ow}x{oh}"
1312            );
1313        }
1314    }
1315}
1316
1317impl FrameEncoder {
1318    fn new(cfg: &EncoderConfig) -> Self {
1319        let (mb_w, mb_h) = (cfg.mb_width(), cfg.mb_height());
1320        let (cw, ch) = (mb_w * 16, mb_h * 16);
1321        let (ccw, cch) = (cw / 2, ch / 2);
1322        Self {
1323            mb_w,
1324            mb_h,
1325            qp: cfg.qp,
1326            qpc: chroma_qp(cfg.qp),
1327            cur_qp: cfg.qp,
1328            bi_w: (32, 32),
1329            cw,
1330            ccw,
1331            rec_y: AlignedBytes::zeroed(cw * ch),
1332            rec_u: AlignedBytes::zeroed(ccw * cch),
1333            rec_v: AlignedBytes::zeroed(ccw * cch),
1334            nnz_y: vec![0; (mb_w * 4) * (mb_h * 4)],
1335            nnz_c: [vec![0; (mb_w * 2) * (mb_h * 2)], vec![0; (mb_w * 2) * (mb_h * 2)]],
1336            modes_y: vec![2; (mb_w * 4) * (mb_h * 4)],
1337            coded_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1338            mv_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1339            inter_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1340            ref_idx_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1341            mv1_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1342            ref_idx1_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1343            // All-intra (no inter references) tolerates the larger dead-zone; in
1344            // an I+P stream the IDR is a reference, so keep the standard offset.
1345            idz: if cfg.gop_size <= 1 { 2 } else { 3 },
1346            rdoq_strength: 0.0, // set >0 only in the CABAC slice coders
1347            transform_8x8: cfg.transform_8x8,
1348            // sub8x8 stays OPT-IN: the four P_8x8 sub-MVs feed the B-frames'
1349            // spatial-direct predictor, so on DIVERGENT motion (rotation/zoom/mixed)
1350            // it regresses with B-frames (mixed +0.24%, rot +0.42%, zoom +0.40%) —
1351            // a global effect its local RD gate can't see, and no clean dispatch
1352            // signal separates it yet (unlike me_wide's pure-pan coherence gate).
1353            // DEFAULT-ON for Quality (net real-content win; a 6-channel discovery
1354            // harvest proved no cheap gate beats always-on). Quality-only (Fast never
1355            // runs it). env RFF_SUB8X8 (0/1) > cfg.sub_8x8 (Some) > preset default.
1356            sub8x8: std::env::var("RFF_SUB8X8").ok().map(|s| s == "1")
1357                .or(cfg.sub_8x8)
1358                .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1359            // me_wide is DEFAULT-ON for the Quality preset. VALIDATED 2026-07-27 on the
1360            // full 20-clip `video-tests` Derf corpus (4-QP BD-rate, PSNR+SSIM, anchor =
1361            // me_wide ON): **mean +0.62% BD-PSNR / +0.69% BD-SSIM**, i.e. turning it off
1362            // costs that much. Biggest wins blue_sky +4.70, bus +4.57, football +1.51,
1363            // park_joy +0.91; synthesized boundary content (smooth fast-pan / rotation /
1364            // zoom) reaches +2.6..+6.7%. The static clips (akiyo, FourPeople) sit at
1365            // exactly 0.00 at ~1.0x — the online payoff gate correctly disables it there.
1366            //
1367            // ⚠ UNFINISHED DISPATCH — the per-clip BD SIGN-FLIPS (+4.70 blue_sky ..
1368            // -1.08 foreman_qcif), and the cost when it fires is 1.0-5.1x. Worst value:
1369            // soccer_4cif 1.70x for +0.00, park_joy 5.08x for +0.91. `me_range` is NOT
1370            // the separating axis — it is a compromise dial (foreman_qcif loses at EVERY
1371            // range 24/16/8/4 = -1.08/-0.55/-0.50/-0.19 while blue_sky wins at every one
1372            // = +4.70/+3.10/+0.73), so shrinking it just trades the win away. The real
1373            // fix is a content signal that predicts the sign; the truth table for it is
1374            // in docs/WHYS-speed-gap.md.
1375            //
1376            // Quality-only (Fast never runs it). Precedence:
1377            // env RFF_ME_WIDE (0/1, for A/B) > cfg.me_wide (Some) > preset default.
1378            sadfp: me_sadfp_mode() == 2,
1379            mv_smooth: false,
1380            do_splits: true,
1381            me_wide: std::env::var("RFF_ME_WIDE").ok().map(|s| s == "1")
1382                .or(cfg.me_wide)
1383                .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1384            me_wide_var: std::env::var("RFF_ME_WIDE_VAR").ok().and_then(|s| s.parse().ok()).unwrap_or(800),
1385            me_rescue: std::env::var("RFF_ME_RESCUE").ok().and_then(|s| s.parse().ok()).unwrap_or(3),
1386            me_wide_coh: std::env::var("RFF_ME_COH").ok().and_then(|s| s.parse().ok()).unwrap_or(4.0),
1387            me_range: std::env::var("RFF_ME_RANGE").ok().and_then(|s| s.parse().ok()).unwrap_or(24),
1388            me_fast: std::env::var("RFF_ME_FASTMO").map(|s| s != "0").unwrap_or(true),
1389            me_learn: std::env::var("RFF_ME_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(40),
1390            me_payoff_pct: std::env::var("RFF_ME_PAYOFF").ok().and_then(|s| s.parse().ok()).unwrap_or(15),
1391            // U3: `balanced` runs SINGLE-PASS sub-pel. Measured on the 4-QP corpus,
1392            // a single pass captures 95.5–99.4% of the full refinement's BD benefit
1393            // (foreman −38.14 vs −39.94, mobile −49.38 vs −49.66, akiyo −26.10 vs
1394            // −26.43) for 1.03–1.31× less time — a straight Pareto improvement on the
1395            // preset. `RFF_SUBPEL_PAT=0` restores the full walk-to-convergence.
1396            sp_single_pass: cfg.preset == crate::config::Preset::Balanced,
1397            sp_defer: std::cell::Cell::new({
1398                let a = DEFER_SUBPEL.load(std::sync::atomic::Ordering::Relaxed) != 0
1399                    || std::env::var("RFF_DEFER_SUBPEL").map(|v| v != "0").unwrap_or(false);
1400                // ONLY the Quality preset runs the multi-shape partition driver. On the
1401                // fast/balanced path there is a single 16×16 candidate, so there is no
1402                // losing shape to skip — deferring there does not save the refinement,
1403                // it DELETES it (measured +91..+145% BD before this guard).
1404                a && cfg.preset == crate::config::Preset::Quality
1405            }),
1406            sp_learn_n: std::cell::Cell::new(0),
1407            sp_ring1: std::cell::Cell::new(0),
1408            sp_total: std::cell::Cell::new(0),
1409            sp_1pass: std::cell::Cell::new(false),
1410            resc_n: std::cell::Cell::new(0),
1411            resc_big: std::cell::Cell::new(0),
1412            resc_off: std::cell::Cell::new(false),
1413            inter8x8: std::env::var("RFF_INTER8")
1414                .ok()
1415                .and_then(|s| s.parse().ok())
1416                .unwrap_or(1),
1417            // ~2 bits per 8x8 luma block (×4) of CAVLC-8x8 overhead the level-aware
1418            // rate still under-charges (no native 8x8 entropy model in CAVLC). Keeps
1419            // the per-MB transform RD from over-picking 8x8 on fine-texture MBs where
1420            // it doesn't compact — content-adaptive: only decisively-favorable MBs win.
1421            inter8_pen: std::env::var("RFF_INTER8_PEN")
1422                .ok()
1423                .and_then(|s| s.parse().ok())
1424                .unwrap_or(8),
1425            // Balanced shares Fast's decision path; only sub-pel differs.
1426            fast: cfg.preset != crate::config::Preset::Quality,
1427            skip_accel_check: cfg.tune_skip_accel_check,
1428            coded_path_v2: cfg.coded_path_v2,
1429            aq_strength: cfg.aq_strength,
1430            tune_lambda_scale: cfg.tune_lambda_scale,
1431            tune_intra_penalty: cfg.tune_intra_penalty,
1432            satd_q: cfg.tune_satd_q,
1433            subpel_force: cfg.tune_subpel || cfg.preset == crate::config::Preset::Balanced,
1434            me_snap: cfg.tune_me_snap,
1435            me_subpel_iter: cfg.tune_me_subpel_iter,
1436            greedy_skip: cfg.tune_greedy_skip,
1437            greedy_min_free: cfg.tune_greedy_skip_min_free.unwrap_or(85),
1438            rd_skip: cfg.tune_rd_skip,
1439            rd_skip_fast_t: cfg.tune_rd_skip_fast_t.unwrap_or(0.0),
1440            rd_skip_min_free: cfg.tune_rd_skip_min_free.unwrap_or(
1441                if cfg.preset == crate::config::Preset::Fast { 60 } else { 90 },
1442            ),
1443            satd_var_thresh: i64::MAX,
1444            mb_use_satd: false,
1445            nnz_l_cache: [0x80; 25],
1446            nnz_c_cache: [[0x80; 9]; 2],
1447            mb_skip_sad: vec![0; mb_w * mb_h],
1448            mb_was_skip: vec![false; mb_w * mb_h],
1449        }
1450    }
1451
1452    /// openh264 `PredictSadSkip`: the greedy P_Skip threshold = the median of the
1453    /// skip SADs of the *skip* neighbours (left A, top B, top-right C, top-left
1454    /// fallback for C). Non-skip neighbours contribute 0, so with no skip neighbour
1455    /// the threshold is 0 (no greedy skip). This makes the skip self-calibrating —
1456    /// it only spreads where a neighbour already skipped at a comparable SAD.
1457    fn pred_skip_sad(&self, mb_x: usize, mb_y: usize) -> u32 {
1458        let mbw = self.mb_w;
1459        let at = |x: isize, y: isize| -> Option<(bool, u32)> {
1460            if x < 0 || y < 0 || x >= mbw as isize {
1461                return None;
1462            }
1463            let i = y as usize * mbw + x as usize;
1464            Some((self.mb_was_skip[i], self.mb_skip_sad[i]))
1465        };
1466        let a = at(mb_x as isize - 1, mb_y as isize); // left
1467        let b = at(mb_x as isize, mb_y as isize - 1); // top
1468        let c = at(mb_x as isize + 1, mb_y as isize - 1) // top-right
1469            .or_else(|| at(mb_x as isize - 1, mb_y as isize - 1)); // top-left fallback
1470        let sad = |n: Option<(bool, u32)>| n.filter(|&(s, _)| s).map_or(0, |(_, v)| v);
1471        let (sa, sb, sc) = (sad(a), sad(b), sad(c));
1472        // B and C unavailable but A available → A only.
1473        if b.is_none() && c.is_none() && a.is_some() {
1474            return sa;
1475        }
1476        match (
1477            a.is_some_and(|(s, _)| s),
1478            b.is_some_and(|(s, _)| s),
1479            c.is_some_and(|(s, _)| s),
1480        ) {
1481            (true, false, false) => sa,
1482            (false, true, false) => sb,
1483            (false, false, true) => sc,
1484            _ => sb.max(sa.min(sc)).min(sa.max(sc)), // median(sa, sb, sc)
1485        }
1486    }
1487
1488    /// The `mb_qp_delta` for the current macroblock (`qp − cur_qp`) and commits the
1489    /// running QPy — called ONLY where the syntax actually codes a delta (I_16x16
1490    /// always; inter / I_4x4 when `cbp != 0`), so a skip / cbp==0 MB leaves `cur_qp`
1491    /// unchanged and inherits it, exactly as the decoder's `step_qp` does.
1492    fn qp_delta(&mut self) -> i32 {
1493        let d = self.qp as i32 - self.cur_qp as i32;
1494        self.cur_qp = self.qp;
1495        d
1496    }
1497
1498    /// MV-predictor neighbors (left, above, above-right) for the 16×16 partition
1499    /// of macroblock `(mb_x, mb_y)`, read from the per-4×4-block grids.
1500    fn mv_neighbors(&self, mb_x: usize, mb_y: usize) -> [MvNeighbor; 3] {
1501        let w4 = self.mb_w * 4;
1502        let get = |avail: bool, bx: isize, by: isize| {
1503            if avail {
1504                let idx = by as usize * w4 + bx as usize;
1505                MvNeighbor {
1506                    available: true,
1507                    mv: self.mv_y[idx],
1508                    ref_idx: self.ref_idx_y[idx],
1509                }
1510            } else {
1511                MvNeighbor::NONE
1512            }
1513        };
1514        let (bx, by) = (mb_x as isize * 4, mb_y as isize * 4);
1515        let a = get(mb_x > 0, bx - 1, by);
1516        let b = get(mb_y > 0, bx, by - 1);
1517        // C = above-right; if unavailable, fall back to D = above-left.
1518        let c = if mb_y > 0 && mb_x + 1 < self.mb_w {
1519            get(true, bx + 4, by - 1)
1520        } else {
1521            get(mb_x > 0 && mb_y > 0, bx - 1, by - 1)
1522        };
1523        [a, b, c]
1524    }
1525
1526    /// The `P_Skip` motion vector (spec §8.4.1.1). P_Skip always references
1527    /// index 0 (the most recent picture).
1528    fn skip_mv(&self, mb_x: usize, mb_y: usize) -> (i32, i32) {
1529        let [a, b, c] = self.mv_neighbors(mb_x, mb_y);
1530        if !a.available
1531            || !b.available
1532            || (a.ref_idx == 0 && a.mv == (0, 0))
1533            || (b.ref_idx == 0 && b.mv == (0, 0))
1534        {
1535            (0, 0)
1536        } else {
1537            predict_mv(a, b, c, 0)
1538        }
1539    }
1540
1541    /// Records a macroblock's per-4×4-block motion state (`ref` = reference index
1542    /// for inter, ignored for intra where `inter` is false).
1543    fn set_mb_mv(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32), inter: bool, refi: i32) {
1544        let w4 = self.mb_w * 4;
1545        for dy in 0..4 {
1546            for dx in 0..4 {
1547                let idx = (mb_y * 4 + dy) * w4 + (mb_x * 4 + dx);
1548                self.mv_y[idx] = mv;
1549                self.inter_y[idx] = inter;
1550                self.ref_idx_y[idx] = if inter { refi } else { -1 };
1551            }
1552        }
1553    }
1554
1555    /// Block-level MV-predictor neighbors for a partition whose top-left 4×4
1556    /// block is `(pbx, pby)` and which is `pwb` blocks wide. Availability uses
1557    /// the decoded-block grid, so in-macroblock partitions see earlier ones.
1558    fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
1559        let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1560        let get = |bx: isize, by: isize| -> MvNeighbor {
1561            if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1562                MvNeighbor::NONE
1563            } else {
1564                let idx = (by * w4 + bx) as usize;
1565                MvNeighbor { available: true, mv: self.mv_y[idx], ref_idx: self.ref_idx_y[idx] }
1566            }
1567        };
1568        let a = get(pbx - 1, pby);
1569        let b = get(pbx, pby - 1);
1570        let mut c = get(pbx + pwb, pby - 1);
1571        if !c.available {
1572            c = get(pbx - 1, pby - 1); // D fallback
1573        }
1574        [a, b, c]
1575    }
1576
1577    /// List-aware block MV-predictor neighbors (`list` 0 or 1), for the B-slice
1578    /// per-list `mvd` predictor. Identical geometry to [`Self::mv_neighbors_block`]
1579    /// but reads the List-1 motion grid when `list == 1`, matching the decoder's
1580    /// `mv_neighbors_list`. A neighbor not coded in this list reads `ref_idx = -1`
1581    /// (so `predict_partition_mv` treats it as non-matching, exactly as the decoder).
1582    fn mv_neighbors_block_list(&self, pbx: isize, pby: isize, pwb: isize, list: usize) -> [MvNeighbor; 3] {
1583        let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1584        let (mvg, refg): (&[(i32, i32)], &[i32]) = if list == 0 {
1585            (&self.mv_y, &self.ref_idx_y)
1586        } else {
1587            (&self.mv1_y, &self.ref_idx1_y)
1588        };
1589        let get = |bx: isize, by: isize| -> MvNeighbor {
1590            if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1591                MvNeighbor::NONE
1592            } else {
1593                let idx = (by * w4 + bx) as usize;
1594                MvNeighbor { available: true, mv: mvg[idx], ref_idx: refg[idx] }
1595            }
1596        };
1597        let a = get(pbx - 1, pby);
1598        let b = get(pbx, pby - 1);
1599        let mut c = get(pbx + pwb, pby - 1);
1600        if !c.available {
1601            c = get(pbx - 1, pby - 1); // D fallback
1602        }
1603        [a, b, c]
1604    }
1605
1606    /// SATD cost of a motion-compensated `rw`×`rh` luma region against the source —
1607    /// THE per-candidate ME cost function (Challenge-1 A2 shape: the per-search
1608    /// invariants arrive as parameters instead of being re-derived per candidate).
1609    /// `hp` is the already-resolved plane cache (`None` ⇔ the fast preset, whose
1610    /// SATD path never reads planes), `hr_on` the hoisted `RFF_HPEL_REF` knob,
1611    /// `src_row` the hoisted source slice base. Dispatch order (interior full-pel →
1612    /// in-place plane read → fused avg+SATD → materialize → `mc_luma` fallback) is
1613    /// the historical `mc_satd` order, so the accepted candidate set — and the
1614    /// bitstream — are byte-identical to it.
1615    #[allow(clippy::too_many_arguments)]
1616    #[inline]
1617    fn mc_satd_hp(
1618        &self,
1619        reference: &crate::RefFrame,
1620        hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1621        hr_on: bool,
1622        // `hr_on && RFF_SATD_AVG` (and accel compiled in) — hoisted per search like
1623        // `hr_on`, so the fused-kernel gate costs zero OnceLock loads per candidate.
1624        // Unused (and always false) on non-accel builds.
1625        sa_on: bool,
1626        src_row: &[u8],
1627        lx: usize,
1628        ly: usize,
1629        rw: usize,
1630        rh: usize,
1631        mv: (i32, i32),
1632    ) -> i64 {
1633        #[cfg(not(accel))]
1634        let _ = sa_on;
1635        #[cfg(feature = "profile")]
1636        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1637        let ch = self.mb_h * 16;
1638        let cw = self.cw;
1639        let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1640        let interior_fullpel = mv.0 & 3 == 0
1641            && mv.1 & 3 == 0
1642            && ix0 >= 0
1643            && iy0 >= 0
1644            && ix0 + rw as isize <= cw as isize
1645            && iy0 + rh as isize <= ch as isize;
1646        #[cfg(feature = "profile")]
1647        {
1648            let fullpel = mv.0 & 3 == 0 && mv.1 & 3 == 0;
1649            satdpath::bump(if interior_fullpel { 0 } else if fullpel { 1 } else { 2 });
1650        }
1651        if interior_fullpel {
1652            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1653            let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1654            return satd_px(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1655        }
1656        if let Some(hp) = hp {
1657            if hr_on {
1658                if let Some((plane, base, stride)) =
1659                    rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1660                {
1661                    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1662                    return satd_px(src_row, cw, &plane[base..], stride, rw, rh);
1663                }
1664            }
1665            // A3: QUARTER-pel — fuse the two-plane (a+b+1)>>1 average into the
1666            // SATD kernel itself (no 256-byte materialize + reload, no FFI hop).
1667            // `satd_avg` returns the exact `Σ|H·d|` that `satd_px` computes on
1668            // the materialized average, so the cost value — and the bitstream —
1669            // are byte-identical; on non-AVX2 (or a declined size) it returns
1670            // `None` and the old materialize path below runs unchanged.
1671            #[cfg(accel)]
1672            if sa_on {
1673                if let Some((pa, ba, pb, bb, stride)) =
1674                    rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1675                {
1676                    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1677                    if let Some(v) = rusty_h264_accel::satd_avg(
1678                        src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh,
1679                    ) {
1680                        return v as i64;
1681                    }
1682                }
1683            }
1684            let mut pred = [0u8; 256];
1685            if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1686                return satd_px(src_row, cw, &pred, rw, rw, rh);
1687            }
1688            mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1689            return satd_px(src_row, cw, &pred, rw, rw, rh);
1690        }
1691        let mut pred = [0u8; 256];
1692        mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1693        satd_px(src_row, cw, &pred, rw, rw, rh)
1694    }
1695
1696    /// Track-B B2.1: the SAD twin of `mc_satd_hp` — the SAME dispatch ladder
1697    /// (interior full-pel → in-place plane read → fused avg → materialize →
1698    /// `mc_luma`), with SAD (`psadbw`-class) distortion. `mc_sad` (the fast
1699    /// preset's function) had NONE of the SATD path's accumulated wins, so the
1700    /// first B2 cut measured 61% MORE `mc_luma` fallbacks; this is the parity fix.
1701    /// Every arm reads the same samples the materializing path would, so the SAD
1702    /// value — and therefore the B2-on bitstream — is unchanged by this function.
1703    #[allow(clippy::too_many_arguments)]
1704    #[inline]
1705    fn mc_sad_hp(
1706        &self,
1707        reference: &crate::RefFrame,
1708        hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1709        hr_on: bool,
1710        src_row: &[u8],
1711        lx: usize,
1712        ly: usize,
1713        rw: usize,
1714        rh: usize,
1715        mv: (i32, i32),
1716        _asrc: Option<&[u8; 256]>,
1717    ) -> i64 {
1718        #[cfg(feature = "profile")]
1719        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1720        let ch = self.mb_h * 16;
1721        let cw = self.cw;
1722        let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1723        let interior_fullpel = mv.0 & 3 == 0
1724            && mv.1 & 3 == 0
1725            && ix0 >= 0
1726            && iy0 >= 0
1727            && ix0 + rw as isize <= cw as isize
1728            && iy0 + rh as isize <= ch as isize;
1729        if interior_fullpel {
1730            let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1731            #[cfg(accel)]
1732            if rw == 16 && rh == 16 {
1733                if let Some(src) = _asrc {
1734                    return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1735                        as i64;
1736                }
1737            }
1738            return sad_strided(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1739        }
1740        if let Some(hp) = hp {
1741            if hr_on {
1742                // Single-plane phases (h/v/c half-pel AND edge full-pel via the
1743                // padded `f` plane — the E-3 move, which `mc_sad` never had).
1744                if let Some((plane, base, stride)) =
1745                    rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1746                {
1747                    return sad_strided(src_row, cw, &plane[base..], stride, rw, rh);
1748                }
1749                // Quarter-pel: fused (a+b+1)>>1 + SAD, no materialize.
1750                if let Some((pa, ba, pb, bb, stride)) =
1751                    rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1752                {
1753                    return sad_avg_strided(src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh);
1754                }
1755            }
1756            let mut pred = [0u8; 256];
1757            if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1758                return sad_strided(src_row, cw, &pred, rw, rw, rh);
1759            }
1760            mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1761            return sad_strided(src_row, cw, &pred, rw, rw, rh);
1762        }
1763        let mut pred = [0u8; 256];
1764        mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1765        sad_strided(src_row, cw, &pred, rw, rw, rh)
1766    }
1767
1768    /// SAD (sum of absolute differences) of a motion-compensated `rw`×`rh` luma
1769    /// region against the source — the **fast** preset's motion-search cost.
1770    ///
1771    /// SAD is far cheaper than SATD (no Hadamard transform), and the inner loop is
1772    /// written as `Σ a.abs_diff(b)` over `u8` slices, the exact pattern LLVM
1773    /// auto-vectorizes to the `psadbw` SAD instruction — the same instruction
1774    /// x264's hand-written assembly uses, but reached without any `unsafe`. (x264's
1775    /// fast presets use SAD for the full-pel search for precisely this reason.)
1776    #[allow(clippy::too_many_arguments)]
1777    fn mc_sad(
1778        &self,
1779        reference: &crate::RefFrame,
1780        sy: &[u8],
1781        lx: usize,
1782        ly: usize,
1783        rw: usize,
1784        rh: usize,
1785        mv: (i32, i32),
1786        // 16-aligned source MB (built once per search) for the asm SAD; `None`
1787        // (and unused) on the scalar build.
1788        _asrc: Option<&[u8; 256]>,
1789    ) -> i64 {
1790        // Descent E depth-6: tag WHO is calling mc_luma. The search's edge fallback and
1791        // reconstruction land in the same `inter-mc` bucket; pricing a recon-side lever
1792        // against the merged total is pricing the wrong population.
1793        #[cfg(feature = "profile")]
1794        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1795        let ch = self.mb_h * 16;
1796        let cw = self.cw;
1797        let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1798        let interior_fullpel = mv.0 & 3 == 0
1799            && mv.1 & 3 == 0
1800            && ix0 >= 0
1801            && iy0 >= 0
1802            && ix0 + rw as isize <= cw as isize
1803            && iy0 + rh as isize <= ch as isize;
1804        // Full-pel interior 16×16: openh264's `psadbw` SAD of the aligned source vs
1805        // the (movdqu) reference block. SAD is exact, so this is byte-identical to the
1806        // scalar path — a pure ME speedup (~2.4× the kernel).
1807        #[cfg(accel)]
1808        if interior_fullpel && rw == 16 && rh == 16 {
1809            if let Some(src) = _asrc {
1810                let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1811                return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1812                    as i64;
1813            }
1814        }
1815        let mut sad = 0u32;
1816        if interior_fullpel {
1817            // Direct from the reference (a copy at full-pel) — no interpolation.
1818            let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1819            let refy = &reference.y;
1820            for dy in 0..rh {
1821                let s = &sy[(ly + dy) * cw + lx..][..rw];
1822                let r = &refy[(ry0 + dy) * cw + rx0..][..rw];
1823                sad += s.iter().zip(r).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1824            }
1825        } else {
1826            let mut pred = [0u8; 256];
1827            // Same plane-cache read (and same preset gate) as `mc_satd`.
1828            let from_planes = !self.fast
1829                && rusty_h264_common::inter::hpel_block(
1830                    reference.hpel(cw, ch),
1831                    lx,
1832                    ly,
1833                    rw,
1834                    rh,
1835                    mv.0,
1836                    mv.1,
1837                    &mut pred,
1838                );
1839            if !from_planes {
1840                mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1841            }
1842            for dy in 0..rh {
1843                let s = &sy[(ly + dy) * cw + lx..][..rw];
1844                let p = &pred[dy * rw..][..rw];
1845                sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1846            }
1847        }
1848        sad as i64
1849    }
1850
1851    /// `bi_dist` for an arbitrary rect — the B 16×8 / 8×16 partition search needs
1852    /// the bi-blend distortion of a half, not of the whole macroblock. Same blend
1853    /// and same SAD/SATD choice as the 16×16 form.
1854    #[allow(clippy::too_many_arguments)]
1855    fn bi_dist_rect(
1856        &self,
1857        l0: &crate::RefFrame,
1858        l1: &crate::RefFrame,
1859        sy: &[u8],
1860        lx: usize,
1861        ly: usize,
1862        rw: usize,
1863        rh: usize,
1864        mv0: (i32, i32),
1865        mv1: (i32, i32),
1866    ) -> i64 {
1867        let ch = self.mb_h * 16;
1868        let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
1869        mc_luma(&l0.y, self.cw, ch, lx, ly, rw, rh, mv0.0, mv0.1, &mut a);
1870        mc_luma(&l1.y, self.cw, ch, lx, ly, rw, rh, mv1.0, mv1.1, &mut b);
1871        let n = rw * rh;
1872        let mut avg = [0u8; 256];
1873        for i in 0..n {
1874            avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
1875        }
1876        if self.fast && !self.mb_use_satd {
1877            let mut sad = 0u32;
1878            for dy in 0..rh {
1879                let s = &sy[(ly + dy) * self.cw + lx..][..rw];
1880                let p = &avg[dy * rw..][..rw];
1881                sad += s.iter().zip(p).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
1882            }
1883            sad as i64
1884        } else {
1885            satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, rw, rw, rh)
1886        }
1887    }
1888
1889    /// Luma distortion of a `B_Bi` 16×16 prediction: motion-compensate `l0`/`l1`,
1890    /// average `(p+q+1)>>1` (the decoder's `b_mc` blend at `weighted_bipred_idc=0`),
1891    /// and score vs the source with the SAME metric the per-list searches used —
1892    /// SAD on the fast path, SATD when this MB is SATD-routed — so `J_bi` compares
1893    /// directly against `J0`/`J1`.
1894    fn bi_dist(
1895        &self,
1896        l0: &crate::RefFrame,
1897        l1: &crate::RefFrame,
1898        sy: &[u8],
1899        lx: usize,
1900        ly: usize,
1901        mv0: (i32, i32),
1902        mv1: (i32, i32),
1903    ) -> i64 {
1904        // Descent E/F: identify this mc_luma population by call site.
1905        #[cfg(feature = "profile")]
1906        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1907        let ch = self.mb_h * 16;
1908        let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
1909        mc_luma(&l0.y, self.cw, ch, lx, ly, 16, 16, mv0.0, mv0.1, &mut a);
1910        mc_luma(&l1.y, self.cw, ch, lx, ly, 16, 16, mv1.0, mv1.1, &mut b);
1911        let mut avg = [0u8; 256];
1912        for i in 0..256 {
1913            avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
1914        }
1915        if self.fast && !self.mb_use_satd {
1916            let mut sad = 0u32;
1917            for dy in 0..16 {
1918                let s = &sy[(ly + dy) * self.cw + lx..][..16];
1919                let p = &avg[dy * 16..][..16];
1920                sad += s.iter().zip(p).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
1921            }
1922            sad as i64
1923        } else {
1924            satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, 16, 16, 16)
1925        }
1926    }
1927
1928    /// Distortion of a pre-formed 16×16 luma prediction vs the source (SAD on the
1929    /// fast path, SATD when SATD-routed) — the mode-decision cost for `B_Direct`,
1930    /// on the same scale as the per-list search J so they compare directly.
1931    fn pred_dist(&self, sy: &[u8], lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
1932        if self.fast && !self.mb_use_satd {
1933            let mut sad = 0u32;
1934            for dy in 0..16 {
1935                let s = &sy[(ly + dy) * self.cw + lx..][..16];
1936                let p = &pred[dy * 16..][..16];
1937                sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1938            }
1939            sad as i64
1940        } else {
1941            satd_px(&sy[ly * self.cw + lx..], self.cw, pred, 16, 16, 16)
1942        }
1943    }
1944
1945    /// `colZeroFlag` for absolute 4×4 block `(bx, by)` (spec §8.4.1.2.2): true when
1946    /// the co-located picture `RefPicList1[0]` (`l1`) is short-term (always, here —
1947    /// we use no long-term refs) and its co-located block uses List-0 reference 0
1948    /// with a near-zero (|·| ≤ 1) motion vector. Must match the decoder's `col_zero`.
1949    fn col_zero(&self, l1: &crate::RefFrame, bx: usize, by: usize) -> bool {
1950        if l1.w4 == 0 {
1951            return false;
1952        }
1953        let idx = by * l1.w4 + bx;
1954        if idx >= l1.ref_idx.len() {
1955            return false;
1956        }
1957        l1.ref_idx[idx] == 0 && l1.mv[idx].0.abs() <= 1 && l1.mv[idx].1.abs() <= 1
1958    }
1959
1960    /// Bi-predictive MC of one small region into `pred_y`/`c_pred` at MB-relative
1961    /// offset `(dx, dy)` — the per-4×4 primitive the spatial-direct derivation uses.
1962    /// Mirrors the decoder's `b_mc` (average `(p+q+1)>>1` for bi, copy for uni).
1963    #[allow(clippy::too_many_arguments)]
1964    fn b_mc_block(
1965        &self,
1966        l0: &crate::RefFrame,
1967        l1: &crate::RefFrame,
1968        mb_x: usize,
1969        mb_y: usize,
1970        dx: usize,
1971        dy: usize,
1972        refi0: i32,
1973        m0: (i32, i32),
1974        refi1: i32,
1975        m1: (i32, i32),
1976        pred_y: &mut [u8; 256],
1977        c_pred: &mut [[u8; 64]; 2],
1978    ) {
1979        // Descent E/F: identify this mc_luma population by call site.
1980        #[cfg(feature = "profile")]
1981        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1982        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
1983        let (px, py) = (mb_x * 16 + dx, mb_y * 16 + dy);
1984        let (mut a, mut b) = ([0u8; 16], [0u8; 16]);
1985        // 4-wide luma exists ONLY here: P partitions bottom out at 8×8, so B-frame
1986        // spatial-direct is the encoder's only 4×4 MC. With B-frames on it is ~8% of
1987        // all MC calls and HALF of all sub-pel ones — and `luma_h`/`luma_v` dispatch
1988        // to asm only at width 16/8, so 4-wide otherwise runs the scalar 6-tap.
1989        // Serving it from the cached half-pel planes (bit-identical, and they are
1990        // already built for this reference by the motion search) is strictly better
1991        // than adding a 4-wide asm kernel.
1992        let mc4 = |r: &crate::RefFrame, mv: (i32, i32), out: &mut [u8; 16]| {
1993            if !self.fast
1994                && bdirect_planes_enabled()
1995                && rusty_h264_common::inter::hpel_block(
1996                    r.hpel(self.cw, ch), px, py, 4, 4, mv.0, mv.1, out,
1997                )
1998            {
1999                return;
2000            }
2001            mc_luma(&r.y, self.cw, ch, px, py, 4, 4, mv.0, mv.1, out);
2002        };
2003        if refi0 >= 0 {
2004            mc4(l0, m0, &mut a);
2005        }
2006        if refi1 >= 0 {
2007            mc4(l1, m1, &mut b);
2008        }
2009        for yy in 0..4 {
2010            for xx in 0..4 {
2011                let i = yy * 4 + xx;
2012                let v = match (refi0 >= 0, refi1 >= 0) {
2013                    (true, true) => bi_blend(a[i] as i32, b[i] as i32, self.bi_w),
2014                    (true, false) => a[i],
2015                    _ => b[i],
2016                };
2017                pred_y[(dy + yy) * 16 + (dx + xx)] = v;
2018            }
2019        }
2020        // Chroma: the co-located 2×2 block at half resolution.
2021        let (cpx, cpy) = (mb_x * 8 + dx / 2, mb_y * 8 + dy / 2);
2022        for c in 0..2 {
2023            let (r0, r1) = if c == 0 { (&l0.u, &l1.u) } else { (&l0.v, &l1.v) };
2024            let (mut ca, mut cb) = ([0u8; 4], [0u8; 4]);
2025            if refi0 >= 0 {
2026                mc_chroma(r0, self.ccw, cch, cpx, cpy, 2, 2, m0.0, m0.1, &mut ca);
2027            }
2028            if refi1 >= 0 {
2029                mc_chroma(r1, self.ccw, cch, cpx, cpy, 2, 2, m1.0, m1.1, &mut cb);
2030            }
2031            for yy in 0..2 {
2032                for xx in 0..2 {
2033                    let i = yy * 2 + xx;
2034                    let v = match (refi0 >= 0, refi1 >= 0) {
2035                        (true, true) => bi_blend(ca[i] as i32, cb[i] as i32, self.bi_w),
2036                        (true, false) => ca[i],
2037                        _ => cb[i],
2038                    };
2039                    c_pred[c][(dy / 2 + yy) * 8 + (dx / 2 + xx)] = v;
2040                }
2041            }
2042        }
2043    }
2044
2045    /// Spatial-direct (`direct_spatial_mv_pred_flag == 1`) prediction for a 16×16 B
2046    /// macroblock — the shared basis of `B_Skip` and `B_Direct_16x16`. Returns the
2047    /// prediction and the per-4×4 `(refIdxL0, mvL0, refIdxL1, mvL1)` motion the
2048    /// decoder's `decode_b_direct` derives (so the caller commits identical motion).
2049    fn b_direct(
2050        &self,
2051        l0: &crate::RefFrame,
2052        l1: &crate::RefFrame,
2053        mb_x: usize,
2054        mb_y: usize,
2055    ) -> ([u8; 256], [[u8; 64]; 2], [(i32, (i32, i32), i32, (i32, i32)); 16]) {
2056        let (nbx, nby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
2057        let n0 = self.mv_neighbors_block_list(nbx, nby, 4, 0);
2058        let n1 = self.mv_neighbors_block_list(nbx, nby, 4, 1);
2059        let min_pos = |a: i32, b: i32| if a < 0 { b } else if b < 0 { a } else { a.min(b) };
2060        let rid = |n: &[MvNeighbor; 3]| min_pos(min_pos(n[0].ref_idx, n[1].ref_idx), n[2].ref_idx);
2061        let (mut refi0, mut refi1) = (rid(&n0), rid(&n1));
2062        let direct_zero = refi0 < 0 && refi1 < 0;
2063        if direct_zero {
2064            refi0 = 0;
2065            refi1 = 0;
2066        }
2067        let mv0 = if refi0 >= 0 && !direct_zero { predict_mv(n0[0], n0[1], n0[2], refi0) } else { (0, 0) };
2068        let mv1 = if refi1 >= 0 && !direct_zero { predict_mv(n1[0], n1[1], n1[2], refi1) } else { (0, 0) };
2069        let mut pred_y = [0u8; 256];
2070        let mut c_pred = [[0u8; 64]; 2];
2071        let mut motion = [(0i32, (0i32, 0i32), 0i32, (0i32, 0i32)); 16];
2072        for sby in 0..4 {
2073            for sbx in 0..4 {
2074                let cz = !direct_zero && self.col_zero(l1, mb_x * 4 + sbx, mb_y * 4 + sby);
2075                let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
2076                let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
2077                motion[sby * 4 + sbx] = (refi0, m0, refi1, m1);
2078                self.b_mc_block(l0, l1, mb_x, mb_y, sbx * 4, sby * 4, refi0, m0, refi1, m1, &mut pred_y, &mut c_pred);
2079            }
2080        }
2081        (pred_y, c_pred, motion)
2082    }
2083
2084    /// Commits a spatial-direct MB's per-4×4 motion into the List-0/List-1 grids so
2085    /// later MBs' neighbor predictors see it (mirrors the decoder's `b_set_motion`).
2086    fn commit_direct_motion(&mut self, mb_x: usize, mb_y: usize, motion: &[(i32, (i32, i32), i32, (i32, i32)); 16]) {
2087        let w4 = self.mb_w * 4;
2088        for sby in 0..4 {
2089            for sbx in 0..4 {
2090                let (refi0, m0, refi1, m1) = motion[sby * 4 + sbx];
2091                let idx = (mb_y * 4 + sby) * w4 + (mb_x * 4 + sbx);
2092                self.inter_y[idx] = true;
2093                self.coded_y[idx] = true;
2094                self.mv_y[idx] = m0;
2095                self.ref_idx_y[idx] = refi0;
2096                self.mv1_y[idx] = m1;
2097                self.ref_idx1_y[idx] = refi1;
2098            }
2099        }
2100    }
2101
2102    /// Rate-aware motion search for a luma region: full-pel diamond + half/
2103    /// quarter-pel refinement minimizing `J = SATD + λ·bits(mvd)`, where the
2104    /// motion cost is measured against `predictors[0]` (the MV predictor the
2105    /// `mvd` will actually be coded against). The search is seeded from every
2106    /// entry in `predictors` plus `(0,0)`. Returns the best MV and its `J`.
2107    ///
2108    /// The rate term is only a *search heuristic* — whatever MV it picks is still
2109    /// coded as a correct `mvd`, so this never affects decodability.
2110    #[allow(clippy::too_many_arguments)]
2111    /// ME ORACLE PROBE (`RFF_ME_ORACLE=1`): does our search actually FIND the best
2112    /// motion vector available to it? Accumulates our chosen cost against an
2113    /// exhaustive +-24 full-pel search refined by the identical sub-pel pass, so a
2114    /// gap is attributable to the SEARCH, not to the cost function or precision.
2115    /// [n, sum(our cost), sum(oracle cost), blocks where oracle won, cost() evals]
2116    fn motion_search(
2117        &self,
2118        reference: &crate::RefFrame,
2119        sy: &[u8],
2120        lx: usize,
2121        ly: usize,
2122        rw: usize,
2123        rh: usize,
2124        predictors: &[(i32, i32)],
2125        lambda_me: f64,
2126        // Some(mv) => skip the full-pel search entirely and refine THIS vector. The
2127        // starting COST is recomputed here rather than passed in, so the baseline the
2128        // refinement must beat is priced by the same closure as every candidate.
2129        start: Option<(i32, i32)>,
2130    ) -> ((i32, i32), i64) {
2131        // Bit length of `se(d)` (Exp-Golomb), i.e. what an `mvd` component costs.
2132        // Branchless closed form of the old `while n > 1 { n >>= 1; len += 2 }` loop:
2133        // that loop yields `len = 1 + 2·floor(log2(codenum+1))`, and for x ≥ 1
2134        // `floor(log2(x)) == 31 - x.leading_zeros()`. Removes a data-dependent branch
2135        // from the innermost ME cost — bit-identical (verified over the d range).
2136        let mvk = mv_cost_kind(self.mv_smooth);
2137        let mvbits = |d: i32| -> u32 {
2138            // H-23: the ME rate model. `RFF_MVCOST=1` swaps the Exp-Golomb STEP
2139            // function for x264's smooth curve `2·log2(|d|+1) + 0.718 + (d!=0)`.
2140            // The step function is FLAT inside a power-of-two bracket — it prices
2141            // d=4 and d=7 identically, so the search takes the far end of a
2142            // bracket for free, inflating |mvd| (and with it the sign+prefix bits
2143            // the accountant found are ~14% of the payload). λ cannot fix this:
2144            // scaling a flat region leaves it flat. Table is in WHOLE bits to keep
2145            // the caller's integer arithmetic; ×4 internally then rounded, so the
2146            // curve's ordering survives quantization.
2147            match mvk {
2148                1 => {
2149                    let a = d.unsigned_abs().min(4095) as usize;
2150                    MV_COST_TAB.get_or_init(build_mv_cost)[a] as u32
2151                }
2152                2 => {
2153                    let a = d.unsigned_abs().min(4095) as usize;
2154                    MV_TRUE_BIASED.get_or_init(build_true_biased)[a] as u32
2155                }
2156                _ => {
2157                    let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
2158                    1 + 2 * (31 - (codenum + 1).leading_zeros())
2159                }
2160            }
2161        };
2162        let center = predictors[0];
2163        let probe = me_oracle_on();
2164        // Track-B B2: the full-pel phase (seeds/snap/diamond) prices candidates in
2165        // the SAD domain; the winner is repriced in SATD before rescue/sub-pel.
2166        // Refine-only searches have no full-pel phase, so B2 does not apply there.
2167        // `self.sadfp` is force-mode at construction or the per-frame `b2_mgain`
2168        // dispatcher's routing (mode 1).
2169        let sadfp = !self.fast && start.is_none() && self.sadfp;
2170        // Build the 16-aligned source MB ONCE per search for the asm SAD path (fast
2171        // preset — and B2's SAD full-pel phase — full 16×16). Amortized over every
2172        // candidate's SAD; the reference block stays unaligned (movdqu). Scalar
2173        // build does no copy.
2174        #[cfg(accel)]
2175        let asrc_buf = if (self.fast || sadfp) && rw == 16 && rh == 16 {
2176            let mut a = AlignedMb([0u8; 256]);
2177            for dy in 0..16 {
2178                a.0[dy * 16..dy * 16 + 16].copy_from_slice(&sy[(ly + dy) * self.cw + lx..][..16]);
2179            }
2180            Some(a)
2181        } else {
2182            None
2183        };
2184        #[cfg(accel)]
2185        let asrc: Option<&[u8; 256]> = asrc_buf.as_ref().map(|a| &a.0);
2186        #[cfg(not(accel))]
2187        let asrc: Option<&[u8; 256]> = None;
2188        // Challenge-1 A2: hoist the SATD path's per-search invariants OUT of the
2189        // per-candidate closure. `mc_satd` re-derived, for EVERY candidate: the
2190        // plane-cache OnceLock (an acquire load + branch, twice on the quarter-pel
2191        // arm), the `RFF_HPEL_REF` OnceLock, and the source-row slice base (a bounds
2192        // check). All are constant across the ~20-50 evaluations of one search.
2193        // `mc_satd_hp` is the same dispatch with those values passed in — the same
2194        // arms in the same order, so the accepted candidate set is byte-identical.
2195        let use_sad = self.fast && !self.mb_use_satd;
2196        let cw = self.cw;
2197        // Every non-fast search sub-pel-refines at the end, so the planes are built
2198        // for any reference a search touches — hoisting the get_or_init here does not
2199        // build planes a lazy path would have avoided.
2200        let hp: Option<&rusty_h264_common::inter::HpelPlanes> =
2201            if !self.fast { Some(reference.hpel(cw, self.mb_h * 16)) } else { None };
2202        let hr_on = hpel_ref_enabled();
2203        // A3 gate, hoisted with the rest (`RFF_HPEL_REF=0` restores the FULL pre-C/A3
2204        // copy path, so the fused kernel rides the same master anchor).
2205        let sa_on = cfg!(accel) && hr_on && satd_avg_enabled();
2206        let src_row = &sy[ly * cw + lx..];
2207        // H-14 R3: the MeCtx fast evaluator — ONE geometry validation per search,
2208        // then per-eval integer bounds + direct kernel (collects the measured
2209        // ~23 ns/eval dispatch chain). Values are exactly the safe path's, so a
2210        // candidate served here cannot change the bitstream; out-of-window
2211        // candidates fall back to `mc_satd_hp` (equal values there too).
2212        #[cfg(accel)]
2213        let mectx = if !use_sad && mectx_enabled() {
2214            hp.and_then(|p| {
2215                rusty_h264_accel::MeCtx::new(
2216                    src_row, cw, &p.f, &p.h, &p.v, &p.c, p.stride, p.pad, p.pw, p.ph,
2217                    lx, ly, rw, rh,
2218                )
2219            })
2220        } else {
2221            None
2222        };
2223        let cost = |mv: (i32, i32)| -> i64 {
2224            let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2225            // The smooth table carries 4× resolution; fold that into λ so the
2226            // rate/distortion balance is unchanged and only the SHAPE differs.
2227            let lam_r = if mvk != 0 { lambda_me * 0.25 } else { lambda_me };
2228            // Fast preset: SAD (psadbw — asm kernel on `--features asm`, else auto-vec)
2229            // — far cheaper than SATD, the single biggest reason x264 fast out-runs us.
2230            let dist = if use_sad {
2231                self.mc_sad(reference, sy, lx, ly, rw, rh, mv, asrc)
2232            } else {
2233                #[cfg(accel)]
2234                {
2235                    match mectx.as_ref().and_then(|c| c.eval(mv.0, mv.1)) {
2236                        Some(d) => d as i64,
2237                        None => {
2238                            self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
2239                        }
2240                    }
2241                }
2242                #[cfg(not(accel))]
2243                {
2244                    self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
2245                }
2246            };
2247            dist + (lam_r * rate as f64) as i64
2248        };
2249        // B2's full-pel-phase cost: SAD distortion, λ scaled to the SAD domain
2250        // (`RFF_ME_SADL`, hoisted). Falls through to `cost` (SATD) whenever B2 is
2251        // off, so every pre-B2 path is untouched.
2252        let lam_fp = lambda_me * if sadfp { me_sadfp_lambda() } else { 1.0 };
2253        let cost_fp = |mv: (i32, i32)| -> i64 {
2254            if !sadfp {
2255                return cost(mv);
2256            }
2257            let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2258            self.mc_sad_hp(reference, hp, hr_on, src_row, lx, ly, rw, rh, mv, asrc)
2259                + (lam_fp * rate as f64) as i64
2260        };
2261        // Seed from (0,0) and each predictor; keep the cheapest.
2262        let refine_only = start.is_some();
2263        let (mut best, mut best_c) = match start {
2264            Some(mv) => (mv, cost(mv)),
2265            None => {
2266                let mut b = (0, 0);
2267                let mut bc = cost_fp(b);
2268                for &p in predictors {
2269                    let pc = cost_fp(p);
2270                    if pc < bc {
2271                        bc = pc;
2272                        b = p;
2273                    }
2274                }
2275                (b, bc)
2276            }
2277        };
2278        // SNAP THE DIAMOND CENTRE TO INTEGER-PEL. The diamond below steps by whole
2279        // pels, so a fractional centre makes EVERY candidate fractional and forces
2280        // all of them through `mc_luma`'s 6-tap filter — measured at 84-90% of all
2281        // SATD evaluations. Snapping puts the whole full-pel phase on the direct
2282        // (no-interpolation) SATD path. The pre-snap seed is kept and re-compared
2283        // after refinement, so this can only change WHERE we search, never make the
2284        // returned vector worse than the seed we started from.
2285        let (seed_mv, mut seed_c) = (best, best_c);
2286        if !refine_only && self.me_snap && (best.0 & 3 != 0 || best.1 & 3 != 0) {
2287            let snapped = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2288            best_c = cost_fp(snapped);
2289            best = snapped;
2290        }
2291        // Coarse-to-fine full-pel search: a 4-point diamond walked at each step
2292        // size from 16 px down to 1 px (steps in quarter-pel units: 64,32,…,4).
2293        // The larger initial steps reach fast motion the predictor missed; the
2294        // diamond stays orthogonal (no diagonals) — diagonal probes were found to
2295        // chase equally-good far matches on ambiguous motion, wrecking MV-field
2296        // coherence and the neighbor predictors.
2297        // The fast preset trusts the neighbour MV predictor and refines locally
2298        // (one coarse reach + fine), like x264's `me=dia`; quality sweeps the full
2299        // coarse-to-fine range. Each step's diamond still walks until no
2300        // improvement, so even fast reaches far motion — just in smaller hops.
2301        // Descent A: the coarse rungs are ~76-80% of full-pel evals at a 0.05-1.0% hit
2302        // rate (near-equal eval counts per rung = the walk almost never walks, so each
2303        // rung is a flat ~4-eval toll). RFF_DIA_LADDER selects which rungs to pay for.
2304        let mut ladder = [0i32; 5];
2305        let mut nladder = 0usize;
2306        let steps: &[i32] = if self.fast {
2307            &[16, 4]
2308        } else {
2309            let m = dia_mask();
2310            for (i, r) in DIA_RUNGS.iter().enumerate() {
2311                if m & (1 << i) != 0 {
2312                    ladder[nladder] = *r;
2313                    nladder += 1;
2314                }
2315            }
2316            &ladder[..nladder]
2317        };
2318        let _gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeDiamond);
2319        // FC: batch a fixed-centre diamond pass through the x4 kernels when every
2320        // candidate is an interior full-pel 16×16 read — one source band covers all
2321        // four candidates. Applies to BOTH cost domains (`sad_16x16_x4` on
2322        // SAD-routed frames, `satd_16x16_x4` otherwise); the fast preset keeps its
2323        // own untouched path. Argmin-of-4 replaces the first-improver cascade —
2324        // measured BD-POSITIVE on the SAD domain (bus −1.71→−2.61) and gated on the
2325        // corpus for the SATD domain the same way. `RFF_ME_FC=0` restores cascade.
2326        let fc = !self.fast && cfg!(accel) && me_fc_enabled()
2327            && matches!((rw, rh), (16, 16) | (16, 8) | (8, 16) | (8, 8));
2328        let ch_px = self.mb_h as isize * 16;
2329        for (_si, &step) in steps.iter().enumerate() {
2330            if refine_only {
2331                break;
2332            }
2333            loop {
2334                #[cfg(accel)]
2335                if fc && best.0 & 3 == 0 && best.1 & 3 == 0 {
2336                    // All four candidates full-pel; interior iff the ±step box is.
2337                    let s = (step >> 2) as isize;
2338                    let (bx, by) = (lx as isize + (best.0 >> 2) as isize, ly as isize + (best.1 >> 2) as isize);
2339                    if bx - s >= 0 && by - s >= 0 && bx + s + rw as isize <= cw as isize && by + s + rh as isize <= ch_px {
2340                        let offs = [
2341                            (by * cw as isize + bx + s) as usize,
2342                            (by * cw as isize + bx - s) as usize,
2343                            ((by + s) * cw as isize + bx) as usize,
2344                            ((by - s) * cw as isize + bx) as usize,
2345                        ];
2346                        // 16-wide shapes go through the batch kernel; 8-wide ones
2347                        // measured SLOWER batched than the per-candidate Wels asm
2348                        // (H-8 speed gate), so they evaluate individually inside the
2349                        // SAME argmin — identical values, identical comparisons,
2350                        // identical bitstream.
2351                        let batch = if rw != 16 {
2352                            None
2353                        } else if sadfp {
2354                            rusty_h264_accel::sad_x4(src_row, cw, &reference.y, offs, cw, rw, rh)
2355                        } else {
2356                            rusty_h264_accel::satd_x4(src_row, cw, &reference.y, offs, cw, rw, rh)
2357                        };
2358                        {
2359                            let ring = [(step, 0), (-step, 0), (0, step), (0, -step)];
2360                            let (mut bi, mut bc) = (usize::MAX, best_c);
2361                            for (i, &(dx, dy)) in ring.iter().enumerate() {
2362                                let mv = (best.0 + dx, best.1 + dy);
2363                                let cc = match batch {
2364                                    Some(sads) => {
2365                                        let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2366                                        sads[i] as i64 + (lam_fp * rate as f64) as i64
2367                                    }
2368                                    None => cost_fp(mv),
2369                                };
2370                                #[cfg(feature = "profile")]
2371                                diastats::ev(_si);
2372                                if cc < bc {
2373                                    bc = cc;
2374                                    bi = i;
2375                                }
2376                            }
2377                            if bi == usize::MAX {
2378                                break;
2379                            }
2380                            best_c = bc;
2381                            best = (best.0 + ring[bi].0, best.1 + ring[bi].1);
2382                            #[cfg(feature = "profile")]
2383                            diastats::imp(_si);
2384                            continue;
2385                        }
2386                    }
2387                }
2388                let mut improved = false;
2389                for &(dx, dy) in &[(step, 0), (-step, 0), (0, step), (0, -step)] {
2390                    let c = (best.0 + dx, best.1 + dy);
2391                    let cc = cost_fp(c);
2392                    #[cfg(feature = "profile")]
2393                    diastats::ev(_si);
2394                    if cc < best_c {
2395                        best_c = cc;
2396                        best = c;
2397                        improved = true;
2398                        #[cfg(feature = "profile")]
2399                        diastats::imp(_si);
2400                    }
2401                }
2402                if !improved {
2403                    break;
2404                }
2405            }
2406        }
2407        // DIAMOND-STALLED RESCUE (content-adaptive: fires on the FAILURE, not a proxy).
2408        // The gradient-descent diamond stalls at a plateau on FLAT cost surfaces and
2409        // never reaches the far-but-better MV that exists within ±16 (measured: ~+22%
2410        // BD-rate vs x264's simple dia on smooth content). The precise stall signal is
2411        // the CONJUNCTION: a FLAT source block (low variance) whose diamond match STILL
2412        // has a high residual — because on a flat surface the RIGHT MV predicts near-
2413        // perfectly, so a high residual there means the diamond missed it (a stall).
2414        // (Residual alone fires on busy blocks where a high residual is inherent — that
2415        // was 3.3× slower on mand for nothing; variance alone fires on flat-but-well-
2416        // predicted blocks. The AND targets exactly the stalls.) Then a FINE ±16 step-2
2417        // grid reaches the true minimum. Fires on a fraction of blocks → affordable.
2418        // Quality preset only.
2419        drop(_gd);
2420        // B2: the full-pel phase priced in the SAD domain — reprice the winner AND
2421        // the pre-snap seed into the SATD domain the rescue + sub-pel phases (and the
2422        // final seed-vs-refined comparison) trade in. Two SATD evaluations per
2423        // search, against the ~20-50 candidate evaluations the SAD domain cheapened.
2424        if sadfp {
2425            best_c = cost(best);
2426            seed_c = cost(seed_mv);
2427        }
2428        let _gr = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeRescue);
2429        // H-14 R1 brick 1: `me_fast` defaults TRUE, which makes `flat`'s value
2430        // IRRELEVANT to the gate below on every default search — yet the full
2431        // rw×rh sum+sum-of-squares walk (256 pixel loads + muls) ran EAGERLY per
2432        // search. Lazy-evaluate it: same boolean outcome in every case (me_fast
2433        // short-circuits first), the dead variance pass simply never runs.
2434        let flat = |sself: &Self| {
2435            !refine_only && {
2436                let (mut s, mut ss) = (0u64, 0u64);
2437                for dy in 0..rh {
2438                    for dx in 0..rw {
2439                        let v = sy[(ly + dy) * sself.cw + lx + dx] as u64;
2440                        s += v;
2441                        ss += v * v;
2442                    }
2443                }
2444                let n = (rw * rh) as u64;
2445                (ss - s * s / n) / n < sself.me_wide_var
2446            }
2447        };
2448        // The online payoff gate may have disabled the rescue for the rest of this
2449        // frame (irreducible-residual content — rotation/fractal — where the fine grid
2450        // fixes almost nothing; measured 2.25× on rot for a ~0% BD gain). A gated-off
2451        // frame runs exactly the diamond → identical to me_wide-off → never worse.
2452        // FAST-MOTION extension: the flat gate targets smooth-surface stalls, but the
2453        // diamond ALSO stalls on FAST motion (bus/football: an exhaustive ±24 search
2454        // recovers 6-15% BD) — those blocks are high-VARIANCE (detail) so `flat` misses
2455        // them. `me_fast` also fires on any high-residual block; the online payoff gate
2456        // then keeps it only where a wider search actually pays off (fast motion), and
2457        // disables it on irreducible-residual detail — the same self-tuning as flat.
2458        if self.me_wide && !self.fast && (self.me_fast || flat(self)) && !self.resc_off.get() {
2459            // H-14 R1 brick 2: `best` was priced by the SAME `dist + (λ·rate) as
2460            // i64` formula on every path that can reach here (cost, cost_fp after
2461            // the B2 reprice, the FC batch with lam_fp == λ off SAD frames), so
2462            // its distortion is recoverable EXACTLY by subtraction — the extra
2463            // full SATD kernel call per search was pure recompute (the
2464            // codec-eliminate-redundancy "return the already-computed value").
2465            let rate_b = mvbits(best.0 - center.0) + mvbits(best.1 - center.1);
2466            let dist = best_c - (lambda_me * rate_b as f64) as i64;
2467            if dist / (rw * rh).max(1) as i64 > self.me_rescue {
2468                // FINE ±16 step-2 grid + ±1 refine — recover the true minimum the
2469                // diamond missed. Fires only on flat-block stalls, so it is affordable.
2470                // SNAP THE GRID CENTRE TO INTEGER-PEL: the diamond seed can be sub-pel
2471                // (sub-pel neighbour predictors), and since every grid point shares
2472                // cx&3, a sub-pel centre forces the WHOLE ±16 grid through mc_luma
2473                // interpolation — measured 89% of zoom's rescue cost. The rescue only
2474                // needs the right REGION (a far MV the diamond missed); the sub-pel
2475                // refine that follows recovers the fraction. Integer centre → the grid
2476                // hits the fast full-pel SATD path (no interpolation).
2477                let pre_c = best_c;
2478                let (cx, cy) = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2479                let mut gb = best;
2480                // BATCHED FULL-PEL GRID (accel): now that the grid centre is integer-pel
2481                // (all points interior full-pel), hoist the interior/bounds check out of
2482                // the loop and call the AVX2 SATD directly — skipping mc_satd's per-point
2483                // interior test + satd_px dispatch. BYTE-IDENTICAL to the cost() path
2484                // (same 2·satd_16x16 + rate), so it is default-on (RFF_ME_BATCH=0 to A/B
2485                // it off). ~+7% zoom / +4% tsrc on top of the snap; the SATD kernel itself
2486                // is already AVX2 and its transform can't amortise across the grid, so
2487                // this per-call-overhead trim is the ceiling for an "asm grid kernel".
2488                let cw = self.cw;
2489                let r = self.me_range;
2490                let batched = rw == 16 && rh == 16 && cfg!(accel) && {
2491                    let (icdx, icdy) = (cx >> 2, cy >> 2);
2492                    lx as i32 + icdx >= r
2493                        && lx as i32 + icdx + r + 16 <= cw as i32
2494                        && ly as i32 + icdy >= r
2495                        && ly as i32 + icdy + r + 16 <= (self.mb_h * 16) as i32
2496                        && me_batch_enabled()
2497                };
2498                #[cfg(accel)]
2499                if batched {
2500                    let (icdx, icdy) = ((cx >> 2), (cy >> 2));
2501                    let src = &sy[ly * cw + lx..];
2502                    let mut dy = -r;
2503                    while dy <= r {
2504                        let rby = (ly as i32 + icdy + dy) as usize;
2505                        let mut dx = -r;
2506                        while dx <= r {
2507                            let rbx = (lx as i32 + icdx + dx) as usize;
2508                            let satd =
2509                                2 * rusty_h264_accel::satd_16x16(src, cw, &reference.y[rby * cw + rbx..], cw) as i64;
2510                            let mv = (cx + dx * 4, cy + dy * 4);
2511                            let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2512                            let cc = satd + (lambda_me * rate as f64) as i64;
2513                            if cc < best_c {
2514                                best_c = cc;
2515                                gb = mv;
2516                            }
2517                            dx += 2;
2518                        }
2519                        dy += 2;
2520                    }
2521                }
2522                if !batched {
2523                    let mut dy = -r;
2524                    while dy <= r {
2525                        let mut dx = -r;
2526                        while dx <= r {
2527                            let cc = cost((cx + dx * 4, cy + dy * 4));
2528                            if cc < best_c {
2529                                best_c = cc;
2530                                gb = (cx + dx * 4, cy + dy * 4);
2531                            }
2532                            dx += 2;
2533                        }
2534                        dy += 2;
2535                    }
2536                }
2537                best = gb;
2538                for dy in -1..=1 {
2539                    for dx in -1..=1 {
2540                        let c = (best.0 + dx * 4, best.1 + dy * 4);
2541                        let cc = cost(c);
2542                        if cc < best_c {
2543                            best_c = cc;
2544                            best = c;
2545                        }
2546                    }
2547                }
2548                // LEARNING PHASE: for the first `me_learn` stalls of the frame, tally
2549                // whether the grid actually paid off (≥6.25% cost cut). Once the window
2550                // fills, if too few paid off the residual is irreducible on this content
2551                // → disable the rescue for the rest of the frame. The window's own MVs
2552                // are committed, but they're a small spatially-clustered set (not
2553                // improvement-selected), so on net-neutral content (rot) they can't
2554                // regress — only frame-level on/off avoids the per-block B-direct
2555                // selection effect.
2556                let n = self.resc_n.get();
2557                if n < self.me_learn {
2558                    self.resc_n.set(n + 1);
2559                    if best_c * 16 <= pre_c * 15 {
2560                        self.resc_big.set(self.resc_big.get() + 1);
2561                    }
2562                    if n + 1 == self.me_learn
2563                        && self.resc_big.get() * 100 < self.me_learn * self.me_payoff_pct
2564                    {
2565                        self.resc_off.set(true);
2566                    }
2567                }
2568            }
2569        }
2570        drop(_gr);
2571        let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeSubpel);
2572        // Sub-pel refinement uses the 6-tap/bilinear interpolation — the expensive
2573        // per-pixel `mc_luma` path that profiling pinned at ~55% of the entire
2574        // encode. The fast preset skips it (integer-pel only, like x264's fastest
2575        // presets `subme=0`): ~3× faster, trading a little quality on sub-pixel
2576        // motion. The quality preset does the full half-pel + quarter-pel rings.
2577        if probe {
2578            // Exhaustive +-24 full-pel around the same centre, then the SAME sub-pel
2579            // pass, so only the full-pel search strategy differs.
2580            let mut ob = center;
2581            let mut oc = i64::MAX;
2582            for gy in -24i32..=24 {
2583                for gx in -24i32..=24 {
2584                    let c = (center.0 + gx * 4, center.1 + gy * 4);
2585                    let cc = cost(c);
2586                    if cc < oc {
2587                        oc = cc;
2588                        ob = c;
2589                    }
2590                }
2591            }
2592            let fullpel_best = ob;
2593            for &st in &[2i32, 1] {
2594                for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2595                    let c = (ob.0 + dx, ob.1 + dy);
2596                    let cc = cost(c);
2597                    if cc < oc {
2598                        oc = cc;
2599                        ob = c;
2600                    }
2601                }
2602            }
2603            // EXHAUSTIVE sub-pel: every quarter-pel offset in +-3 around the full-pel
2604            // winner. Our own pass is a single 4-point probe at half then quarter, so
2605            // this is what separates a sub-pel deficiency from a full-pel one.
2606            let mut oc_sp = oc;
2607            for dy in -3i32..=3 {
2608                for dx in -3i32..=3 {
2609                    let c = (fullpel_best.0 + dx, fullpel_best.1 + dy);
2610                    let cc = cost(c);
2611                    if cc < oc_sp {
2612                        oc_sp = cc;
2613                    }
2614                }
2615            }
2616            // our own sub-pel pass has not run yet; replicate it for a fair compare
2617            let (mut mb_, mut mc_) = (best, best_c);
2618            for &st in &[2i32, 1] {
2619                for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2620                    let c = (mb_.0 + dx, mb_.1 + dy);
2621                    let cc = cost(c);
2622                    if cc < mc_ {
2623                        mc_ = cc;
2624                        mb_ = c;
2625                    }
2626                }
2627            }
2628            use std::sync::atomic::Ordering::Relaxed;
2629            ME_PROBE[0].fetch_add(1, Relaxed);
2630            ME_PROBE[1].fetch_add(mc_.max(0) as u64, Relaxed);
2631            ME_PROBE[2].fetch_add(oc.max(0) as u64, Relaxed);
2632            ME_PROBE[3].fetch_add((mc_ > oc) as u64, Relaxed);
2633            ME_PROBE[5].fetch_add(oc_sp.max(0) as u64, Relaxed);
2634            ME_PROBE[6].fetch_add((mc_ > oc_sp) as u64, Relaxed);
2635        }
2636        let subpel: &[i32] = if (self.fast && !self.subpel_force) || (self.sp_defer.get() && !refine_only) {
2637            &[]
2638        } else {
2639            &[2, 1]
2640        };
2641        // U1 harvest: the null arm is the full-pel winner we would keep on a skip.
2642        let (hv_pre, mut hv_evals) = (best_c, 0u32);
2643        // `to_best` = eval index of the LAST improvement; `ring1` = the cost after the
2644        // first 8-point half-pel ring. Together they answer "how many of these 29
2645        // evaluations actually matter", which is the ceiling for any cheaper pattern.
2646        let (mut hv_to_best, mut hv_ring1) = (0u32, i64::MIN);
2647        let mut pat = subpel_pattern_override()
2648            .unwrap_or(if self.sp_single_pass { 2 } else { 0 });
2649        let (sp_learn, sp_t) = sp_dispatch_cfg();
2650        // Only dispatch when the caller has not pinned a pattern (pat 0 = default).
2651        let sp_dispatching = sp_learn > 0 && pat == 0 && !subpel.is_empty();
2652        if sp_dispatching && self.sp_learn_n.get() >= sp_learn && self.sp_1pass.get() {
2653            pat = 2;
2654        }
2655        // Descent D-2 MEMO. The ring walks around a MOVING centre, so iteration N+1's
2656        // ring necessarily re-contains the previous centre and several previous ring
2657        // points: 27-44% of sub-pel evaluations re-price an MV this refinement already
2658        // priced. `cost()` is PURE in `mv` (rate from mv-centre; distortion from the
2659        // fixed reference/source/block captures), so memoizing is EXACT -- identical
2660        // costs, identical comparisons, identical chosen MV, byte-identical output.
2661        // A miss simply recomputes, so the table's hit rate is a SPEED property only.
2662        //
2663        // 64-entry direct-mapped on the low bits of the MV, tagged with the full MV so
2664        // a collision is a miss rather than a wrong answer. Stack-resident (1 KiB) and
2665        // re-initialized per refinement: measured cheaper than a thread-local + RefCell
2666        // borrow on every evaluation, since ~60% of lookups miss.
2667        const SP_MEMO_N: usize = 64;
2668        #[inline(always)]
2669        fn sp_slot(mv: (i32, i32)) -> usize {
2670            ((mv.0 & 7) as usize) | (((mv.1 & 7) as usize) << 3)
2671        }
2672        let mut memo_mv = [(i32::MIN, i32::MIN); SP_MEMO_N];
2673        let mut memo_c = [0i64; SP_MEMO_N];
2674        if !subpel.is_empty() {
2675            let s0 = sp_slot(best);
2676            memo_mv[s0] = best;
2677            memo_c[s0] = best_c;
2678        }
2679        // Descent D-2 census: the ring walks around a MOVING centre, so iteration N+1's ring
2680        // necessarily re-contains the previous centre and several previous ring points.
2681        // Count how many sub-pel evaluations price an MV this refinement ALREADY priced
2682        // -- redundant recompute is byte-identically removable, unlike dropping work.
2683        #[cfg(feature = "profile")]
2684        let mut seen: Vec<(i32, i32)> = Vec::with_capacity(64);
2685        #[cfg(feature = "profile")]
2686        {
2687            seen.push(best);
2688        }
2689        // Track-B B3: the sub-pel iteration BUDGET. The ring walks until no
2690        // improvement; Descent D's census says iteration 1 carries 55% of evals at
2691        // an 11-13% hit rate, iteration 2 another 35-40% at 1.5-2.5%, and the tail
2692        // past that almost never pays — but under B2's SAD-chosen starts the tail
2693        // GROWS (+27% ns/search), eating the SAD savings. A cap bounds the walk the
2694        // way x264's fixed subme budget does. 0 (default) = unlimited =
2695        // byte-identical; bitstream-changing otherwise → BD-gated, opt-in.
2696        let sp_cap = sp_maxit();
2697        // ③: batched fixed-centre half-pel ring (see `sp_fc_enabled`).
2698        let sp_fc = sp_fc_enabled() && !self.fast && cfg!(accel)
2699            && matches!((rw, rh), (16, 16) | (16, 8) | (8, 16) | (8, 8));
2700        for &step in subpel {
2701            // Snapping starts this refine from an integer centre instead of the
2702            // seed's own fractional lattice, so a single 8-point pass can leave
2703            // precision behind. Walk it until it stops improving to compensate —
2704            // the snap is what pays for the extra probes.
2705            let ring8 = [
2706                (step, 0), (-step, 0), (0, step), (0, -step),
2707                (step, step), (-step, -step), (step, -step), (-step, step),
2708            ];
2709            let ring4 = [(step, 0), (-step, 0), (0, step), (0, -step)];
2710            let ring: &[(i32, i32)] = if pat & 1 != 0 { &ring4 } else { &ring8 };
2711            let mut _iter = 0u32;
2712            loop {
2713                // ③: from an INTEGER centre at step 2, all 8 ring candidates are
2714                // single-plane reads (h/h/v/v axes, c/c/c/c diagonals) — batch them
2715                // as two x4 kernel calls and take the argmin (first-wins in ring
2716                // order). Any decline (edge, half-pel centre, ring4 pattern) falls
2717                // through to the cascading walk for this pass.
2718                // ③b: the QUARTER step — every ±1 offset makes a component odd, so
2719                // all 8 candidates are two-plane average pairs regardless of the
2720                // centre's phase; two `satd_avg_x4` calls cover the ring.
2721                #[cfg(accel)]
2722                if sp_fc && step == 1 && pat & 1 == 0 {
2723                    _iter += 1;
2724                    let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2725                    let ring8 = [
2726                        (1, 0), (-1, 0), (0, 1), (0, -1),
2727                        (1, 1), (-1, -1), (1, -1), (-1, 1),
2728                    ];
2729                    let mut prs: [Option<(&[u8], usize, &[u8], usize, usize)>; 8] = [None; 8];
2730                    let mut all = true;
2731                    for (i, &(dx, dy)) in ring8.iter().enumerate() {
2732                        prs[i] = rusty_h264_common::inter::hpel_qpel_refs(
2733                            hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2734                        );
2735                        all &= prs[i].is_some();
2736                    }
2737                    if all {
2738                        let stride = prs[0].unwrap().4;
2739                        // Batch kernel for 16-wide only (8-wide measured slower
2740                        // batched than the per-candidate fused path — H-8 gate);
2741                        // either way the SAME argmin over the SAME values.
2742                        let pack = |a: usize, b: usize, c2: usize, d: usize| {
2743                            if rw != 16 {
2744                                return None;
2745                            }
2746                            let g = |i: usize| {
2747                                let (pa, oa, pb, ob, _) = prs[i].unwrap();
2748                                (pa, oa, pb, ob)
2749                            };
2750                            rusty_h264_accel::satd_avg_x4(
2751                                src_row, cw, [g(a), g(b), g(c2), g(d)], stride, rw, rh,
2752                            )
2753                        };
2754                        {
2755                            let (ax, di) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7));
2756                            let (mut bi, mut bc) = (usize::MAX, best_c);
2757                            for i in 0..8 {
2758                                let (dx, dy) = ring8[i];
2759                                let mv = (best.0 + dx, best.1 + dy);
2760                                let cc = match (i < 4, &ax, &di) {
2761                                    (true, Some(ax), _) => {
2762                                        let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2763                                        ax[i] as i64 + (lambda_me * rate as f64) as i64
2764                                    }
2765                                    (false, _, Some(di)) => {
2766                                        let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2767                                        di[i - 4] as i64 + (lambda_me * rate as f64) as i64
2768                                    }
2769                                    _ => cost(mv),
2770                                };
2771                                hv_evals += 1;
2772                                if cc < bc {
2773                                    bc = cc;
2774                                    bi = i;
2775                                }
2776                            }
2777                            if hv_ring1 == i64::MIN {
2778                                hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2779                            }
2780                            if bi == usize::MAX
2781                                || !self.me_subpel_iter
2782                                || pat & 2 != 0
2783                                || (sp_cap != 0 && _iter >= sp_cap)
2784                            {
2785                                if bi != usize::MAX {
2786                                    best_c = bc;
2787                                    best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2788                                    hv_to_best = hv_evals;
2789                                }
2790                                break;
2791                            }
2792                            best_c = bc;
2793                            best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2794                            hv_to_best = hv_evals;
2795                            continue;
2796                        }
2797                    }
2798                    _iter -= 1;
2799                }
2800                #[cfg(accel)]
2801                if sp_fc && step == 2 && best.0 & 3 == 0 && best.1 & 3 == 0 && pat & 1 == 0 {
2802                    _iter += 1;
2803                    let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2804                    let ring8 = [
2805                        (step, 0), (-step, 0), (0, step), (0, -step),
2806                        (step, step), (-step, -step), (step, -step), (-step, step),
2807                    ];
2808                    let mut refs8: [Option<(&[u8], usize, usize)>; 8] = [None; 8];
2809                    let mut all = true;
2810                    for (i, &(dx, dy)) in ring8.iter().enumerate() {
2811                        refs8[i] = rusty_h264_common::inter::hpel_ref(
2812                            hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2813                        );
2814                        all &= refs8[i].is_some();
2815                    }
2816                    if all {
2817                        let stride = refs8[0].unwrap().2;
2818                        // 16-wide batches; 8-wide evaluates per candidate (H-8 gate)
2819                        // — identical values, identical argmin, identical bitstream.
2820                        let pack = |a: usize, b: usize, c2: usize, d: usize| {
2821                            if rw != 16 {
2822                                return None;
2823                            }
2824                            let g = |i: usize| {
2825                                let (p, o, _) = refs8[i].unwrap();
2826                                (p, o)
2827                            };
2828                            rusty_h264_accel::satd_x4p(
2829                                src_row, cw, [g(a), g(b), g(c2), g(d)], stride, rw, rh,
2830                            )
2831                        };
2832                        {
2833                            let (ax, di) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7));
2834                            let (mut bi, mut bc) = (usize::MAX, best_c);
2835                            for i in 0..8 {
2836                                let (dx, dy) = ring8[i];
2837                                let mv = (best.0 + dx, best.1 + dy);
2838                                let cc = match (i < 4, &ax, &di) {
2839                                    (true, Some(ax), _) => {
2840                                        let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2841                                        ax[i] as i64 + (lambda_me * rate as f64) as i64
2842                                    }
2843                                    (false, _, Some(di)) => {
2844                                        let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2845                                        di[i - 4] as i64 + (lambda_me * rate as f64) as i64
2846                                    }
2847                                    _ => cost(mv),
2848                                };
2849                                hv_evals += 1;
2850                                if cc < bc {
2851                                    bc = cc;
2852                                    bi = i;
2853                                }
2854                            }
2855                            if hv_ring1 == i64::MIN {
2856                                hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2857                            }
2858                            if bi == usize::MAX
2859                                || !self.me_subpel_iter
2860                                || pat & 2 != 0
2861                                || (sp_cap != 0 && _iter >= sp_cap)
2862                            {
2863                                if bi != usize::MAX {
2864                                    best_c = bc;
2865                                    best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2866                                    hv_to_best = hv_evals;
2867                                }
2868                                break;
2869                            }
2870                            best_c = bc;
2871                            best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2872                            hv_to_best = hv_evals;
2873                            continue;
2874                        }
2875                    }
2876                    _iter -= 1; // declined — the cascade pass below re-counts it
2877                }
2878                let mut improved = false;
2879                _iter += 1;
2880                for (_pi, &(dx, dy)) in ring.iter().enumerate() {
2881                    let c = (best.0 + dx, best.1 + dy);
2882                    let slot = sp_slot(c);
2883                    let cc = if memo_mv[slot] == c {
2884                        memo_c[slot]
2885                    } else {
2886                        let v = cost(c);
2887                        memo_mv[slot] = c;
2888                        memo_c[slot] = v;
2889                        v
2890                    };
2891                    hv_evals += 1;
2892                    // Descent D: which ring POSITION and which ITERATION actually pay?
2893                    // Same census that showed the diamond's coarse rungs were noise,
2894                    // aimed at the stage that is now 41% of encode.
2895                    #[cfg(feature = "profile")]
2896                    {
2897                        spstats::ev(if step == 2 { 0 } else { 1 }, _pi, _iter);
2898                        if seen.contains(&c) {
2899                            spstats::redundant();
2900                        } else {
2901                            seen.push(c);
2902                        }
2903                    }
2904                    if cc < best_c {
2905                        best_c = cc;
2906                        best = c;
2907                        improved = true;
2908                        hv_to_best = hv_evals;
2909                        #[cfg(feature = "profile")]
2910                        spstats::imp(if step == 2 { 0 } else { 1 }, _pi, _iter);
2911                    }
2912                }
2913                if hv_ring1 == i64::MIN {
2914                    hv_ring1 = best_c;
2915                }
2916                if !improved
2917                    || !self.me_subpel_iter
2918                    || pat & 2 != 0
2919                    || (sp_cap != 0 && _iter >= sp_cap)
2920                {
2921                    break;
2922                }
2923            }
2924        }
2925        if sp_dispatching {
2926            let n = self.sp_learn_n.get();
2927            if n < sp_learn {
2928                self.sp_learn_n.set(n + 1);
2929                if hv_ring1 != i64::MIN {
2930                    self.sp_ring1.set(self.sp_ring1.get() + (hv_pre - hv_ring1).max(0));
2931                    self.sp_total.set(self.sp_total.get() + (hv_pre - best_c).max(0));
2932                }
2933                if n + 1 == sp_learn {
2934                    let tot = self.sp_total.get();
2935                    // Concentrated in ring 1 -> the later rings are affordable to drop.
2936                    self.sp_1pass.set(tot > 0 && self.sp_ring1.get() * 100 >= tot * sp_t);
2937                }
2938            }
2939        }
2940        if !subpel.is_empty() && subpel_harvest::enabled() {
2941            subpel_harvest::record(hv_pre, best_c, lambda_me, rw, rh, hv_evals, hv_to_best, hv_ring1);
2942        }
2943        // The snap moved the search off the seed; if the seed was better after all,
2944        // keep it. This is what makes the snap safe by construction.
2945        if self.me_snap && seed_c < best_c {
2946            best = seed_mv;
2947            best_c = seed_c;
2948        }
2949        (best, best_c)
2950    }
2951
2952    /// Encodes macroblock `(mb_x, mb_y)` as an inter macroblock of the given
2953    /// `mode` (0 = P_L0_16x16, 1 = P_16x8, 2 = P_8x16) with one motion vector
2954    /// per partition: motion-compensate each partition, code the macroblock
2955    /// residual, and reconstruct.
2956    #[allow(clippy::too_many_arguments)]
2957    /// Dispatch to the current coded path (`_v1`) or the isolated fused path
2958    /// (`_v2`), selected by the hidden `coded_path_v2` A/B knob. Both produce
2959    /// byte-identical bitstreams (gated by the `coded_path_ab` test); the split
2960    /// exists so the two run side-by-side in one binary for honest timing.
2961    #[allow(clippy::too_many_arguments)]
2962    fn encode_inter_mb(
2963        &mut self,
2964        w: &mut BitWriter,
2965        refs: &[crate::RefFrame],
2966        sy: &[u8],
2967        su: &[u8],
2968        sv: &[u8],
2969        mb_x: usize,
2970        mb_y: usize,
2971        mode: u8,
2972        parts: &[(i32, (i32, i32))],
2973    ) {
2974        if self.coded_path_v2 {
2975            self.encode_inter_mb_v2(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2976        } else {
2977            self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2978        }
2979    }
2980
2981    /// Isolated, coefficient-fused inter coding path (A/B twin of `_v1`). The
2982    /// quantized luma levels stay in the hot 16-byte-aligned i16 DCT buffer for the
2983    /// whole MB; the i32 form is materialized on demand only for *coded* blocks
2984    /// (CAVLC scan + recon dequant), so uncoded quads never pay the conversion and
2985    /// there is no 256-word i32 `q_blocks` round-trip. Byte-identical to `_v1`
2986    /// (gated by `coded_path_ab`). Accel-only optimization; the scalar build reuses
2987    /// `_v1` unchanged.
2988    #[allow(clippy::too_many_arguments)]
2989    fn encode_inter_mb_v2(
2990        &mut self,
2991        w: &mut BitWriter,
2992        refs: &[crate::RefFrame],
2993        sy: &[u8],
2994        su: &[u8],
2995        sv: &[u8],
2996        mb_x: usize,
2997        mb_y: usize,
2998        mode: u8,
2999        parts: &[(i32, (i32, i32))],
3000    ) {
3001        // Descent E/F: identify this mc_luma population by call site.
3002        #[cfg(feature = "profile")]
3003        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
3004        #[cfg(not(accel))]
3005        {
3006            self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
3007        }
3008        #[cfg(accel)]
3009        {
3010            let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
3011            let (qp, qpc) = (self.qp, self.qpc);
3012            let w4 = self.mb_w * 4;
3013            let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3014
3015            // ---- per-partition motion compensation + MV prediction (== v1) ----
3016            let mut pred_y = [0u8; 256];
3017            let mut c_pred = [[0u8; 64]; 2];
3018            let mut mvds = [(0i32, 0i32); 4];
3019            let mut n_mvd = 0;
3020            let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
3021            for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
3022                let (refi, mv) = parts[part];
3023                let reference = &refs[refi as usize];
3024                let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3025                let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
3026                let pmv = predict_partition_mv(mode, part, a, b, c, refi);
3027                mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
3028                n_mvd += 1;
3029                for by in ry / 4..ry / 4 + rh / 4 {
3030                    for bx in rx / 4..rx / 4 + rw / 4 {
3031                        let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3032                        self.mv_y[idx] = mv;
3033                        self.inter_y[idx] = true;
3034                        self.ref_idx_y[idx] = refi;
3035                        self.coded_y[idx] = true;
3036                    }
3037                }
3038                if rw == 16 && rh == 16 {
3039                    self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3040                } else {
3041                    let mut tmp = [0u8; 256];
3042                    self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
3043                    // H-17: the per-pixel re-stride was the runtime-width copy trap
3044                    // (a bounds-checked store per pixel); const-width row copies are
3045                    // byte-identical and lower to inline moves.
3046                    if rw == 8 {
3047                        for dy in 0..rh {
3048                            pred_y[(ry + dy) * 16 + rx..][..8].copy_from_slice(&tmp[dy * 8..][..8]);
3049                        }
3050                    } else {
3051                        for dy in 0..rh {
3052                            pred_y[(ry + dy) * 16 + rx..][..16].copy_from_slice(&tmp[dy * 16..][..16]);
3053                        }
3054                    }
3055                }
3056                let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
3057                for cc in 0..2 {
3058                    let rc = if cc == 0 { &reference.u } else { &reference.v };
3059                    if crw == 8 && crh == 8 {
3060                        mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
3061                    } else {
3062                        let mut tc = [0u8; 64];
3063                        mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
3064                        // H-17: same const-width row-copy fix as luma.
3065                        if crw == 4 {
3066                            for dy in 0..crh {
3067                                c_pred[cc][(cry + dy) * 8 + crx..][..4].copy_from_slice(&tc[dy * 4..][..4]);
3068                            }
3069                        } else {
3070                            for dy in 0..crh {
3071                                c_pred[cc][(cry + dy) * 8 + crx..][..8].copy_from_slice(&tc[dy * 8..][..8]);
3072                            }
3073                        }
3074                    }
3075                }
3076            }
3077
3078            // ---- luma residual + quantization: keep levels in the i16 buffer ----
3079            let mut dctw = AlignedDct([0i16; 256]);
3080            let dct = &mut dctw.0;
3081            let mut cbp_luma = 0u32;
3082            drop(_g_mc);
3083            let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
3084            let base = mb_y * 16 * self.cw + mb_x * 16;
3085            for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3086                rusty_h264_accel::dct_four_t4(
3087                    &mut dct[qi * 64..qi * 64 + 64],
3088                    &sy[base + qy * self.cw + qx..],
3089                    self.cw,
3090                    &pred_y[qy * 16 + qx..],
3091                    16,
3092                );
3093            }
3094            let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3095            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3096            for qi in 0..4 {
3097                rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
3098            }
3099            // cbp per quad straight from the i16 levels (no i32 q_blocks copy).
3100            for blk in 0..16 {
3101                if dct[blk * 16..blk * 16 + 16].iter().any(|&v| v != 0) {
3102                    cbp_luma |= 1 << (blk / 4);
3103                }
3104            }
3105
3106            // ---- chroma residual (identical to v1: c_q stays i32) ----
3107            let mut c_dc_levels = [[0i32; 4]; 2];
3108            let mut c_recon_dc = [[0i32; 4]; 2];
3109            let mut c_q = [[[0i32; 16]; 4]; 2];
3110            let (mut any_ac, mut any_dc) = (false, false);
3111            for c in 0..2 {
3112                let src = if c == 0 { su } else { sv };
3113                let dc2x2 = {
3114                    #[repr(align(16))]
3115                    struct A([i16; 64]);
3116                    let mut cdct = A([0i16; 64]);
3117                    rusty_h264_accel::dct_four_t4(
3118                        &mut cdct.0,
3119                        &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3120                        self.ccw,
3121                        &c_pred[c],
3122                        8,
3123                    );
3124                    let dc = [cdct.0[0] as i32, cdct.0[16] as i32, cdct.0[32] as i32, cdct.0[48] as i32];
3125                    let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3126                    let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3127                    rusty_h264_accel::quant_four_4x4(&mut cdct.0, &ffc, mfc);
3128                    for i in 0..4 {
3129                        let q = &mut c_q[c][i];
3130                        q[0] = 0;
3131                        for j in 1..16 {
3132                            let v = cdct.0[i * 16 + j] as i32;
3133                            q[j] = v;
3134                            if v != 0 {
3135                                any_ac = true;
3136                            }
3137                        }
3138                    }
3139                    dc
3140                };
3141                let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
3142                if dl.iter().any(|&v| v != 0) {
3143                    any_dc = true;
3144                }
3145                c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
3146                c_dc_levels[c] = dl;
3147            }
3148            let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
3149            let cbp = cbp_luma | (cbp_chroma << 4);
3150
3151            // ---- emit syntax (== v1) ----
3152            drop(_g_tq);
3153            let _g_syn = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
3154            w.write_ue(mode as u32);
3155            let num_refs = refs.len();
3156            if num_refs > 1 {
3157                for &(refi, _) in parts {
3158                    write_ref_idx(w, refi, num_refs);
3159                }
3160            }
3161            for &(mvdx, mvdy) in &mvds[..n_mvd] {
3162                w.write_se(mvdx);
3163                w.write_se(mvdy);
3164            }
3165            write_cbp_inter(w, cbp);
3166            if cbp != 0 {
3167                w.write_se(self.qp_delta()); // mb_qp_delta (AQ per-MB QPy)
3168            }
3169            self.nnz_cache_load(mb_x, mb_y);
3170            drop(_g_syn);
3171
3172            // ---- CAVLC: scan straight from the i16 levels for coded blocks ----
3173            let _g_scan = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Scatter);
3174            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3175                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
3176                let total = if cbp_luma & (1 << (blk / 4)) != 0 {
3177                    let nc = self.nc_pred(lbx, lby);
3178                    let scan16 = scan_4x4_dcac_i16(&dct[blk * 16..blk * 16 + 16]);
3179                    encode_residual_block(w, &scan16, 16, nc) as u8
3180                } else {
3181                    0
3182                };
3183                self.nnz_cache_set(lbx, lby, total);
3184                self.nnz_y[by * w4 + bx] = total;
3185            }
3186            if cbp_chroma != 0 {
3187                for c in 0..2 {
3188                    encode_residual_block(w, &c_dc_levels[c], 4, -1);
3189                }
3190            }
3191            if cbp_chroma == 2 {
3192                self.chroma_cache_load(mb_x, mb_y);
3193                let w2 = self.mb_w * 2;
3194                for c in 0..2 {
3195                    for &(bx, by) in &CHROMA_4X4_SCAN_XY {
3196                        let nc = self.chroma_nc_pred(c, bx, by);
3197                        let ac = scan_4x4_ac(&c_q[c][by * 2 + bx]);
3198                        let total = encode_residual_block(w, &ac, 15, nc) as u8;
3199                        self.chroma_nnz_cache_set(c, bx, by, total);
3200                        self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
3201                    }
3202                }
3203            }
3204            drop(_g_scan);
3205
3206            // ---- reconstruction: dequantize luma straight from the i16 levels ----
3207            let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
3208            #[repr(align(16))]
3209            struct Align16([i16; 64]);
3210            let mut dct_in = Align16([0i16; 64]);
3211            for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3212                let rec_off = base + qy * self.cw + qx;
3213                if cbp_luma & (1 << qi) == 0 {
3214                    for r in 0..8 {
3215                        let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
3216                        self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
3217                    }
3218                    continue;
3219                }
3220                for k in 0..4 {
3221                    let blk = qi * 4 + k;
3222                    let mut lvl = [0i32; 16];
3223                    for i in 0..16 {
3224                        lvl[i] = dct[blk * 16 + i] as i32;
3225                    }
3226                    let deq = dequantize(&lvl, qp);
3227                    for i in 0..16 {
3228                        dct_in.0[k * 16 + i] = deq[i] as i16;
3229                    }
3230                }
3231                rusty_h264_accel::idct_four_t4_rec(
3232                    &mut self.rec_y[rec_off..],
3233                    self.cw,
3234                    &pred_y[qy * 16 + qx..],
3235                    16,
3236                    &dct_in.0,
3237                );
3238            }
3239            // chroma recon (identical to v1)
3240            for c in 0..2 {
3241                let base_c = (mb_y * 8) * self.ccw + mb_x * 8;
3242                let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3243                if cbp_chroma == 0 {
3244                    for r in 0..8 {
3245                        let dsti = base_c + r * self.ccw;
3246                        plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
3247                    }
3248                } else {
3249                    #[repr(align(16))]
3250                    struct A([i16; 64]);
3251                    let mut d = A([0i16; 64]);
3252                    for i in 0..4 {
3253                        let deq = dequantize(&c_q[c][i], qpc);
3254                        for j in 0..16 {
3255                            d.0[i * 16 + j] = deq[j] as i16;
3256                        }
3257                        d.0[i * 16] = c_recon_dc[c][i] as i16;
3258                    }
3259                    rusty_h264_accel::idct_four_t4_rec(&mut plane[base_c..], self.ccw, &c_pred[c], 8, &d.0);
3260                }
3261            }
3262            for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3263                self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
3264            }
3265        }
3266    }
3267
3268    #[allow(clippy::too_many_arguments)]
3269    fn encode_inter_mb_v1(
3270        &mut self,
3271        w: &mut BitWriter,
3272        refs: &[crate::RefFrame],
3273        sy: &[u8],
3274        su: &[u8],
3275        sv: &[u8],
3276        mb_x: usize,
3277        mb_y: usize,
3278        mode: u8,
3279        parts: &[(i32, (i32, i32))],
3280    ) {
3281        self.encode_inter_mb_v1_b(w, refs, sy, su, sv, mb_x, mb_y, mode, parts, None);
3282    }
3283
3284    /// As [`Self::encode_inter_mb_v1`], but `b_mode` selects B-slice framing: the
3285    /// macroblock is coded as `B_L0_16x16` (`mb_type == 1`) instead of the P-slice
3286    /// `mb_type == mode`. Everything else — the single List-0 partition, the median
3287    /// `mvd_l0` predictor, the residual, and the reconstruction — is byte-identical
3288    /// to `P_L0_16x16`, so the caller passes `mode == 0`, `refs == &[L0_anchor]`
3289    /// (length 1 ⇒ no `ref_idx` coded), and `parts == &[(0, mv)]`.
3290    /// Decide + reconstruct one inter macroblock (motion compensation, residual,
3291    /// quantize, reconstruct, commit motion grids) — everything except entropy
3292    /// coding. Returns an [`InterPlan`] coded by either backend, so CAVLC and CABAC
3293    /// share this whole path bit-for-bit (the P/B analogue of [`plan_mb`]).
3294    #[allow(clippy::too_many_arguments)]
3295    fn plan_inter_mb(
3296        &mut self,
3297        refs: &[crate::RefFrame],
3298        sy: &[u8],
3299        su: &[u8],
3300        sv: &[u8],
3301        mb_x: usize,
3302        mb_y: usize,
3303        mode: u8,
3304        parts: &[(i32, (i32, i32))],
3305        bspec: Option<BInter>,
3306    ) -> InterPlan {
3307        // Descent E/F: identify this mc_luma population by call site.
3308        #[cfg(feature = "profile")]
3309        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
3310        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
3311        let (qp, qpc) = (self.qp, self.qpc);
3312        let w4 = self.mb_w * 4;
3313        let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3314
3315        // ---- per-partition motion compensation + MV prediction ----
3316        let mut pred_y = [0u8; 256];
3317        let mut c_pred = [[0u8; 64]; 2];
3318        let mut mvds = [(0i32, 0i32); 4]; // ≤4 partitions; no per-MB Vec alloc
3319        let mut plan_refs = [0i32; 4]; // per-partition ref_idx_l0 (0 for B / 1-ref)
3320        let mut n_mvd = 0;
3321        let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
3322        // The SPLIT test must come FIRST. `mvmode > 0` means a 16x8/8x16 partition
3323        // beat every 16x16 mode INCLUDING direct, and when it beat direct the caller
3324        // leaves `dir` at 0 -- so a `dir == 0` test placed ahead of this one claims
3325        // the macroblock, reconstructs B_Direct, and emits nothing into `mvds`, while
3326        // `emit_mb_cabac_b` still writes the split mb_type. The decoder then reads a
3327        // B_Bi_16x8 with two zero mvds where the encoder reconstructed direct motion.
3328        // Measured: 34 macroblocks per 6 frames, -2.4 dB luma, and INVISIBLE to the
3329        // conformance matrix -- both decoders agree with each other, they just
3330        // disagree with the encoder.
3331        if let Some(b) = bspec.filter(|b| b.mvmode > 0) {
3332            // ---- B 16x8 / 8x16: two partitions, each L0 / L1 / Bi ----
3333            // Prediction and commit run PARTITION-major (partition 1 predicts off
3334            // partition 0's committed motion, exactly as the decoder's recon does);
3335            // the mvds are then serialised LIST-major for the emit, which is the
3336            // spec 7.3.5.1 order the decoder parses.
3337            let (rects, _) = b_part_layout(b.mvmode);
3338            let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3339            let mut pm: [[(i32, i32); 2]; 2] = [[(0, 0); 2]; 2]; // [part][list] mvd
3340            for (part, &(rx, ry, rw, rh)) in rects.iter().enumerate() {
3341                let (pred, mv0, mv1) = b.parts2[part];
3342                let (u0, u1) = (pred == 1 || pred == 3, pred == 2 || pred == 3);
3343                let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3344                if u0 {
3345                    let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, (rw / 4) as isize, 0);
3346                    let p = predict_partition_mv(b.mvmode, part, a, c0, c1, 0);
3347                    pm[part][0] = (mv0.0 - p.0, mv0.1 - p.1);
3348                }
3349                if u1 {
3350                    let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, (rw / 4) as isize, 1);
3351                    let p = predict_partition_mv(b.mvmode, part, a, c0, c1, 0);
3352                    pm[part][1] = (mv1.0 - p.0, mv1.1 - p.1);
3353                }
3354                // Motion compensation for this rect.
3355                let (lx, ly) = (mb_x * 16 + rx, mb_y * 16 + ry);
3356                let (cx, cy) = (mb_x * 8 + rx / 2, mb_y * 8 + ry / 2);
3357                let (cw2, ch2) = (rw / 2, rh / 2);
3358                let mut ay = [0u8; 256];
3359                let mut by_ = [0u8; 256];
3360                let mut ac = [[0u8; 64]; 2];
3361                let mut bc = [[0u8; 64]; 2];
3362                if u0 {
3363                    mc_luma(&refs[0].y, self.cw, ch, lx, ly, rw, rh, mv0.0, mv0.1, &mut ay);
3364                    mc_chroma(&refs[0].u, self.ccw, cch, cx, cy, cw2, ch2, mv0.0, mv0.1, &mut ac[0]);
3365                    mc_chroma(&refs[0].v, self.ccw, cch, cx, cy, cw2, ch2, mv0.0, mv0.1, &mut ac[1]);
3366                }
3367                if u1 {
3368                    mc_luma(&b.l1.y, self.cw, ch, lx, ly, rw, rh, mv1.0, mv1.1, &mut by_);
3369                    mc_chroma(&b.l1.u, self.ccw, cch, cx, cy, cw2, ch2, mv1.0, mv1.1, &mut bc[0]);
3370                    mc_chroma(&b.l1.v, self.ccw, cch, cx, cy, cw2, ch2, mv1.0, mv1.1, &mut bc[1]);
3371                }
3372                for r in 0..rh {
3373                    for c in 0..rw {
3374                        let d = (ry + r) * 16 + rx + c;
3375                        let sidx = r * rw + c;
3376                        pred_y[d] = match (u0, u1) {
3377                            (true, true) => bi_blend(ay[sidx] as i32, by_[sidx] as i32, self.bi_w),
3378                            (true, false) => ay[sidx],
3379                            _ => by_[sidx],
3380                        };
3381                    }
3382                }
3383                for cc in 0..2 {
3384                    for r in 0..ch2 {
3385                        for c in 0..cw2 {
3386                            let d = (ry / 2 + r) * 8 + rx / 2 + c;
3387                            let sidx = r * cw2 + c;
3388                            c_pred[cc][d] = match (u0, u1) {
3389                                (true, true) => bi_blend(ac[cc][sidx] as i32, bc[cc][sidx] as i32, self.bi_w),
3390                                (true, false) => ac[cc][sidx],
3391                                _ => bc[cc][sidx],
3392                            };
3393                        }
3394                    }
3395                }
3396                // Commit this partition before the next one predicts.
3397                for by2 in ry / 4..(ry + rh) / 4 {
3398                    for bx2 in rx / 4..(rx + rw) / 4 {
3399                        let idx = (mb_y * 4 + by2) * w4 + (mb_x * 4 + bx2);
3400                        self.inter_y[idx] = true;
3401                        self.coded_y[idx] = true;
3402                        self.mv_y[idx] = if u0 { mv0 } else { (0, 0) };
3403                        self.ref_idx_y[idx] = if u0 { 0 } else { -1 };
3404                        self.mv1_y[idx] = if u1 { mv1 } else { (0, 0) };
3405                        self.ref_idx1_y[idx] = if u1 { 0 } else { -1 };
3406                    }
3407                }
3408            }
3409            // Serialise LIST-major: all L0 mvds, then all L1.
3410            for list in 0..2 {
3411                for part in 0..2 {
3412                    let pred = b.parts2[part].0;
3413                    let used = if list == 0 { pred == 1 || pred == 3 } else { pred == 2 || pred == 3 };
3414                    if used {
3415                        mvds[n_mvd] = pm[part][list];
3416                        n_mvd += 1;
3417                    }
3418                }
3419            }
3420        } else if let Some(b) = bspec.filter(|b| b.dir == 0) {
3421            // ---- B_Direct_16x16 (mb_type 0): spatial-direct prediction, no mvd ----
3422            let (dp, dc, motion) = self.b_direct(&refs[0], b.l1, mb_x, mb_y);
3423            pred_y = dp;
3424            c_pred = dc;
3425            self.commit_direct_motion(mb_x, mb_y, &motion);
3426        } else if let Some(b) = bspec {
3427            // ---- B 16×16 prediction: List-0 / List-1 / Bi ----
3428            let use0 = b.dir == 1 || b.dir == 3;
3429            let use1 = b.dir == 2 || b.dir == 3;
3430            let (lx, ly) = (mb_x * 16, mb_y * 16);
3431            let (cx, cy) = (mb_x * 8, mb_y * 8);
3432            let (pbx, pby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
3433            // Per-list `mvd` against the median predictor over that list's neighbors.
3434            if use0 {
3435                let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 0);
3436                let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3437                mvds[n_mvd] = (b.mv0.0 - p.0, b.mv0.1 - p.1);
3438                n_mvd += 1;
3439            }
3440            if use1 {
3441                let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 1);
3442                let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3443                mvds[n_mvd] = (b.mv1.0 - p.0, b.mv1.1 - p.1);
3444                n_mvd += 1;
3445            }
3446            // Motion compensation. L0/L1 write straight into pred; Bi averages
3447            // (p+q+1)>>1 — the decoder's `b_mc` blend with weighted_bipred_idc=0.
3448            let mut a_y = [0u8; 256];
3449            let mut b_y = [0u8; 256];
3450            let mut a_c = [[0u8; 64]; 2];
3451            let mut b_c = [[0u8; 64]; 2];
3452            if use0 {
3453                mc_luma(&refs[0].y, self.cw, ch, lx, ly, 16, 16, b.mv0.0, b.mv0.1, &mut a_y);
3454                mc_chroma(&refs[0].u, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[0]);
3455                mc_chroma(&refs[0].v, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[1]);
3456            }
3457            if use1 {
3458                mc_luma(&b.l1.y, self.cw, ch, lx, ly, 16, 16, b.mv1.0, b.mv1.1, &mut b_y);
3459                mc_chroma(&b.l1.u, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[0]);
3460                mc_chroma(&b.l1.v, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[1]);
3461            }
3462            match (use0, use1) {
3463                (true, true) => {
3464                    for i in 0..256 {
3465                        pred_y[i] = bi_blend(a_y[i] as i32, b_y[i] as i32, self.bi_w);
3466                    }
3467                    for c in 0..2 {
3468                        for i in 0..64 {
3469                            c_pred[c][i] = bi_blend(a_c[c][i] as i32, b_c[c][i] as i32, self.bi_w);
3470                        }
3471                    }
3472                }
3473                (true, false) => {
3474                    pred_y = a_y;
3475                    c_pred = a_c;
3476                }
3477                _ => {
3478                    pred_y = b_y;
3479                    c_pred = b_c;
3480                }
3481            }
3482            // Commit per-list motion so later MBs' per-list predictors see it.
3483            for by in 0..4 {
3484                for bx in 0..4 {
3485                    let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3486                    self.inter_y[idx] = true;
3487                    self.coded_y[idx] = true;
3488                    self.mv_y[idx] = if use0 { b.mv0 } else { (0, 0) };
3489                    self.ref_idx_y[idx] = if use0 { 0 } else { -1 };
3490                    self.mv1_y[idx] = if use1 { b.mv1 } else { (0, 0) };
3491                    self.ref_idx1_y[idx] = if use1 { 0 } else { -1 };
3492                }
3493            }
3494        } else {
3495        for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
3496            let (refi, mv) = parts[part];
3497            plan_refs[part] = refi; // per-partition ref_idx_l0 → carried to the CABAC emit
3498            let reference = &refs[refi as usize];
3499            let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3500            let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
3501            let pmv = predict_partition_mv(mode, part, a, b, c, refi);
3502            mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
3503            n_mvd += 1;
3504            // Commit this partition's motion so later partitions can predict from it.
3505            for by in ry / 4..ry / 4 + rh / 4 {
3506                for bx in rx / 4..rx / 4 + rw / 4 {
3507                    let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3508                    self.mv_y[idx] = mv;
3509                    self.inter_y[idx] = true;
3510                    self.ref_idx_y[idx] = refi;
3511                    self.coded_y[idx] = true;
3512                }
3513            }
3514            // Luma MC into the partition's sub-region. A full-MB (16×16) partition is
3515            // the whole `pred_y`, so MC straight into it — no scratch + repack copy.
3516            if rw == 16 && rh == 16 {
3517                self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3518            } else {
3519                let mut tmp = [0u8; 256];
3520                self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
3521                // H-17: const-width row copies (see the v2 twin).
3522                if rw == 8 {
3523                    for dy in 0..rh {
3524                        pred_y[(ry + dy) * 16 + rx..][..8].copy_from_slice(&tmp[dy * 8..][..8]);
3525                    }
3526                } else {
3527                    for dy in 0..rh {
3528                        pred_y[(ry + dy) * 16 + rx..][..16].copy_from_slice(&tmp[dy * 16..][..16]);
3529                    }
3530                }
3531            }
3532            // Chroma MC (half-resolution region); 8×8 = the whole plane prediction.
3533            let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
3534            for cc in 0..2 {
3535                let rc = if cc == 0 { &reference.u } else { &reference.v };
3536                if crw == 8 && crh == 8 {
3537                    mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
3538                } else {
3539                    let mut tc = [0u8; 64];
3540                    mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
3541                    // H-17: const-width row copies (see the v2 twin).
3542                    if crw == 4 {
3543                        for dy in 0..crh {
3544                            c_pred[cc][(cry + dy) * 8 + crx..][..4].copy_from_slice(&tc[dy * 4..][..4]);
3545                        }
3546                    } else {
3547                        for dy in 0..crh {
3548                            c_pred[cc][(cry + dy) * 8 + crx..][..8].copy_from_slice(&tc[dy * 8..][..8]);
3549                        }
3550                    }
3551                }
3552            }
3553        }
3554        } // end P per-partition formation (else of the B branch)
3555
3556        // ---- luma residual + quantization ----
3557        let mut q_blocks = [[0i32; 16]; 16]; // raster, levels
3558        let mut cbp_luma = 0u32;
3559        // Inter 8x8-transform candidate (High profile, scalar path). Filled by the
3560        // per-MB 4x4-vs-8x8 RD below; false/zero means the 4x4 residual is used.
3561        #[allow(unused_mut)]
3562        let mut t8x8 = false;
3563        #[allow(unused_mut)]
3564        let mut q8 = [[0i32; 64]; 4];
3565        drop(_g_mc);
3566        let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
3567        #[cfg(accel)]
3568        {
3569            // openh264 `WelsDctFourT4_sse2` (fused residual+DCT) → i16, then
3570            // `WelsQuantFour4x4_sse2` in place — the whole DCT→quant chain stays in i16,
3571            // no i32 round-trip. Quant is openh264's structure carrying OUR deadzone
3572            // (`quant_dz_ff` + `QUANT_MF_OH`), so levels are bit-identical to `quantize`.
3573            let mut dctw = AlignedDct([0i16; 256]);
3574            let dct = &mut dctw.0;
3575            let base = mb_y * 16 * self.cw + mb_x * 16;
3576            for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3577                rusty_h264_accel::dct_four_t4(
3578                    &mut dct[qi * 64..qi * 64 + 64],
3579                    &sy[base + qy * self.cw + qx..],
3580                    self.cw,
3581                    &pred_y[qy * 16 + qx..],
3582                    16,
3583                );
3584            }
3585            let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3586            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3587            for qi in 0..4 {
3588                rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
3589            }
3590            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3591                let mut nz = false;
3592                for i in 0..16 {
3593                    let v = dct[blk * 16 + i] as i32;
3594                    q_blocks[lby * 4 + lbx][i] = v;
3595                    nz |= v != 0;
3596                }
3597                if nz {
3598                    cbp_luma |= 1 << (blk / 4);
3599                }
3600            }
3601        }
3602        #[cfg(not(accel))]
3603        {
3604            // Scalar/`wide`: gather all 16 residual blocks, batched forward-DCT, quantize.
3605            let mut res_blocks = [[0i32; 16]; 16]; // raster
3606            for lby in 0..4 {
3607                for lbx in 0..4 {
3608                    let b = &mut res_blocks[lby * 4 + lbx];
3609                    for dy in 0..4 {
3610                        for dx in 0..4 {
3611                            let sx = mb_x * 16 + lbx * 4 + dx;
3612                            let syy = mb_y * 16 + lby * 4 + dy;
3613                            b[dy * 4 + dx] = sy[syy * self.cw + sx] as i32
3614                                - pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3615                        }
3616                    }
3617                }
3618            }
3619            let mut coeffs = [[0i32; 16]; 16];
3620            forward_dct_blocks(&res_blocks, &mut coeffs);
3621            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3622                let q = rdoq(&coeffs[lby * 4 + lbx], qp, 6, self.rdoq_strength, 0);
3623                if q.iter().any(|&v| v != 0) {
3624                    cbp_luma |= 1 << (blk / 4);
3625                }
3626                q_blocks[lby * 4 + lbx] = q;
3627            }
3628        }
3629
3630        // Per-MB transform-size RD (runs in scalar AND accel builds — q_blocks +
3631        // cbp_luma are filled by whichever quant path ran; the 8x8 candidate + its
3632        // recon are pure Rust). One 8x8 DCT per 8x8 block vs four 4x4s. Every inter
3633        // partition here is >= 8x8, so transform_size_8x8_flag is always allowed.
3634        // Content-adaptive by construction — the winner is chosen per MB.
3635        {
3636            if self.transform_8x8 && self.inter8x8 != 0 {
3637                let lambda =
3638                    0.85 * self.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
3639                let mut ssd4 = 0i64;
3640                let mut rate4 = 0f64;
3641                for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3642                    let mut predb = [0i32; 16];
3643                    for dy in 0..4 {
3644                        for dx in 0..4 {
3645                            predb[dy * 4 + dx] =
3646                                pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3647                        }
3648                    }
3649                    let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3650                    let s = reconstruct_4x4(&deq, &predb);
3651                    for dy in 0..4 {
3652                        for dx in 0..4 {
3653                            let sx = mb_x * 16 + lbx * 4 + dx;
3654                            let syy = mb_y * 16 + lby * 4 + dy;
3655                            let d = s[dy * 4 + dx] as i64 - sy[syy * self.cw + sx] as i64;
3656                            ssd4 += d * d;
3657                        }
3658                    }
3659                    for &l in &q_blocks[lby * 4 + lbx] {
3660                        if l != 0 {
3661                            rate4 += rdoq_rate((l as i64).abs());
3662                        }
3663                    }
3664                }
3665                let (q8c, cbp8, rate8, _rec8, ssd8) =
3666                    plan_inter8_luma(sy, self.cw, mb_x, mb_y, &pred_y, qp);
3667                // Both candidates priced with the SAME level-aware rate (Σ rdoq_rate);
3668                // `inter8_pen` is an optional extra bias (default 0) on the 8x8 flag.
3669                let j4 = ssd4 as f64 + lambda * (rate4 + 16.0);
3670                let j8 = ssd8 as f64 + lambda * (rate8 + 16.0 + self.inter8_pen as f64);
3671                if cbp8 > 0 && j8 < j4 {
3672                    t8x8 = true;
3673                    cbp_luma = cbp8;
3674                    q8 = q8c;
3675                }
3676            }
3677        }
3678
3679        // ---- chroma residual (prediction already built per partition) ----
3680        let mut c_dc_levels = [[0i32; 4]; 2];
3681        let mut c_recon_dc = [[0i32; 4]; 2];
3682        let mut c_q = [[[0i32; 16]; 4]; 2];
3683        let (mut any_ac, mut any_dc) = (false, false);
3684        for c in 0..2 {
3685            let src = if c == 0 { su } else { sv };
3686            // Fast path: one dct_four_t4 covers the whole 8x8 chroma region (all 4
3687            // blocks, residual+DCT fused straight from the planes); block b's pre-quant
3688            // DC is dct[b*16] (quad z-scan == 2x2 raster); quant_four_4x4 with our
3689            // FF/MF is bit-identical to scalar `quantize`. Same pairing the P_Skip
3690            // free-check proved byte-identical over the corpus.
3691            #[cfg(accel)]
3692            let (mut dc2x2, applied) = {
3693                #[repr(align(16))]
3694                struct A([i16; 64]);
3695                let mut dct = A([0i16; 64]);
3696                rusty_h264_accel::dct_four_t4(
3697                    &mut dct.0,
3698                    &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3699                    self.ccw,
3700                    &c_pred[c],
3701                    8,
3702                );
3703                let dc = [
3704                    dct.0[0] as i32,
3705                    dct.0[16] as i32,
3706                    dct.0[32] as i32,
3707                    dct.0[48] as i32,
3708                ];
3709                let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3710                let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3711                rusty_h264_accel::quant_four_4x4(&mut dct.0, &ffc, mfc);
3712                for i in 0..4 {
3713                    let q = &mut c_q[c][i];
3714                    q[0] = 0;
3715                    for j in 1..16 {
3716                        let v = dct.0[i * 16 + j] as i32;
3717                        q[j] = v;
3718                        if v != 0 {
3719                            any_ac = true;
3720                        }
3721                    }
3722                }
3723                (dc, true)
3724            };
3725            #[cfg(not(accel))]
3726            let (mut dc2x2, applied) = ([0i32; 4], false);
3727            if !applied {
3728                // Scalar/`wide` twin: gather, batch forward DCT, quantize per block.
3729                let mut res_blocks = [[0i32; 16]; 4];
3730                for by in 0..2 {
3731                    for bx in 0..2 {
3732                        let b = &mut res_blocks[by * 2 + bx];
3733                        for dy in 0..4 {
3734                            for dx in 0..4 {
3735                                let sx = mb_x * 8 + bx * 4 + dx;
3736                                let syy = mb_y * 8 + by * 4 + dy;
3737                                b[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
3738                                    - c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3739                            }
3740                        }
3741                    }
3742                }
3743                let mut coeffs = [[0i32; 16]; 4];
3744                forward_dct_blocks(&res_blocks, &mut coeffs);
3745                for i in 0..4 {
3746                    dc2x2[i] = coeffs[i][0];
3747                    let mut q = rdoq(&coeffs[i], qpc, 6, self.rdoq_strength, 1);
3748                    q[0] = 0;
3749                    if q[1..].iter().any(|&v| v != 0) {
3750                        any_ac = true;
3751                    }
3752                    c_q[c][i] = q;
3753                }
3754            }
3755            let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
3756            if dl.iter().any(|&v| v != 0) {
3757                any_dc = true;
3758            }
3759            c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
3760            c_dc_levels[c] = dl;
3761        }
3762        let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
3763        let cbp = cbp_luma | (cbp_chroma << 4);
3764
3765        drop(_g_tq);
3766        let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
3767        // ---- reconstruction (luma) ----
3768        #[cfg(accel)]
3769        if t8x8 {
3770            // 8x8-transform recon is pure Rust (no asm 8x8 kernels yet); inverse of
3771            // the decoder's t8x8 inter path. Same code as the scalar branch below.
3772            let weight = [16i32; 64];
3773            for b8 in 0..4usize {
3774                let (b8x, b8y) = (b8 % 2, b8 / 2);
3775                let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3776                let predb: [i32; 64] = std::array::from_fn(|i| {
3777                    pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3778                });
3779                let recon = add_residual_8x8(&res_r, &predb);
3780                for dy in 0..8 {
3781                    for dx in 0..8 {
3782                        let px = mb_x * 16 + b8x * 8 + dx;
3783                        let py = mb_y * 16 + b8y * 8 + dy;
3784                        self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3785                    }
3786                }
3787            }
3788        } else {
3789            // Dequantize all 16 blocks into the 4-quadrant int16 layout (16-byte
3790            // aligned — the kernel uses movdqa coeff loads), then inverse-DCT + add
3791            // prediction + clip per quadrant via openh264. The inverse butterfly +
3792            // (x+32)>>6 is bit-identical to reconstruct_4x4 (verified in accel).
3793            // An 8x8 quad whose cbp bit is clear has ZERO residual: reconstruction
3794            // IS the prediction (the decoder's own uncoded-region fast path) — a row
3795            // copy replaces dequant + convert + idct for that quad. Byte-identical:
3796            // idct of an all-zero block adds (0+32)>>6 = 0 to pred, clip is identity.
3797            #[repr(align(16))]
3798            struct Align16([i16; 64]);
3799            let mut dct_in = Align16([0i16; 64]);
3800            let base = mb_y * 16 * self.cw + mb_x * 16;
3801            for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3802                let rec_off = base + qy * self.cw + qx;
3803                if cbp_luma & (1 << qi) == 0 {
3804                    for r in 0..8 {
3805                        let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
3806                        self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
3807                    }
3808                    continue;
3809                }
3810                for k in 0..4 {
3811                    let blk = qi * 4 + k;
3812                    let (lbx, lby) = LUMA_4X4_SCAN_XY[blk];
3813                    let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3814                    for i in 0..16 {
3815                        dct_in.0[k * 16 + i] = deq[i] as i16;
3816                    }
3817                }
3818                rusty_h264_accel::idct_four_t4_rec(
3819                    &mut self.rec_y[rec_off..],
3820                    self.cw,
3821                    &pred_y[qy * 16 + qx..],
3822                    16,
3823                    &dct_in.0,
3824                );
3825            }
3826        }
3827        #[cfg(not(accel))]
3828        if t8x8 {
3829            // 8x8-transform reconstruction (inverse of the decoder's t8x8 inter path).
3830            let weight = [16i32; 64];
3831            for b8 in 0..4usize {
3832                let (b8x, b8y) = (b8 % 2, b8 / 2);
3833                let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3834                let predb: [i32; 64] = std::array::from_fn(|i| {
3835                    pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3836                });
3837                let recon = add_residual_8x8(&res_r, &predb);
3838                for dy in 0..8 {
3839                    for dx in 0..8 {
3840                        let px = mb_x * 16 + b8x * 8 + dx;
3841                        let py = mb_y * 16 + b8y * 8 + dy;
3842                        self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3843                    }
3844                }
3845            }
3846        } else {
3847            for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3848                let mut predb = [0i32; 16];
3849                for dy in 0..4 {
3850                    for dx in 0..4 {
3851                        predb[dy * 4 + dx] = pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3852                    }
3853                }
3854                let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3855                let s = reconstruct_4x4(&deq, &predb);
3856                store(&mut self.rec_y, self.cw, mb_x * 16 + lbx * 4, mb_y * 16 + lby * 4, &s);
3857            }
3858        }
3859        for c in 0..2 {
3860            // Fast path: dequantize into the quad i16 layout (raster == the kernel's
3861            // z-order for a 2x2) with the Hadamard DC injected, then ONE
3862            // idct+add-pred+clip kernel writes the 8x8 straight into the plane —
3863            // bit-identical to the scalar tail below (verified kernel pairing).
3864            #[cfg(accel)]
3865            {
3866                let base = (mb_y * 8) * self.ccw + mb_x * 8;
3867                let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3868                if cbp_chroma == 0 {
3869                    // No chroma residual at all: recon = prediction (row copies).
3870                    for r in 0..8 {
3871                        let dsti = base + r * self.ccw;
3872                        plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
3873                    }
3874                } else {
3875                    #[repr(align(16))]
3876                    struct A([i16; 64]);
3877                    let mut d = A([0i16; 64]);
3878                    for i in 0..4 {
3879                        let deq = dequantize(&c_q[c][i], qpc);
3880                        for j in 0..16 {
3881                            d.0[i * 16 + j] = deq[j] as i16;
3882                        }
3883                        d.0[i * 16] = c_recon_dc[c][i] as i16;
3884                    }
3885                    rusty_h264_accel::idct_four_t4_rec(&mut plane[base..], self.ccw, &c_pred[c], 8, &d.0);
3886                }
3887            }
3888            #[cfg(not(accel))]
3889            {
3890                // Dequantize the 4 blocks (raster, DC overridden by the 2×2-Hadamard
3891                // recon), then batch the inverse DCT and share the add+clip tail.
3892                let mut deq_blocks = [[0i32; 16]; 4];
3893                for i in 0..4 {
3894                    deq_blocks[i] = dequantize(&c_q[c][i], qpc);
3895                    deq_blocks[i][0] = c_recon_dc[c][i];
3896                }
3897                let mut res = [[0i32; 16]; 4];
3898                inverse_dct_blocks(&deq_blocks, &mut res);
3899                let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3900                for by in 0..2 {
3901                    for bx in 0..2 {
3902                        let mut predb = [0i32; 16];
3903                        for dy in 0..4 {
3904                            for dx in 0..4 {
3905                                predb[dy * 4 + dx] = c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3906                            }
3907                        }
3908                        let s = add_residual_4x4(&res[by * 2 + bx], &predb);
3909                        store(plane, self.ccw, mb_x * 8 + bx * 4, mb_y * 8 + by * 4, &s);
3910                    }
3911                }
3912            }
3913        }
3914        // MV grid + coded flags were set per partition; mark modes as DC.
3915        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3916            self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
3917        }
3918        InterPlan { mvds, plan_refs, n_mvd, cbp, q_blocks, c_dc_levels, c_q, t8x8, q8 }
3919    }
3920
3921    /// Code one planned inter macroblock as CAVLC (the original `encode_inter_mb_v1_b`
3922    /// tail). `plan_inter_mb` already committed the reconstruction + motion grids.
3923    #[allow(clippy::too_many_arguments)]
3924    fn encode_inter_mb_v1_b(
3925        &mut self,
3926        w: &mut BitWriter,
3927        refs: &[crate::RefFrame],
3928        sy: &[u8],
3929        su: &[u8],
3930        sv: &[u8],
3931        mb_x: usize,
3932        mb_y: usize,
3933        mode: u8,
3934        parts: &[(i32, (i32, i32))],
3935        bspec: Option<BInter>,
3936    ) {
3937        let plan = self.plan_inter_mb(refs, sy, su, sv, mb_x, mb_y, mode, parts, bspec);
3938        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
3939        self.emit_inter_cavlc(w, refs.len(), mb_x, mb_y, mode, parts, bspec, &plan);
3940    }
3941
3942    /// CAVLC entropy coding for a planned inter macroblock.
3943    #[allow(clippy::too_many_arguments)]
3944    fn emit_inter_cavlc(
3945        &mut self,
3946        w: &mut BitWriter,
3947        num_refs: usize,
3948        mb_x: usize,
3949        mb_y: usize,
3950        mode: u8,
3951        parts: &[(i32, (i32, i32))],
3952        bspec: Option<BInter>,
3953        plan: &InterPlan,
3954    ) {
3955        let w4 = self.mb_w * 4;
3956        let (cbp, cbp_luma, cbp_chroma) = (plan.cbp, plan.cbp & 15, plan.cbp >> 4);
3957        // mb_pred order (spec 7.3.5.1): mb_type, then all ref_idx_l0, then all mvd_l0.
3958        // B-slice mb_type = the B direction 1/2/3; P-slice uses `mode`. ref_idx coded
3959        // only when >1 reference is active.
3960        w.write_ue(bspec.map_or(mode as u32, |b| b.dir as u32)); // inter mb_type
3961        // P_8x8 (mb_type 3): sub_mb_type per 8×8 (spec 7.3.5.2, before ref_idx/mvd).
3962        // 0 = P_L0_8x8 (one MV) — the only shape emitted for now.
3963        if mode == 3 {
3964            for _ in 0..4 {
3965                w.write_ue(0);
3966            }
3967        }
3968        if num_refs > 1 {
3969            for &(refi, _) in parts {
3970                write_ref_idx(w, refi, num_refs);
3971            }
3972        }
3973        for &(mvdx, mvdy) in &plan.mvds[..plan.n_mvd] {
3974            w.write_se(mvdx);
3975            w.write_se(mvdy);
3976        }
3977        write_cbp_inter(w, cbp);
3978        // transform_size_8x8_flag: after cbp, before mb_qp_delta, present only when
3979        // luma has coefficients and the 8x8 transform is enabled. Every inter partition
3980        // here is >= 8x8, so the spec's allow_8x8 (all partitions >= 8x8) always holds.
3981        if cbp_luma > 0 && self.transform_8x8 {
3982            w.write_bit(plan.t8x8);
3983        }
3984        if cbp != 0 {
3985            w.write_se(self.qp_delta()); // mb_qp_delta (AQ per-MB QPy)
3986        }
3987        self.nnz_cache_load(mb_x, mb_y);
3988        if plan.t8x8 {
3989            // 8x8 residual: four interleaved 4x4 CAVLC sub-blocks per 8x8 block
3990            // (coeff k of sub s -> 8x8 scan position 4k+s), the inverse of the
3991            // decoder's t8x8 inter luma read. nnz set per 4x4 sub-block.
3992            for b8 in 0..4usize {
3993                let (b8x, b8y) = (b8 % 2, b8 / 2);
3994                let scan8 = scan_8x8_fwd(&plan.q8[b8]);
3995                for sub in 0..4usize {
3996                    let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
3997                    let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
3998                    let total = if cbp_luma & (1 << b8) != 0 {
3999                        let nc = self.nc_pred(cx, cy);
4000                        let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
4001                        encode_residual_block(w, &blk, 16, nc) as u8
4002                    } else {
4003                        0
4004                    };
4005                    self.nnz_cache_set(cx, cy, total);
4006                    self.nnz_y[by * w4 + bx] = total;
4007                }
4008            }
4009        } else {
4010            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
4011                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
4012                let total = if cbp_luma & (1 << (blk / 4)) != 0 {
4013                    let nc = self.nc_pred(lbx, lby);
4014                    let scan16 = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
4015                    encode_residual_block(w, &scan16, 16, nc) as u8
4016                } else {
4017                    0
4018                };
4019                self.nnz_cache_set(lbx, lby, total);
4020                self.nnz_y[by * w4 + bx] = total;
4021            }
4022        }
4023        if cbp_chroma != 0 {
4024            for c in 0..2 {
4025                encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
4026            }
4027        }
4028        if cbp_chroma == 2 {
4029            self.chroma_cache_load(mb_x, mb_y);
4030            let w2 = self.mb_w * 2;
4031            for c in 0..2 {
4032                for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4033                    let nc = self.chroma_nc_pred(c, bx, by);
4034                    let ac = scan_4x4_ac(&plan.c_q[c][by * 2 + bx]);
4035                    let total = encode_residual_block(w, &ac, 15, nc) as u8;
4036                    self.chroma_nnz_cache_set(c, bx, by, total);
4037                    self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
4038                }
4039            }
4040        }
4041    }
4042
4043    /// Descent F: reconstruction / skip-check MC through the cached half-pel planes
4044    /// instead of the per-pixel 6-tap. `hpel_block` is proven bit-identical to `mc_luma`
4045    /// (`hpel_block_matches_mc_luma_exactly`) and the `f` plane is the padded,
4046    /// edge-replicated reference, so both paths are BYTE-IDENTICAL; anything outside the
4047    /// padded plane still falls back to `mc_luma`.
4048    ///
4049    /// Census that motivated it: with the search's edge fallback fixed, `mc_luma` is
4050    /// 3.8-5.2% of encode and splits recon ~56-67% / skip-check ~24-35%, the latter at a
4051    /// content-independent one call per macroblock.
4052    #[inline]
4053    fn mc_luma_cached(
4054        &self,
4055        reference: &crate::RefFrame,
4056        x0: usize,
4057        y0: usize,
4058        bw: usize,
4059        bh: usize,
4060        mvx: i32,
4061        mvy: i32,
4062        out: &mut [u8],
4063    ) {
4064        let ch = self.mb_h * 16;
4065        let cw = self.cw;
4066        if !self.fast {
4067            let p = reference.hpel(cw, ch);
4068            if rusty_h264_common::inter::hpel_block(p, x0, y0, bw, bh, mvx, mvy, out) {
4069                return;
4070            }
4071            if let Some((plane, base, stride)) =
4072                rusty_h264_common::inter::hpel_ref(p, x0, y0, bw, bh, mvx, mvy)
4073            {
4074                for r in 0..bh {
4075                    out[r * bw..r * bw + bw].copy_from_slice(&plane[base + r * stride..][..bw]);
4076                }
4077                return;
4078            }
4079        }
4080        mc_luma(&reference.y, cw, ch, x0, y0, bw, bh, mvx, mvy, out);
4081    }
4082
4083    /// Motion-compensates the `P_Skip` prediction (luma + both chroma) from
4084    /// reference 0 at the skip MV.
4085    /// Luma half of the P_Skip prediction. Split out so the fast path can test the
4086    /// luma residual first and only motion-compensate chroma when luma is free —
4087    /// for the majority of (non-free) macroblocks the chroma MC is never needed.
4088    fn skip_predict_luma(
4089        &self,
4090        refs: &[crate::RefFrame],
4091        mb_x: usize,
4092        mb_y: usize,
4093        mv: (i32, i32),
4094    ) -> [u8; 256] {
4095        // Descent E/F: identify this mc_luma population by call site.
4096        #[cfg(feature = "profile")]
4097        let _site = rusty_h264_common::inter::mcstats::SiteTag::new(3);
4098        let reference = &refs[0]; // P_Skip always references index 0
4099        let ch = self.mb_h * 16;
4100        let mut pred_y = [0u8; 256];
4101        self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
4102        pred_y
4103    }
4104
4105    /// Chroma half of the P_Skip prediction (see [`Self::skip_predict_luma`]).
4106    fn skip_predict_chroma(
4107        &self,
4108        refs: &[crate::RefFrame],
4109        mb_x: usize,
4110        mb_y: usize,
4111        mv: (i32, i32),
4112    ) -> [[u8; 64]; 2] {
4113        let reference = &refs[0];
4114        let cch = self.mb_h * 8;
4115        let mut pred_c = [[0u8; 64]; 2];
4116        for c in 0..2 {
4117            let rc = if c == 0 { &reference.u } else { &reference.v };
4118            mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut pred_c[c]);
4119        }
4120        pred_c
4121    }
4122
4123    /// Whether the luma half of the P_Skip prediction has an all-zero quantized
4124    /// residual. Tested first and independently so the caller can defer the chroma
4125    /// MC + test for the common case where luma already disqualifies the skip (a
4126    /// "free", exact P_Skip costs no bits and is strictly beneficial).
4127    fn skip_luma_is_free(&self, sy: &[u8], mb_x: usize, mb_y: usize, pred_y: &[u8; 256]) -> bool {
4128        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
4129        let qp = self.qp;
4130        // Fast path (deployment): the SAME asm kernels the coding path uses —
4131        // `dct_four_t4` computes the 4x4 DCTs of (src - pred) for an 8x8 quad
4132        // STRAIGHT FROM THE PLANES (no scalar gather), `quant_four_4x4` quantizes
4133        // with the identical FF/MF math as scalar `quantize` (bit-identical), and
4134        // "free" = all 64 levels zero, which is order-independent. Per-quad early
4135        // exit. The knob interleaves this against the scalar twin for A/B.
4136        #[cfg(accel)]
4137        if self.skip_accel_check {
4138            #[repr(align(16))]
4139            struct Align16([i16; 64]);
4140            let mut dct = Align16([0i16; 64]);
4141            let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
4142            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
4143            for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
4144                rusty_h264_accel::dct_four_t4(
4145                    &mut dct.0,
4146                    &sy[(mb_y * 16 + qy) * self.cw + mb_x * 16 + qx..],
4147                    self.cw,
4148                    &pred_y[qy * 16 + qx..],
4149                    16,
4150                );
4151                rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
4152                if dct.0.iter().any(|&v| v != 0) {
4153                    return false;
4154                }
4155            }
4156            return true;
4157        }
4158        // Exact quantize-to-zero bounds (mirrors `quantize`: level != 0 iff
4159        // (|c| + ff[p])·mf_oh[p] >= 2^16). With |C_ij| <= 4·SAD (max |H| entry = 2)
4160        // and C_DC = Σres, most blocks are decided by one SAD/sum pass — the full
4161        // scalar DCT+quant proof only runs for the rare undecided middle band.
4162        // BIT-EXACT: both shortcuts are sufficient conditions of the exact check.
4163        let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
4164        let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
4165        let mut t_min = i32::MAX;
4166        for p in 0..8 {
4167            let t = (65536 + mf[p] as i32 - 1) / mf[p] as i32 - ff[p] as i32;
4168            t_min = t_min.min(t);
4169        }
4170        let t_dc = (65536 + mf[0] as i32 - 1) / mf[0] as i32 - ff[0] as i32;
4171        // Whole-MB gate: SAD(any 4x4) <= SAD(MB), so 4*SAD_MB < T_min proves all 16
4172        // blocks quantize to zero from ONE (psadbw) SAD. On skip-heavy content most
4173        // free MBs are exact/near-exact copies (SAD_MB ~ 0) - they skip the whole
4174        // per-block walk. Not-free MBs pay one extra SAD (~2% of their check).
4175        for by in 0..4 {
4176            for bx in 0..4 {
4177                let mut res = [0i32; 16];
4178                let (mut sad, mut dc) = (0i32, 0i32);
4179                for dy in 0..4 {
4180                    for dx in 0..4 {
4181                        let sx = mb_x * 16 + bx * 4 + dx;
4182                        let syy = mb_y * 16 + by * 4 + dy;
4183                        let d = sy[syy * self.cw + sx] as i32
4184                            - pred_y[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
4185                        res[dy * 4 + dx] = d;
4186                        sad += d.abs();
4187                        dc += d;
4188                    }
4189                }
4190                if 4 * sad < t_min {
4191                    continue; // every |C| <= 4·SAD < T_min → all levels zero
4192                }
4193                if dc.abs() >= t_dc {
4194                    return false; // DC level provably nonzero
4195                }
4196                if quantize(&forward_core(&res), qp, 6).iter().any(|&v| v != 0) {
4197                    return false;
4198                }
4199            }
4200        }
4201        true
4202    }
4203
4204    /// Chroma half of [`Self::skip_is_free`].
4205    fn skip_chroma_is_free(
4206        &self,
4207        su: &[u8],
4208        sv: &[u8],
4209        mb_x: usize,
4210        mb_y: usize,
4211        pred_c: &[[u8; 64]; 2],
4212    ) -> bool {
4213        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
4214        let qpc = self.qpc;
4215        // Fast path: one dct_four_t4 covers the whole 8x8 chroma plane region (all 4
4216        // blocks, residual+DCT fused, no scalar gather). Block order is the quad's
4217        // z-scan == raster for 2x2, so block b's DC (pre-quant) sits at dct[b*16] —
4218        // exactly the dc2x2 the Hadamard check needs. quant_four_4x4 with our FF/MF
4219        // is bit-identical to scalar `quantize`; AC-free = positions 1..16 all zero.
4220        #[cfg(accel)]
4221        if self.skip_accel_check {
4222            #[repr(align(16))]
4223            struct Align16C([i16; 64]);
4224            let mut dct = Align16C([0i16; 64]);
4225            let ff = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
4226            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
4227            for c in 0..2 {
4228                let src = if c == 0 { su } else { sv };
4229                rusty_h264_accel::dct_four_t4(
4230                    &mut dct.0,
4231                    &src[(mb_y * 8) * self.ccw + mb_x * 8..],
4232                    self.ccw,
4233                    &pred_c[c],
4234                    8,
4235                );
4236                let dc2x2 = [
4237                    dct.0[0] as i32,
4238                    dct.0[16] as i32,
4239                    dct.0[32] as i32,
4240                    dct.0[48] as i32,
4241                ];
4242                rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
4243                for b in 0..4 {
4244                    if dct.0[b * 16 + 1..b * 16 + 16].iter().any(|&v| v != 0) {
4245                        return false;
4246                    }
4247                }
4248                if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
4249                    return false;
4250                }
4251            }
4252            return true;
4253        }
4254        for c in 0..2 {
4255            let src = if c == 0 { su } else { sv };
4256            let mut dc2x2 = [0i32; 4];
4257            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4258                let mut res = [0i32; 16];
4259                for dy in 0..4 {
4260                    for dx in 0..4 {
4261                        let sx = mb_x * 8 + bx * 4 + dx;
4262                        let syy = mb_y * 8 + by * 4 + dy;
4263                        res[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
4264                            - pred_c[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
4265                    }
4266                }
4267                let coeffs = forward_core(&res);
4268                dc2x2[by * 2 + bx] = coeffs[0];
4269                if quantize(&coeffs, qpc, 6)[1..].iter().any(|&v| v != 0) {
4270                    return false;
4271                }
4272            }
4273            if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
4274                return false;
4275            }
4276        }
4277        true
4278    }
4279
4280    /// SSD between the source and a macroblock prediction (luma + chroma).
4281    #[allow(clippy::too_many_arguments)]
4282    fn pred_ssd(
4283        &self,
4284        sy: &[u8],
4285        su: &[u8],
4286        sv: &[u8],
4287        mb_x: usize,
4288        mb_y: usize,
4289        pred_y: &[u8; 256],
4290        pred_c: &[[u8; 64]; 2],
4291    ) -> i64 {
4292        let mut ssd = 0i64;
4293        for dy in 0..16 {
4294            for dx in 0..16 {
4295                let d = sy[(mb_y * 16 + dy) * self.cw + mb_x * 16 + dx] as i64
4296                    - pred_y[dy * 16 + dx] as i64;
4297                ssd += d * d;
4298            }
4299        }
4300        for c in 0..2 {
4301            let src = if c == 0 { su } else { sv };
4302            for dy in 0..8 {
4303                for dx in 0..8 {
4304                    let d = src[(mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx] as i64
4305                        - pred_c[c][dy * 8 + dx] as i64;
4306                    ssd += d * d;
4307                }
4308            }
4309        }
4310        ssd
4311    }
4312
4313    /// SSD between the *reconstructed* macroblock and the source.
4314    fn mb_ssd(&self, sy: &[u8], su: &[u8], sv: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4315        let mut ssd = 0i64;
4316        for dy in 0..16 {
4317            for dx in 0..16 {
4318                let i = (mb_y * 16 + dy) * self.cw + mb_x * 16 + dx;
4319                let d = sy[i] as i64 - self.rec_y[i] as i64;
4320                ssd += d * d;
4321            }
4322        }
4323        for c in 0..2 {
4324            let (src, rec) = if c == 0 { (su, &self.rec_u) } else { (sv, &self.rec_v) };
4325            for dy in 0..8 {
4326                for dx in 0..8 {
4327                    let i = (mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx;
4328                    let d = src[i] as i64 - rec[i] as i64;
4329                    ssd += d * d;
4330                }
4331            }
4332        }
4333        ssd
4334    }
4335
4336    /// Reconstructs a `P_Skip` macroblock (reconstruction *is* the prediction —
4337    /// no residual coded) and records its motion state.
4338    #[allow(clippy::too_many_arguments)]
4339    fn commit_skip_probe_marker(&self) {}
4340    fn commit_skip(
4341        &mut self,
4342        mb_x: usize,
4343        mb_y: usize,
4344        mv: (i32, i32),
4345        pred_y: &[u8; 256],
4346        pred_c: &[[u8; 64]; 2],
4347    ) {
4348        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
4349        // Skip recon = the prediction verbatim: straight row copies (byte-identical
4350        // to the old per-4x4 gather + store scatter, ~5x fewer ops).
4351        let base = mb_y * 16 * self.cw + mb_x * 16;
4352        for r in 0..16 {
4353            let d = base + r * self.cw;
4354            self.rec_y[d..d + 16].copy_from_slice(&pred_y[r * 16..r * 16 + 16]);
4355        }
4356        let cbase = mb_y * 8 * self.ccw + mb_x * 8;
4357        for c in 0..2 {
4358            let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
4359            for r in 0..8 {
4360                let d = cbase + r * self.ccw;
4361                plane[d..d + 8].copy_from_slice(&pred_c[c][r * 8..r * 8 + 8]);
4362            }
4363        }
4364        self.set_mb_mv(mb_x, mb_y, mv, true, 0);
4365        let w4 = self.mb_w * 4;
4366        for row in 0..4 {
4367            let st = (mb_y * 4 + row) * w4 + mb_x * 4;
4368            self.modes_y[st..st + 4].fill(2);
4369            self.coded_y[st..st + 4].fill(true);
4370        }
4371    }
4372
4373    /// Trial-encodes an inter macroblock to measure its rate-distortion cost
4374    /// `(SSD, bits)` without committing: snapshot the macroblock's grid + recon
4375    /// region, run the real `encode_inter_mb` into a scratch writer, read the
4376    /// bit count and reconstruction SSD, then restore. Neighbor CAVLC context is
4377    /// read (not mutated), so the bit count is accurate.
4378    #[allow(clippy::too_many_arguments)]
4379    fn trial_inter(
4380        &mut self,
4381        refs: &[crate::RefFrame],
4382        sy: &[u8],
4383        su: &[u8],
4384        sv: &[u8],
4385        mb_x: usize,
4386        mb_y: usize,
4387        mode: u8,
4388        parts: &[(i32, (i32, i32))],
4389    ) -> (i64, usize) {
4390        let snap = self.save_mb(mb_x, mb_y);
4391        let mut scratch = BitWriter::new();
4392        self.encode_inter_mb(&mut scratch, refs, sy, su, sv, mb_x, mb_y, mode, parts);
4393        let bits = scratch.bit_len();
4394        let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
4395        self.load_mb(mb_x, mb_y, &snap);
4396        (ssd, bits)
4397    }
4398
4399    /// Trial-encodes the macroblock as **intra** (`encode_mb` runs its own
4400    /// I_16x16-vs-I_4x4 decision), measuring `(SSD, bits)` without committing —
4401    /// the intra candidate for the RD mode decision.
4402    fn trial_intra(
4403        &mut self,
4404        sy: &[u8],
4405        su: &[u8],
4406        sv: &[u8],
4407        mb_x: usize,
4408        mb_y: usize,
4409        is_p: bool,
4410    ) -> (i64, usize) {
4411        let snap = self.save_mb(mb_x, mb_y);
4412        let mut scratch = BitWriter::new();
4413        encode_mb(self, &mut scratch, mb_x, mb_y, sy, su, sv, is_p);
4414        let bits = scratch.bit_len();
4415        let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
4416        self.load_mb(mb_x, mb_y, &snap);
4417        (ssd, bits)
4418    }
4419
4420    /// Best `(ref_idx, mv, cost)` for one partition by `SATD + λ·bits`, searched
4421    /// across every reference (`cost` is that SATD-domain rate-distortion cost).
4422    /// `extra` seeds the search with already-found MVs (e.g. the 16×16 result when
4423    /// refining a sub-partition).
4424    #[allow(clippy::too_many_arguments)]
4425    fn best_part(
4426        &self,
4427        refs: &[crate::RefFrame],
4428        sy: &[u8],
4429        nb: &[MvNeighbor; 3],
4430        num_refs: usize,
4431        rx: usize,
4432        ry: usize,
4433        rw: usize,
4434        rh: usize,
4435        extra: &[(i32, i32)],
4436        lme: f64,
4437    ) -> (i32, (i32, i32), i64) {
4438        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4439        let [a, b, c] = *nb;
4440        let (mut br, mut bmv, mut bc) = (0i32, (0, 0), i64::MAX);
4441        for r in 0..num_refs {
4442            let mut seeds = vec![predict_mv(a, b, c, r as i32)];
4443            seeds.extend_from_slice(extra);
4444            let (mv, cost) = self.motion_search(&refs[r], sy, rx, ry, rw, rh, &seeds, lme, None);
4445            let cost = cost + (lme * ref_bits(r, num_refs) as f64) as i64;
4446            if cost < bc {
4447                bc = cost;
4448                br = r as i32;
4449                bmv = mv;
4450            }
4451        }
4452        (br, bmv, bc)
4453    }
4454
4455    /// Sub-pel-refines ONE already-chosen partition, reusing `motion_search`'s cost
4456    /// closure via its `start` hook so the rate term and predictor centre are exactly
4457    /// the ones the full search used. Companion to `best_part` under `sp_defer`.
4458    #[allow(clippy::too_many_arguments)]
4459    fn refine_part(
4460        &self,
4461        refs: &[crate::RefFrame],
4462        sy: &[u8],
4463        nb: &[MvNeighbor; 3],
4464        num_refs: usize,
4465        rx: usize,
4466        ry: usize,
4467        rw: usize,
4468        rh: usize,
4469        lme: f64,
4470        r: i32,
4471        mv: (i32, i32),
4472    ) -> ((i32, i32), i64) {
4473        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4474        let [a, b, c] = *nb;
4475        let rb = (lme * ref_bits(r as usize, num_refs) as f64) as i64;
4476        let seeds = [predict_mv(a, b, c, r)];
4477        let (m, cc) = self.motion_search(&refs[r as usize], sy, rx, ry, rw, rh, &seeds, lme, Some(mv));
4478        (m, cc + rb)
4479    }
4480
4481    /// Cheapest `I_16x16` prediction's SAD over the four whole-block modes, using
4482    /// the already-reconstructed top/left neighbours — the intra candidate's cost
4483    /// in the fast (SAD) mode decision, without the full `I_4x4` search.
4484    fn best_i16_sad(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4485        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4486        let (lx, ly) = (mb_x * 16, mb_y * 16);
4487        let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4488        let mut top = [0u8; 16];
4489        let mut left = [0u8; 16];
4490        if avail_top {
4491            for i in 0..16 {
4492                top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4493            }
4494        }
4495        if avail_left {
4496            for i in 0..16 {
4497                left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4498            }
4499        }
4500        let corner = if avail_top && avail_left {
4501            self.rec_y[(ly - 1) * self.cw + lx - 1]
4502        } else {
4503            0
4504        };
4505        let mut best = i64::MAX;
4506        for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4507            if !mode.available(avail_top, avail_left) {
4508                continue;
4509            }
4510            let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4511            best = best.min(sad_16x16(sy, self.cw, lx, ly, &pred));
4512        }
4513        best
4514    }
4515
4516    /// SATD sibling of [`Self::best_i16_sad`] — the intra candidate's cost in the
4517    /// quality preset's SATD mode decision (openh264's `WelsMdI16x16`).
4518    fn best_i16_satd(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4519        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4520        let (lx, ly) = (mb_x * 16, mb_y * 16);
4521        let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4522        let mut top = [0u8; 16];
4523        let mut left = [0u8; 16];
4524        if avail_top {
4525            for i in 0..16 {
4526                top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4527            }
4528        }
4529        if avail_left {
4530            for i in 0..16 {
4531                left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4532            }
4533        }
4534        let corner = if avail_top && avail_left {
4535            self.rec_y[(ly - 1) * self.cw + lx - 1]
4536        } else {
4537            0
4538        };
4539        let mut best = i64::MAX;
4540        for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4541            if !mode.available(avail_top, avail_left) {
4542                continue;
4543            }
4544            let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4545            best = best.min(satd_16x16(sy, self.cw, lx, ly, &pred));
4546        }
4547        best
4548    }
4549
4550    /// Snapshots the per-block grids and reconstruction for one macroblock, so a
4551    /// trial encode can be rolled back.
4552    fn save_mb(&self, mb_x: usize, mb_y: usize) -> MbState {
4553        let mut d = MbState::default();
4554        self.save_mb_into(mb_x, mb_y, &mut d);
4555        d
4556    }
4557
4558    /// [`save_mb`](Self::save_mb) into an existing buffer, reusing its allocations.
4559    /// The per-macroblock region is a fixed size, so after the first call every
4560    /// `Vec` already has the capacity it needs and refilling is a pure copy.
4561    fn save_mb_into(&self, mb_x: usize, mb_y: usize, d: &mut MbState) {
4562        let w4 = self.mb_w * 4;
4563        let w2 = self.mb_w * 2;
4564        macro_rules! reg4 {
4565            ($v:expr, $o:expr) => {{
4566                $o.clear();
4567                for dy in 0..4 {
4568                    for dx in 0..4 {
4569                        $o.push($v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx]);
4570                    }
4571                }
4572            }};
4573        }
4574        macro_rules! regn {
4575            ($v:expr, $o:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {{
4576                $o.clear();
4577                for dy in 0..$n {
4578                    for dx in 0..$n {
4579                        $o.push($v[($oy + dy) * $stride + $ox + dx]);
4580                    }
4581                }
4582            }};
4583        }
4584        regn!(self.rec_y, d.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4585        regn!(self.rec_u, d.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4586        regn!(self.rec_v, d.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4587        reg4!(self.nnz_y, d.nnz_y);
4588        regn!(self.nnz_c[0], d.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4589        regn!(self.nnz_c[1], d.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4590        reg4!(self.mv_y, d.mv_y);
4591        reg4!(self.inter_y, d.inter_y);
4592        reg4!(self.ref_idx_y, d.ref_idx_y);
4593        reg4!(self.coded_y, d.coded_y);
4594        reg4!(self.modes_y, d.modes_y);
4595        d.cur_qp = self.cur_qp;
4596    }
4597
4598    /// Restores a macroblock's grids + reconstruction from a [`save_mb`] snapshot.
4599    fn load_mb(&mut self, mb_x: usize, mb_y: usize, s: &MbState) {
4600        let w4 = self.mb_w * 4;
4601        let w2 = self.mb_w * 2;
4602        macro_rules! put4 {
4603            ($v:expr, $src:expr) => {
4604                for dy in 0..4 {
4605                    for dx in 0..4 {
4606                        $v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx] = $src[dy * 4 + dx];
4607                    }
4608                }
4609            };
4610        }
4611        macro_rules! putn {
4612            ($v:expr, $src:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {
4613                for dy in 0..$n {
4614                    for dx in 0..$n {
4615                        $v[($oy + dy) * $stride + $ox + dx] = $src[dy * $n + dx];
4616                    }
4617                }
4618            };
4619        }
4620        putn!(self.rec_y, s.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4621        putn!(self.rec_u, s.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4622        putn!(self.rec_v, s.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4623        put4!(self.nnz_y, s.nnz_y);
4624        putn!(self.nnz_c[0], s.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4625        putn!(self.nnz_c[1], s.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4626        put4!(self.mv_y, s.mv_y);
4627        put4!(self.inter_y, s.inter_y);
4628        put4!(self.ref_idx_y, s.ref_idx_y);
4629        put4!(self.coded_y, s.coded_y);
4630        put4!(self.modes_y, s.modes_y);
4631        self.cur_qp = s.cur_qp;
4632    }
4633
4634    /// Loads the per-MB luma nnz prediction cache (openh264 `scan8` style): the top
4635    /// row from the macroblock above and the left column from the macroblock to the
4636    /// left (both already in `nnz_y`), with `0x80` at the picture edges. After this,
4637    /// neighbour nnz reads are branchless cache indexing — no bounds-checked `Option`.
4638    fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4639        let w4 = self.mb_w * 4;
4640        for lbx in 0..4 {
4641            self.nnz_l_cache[1 + lbx] = if mb_y == 0 {
4642                0x80
4643            } else {
4644                self.nnz_y[(mb_y * 4 - 1) * w4 + (mb_x * 4 + lbx)]
4645            };
4646        }
4647        for lby in 0..4 {
4648            self.nnz_l_cache[(lby + 1) * 5] = if mb_x == 0 {
4649                0x80
4650            } else {
4651                self.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 - 1)]
4652            };
4653        }
4654    }
4655
4656    /// Branchless nnz prediction (`nC`) for luma block `(lbx,lby)` from the cache —
4657    /// the `0x80` sentinel + `& 0x7f` mask collapse the four availability cases
4658    /// (matches the scalar nnz predict). Call after the block's left/top are cached.
4659    #[inline]
4660    fn nc_pred(&self, lbx: usize, lby: usize) -> i32 {
4661        let left = self.nnz_l_cache[(lby + 1) * 5 + lbx] as i32; // (lbx-1)+1
4662        let top = self.nnz_l_cache[lby * 5 + (lbx + 1)] as i32; // (lby-1)+1
4663        let r = left + top;
4664        if r < 0x80 {
4665            (r + 1) >> 1
4666        } else {
4667            r & 0x7f
4668        }
4669    }
4670
4671    /// Records a luma block's nnz into the per-MB cache (for later neighbour reads).
4672    #[inline]
4673    fn nnz_cache_set(&mut self, lbx: usize, lby: usize, total: u8) {
4674        self.nnz_l_cache[(lby + 1) * 5 + (lbx + 1)] = total;
4675    }
4676
4677    /// Loads the per-MB chroma nnz prediction cache (both planes) from the chroma
4678    /// blocks above/left, `0x80` at the picture edges — the chroma analogue of
4679    /// [`Self::nnz_cache_load`] (2×2 blocks → padded 3×3 grid).
4680    fn chroma_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4681        let w2 = self.mb_w * 2;
4682        for c in 0..2 {
4683            for bx in 0..2 {
4684                self.nnz_c_cache[c][1 + bx] = if mb_y == 0 {
4685                    0x80
4686                } else {
4687                    self.nnz_c[c][(mb_y * 2 - 1) * w2 + (mb_x * 2 + bx)]
4688                };
4689            }
4690            for by in 0..2 {
4691                self.nnz_c_cache[c][(by + 1) * 3] = if mb_x == 0 {
4692                    0x80
4693                } else {
4694                    self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 - 1)]
4695                };
4696            }
4697        }
4698    }
4699
4700    /// Branchless chroma nnz prediction (`nC`) for plane `c`, block `(bx,by)`.
4701    #[inline]
4702    fn chroma_nc_pred(&self, c: usize, bx: usize, by: usize) -> i32 {
4703        let left = self.nnz_c_cache[c][(by + 1) * 3 + bx] as i32;
4704        let top = self.nnz_c_cache[c][by * 3 + (bx + 1)] as i32;
4705        let r = left + top;
4706        if r < 0x80 {
4707            (r + 1) >> 1
4708        } else {
4709            r & 0x7f
4710        }
4711    }
4712
4713    /// Records a chroma block's nnz into the per-MB cache.
4714    #[inline]
4715    fn chroma_nnz_cache_set(&mut self, c: usize, bx: usize, by: usize, total: u8) {
4716        self.nnz_c_cache[c][(by + 1) * 3 + (bx + 1)] = total;
4717    }
4718}
4719
4720/// Encodes a slice's macroblocks then RBSP trailing bits, returning the
4721/// **deblocked** reconstruction to serve as the next frame's reference.
4722///
4723/// `is_p` selects P-slice framing (`mb_skip_run` prefix + intra `mb_type` +5
4724/// offset). In phase 4a every macroblock is still coded intra; motion-compensated
4725/// macroblocks arrive in 4b (using `reference`).
4726/// Boundary strengths for one macroblock, derived from the encoder's own grids
4727/// the moment it finishes coding.
4728///
4729/// `ref_idx_y` holds raw indices (-1 for intra) rather than the deblocker's
4730/// `NO_REF` sentinel; safe because reference identity is only compared between
4731/// two INTER blocks, which always carry a valid index.
4732// NOT inlined: this sits at three exits of the hottest loop in the encoder, and
4733// inlining it there costs more in I-cache and register pressure on the
4734// surrounding code than the call saves (measured: the loop grew ~2x the
4735// derivation's own cost).
4736#[inline(never)]
4737fn derive_mb_bs_from(
4738    fe: &FrameEncoder,
4739    mb_x: usize,
4740    mb_y: usize,
4741    kind: rusty_h264_common::deblock::MbKind,
4742) -> rusty_h264_common::deblock::MbBs {
4743    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncBs);
4744    let view = rusty_h264_common::deblock::BlockInfo {
4745        inter: &fe.inter_y,
4746        nnz: &fe.nnz_y,
4747        mv: &fe.mv_y,
4748        ref_id: &fe.ref_idx_y,
4749        mv1: &[],
4750        ref_id1: &[],
4751        w4: fe.mb_w * 4,
4752        t8x8: &[],
4753        bs: &[], kind: &[],
4754    };
4755    rusty_h264_common::deblock::derive_mb_kind(&view, mb_x, mb_y, kind)
4756}
4757
4758pub fn encode_slice_data(
4759    w: &mut BitWriter,
4760    cfg: &EncoderConfig,
4761    frame: &YuvFrame,
4762    qp: u8,
4763    is_p: bool,
4764    refs: &[crate::RefFrame],
4765    qpo: &[i32],
4766) -> crate::RefFrame {
4767    let _g_prep = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncPrep);
4768    let mut fe = FrameEncoder::new(cfg);
4769    let precomp = rusty_h264_common::deblock::precomputed_bs_enabled();
4770    let mut bs_grid =
4771        vec![rusty_h264_common::deblock::MbBs::UNSET; if precomp { fe.mb_w * fe.mb_h } else { 0 }];
4772    fe.qp = qp;
4773    fe.qpc = chroma_qp(qp);
4774    fe.cur_qp = qp;
4775    if cfg.cabac_dz_div > 0 {
4776        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
4777    } // QPY_PREV starts at the slice QP so the first mb_qp_delta is 0
4778    let (sy, su, sv) = coded_source(cfg, frame);
4779    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
4780    let num_refs = refs.len();
4781    // me_wide CONTENT GATE: on a pure PAN the global-MC residual ≈ 0, so the diamond's
4782    // seed (median = pan MV) is already right and the wide rescue only over-fits
4783    // (spurious MVs that hurt the B-frames' spatial-direct — the panc regression).
4784    // Gate it off there; non-uniform content (real stalls) reads well above 0.
4785    if is_p && fe.me_wide && !refs.is_empty()
4786        && global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y) < fe.me_wide_coh
4787    {
4788        fe.me_wide = false;
4789    }
4790    // me_wide HEAD-ROOM GATE (the dispatcher the truth table asked for). The rescue
4791    // only pays where a wide search actually beats a predictor-local one; measure
4792    // that directly per frame and route the frame. `RFF_ME_HR` sets the threshold
4793    // (percent); 0 disables the gate and restores the always-on behaviour.
4794    // Skip the probe entirely when the gate is disabled: it must not tax the
4795    // default path (`RFF_ME_HR=0`), which stays byte-identical to pre-gate output.
4796    if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
4797        let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4798        if me_wide_hr_dbg() {
4799            eprintln!("ME_HR qp{qp} headroom={hr:.2}");
4800        }
4801        if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
4802            fe.me_wide = false;
4803        }
4804    }
4805    // Track-B B2 DISPATCH (WHYS H-2): SAD full-pel wins where a plain full-pel
4806    // translational search actually improves on zero motion (`b2_mgain`) and loses
4807    // on flash/fine-detail content. Probe per frame, route the frame — per-frame,
4808    // not cross-frame, so it stays deterministic under GOP-parallel encode.
4809    if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
4810        let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4811        if me_sadt_dbg() {
4812            eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
4813        }
4814        fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
4815        // H-24: the mv-cost SHAPE rides the same probe (its BD sign-flip tracks
4816        // motion for the same physical reason B2's does).
4817        if mv_smooth_mode() == 1 {
4818            // dcfrac veto mirrors B2's: crew-class FLASH frames satisfy the mgain
4819            // test but SAD/mvd statistics mislead there (H-13/H-26).
4820            fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
4821        }
4822        // H-13: near-static frames skip the split searches entirely.
4823        let smg = split_mg();
4824        if smg > 0.0 {
4825            fe.do_splits = mg >= smg;
4826        }
4827    }
4828    // Content-adaptive cost-function dispatch (codec-content-adaptive-dispatch): the
4829    // fast preset prices modes by cheap SAD, which is rate-blind on detailed MBs;
4830    // route the top `satd_q` fraction of highest-VARIANCE MBs to the rate-faithful
4831    // SATD cost. A per-frame PERCENTILE threshold makes the routed fraction — hence
4832    // the speed/quality split — content-invariant (same q → same fraction on any
4833    // clip). `satd_q == 0` leaves the threshold at MAX (pure SAD, byte-identical).
4834    if is_p && fe.satd_q > 0.0 {
4835        let mut vars: Vec<i64> = (0..fe.mb_h)
4836            .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
4837            .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
4838            .collect();
4839        vars.sort_unstable();
4840        let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
4841        fe.satd_var_thresh = vars[idx];
4842    }
4843    // Adaptive Quantization: per-MB target QPy from content (finer on flat MBs,
4844    // coarser on busy ones). `mb_qpy` records each MB's ACTUAL QPy (a skip / cbp==0
4845    // MB inherits `cur_qp`), for the deblock filter. `strength 0` → uniform → the
4846    // mb_qp_delta stays 0, byte-identical.
4847    let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
4848    apply_mbtree_qpo(&mut aq_qp, qpo); // mb-tree temporal AQ (empty = byte-identical)
4849    fe.cur_qp = qp;
4850    let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
4851    let mut skip_run = 0u32;
4852    // ---- adaptive RD-skip gate -------------------------------------------
4853    // RD P_Skip is a large win on temporally redundant content and a large LOSS
4854    // on detailed content (SSIM: akiyo -13.1%, FourPeople -5.6% vs in_to_tree
4855    // +34.0%, stockholm +95.7%). The separating signal is the content's own
4856    // FREE-skip rate — how much of it is already exactly redundant — and the gap
4857    // is wide (winners >=58.7%, losers <=6.4%). Measure it ONLINE over the first
4858    // slice of the frame and enable RD skip for the remainder only if it clears
4859    // the bar. Within-frame, so it stays deterministic under GOP-parallel encode.
4860    if is_p && mv_cmp_on() {
4861        MVCMP_FRAME.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4862    }
4863    // Reused across every RD-skip candidate — see `MbState`.
4864    let mut rdskip_snap = MbState::default();
4865    let mut rdskip_free = 0usize;
4866    let mut rdskip_seen = 0usize;
4867    let mut rdskip_on = false;
4868    let mut greedy_on = fe.greedy_min_free == 0; // 0 = ungated (historic behaviour)
4869    let rdskip_learn = (fe.mb_w * fe.mb_h / 8).max(64);
4870    let rdskip_min_free = fe.rd_skip_min_free as usize;
4871
4872    drop(_g_prep);
4873    let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
4874    for mb_y in 0..fe.mb_h {
4875        for mb_x in 0..fe.mb_w {
4876            let mb_idx = mb_y * fe.mb_w + mb_x;
4877            fe.qp = aq_qp[mb_idx];
4878            fe.qpc = chroma_qp(aq_qp[mb_idx]);
4879            // P_Skip: motion-compensate from the most-recent reference; accept if free.
4880            // Chosen inter coding: (mb_type, per-partition (ref_idx, mv)).
4881            let mut inter: Option<InterChoice> = None;
4882            // Bits of an inter macroblock already encoded by the skip decision
4883            // below. When present the emit path splices them instead of encoding
4884            // the same macroblock a second time.
4885            let mut coded: Option<BitWriter> = None;
4886            if is_p {
4887                if num_refs > 0 {
4888                    // P_Skip prediction (reference 0). A free skip (zero residual) is
4889                    // taken immediately; the quality preset also takes a greedy P_Skip
4890                    // when its SAD is below the neighbour-predicted bound (below).
4891                    let _g_skip = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSkip);
4892                    let _g_smc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
4893                    rdskip_seen += 1;
4894                    if rdskip_seen >= rdskip_learn {
4895                        rdskip_on = rdskip_free * 100 >= rdskip_seen * rdskip_min_free;
4896                        greedy_on = fe.greedy_min_free == 0
4897                            || rdskip_free * 100 >= rdskip_seen * fe.greedy_min_free as usize;
4898                    }
4899                    let mv_skip = fe.skip_mv(mb_x, mb_y);
4900                    let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
4901                    drop(_g_smc);
4902                    let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
4903                    // Chroma MC only when it can matter: luma already free (so the
4904                    // skip might be taken) or the quality path needs it below.
4905                    let skip_c = if luma_free || !fe.fast {
4906                        fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
4907                    } else {
4908                        [[0u8; 64]; 2]
4909                    };
4910                    let is_free =
4911                        luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
4912                    // Skip-prediction luma SAD (the quality preset's predicted-SAD apparatus).
4913                    let skip_sad = if fe.fast {
4914                        0
4915                    } else {
4916                        let (lx, ly) = (mb_x * 16, mb_y * 16);
4917                        let mut s = 0u32;
4918                        for dy in 0..16 {
4919                            let src = &sy[(ly + dy) * fe.cw + lx..][..16];
4920                            let p = &skip_y[dy * 16..][..16];
4921                            s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
4922                        }
4923                        s
4924                    };
4925                    if is_free {
4926                        fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4927                        if !fe.fast {
4928                            fe.mb_was_skip[mb_idx] = true;
4929                            fe.mb_skip_sad[mb_idx] = skip_sad;
4930                        }
4931                        mb_qpy[mb_idx] = fe.cur_qp; // skip inherits QPy
4932                        rdskip_free += 1;
4933                        if precomp {
4934                            bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4935                        }
4936                        skip_run += 1;
4937                        continue;
4938                    }
4939                    drop(_g_skip);
4940                    let (lx, ly) = (mb_x * 16, mb_y * 16);
4941                    let nb = {
4942                        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
4943                        fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
4944                    };
4945                    let lme = lambda.sqrt();
4946
4947                    if fe.fast {
4948                        // Fast preset: pick the cheapest *prediction* by SATD (no
4949                        // trial-encoding), then always code its residual — P_16x16 vs
4950                        // I_16x16 only, no sub-partitions. Crucially it does NOT make a
4951                        // SATD skip-vs-code decision: P_Skip is taken only for a truly
4952                        // free (zero-residual) macroblock, handled above. Pricing skip
4953                        // by SATD would drop residual the QP wants coded and tank PSNR;
4954                        // like x264's fast presets, fast trades *efficiency* (more bits)
4955                        // for speed, not quality. The faster ME is what makes it fast.
4956                        // Adaptive dispatch: high-variance MBs price by SATD (both
4957                        // inter — via `mb_use_satd` in `best_part` — and intra), the
4958                        // rest by cheap SAD. Set the per-MB flag before best_part.
4959                        fe.mb_use_satd = fe.satd_q > 0.0
4960                            && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
4961                        let (r16, mv16, cost_inter) =
4962                            fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4963                        let cost_intra = if fe.mb_use_satd {
4964                            fe.best_i16_satd(&sy, mb_x, mb_y)
4965                        } else {
4966                            fe.best_i16_sad(&sy, mb_x, mb_y)
4967                        } + (lme * fe.tune_intra_penalty) as i64;
4968                        inter = if cost_intra < cost_inter {
4969                            None // intra wins → encode_mb below
4970                        } else {
4971                            Some((0, vec![(r16, mv16)]))
4972                        };
4973                    } else {
4974                        // Quality preset: openh264's mode-decision model — SATD + λ·mvbits
4975                        // cost ESTIMATE (no per-candidate trial-encode); modes are ranked
4976                        // by that cost and only the winner is encoded (once) below. This
4977                        // removes ~the 93%-of-quality re-encode cost.
4978
4979                        // Greedy P_Skip (openh264 `PredictSadSkip`): take the skip when its
4980                        // luma SAD is below the neighbour-predicted skip SAD. The threshold
4981                        // is what skip neighbours achieved, so the skip propagates from the
4982                        // free skips and self-limits — no fixed bound, no inter-chain drift.
4983                        if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
4984                            fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4985                            fe.mb_was_skip[mb_idx] = true;
4986                            fe.mb_skip_sad[mb_idx] = skip_sad;
4987                            mb_qpy[mb_idx] = fe.cur_qp; // skip inherits QPy
4988                            if precomp {
4989                                bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4990                            }
4991                            skip_run += 1;
4992                            continue;
4993                        }
4994
4995                        // 16×16 baseline (SATD + λ·bits, with sub-pel refinement).
4996                        let (r16, mv16, c16) =
4997                            fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4998                        let mut best_c = c16;
4999                        let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
5000
5001                        // Sub-partitions, ranked by SATD, gated on a heavy 16×16 (a likely
5002                        // motion boundary — the 4 sub-pel searches are the expensive part).
5003                        const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
5004                        let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
5005                        let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
5006                        let split_t = split_t();
5007                        if fe.do_splits && c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
5008                            let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
5009                            let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
5010                            let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
5011                            let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
5012                            if ct + cb < best_c {
5013                                best_c = ct + cb;
5014                                pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
5015                            }
5016                            if cl + cr < best_c {
5017                                best_c = cl + cr;
5018                                pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
5019                            }
5020
5021                            // P_8x8: four independent 8×8 sub-partitions (finer motion
5022                            // granularity — the win on complex/boundary motion). Each 8×8
5023                            // seeded by the 16×16 MV; the exact chained MVD is computed in
5024                            // plan_inter_mb. Same heavy-16×16 gate as the 2-way splits.
5025                            if fe.sub8x8 {
5026                                let mut c8 = (lme * 4.0) as i64; // ~4 sub_mb_type bits
5027                                let mut p8 = Vec::with_capacity(4);
5028                                for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
5029                                    let (r, mv, c) = fe.best_part(
5030                                        refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
5031                                    );
5032                                    c8 += c;
5033                                    p8.push((r, mv));
5034                                }
5035                                if c8 < best_c {
5036                                    best_c = c8;
5037                                    pick = Some((3u8, p8));
5038                                }
5039                            }
5040                        }
5041
5042                        // U5-struct: everything above searched FULL-PEL only when
5043                        // `sp_defer` is set. Now that a shape has won, refine just its
5044                        // sub-blocks — the losing shapes' refinements were the waste
5045                        // (measured 3.4–6.4× more refinement than necessary).
5046                        if fe.sp_defer.get() {
5047                            if let Some((mode, parts)) = pick.as_mut() {
5048                                let regions: &[(usize, usize, usize, usize)] = match mode {
5049                                    1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
5050                                    2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
5051                                    3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
5052                                    _ => &[(0, 0, 16, 16)],
5053                                };
5054                                let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
5055                                for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
5056                                    let (r, mv) = parts[i];
5057                                    let (m2, c2) = fe.refine_part(
5058                                        refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
5059                                    );
5060                                    parts[i] = (r, m2);
5061                                    tot += c2;
5062                                }
5063                                best_c = tot;
5064                            }
5065                        }
5066                        if split_harvest::enabled() {
5067                            let won = match pick.as_ref().map(|p| p.0) {
5068                                Some(0) | None => 0u8,
5069                                Some(m) => m,
5070                            };
5071                            split_harvest::record(c16, best_c, lme, split_gate, won);
5072                        }
5073                        // Intra is ALWAYS a candidate (textured / occluded content):
5074                        // I_16x16 SATD + λ·mode bits.
5075                        let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
5076                            + (lme * fe.tune_intra_penalty) as i64;
5077                        inter = if c_intra < best_c { None } else { pick };
5078                        fe.mb_was_skip[mb_idx] = false;
5079                        fe.mb_skip_sad[mb_idx] = skip_sad;
5080                    }
5081
5082                    // ---- RD P_Skip ----------------------------------------
5083                    // The default criterion skips only when the residual quantizes
5084                    // to EXACTLY zero. That matches x264 at both extremes (akiyo
5085                    // 72.5% vs 73.6%, mobile 1.0% vs 1.4%) but falls 17-23 points
5086                    // short in the middle (foreman 6.4% vs 23.6%), because x264
5087                    // also skips macroblocks whose residual is small-but-nonzero.
5088                    // Decide it properly: trial-encode the chosen mode for real
5089                    // bits + reconstruction SSD, and compare J = SSD + lambda*R
5090                    // against the skip. Raw-SAD versions of this comparison fail
5091                    // badly (coding REPAIRS the residual, skipping keeps it), so
5092                    // the distortion term has to come from the reconstruction.
5093                    if fe.rd_skip && rdskip_on && inter.is_some() {
5094                        let skip_cp = fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip);
5095                        // A P_Skip carries no residual, so its RECONSTRUCTION *is*
5096                        // its prediction — the skip SSD needs no state mutation at
5097                        // all. The commit / mb_ssd / restore round trip this
5098                        // replaces cost a full macroblock save+restore on every
5099                        // candidate, including the ones that go on to code.
5100                        let ssd_s = fe.pred_ssd(&sy, &su, &sv, mb_x, mb_y, &skip_y, &skip_cp);
5101                        debug_assert_eq!(ssd_s, {
5102                            let snap = fe.save_mb(mb_x, mb_y);
5103                            fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
5104                            let v = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
5105                            fe.load_mb(mb_x, mb_y, &snap);
5106                            v
5107                        }, "skip prediction SSD must equal the committed-skip reconstruction SSD");
5108                        // A skip inside a run costs ~1 bit of mb_skip_run.
5109                        let j_skip = ssd_s as f64 + lambda;
5110                        // Search-skip gate: when the null arm is this cheap it
5111                        // almost always wins, so take it without pricing the coded
5112                        // arm at all. This is where the decision's remaining cost
5113                        // lives — the coded arm is encoded and then discarded on
5114                        // 55-80% of candidates.
5115                        let take_skip = if fe.rd_skip_fast_t > 0.0
5116                            && (ssd_s as f64) <= lambda * fe.rd_skip_fast_t
5117                        {
5118                            true
5119                        } else {
5120                        // Otherwise encode ONCE, into scratch, and KEEP the state.
5121                        // If the skip loses, those are the real bits and they splice
5122                        // straight into the slice. The previous shape trial-encoded,
5123                        // threw the result away, and then encoded again — paying
5124                        // twice on the path that actually codes.
5125                            fe.save_mb_into(mb_x, mb_y, &mut rdskip_snap);
5126                            let mut scratch = BitWriter::new();
5127                            {
5128                                let (m, p) = inter.as_ref().unwrap();
5129                                fe.encode_inter_mb(
5130                                    &mut scratch, refs, &sy, &su, &sv, mb_x, mb_y, *m, p,
5131                                );
5132                            }
5133                            let bits_c = scratch.bit_len();
5134                            let ssd_c = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
5135                            let won = j_skip <= ssd_c as f64 + lambda * bits_c as f64;
5136                            if won {
5137                                fe.load_mb(mb_x, mb_y, &rdskip_snap); // undo it; take the skip
5138                                true
5139                            } else {
5140                                coded = Some(scratch); // keep it — no second encode
5141                                false
5142                            }
5143                        };
5144                        if take_skip {
5145                            fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
5146                            if !fe.fast {
5147                                fe.mb_was_skip[mb_idx] = true;
5148                                fe.mb_skip_sad[mb_idx] = skip_sad;
5149                            }
5150                            mb_qpy[mb_idx] = fe.cur_qp;
5151                            if precomp {
5152                                bs_grid[mb_idx] = derive_mb_bs_from(
5153                                    &fe, mb_x, mb_y,
5154                                    rusty_h264_common::deblock::MbKind::Skip,
5155                                );
5156                            }
5157                            skip_run += 1;
5158                            continue;
5159                        }
5160                    }
5161                }
5162                w.write_ue(skip_run); // run of skipped macroblocks before this one
5163                skip_run = 0;
5164            }
5165            if mv_force_on() && is_p && inter.is_some() {
5166                let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
5167                let ext = EXT_MV.lock().unwrap();
5168                if let Some(field) = ext.get(fi) {
5169                    let w4 = fe.mb_w * 4;
5170                    let b0 = (mb_y * 4) * w4 + mb_x * 4;
5171                    // uniform 16x16 only: a sub-partitioned macroblock has no single
5172                    // vector to transplant, so leave those to our own decision
5173                    let uniform = (0..4).all(|r| {
5174                        (0..4).all(|c| field.get(b0 + r * w4 + c) == field.get(b0))
5175                    });
5176                    if uniform {
5177                        if let Some(&emv) = field.get(b0) {
5178                            inter = Some((0, vec![(0, emv)]));
5179                            MVCMP[6].fetch_add(1, std::sync::atomic::Ordering::Relaxed);
5180                        }
5181                    }
5182                }
5183            }
5184            if mv_cmp_on() && is_p {
5185                if let Some((mode, parts)) = inter.as_ref() {
5186                    let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
5187                    let ext = EXT_MV.lock().unwrap();
5188                    if let Some(field) = ext.get(fi) {
5189                        let bidx = (mb_y * 4) * (fe.mb_w * 4) + mb_x * 4;
5190                        if let Some(&emv) = field.get(bidx) {
5191                            let (mode, parts) = (*mode, parts.clone());
5192                            drop(ext);
5193                            // Both priced through the SAME pipeline: MC, transform,
5194                            // quantize, CAVLC. Real bits, real reconstruction SSD.
5195                            let (so, bo) =
5196                                fe.trial_inter(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts);
5197                            let (se, be) = fe.trial_inter(
5198                                refs, &sy, &su, &sv, mb_x, mb_y, 0, &[(0, emv)],
5199                            );
5200                            let jo = so as f64 + lambda * bo as f64;
5201                            let je = se as f64 + lambda * be as f64;
5202                            use std::sync::atomic::Ordering::Relaxed;
5203                            MVCMP[0].fetch_add(1, Relaxed);
5204                            MVCMP[1].fetch_add(bo as u64, Relaxed);
5205                            MVCMP[2].fetch_add(be as u64, Relaxed);
5206                            MVCMP[3].fetch_add(so.max(0) as u64, Relaxed);
5207                            MVCMP[4].fetch_add(se.max(0) as u64, Relaxed);
5208                            MVCMP[5].fetch_add((je < jo) as u64, Relaxed);
5209                            MVCMP[6].fetch_add((parts[0].1 != emv) as u64, Relaxed);
5210                        }
5211                    }
5212                }
5213            }
5214            // Capture the kind before `inter` is consumed: the deblocking
5215            // strengths of an intra macroblock are pure constants.
5216            let mb_kind = match &inter {
5217                // A single partition covers the whole macroblock with one
5218                // (ref, mv), which collapses the internal derivation to nnz.
5219                Some((_, parts)) if parts.len() == 1 => {
5220                    rusty_h264_common::deblock::MbKind::InterUniform
5221                }
5222                Some(_) => rusty_h264_common::deblock::MbKind::Inter,
5223                None => rusty_h264_common::deblock::MbKind::Intra,
5224            };
5225            match inter {
5226                Some((mode, parts)) => match coded {
5227                    // Encoded already, during the skip decision — splice the bits in
5228                    // rather than encoding this macroblock for a second time.
5229                    Some(sc) => w.append(&sc),
5230                    None => {
5231                        fe.encode_inter_mb(w, refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts)
5232                    }
5233                },
5234                None => encode_mb(&mut fe, w, mb_x, mb_y, &sy, &su, &sv, is_p),
5235            }
5236            mb_qpy[mb_idx] = fe.cur_qp; // ACTUAL QPy (updated iff an mb_qp_delta was coded)
5237            if precomp {
5238                bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, mb_kind);
5239            }
5240        }
5241    }
5242    debug_assert!(
5243        !precomp || bs_grid.iter().all(|b| *b != rusty_h264_common::deblock::MbBs::UNSET),
5244        "a macroblock loop exit failed to store its boundary strengths"
5245    );
5246    if is_p && skip_run > 0 {
5247        w.write_ue(skip_run); // trailing skipped macroblocks
5248    }
5249    w.rbsp_trailing_bits();
5250
5251    // Deblock the reconstruction; the result is the inter reference. Baseline: the
5252    // intra mask is `!inter_y` (passed directly, no alloc); no B (List-1 empty); no
5253    // 8×8 transform (t8x8 empty). ref_id is each block's List-0 ref index.
5254    drop(_g_loop);
5255    let _g_fin = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFinal);
5256    // No NO_REF-mapping collect: it ran over every 4x4 block every frame (~1.9 MB
5257    // of allocation + map at 1080p) to produce a grid that is only ever read for
5258    // INTER-vs-INTER comparisons, where the encoder's raw indices are already
5259    // equivalent. Intra blocks short-circuit before reference identity is touched.
5260    let info = rusty_h264_common::deblock::BlockInfo {
5261        inter: &fe.inter_y,
5262        nnz: &fe.nnz_y,
5263        mv: &fe.mv_y,
5264        ref_id: &fe.ref_idx_y,
5265        mv1: &[],
5266        ref_id1: &[],
5267        w4: fe.mb_w * 4,
5268        t8x8: &[],
5269        bs: &bs_grid,
5270        kind: &[],
5271    };
5272    // Per-MB actual QPy (AQ varies it; `mb_qp_delta`-driven). With `aq_strength 0`
5273    // this is uniform, reproducing the old scalar-QP filtering exactly.
5274    drop(_g_fin);
5275    rusty_h264_common::deblock::filter_frame(
5276        &mut fe.rec_y,
5277        &mut fe.rec_u,
5278        &mut fe.rec_v,
5279        fe.mb_w,
5280        fe.mb_h,
5281        &mb_qpy,
5282        0, // chroma_qp_index_offset — the encoder emits 0
5283        0, // slice_alpha_c0_offset — the encoder always signals zero offsets
5284        0, // slice_beta_offset
5285        &info,
5286    );
5287    let w4 = fe.mb_w * 4;
5288    crate::RefFrame {
5289        y: fe.rec_y,
5290        u: fe.rec_u,
5291        v: fe.rec_v,
5292        poc: 0,       // set by the caller (it knows the display order)
5293        frame_num: 0, // set by the caller
5294        // List-0 motion field, for a later B-frame's spatial-direct colZeroFlag.
5295        mv: fe.mv_y,
5296        ref_idx: fe.ref_idx_y,
5297        w4,
5298        // Filtered lazily on first sub-pel search use (see `RefFrame::hpel`).
5299        hpel: std::sync::OnceLock::new(),
5300    }
5301}
5302
5303/// Codes a B-slice's macroblock layer. B-frames are **non-reference**, so the
5304/// reconstruction is computed (the CAVLC nnz predictor needs it) but discarded.
5305///
5306/// This brick: every MB is coded `B_L0_16x16` (`mb_type == 1`) — a real
5307/// motion-compensated prediction from `l0` (the nearest PAST anchor, List-0 index
5308/// 0) plus a coded residual. Because every MB is List-0-only with `ref_idx`
5309/// inferred 0, the per-4×4 List-0 motion field and its median `mvd` predictor are
5310/// byte-identical to the P-slice `P_L0_16x16` path — so this reuses
5311/// [`FrameEncoder::encode_inter_mb_v1_b`] verbatim, differing from P only in the
5312/// `mb_type` value. `l1` (nearest future anchor) is unused until `B_Bi` lands.
5313#[allow(clippy::too_many_arguments)]
5314#[allow(clippy::too_many_arguments)]
5315pub fn encode_slice_data_b(
5316    w: &mut BitWriter,
5317    cfg: &EncoderConfig,
5318    frame: &YuvFrame,
5319    qp: u8,
5320    poc: i32,
5321    l0: &crate::RefFrame,
5322    l1: &crate::RefFrame,
5323    qpo: &[i32],
5324) {
5325    let mut fe = FrameEncoder::new(cfg);
5326    fe.qp = qp;
5327    fe.qpc = chroma_qp(qp);
5328    fe.cur_qp = qp;
5329    if cfg.cabac_dz_div > 0 {
5330        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
5331    } // QPY_PREV starts at the slice QP so the first mb_qp_delta is 0
5332    // Implicit bi-prediction weights from the anchor POC distances (matches the
5333    // decoder). Equidistant B (bframes==1) → 32:32 (plain average); unequal → weighted.
5334    fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
5335    let (sy, su, sv) = coded_source(cfg, frame);
5336    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
5337    let lme = lambda.sqrt();
5338    let refs = std::slice::from_ref(l0); // List-0 = [nearest past anchor]
5339    // Same content-adaptive SAD→SATD dispatch as the P path (codec-content-adaptive-
5340    // dispatch): the top `satd_q` fraction of highest-variance MBs price by SATD.
5341    if fe.satd_q > 0.0 {
5342        let mut vars: Vec<i64> = (0..fe.mb_h)
5343            .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
5344            .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
5345            .collect();
5346        vars.sort_unstable();
5347        let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
5348        fe.satd_var_thresh = vars[idx];
5349    }
5350    let mut skip_run = 0u32; // run of consecutive B_Skip MBs pending a coded MB
5351    for mb_y in 0..fe.mb_h {
5352        for mb_x in 0..fe.mb_w {
5353            let (lx, ly) = (mb_x * 16, mb_y * 16);
5354            let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
5355            fe.mb_use_satd =
5356                fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
5357            // Per-list median MV predictors — the search-rate center AND the actual
5358            // `mvd` predictor (identical to the decoder's `predict_partition_mv`).
5359            let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
5360            let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
5361            let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
5362            let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
5363            // Independent List-0 / List-1 motion searches (their J already includes
5364            // the mvd rate against the matching predictor, so J0/J1 compare directly).
5365            // Spatial-direct prediction (basis of B_Skip and B_Direct_16x16).
5366            let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
5367            // B_Skip: take the direct prediction with NO coded residual (~1 bit in
5368            // the mb_skip_run) only when it is truly FREE — its residual quantizes to
5369            // zero at the B QP, so skipping loses nothing. (A looser SATD-threshold
5370            // skip was measured strictly WORSE: on B's derived prediction the SATD
5371            // proxy over-values the skip, dropping residual the quantizer wanted —
5372            // the same proxy-vs-quantization gap seen on sub-pel. So skip only when
5373            // provably free; the rest goes through the L0/L1/Bi/Direct RD decision.)
5374            if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
5375                && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
5376            {
5377                fe.commit_direct_motion(mb_x, mb_y, &dmotion);
5378                skip_run += 1;
5379                continue;
5380            }
5381            let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
5382            let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
5383            let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
5384            // Bi: average the two winners' predictions; rate = both mvds.
5385            let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
5386            let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
5387                + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
5388            let j_bi = d_bi + (lme * r_bi as f64) as i64;
5389            // B_Direct (mb_type 0): spatial-direct prediction, NO coded MV — so its
5390            // J (d_direct, computed above) carries zero mvd rate and it wins wherever
5391            // the derived motion predicts as well as an explicit vector.
5392            // Pick the cheapest of {0=Direct, 1=L0, 2=L1, 3=Bi}; Direct wins ties.
5393            let (mut dir, mut best) = (0u8, d_direct);
5394            if j0 < best { dir = 1; best = j0; }
5395            if j1 < best { dir = 2; best = j1; }
5396            if j_bi < best { dir = 3; best = j_bi; }
5397            let _ = best; // CAVLC B has no partition search to price against it
5398            w.write_ue(skip_run); // run of B_Skips preceding this coded MB
5399            skip_run = 0;
5400            let bspec = BInter { dir, l1, mv0, mv1, mvmode: 0, parts2: [(0, (0, 0), (0, 0)); 2] };
5401            fe.encode_inter_mb_v1_b(w, refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
5402        }
5403    }
5404    if skip_run > 0 {
5405        w.write_ue(skip_run); // trailing B_Skip run
5406    }
5407    w.rbsp_trailing_bits();
5408}
5409
5410/// `se(d)` Exp-Golomb bit length — the `mvd`-component rate for the B mode
5411/// decision. Same closed form as `motion_search`'s private `mvbits` (kept separate
5412/// so the P search's heuristic — and thus P output — is untouched).
5413#[inline(always)]
5414fn mvd_bits(d: i32) -> u32 {
5415    let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
5416    1 + 2 * (31 - (codenum + 1).leading_zeros())
5417}
5418
5419/// Reads a 4×4 residual block (source minus a raster prediction block).
5420/// Writes `ref_idx_l0` (spec: `te(v)` when two references are active — a single
5421/// flag — else `ue(v)`). Only called when more than one reference is active.
5422fn write_ref_idx(w: &mut BitWriter, refi: i32, num_refs: usize) {
5423    if num_refs == 2 {
5424        w.write_bit(refi == 0); // te(v): value = !bit
5425    } else {
5426        w.write_ue(refi as u32);
5427    }
5428}
5429
5430/// Approximate bit cost of coding `ref_idx = r` with `num_refs` active, for the
5431/// motion-estimation rate term. Zero with a single reference (no `ref_idx` coded).
5432fn ref_bits(r: usize, num_refs: usize) -> u32 {
5433    if num_refs <= 1 {
5434        0
5435    } else if num_refs == 2 {
5436        1
5437    } else {
5438        let mut n = r as u32 + 1;
5439        let mut len = 1;
5440        while n > 1 {
5441            n >>= 1;
5442            len += 2;
5443        }
5444        len
5445    }
5446}
5447
5448fn residual(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[i32; 16]) -> [i32; 16] {
5449    let mut r = [0i32; 16];
5450    for dy in 0..4 {
5451        for dx in 0..4 {
5452            r[dy * 4 + dx] = src[(y0 + dy) * stride + (x0 + dx)] as i32 - pred[dy * 4 + dx];
5453        }
5454    }
5455    r
5456}
5457
5458/// Writes reconstructed samples back into a plane.
5459fn store(plane: &mut [u8], stride: usize, x0: usize, y0: usize, s: &[u8; 16]) {
5460    for dy in 0..4 {
5461        for dx in 0..4 {
5462            plane[(y0 + dy) * stride + (x0 + dx)] = s[dy * 4 + dx];
5463        }
5464    }
5465}
5466
5467/// Extracts the 4×4 raster prediction block at `(bx, by)` from a 16×16 (256-sample)
5468/// luma prediction.
5469fn pred_block(pred: &[u8; 256], bx: usize, by: usize) -> [i32; 16] {
5470    let mut p = [0i32; 16];
5471    for dy in 0..4 {
5472        for dx in 0..4 {
5473            p[dy * 4 + dx] = pred[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
5474        }
5475    }
5476    p
5477}
5478
5479/// Sum of absolute transformed differences over a 16×16 luma macroblock — the
5480/// mode-decision cost (correlates with coded bits better than plain SAD).
5481/// SATD of a `w`×`h` luma block: `src` (stride `ss`) vs `pred` (stride `ps`).
5482///
5483/// With `--features asm` and a supported size this is `2 · WelsSampleSatd_sse2`, which
5484/// is **byte-identical** to the scalar `Σ|H·d|` Hadamard: the openh264 kernel returns
5485/// `(Σ+1)>>1`, and `Σ` is always even (every 4×4 Hadamard coefficient shares the block
5486/// sum's parity, so 16 of them sum even), so `×2` recovers `Σ` exactly — proven over
5487/// 20 k random blocks at 4×4/8×8/16×16 in `tests/satd_asm_compare.rs`. Without asm (or
5488/// for an unsupported size) it falls back to the scalar Hadamard — the original path.
5489#[inline]
5490pub(crate) fn satd_px(src: &[u8], ss: usize, pred: &[u8], ps: usize, w: usize, h: usize) -> i64 {
5491    #[cfg(accel)]
5492    {
5493        let asm = match (w, h) {
5494            (16, 16) => Some(rusty_h264_accel::satd_16x16(src, ss, pred, ps)),
5495            (16, 8) => Some(rusty_h264_accel::satd_16x8(src, ss, pred, ps)),
5496            (8, 16) => Some(rusty_h264_accel::satd_8x16(src, ss, pred, ps)),
5497            (8, 8) => Some(rusty_h264_accel::satd_8x8(src, ss, pred, ps)),
5498            (4, 4) => Some(rusty_h264_accel::satd_4x4(src, ss, pred, ps)),
5499            _ => None,
5500        };
5501        if let Some(v) = asm {
5502            return 2 * v as i64;
5503        }
5504    }
5505    // Scalar Hadamard (also the no-asm path): Σ over the 4×4 sub-blocks.
5506    let (nbx, nby) = (w / 4, h / 4);
5507    let mut blocks = [[0i32; 16]; 16];
5508    let mut bi = 0;
5509    for by in 0..nby {
5510        for bx in 0..nbx {
5511            let blk = &mut blocks[bi];
5512            for dy in 0..4 {
5513                for dx in 0..4 {
5514                    blk[dy * 4 + dx] =
5515                        src[(by * 4 + dy) * ss + bx * 4 + dx] as i32 - pred[(by * 4 + dy) * ps + bx * 4 + dx] as i32;
5516                }
5517            }
5518            bi += 1;
5519        }
5520    }
5521    satd_4x4_sum(&blocks[..nbx * nby])
5522}
5523
5524/// SAD of a `w`×`h` block: `src` (stride `ss`) vs a strided region `r` (stride `rs`)
5525/// — the openh264 `psadbw` kernels for the shapes that ship them (they take strides,
5526/// so in-place plane reads need NO materialize), scalar `Σ abs_diff` rows otherwise
5527/// (LLVM lowers the idiom to `psadbw` for contiguous rows).
5528#[inline]
5529fn sad_strided(src: &[u8], ss: usize, r: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5530    #[cfg(accel)]
5531    {
5532        match (w, h) {
5533            (16, 16) => return rusty_h264_accel::sad_16x16(src, ss, r, rs) as i64,
5534            (16, 8) => return rusty_h264_accel::sad_16x8(src, ss, r, rs) as i64,
5535            (8, 16) => return rusty_h264_accel::sad_8x16(src, ss, r, rs) as i64,
5536            _ => {}
5537        }
5538    }
5539    let mut sad = 0u32;
5540    for dy in 0..h {
5541        let a = &src[dy * ss..][..w];
5542        let b = &r[dy * rs..][..w];
5543        sad += a.iter().zip(b).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
5544    }
5545    sad as i64
5546}
5547
5548/// Fused `SAD(src, (a+b+1)>>1)` — the quarter-pel SAD without materializing the
5549/// average (the B2 sibling of the A3 `satd_avg` kernel, scalar because the avg+SAD
5550/// idiom auto-vectorizes and quarter-phase SAD evals are seed-frequency only).
5551#[inline]
5552fn sad_avg_strided(src: &[u8], ss: usize, a: &[u8], b: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5553    let mut sad = 0u32;
5554    for dy in 0..h {
5555        let s = &src[dy * ss..][..w];
5556        let pa = &a[dy * rs..][..w];
5557        let pb = &b[dy * rs..][..w];
5558        for i in 0..w {
5559            let p = ((pa[i] as u16 + pb[i] as u16 + 1) >> 1) as u8;
5560            sad += s[i].abs_diff(p) as u32;
5561        }
5562    }
5563    sad as i64
5564}
5565
5566fn satd_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5567    satd_px(&src[ly * stride + lx..], stride, pred, 16, 16, 16)
5568}
5569
5570/// SAD over a 16×16 luma macroblock against a prediction — the fast preset's
5571/// intra cost, kept in the same (SAD) domain as its inter cost. `Σ a.abs_diff(b)`
5572/// over `u8` slices auto-vectorizes to `psadbw`.
5573fn sad_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5574    let mut sad = 0u32;
5575    for dy in 0..16 {
5576        let s = &src[(ly + dy) * stride + lx..][..16];
5577        let p = &pred[dy * 16..][..16];
5578        sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
5579    }
5580    sad as i64
5581}
5582
5583/// SATD over an 8×8 chroma block (four 4×4 sub-blocks) against a prediction.
5584fn satd_8x8(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[u8; 64]) -> i64 {
5585    satd_px(&src[y0 * stride + x0..], stride, pred, 8, 8, 8)
5586}
5587
5588/// SATD of one 4×4 luma block against a prediction.
5589fn satd_4x4(src: &[u8], stride: usize, px: usize, py: usize, pred: &[u8; 16]) -> i64 {
5590    satd_px(&src[py * stride + px..], stride, pred, 4, 4, 4)
5591}
5592
5593/// Whether an `Intra_4x4` mode is usable given top/left neighbor availability.
5594fn i4_mode_available(mode: u8, top: bool, left: bool) -> bool {
5595    match mode {
5596        0 | 3 | 7 => top,        // vertical, diag-down-left, vertical-left
5597        1 | 8 => left,           // horizontal, horizontal-up
5598        2 => true,               // DC
5599        _ => top && left,        // diag-down-right, vertical-right, horizontal-down
5600    }
5601}
5602
5603/// Result of planning an I_4x4 macroblock (luma). Reconstruction has already
5604/// been written into the frame's `rec_y` and `coded_y` by [`plan_i4x4`].
5605struct I4Plan {
5606    modes: [u8; 16],       // per-block intra4x4 mode, raster [lby*4+lbx]
5607    q: [[i32; 16]; 16],    // per-block quantized coefficients (full, raster)
5608    cbp_luma: u32,         // 4-bit coded-block-pattern (one bit per 8×8 region)
5609    nonzero: i64,          // total non-zero coefficients (rate proxy)
5610}
5611
5612/// A fully-decided intra macroblock: the mode decision, the quantized coefficients,
5613/// and the committed reconstruction. Produced by [`plan_mb`] (which reuses the
5614/// entire mode-decision + transform + reconstruct path), then consumed by an
5615/// entropy backend — `emit_mb_cavlc` or `emit_mb_cabac` — so the two coders share
5616/// every non-entropy decision bit-for-bit (the bringup-encoder reuse guarantee).
5617struct MbPlan {
5618    use_i4: bool,
5619    // I_16x16 (when !use_i4): prediction mode, whether any AC is coded (cbp_luma=15),
5620    // luma DC levels (block order), per-4×4 quantized AC (raster).
5621    i16_mode: I16Mode,
5622    i16_cbp15: bool,
5623    i16_dc_levels: [i32; 16],
5624    i16_q: [[i32; 16]; 16],
5625    // I_4x4 (when use_i4 && i8 is None): the sub-plan, already reconstructed.
5626    i4: Option<I4Plan>,
5627    // I_8x8 (High profile; when use_i4 && i8 is Some): the sub-plan, already
5628    // reconstructed. use_i4 means "I_NxN"; i8 present disambiguates 8x8 from 4x4.
5629    i8: Option<I8Plan>,
5630    // Chroma (shared by both luma types).
5631    chroma_mode: u8,
5632    cbp_chroma: u32,
5633    c_dc_levels: [[i32; 4]; 2],
5634    c_q_blocks: [[[i32; 16]; 4]; 2],
5635}
5636
5637/// A fully-decided inter macroblock: the per-partition motion residuals, coded
5638/// block pattern, and quantized residual, with the reconstruction + motion grids
5639/// already committed. Produced by [`FrameEncoder::plan_inter_mb`] (which reuses the
5640/// whole MC + residual + reconstruct path), then coded by `emit_inter_cavlc` or
5641/// `emit_inter_cabac` — so the two entropy backends share every non-entropy
5642/// decision bit-for-bit (the P/B analogue of [`MbPlan`]).
5643struct InterPlan {
5644    mvds: [(i32, i32); 4], // per-partition mvd (P: mvd_l0; B: mvd_l0 then mvd_l1)
5645    plan_refs: [i32; 4],   // per-partition ref_idx_l0 (multi-ref P; 0 for B / single-ref)
5646    n_mvd: usize,
5647    cbp: u32,
5648    q_blocks: [[i32; 16]; 16], // luma quantized levels (raster) — used when !t8x8
5649    c_dc_levels: [[i32; 4]; 2],
5650    c_q: [[[i32; 16]; 4]; 2],
5651    t8x8: bool,           // transform_size_8x8_flag (High profile, 8x8 luma residual)
5652    q8: [[i32; 64]; 4],   // per-8x8-block quantized levels (raster) — used when t8x8
5653}
5654
5655/// Gathers the 4×4 luma intra neighbors at pixel `(px, py)` from `rec_y`.
5656fn gather_i4(
5657    fe: &FrameEncoder,
5658    px: usize,
5659    py: usize,
5660    avail_top: bool,
5661    avail_left: bool,
5662    bx: usize,
5663    by: usize,
5664) -> ([u8; 8], [u8; 4], u8) {
5665    let (cw, w4) = (fe.cw, fe.mb_w * 4);
5666    let mut top = [0u8; 8];
5667    let mut left = [0u8; 4];
5668    let mut corner = 0;
5669    if avail_top {
5670        for i in 0..4 {
5671            top[i] = fe.rec_y[(py - 1) * cw + px + i];
5672        }
5673        let tr_avail = bx + 1 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 1)];
5674        for i in 0..4 {
5675            top[4 + i] = if tr_avail {
5676                fe.rec_y[(py - 1) * cw + px + 4 + i]
5677            } else {
5678                top[3]
5679            };
5680        }
5681    }
5682    if avail_left {
5683        for i in 0..4 {
5684            left[i] = fe.rec_y[(py + i) * cw + px - 1];
5685        }
5686    }
5687    if avail_top && avail_left {
5688        corner = fe.rec_y[(py - 1) * cw + px - 1];
5689    }
5690    (top, left, corner)
5691}
5692
5693/// Plans an I_4x4 macroblock: picks a mode per 4×4 block (lowest-SATD available
5694/// mode), quantizes, and reconstructs serially into `rec_y` so each block can
5695/// predict from the previous one.
5696/// Neighbour 4x4 block intra mode for the MPM candidate: in-MB blocks read the
5697/// in-progress local `modes`; blocks in earlier MBs read `fe.modes_y`. (bx, by)
5698/// are the current block's absolute 4x4 grid coords.
5699#[inline]
5700fn modes_at(fe: &FrameEncoder, modes: &[u8; 16], lbx: usize, lby: usize, dx: isize, dy: isize, bx: usize, by: usize) -> u8 {
5701    let (nx, ny) = (lbx as isize + dx, lby as isize + dy);
5702    if (0..4).contains(&nx) && (0..4).contains(&ny) {
5703        modes[ny as usize * 4 + nx as usize]
5704    } else {
5705        let w4 = fe.mb_w * 4;
5706        let gx = (bx as isize + dx) as usize;
5707        let gy = (by as isize + dy) as usize;
5708        fe.modes_y[gy * w4 + gx]
5709    }
5710}
5711
5712fn plan_i4x4(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I4Plan {
5713    let w4 = fe.mb_w * 4;
5714    let mut modes = [2u8; 16];
5715    let mut q = [[0i32; 16]; 16];
5716    let mut cbp_luma = 0u32;
5717    let mut nonzero = 0i64;
5718
5719    for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
5720        let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
5721        let (px, py) = (bx * 4, by * 4);
5722        let avail_top = by > 0;
5723        let avail_left = bx > 0;
5724        let (top, left, corner) = gather_i4(fe, px, py, avail_top, avail_left, bx, by);
5725
5726        // Pick the lowest-SATD available mode. RUSTY_FAST_INTRA prunes the
5727        // candidate set to {MPM, DC, V, H} (x264-ultrafast-style); the H.264
5728        // predicted mode (min of left/top block modes, DC on the edge) keeps the
5729        // 1-bit prev_intra4x4_pred_mode signalling cheap for the common winner.
5730        let mut best_m = 2u8;
5731        let mut best_cost = i64::MAX;
5732        if fe.fast && fast_intra_enabled() {
5733            let lm = if bx > 0 { modes_at(fe, &modes, lbx, lby, -1, 0, bx, by) } else { 2 };
5734            let tm = if by > 0 { modes_at(fe, &modes, lbx, lby, 0, -1, bx, by) } else { 2 };
5735            let mpm = lm.min(tm);
5736            let mut cands = [mpm, 2u8, 0, 1];
5737            for i in 1..4 {
5738                for j in 0..i {
5739                    if cands[i] == cands[j] {
5740                        cands[i] = 255;
5741                    }
5742                }
5743            }
5744            for &m in cands.iter() {
5745                if m == 255 || !i4_mode_available(m, avail_top, avail_left) {
5746                    continue;
5747                }
5748                let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5749                let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5750                if cost < best_cost {
5751                    best_cost = cost;
5752                    best_m = m;
5753                }
5754            }
5755        } else {
5756            for m in 0..9u8 {
5757                if !i4_mode_available(m, avail_top, avail_left) {
5758                    continue;
5759                }
5760                let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5761                let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5762                if cost < best_cost {
5763                    best_cost = cost;
5764                    best_m = m;
5765                }
5766            }
5767        }
5768
5769        // Quantize + reconstruct with the chosen mode.
5770        let pred = intra4x4_pred(best_m, avail_top, avail_left, &top, &left, corner);
5771        let mut predb = [0i32; 16];
5772        for i in 0..16 {
5773            predb[i] = pred[i] as i32;
5774        }
5775        let res = residual(sy, fe.cw, px, py, &predb);
5776        let qb = rdoq(&forward_core(&res), qp, fe.idz, fe.rdoq_strength, 0); // full 16 incl DC
5777        let s = reconstruct_4x4(&dequantize(&qb, qp), &predb);
5778        store(&mut fe.rec_y, fe.cw, px, py, &s);
5779        fe.coded_y[by * w4 + bx] = true;
5780
5781        let nz = qb.iter().filter(|&&v| v != 0).count();
5782        if nz > 0 {
5783            cbp_luma |= 1 << ((lby / 2) * 2 + (lbx / 2));
5784        }
5785        nonzero += nz as i64;
5786        modes[lby * 4 + lbx] = best_m;
5787        q[lby * 4 + lbx] = qb;
5788    }
5789    I4Plan {
5790        modes,
5791        q,
5792        cbp_luma,
5793        nonzero,
5794    }
5795}
5796
5797/// A planned I_8x8 macroblock (High profile): one intra8x8 mode + one 8x8 DCT per
5798/// 8x8 block. Reconstructed serially into `rec_y` (each block predicts from the
5799/// previous), and `modes_y` written per block so later blocks' MPM sees earlier.
5800struct I8Plan {
5801    modes: [u8; 4],     // per-8x8-block intra8x8 mode (raster b8 0..3)
5802    q: [[i32; 64]; 4],  // per-8x8-block quantized levels (raster)
5803    cbp_luma: u32,      // 4-bit coded-block-pattern (one bit per 8x8 block)
5804    nonzero: i64,       // rate proxy
5805}
5806
5807/// Forward zig-zag scan of a raster 8x8 block: `scan[i] = raster[ZIGZAG_8X8[i]]`
5808/// (the inverse of the decoder's `un_scan_8x8`).
5809const ZIGZAG_8X8: [usize; 64] = [
5810    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,
5811    13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
5812    52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
5813];
5814
5815#[inline]
5816fn scan_8x8_fwd(raster: &[i32; 64]) -> [i32; 64] {
5817    std::array::from_fn(|i| raster[ZIGZAG_8X8[i]])
5818}
5819
5820/// Gather the 8x8 intra reference samples (top[16] incl top-right, left[8], corner)
5821/// from `rec_y` — the encoder counterpart of the decoder's `gather_i8`.
5822fn gather_i8_enc(
5823    fe: &FrameEncoder,
5824    px: usize,
5825    py: usize,
5826    avail_top: bool,
5827    avail_left: bool,
5828    bx: usize,
5829    by: usize,
5830) -> ([u8; 16], [u8; 8], u8, bool) {
5831    let (cw, w4) = (fe.cw, fe.mb_w * 4);
5832    let mut top = [0u8; 16];
5833    let mut left = [0u8; 8];
5834    let mut corner = 0;
5835    if avail_top {
5836        for i in 0..8 {
5837            top[i] = fe.rec_y[(py - 1) * cw + px + i];
5838        }
5839        let tr_avail = bx + 2 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 2)];
5840        for i in 0..8 {
5841            top[8 + i] = if tr_avail {
5842                fe.rec_y[(py - 1) * cw + px + 8 + i]
5843            } else {
5844                top[7]
5845            };
5846        }
5847    }
5848    if avail_left {
5849        for i in 0..8 {
5850            left[i] = fe.rec_y[(py + i) * cw + px - 1];
5851        }
5852    }
5853    let avail_corner = avail_top && avail_left;
5854    if avail_corner {
5855        corner = fe.rec_y[(py - 1) * cw + px - 1];
5856    }
5857    (top, left, corner, avail_corner)
5858}
5859
5860/// Plans an I_8x8 macroblock: per 8x8 block, picks the lowest-SATD intra8x8 mode,
5861/// 8x8-forward-transforms + quantizes, and reconstructs serially into `rec_y`.
5862fn plan_i8x8(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I8Plan {
5863    let w4 = fe.mb_w * 4;
5864    let mut modes = [2u8; 4];
5865    let mut q = [[0i32; 64]; 4];
5866    let mut cbp_luma = 0u32;
5867    let mut nonzero = 0i64;
5868    let weight = [16i32; 64];
5869
5870    for b8 in 0..4usize {
5871        let (b8x, b8y) = (b8 % 2, b8 / 2);
5872        let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
5873        let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2); // top-left 4x4 cell
5874        let avail_top = b8y > 0 || mb_y > 0;
5875        let avail_left = b8x > 0 || mb_x > 0;
5876        let (top, left, corner, avail_corner) =
5877            gather_i8_enc(fe, px, py, avail_top, avail_left, bx, by);
5878
5879        // Mode decision: lowest-SATD available intra8x8 mode (same 9 modes / avail
5880        // rules as intra4x4). The MPM (predict_i4_mode on the top-left 4x4) keeps the
5881        // 1-bit prev-mode signalling cheap; a small penalty biases toward it.
5882        let predicted = predict_i4_mode(fe, bx, by);
5883        let mut best_m = 2u8;
5884        let mut best_cost = i64::MAX;
5885        for m in 0..9u8 {
5886            if !i4_mode_available(m, avail_top, avail_left) {
5887                continue;
5888            }
5889            let pred = intra8x8_pred(m, avail_top, avail_left, avail_corner, &top, &left, corner);
5890            let mut cost = satd_8x8(sy, fe.cw, px, py, &pred);
5891            if m != predicted {
5892                cost += 4 * fe.qp as i64; // ~mode-signal penalty (rem vs prev flag)
5893            }
5894            if cost < best_cost {
5895                best_cost = cost;
5896                best_m = m;
5897            }
5898        }
5899        modes[b8] = best_m;
5900
5901        // Forward 8x8 transform + quantize + reconstruct (shared decoder primitives).
5902        let pred = intra8x8_pred(best_m, avail_top, avail_left, avail_corner, &top, &left, corner);
5903        let mut res = [0i32; 64];
5904        for dy in 0..8 {
5905            for dx in 0..8 {
5906                res[dy * 8 + dx] =
5907                    sy[(py + dy) * fe.cw + (px + dx)] as i32 - pred[dy * 8 + dx] as i32;
5908            }
5909        }
5910        let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, fe.idz);
5911        let nz = levels.iter().filter(|&&v| v != 0).count();
5912        if nz > 0 {
5913            cbp_luma |= 1 << b8;
5914        }
5915        nonzero += nz as i64;
5916        q[b8] = levels;
5917
5918        let res_r = inverse_quant_8x8(&levels, qp, &weight);
5919        let predb: [i32; 64] = std::array::from_fn(|i| pred[i] as i32);
5920        let recon = add_residual_8x8(&res_r, &predb);
5921        for dy in 0..8 {
5922            for dx in 0..8 {
5923                fe.rec_y[(py + dy) * fe.cw + (px + dx)] = recon[dy * 8 + dx];
5924            }
5925        }
5926        // Publish the mode into all four 4x4 cells + mark coded — so the next 8x8
5927        // block's MPM (and later MBs' neighbours) see it, exactly as the decoder does.
5928        for sry in 0..2 {
5929            for srx in 0..2 {
5930                fe.modes_y[(by + sry) * w4 + (bx + srx)] = best_m;
5931                fe.coded_y[(by + sry) * w4 + (bx + srx)] = true;
5932            }
5933        }
5934    }
5935    I8Plan {
5936        modes,
5937        q,
5938        cbp_luma,
5939        nonzero,
5940    }
5941}
5942
5943/// Inter 8×8-transform luma candidate. Forward-8×8 + quantize + reconstruct each of
5944/// the four 8×8 blocks of the motion-compensated residual `(source − pred_y)`, the
5945/// pure inverse of the decoder's t8x8 inter luma path (`inv_quant8` ∘ `un_scan_8x8`
5946/// ∘ `add_residual_8x8`). Returns the quantized levels, `cbp_luma`, a LEVEL-AWARE rate
5947/// estimate (Σ `rdoq_rate(|level|)` — charges the 8×8's fewer-but-larger coeffs at
5948/// their true bit cost, not a blind count), the 256-sample reconstruction, and its
5949/// SSD vs source. Inter deadzone `dz_div = 6`; scaling list flat (16).
5950#[allow(clippy::too_many_arguments)]
5951fn plan_inter8_luma(
5952    sy: &[u8],
5953    cw: usize,
5954    mb_x: usize,
5955    mb_y: usize,
5956    pred_y: &[u8; 256],
5957    qp: u8,
5958) -> ([[i32; 64]; 4], u32, f64, [u8; 256], i64) {
5959    let weight = [16i32; 64];
5960    let mut q8 = [[0i32; 64]; 4];
5961    let mut cbp = 0u32;
5962    let mut rate = 0f64;
5963    let mut rec = [0u8; 256];
5964    let mut ssd = 0i64;
5965    for b8 in 0..4usize {
5966        let (b8x, b8y) = (b8 % 2, b8 / 2);
5967        let mut res = [0i32; 64];
5968        for dy in 0..8 {
5969            for dx in 0..8 {
5970                let sx = mb_x * 16 + b8x * 8 + dx;
5971                let syy = mb_y * 16 + b8y * 8 + dy;
5972                let p = pred_y[(b8y * 8 + dy) * 16 + (b8x * 8 + dx)] as i32;
5973                res[dy * 8 + dx] = sy[syy * cw + sx] as i32 - p;
5974            }
5975        }
5976        let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, 6);
5977        let mut nz = false;
5978        for &l in &levels {
5979            if l != 0 {
5980                nz = true;
5981                rate += rdoq_rate((l as i64).abs());
5982            }
5983        }
5984        if nz {
5985            cbp |= 1 << b8;
5986        }
5987        q8[b8] = levels;
5988
5989        let res_r = inverse_quant_8x8(&levels, qp, &weight);
5990        let predb: [i32; 64] =
5991            std::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
5992        let recon = add_residual_8x8(&res_r, &predb);
5993        for dy in 0..8 {
5994            for dx in 0..8 {
5995                let ri = (b8y * 8 + dy) * 16 + (b8x * 8 + dx);
5996                rec[ri] = recon[dy * 8 + dx];
5997                let sx = mb_x * 16 + b8x * 8 + dx;
5998                let syy = mb_y * 16 + b8y * 8 + dy;
5999                let d = recon[dy * 8 + dx] as i64 - sy[syy * cw + sx] as i64;
6000                ssd += d * d;
6001            }
6002        }
6003    }
6004    (q8, cbp, rate, rec, ssd)
6005}
6006
6007/// 16×16 luma intra prediction. For interior MBs (both neighbors available) this
6008/// dispatches to openh264's `WelsI16x16LumaPred*_sse2` (bit-identical to the spec
6009/// predictor); edge MBs (partial availability → C-only DC variants) use the scalar
6010/// path. The scalar `top`/`left`/`corner` are gathered by the caller regardless.
6011#[inline]
6012fn i16_pred(
6013    fe: &FrameEncoder,
6014    mode: I16Mode,
6015    avail_top: bool,
6016    avail_left: bool,
6017    top: &[u8; 16],
6018    left: &[u8; 16],
6019    corner: u8,
6020    lx: usize,
6021    ly: usize,
6022) -> [u8; 256] {
6023    #[cfg(accel)]
6024    if avail_top && avail_left {
6025        let mode_n = match mode {
6026            I16Mode::Vertical => 0,
6027            I16Mode::Horizontal => 1,
6028            I16Mode::Dc => 2,
6029            I16Mode::Plane => 3,
6030        };
6031        let mut p = AlignedMb([0; 256]);
6032        rusty_h264_accel::i16x16_luma_pred(mode_n, &mut p.0, &fe.rec_y[..], ly * fe.cw + lx, fe.cw);
6033        return p.0;
6034    }
6035    let _ = (fe, lx, ly);
6036    luma16x16_pred(mode, avail_top, avail_left, top, left, corner)
6037}
6038
6039/// 8×8 chroma intra prediction. Interior MBs use openh264's `WelsIChromaPred{V,Plane}_sse2`
6040/// for the V/Plane modes (bit-identical); DC/Horizontal (C-only in openh264) and edge MBs
6041/// use the scalar path.
6042#[inline]
6043#[allow(clippy::too_many_arguments)]
6044fn chroma_pred(
6045    fe: &FrameEncoder,
6046    mode: u8,
6047    avail_top: bool,
6048    avail_left: bool,
6049    c: usize,
6050    top: &[u8; 8],
6051    left: &[u8; 8],
6052    corner: u8,
6053    cx: usize,
6054    cy: usize,
6055) -> [u8; 64] {
6056    #[cfg(accel)]
6057    if avail_top && avail_left && (mode == 2 || mode == 3) {
6058        let plane = if c == 0 { &fe.rec_u } else { &fe.rec_v };
6059        let mut p = AlignedMb([0; 256]);
6060        rusty_h264_accel::chroma8x8_pred(mode, &mut p.0[..64], &plane[..], cy * fe.ccw + cx, fe.ccw);
6061        let mut out = [0u8; 64];
6062        out.copy_from_slice(&p.0[..64]);
6063        return out;
6064    }
6065    let _ = (fe, c, cx, cy);
6066    chroma8x8_pred(mode, avail_top, avail_left, top, left, corner)
6067}
6068
6069/// Predicted `Intra_4x4` mode for the block at absolute coords `(bx, by)` —
6070/// `min` of the left/top neighbor modes, or DC if either is unavailable.
6071fn predict_i4_mode(fe: &FrameEncoder, bx: usize, by: usize) -> u8 {
6072    if bx == 0 || by == 0 {
6073        return 2;
6074    }
6075    let w4 = fe.mb_w * 4;
6076    fe.modes_y[by * w4 + (bx - 1)].min(fe.modes_y[(by - 1) * w4 + bx])
6077}
6078
6079#[allow(clippy::too_many_arguments)]
6080/// Zig-zag scan: block (raster 4×4) index at scan position i.
6081const RDOQ_ZZ: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
6082
6083/// Approximate CABAC bit cost of coding one residual coefficient at magnitude
6084/// `level`: significant_coeff_flag (~1) + coeff_abs_level_minus1 bins (gt1 + UEG0)
6085/// + sign (~1); `level == 0` is significant_coeff_flag = 0 (~1). A coarse model —
6086/// the transform-norm/bin-to-bit scaling is absorbed into the calibrated strength.
6087#[inline]
6088fn rdoq_rate(level: i64) -> f64 {
6089    if level == 0 {
6090        1.0
6091    } else if level == 1 {
6092        3.0 // sig(1) + gt1=0 (1) + sign(1)
6093    } else {
6094        // sig(1) + gt1=1 (1) + UEG0(level-2) prefix (~level-1, capped) + sign(1)
6095        3.0 + (level - 1).min(13) as f64
6096    }
6097}
6098
6099/// Rate-distortion optimized quantization (CABAC trellis, RDOQ) for one 4×4 residual
6100/// block. Refines the hard-decision levels toward min over {|q|, |q|-1} of
6101/// `SSD_coef + λ·R_cabac` per coefficient (coefficient-domain distortion
6102/// `(|coeff| - level·deq_step)²`; `λ = strength·2^((qp-12)/3)`). `strength == 0`
6103/// returns the hard quantization unchanged (the CAVLC path). `first` = 1 skips the
6104/// DC (AC-only categories: I_16x16 AC, chroma AC), else 0.
6105fn rdoq(coeffs: &[i32; 16], qp: u8, dz_div: i64, strength: f64, first: usize) -> [i32; 16] {
6106    let mut q = quantize(coeffs, qp, dz_div);
6107    if strength <= 0.0 {
6108        return q;
6109    }
6110    let lambda = strength * 2f64.powf((qp as f64 - 12.0) / 3.0);
6111    // Distortion is measured in the QUANTIZER-INPUT (forward-transform) domain, where
6112    // level L reconstructs to L·qstep, qstep = 2^16 / MF (the inverse of the forward
6113    // quant scale). The transform norm (forward↔pixel) folds into `strength`.
6114    let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
6115    const POS: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7];
6116    let dist = |p: usize, level: i64| -> f64 {
6117        let e = coeffs[p].unsigned_abs() as f64 - level as f64 * (65536.0 / mf[POS[p]] as f64);
6118        e * e
6119    };
6120    // Pass 1: per-coefficient level lowering (|q| → |q|-1) minimizing D + λ·R.
6121    for i in first..16 {
6122        let p = RDOQ_ZZ[i];
6123        let m = q[p].unsigned_abs() as i64;
6124        if m == 0 {
6125            continue;
6126        }
6127        let j_keep = dist(p, m) + lambda * rdoq_rate(m);
6128        let j_down = dist(p, m - 1) + lambda * rdoq_rate(m - 1);
6129        if j_down < j_keep {
6130            let nl = (m - 1) as i32;
6131            q[p] = if q[p] < 0 { -nl } else { nl };
6132        }
6133    }
6134    // Pass 2: last-significant-position trimming. Zeroing the trailing significant
6135    // coefficient frees its own bits AND the last_significant flag + every sig=0 flag
6136    // between it and the previous significant coefficient (positions past the new last
6137    // aren't coded at all) — the dominant RDOQ gain on sparse (inter) residuals.
6138    loop {
6139        let Some(li) = (first..16).rev().find(|&i| q[RDOQ_ZZ[i]] != 0) else {
6140            break;
6141        };
6142        let p = RDOQ_ZZ[li];
6143        let m = q[p].unsigned_abs() as i64;
6144        let prev = (first..li).rev().find(|&i| q[RDOQ_ZZ[i]] != 0);
6145        let base = prev.map_or(first, |j| j + 1);
6146        let bits = rdoq_rate(m) + 1.0 + (li - base) as f64; // coeff + last-flag + freed sig=0
6147        let d_add = dist(p, 0) - dist(p, m);
6148        if d_add < lambda * bits {
6149            q[p] = 0;
6150        } else {
6151            break;
6152        }
6153    }
6154    q
6155}
6156
6157/// Decide one intra macroblock (I_16x16 vs I_4x4, prediction modes, chroma),
6158/// forward-transform + quantize, and commit the reconstruction + neighbour mode
6159/// state — everything except entropy coding. The returned [`MbPlan`] is coded by
6160/// either entropy backend, so CAVLC and CABAC share this whole path bit-for-bit.
6161fn plan_mb(
6162    fe: &mut FrameEncoder,
6163    mb_x: usize,
6164    mb_y: usize,
6165    sy: &[u8],
6166    su: &[u8],
6167    sv: &[u8],
6168) -> MbPlan {
6169    let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCode);
6170    let qp = fe.qp;
6171    let qpc = fe.qpc;
6172    // Lagrangian λ for rate-distortion decisions (standard H.264 form).
6173    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
6174
6175    // ---------------- luma ----------------
6176    let (lx, ly) = (mb_x * 16, mb_y * 16);
6177    let avail_top = mb_y > 0;
6178    let avail_left = mb_x > 0;
6179    let mut top = [0u8; 16];
6180    let mut left = [0u8; 16];
6181    if avail_top {
6182        for i in 0..16 {
6183            top[i] = fe.rec_y[(ly - 1) * fe.cw + lx + i];
6184        }
6185    }
6186    if avail_left {
6187        for i in 0..16 {
6188            left[i] = fe.rec_y[(ly + i) * fe.cw + lx - 1];
6189        }
6190    }
6191    let corner = if avail_top && avail_left {
6192        fe.rec_y[(ly - 1) * fe.cw + lx - 1]
6193    } else {
6194        0
6195    };
6196
6197    let w4 = fe.mb_w * 4;
6198
6199    // ============ I_16x16 plan (reconstruct into a local buffer) ============
6200    let mut i16_mode = I16Mode::Dc;
6201    let mut best_pred = i16_pred(fe, I16Mode::Dc, avail_top, avail_left, &top, &left, corner, lx, ly);
6202    let mut best_cost = satd_16x16(sy, fe.cw, lx, ly, &best_pred);
6203    for mode in [I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
6204        if !mode.available(avail_top, avail_left) {
6205            continue;
6206        }
6207        let pred = i16_pred(fe, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
6208        let cost = satd_16x16(sy, fe.cw, lx, ly, &pred);
6209        if cost < best_cost {
6210            best_cost = cost;
6211            i16_mode = mode;
6212            best_pred = pred;
6213        }
6214    }
6215    // I_16x16 blocks are independent (one fixed whole-MB prediction), so batch the
6216    // forward DCT (`forward_dct_blocks` → SIMD), bit-identical to `forward_core`.
6217    let mut dc4x4 = [0i32; 16];
6218    let mut i16_q = [[0i32; 16]; 16];
6219    // Fast path: forward DCT of (src - pred) straight from the planes per 8x8 quad,
6220    // quantize with the identical FF/MF math (deadzone = fe.idz), recon via the
6221    // bit-identical idct+add+clip kernel — the same pairing encode_inter_mb and the
6222    // P_Skip free-check already use, byte-identical to the scalar twin below.
6223    #[cfg(accel)]
6224    let (i16_dc_levels, _i16_recon_dc, recon16) = {
6225        #[repr(align(16))]
6226        struct A([i16; 256]);
6227        let mut dct = A([0i16; 256]);
6228        let base = ly * fe.cw + lx;
6229        for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
6230            rusty_h264_accel::dct_four_t4(
6231                &mut dct.0[qi * 64..qi * 64 + 64],
6232                &sy[base + qy * fe.cw + qx..],
6233                fe.cw,
6234                &best_pred[qy * 16 + qx..],
6235                16,
6236            );
6237        }
6238        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6239            dc4x4[lby * 4 + lbx] = dct.0[blk * 16] as i32;
6240        }
6241        if fe.rdoq_strength > 0.0 {
6242            // Trellis (all-intra only): scalar RDOQ from the asm DCT output instead of
6243            // the asm hard quantizer. dct.0 keeps the raw DCT here; the recon loop below
6244            // overwrites it with the dequantized RDOQ levels.
6245            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6246                let coeffs: [i32; 16] = std::array::from_fn(|i| dct.0[blk * 16 + i] as i32);
6247                let mut q = rdoq(&coeffs, qp, fe.idz, fe.rdoq_strength, 1);
6248                q[0] = 0;
6249                i16_q[lby * 4 + lbx] = q;
6250            }
6251        } else {
6252            let ff = rusty_h264_common::transform::quant_dz_ff(qp, fe.idz);
6253            let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
6254            for qi in 0..4 {
6255                rusty_h264_accel::quant_four_4x4(&mut dct.0[qi * 64..qi * 64 + 64], &ff, mf);
6256            }
6257            for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6258                let q = &mut i16_q[lby * 4 + lbx];
6259                q[0] = 0;
6260                for i in 1..16 {
6261                    q[i] = dct.0[blk * 16 + i] as i32;
6262                }
6263            }
6264        }
6265        let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
6266        let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
6267        // Recon: dequantize (DC injected from the Hadamard) back into quad layout,
6268        // then idct+add-pred+clip into the trial buffer.
6269        let mut recon16 = [0u8; 256];
6270        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6271            let mut deq = dequantize(&i16_q[lby * 4 + lbx], qp);
6272            deq[0] = i16_recon_dc[lby * 4 + lbx];
6273            for i in 0..16 {
6274                dct.0[blk * 16 + i] = deq[i] as i16;
6275            }
6276        }
6277        for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
6278            rusty_h264_accel::idct_four_t4_rec(
6279                &mut recon16[qy * 16 + qx..],
6280                16,
6281                &best_pred[qy * 16 + qx..],
6282                16,
6283                &dct.0[qi * 64..qi * 64 + 64],
6284            );
6285        }
6286        (i16_dc_levels, i16_recon_dc, recon16)
6287    };
6288    #[cfg(not(accel))]
6289    let (i16_dc_levels, _i16_recon_dc, recon16) = {
6290        let mut res_blocks = [[0i32; 16]; 16];
6291        for by in 0..4 {
6292            for bx in 0..4 {
6293                let predb = pred_block(&best_pred, bx, by);
6294                res_blocks[by * 4 + bx] = residual(sy, fe.cw, lx + bx * 4, ly + by * 4, &predb);
6295            }
6296        }
6297        let mut coeffs = [[0i32; 16]; 16];
6298        forward_dct_blocks(&res_blocks, &mut coeffs);
6299        for i in 0..16 {
6300            dc4x4[i] = coeffs[i][0];
6301            let mut q = rdoq(&coeffs[i], qp, fe.idz, fe.rdoq_strength, 1);
6302            q[0] = 0;
6303            i16_q[i] = q;
6304        }
6305        let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
6306        let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
6307        let mut recon16 = [0u8; 256];
6308        let mut deq_blocks = [[0i32; 16]; 16];
6309        for i in 0..16 {
6310            deq_blocks[i] = dequantize(&i16_q[i], qp);
6311            deq_blocks[i][0] = i16_recon_dc[i];
6312        }
6313        let mut idct = [[0i32; 16]; 16];
6314        inverse_dct_blocks(&deq_blocks, &mut idct);
6315        for by in 0..4 {
6316            for bx in 0..4 {
6317                let s = add_residual_4x4(&idct[by * 4 + bx], &pred_block(&best_pred, bx, by));
6318                for dy in 0..4 {
6319                    for dx in 0..4 {
6320                        recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)] = s[dy * 4 + dx];
6321                    }
6322                }
6323            }
6324        }
6325        (i16_dc_levels, i16_recon_dc, recon16)
6326    };
6327    let i16_cbp15 = i16_q.iter().any(|b| b[1..].iter().any(|&c| c != 0));
6328    let i16_dc_nz = i16_dc_levels.iter().filter(|&&v| v != 0).count() as i64;
6329    let i16_ac_nz: i64 = i16_q
6330        .iter()
6331        .map(|b| b[1..].iter().filter(|&&v| v != 0).count() as i64)
6332        .sum();
6333    // I_16x16 AC is all-or-nothing: any AC ⇒ all 16 blocks pay a coeff_token.
6334    let i16_rate = i16_dc_nz + i16_ac_nz + if i16_cbp15 { 16 } else { 0 };
6335    // Reconstruction distortion (SSD) for the rate-distortion decision.
6336    let mut ssd16 = 0i64;
6337    for dy in 0..16 {
6338        for dx in 0..16 {
6339            let d = recon16[dy * 16 + dx] as i64 - sy[(ly + dy) * fe.cw + (lx + dx)] as i64;
6340            ssd16 += d * d;
6341        }
6342    }
6343
6344    // ============ chroma (shared by both luma types; commit immediately) ============
6345    let (cx, cy) = (mb_x * 8, mb_y * 8);
6346    // Gather both components' neighbors, then pick a chroma mode by combined SATD.
6347    let mut ntop = [[0u8; 8]; 2];
6348    let mut nleft = [[0u8; 8]; 2];
6349    let mut ncorner = [0u8; 2];
6350    for c in 0..2 {
6351        let rec_c = if c == 0 { &fe.rec_u } else { &fe.rec_v };
6352        if avail_top {
6353            for i in 0..8 {
6354                ntop[c][i] = rec_c[(cy - 1) * fe.ccw + cx + i];
6355            }
6356        }
6357        if avail_left {
6358            for i in 0..8 {
6359                nleft[c][i] = rec_c[(cy + i) * fe.ccw + cx - 1];
6360            }
6361        }
6362        if avail_top && avail_left {
6363            ncorner[c] = rec_c[(cy - 1) * fe.ccw + cx - 1];
6364        }
6365    }
6366    let mut chroma_mode = 0u8;
6367    let mut best_c_cost = i64::MAX;
6368    for m in 0..4u8 {
6369        if !chroma_mode_available(m, avail_top, avail_left) {
6370            continue;
6371        }
6372        let mut cost = 0i64;
6373        for c in 0..2 {
6374            let src = if c == 0 { su } else { sv };
6375            let pred8 = chroma_pred(fe, m, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
6376            cost += satd_8x8(src, fe.ccw, cx, cy, &pred8);
6377        }
6378        if cost < best_c_cost {
6379            best_c_cost = cost;
6380            chroma_mode = m;
6381        }
6382    }
6383
6384    let mut c_dc_levels = [[0i32; 4]; 2];
6385    let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
6386    let mut any_chroma_ac = false;
6387    let mut any_chroma_dc = false;
6388    for c in 0..2 {
6389        let src = if c == 0 { su } else { sv };
6390        let pred8 =
6391            chroma_pred(fe, chroma_mode, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
6392        let pblk = |bx: usize, by: usize| -> [i32; 16] {
6393            let mut predb = [0i32; 16];
6394            for dy in 0..4 {
6395                for dx in 0..4 {
6396                    predb[dy * 4 + dx] = pred8[(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
6397                }
6398            }
6399            predb
6400        };
6401        // Fast path: forward DCT of (src - pred8) straight from the planes, quantize
6402        // with identical FF/MF (idz deadzone), recon via one idct+add+clip kernel —
6403        // bit-identical to the scalar twin below (proven kernel pairings).
6404        let mut dc2x2 = [0i32; 4];
6405        let mut qbs = [[0i32; 16]; 4];
6406        #[cfg(accel)]
6407        let recon_dc = {
6408            #[repr(align(16))]
6409            struct A([i16; 64]);
6410            let mut d = A([0i16; 64]);
6411            rusty_h264_accel::dct_four_t4(&mut d.0, &src[cy * fe.ccw + cx..], fe.ccw, &pred8, 8);
6412            for i in 0..4 {
6413                dc2x2[i] = d.0[i * 16] as i32;
6414            }
6415            if fe.rdoq_strength > 0.0 {
6416                // Trellis (all-intra only): scalar RDOQ from the asm chroma DCT.
6417                for i in 0..4 {
6418                    let coeffs: [i32; 16] = std::array::from_fn(|j| d.0[i * 16 + j] as i32);
6419                    let mut q = rdoq(&coeffs, qpc, fe.idz, fe.rdoq_strength, 1);
6420                    q[0] = 0;
6421                    if q[1..].iter().any(|&v| v != 0) {
6422                        any_chroma_ac = true;
6423                    }
6424                    qbs[i] = q;
6425                }
6426            } else {
6427                let ff = rusty_h264_common::transform::quant_dz_ff(qpc, fe.idz);
6428                let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
6429                rusty_h264_accel::quant_four_4x4(&mut d.0, &ff, mf);
6430                for i in 0..4 {
6431                    let q = &mut qbs[i];
6432                    q[0] = 0;
6433                    for j in 1..16 {
6434                        let v = d.0[i * 16 + j] as i32;
6435                        q[j] = v;
6436                        if v != 0 {
6437                            any_chroma_ac = true;
6438                        }
6439                    }
6440                }
6441            }
6442            let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6443            if dl.iter().any(|&v| v != 0) {
6444                any_chroma_dc = true;
6445            }
6446            let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6447            for i in 0..4 {
6448                let deq = dequantize(&qbs[i], qpc);
6449                for j in 0..16 {
6450                    d.0[i * 16 + j] = deq[j] as i16;
6451                }
6452                d.0[i * 16] = recon_dc[i] as i16;
6453            }
6454            let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6455            rusty_h264_accel::idct_four_t4_rec(&mut plane[cy * fe.ccw + cx..], fe.ccw, &pred8, 8, &d.0);
6456            c_dc_levels[c] = dl;
6457            recon_dc
6458        };
6459        #[cfg(not(accel))]
6460        let recon_dc = {
6461            let mut res_blocks = [[0i32; 16]; 4];
6462            for by in 0..2 {
6463                for bx in 0..2 {
6464                    res_blocks[by * 2 + bx] =
6465                        residual(src, fe.ccw, cx + bx * 4, cy + by * 4, &pblk(bx, by));
6466                }
6467            }
6468            let mut coeffs = [[0i32; 16]; 4];
6469            forward_dct_blocks(&res_blocks, &mut coeffs);
6470            for i in 0..4 {
6471                dc2x2[i] = coeffs[i][0];
6472                let mut q = rdoq(&coeffs[i], qpc, fe.idz, fe.rdoq_strength, 1);
6473                q[0] = 0;
6474                qbs[i] = q;
6475                if q[1..].iter().any(|&v| v != 0) {
6476                    any_chroma_ac = true;
6477                }
6478            }
6479            let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6480            if dl.iter().any(|&v| v != 0) {
6481                any_chroma_dc = true;
6482            }
6483            let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6484            let mut deq_blocks = [[0i32; 16]; 4];
6485            for i in 0..4 {
6486                deq_blocks[i] = dequantize(&qbs[i], qpc);
6487                deq_blocks[i][0] = recon_dc[i];
6488            }
6489            let mut idct = [[0i32; 16]; 4];
6490            inverse_dct_blocks(&deq_blocks, &mut idct);
6491            let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6492            for by in 0..2 {
6493                for bx in 0..2 {
6494                    let s = add_residual_4x4(&idct[by * 2 + bx], &pblk(bx, by));
6495                    store(plane, fe.ccw, cx + bx * 4, cy + by * 4, &s);
6496                }
6497            }
6498            c_dc_levels[c] = dl;
6499            recon_dc
6500        };
6501        let _ = recon_dc;
6502        c_q_blocks[c] = qbs;
6503    }
6504    let cbp_chroma: u32 = if any_chroma_ac {
6505        2
6506    } else if any_chroma_dc {
6507        1
6508    } else {
6509        0
6510    };
6511
6512    // ============ I_NxN plan + RD: I_16x16 vs I_4x4 vs (High profile) I_8x8 ============
6513    // I_4x4 and I_8x8 both reconstruct serially into rec_y, but each block predicts
6514    // only from NEIGHBOURS + earlier blocks it fills itself — never the stale MB
6515    // content — so running I_8x8 after I_4x4 needs no restore. J = SSD + λ·R picks the
6516    // per-MB transform (the content-adaptive win: 8x8 on smooth, 4x4 on detail).
6517    let base = ly * fe.cw + lx;
6518    let i4 = if i16_rate > 2 {
6519        Some(plan_i4x4(fe, sy, mb_x, mb_y, qp))
6520    } else {
6521        None
6522    };
6523    let (j4, i4_recon) = match &i4 {
6524        Some(p) => {
6525            let mut ssd = 0i64;
6526            let mut rec = [0u8; 256];
6527            for i in 0..256 {
6528                let v = fe.rec_y[base + (i / 16) * fe.cw + i % 16];
6529                rec[i] = v;
6530                let d = v as i64 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6531                ssd += d * d;
6532            }
6533            (ssd as f64 + lambda * (p.nonzero + 16) as f64, Some(rec))
6534        }
6535        None => (f64::INFINITY, None),
6536    };
6537    let i8 = if fe.transform_8x8 {
6538        Some(plan_i8x8(fe, sy, mb_x, mb_y, qp))
6539    } else {
6540        None
6541    };
6542    let j8 = match &i8 {
6543        Some(p) => {
6544            let mut ssd = 0i64;
6545            for i in 0..256 {
6546                let d = fe.rec_y[base + (i / 16) * fe.cw + i % 16] as i64
6547                    - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6548                ssd += d * d;
6549            }
6550            ssd as f64 + lambda * (p.nonzero + 16) as f64
6551        }
6552        None => f64::INFINITY,
6553    };
6554    let j16 = ssd16 as f64 + lambda * i16_rate as f64;
6555
6556    // ============ commit the RD winner's reconstruction + neighbour modes ============
6557    let (use_i4, i4, i8) = if i8.is_some() && j8 <= j4 && j8 <= j16 {
6558        // I_8x8: plan_i8x8 already committed rec_y AND modes_y (per 8x8 block).
6559        (true, None, i8)
6560    } else if i4.is_some() && j4 < j16 {
6561        // I_4x4: restore its reconstruction (I_8x8 may have overwritten rec_y), publish modes.
6562        let rec = i4_recon.unwrap();
6563        for i in 0..256 {
6564            fe.rec_y[base + (i / 16) * fe.cw + i % 16] = rec[i];
6565        }
6566        let modes = i4.as_ref().unwrap().modes;
6567        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6568            fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = modes[lby * 4 + lbx];
6569        }
6570        (true, i4, None)
6571    } else {
6572        // I_16x16: commit its reconstruction, mark modes DC.
6573        for by in 0..4 {
6574            for bx in 0..4 {
6575                for dy in 0..4 {
6576                    for dx in 0..4 {
6577                        fe.rec_y[(ly + by * 4 + dy) * fe.cw + (lx + bx * 4 + dx)] =
6578                            recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)];
6579                    }
6580                }
6581            }
6582        }
6583        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6584            fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
6585        }
6586        (false, None, None)
6587    };
6588    // Mark all luma blocks coded for the next macroblock's top-right availability.
6589    for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6590        fe.coded_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = true;
6591    }
6592
6593    MbPlan {
6594        use_i4,
6595        i16_mode,
6596        i16_cbp15,
6597        i16_dc_levels,
6598        i16_q,
6599        i4,
6600        i8,
6601        chroma_mode,
6602        cbp_chroma,
6603        c_dc_levels,
6604        c_q_blocks,
6605    }
6606}
6607
6608/// Emit one planned intra macroblock as CAVLC (the original `encode_mb` tail). Reads
6609/// only the decided values from `plan`; `plan_mb` already committed recon + modes.
6610fn encode_mb(
6611    fe: &mut FrameEncoder,
6612    w: &mut BitWriter,
6613    mb_x: usize,
6614    mb_y: usize,
6615    sy: &[u8],
6616    su: &[u8],
6617    sv: &[u8],
6618    is_p: bool,
6619) {
6620    let plan = plan_mb(fe, mb_x, mb_y, sy, su, sv);
6621    // In a P-slice, intra macroblock types are offset by 5 (0..4 are inter).
6622    let mb_type_offset = if is_p { 5 } else { 0 };
6623    let w4 = fe.mb_w * 4;
6624    let cbp_chroma = plan.cbp_chroma;
6625
6626    // ============ emit luma ============
6627    if let Some(i8) = plan.i8.as_ref().filter(|_| plan.use_i4) {
6628        // ---- I_8x8 (High profile): mb_type = I_NxN, transform_size_8x8_flag = 1, then
6629        // one intra8x8 mode per 8x8 block, cbp, mb_qp_delta, and the 8x8 residual as
6630        // four interleaved 4x4 CAVLC sub-blocks (coeff k of sub s -> 8x8 scan 4k+s). ----
6631        let cbp = i8.cbp_luma | (cbp_chroma << 4);
6632        w.write_ue(mb_type_offset); // mb_type = I_NxN
6633        w.write_bit(true); // transform_size_8x8_flag = 1
6634        for b8 in 0..4usize {
6635            let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
6636            let predicted = predict_i4_mode(fe, bx, by);
6637            let actual = i8.modes[b8];
6638            if actual == predicted {
6639                w.write_bit(true);
6640            } else {
6641                w.write_bit(false);
6642                let rem = if actual < predicted { actual } else { actual - 1 };
6643                w.write_bits(rem as u32, 3);
6644            }
6645        }
6646        w.write_ue(plan.chroma_mode as u32); // intra_chroma_pred_mode
6647        write_cbp_intra(w, cbp);
6648        if cbp != 0 {
6649            w.write_se(fe.qp_delta());
6650        }
6651        fe.nnz_cache_load(mb_x, mb_y);
6652        for b8 in 0..4usize {
6653            let (b8x, b8y) = (b8 % 2, b8 / 2);
6654            let scan8 = scan_8x8_fwd(&i8.q[b8]);
6655            for sub in 0..4usize {
6656                let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
6657                let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
6658                let total = if i8.cbp_luma & (1 << b8) != 0 {
6659                    let nc = fe.nc_pred(cx, cy);
6660                    let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
6661                    encode_residual_block(w, &blk, 16, nc) as u8
6662                } else {
6663                    0
6664                };
6665                fe.nnz_cache_set(cx, cy, total);
6666                fe.nnz_y[by * w4 + bx] = total;
6667            }
6668        }
6669    } else if plan.use_i4 {
6670        let i4 = plan.i4.as_ref().unwrap();
6671        let cbp = i4.cbp_luma | (cbp_chroma << 4);
6672        w.write_ue(mb_type_offset); // mb_type = I_4x4 (+5 in P-slices)
6673        if fe.transform_8x8 {
6674            w.write_bit(false); // transform_size_8x8_flag = 0 (this I_NxN is 4x4)
6675        }
6676        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6677            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6678            let predicted = predict_i4_mode(fe, bx, by);
6679            let actual = i4.modes[lby * 4 + lbx];
6680            if actual == predicted {
6681                w.write_bit(true);
6682            } else {
6683                w.write_bit(false);
6684                let rem = if actual < predicted { actual } else { actual - 1 };
6685                w.write_bits(rem as u32, 3);
6686            }
6687        }
6688        w.write_ue(plan.chroma_mode as u32); // intra_chroma_pred_mode
6689        write_cbp_intra(w, cbp);
6690        if cbp != 0 {
6691            w.write_se(fe.qp_delta()); // mb_qp_delta (AQ per-MB QPy)
6692        }
6693        fe.nnz_cache_load(mb_x, mb_y);
6694        for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6695            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6696            let total = if i4.cbp_luma & (1 << (blk / 4)) != 0 {
6697                let nc = fe.nc_pred(lbx, lby);
6698                let scan16 = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
6699                encode_residual_block(w, &scan16, 16, nc) as u8
6700            } else {
6701                0
6702            };
6703            fe.nnz_cache_set(lbx, lby, total);
6704            fe.nnz_y[by * w4 + bx] = total;
6705        }
6706    } else {
6707        let mb_type = 1 + plan.i16_mode as u32 + 4 * cbp_chroma + if plan.i16_cbp15 { 12 } else { 0 };
6708        w.write_ue(mb_type + mb_type_offset);
6709        w.write_ue(plan.chroma_mode as u32); // intra_chroma_pred_mode
6710        w.write_se(fe.qp_delta()); // mb_qp_delta (I_16x16 always codes it; AQ per-MB QPy)
6711        fe.nnz_cache_load(mb_x, mb_y);
6712        let nc_dc = fe.nc_pred(0, 0);
6713        let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
6714        encode_residual_block(w, &dc_scan, 16, nc_dc);
6715        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6716            fe.nnz_cache_set(lbx, lby, 0);
6717            fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
6718        }
6719        if plan.i16_cbp15 {
6720            for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6721                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6722                let nc = fe.nc_pred(lbx, lby);
6723                let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
6724                let total = encode_residual_block(w, &ac, 15, nc) as u8;
6725                fe.nnz_cache_set(lbx, lby, total);
6726                fe.nnz_y[by * w4 + bx] = total;
6727            }
6728        }
6729    }
6730
6731    // ============ emit chroma residual (shared) ============
6732    if cbp_chroma != 0 {
6733        for c in 0..2 {
6734            encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
6735        }
6736    }
6737    if cbp_chroma == 2 {
6738        fe.chroma_cache_load(mb_x, mb_y);
6739        let w2 = fe.mb_w * 2;
6740        for c in 0..2 {
6741            for &(bx, by) in &CHROMA_4X4_SCAN_XY {
6742                let nc = fe.chroma_nc_pred(c, bx, by);
6743                let ac = scan_4x4_ac(&plan.c_q_blocks[c][by * 2 + bx]);
6744                let total = encode_residual_block(w, &ac, 15, nc) as u8;
6745                fe.chroma_nnz_cache_set(c, bx, by, total);
6746                fe.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
6747            }
6748        }
6749    }
6750}
6751
6752// ============================================================================
6753// CABAC I-slice entropy coding — the exact forward inverse of the decoder's
6754// `decode_slice_data_cabac` I-slice path (rusty_h264-decoder mb16.rs). Every
6755// binarization + context-selection here mirrors a `parse_*_cabac` there; the
6756// neighbour state (nzc cache, cbf_dc, cat, cmode, mb_cbp, last_delta_qp) is
6757// reconstructed identically so the contexts evolve bit-for-bit. Reuses `plan_mb`
6758// for the entire mode-decision/transform/recon (shared with CAVLC).
6759// ============================================================================
6760
6761// --- res-property tables (must match the decoder's mb16.rs g_kBlockCat2CtxOffset*) ---
6762const CB_NZC_CACHE: [usize; 24] = [
6763    9, 10, 17, 18, 11, 12, 19, 20, 25, 26, 33, 34, 27, 28, 35, 36, // luma
6764    14, 15, 22, 23, // Cb
6765    38, 39, 46, 47, // Cr
6766];
6767const CB_RES_MAXPOS: [i32; 11] = [0, 15, 14, 15, 3, 14, 63, 3, 3, 14, 14];
6768const CB_RES_MAXC2: [i32; 11] = [0, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4];
6769const CB_RES_CBF: [usize; 11] = [0, 0, 4, 8, 12, 16, 0, 12, 12, 16, 16];
6770const CB_RES_MAP: [usize; 11] = [0, 0, 15, 29, 44, 47, 0, 44, 44, 47, 47];
6771const CB_RES_ONE: [usize; 11] = [0, 0, 10, 20, 30, 39, 0, 30, 30, 39, 39];
6772const CB_RP_I16_DC: usize = 1;
6773const CB_RP_I16_AC: usize = 2;
6774const CB_RP_LUMA_4X4: usize = 3;
6775const CB_RP_CHROMA_DC: usize = 7;
6776const CB_RP_CHROMA_AC: usize = 9;
6777
6778/// Inverse of `cabac_unary(ctx, off)`: bin0 at `ctx`; for value >= 1, `value-1` ones
6779/// then a terminating 0, all at `ctx+off`.
6780fn cb_unary(cab: &mut CabacEncoder, ctx: usize, off: usize, value: u32) {
6781    if value == 0 {
6782        cab.encode_decision(ctx, 0);
6783        return;
6784    }
6785    cab.encode_decision(ctx, 1);
6786    for _ in 0..value - 1 {
6787        cab.encode_decision(ctx + off, 1);
6788    }
6789    cab.encode_decision(ctx + off, 0);
6790}
6791
6792/// Exp-Golomb order-`k` in bypass — inverse of `cabac_exp_bypass(k)`.
6793fn cb_exp_bypass(cab: &mut CabacEncoder, mut k: i32, mut n: u32) {
6794    while n >= (1 << k) {
6795        cab.encode_bypass(1);
6796        n -= 1 << k;
6797        k += 1;
6798    }
6799    cab.encode_bypass(0);
6800    while k > 0 {
6801        k -= 1;
6802        cab.encode_bypass((n >> k) & 1);
6803    }
6804}
6805
6806/// UEG0 coeff-level suffix — inverse of `cabac_ueg_level(ctx)` (TU prefix <=13 at
6807/// `ctx`, then an EG0 bypass suffix).
6808fn cb_ueg_level(cab: &mut CabacEncoder, ctx: usize, value: u32) {
6809    if value == 0 {
6810        cab.encode_decision(ctx, 0);
6811        return;
6812    }
6813    let ones = value.min(13);
6814    for _ in 0..ones {
6815        cab.encode_decision(ctx, 1);
6816    }
6817    if value < 13 {
6818        cab.encode_decision(ctx, 0);
6819    } else {
6820        cb_exp_bypass(cab, 0, value - 13);
6821    }
6822}
6823
6824/// `mb_qp_delta` — inverse of `parse_mb_qp_delta_cabac` (ctxIdxOffset 60).
6825fn cb_mb_qp_delta(cab: &mut CabacEncoder, last_delta_qp: &mut i32, delta: i32) {
6826    const O: usize = 60;
6827    let ctx_inc = (*last_delta_qp != 0) as usize;
6828    if delta == 0 {
6829        cab.encode_decision(O + ctx_inc, 0);
6830    } else {
6831        cab.encode_decision(O + ctx_inc, 1);
6832        // code = 2|d| - (d>0); the decode's cabac_unary sees code-1.
6833        let code = 2 * delta.unsigned_abs() - (delta > 0) as u32;
6834        cb_unary(cab, O + 2, 1, code - 1);
6835    }
6836    *last_delta_qp = delta;
6837}
6838
6839/// `intra_chroma_pred_mode` (TU cMax=3) — inverse of `parse_intra_chroma_pred_mode_cabac`.
6840fn cb_chroma_pred_mode(cab: &mut CabacEncoder, ctx_inc: usize, mode: u8) {
6841    const C: usize = 64;
6842    if mode == 0 {
6843        cab.encode_decision(C + ctx_inc, 0);
6844        return;
6845    }
6846    cab.encode_decision(C + ctx_inc, 1);
6847    if mode == 1 {
6848        cab.encode_decision(C + 3, 0);
6849    } else if mode == 2 {
6850        cab.encode_decision(C + 3, 1);
6851        cab.encode_decision(C + 3, 0);
6852    } else {
6853        cab.encode_decision(C + 3, 1);
6854        cab.encode_decision(C + 3, 1);
6855    }
6856}
6857
6858/// I-slice `mb_type` — inverse of `parse_mb_type_i_cabac` (ctxIdxOffset 3).
6859fn cb_mb_type_i(
6860    cab: &mut CabacEncoder,
6861    ctx_inc: usize,
6862    use_i4: bool,
6863    i16_mode: u32,
6864    cbp_chroma: u32,
6865    cbp_luma15: bool,
6866) {
6867    const O: usize = 3;
6868    if use_i4 {
6869        cab.encode_decision(O + ctx_inc, 0); // I_NxN
6870        return;
6871    }
6872    cab.encode_decision(O + ctx_inc, 1);
6873    cab.encode_terminate(false); // not I_PCM
6874    cab.encode_decision(O + 3, cbp_luma15 as u32);
6875    if cbp_chroma != 0 {
6876        cab.encode_decision(O + 4, 1);
6877        cab.encode_decision(O + 5, (cbp_chroma == 2) as u32);
6878    } else {
6879        cab.encode_decision(O + 4, 0);
6880    }
6881    cab.encode_decision(O + 6, (i16_mode >> 1) & 1);
6882    cab.encode_decision(O + 7, i16_mode & 1);
6883}
6884
6885/// One `Intra_4x4` pred-mode — inverse of `parse_intra4x4_pred_mode_cabac` (ctx 68).
6886fn cb_intra4x4_pred_mode(cab: &mut CabacEncoder, predicted: u8, actual: u8) {
6887    const IPR: usize = 68;
6888    if actual == predicted {
6889        cab.encode_decision(IPR, 1);
6890    } else {
6891        cab.encode_decision(IPR, 0);
6892        let rem = if actual < predicted { actual } else { actual - 1 } as u32;
6893        cab.encode_decision(IPR + 1, rem & 1);
6894        cab.encode_decision(IPR + 1, (rem >> 1) & 1);
6895        cab.encode_decision(IPR + 1, (rem >> 2) & 1);
6896    }
6897}
6898
6899/// `coded_block_pattern` — inverse of `parse_cbp_cabac` (ctxIdxOffset 73).
6900fn cb_cbp(cab: &mut CabacEncoder, top: Option<u8>, left: Option<u8>, cbp: u32) {
6901    const CBP: usize = 73;
6902    let t = |m: u32| top.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6903    let l = |m: u32| left.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6904    let nb = |x: u32| (x == 0) as u32;
6905    let b0 = cbp & 1;
6906    let b1 = (cbp >> 1) & 1;
6907    let b2 = (cbp >> 2) & 1;
6908    let b3 = (cbp >> 3) & 1;
6909    cab.encode_decision(CBP + (l(1 << 1) + (t(1 << 2) << 1)) as usize, b0);
6910    cab.encode_decision(CBP + (nb(b0) + (t(1 << 3) << 1)) as usize, b1);
6911    cab.encode_decision(CBP + (l(1 << 3) + (nb(b0) << 1)) as usize, b2);
6912    cab.encode_decision(CBP + (nb(b2) + (nb(b1) << 1)) as usize, b3);
6913    let cbp_chroma = cbp >> 4;
6914    let ct = top.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6915    let cl = left.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6916    cab.encode_decision(CBP + 4 + (cl + (ct << 1)) as usize, (cbp_chroma != 0) as u32);
6917    if cbp_chroma != 0 {
6918        let ct2 = top.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6919        let cl2 = left.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6920        cab.encode_decision(CBP + 8 + (cl2 + (ct2 << 1)) as usize, (cbp_chroma == 2) as u32);
6921    }
6922}
6923
6924/// One residual block — inverse of `parse_residual_cabac`. `coeffs` is scan-order
6925/// (len >= maxPos+1). Returns totalCoeffNum (for the nzc cache + deblock nnz).
6926#[allow(clippy::too_many_arguments)]
6927fn cb_residual(
6928    cab: &mut CabacEncoder,
6929    nzc: &mut [u8; 48],
6930    cbf_dc: &mut u16,
6931    iz: usize,
6932    rp: usize,
6933    is_intra: bool,
6934    ndc: (Option<u16>, Option<u16>),
6935    coeffs: &[i32],
6936) -> u32 {
6937    let is_dc = rp == CB_RP_I16_DC || rp == CB_RP_CHROMA_DC || rp == CB_RP_CHROMA_DC + 1;
6938    let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
6939    let scan = CB_NZC_CACHE[iz.min(23)];
6940    if is_dc {
6941        if let Some(t) = ndc.0 {
6942            nb = ((t >> rp) & 1) as u8;
6943        }
6944        if let Some(l) = ndc.1 {
6945            na = ((l >> rp) & 1) as u8;
6946        }
6947    } else {
6948        if nzc[scan - 8] != 0xff {
6949            nb = (nzc[scan - 8] != 0) as u8;
6950        }
6951        if nzc[scan - 1] != 0xff {
6952            na = (nzc[scan - 1] != 0) as u8;
6953        }
6954    }
6955    let maxpos = CB_RES_MAXPOS[rp] as usize;
6956    let coeff_num = coeffs[..=maxpos].iter().filter(|&&c| c != 0).count() as u32;
6957    let cbf = coeff_num != 0;
6958    cab.encode_decision(85 + CB_RES_CBF[rp] + (na + (nb << 1)) as usize, cbf as u32);
6959    if !cbf {
6960        if !is_dc {
6961            nzc[scan] = 0;
6962        }
6963        return 0;
6964    }
6965    if is_dc {
6966        *cbf_dc |= 1 << rp;
6967    }
6968    // significance map
6969    let map = 105 + CB_RES_MAP[rp];
6970    let last = 166 + CB_RES_MAP[rp];
6971    let lastnz = (0..=maxpos).rev().find(|&i| coeffs[i] != 0).unwrap();
6972    for i in 0..maxpos {
6973        let s = coeffs[i] != 0;
6974        cab.encode_decision(map + i, s as u32);
6975        if s {
6976            let is_last = i == lastnz;
6977            cab.encode_decision(last + i, is_last as u32);
6978            if is_last {
6979                break;
6980            }
6981        }
6982    }
6983    // levels (reverse scan)
6984    let one = 227 + CB_RES_ONE[rp];
6985    let abs = 232 + CB_RES_ONE[rp];
6986    let maxc2 = CB_RES_MAXC2[rp];
6987    let (mut c1, mut c2) = (1i32, 0i32);
6988    for i in (0..=maxpos).rev() {
6989        if coeffs[i] != 0 {
6990            let av = coeffs[i].unsigned_abs();
6991            let gt1 = av > 1;
6992            cab.encode_decision(one + c1 as usize, gt1 as u32);
6993            if gt1 {
6994                cb_ueg_level(cab, abs + c2 as usize, av - 2);
6995                c2 = (c2 + 1).min(maxc2);
6996                c1 = 0;
6997            } else if c1 != 0 {
6998                c1 = (c1 + 1).min(4);
6999            }
7000            cab.encode_bypass((coeffs[i] < 0) as u32);
7001        }
7002    }
7003    if !is_dc {
7004        nzc[scan] = coeff_num as u8;
7005    }
7006    coeff_num
7007}
7008
7009/// Build the 48-entry padded nzc cache from the top/left neighbour MB exports
7010/// (openh264 `WelsFillCacheNonZeroCount`) — identical to the decoder.
7011fn cb_build_nzc(mb_nzc: &[[u8; 24]], top: Option<usize>, left: Option<usize>) -> [u8; 48] {
7012    let mut nzc = [0xffu8; 48];
7013    if let Some(t) = top {
7014        let tn = mb_nzc[t];
7015        nzc[1..5].copy_from_slice(&tn[12..16]);
7016        (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
7017        (nzc[6], nzc[7]) = (tn[20], tn[21]);
7018        (nzc[30], nzc[31]) = (tn[22], tn[23]);
7019    }
7020    if let Some(l) = left {
7021        let ln = mb_nzc[l];
7022        (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
7023        (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
7024    }
7025    nzc
7026}
7027
7028/// Extract the 24-entry per-MB nzc (raster luma + chroma) for future neighbours.
7029fn cb_export_nzc(nzc: &[u8; 48]) -> [u8; 24] {
7030    let mut mn = [0u8; 24];
7031    for k in 0..4 {
7032        mn[k] = nzc[9 + k];
7033        mn[4 + k] = nzc[17 + k];
7034        mn[8 + k] = nzc[25 + k];
7035        mn[12 + k] = nzc[33 + k];
7036    }
7037    (mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
7038    (mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
7039    for v in mn.iter_mut() {
7040        if *v == 0xff {
7041            *v = 0;
7042        }
7043    }
7044    mn
7045}
7046
7047/// Per-frame CABAC neighbour state (I-slice): one entry per macroblock, mirroring
7048/// the arrays the decoder's `decode_slice_data_cabac` maintains.
7049struct CabacState {
7050    cat: Vec<u8>,          // 2 = I_16x16, 0 = I_NxN, 100 = inter (mb_type / skip ctxInc)
7051    cmode: Vec<i32>,       // per-MB chroma mode (chroma-pred ctxInc)
7052    mb_cbp: Vec<u8>,       // per-MB cbp byte (cbp ctxInc)
7053    cbf_dc: Vec<u16>,      // per-MB DC coded_block_flag mask (residual DC ctxInc)
7054    mb_nzc: Vec<[u8; 24]>, // per-MB nzc export (residual AC ctxInc)
7055    // Inter (P/B) neighbour state — mirrors the decoder's WelsFillCacheInterCabac.
7056    mb_mvd: Vec<[[i16; 2]; 16]>,  // per-MB per-4x4 List-0 mvd (raster), for the mvd ctxInc cache
7057    mb_ref: Vec<[i8; 16]>,        // per-MB per-4x4 List-0 ref idx (raster); -1 = unavailable
7058    mb_mvd1: Vec<[[i16; 2]; 16]>, // B: per-MB per-4x4 List-1 mvd
7059    mb_ref1: Vec<[i8; 16]>,       // B: per-MB per-4x4 List-1 ref idx
7060    mb_skip: Vec<bool>,           // per-MB mb_skip_flag (skip ctxInc)
7061    mb_direct: Vec<bool>,         // B: per-MB B_Direct/B_Skip (B mb_type ctxInc)
7062    last_delta_qp: i32,
7063}
7064
7065impl CabacState {
7066    fn new(n: usize) -> Self {
7067        CabacState {
7068            cat: vec![0; n],
7069            cmode: vec![0; n],
7070            mb_cbp: vec![0; n],
7071            cbf_dc: vec![0; n],
7072            mb_nzc: vec![[0u8; 24]; n],
7073            mb_mvd: vec![[[0i16; 2]; 16]; n],
7074            mb_ref: vec![[-1i8; 16]; n],
7075            mb_mvd1: vec![[[0i16; 2]; 16]; n],
7076            mb_ref1: vec![[-1i8; 16]; n],
7077            mb_skip: vec![false; n],
7078            mb_direct: vec![false; n],
7079            last_delta_qp: 0,
7080        }
7081    }
7082}
7083
7084/// Emit one planned intra macroblock as CABAC (I-slice). Mirrors the decoder's
7085/// I-slice MB body exactly: `mb_type`, then per luma-type the intra modes / cbp /
7086/// `mb_qp_delta` / residual in spec order, maintaining `cs` and `fe.nnz_y`.
7087fn emit_mb_cabac_i(
7088    fe: &mut FrameEncoder,
7089    cab: &mut CabacEncoder,
7090    cs: &mut CabacState,
7091    plan: &MbPlan,
7092    mb_x: usize,
7093    mb_y: usize,
7094) {
7095    let mb_w = fe.mb_w;
7096    let addr = mb_y * mb_w + mb_x;
7097    let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7098    let left = if mb_x > 0 { Some(addr - 1) } else { None };
7099
7100    // ---- mb_type (I-slice prefix; carries I_16x16 pred-mode/cbp) ----
7101    let li = left.map_or(0, |a| (cs.cat[a] >= 2) as usize);
7102    let ti = top.map_or(0, |a| (cs.cat[a] >= 2) as usize);
7103    let acct = crate::bitacct::enabled();
7104    let t0 = if acct { cab.pos() } else { 0 };
7105    if plan.use_i4 {
7106        cb_mb_type_i(cab, li + ti, true, 0, 0, false);
7107    } else {
7108        cb_mb_type_i(cab, li + ti, false, plan.i16_mode as u32, plan.cbp_chroma, plan.i16_cbp15);
7109    }
7110    if acct {
7111        crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
7112    }
7113    let t1 = if acct { cab.pos() } else { 0 };
7114    emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7115    if acct {
7116        crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t1);
7117    }
7118}
7119
7120/// The intra macroblock body (chroma pred mode, intra modes, cbp, mb_qp_delta,
7121/// residual) shared by I-slice intra and P/B-slice intra — everything AFTER the
7122/// slice-specific `mb_type` prefix (which already carries the I_16x16 pred-mode/cbp).
7123#[allow(clippy::too_many_arguments)]
7124fn emit_intra_body_cabac(
7125    fe: &mut FrameEncoder,
7126    cab: &mut CabacEncoder,
7127    cs: &mut CabacState,
7128    plan: &MbPlan,
7129    mb_x: usize,
7130    mb_y: usize,
7131    addr: usize,
7132    top: Option<usize>,
7133    left: Option<usize>,
7134) {
7135    let w4 = fe.mb_w * 4;
7136    let cbp_chroma = plan.cbp_chroma;
7137    // chroma-pred-mode ctxInc from neighbour chroma modes (1..=3).
7138    let cci = left.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize)
7139        + top.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize);
7140
7141    let mut nzc;
7142    let mut cbfdc = 0u16;
7143    let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7144
7145    if !plan.use_i4 {
7146        // ---- I_16x16 ----
7147        cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
7148        cs.cmode[addr] = plan.chroma_mode as i32;
7149        cs.cat[addr] = 2;
7150        cs.mb_cbp[addr] = ((cbp_chroma as u8) << 4) | if plan.i16_cbp15 { 15 } else { 0 };
7151        nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7152
7153        let delta = fe.qp_delta();
7154        cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7155
7156        // luma DC
7157        let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
7158        cb_residual(cab, &mut nzc, &mut cbfdc, 0, CB_RP_I16_DC, true, ndc, &dc_scan);
7159        // luma AC
7160        for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
7161            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7162            let total = if plan.i16_cbp15 {
7163                let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
7164                cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_I16_AC, true, ndc, &ac)
7165            } else {
7166                nzc[CB_NZC_CACHE[iz]] = 0;
7167                0
7168            };
7169            fe.nnz_y[by * w4 + bx] = total as u8;
7170        }
7171        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);
7172    } else {
7173        // ---- I_NxN (I_4x4) ----
7174        let i4 = plan.i4.as_ref().unwrap();
7175        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7176            let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7177            let predicted = predict_i4_mode(fe, bx, by);
7178            cb_intra4x4_pred_mode(cab, predicted, i4.modes[lby * 4 + lbx]);
7179        }
7180        cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
7181        cs.cmode[addr] = plan.chroma_mode as i32;
7182        cs.cat[addr] = 0;
7183        let cbp = i4.cbp_luma | (cbp_chroma << 4);
7184        cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7185        cs.mb_cbp[addr] = cbp as u8;
7186        nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7187
7188        if cbp == 0 {
7189            cs.last_delta_qp = 0;
7190            for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7191                fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7192            }
7193        } else {
7194            let delta = fe.qp_delta();
7195            cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7196            for id8 in 0..4usize {
7197                for id4 in 0..4usize {
7198                    let iz = id8 * 4 + id4;
7199                    let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7200                    let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7201                    let total = if i4.cbp_luma & (1 << id8) != 0 {
7202                        let sc = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
7203                        cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, true, ndc, &sc)
7204                    } else {
7205                        nzc[CB_NZC_CACHE[iz]] = 0;
7206                        0
7207                    };
7208                    fe.nnz_y[by * w4 + bx] = total as u8;
7209                }
7210            }
7211            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);
7212        }
7213    }
7214
7215    cs.cbf_dc[addr] = cbfdc;
7216    cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7217}
7218
7219/// Chroma DC + AC residual (shared by intra I_16x16/I_NxN and inter) — matches the
7220/// decoder's chroma residual order. `is_intra` selects the coded_block_flag default
7221/// (nA=nB default to is_intra). Populates the chroma nnz grid for deblock.
7222#[allow(clippy::too_many_arguments)]
7223fn cb_emit_chroma_residual(
7224    cab: &mut CabacEncoder,
7225    fe: &mut FrameEncoder,
7226    nzc: &mut [u8; 48],
7227    cbfdc: &mut u16,
7228    ndc: (Option<u16>, Option<u16>),
7229    is_intra: bool,
7230    cbp_chroma: u32,
7231    c_dc_levels: &[[i32; 4]; 2],
7232    c_q: &[[[i32; 16]; 4]; 2],
7233    mb_x: usize,
7234    mb_y: usize,
7235) {
7236    let w2 = fe.mb_w * 2;
7237    if cbp_chroma >= 1 {
7238        for i in 0..2usize {
7239            cb_residual(cab, nzc, cbfdc, 16 + i * 4, CB_RP_CHROMA_DC + i, is_intra, ndc, &c_dc_levels[i]);
7240        }
7241    }
7242    if cbp_chroma == 2 {
7243        for i in 0..2usize {
7244            for (id4, &(bx, by)) in CHROMA_4X4_SCAN_XY.iter().enumerate() {
7245                let ac = scan_4x4_ac(&c_q[i][by * 2 + bx]);
7246                let total = cb_residual(
7247                    cab, nzc, cbfdc, 16 + i * 4 + id4, CB_RP_CHROMA_AC + i, is_intra, ndc, &ac,
7248                );
7249                fe.nnz_c[i][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total as u8;
7250            }
7251        }
7252    }
7253}
7254
7255/// CABAC all-intra slice-data coder (IDR / I-slice). Mirrors `encode_slice_data`'s
7256/// setup + deblock + `RefFrame` construction, but codes every MB via `plan_mb` +
7257/// `emit_mb_cabac_i` into a CABAC bitstream. `w` already holds the byte-aligned
7258/// slice header; the CABAC bytes are appended after `cabac_alignment_one_bit`.
7259pub fn encode_slice_data_cabac_intra(
7260    w: &mut BitWriter,
7261    cfg: &EncoderConfig,
7262    frame: &YuvFrame,
7263    qp: u8,
7264    qpo: &[i32],
7265) -> crate::RefFrame {
7266    let mut fe = FrameEncoder::new(cfg);
7267    fe.qp = qp;
7268    fe.qpc = chroma_qp(qp);
7269    fe.cur_qp = qp;
7270    if cfg.cabac_dz_div > 0 {
7271        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
7272    }
7273    let (sy, su, sv) = coded_source(cfg, frame);
7274    let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7275    apply_mbtree_qpo(&mut aq_qp, qpo); // mb-tree temporal AQ (empty = byte-identical)
7276    fe.cur_qp = qp;
7277    let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7278
7279    // CABAC trellis (RDOQ): structure-adaptive. ON only for ALL-INTRA streams
7280    // (gop_size<=1), where each IDR is independent so trading a little distortion for
7281    // rate is a clean −0.5..−1.3% BD-rate win. OFF inside a GOP: there the I-frame is
7282    // a REFERENCE, and degrading it costs the dependent P-frames more than the I-frame
7283    // saves (measured ~+0.1% net) — so the safe end is a true no-op (never regresses).
7284    fe.rdoq_strength = if cfg.gop_size <= 1 { cfg.cabac_rdoq } else { 0.0 };
7285    // Contexts init from SliceQPY (the slice qp), init_idc unused for I, is_i = true.
7286    let mut cab = CabacEncoder::new(qp as i32, 0, true);
7287    let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7288    let total = fe.mb_w * fe.mb_h;
7289
7290    for mb_y in 0..fe.mb_h {
7291        for mb_x in 0..fe.mb_w {
7292            let mb_idx = mb_y * fe.mb_w + mb_x;
7293            fe.qp = aq_qp[mb_idx];
7294            fe.qpc = chroma_qp(aq_qp[mb_idx]);
7295            let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
7296            emit_mb_cabac_i(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
7297            mb_qpy[mb_idx] = fe.cur_qp;
7298            // end_of_slice_flag (EncodeTerminate): 1 on the last MB, else 0.
7299            {
7300                let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7301                cab.encode_terminate(mb_idx + 1 == total);
7302                if crate::bitacct::enabled() {
7303                    crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
7304                }
7305            }
7306        }
7307    }
7308
7309    // Append CABAC slice data after cabac_alignment_one_bit (pad header with 1-bits).
7310    while !w.is_byte_aligned() {
7311        w.write_bit(true);
7312    }
7313    for b in cab.into_bytes() {
7314        w.write_bits(b as u32, 8);
7315    }
7316
7317    // Deblock the reconstruction (all-intra: BS derives from intra-ness) -> reference.
7318    let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
7319    let info = rusty_h264_common::deblock::BlockInfo {
7320        inter: &fe.inter_y,
7321        nnz: &fe.nnz_y,
7322        mv: &fe.mv_y,
7323        ref_id: &ref_id,
7324        mv1: &[],
7325        ref_id1: &[],
7326        w4: fe.mb_w * 4,
7327        t8x8: &[],
7328        bs: &[], kind: &[],
7329        };
7330    rusty_h264_common::deblock::filter_frame(
7331        &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
7332    );
7333    let w4 = fe.mb_w * 4;
7334    crate::RefFrame {
7335        y: fe.rec_y,
7336        u: fe.rec_u,
7337        v: fe.rec_v,
7338        poc: 0,
7339        frame_num: 0,
7340        mv: fe.mv_y,
7341        ref_idx: fe.ref_idx_y,
7342        w4,
7343        // Filtered lazily on first sub-pel search use (see `RefFrame::hpel`).
7344        hpel: std::sync::OnceLock::new(),
7345    }
7346}
7347
7348// ============================================================================
7349// CABAC P-slice entropy coding — the forward inverse of the decoder's
7350// decode_slice_data_cabac P-slice path. mb_skip_flag / mb_type_p / mvd (UEG3) /
7351// inter residual, plus intra-in-P (the shared intra body under a P mb_type prefix).
7352// Scope: 1 reference (no ref_idx), P_16x16/16x8/8x16 (no P_8x8/sub_mb_type) — the
7353// modes the encoder's decision produces.
7354// ============================================================================
7355
7356// z-order 4x4 block -> 30-entry (6-stride) mvd/ref cache index (openh264 g_kCache30ScanIdx).
7357const CB_CACHE30: [usize; 16] = [7, 8, 13, 14, 9, 10, 15, 16, 19, 20, 25, 26, 21, 22, 27, 28];
7358// z-order 4x4 block -> raster index (openh264 g_kuiScan4): the per-MB mvd/ref grid layout.
7359const CB_G_SCAN4: [usize; 16] = [0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15];
7360
7361/// UEG3 mvd suffix — inverse of `decode_ueg_mv(base)` (TU prefix at base+{0,1,2,3,3..},
7362/// cMax 7, then EG3 bypass). `v` is the value decode_ueg_mv returns.
7363fn cb_ueg_mv(cab: &mut CabacEncoder, base: usize, v: u32) {
7364    const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
7365    if v == 0 {
7366        cab.encode_decision(base, 0);
7367        return;
7368    }
7369    cab.encode_decision(base, 1);
7370    if v <= 7 {
7371        // (v-1) ones then a terminating 0, at base+P2C[count] for count = 1..
7372        let mut count = 1;
7373        for _ in 0..v - 1 {
7374            cab.encode_decision(base + P2C[count], 1);
7375            count += 1;
7376        }
7377        cab.encode_decision(base + P2C[count], 0);
7378    } else {
7379        // prefix maxes out: 7 ones (count 1..7) then EG3(v-8).
7380        let mut count = 1;
7381        for _ in 0..7 {
7382            cab.encode_decision(base + P2C[count], 1);
7383            count += 1;
7384        }
7385        let tb = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7386        cb_exp_bypass(cab, 3, v - 8);
7387        if crate::bitacct::enabled() {
7388            crate::bitacct::add(crate::bitacct::B::MvdBypass, cab.pos() - tb);
7389        }
7390    }
7391}
7392
7393/// One `mvd` component — inverse of `parse_mvd_cabac(comp, ctx_inc)` (ctxIdxOffset
7394/// 40 for x, 47 for y).
7395fn cb_mvd(cab: &mut CabacEncoder, comp: usize, ctx_inc: usize, d: i32) {
7396    let th = if crate::bitacct::enabled() { cab.pos() } else { u64::MAX };
7397    let base = 40 + comp * 7;
7398    if d == 0 {
7399        cab.encode_decision(base + ctx_inc, 0);
7400        if th != u64::MAX {
7401            crate::bitacct::add_mvd_sample(0, cab.pos() - th);
7402        }
7403        return;
7404    }
7405    cab.encode_decision(base + ctx_inc, 1);
7406    cb_ueg_mv(cab, base + 3, d.unsigned_abs() - 1); // decode adds 1 back
7407    let ts = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7408    cab.encode_bypass((d < 0) as u32);
7409    if crate::bitacct::enabled() {
7410        crate::bitacct::add(crate::bitacct::B::MvdSign, cab.pos() - ts);
7411    }
7412    if th != u64::MAX {
7413        crate::bitacct::add_mvd_sample(d.unsigned_abs(), cab.pos() - th);
7414    }
7415}
7416
7417/// `mb_skip_flag` — inverse of `parse_mb_skip_cabac` (ctx 11 P + neighbour-not-skip).
7418fn cb_mb_skip(cab: &mut CabacEncoder, ctx_inc: usize, skip: bool) {
7419    cab.encode_decision(ctx_inc, skip as u32);
7420}
7421
7422/// `ref_idx_l0` (P) — inverse of `parse_ref_idx_cabac`. Unary binarization,
7423/// ctxIdxOffset 54: binIdx 0 → `ctx0` (condTermFlagA + 2·condTermFlagB, condTermFlagN =
7424/// neighbour partition's ref_idx > 0), binIdx 1 → 4, binIdx ≥2 → 5 (spec 9.3.3.1.1.6).
7425fn cb_ref_idx(cab: &mut CabacEncoder, ctx0: usize, r: u32) {
7426    const B: usize = 54;
7427    let mut v = r;
7428    let mut bin_idx = 0u32;
7429    loop {
7430        let bin = (v > 0) as u32;
7431        let ctx = match bin_idx {
7432            0 => ctx0,
7433            1 => 4,
7434            _ => 5,
7435        };
7436        cab.encode_decision(B + ctx, bin);
7437        if bin == 0 {
7438            break;
7439        }
7440        v -= 1;
7441        bin_idx += 1;
7442    }
7443}
7444
7445/// P-slice inter `mb_type` (0/1/2 = P_L0_16x16 / P_16x8 / P_8x16) — inverse of the
7446/// inter branch of `parse_mb_type_p_cabac` (ctx base 11).
7447fn cb_mb_type_p_inter(cab: &mut CabacEncoder, mode: u8) {
7448    const S: usize = 11;
7449    cab.encode_decision(S + 3, 0); // inter (prefix bit 0)
7450    match mode {
7451        0 => {
7452            cab.encode_decision(S + 4, 0);
7453            cab.encode_decision(S + 5, 0);
7454        }
7455        3 => {
7456            // P_8x8 (bins "0 0 1")
7457            cab.encode_decision(S + 4, 0);
7458            cab.encode_decision(S + 5, 1);
7459        }
7460        1 => {
7461            cab.encode_decision(S + 4, 1);
7462            cab.encode_decision(S + 6, 1);
7463        }
7464        _ => {
7465            // mode == 2 (P_8x16)
7466            cab.encode_decision(S + 4, 1);
7467            cab.encode_decision(S + 6, 0);
7468        }
7469    }
7470}
7471
7472/// P `sub_mb_type` CABAC — inverse of `parse_sub_mb_type_p_cabac` (ctx base 21).
7473/// Only 0 = P_L0_8x8 (bin "1") is emitted (8×8 sub-partitions only).
7474fn cb_sub_mb_type_p(cab: &mut CabacEncoder, sub_type: u8) {
7475    const S: usize = 21;
7476    match sub_type {
7477        0 => cab.encode_decision(S, 1),
7478        _ => unreachable!("only 8x8 sub_mb_type (0) emitted"),
7479    }
7480}
7481
7482/// P-slice intra `mb_type` prefix — inverse of the intra branch of
7483/// `parse_mb_type_p_cabac` (ctx base 11). Carries the I_16x16 pred-mode/cbp exactly
7484/// like the I-slice mb_type, so the shared intra body re-emits neither.
7485fn cb_mb_type_p_intra(cab: &mut CabacEncoder, plan: &MbPlan) {
7486    const S: usize = 11;
7487    cab.encode_decision(S + 3, 1); // intra (prefix bit 1)
7488    if plan.use_i4 {
7489        cab.encode_decision(S + 6, 0); // I_4x4
7490        return;
7491    }
7492    cab.encode_decision(S + 6, 1); // I_16x16
7493    cab.encode_terminate(false); // not I_PCM
7494    cab.encode_decision(S + 7, plan.i16_cbp15 as u32);
7495    if plan.cbp_chroma != 0 {
7496        cab.encode_decision(S + 8, 1);
7497        cab.encode_decision(S + 8, (plan.cbp_chroma == 2) as u32);
7498    } else {
7499        cab.encode_decision(S + 8, 0);
7500    }
7501    cab.encode_decision(S + 9, (plan.i16_mode as u32 >> 1) & 1);
7502    cab.encode_decision(S + 9, plan.i16_mode as u32 & 1);
7503}
7504
7505/// P-slice partition layout: `(part_idx, z-blocks)` per motion partition (matches
7506/// the decoder's `part!` invocations). part_idx = the partition's top-left z-block
7507/// (its `CACHE30` slot for the mvd ctxInc); z-blocks = every 4x4 it covers.
7508fn p_partition_layout(mode: u8) -> &'static [(usize, &'static [usize])] {
7509    match mode {
7510        1 => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7]), (8, &[8, 9, 10, 11, 12, 13, 14, 15])],
7511        2 => &[(0, &[0, 1, 2, 3, 8, 9, 10, 11]), (4, &[4, 5, 6, 7, 12, 13, 14, 15])],
7512        // P_8x8: four 8×8 quads (z-order 4×4 blocks), part order == inter_partitions(3).
7513        3 => &[(0, &[0, 1, 2, 3]), (4, &[4, 5, 6, 7]), (8, &[8, 9, 10, 11]), (12, &[12, 13, 14, 15])],
7514        _ => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])],
7515    }
7516}
7517
7518/// Emit one motion partition's `mvd` (x,y) and splat it into the 30-entry cache +
7519/// per-MB raster mvd/ref grids — inverse of the decoder's `parse_mvd_partition`.
7520#[allow(clippy::too_many_arguments)]
7521fn cb_emit_mvd_partition(
7522    cab: &mut CabacEncoder,
7523    part_idx: usize,
7524    zblocks: &[usize],
7525    mvdc: &mut [[i16; 2]; 30],
7526    refc: &mut [i8; 30],
7527    mmvd: &mut [[i16; 2]; 16],
7528    mref: &mut [i8; 16],
7529    mvd: (i32, i32),
7530    ref_idx: i8, // the partition's ref_idx_l0 (0 for single-ref) — stored for neighbour context
7531) {
7532    let s = CB_CACHE30[part_idx];
7533    let ctx = |comp: usize| -> usize {
7534        let mut a = 0i32;
7535        if refc[s - 6] >= 0 {
7536            a += mvdc[s - 6][comp].unsigned_abs() as i32;
7537        }
7538        if refc[s - 1] >= 0 {
7539            a += mvdc[s - 1][comp].unsigned_abs() as i32;
7540        }
7541        if a >= 3 {
7542            1 + (a > 32) as usize
7543        } else {
7544            0
7545        }
7546    };
7547    cb_mvd(cab, 0, ctx(0), mvd.0);
7548    cb_mvd(cab, 1, ctx(1), mvd.1);
7549    let (mx, my) = (mvd.0 as i16, mvd.1 as i16);
7550    for &zb in zblocks {
7551        mvdc[CB_CACHE30[zb]] = [mx, my];
7552        refc[CB_CACHE30[zb]] = ref_idx;
7553        mmvd[CB_G_SCAN4[zb]] = [mx, my];
7554        mref[CB_G_SCAN4[zb]] = ref_idx;
7555    }
7556}
7557
7558/// Emit one planned INTER macroblock as CABAC (P-slice, mb_skip_flag already coded
7559/// as 0). `mode`/`parts` + `plan` from `plan_inter_mb`. 1-ref: no ref_idx.
7560fn emit_mb_cabac_p_inter(
7561    fe: &mut FrameEncoder,
7562    cab: &mut CabacEncoder,
7563    cs: &mut CabacState,
7564    mode: u8,
7565    plan: &InterPlan,
7566    mb_x: usize,
7567    mb_y: usize,
7568    num_refs: usize,
7569) {
7570    let mb_w = fe.mb_w;
7571    let addr = mb_y * mb_w + mb_x;
7572    let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7573    let left = if mb_x > 0 { Some(addr - 1) } else { None };
7574
7575    // Bit accountant (instrument #6): each tap is a `pos()` delta — exact coded
7576    // bits for that element — behind an atomic-bool check when disabled.
7577    let acct = crate::bitacct::enabled();
7578    let mut t0 = if acct { cab.pos() } else { 0 };
7579    cb_mb_type_p_inter(cab, mode);
7580    // P_8x8: four sub_mb_type (all 0 = 8×8), spec order before ref_idx/mvd.
7581    if mode == 3 {
7582        for _ in 0..4 {
7583            cb_sub_mb_type_p(cab, 0);
7584        }
7585    }
7586    if acct {
7587        crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
7588        t0 = cab.pos();
7589    }
7590
7591    // ---- mb_pred (spec 7.3.5.1): all ref_idx_l0 FIRST, then all mvd_l0 ----
7592    let mut mvdc = [[0i16; 2]; 30];
7593    let mut refc = [-1i8; 30];
7594    cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc, &mut mvdc, top, left, addr, mb_w);
7595    let mut mmvd = [[0i16; 2]; 16];
7596    let mut mref = [0i8; 16];
7597    let layout = p_partition_layout(mode);
7598    // Phase 1: ref_idx_l0 per partition, only when the slice has >1 active reference.
7599    // Update refc after each so a later partition's ref context sees the earlier one.
7600    if num_refs > 1 {
7601        for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7602            let r = plan.plan_refs[part];
7603            let s = CB_CACHE30[part_idx];
7604            let ctx0 = (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
7605            cb_ref_idx(cab, ctx0, r as u32);
7606            for &zb in zblocks {
7607                refc[CB_CACHE30[zb]] = r as i8;
7608            }
7609        }
7610    }
7611    if acct {
7612        crate::bitacct::add(crate::bitacct::B::RefIdx, cab.pos() - t0);
7613        t0 = cab.pos();
7614    }
7615    // Phase 2: mvd per partition (carries the ref into refc/mref for neighbour context).
7616    for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7617        cb_emit_mvd_partition(
7618            cab, part_idx, zblocks, &mut mvdc, &mut refc, &mut mmvd, &mut mref, plan.mvds[part],
7619            plan.plan_refs[part] as i8,
7620        );
7621    }
7622    if acct {
7623        crate::bitacct::add(crate::bitacct::B::Mvd, cab.pos() - t0);
7624    }
7625    cs.mb_mvd[addr] = mmvd;
7626    cs.mb_ref[addr] = mref;
7627    cs.cat[addr] = 100;
7628    cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7629}
7630
7631/// Inter cbp + residual (is_intra = false) — shared by P and B inter MBs. Maintains
7632/// cs.mb_cbp/cbf_dc/mb_nzc/last_delta_qp + fe.nnz_y.
7633#[allow(clippy::too_many_arguments)]
7634fn cb_emit_inter_residual(
7635    fe: &mut FrameEncoder,
7636    cab: &mut CabacEncoder,
7637    cs: &mut CabacState,
7638    plan: &InterPlan,
7639    mb_x: usize,
7640    mb_y: usize,
7641    addr: usize,
7642    top: Option<usize>,
7643    left: Option<usize>,
7644) {
7645    let w4 = fe.mb_w * 4;
7646    let cbp = plan.cbp;
7647    let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
7648    let acct = crate::bitacct::enabled();
7649    let mut t0 = if acct { cab.pos() } else { 0 };
7650    cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7651    if acct {
7652        crate::bitacct::add(crate::bitacct::B::Cbp, cab.pos() - t0);
7653    }
7654    cs.mb_cbp[addr] = cbp as u8;
7655    let mut nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7656    let mut cbfdc = 0u16;
7657    let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7658
7659    if cbp == 0 {
7660        cs.last_delta_qp = 0;
7661        for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7662            fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7663        }
7664    } else {
7665        let delta = fe.qp_delta();
7666        if acct { t0 = cab.pos(); }
7667        cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7668        if acct {
7669            crate::bitacct::add(crate::bitacct::B::QpDelta, cab.pos() - t0);
7670            t0 = cab.pos();
7671        }
7672        for id8 in 0..4usize {
7673            for id4 in 0..4usize {
7674                let iz = id8 * 4 + id4;
7675                let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7676                let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7677                let total = if cbp_luma & (1 << id8) != 0 {
7678                    let sc = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
7679                    cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, false, ndc, &sc)
7680                } else {
7681                    nzc[CB_NZC_CACHE[iz]] = 0;
7682                    0
7683                };
7684                fe.nnz_y[by * w4 + bx] = total as u8;
7685            }
7686        }
7687        if acct {
7688            crate::bitacct::add(crate::bitacct::B::ResidLuma, cab.pos() - t0);
7689            t0 = cab.pos();
7690        }
7691        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);
7692        if acct {
7693            crate::bitacct::add(crate::bitacct::B::ResidChroma, cab.pos() - t0);
7694        }
7695    }
7696    cs.cbf_dc[addr] = cbfdc;
7697    cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7698}
7699
7700/// Emit one planned INTRA macroblock inside a P-slice: the P mb_type prefix (which
7701/// carries the I_16x16 pred-mode/cbp) then the shared intra body.
7702fn emit_mb_cabac_p_intra(
7703    fe: &mut FrameEncoder,
7704    cab: &mut CabacEncoder,
7705    cs: &mut CabacState,
7706    plan: &MbPlan,
7707    mb_x: usize,
7708    mb_y: usize,
7709) {
7710    let mb_w = fe.mb_w;
7711    let addr = mb_y * mb_w + mb_x;
7712    let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7713    let left = if mb_x > 0 { Some(addr - 1) } else { None };
7714    let acct = crate::bitacct::enabled();
7715    let t0 = if acct { cab.pos() } else { 0 };
7716    cb_mb_type_p_intra(cab, plan);
7717    emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7718    if acct {
7719        // Whole intra MB (mb_type + modes + its residual) — intra MBs are ~5% of
7720        // P-frame MBs; splitting them further is a separate tap set.
7721        crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t0);
7722    }
7723}
7724
7725/// Emit a P_Skip macroblock's `mb_skip_flag = 1` and update neighbour state. The
7726/// motion grid was committed by `commit_skip`; the mvd/ref cache is LEFT at its
7727/// init (-1 ref) — matching the decoder, which does not touch mb_mvd/mb_ref for a
7728/// P_Skip (so a skip neighbour contributes nothing to a later mvd ctxInc).
7729fn emit_p_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
7730    let sctx = 11
7731        + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7732        + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7733    let t0 = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7734    cb_mb_skip(cab, sctx, true);
7735    if crate::bitacct::enabled() {
7736        crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
7737    }
7738    cs.mb_skip[addr] = true;
7739    cs.cat[addr] = 100;
7740    cs.last_delta_qp = 0;
7741}
7742
7743/// CABAC P-slice data coder. Mirrors `encode_slice_data`'s decision (P_Skip check +
7744/// fast/quality inter-vs-intra RD) exactly — only the emit differs (per-MB
7745/// mb_skip_flag + CABAC syntax + per-MB end_of_slice terminate).
7746/// Median source macroblock variance for a frame — the TEXTURE dispatch signal for
7747/// the ME lambda scale.
7748///
7749/// Raising the ME rate term biases the search toward cheaper motion vectors, which
7750/// costs texture detail. SSIM is texture-sensitive where PSNR is not, so on maximum-
7751/// texture content a higher lambda improves BD-PSNR while REGRESSING BD-SSIM.
7752/// Measured median MB variance vs BD-SSIM at lme 1.8:
7753///   akiyo 61 (-0.20), foreman 219 (-0.61), city 300 (-0.89), bus 454 (-0.01),
7754///   football 583 (-1.03), **mobile 1554 (+0.45 LOSS)**
7755/// The one loser carries 2.7x the texture of the next clip, so the split is wide.
7756///
7757/// Computed from the SOURCE, so it is available in-slice for BOTH P and B and needs
7758/// no cross-frame state — unlike the B-only direct-win rate, which cannot gate a knob
7759/// that both encoders read and whose carry-forward would be nondeterministic under
7760/// frame-parallel encode. Subsampled 2x2 (16x fewer loads) — a median over ~400
7761/// macroblocks does not need every pixel.
7762fn frame_median_mb_var(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize) -> i64 {
7763    let mut vs: Vec<i64> = Vec::with_capacity(mb_w * mb_h);
7764    for my in 0..mb_h {
7765        for mx in 0..mb_w {
7766            let (mut sum, mut sq) = (0i64, 0i64);
7767            for r in (0..16).step_by(2) {
7768                let row = (my * 16 + r) * cw + mx * 16;
7769                for c in (0..16).step_by(2) {
7770                    let v = sy[row + c] as i64;
7771                    sum += v;
7772                    sq += v * v;
7773                }
7774            }
7775            let n = 64i64;
7776            vs.push((sq - sum * sum / n) / n);
7777        }
7778    }
7779    vs.sort_unstable();
7780    vs.get(vs.len() / 2).copied().unwrap_or(0)
7781}
7782
7783/// Texture-dispatched ME lambda scale: the calibrated high value on normal content,
7784/// the conservative shipped value on maximum-texture content where it costs SSIM.
7785fn me_lambda_scale(
7786    cfg: &EncoderConfig,
7787    sy: &[u8],
7788    cw: usize,
7789    mb_w: usize,
7790    mb_h: usize,
7791    ref_y: Option<&[u8]>,
7792) -> f64 {
7793    let hi = match cfg.tune_lme_hi {
7794        Some(v) if v > 0.0 => v,
7795        _ => return cfg.cabac_lambda_scale,
7796    };
7797    // TWO terms, and each is justified by a DIFFERENT clip it must classify —
7798    // neither alone is sufficient, which is why the texture-only version shipped
7799    // disabled.
7800    //
7801    //   clip      global-MC resid   median var   wants hi lme?
7802    //   akiyo          1.51             61          yes
7803    //   foreman        9.59            219          yes
7804    //   city          12.44            300          yes
7805    //   football      24.83            583          yes
7806    //   TEMPETE       10.52            746          NO  <- caught by TEXTURE (650)
7807    //   MOBILE        19.50           1554          NO  <- caught by TEXTURE
7808    //   BUS           27.47            454          NO  <- caught by MOTION (20)
7809    //
7810    // mobile is maximum texture: a higher ME rate term biases toward cheaper MVs,
7811    // costing texture detail, and SSIM is texture-sensitive where PSNR is not.
7812    // bus is fast GLOBAL motion (a pan): its cost surface is dominated by one
7813    // global vector, so pushing the rate term drags MVs off it. football is
7814    // chaotic LOCAL motion at similar texture and WANTS the high value, so texture
7815    // cannot separate the two — the global-MC residual can, and in the opposite
7816    // direction, which is exactly why the pair works where either alone fails.
7817    if frame_median_mb_var(sy, cw, mb_w, mb_h) >= cfg.tune_lme_tex_thresh.unwrap_or(650) {
7818        return cfg.cabac_lambda_scale;
7819    }
7820    if let Some(r) = ref_y {
7821        let mot = cfg.tune_lme_motion_thresh.unwrap_or(26.0);
7822        if global_mc_residual(sy, cw, mb_h * 16, r) >= mot {
7823            return cfg.cabac_lambda_scale;
7824        }
7825    }
7826    hi
7827}
7828
7829pub fn encode_slice_data_cabac_p(
7830    w: &mut BitWriter,
7831    cfg: &EncoderConfig,
7832    frame: &YuvFrame,
7833    qp: u8,
7834    refs: &[crate::RefFrame],
7835    qpo: &[i32],
7836) -> crate::RefFrame {
7837    let mut fe = FrameEncoder::new(cfg);
7838    fe.qp = qp;
7839    fe.qpc = chroma_qp(qp);
7840    fe.cur_qp = qp;
7841    if cfg.cabac_dz_div > 0 {
7842        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
7843    }
7844    let (sy, su, sv) = coded_source(cfg, frame);
7845    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
7846    // Hoisted to SLICE level: the texture median is O(pixels) and the site below
7847    // sits inside the macroblock loop, where recomputing it would be quadratic.
7848    let lme_scale = me_lambda_scale(cfg, &sy, fe.cw, fe.mb_w, fe.mb_h, refs.first().map(|r| &r.y[..]));
7849    let num_refs = refs.len();
7850    // me_wide content gate (pure-pan → global-MC residual ≈ 0 → off; see encode_slice_data).
7851    if fe.me_wide && !refs.is_empty() {
7852        let coh = global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7853        if std::env::var("RFF_ME_COH_DBG").is_ok() {
7854            eprintln!("ME_COH qp{qp} residual={coh:.2}");
7855        }
7856        if coh < fe.me_wide_coh {
7857            fe.me_wide = false;
7858        }
7859    }
7860    // me_wide HEAD-ROOM GATE (the dispatcher the truth table asked for). The rescue
7861    // only pays where a wide search actually beats a predictor-local one; measure
7862    // that directly per frame and route the frame. `RFF_ME_HR` sets the threshold
7863    // (percent); 0 disables the gate and restores the always-on behaviour.
7864    // Skip the probe entirely when the gate is disabled: it must not tax the
7865    // default path (`RFF_ME_HR=0`), which stays byte-identical to pre-gate output.
7866    if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
7867        let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7868        if me_wide_hr_dbg() {
7869            eprintln!("ME_HR qp{qp} headroom={hr:.2}");
7870        }
7871        if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
7872            fe.me_wide = false;
7873        }
7874    }
7875    // Track-B B2 DISPATCH — same probe/route as the CAVLC driver above (the two
7876    // drivers must stay in lockstep; the U5-struct bug came from patching one).
7877    if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
7878        let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7879        if me_sadt_dbg() {
7880            eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
7881        }
7882        fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
7883        // H-24: the mv-cost SHAPE rides the same probe (its BD sign-flip tracks
7884        // motion for the same physical reason B2's does).
7885        if mv_smooth_mode() == 1 {
7886            // dcfrac veto mirrors B2's: crew-class FLASH frames satisfy the mgain
7887            // test but SAD/mvd statistics mislead there (H-13/H-26).
7888            fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
7889        }
7890        // H-13: near-static frames skip the split searches entirely.
7891        let smg = split_mg();
7892        if smg > 0.0 {
7893            fe.do_splits = mg >= smg;
7894        }
7895    }
7896    if fe.satd_q > 0.0 {
7897        let mut vars: Vec<i64> = (0..fe.mb_h)
7898            .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
7899            .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
7900            .collect();
7901        vars.sort_unstable();
7902        let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
7903        fe.satd_var_thresh = vars[idx];
7904    }
7905    let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7906    apply_mbtree_qpo(&mut aq_qp, qpo); // mb-tree temporal AQ (empty = byte-identical)
7907    fe.cur_qp = qp;
7908    let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7909
7910    // Same online free-skip dispatch as the CAVLC path gates the greedy P_Skip on
7911    // (see `encode_slice_data`): measured over the frame so far, within-frame so it
7912    // stays deterministic under GOP-parallel encode.
7913    let mut greedy_free = 0usize;
7914    let mut greedy_seen = 0usize;
7915    let mut greedy_on = fe.greedy_min_free == 0;
7916    let greedy_learn = (fe.mb_w * fe.mb_h / 8).max(64);
7917    let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false); // P-slice
7918    let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7919    let total = fe.mb_w * fe.mb_h;
7920
7921    // ② residue naming: the CABAC driver's MB loop was untapped (the CAVLC twin
7922    // has this scope) — `EncMbLoop − Σ(per-MB stages)` is the per-MB glue.
7923    let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
7924    for mb_y in 0..fe.mb_h {
7925        for mb_x in 0..fe.mb_w {
7926            let mb_idx = mb_y * fe.mb_w + mb_x;
7927            let addr = mb_idx;
7928            let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
7929            let left = if mb_x > 0 { Some(addr - 1) } else { None };
7930            fe.qp = aq_qp[mb_idx];
7931            fe.qpc = chroma_qp(aq_qp[mb_idx]);
7932
7933            // ---- P_Skip check (identical logic to encode_slice_data) ----
7934            let mut inter: Option<InterChoice> = None;
7935            let mut did_skip = false;
7936            if num_refs > 0 {
7937                let mv_skip = fe.skip_mv(mb_x, mb_y);
7938                let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
7939                let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
7940                let skip_c = if luma_free || !fe.fast {
7941                    fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
7942                } else {
7943                    [[0u8; 64]; 2]
7944                };
7945                let is_free = luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
7946                let skip_sad = if fe.fast {
7947                    0
7948                } else {
7949                    let (lx, ly) = (mb_x * 16, mb_y * 16);
7950                    let mut s = 0u32;
7951                    for dy in 0..16 {
7952                        let src = &sy[(ly + dy) * fe.cw + lx..][..16];
7953                        let p = &skip_y[dy * 16..][..16];
7954                        s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
7955                    }
7956                    s
7957                };
7958                greedy_seen += 1;
7959                if greedy_seen >= greedy_learn {
7960                    greedy_on = fe.greedy_min_free == 0
7961                        || greedy_free * 100 >= greedy_seen * fe.greedy_min_free as usize;
7962                }
7963                if is_free {
7964                    fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7965                    if !fe.fast {
7966                        fe.mb_was_skip[mb_idx] = true;
7967                        fe.mb_skip_sad[mb_idx] = skip_sad;
7968                    }
7969                    greedy_free += 1;
7970                    did_skip = true;
7971                } else {
7972                    let (lx, ly) = (mb_x * 16, mb_y * 16);
7973                    let nb = {
7974                        let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
7975                        fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
7976                    };
7977                    let lme = lambda.sqrt() * lme_scale;
7978                    if fe.fast {
7979                        fe.mb_use_satd = fe.satd_q > 0.0
7980                            && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
7981                        let (r16, mv16, cost_inter) =
7982                            fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7983                        let cost_intra = if fe.mb_use_satd {
7984                            fe.best_i16_satd(&sy, mb_x, mb_y)
7985                        } else {
7986                            fe.best_i16_sad(&sy, mb_x, mb_y)
7987                        } + (lme * fe.tune_intra_penalty) as i64;
7988                        inter = if cost_intra < cost_inter {
7989                            None
7990                        } else {
7991                            Some((0, vec![(r16, mv16)]))
7992                        };
7993                    } else {
7994                        // Quality preset: greedy P_Skip, then 16x16 baseline + sub-partitions + intra.
7995                        if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
7996                            fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7997                            fe.mb_was_skip[mb_idx] = true;
7998                            fe.mb_skip_sad[mb_idx] = skip_sad;
7999                            did_skip = true;
8000                        } else {
8001                            let (r16, mv16, c16) =
8002                                fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
8003                            let mut best_c = c16;
8004                            let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
8005                            const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
8006                            let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
8007                            let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
8008                            let split_t = split_t();
8009                        if fe.do_splits && c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
8010                                let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
8011                                let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
8012                                let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
8013                                let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
8014                                if ct + cb < best_c {
8015                                    best_c = ct + cb;
8016                                    pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
8017                                }
8018                                if cl + cr < best_c {
8019                                    best_c = cl + cr;
8020                                    pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
8021                                }
8022                                // P_8x8: four 8×8 sub-partitions (see the CAVLC path).
8023                                if fe.sub8x8 {
8024                                    let mut c8 = (lme * 4.0) as i64;
8025                                    let mut p8 = Vec::with_capacity(4);
8026                                    for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
8027                                        let (r, mv, c) = fe.best_part(
8028                                            refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
8029                                        );
8030                                        c8 += c;
8031                                        p8.push((r, mv));
8032                                    }
8033                                    if c8 < best_c {
8034                                        best_c = c8;
8035                                        pick = Some((3u8, p8));
8036                                    }
8037                                }
8038                            }
8039                            // U5-struct: refine ONLY the winning shape (see the twin
8040                            // block in the CAVLC driver). This site is the CABAC path —
8041                            // which is now the DEFAULT, so omitting it here left sub-pel
8042                            // deferred but never refined on every default encode.
8043                            if fe.sp_defer.get() {
8044                                if let Some((mode, parts)) = pick.as_mut() {
8045                                    let regions: &[(usize, usize, usize, usize)] = match mode {
8046                                        1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
8047                                        2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
8048                                        3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
8049                                        _ => &[(0, 0, 16, 16)],
8050                                    };
8051                                    let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
8052                                    for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
8053                                        let (r, mv) = parts[i];
8054                                        let (m2, c2) = fe.refine_part(
8055                                            refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
8056                                        );
8057                                        parts[i] = (r, m2);
8058                                        tot += c2;
8059                                    }
8060                                    best_c = tot;
8061                                }
8062                            }
8063                            let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
8064                                + (lme * fe.tune_intra_penalty) as i64;
8065                            inter = if c_intra < best_c { None } else { pick };
8066                            fe.mb_was_skip[mb_idx] = false;
8067                            fe.mb_skip_sad[mb_idx] = skip_sad;
8068                        }
8069                    }
8070                }
8071            }
8072
8073            // ---- emit ----
8074            if did_skip {
8075                emit_p_skip_cabac(&mut cab, &mut cs, addr, top, left);
8076                mb_qpy[mb_idx] = fe.cur_qp;
8077                {
8078                    let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8079                    {
8080                let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8081                cab.encode_terminate(mb_idx + 1 == total);
8082                if crate::bitacct::enabled() {
8083                    crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8084                }
8085            }
8086                    if crate::bitacct::enabled() {
8087                        crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8088                    }
8089                }
8090                continue;
8091            }
8092            // mb_skip_flag = 0
8093            let sctx = 11
8094                + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8095                + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8096            let tskip = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8097            cb_mb_skip(&mut cab, sctx, false);
8098            if crate::bitacct::enabled() {
8099                crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
8100            }
8101            cs.mb_skip[addr] = false;
8102            match inter {
8103                Some((mode, parts)) => {
8104                    let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts, None);
8105                    // ② residue naming: the CABAC entropy EMIT was untapped on the
8106                    // (default) CABAC driver — the whole encoder-side arithmetic
8107                    // coder was landing in `mgmt/other`.
8108                    let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
8109                    emit_mb_cabac_p_inter(&mut fe, &mut cab, &mut cs, mode, &plan, mb_x, mb_y, num_refs);
8110                }
8111                None => {
8112                    let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
8113                    let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
8114                    emit_mb_cabac_p_intra(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
8115                }
8116            }
8117            mb_qpy[mb_idx] = fe.cur_qp;
8118            {
8119                let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8120                cab.encode_terminate(mb_idx + 1 == total);
8121                if crate::bitacct::enabled() {
8122                    crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8123                }
8124            }
8125        }
8126    }
8127
8128    while !w.is_byte_aligned() {
8129        w.write_bit(true);
8130    }
8131    for b in cab.into_bytes() {
8132        w.write_bits(b as u32, 8);
8133    }
8134
8135    // Deblock -> inter reference (same as encode_slice_data).
8136    let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
8137    let info = rusty_h264_common::deblock::BlockInfo {
8138        inter: &fe.inter_y,
8139        nnz: &fe.nnz_y,
8140        mv: &fe.mv_y,
8141        ref_id: &ref_id,
8142        mv1: &[],
8143        ref_id1: &[],
8144        w4: fe.mb_w * 4,
8145        t8x8: &[],
8146        bs: &[], kind: &[],
8147        };
8148    rusty_h264_common::deblock::filter_frame(
8149        &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
8150    );
8151    let w4 = fe.mb_w * 4;
8152    crate::RefFrame {
8153        y: fe.rec_y,
8154        u: fe.rec_u,
8155        v: fe.rec_v,
8156        poc: 0,
8157        frame_num: 0,
8158        mv: fe.mv_y,
8159        ref_idx: fe.ref_idx_y,
8160        w4,
8161        // Filtered lazily on first sub-pel search use (see `RefFrame::hpel`).
8162        hpel: std::sync::OnceLock::new(),
8163    }
8164}
8165
8166// ============================================================================
8167// CABAC B-slice entropy coding — inverse of the decoder's decode_slice_data_cabac
8168// B-slice path. Scope: the modes the encoder's B decision produces — B_Skip,
8169// B_Direct_16x16 (0), B_L0/L1/Bi_16x16 (1/2/3) — no sub_mb_type, no intra-in-B.
8170// The new piece vs P is the dual-list (L0 + L1) mvd/ref neighbour cache.
8171// ============================================================================
8172
8173/// Fill one list's 30-entry mvd/ref neighbour cache from the per-MB export grids
8174/// (openh264 WelsFillCacheInterCabac). Shared by P (List-0) and B (both lists).
8175fn cb_fill_inter_cache(
8176    mb_ref: &[[i8; 16]],
8177    mb_mvd: &[[[i16; 2]; 16]],
8178    refc: &mut [i8; 30],
8179    mvdc: &mut [[i16; 2]; 30],
8180    top: Option<usize>,
8181    left: Option<usize>,
8182    addr: usize,
8183    mb_w: usize,
8184) {
8185    if let Some(l) = left {
8186        for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
8187            refc[ci] = mb_ref[l][bi];
8188            mvdc[ci] = mb_mvd[l][bi];
8189        }
8190    }
8191    if let Some(t) = top {
8192        for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
8193            refc[ci] = mb_ref[t][bi];
8194            mvdc[ci] = mb_mvd[t][bi];
8195        }
8196    }
8197    let mb_x = addr % mb_w;
8198    let mb_y = addr / mb_w;
8199    if mb_x > 0 && mb_y > 0 {
8200        let a = addr - mb_w - 1;
8201        (refc[0], mvdc[0]) = (mb_ref[a][15], mb_mvd[a][15]);
8202    }
8203    if mb_y > 0 && mb_x + 1 < mb_w {
8204        let a = addr - mb_w + 1;
8205        (refc[5], mvdc[5]) = (mb_ref[a][12], mb_mvd[a][12]);
8206    }
8207}
8208
8209/// B-slice `mb_type` for the encoder's B modes (0 = B_Direct_16x16, 1 = B_L0_16x16,
8210/// 2 = B_L1_16x16, 3 = B_Bi_16x16) — inverse of `parse_mb_type_b_cabac` (ctx 27).
8211/// B `mb_type` (Table 7-14) for a two-partition macroblock. `p0`/`p1` are 1=L0,
8212/// 2=L1, 3=Bi; `mvmode` 1 = 16x8, 2 = 8x16 (the odd types).
8213pub fn b_part_mb_type(p0: u8, p1: u8, mvmode: u8) -> u32 {
8214    let base = match (p0, p1) {
8215        (1, 1) => 4,
8216        (2, 2) => 6,
8217        (1, 2) => 8,
8218        (2, 1) => 10,
8219        (1, 3) => 12,
8220        (2, 3) => 14,
8221        (3, 1) => 16,
8222        (3, 2) => 18,
8223        _ => 20, // (Bi, Bi)
8224    };
8225    base + if mvmode == 2 { 1 } else { 0 }
8226}
8227
8228/// The two partition rects `(x, y, w, h)` and their z-order block lists for a B
8229/// 16x8 / 8x16 macroblock — the same split the decoder's `b_inter_layout` uses.
8230fn b_part_layout(mvmode: u8) -> ([(usize, usize, usize, usize); 2], [(usize, &'static [usize]); 2]) {
8231    if mvmode == 1 {
8232        ([(0, 0, 16, 8), (0, 8, 16, 8)],
8233         [(0, &[0, 1, 2, 3, 4, 5, 6, 7][..]), (8, &[8, 9, 10, 11, 12, 13, 14, 15][..])])
8234    } else {
8235        ([(0, 0, 8, 16), (8, 0, 8, 16)],
8236         [(0, &[0, 1, 2, 3, 8, 9, 10, 11][..]), (4, &[4, 5, 6, 7, 12, 13, 14, 15][..])])
8237    }
8238}
8239
8240/// B `mb_type` CABAC — the exact inverse of the decoder's `parse_mb_type_b_cabac`
8241/// (ctx base 27). Accepts the FULL spec range 0..=22, not just the four 16x16
8242/// modes, so the B 16x8 / 8x16 / 8x8 partitions become emittable.
8243///
8244/// Binarization, derived from the decoder branch-for-branch:
8245/// ```text
8246///   prefix (non-direct):  B+ctx_inc = 1 ; B+3 = 1
8247///   4-bit m4:             B+4 = bit3 ; B+5 = bit2 ; B+5 = bit1 ; B+5 = bit0
8248///   type 3..10  -> m4 = type - 3        (m4 < 8 -> bit3 = 0)      4 bins
8249///   type 11     -> m4 = 14  (B_Bi_8x16)                           4 bins
8250///   type 22     -> m4 = 15  (B_8x8)                               4 bins
8251///   type 12..21 -> v = type + 4 ; m4 = v >> 1 ; B+5 = v & 1       5 bins
8252/// ```
8253/// The decoder returns `m + 3` for `m < 8`, escapes at 13 (intra) / 14 / 15, and
8254/// otherwise reads a 5th bin and returns `m - 4`; the mapping above reproduces
8255/// every one of those branches. For 0..=3 the value equals the old `dir`, so
8256/// existing call sites are unchanged.
8257pub fn cb_mb_type_b(cab: &mut CabacEncoder, ctx_inc: usize, mb_type: u32) {
8258    const B: usize = 27;
8259    if mb_type == 0 {
8260        cab.encode_decision(B + ctx_inc, 0); // B_Direct_16x16
8261        return;
8262    }
8263    cab.encode_decision(B + ctx_inc, 1);
8264    if mb_type <= 2 {
8265        cab.encode_decision(B + 3, 0);
8266        cab.encode_decision(B + 5, mb_type - 1); // 16x16 L0 / L1
8267        return;
8268    }
8269    cab.encode_decision(B + 3, 1);
8270    let (m4, extra) = if mb_type <= 10 {
8271        (mb_type - 3, None)
8272    } else if mb_type == 11 {
8273        (14, None) // B_Bi_8x16
8274    } else if mb_type == 22 {
8275        (15, None) // B_8x8
8276    } else {
8277        let v = mb_type + 4;
8278        (v >> 1, Some(v & 1))
8279    };
8280    cab.encode_decision(B + 4, (m4 >> 3) & 1);
8281    cab.encode_decision(B + 5, (m4 >> 2) & 1);
8282    cab.encode_decision(B + 5, (m4 >> 1) & 1);
8283    cab.encode_decision(B + 5, m4 & 1);
8284    if let Some(e) = extra {
8285        cab.encode_decision(B + 5, e);
8286    }
8287}
8288
8289const CB_ALL16: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
8290
8291/// Emit one planned INTER B macroblock (mb_skip_flag already coded 0). `dir` is the
8292/// B direction 0/1/2/3; `plan.mvds` holds mvd_l0 then mvd_l1 (per used list).
8293fn emit_mb_cabac_b(
8294    fe: &mut FrameEncoder,
8295    cab: &mut CabacEncoder,
8296    cs: &mut CabacState,
8297    dir: u8,
8298    bsplit: Option<(u8, [(u8, (i32, i32), (i32, i32)); 2])>,
8299    plan: &InterPlan,
8300    mb_x: usize,
8301    mb_y: usize,
8302) {
8303    let mb_w = fe.mb_w;
8304    let addr = mb_y * mb_w + mb_x;
8305    let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
8306    let left = if mb_x > 0 { Some(addr - 1) } else { None };
8307
8308    let bci = left.map_or(0, |a| (!cs.mb_direct[a]) as usize)
8309        + top.map_or(0, |a| (!cs.mb_direct[a]) as usize);
8310    let bmt = match bsplit {
8311        Some((mvmode, parts2)) => b_part_mb_type(parts2[0].0, parts2[1].0, mvmode),
8312        None => dir as u32,
8313    };
8314    cb_mb_type_b(cab, bci, bmt);
8315
8316    // Dual-list mvd/ref caches (L0 = mb_ref/mb_mvd, L1 = mb_ref1/mb_mvd1).
8317    let mut mvdc0 = [[0i16; 2]; 30];
8318    let mut refc0 = [-1i8; 30];
8319    let mut mvdc1 = [[0i16; 2]; 30];
8320    let mut refc1 = [-1i8; 30];
8321    cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc0, &mut mvdc0, top, left, addr, mb_w);
8322    cb_fill_inter_cache(&cs.mb_ref1, &cs.mb_mvd1, &mut refc1, &mut mvdc1, top, left, addr, mb_w);
8323    let mut mmvd0 = [[0i16; 2]; 16];
8324    let mut mref0 = [-1i8; 16];
8325    let mut mmvd1 = [[0i16; 2]; 16];
8326    let mut mref1 = [-1i8; 16];
8327    let (use0, use1) = (dir == 1 || dir == 3, dir == 2 || dir == 3);
8328    if let Some((mvmode, parts2)) = bsplit {
8329        // Two partitions: mvds arrive LIST-major from the plan (spec 7.3.5.1), and
8330        // `cb_emit_mvd_partition` needs each partition's z-order block list so a
8331        // later macroblock's mvd ctxInc sees the right neighbours. B here runs a
8332        // single L0 and single L1, so num_ref_idx_active is 1 and NO ref_idx is
8333        // coded — only the mvds.
8334        let (_, zb) = b_part_layout(mvmode);
8335        let mut k = 0;
8336        for list in 0..2 {
8337            for part in 0..2 {
8338                let pred = parts2[part].0;
8339                let used = if list == 0 { pred == 1 || pred == 3 } else { pred == 2 || pred == 3 };
8340                if !used {
8341                    continue;
8342                }
8343                let (pidx, blocks) = zb[part];
8344                if list == 0 {
8345                    cb_emit_mvd_partition(cab, pidx, blocks, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
8346                } else {
8347                    cb_emit_mvd_partition(cab, pidx, blocks, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
8348                }
8349                k += 1;
8350            }
8351        }
8352    } else if dir == 0 {
8353        // B_Direct_16x16: no coded motion; ref 0 in both lists (mvd stays 0) so a
8354        // later MB's mvd ctxInc sums |0|.
8355        mref0 = [0i8; 16];
8356        mref1 = [0i8; 16];
8357    } else {
8358        // mvd parse order: list-major (L0 then L1); a single 16x16 partition (idx 0).
8359        let mut k = 0;
8360        if use0 {
8361            cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
8362            k += 1;
8363        }
8364        if use1 {
8365            cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
8366        }
8367    }
8368    cs.mb_mvd[addr] = mmvd0;
8369    cs.mb_ref[addr] = mref0;
8370    cs.mb_mvd1[addr] = mmvd1;
8371    cs.mb_ref1[addr] = mref1;
8372    cs.mb_direct[addr] = dir == 0 && bsplit.is_none();
8373    cs.cat[addr] = 100;
8374    cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
8375}
8376
8377/// Emit a B_Skip macroblock's mb_skip_flag = 1 (ctx 24 base) + neighbour state. The
8378/// direct motion was committed by `commit_direct_motion`; ref 0 in both lists, mvd 0
8379/// (matching the decoder's decode_b_skip handling).
8380fn emit_b_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
8381    let sctx = 24
8382        + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8383        + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8384    let t0 = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8385    cb_mb_skip(cab, sctx, true);
8386    if crate::bitacct::enabled() {
8387        crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
8388    }
8389    cs.mb_skip[addr] = true;
8390    cs.cat[addr] = 100;
8391    cs.mb_direct[addr] = true;
8392    cs.mb_ref[addr] = [0i8; 16];
8393    cs.mb_ref1[addr] = [0i8; 16];
8394    cs.last_delta_qp = 0;
8395}
8396
8397/// CABAC B-slice data coder. Mirrors `encode_slice_data_b`'s B_Skip-free check +
8398/// L0/L1/Bi/Direct RD decision verbatim; only the emit differs (per-MB
8399/// mb_skip_flag + CABAC + per-MB terminate). B is non-reference → no deblock/return.
8400#[allow(clippy::too_many_arguments)]
8401/// B-slice mode census (env `RFF_BSTATS=1`), so our B_Skip / B_Direct / coded
8402/// split can be compared directly with x264's `mb B ... direct:N% skip:N%` line.
8403/// Counts only; no effect on the bitstream.
8404pub mod bstats {
8405    use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
8406    pub static SKIP: AtomicU64 = AtomicU64::new(0);
8407    pub static CODED: AtomicU64 = AtomicU64::new(0);
8408    /// Of the NOT-free macroblocks, how often direct still won the mode decision.
8409    /// B_Skip rides direct-mode motion, so this is the physical quality of the
8410    /// thing the skip is betting on -- the candidate dispatch signal for how hard
8411    /// to push the skip.
8412    pub static DIRWIN: AtomicU64 = AtomicU64::new(0);
8413    /// Of the NOT-free macroblocks, how often a 16×8 / 8×16 partition beat every
8414    /// 16×16 mode. x264 puts 13.5% of its B macroblocks here; this is the column
8415    /// that makes ours comparable.
8416    pub static SPLIT: AtomicU64 = AtomicU64::new(0);
8417    pub fn on() -> bool {
8418        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8419        *E.get_or_init(|| std::env::var_os("RFF_BSTATS").is_some())
8420    }
8421    pub fn bump(c: &AtomicU64) {
8422        if on() {
8423            c.fetch_add(1, Relaxed);
8424        }
8425    }
8426    /// NOTE: this splits B_Skip from everything-else only. It does NOT separate
8427    /// B_Direct_16x16 (chosen inside the coded path) from genuinely coded modes, so
8428    /// it is comparable to x264's `skip:` column but NOT to its `direct:` column.
8429    pub fn dump() {
8430        let (s, c) = (SKIP.load(Relaxed), CODED.load(Relaxed));
8431        let t = (s + c).max(1) as f64;
8432        eprintln!(
8433            "B-slice census: B_Skip {:.1}%  not-skipped {:.1}%  direct-wins-of-coded {:.1}%  16x8/8x16-of-coded {:.1}%   (n={})",
8434            s as f64 * 100.0 / t, c as f64 * 100.0 / t,
8435            DIRWIN.load(Relaxed) as f64 * 100.0 / c.max(1) as f64,
8436            SPLIT.load(Relaxed) as f64 * 100.0 / c.max(1) as f64, s + c
8437        );
8438    }
8439}
8440
8441pub fn encode_slice_data_cabac_b(
8442    w: &mut BitWriter,
8443    cfg: &EncoderConfig,
8444    frame: &YuvFrame,
8445    qp: u8,
8446    poc: i32,
8447    l0: &crate::RefFrame,
8448    l1: &crate::RefFrame,
8449    qpo: &[i32],
8450) {
8451    let mut fe = FrameEncoder::new(cfg);
8452    fe.qp = qp;
8453    fe.qpc = chroma_qp(qp);
8454    fe.cur_qp = qp;
8455    if cfg.cabac_dz_div > 0 {
8456        fe.idz = cfg.cabac_dz_div; // CABAC-specific dead-zone override
8457    }
8458    fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
8459    let (sy, su, sv) = coded_source(cfg, frame);
8460    let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
8461    let lme = lambda.sqrt() * me_lambda_scale(cfg, &sy, fe.cw, fe.mb_w, fe.mb_h, Some(&l0.y[..]));
8462    let refs = std::slice::from_ref(l0);
8463    if fe.satd_q > 0.0 {
8464        let mut vars: Vec<i64> = (0..fe.mb_h)
8465            .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
8466            .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
8467            .collect();
8468        vars.sort_unstable();
8469        let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
8470        fe.satd_var_thresh = vars[idx];
8471    }
8472    let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
8473    apply_mbtree_qpo(&mut aq_qp, qpo); // mb-tree temporal AQ (empty = byte-identical)
8474    fe.cur_qp = qp;
8475
8476    let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
8477    let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
8478    let total = fe.mb_w * fe.mb_h;
8479    // RD B_Skip knobs + the online free-skip census that dispatches it.
8480    let bskip_t = std::env::var("RFF_BSKIP_T").ok().and_then(|v| v.parse::<f64>().ok())
8481        .or(cfg.tune_bskip_rd)
8482        .unwrap_or(0.0);
8483    let bskip_busy_pct = std::env::var("RFF_BSKIP_BUSY").ok().and_then(|v| v.parse::<usize>().ok())
8484        .or(cfg.tune_bskip_busy_pct)
8485        .unwrap_or(60);
8486    let (mut b_seen, mut b_free) = (0usize, 0usize);
8487    // B 16x8/8x16 partition search. Opt-in until the 4-QP per-clip table clears.
8488    let bsplit_env = std::env::var("RFF_BSPLIT").ok().and_then(|v| v.parse::<u32>().ok());
8489    let bsplit_on = bsplit_env.map(|v| v == 1).unwrap_or(cfg.tune_b_split);
8490    let bsplit_probe = bsplit_env.filter(|&v| v >= 2).unwrap_or(0);
8491    // Online DIRECT-WIN rate: of the macroblocks that were not exactly-free, how
8492    // often direct still won the mode decision. B_Skip rides direct-mode motion,
8493    // so this measures the quality of the thing the skip bets on.
8494    let (mut b_coded, mut b_dirwin) = (0usize, 0usize);
8495    let bskip_dirwin_pct = std::env::var("RFF_BSKIP_DIRWIN").ok().and_then(|v| v.parse::<usize>().ok())
8496        .or(cfg.tune_bskip_dirwin_pct)
8497        .unwrap_or(10);
8498
8499    for mb_y in 0..fe.mb_h {
8500        for mb_x in 0..fe.mb_w {
8501            let mb_idx = mb_y * fe.mb_w + mb_x;
8502            let addr = mb_idx;
8503            let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
8504            let left = if mb_x > 0 { Some(addr - 1) } else { None };
8505            fe.qp = aq_qp[mb_idx];
8506            fe.qpc = chroma_qp(aq_qp[mb_idx]);
8507            let (lx, ly) = (mb_x * 16, mb_y * 16);
8508            let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
8509            fe.mb_use_satd =
8510                fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
8511            let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
8512            let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
8513            let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
8514            let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
8515            let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
8516            // B_Skip: free direct prediction → mb_skip_flag = 1.
8517            let free_skip = fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
8518                && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc);
8519            b_seen += 1;
8520            if free_skip {
8521                b_free += 1;
8522            }
8523            if free_skip {
8524                bstats::bump(&bstats::SKIP);
8525                fe.commit_direct_motion(mb_x, mb_y, &dmotion);
8526                emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
8527                {
8528                    let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8529                    {
8530                let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8531                cab.encode_terminate(mb_idx + 1 == total);
8532                if crate::bitacct::enabled() {
8533                    crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8534                }
8535            }
8536                    if crate::bitacct::enabled() {
8537                        crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8538                    }
8539                }
8540                continue;
8541            }
8542            bstats::bump(&bstats::CODED);
8543            let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
8544            let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
8545            let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
8546            let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
8547            let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
8548                + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
8549            let j_bi = d_bi + (lme * r_bi as f64) as i64;
8550            let (mut dir, mut best) = (0u8, d_direct);
8551            if j0 < best { dir = 1; best = j0; }
8552            if j1 < best { dir = 2; best = j1; }
8553            if j_bi < best { dir = 3; best = j_bi; }
8554            if dir == 0 { bstats::bump(&bstats::DIRWIN); }
8555            b_coded += 1;
8556            if dir == 0 {
8557                b_dirwin += 1;
8558            }
8559            // ---- RD B_Skip (env RFF_BSKIP_T; unset = byte-identical) ----------
8560            // Our B_Skip previously required the direct residual to quantize to
8561            // EXACTLY zero. Measured against x264 at qp27: that reaches 93.5% of B
8562            // macroblocks on akiyo and 34.5% on foreman -- at or ABOVE x264 -- but
8563            // collapses to 7.8% on mobile where x264 still finds 27.4%. The deficit
8564            // is BUSY-CONTENT-ONLY, a sign flip, so this is a DISPATCH, not a new
8565            // constant: engage only where the free-skip rate is low.
8566            //
8567            // Two terms, both required:
8568            //   * `dir == 0` -- direct actually WON the mode decision. `best` starts
8569            //     at `d_direct` and only falls, so without this the test would fire
8570            //     on macroblocks the search proved are better coded.
8571            //   * distortion under T*lambda -- the residual is not worth its bits.
8572            // Gated on the ONLINE free-skip rate of this frame so far (the same
8573            // signal shape the P path's rd_skip uses, inverted: engage where free
8574            // skips are RARE, which is exactly where we under-skip).
8575            if bskip_t > 0.0
8576                && dir == 0
8577                && b_seen >= 32
8578                && b_free * 100 < b_seen * bskip_busy_pct
8579                // DIRECT-WIN FLOOR. Measured truth table at T=48 (BD-PSNR):
8580                //   football 7.0% direct-win  -> +0.08  LOSS   <- the only loser
8581                //   foreman 14.1%             -> -0.17  win
8582                //   bus     21.4%             -> -0.05  win
8583                //   akiyo   24.7%             -> inert
8584                //   tempete 30.9%             -> -0.16  win
8585                //   mobile  43.1%             -> -0.38  win
8586                // The one regressing clip has by far the lowest direct-win rate, and
8587                // the term is justified by exactly that clip: where direct almost
8588                // never wins the mode decision, its prediction is unreliable and
8589                // skipping on it costs more than the bits it saves.
8590                && b_coded >= 32
8591                && b_dirwin * 100 >= b_coded * bskip_dirwin_pct
8592                && (d_direct as f64) <= bskip_t * lambda
8593            {
8594                bstats::bump(&bstats::SKIP);
8595                fe.commit_direct_motion(mb_x, mb_y, &dmotion);
8596                emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
8597                cab.encode_terminate(mb_idx + 1 == total);
8598                continue;
8599            }
8600            // mb_skip_flag = 0, then the coded B MB.
8601            let sctx = 24
8602                + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8603                + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8604            let tskip = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8605            cb_mb_skip(&mut cab, sctx, false);
8606            if crate::bitacct::enabled() {
8607                crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
8608            }
8609            cs.mb_skip[addr] = false;
8610            // ---- B 16x8 / 8x16 partition search --------------------------------
8611            // x264 puts 13.5% of its B macroblocks here (`B16..8: 31.1 13.5 8.2`);
8612            // we had none, which is why the B bucket kept reading as a CODING gap
8613            // after every constant in it had been swept flat. Each half runs the
8614            // SAME 16x16 motion search that already exists, then the 9 (p0,p1)
8615            // pairings are priced against the 16x16 winner.
8616            let mut bsplit: Option<(u8, [(u8, (i32, i32), (i32, i32)); 2])> = None;
8617            // ORACLE PROBE (RFF_BSPLIT=2/3): force a 16x8 (2) or 8x16 (3) whose two
8618            // halves carry the SAME pred and the SAME motion as the 16x16 winner.
8619            // That is semantically identical to the 16x16 macroblock, so the
8620            // reconstruction MUST match it bit for bit -- any quality loss under this
8621            // probe is emit/predict PLUMBING drift and nothing to do with the mode
8622            // decision. (Separating those two is otherwise guesswork: both present as
8623            // "quality fell at the same rate".)
8624            if bsplit_probe > 0 && dir != 0 {
8625                let m = if bsplit_probe == 2 { 1u8 } else { 2u8 };
8626                bsplit = Some((m, [(dir, mv0, mv1); 2]));
8627            } else if bsplit_on {
8628                for mvmode in 1u8..=2 {
8629                    let (rects, _) = b_part_layout(mvmode);
8630                    let mut cand = [(0u8, (0i32, 0i32), (0i32, 0i32)); 2];
8631                    let mut jsum = 0i64;
8632                    for (part, &(rx, ry, rw, rh)) in rects.iter().enumerate() {
8633                        let (px, py) = (lx + rx, ly + ry);
8634                        let (m0, c0) = fe.motion_search(l0, &sy, px, py, rw, rh, &[pmv0], lme, None);
8635                        let (m1, c1) = fe.motion_search(l1, &sy, px, py, rw, rh, &[pmv1], lme, None);
8636                        // Bi for this rect: blend distortion + both mvd rates.
8637                        let dbi = fe.bi_dist_rect(l0, l1, &sy, px, py, rw, rh, m0, m1);
8638                        let rbi = mvd_bits(m0.0 - pmv0.0) + mvd_bits(m0.1 - pmv0.1)
8639                            + mvd_bits(m1.0 - pmv1.0) + mvd_bits(m1.1 - pmv1.1);
8640                        let jbi = dbi + (lme * rbi as f64) as i64;
8641                        let (mut bp, mut bj) = (1u8, c0);
8642                        if c1 < bj { bp = 2; bj = c1; }
8643                        if jbi < bj { bp = 3; bj = jbi; }
8644                        cand[part] = (bp, m0, m1);
8645                        jsum += bj;
8646                    }
8647                    // ~4 extra bins for the longer mb_type binarization.
8648                    let jsplit = jsum + (lme * 4.0) as i64;
8649                    if jsplit < best {
8650                        best = jsplit;
8651                        bsplit = Some((mvmode, cand));
8652                    }
8653                }
8654                if bsplit.is_some() {
8655                    bstats::bump(&bstats::SPLIT);
8656                }
8657            }
8658            let bspec = if let Some((mvmode, parts2)) = bsplit {
8659                BInter { dir, l1, mv0, mv1, mvmode, parts2 }
8660            } else {
8661                BInter { dir, l1, mv0, mv1, mvmode: 0, parts2: [(0, (0, 0), (0, 0)); 2] }
8662            };
8663            let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
8664            emit_mb_cabac_b(&mut fe, &mut cab, &mut cs, dir, bsplit, &plan, mb_x, mb_y);
8665            {
8666                let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8667                cab.encode_terminate(mb_idx + 1 == total);
8668                if crate::bitacct::enabled() {
8669                    crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8670                }
8671            }
8672        }
8673    }
8674
8675    while !w.is_byte_aligned() {
8676        w.write_bit(true);
8677    }
8678    for b in cab.into_bytes() {
8679        w.write_bits(b as u32, 8);
8680    }
8681    // B is non-reference: no deblock, no RefFrame (the decoder deblocks for display).
8682}
8683
8684/// Minimal all-B_Skip CABAC B-slice (the rare no-bracketing-anchor fallback in
8685/// `code_picture`): every MB is mb_skip_flag = 1. B is non-reference so the recon
8686/// is irrelevant; this only needs to be a legal CABAC slice.
8687pub fn encode_all_skip_b_cabac(w: &mut BitWriter, cfg: &EncoderConfig, qp: u8, n: usize) {
8688    let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
8689    for i in 0..n {
8690        // ctxInc = 24 + (left avail & not-skip) + (top avail & not-skip). Every
8691        // neighbour is either a skip (contributes 0) or unavailable (0) → always 24.
8692        cab.encode_decision(24, 1);
8693        cab.encode_terminate(i + 1 == n);
8694    }
8695    while !w.is_byte_aligned() {
8696        w.write_bit(true);
8697    }
8698    for b in cab.into_bytes() {
8699        w.write_bits(b as u32, 8);
8700    }
8701}