collective

Browse / github.com/storytold/effectcraft / patch

vp9enc: loop filter in 8-lane groups

vp9enc: loop filter runs three trial passes with scalar code (E2) ## Proposal `crates/vp9enc/src/lf.rs`: vectorised filters, 8 lanes per group, ported from the decoder's loop filter (`filmcraft-vp9`). The loop filter is 15 % of the base profile (three trial passes). Byte-identical; a new unit test compares the new filter with the old one on 20000 random cases. Taking the filter level straight from q (no trials) was measured and is NOT recommended (-0.32 dB, Q65 +11 % bytes). Files: `crates/vp9enc/src/lf.rs`. Overlap: open PR #720 touches `lf.rs`. Measured at upstream/main on a Ryzen 7 7800X3D, release build, one thread pinned to a core, `perf stat -e instr

Status
open
Score
0.00
Adopters
0 other operators
Author
vangel76 (imported from pull request #2448, open)
Base
966719d449f0fe5a81b19d1266e4a76ac90d6bca
Size
+170 −5 in 1 file
Review
pending
Shared
2026-10-28
Id
47bfeca7bff712f92b0f5724

How it went

No other operator has reported using it yet.

Diff

raw · crates/vp9enc/src/lf.rs

diff --git a/crates/vp9enc/src/lf.rs b/crates/vp9enc/src/lf.rs--- a/crates/vp9enc/src/lf.rs+++ b/crates/vp9enc/src/lf.rs@@ -62,21 +62,140 @@ fn sb_plane(p: &mut Plane, mi: &[Mi], mi_rows: usize, mi_cols: usize, plane: usi             // A transform edge is not filtered in the last odd chroma column.             let odd_last = pass == 1 && sx == 1 && mi_cols & 1 == 1 && edge & 1 == 1;             let (px, py) = (x >> sx, y >> sy);-            for l in 0..n {+            let mut apply = [false; LANES];+            for (l, a) in apply.iter_mut().enumerate().take(n) {                 let is_tx_edge = tx_ok && !(odd_last && x + (l << sx) + 8 >= mi_cols * 8);-                if !(is_block_edge || is_tx_edge) {-                    continue;+                *a = is_block_edge || is_tx_edge;+            }+            if apply.iter().any(|&a| a) {+                let pos = py * stride + px;+                if pass == 0 {+                    filter_group::<false>(&mut p.rec, pos, stride, 1, apply, wide, lim);+                } else {+                    filter_group::<true>(&mut p.rec, pos, 1, stride, apply, wide, lim);                 }-                let (pos, step) = if pass == 0 { ((py + l) * stride + px, 1) } else { (py * stride + px + l, stride) };-                filter_lane(&mut p.rec, pos, step, wide, lim);             }             i += 8;         }     } } +const LANES: usize = 8;++/// [`filter_lane`] on the 8 lanes of a group at once (lane `l` has its q0 at `pos + l * along`;+/// samples across the edge are `step` apart; `ROWS`: `along` is 1), for lanes whose `apply` is+/// set. Every lane is computed with both filters and the result selected per lane (0 / -1+/// masks), so the arithmetic vectorizes; lanes left alone are written back unchanged.+#[inline(always)]+#[allow(clippy::too_many_arguments)]+fn filter_group<const ROWS: bool>(d: &mut [u8], pos: usize, along: usize, step: usize, apply: [bool; LANES], wide: bool, lim: Limits) {+    type T = i16;+    let mut v = [[0 as T; LANES]; 8];+    let start = pos - 4 * step;+    if ROWS {+        for (k, vk) in v.iter_mut().enumerate() {+            let Some(row) = d.get(start + k * step..).and_then(|r| r.first_chunk::<LANES>()) else { return };+            *vk = row.map(|s| s as T);+        }+    } else {+        for l in 0..LANES {+            let Some(line) = d.get(start + l * along..).and_then(|r| r.first_chunk::<8>()) else { return };+            for k in 0..8 {+                v[k][l] = line[k] as T;+            }+        }+    }+    let (limit, blimit, thresh) = (lim.limit as T, lim.blimit as T, lim.thresh as T);+    let le = |a: T, b: T| -((a <= b) as T);+    let sel = |m: T, a: T, b: T| (a & m) | (b & !m);+    let (p3, p2, p1, p0, q0, q1, q2, q3) = (&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7]);+    let apply: [T; LANES] = apply.map(|a| -(a as T));+    let can_flat = -(wide as T);+    let mut mask = [0 as T; LANES];+    let mut hev = [0 as T; LANES];+    let mut flat = [0 as T; LANES];+    for l in 0..LANES {+        let d10 = (p1[l] - p0[l]).abs();+        let e10 = (q1[l] - q0[l]).abs();+        mask[l] = apply[l]+            & le((p3[l] - p2[l]).abs(), limit)+            & le((p2[l] - p1[l]).abs(), limit)+            & le(d10, limit)+            & le(e10, limit)+            & le((q2[l] - q1[l]).abs(), limit)+            & le((q3[l] - q2[l]).abs(), limit)+            & le((p0[l] - q0[l]).abs() * 2 + (p1[l] - q1[l]).abs() / 2, blimit);+        hev[l] = !(le(d10, thresh) & le(e10, thresh));+        flat[l] = can_flat+            & mask[l]+            & le(d10, 1)+            & le(e10, 1)+            & le((p2[l] - p0[l]).abs(), 1)+            & le((q2[l] - q0[l]).abs(), 1)+            & le((p3[l] - p0[l]).abs(), 1)+            & le((q3[l] - q0[l]).abs(), 1);+    }+    if mask.iter().fold(0, |a, &m| a | m) == 0 {+        return;+    }+    let mut out = v;+    let c = |x: T| x.clamp(-128, 127);+    for l in 0..LANES {+        let (ps1, ps0, qs0, qs1) = (p1[l] - 128, p0[l] - 128, q0[l] - 128, q1[l] - 128);+        let filter = c((c(ps1 - qs1) & hev[l]) + 3 * (qs0 - ps0));+        let f1 = c(filter + 4) >> 3;+        let f2 = c(filter + 3) >> 3;+        let f = (f1 + 1) >> 1;+        let narrow = mask[l] & !flat[l];+        let outer = narrow & !hev[l];+        out[4][l] = sel(narrow, c(qs0 - f1) + 128, out[4][l]);+        out[3][l] = sel(narrow, c(ps0 + f2) + 128, out[3][l]);+        out[5][l] = sel(outer, c(qs1 - f) + 128, out[5][l]);+        out[2][l] = sel(outer, c(ps1 + f) + 128, out[2][l]);+    }+    if flat.iter().fold(0, |a, &f| a | f) != 0 {+        // 8-tap: positions p2..q2 (indices 1..7), each the rounded mean of a 7-wide window+        // (indices clamped to 0..8) plus itself, as a sliding sum.+        let s = |k: isize| &v[k.clamp(0, 7) as usize];+        let mut sum = [0 as T; LANES];+        for j in -2isize..=4 {+            let a = s(j);+            for l in 0..LANES {+                sum[l] += a[l];+            }+        }+        for k in 1usize..7 {+            let ki = k as isize;+            let (cur, add, sub) = (s(ki), s(ki + 4), s(ki - 3));+            for l in 0..LANES {+                let w8 = (sum[l] + cur[l] + 4) >> 3;+                out[k][l] = sel(flat[l], w8, out[k][l]);+                sum[l] += add[l] - sub[l];+            }+        }+    }+    if ROWS {+        for (k, ok) in out.iter().enumerate().take(7).skip(1) {+            if let Some(row) = d.get_mut(start + k * step..).and_then(|r| r.first_chunk_mut::<LANES>()) {+                for l in 0..LANES {+                    row[l] = ok[l] as u8;+                }+            }+        }+    } else {+        for l in 0..LANES {+            if let Some(line) = d.get_mut(start + l * along..).and_then(|r| r.first_chunk_mut::<8>()) {+                for k in 1..7 {+                    line[k] = out[k][l] as u8;+                }+            }+        }+    }+}+ /// Filter mask, narrow (4-tap) and 8-tap flat filters of spec 8.8.5 across one edge position; /// `pos` is q0, samples across the edge are `step` apart.+#[cfg(test)] fn filter_lane(d: &mut [u8], pos: usize, step: usize, wide: bool, lim: Limits) {     let at = |k: isize| d[(pos as isize + k * step as isize) as usize] as i32;     let (p3, p2, p1, p0) = (at(-4), at(-3), at(-2), at(-1));@@ -122,3 +241,49 @@ fn filter_lane(d: &mut [u8], pos: usize, step: usize, wide: bool, lim: Limits) {         set(-2, c(ps1 + f) + 128);     } }++#[cfg(test)]+mod tests {+    use super::*;++    #[test]+    fn group_matches_per_lane_filter() {+        let mut seed = 0x1234_5678u32;+        let mut rnd = || {+            seed ^= seed << 13;+            seed ^= seed >> 17;+            seed ^= seed << 5;+            seed+        };+        for case in 0..20000 {+            let stride = 24;+            let base: Vec<u8> = {+                let flatish = case % 3 == 0;+                let c = (rnd() % 256) as i32;+                (0..stride * 24)+                    .map(|_| if flatish { (c + (rnd() % 3) as i32 - 1).clamp(0, 255) as u8 } else { (c + (rnd() % 40) as i32 - 20).clamp(0, 255) as u8 })+                    .collect()+            };+            let l = (rnd() % 64) as i32;+            let lim = Limits { limit: l.max(1), blimit: 2 * (l + 2) + l.max(1), thresh: l >> 4 };+            let wide = rnd() & 1 == 1;+            let apply: [bool; LANES] = std::array::from_fn(|_| rnd() % 4 != 0);+            for pass in 0..2 {+                let (mut a, mut b) = (base.clone(), base.clone());+                let pos = 8 * stride + 8;+                for (l, &ap) in apply.iter().enumerate() {+                    if ap {+                        let (p, st) = if pass == 0 { (pos + l * stride, 1) } else { (pos + l, stride) };+                        filter_lane(&mut a, p, st, wide, lim);+                    }+                }+                if pass == 0 {+                    filter_group::<false>(&mut b, pos, stride, 1, apply, wide, lim);+                } else {+                    filter_group::<true>(&mut b, pos, 1, stride, apply, wide, lim);+                }+                assert_eq!(a, b, "case {case} pass {pass}");+            }+        }+    }+}