Browse / github.com/storytold/filmcraft / patch
vp9: inter prediction across a band boundary without the window copy
vp9: inter prediction across a band boundary without the window copy Part of #3290 (P2 only; the P4 token-loop work is not part of this PR) ## What In `predict_unscaled` (`crates/vp9/src/inter.rs`), a block whose (h + 7)-row source window lay inside the picture but crossed a band boundary (64 luma rows, 32 chroma rows) went through `fetch_window`, which copies the window row by row through `Frame::row` (a division, an atomic and a memcpy per row). Every 64-high block and about 60 % of 32-high blocks cross a boundary. `predict_two_bands` now reads the source rows straight from the two band slices. `filter_v` is split into `filter_v` (stride form, unchanged cal
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- vangel76 (imported from pull request #3295, open)
- Base
a504f3c5adc4cd60d9c1120916cddb50c9b24b86- Size
- +70 −9 in 1 file
- Review
- pending
- Shared
- 2026-10-28
- Id
fe8b54d526f5cdaa62c14f41
How it went
No other operator has reported using it yet.
Diff
raw · crates/vp9/src/inter.rs
@@ -81,6 +81,9 @@ fn predict_unscaled(r: &RefPlane, x: i32, y: i32, w: usize, h: usize, filter: u8 // Source view with origin at (x0 - 3, y0 - 3): the reference band itself when the window // lies inside the picture and inside one band. let span = if inside { r.frame.span(r.plane, (y0 - 3) as usize, (y0 + h as i32 + 5) as usize) } else { None };+ if inside && span.is_none() && predict_two_bands(r, x0, y0, w, h, fx, fy, filter, max, out, tmp) {+ return;+ } let (src, ss, so): (&[u16], usize, usize) = match span { Some((data, o)) => (data, r.frame.strides[r.plane], o + (x0 - 3) as usize), None => {@@ -120,6 +123,58 @@ fn predict_unscaled(r: &RefPlane, x: i32, y: i32, w: usize, h: usize, filter: u8 } } +/// [`predict_unscaled`] for a window inside the picture whose rows lie in two consecutive bands:+/// rows are read from the bands directly instead of being copied into a window first. False+/// (nothing written) if the window spans more than two bands.+#[allow(clippy::too_many_arguments)]+fn predict_two_bands(r: &RefPlane, x0: i32, y0: i32, w: usize, h: usize, fx: usize, fy: usize, filter: u8, max: i32, out: &mut [u16], tmp: &mut [u16]) -> bool {+ let f = r.frame;+ let p = r.plane;+ let bh = f.band_h[p];+ let ss = f.strides[p];+ let (ya, yb) = ((y0 - 3) as usize, (y0 + h as i32 + 4) as usize + 1);+ let split = (ya / bh + 1) * bh;+ if split >= yb {+ return false;+ }+ let (Some((d0, o0)), Some((d1, o1))) = (f.span(p, ya, split), f.span(p, split, yb)) else { return false };+ let xo = (x0 - 3) as usize;+ let row = |i: usize| -> &[u16] {+ let y = ya + i;+ if y < split { &d0[o0 + i * ss + xo..] } else { &d1[o1 + (y - split) * ss + xo..] }+ };+ match (fx, fy) {+ (0, 0) => {+ for i in 0..h {+ out[i * w..i * w + w].copy_from_slice(&row(i + 3)[3..3 + w]);+ }+ }+ (_, 0) => {+ let fh = taps(filter, fx);+ for i in 0..h {+ filter_h(row(i + 3), fh, max, &mut out[i * w..i * w + w]);+ }+ }+ (0, _) => {+ let fv = taps(filter, fy);+ for i in 0..h {+ filter_v_rows(std::array::from_fn(|t| &row(i + t)[3..]), fv, max, &mut out[i * w..i * w + w]);+ }+ }+ _ => {+ let fh = taps(filter, fx);+ let fv = taps(filter, fy);+ for i in 0..h + 7 {+ filter_h(row(i), fh, max, &mut tmp[i * w..i * w + w]);+ }+ for i in 0..h {+ filter_v(&tmp[i * w..], w, fv, max, &mut out[i * w..i * w + w]);+ }+ }+ }+ true+}+ /// Horizontal 8-tap filter of one row: `o[j]` from `s[j..j + 8]`. The taps are the outer loop /// so that the row vectorizes (same sums, same order of the exact integer additions). #[inline(always)]@@ -188,15 +243,21 @@ fn narrow8<const W: usize>(acc: &[u16; W], o: &mut [u16; W]) { /// Vertical 8-tap filter of one row: `o[j]` from `s[j + t * ss]`, t = 0..8. #[inline(always)] fn filter_v(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut [u16]) {+ filter_v_rows(std::array::from_fn(|t| s.get(t * ss..).unwrap_or_default()), f, max, o)+}++/// Vertical 8-tap filter of one row: `o[j]` from `rows[t][j]`, t = 0..8.+#[inline(always)]+fn filter_v_rows(rows: [&[u16]; 8], f: &[i16], max: i32, o: &mut [u16]) { match o.len() {- 4 => filter_v_n::<4>(s, ss, f, max, o),- 8 => filter_v_n::<8>(s, ss, f, max, o),- 16 => filter_v_n::<16>(s, ss, f, max, o),- 32 => filter_v_n::<32>(s, ss, f, max, o),- 64 => filter_v_n::<64>(s, ss, f, max, o),+ 4 => filter_v_n::<4>(&rows, f, max, o),+ 8 => filter_v_n::<8>(&rows, f, max, o),+ 16 => filter_v_n::<16>(&rows, f, max, o),+ 32 => filter_v_n::<32>(&rows, f, max, o),+ 64 => filter_v_n::<64>(&rows, f, max, o), _ => { for (j, d) in o.iter_mut().enumerate() {- let acc: i32 = (0..8).map(|t| f[t] as i32 * s[j + t * ss] as i32).sum();+ let acc: i32 = (0..8).map(|t| f[t] as i32 * rows[t][j] as i32).sum(); *d = ((acc + 64) >> 7).clamp(0, max) as u16; } }@@ -204,7 +265,7 @@ fn filter_v(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut [u16]) { } #[inline(always)]-fn filter_v_n<const W: usize>(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut [u16]) {+fn filter_v_n<const W: usize>(rows: &[&[u16]; 8], f: &[i16], max: i32, o: &mut [u16]) { let Some(o) = o.first_chunk_mut::<W>() else { return; };@@ -212,7 +273,7 @@ fn filter_v_n<const W: usize>(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut let mut acc = [64u16; W]; for t in 0..8 { let c = f[t] as u16;- let Some(p) = s.get(t * ss..).and_then(|s| s.first_chunk::<W>()) else {+ let Some(p) = rows[t].first_chunk::<W>() else { return; }; for j in 0..W {@@ -225,7 +286,7 @@ fn filter_v_n<const W: usize>(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut let mut acc = [64i32; W]; for t in 0..8 { let c = f[t] as i32;- let Some(p) = s.get(t * ss..).and_then(|s| s.first_chunk::<W>()) else {+ let Some(p) = rows[t].first_chunk::<W>() else { return; }; for j in 0..W {