av1enc: build the three half-sample reference planes in one pass

Decider::new built the half-sample planes (8,0), (0,8) and (8,8) with
three full 2-D 8-tap passes (subpel_plane), each with per-tap clamping.
half_planes computes them together: the horizontal half-sample pass is
shared by (8,0) and (8,8), the phase-0 kernels are exact shifts
(Round2(128*v, 11) = Round2(v, 4), Round2(16*s, 11) = Round2(s, 7)), and
interior pixels skip the clamps.

subpel_plane is kept as the test-only reference; a new unit test checks
half_planes against it bit for bit (including 1x1, 5x3 and 10 bit).
The only change in decide.rs is the call in Decider::new.

Output is bit-identical.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
vangel76andClaude Sonnet 5.5 committed 2026-10-25 12:15:23 +01:00
1 parent 991904888a
commit b4b7c2d339
2 files changed
+77 -2

No files matched your search

+1 -1
View File
@@ -163,7 +163,7 @@ impl<'a> Decider<'a> {
lambda,
lambda_sad: lambda.sqrt(),
half: match refr {
Some(rf) => [(8, 0), (0, 8), (8, 8)].map(|(fx, fy)| subpel_plane(&rf.planes[0], g.width, g.height, fx, fy, bd)),
Some(rf) => half_planes(&rf.planes[0], g.width, g.height, bd),
None => Default::default(),
},
}
+76 -1
View File
@@ -293,7 +293,8 @@ pub(crate) fn predict_inter(
/// The visible `w x h` area of a reference plane interpolated at the sub-sample phase
/// (`fx`, `fy`) (1/16 units) with the EIGHTTAP filter, as `predict_inter` computes it for
/// blocks wider and taller than 4. Used for motion search.
/// blocks wider and taller than 4. The reference for `half_planes`, which the encoder uses.
#[cfg(test)]
pub(crate) fn subpel_plane(refp: &Plane, w: usize, h: usize, fx: usize, fy: usize, bit_depth: u32) -> Vec<u16> {
let kh = &SUBPEL_FILTERS[EIGHTTAP][fx];
let kv = &SUBPEL_FILTERS[EIGHTTAP][fy];
@@ -323,6 +324,66 @@ pub(crate) fn subpel_plane(refp: &Plane, w: usize, h: usize, fx: usize, fy: usiz
out
}
/// The three half-sample planes (8, 0), (0, 8), (8, 8) of `subpel_plane` in one pass: the
/// horizontal half-sample pass is shared by (8, 0) and (8, 8), and the phase-0 kernels reduce to
/// exact shifts (Round2( 128 * v, 11 ) = Round2( v, 4 ), Round2( 16 * s, 11 ) = Round2( s, 7 )),
/// so the result equals three `subpel_plane` calls bit for bit.
pub(crate) fn half_planes(refp: &Plane, w: usize, h: usize, bit_depth: u32) -> [Vec<u16>; 3] {
let k = &SUBPEL_FILTERS[EIGHTTAP][8];
let max = (1i32 << bit_depth) - 1;
let (lx, ly) = (w as i32 - 1, h as i32 - 1);
// horizontal pass at phase 8: Round2( sum, 3 )
let mut hp = vec![0i32; w * h];
for y in 0..h {
let row = &refp.row(y)[..w];
let out = &mut hp[y * w..y * w + w];
let tap = |x: i32| -> i32 {
let mut s = 0i32;
for t in 0..8 {
s += k[t] as i32 * row[(x + t as i32 - 3).clamp(0, lx) as usize] as i32;
}
round2(s, 3)
};
if w >= 8 {
for x in 0..3 {
out[x] = tap(x as i32);
}
for (x, o) in out.iter_mut().enumerate().take(w - 4).skip(3) {
let seg = &row[x - 3..x + 5];
let mut s = 0i32;
for t in 0..8 {
s += k[t] as i32 * seg[t] as i32;
}
*o = round2(s, 3);
}
for x in w - 4..w {
out[x] = tap(x as i32);
}
} else {
for (x, o) in out.iter_mut().enumerate() {
*o = tap(x as i32);
}
}
}
let p80: Vec<u16> = hp.iter().map(|&v| round2(v, 4).clamp(0, max) as u16).collect();
let mut p08 = vec![0u16; w * h];
let mut p88 = vec![0u16; w * h];
for y in 0..h {
let rows: [usize; 8] = std::array::from_fn(|t| (y as i32 + t as i32 - 3).clamp(0, ly) as usize);
let (o08, o88) = (&mut p08[y * w..y * w + w], &mut p88[y * w..y * w + w]);
for x in 0..w {
let (mut a, mut b) = (0i32, 0i32);
for t in 0..8 {
a += k[t] as i32 * refp.data[rows[t] * refp.stride + x] as i32;
b += k[t] as i32 * hp[rows[t] * w + x];
}
o08[x] = round2(a, 7).clamp(0, max) as u16;
o88[x] = round2(b, 11).clamp(0, max) as u16;
}
}
[p80, p08, p88]
}
#[cfg(test)]
mod tests {
use super::*;
@@ -353,6 +414,20 @@ mod tests {
}
}
#[test]
fn half_planes_match_subpel_plane() {
for (w, h) in [(37usize, 29usize), (5, 3), (1, 1), (64, 64)] {
let mut p = Plane::new(64, 64);
for (i, v) in p.data.iter_mut().enumerate() {
*v = ((i * 7919) % 1024) as u16;
}
let hp = half_planes(&p, w, h, 10);
for (i, (fx, fy)) in [(8, 0), (0, 8), (8, 8)].into_iter().enumerate() {
assert_eq!(hp[i], subpel_plane(&p, w, h, fx, fy, 10), "{w}x{h} phase ({fx}, {fy})");
}
}
}
#[test]
fn phase_zero_filters_are_impulses() {
for f in SUBPEL_FILTERS.iter() {