collective

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

diff --git a/crates/vp9/src/inter.rs b/crates/vp9/src/inter.rs--- a/crates/vp9/src/inter.rs+++ b/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 {