From f3fcea8297da0abae1bc908c3cef55954693af2b Mon Sep 17 00:00:00 2001 From: vangel76 Date: Mon, 26 Oct 2026 00:31:22 +0100 Subject: [PATCH] vp9enc: forward transform accumulates all outputs together (#2412) The 16 outputs of the 4x4 forward transform are accumulated together in the same order as before (no serial add chain per output) and the quantiser truncates instead of calling floor on a non-negative value. The output is byte-identical. Co-Authored-By: Claude Sonnet 5.5 --- crates/vp9enc/src/transform.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/crates/vp9enc/src/transform.rs b/crates/vp9enc/src/transform.rs index e6a685d5a..20996d60b 100644 --- a/crates/vp9enc/src/transform.rs +++ b/crates/vp9enc/src/transform.rs @@ -144,6 +144,16 @@ pub fn forward_matrices() -> &'static [[[f64; 16]; 16]; 4] { }) } +/// [`forward_matrices`] transposed: `[type][q][k]`. +fn forward_matrices_t() -> &'static [[[f64; 16]; 16]; 4] { + use std::sync::OnceLock; + static M: OnceLock<[[[f64; 16]; 16]; 4]> = OnceLock::new(); + M.get_or_init(|| { + let f = forward_matrices(); + std::array::from_fn(|t| std::array::from_fn(|q| std::array::from_fn(|k| f[t][k][q]))) + }) +} + fn invert16(m: [[f64; 16]; 16]) -> [[f64; 16]; 16] { let mut a = m; let mut inv = [[0.0; 16]; 16]; @@ -191,12 +201,23 @@ impl Quant { if self.lossless { return forward_wht(res); } - let f = &forward_matrices()[tx_type as usize]; + let ft = &forward_matrices_t()[tx_type as usize]; + // Every output accumulates its 16 products in the same order (q ascending) as a + // per-output sum, but the 16 outputs advance together: 16 independent chains that + // vectorize instead of one serial chain per output. + let mut acc = [-0.0f64; 16]; + for (q, col) in ft.iter().enumerate() { + let r = res[q] as f64; + for k in 0..16 { + acc[k] += col[k] * r; + } + } let mut lv = [0i32; 16]; for k in 0..16 { - let coef: f64 = (0..16).map(|q| f[k][q] * res[q] as f64).sum(); + let coef = acc[k]; let (qs, rnd) = if k == 0 { (self.dcq, self.round_dc) } else { (self.acq, self.round_ac) }; - let l = (coef.abs() / qs as f64 + rnd).floor() as i32; + // Non-negative: truncation is floor. + let l = (coef.abs() / qs as f64 + rnd) as i32; lv[k] = if coef < 0.0 { -l } else { l }; } lv