collective

Browse / github.com/storytold/filmcraft / patch

hevc: SAO border lines through the vectorised edge run

hevc: SAO border lines without per-sample neighbour checks Found while auditing `crates/hevc` at upstream/main bfc23840 (Ryzen 7800X3D, single thread, `perf stat -e instructions:u,cycles:u` and flat `perf record` profiles; libx265 fixtures: 1080p long-GOP B with/without WPP, 4 slices, Main10, intra-only, and a ~100 Mbit/s 2160p Main10 stream, each with an ffmpeg md5). Today single-thread: 1080p long-GOP ~108 fps, Main10 86 fps, intra 44 fps, 2160p Main10 high bitrate 6.2 fps. Gains marked "estimated" were not measured. **SAO checks neighbours per sample on CTB borders** (`filter.rs:453-500`, `:505-519`): the first and last line of every CTB and the e

Status
open
Score
0.00
Adopters
0 other operators
Author
vangel76 (imported from pull request #3185, open)
Base
bfc23840b603e69c782302c6ed04d4a9f6ec26a8
Size
+75 −73 in 1 file
Review
pending
Shared
2026-10-28
Id
73f9e0494d8238b9dced0ae8

How it went

No other operator has reported using it yet.

Diff

raw · crates/hevc/src/filter.rs

diff --git a/crates/hevc/src/filter.rs b/crates/hevc/src/filter.rs--- a/crates/hevc/src/filter.rs+++ b/crates/hevc/src/filter.rs@@ -405,53 +405,65 @@ fn sao_ctb<S: Sample>(     let max = (1i32 << bd) - 1;     let sc = if c == 0 { 0 } else { 1 };     let offs = p.offsets[c].map(|v| v as i32);-    // samples of CUs that must not be modified-    let nofilter = |pic: &PicState<S>, x: usize, y: usize| pic.blk[((y << sc) >> 2) * pic.w4 + ((x << sc) >> 2)].flags & F_NOFILTER != 0;-    let any_nofilter = {-        let mut a = false;-        for by in ((y0 << sc) >> 2)..(((y1 << sc) + 3) >> 2).min(pic.h4) {-            for bx in ((x0 << sc) >> 2)..(((x1 << sc) + 3) >> 2).min(pic.w4) {-                a |= pic.blk[by * pic.w4 + bx].flags & F_NOFILTER != 0;-            }-        }-        a-    };+    let plane = &pic.planes[c];     if p.type_idx[c] == 1 {-        let shift = bd - 5;-        let mut table = [0i32; 32];-        for k in 0..4 {-            table[(k + p.band_pos[c] as usize) & 31] = offs[k];-        }+        let band = (p.band_pos[c] as i32, offs);         for y in y0..y1 {-            if !any_nofilter {-                let band = (p.band_pos[c] as i32, offs);-                sao_band_run(&pic.planes[c][y * w + x0..y * w + x1], &mut dst[(y - y0) * w + x0..(y - y0) * w + x1], shift, band, max);-                continue;-            }-            for x in x0..x1 {-                if nofilter(pic, x, y) {+            sao_band_run(&plane[y * w + x0..y * w + x1], &mut dst[(y - y0) * w + x0..(y - y0) * w + x1], bd - 5, band, max);+        }+    } else {+        sao_edge_ctb(pic, c, rs, x0, x1, y0, y1, w, h, p, max, dst);+    }+    // Samples of CUs that must not be modified (transquant bypass, PCM without loop filter) get+    // their deblocked values back.+    if pic.pps.transquant_bypass || pic.sps.pcm_loop_filter_disabled {+        let n = 4 >> sc;+        for by in ((y0 << sc) >> 2)..(((y1 << sc) + 3) >> 2).min(pic.h4) {+            let ya = (by * 4) >> sc;+            for bx in ((x0 << sc) >> 2)..(((x1 << sc) + 3) >> 2).min(pic.w4) {+                if pic.blk[by * pic.w4 + bx].flags & F_NOFILTER == 0 {                     continue;                 }-                let v = pic.planes[c][y * w + x].to_i32();-                dst[(y - y0) * w + x] = S::from_i32((v + table[(v >> shift) as usize]).clamp(0, max));+                let xa = (bx * 4) >> sc;+                let xb = (xa + n).min(x1);+                for y in ya..(ya + n).min(y1) {+                    dst[(y - y0) * w + xa..(y - y0) * w + xb].copy_from_slice(&plane[y * w + xa..y * w + xb]);+                }             }         }-        return;     }-    let (hp, vp): ([i32; 2], [i32; 2]) = match p.eo_class[c] {+}++/// Edge offset of one CTB. Which of the 8 neighbouring CTBs may be used (inside the picture, not+/// in another slice / tile without cross-boundary filtering) is worked out once; each line is then+/// split into the left column, the inner run and the right column, and each part whose neighbours+/// are usable goes through [`sao_edge_run`].+#[allow(clippy::too_many_arguments)]+fn sao_edge_ctb<S: Sample>(+    pic: &PicState<S>,+    c: usize,+    rs: usize,+    x0: usize,+    x1: usize,+    y0: usize,+    y1: usize,+    w: usize,+    h: usize,+    p: &crate::slicedec::SaoParams,+    max: i32,+    dst: &mut [S],+) {+    let offs = p.offsets[c].map(|v| v as i32);+    let (hp, vp): ([isize; 2], [isize; 2]) = match p.eo_class[c] {         0 => ([-1, 1], [0, 0]),         1 => ([0, 0], [-1, 1]),         2 => ([-1, 1], [-1, 1]),         _ => ([1, -1], [-1, 1]),     };-    // Neighbouring CTBs whose samples may not be used (other slice / tile without cross-boundary-    // filtering), checked per sample only near CTB borders.-    let s = pic.log2_ctb as usize - sc;     let cur_slice = pic.ctb_slice[rs];     let lf_cur = pic.slices[cur_slice as usize].lf_across;     let cur_ts = pic.layout.rs_to_ts[rs];-    let cross_ok = |pic: &PicState<S>, xn: usize, yn: usize| -> bool {-        let nrs = (yn >> s) * pic.wctb + (xn >> s);+    let cross_ok = |nrs: usize| -> bool {         if nrs == rs {             return true;         }@@ -474,57 +486,47 @@ fn sao_ctb<S: Sample>(         }         true     };+    // avail[dy][dx]: the CTB at (rx + dx - 1, ry + dy - 1) may be used.+    let (rx, ry) = (rs % pic.wctb, rs / pic.wctb);+    let mut avail = [[false; 3]; 3];+    for (dy, row) in avail.iter_mut().enumerate() {+        for (dx, a) in row.iter_mut().enumerate() {+            let inside = (dx != 0 || x0 > 0) && (dx != 2 || x1 < w) && (dy != 0 || y0 > 0) && (dy != 2 || y1 < h);+            *a = inside && cross_ok((ry + dy - 1) * pic.wctb + rx + dx - 1);+        }+    }     let edge_map = [1usize, 2, 0, 3, 4];     // Offset per (2 + sum of neighbour signs); edgeIdx 0 adds nothing.     let by_sum = edge_map.map(|e| if e == 0 { 0 } else { offs[e - 1] });-    // Any sample: neighbours outside the picture or in a CTB that may not be used leave it as is.-    let general = |x: usize, y: usize| -> Option<S> {-        if any_nofilter && nofilter(pic, x, y) {-            return None;-        }-        let mut sum = 0i32;-        let v = pic.planes[c][y * w + x].to_i32();-        for k in 0..2 {-            let xn = x as i32 + hp[k];-            let yn = y as i32 + vp[k];-            if xn < 0 || yn < 0 || xn >= w as i32 || yn >= h as i32 {-                return None;-            }-            let (xn, yn) = (xn as usize, yn as usize);-            if (xn < x0 || xn >= x1 || yn < y0 || yn >= y1) && !cross_ok(pic, xn, yn) {-                return None;-            }-            sum += (v - pic.planes[c][yn * w + xn].to_i32()).signum();+    let side = |v: isize, lo: usize, hi: usize| {+        if v < lo as isize {+            0+        } else if v >= hi as isize {+            2+        } else {+            1         }-        Some(S::from_i32((v + by_sum[(2 + sum) as usize]).clamp(0, max)))     };-    // Samples whose two neighbours lie inside this CTB need none of those checks: a tight loop-    // (same arithmetic) for them, the general path for the CTB border.-    let (mx, my) = ((hp[0] != 0) as usize, (vp[0] != 0) as usize);+    let mx = (hp[0] != 0) as usize;     let plane = &pic.planes[c];     for y in y0..y1 {-        let inner_row = !any_nofilter && y >= y0 + my && y + my < y1 && x1 - x0 > 2 * mx;-        if !inner_row {-            for x in x0..x1 {-                if let Some(v) = general(x, y) {-                    dst[(y - y0) * w + x] = v;-                }+        let (ya, yb) = (y as isize + vp[0], y as isize + vp[1]);+        let (dya, dyb) = (side(ya, y0, y1), side(yb, y0, y1));+        for (xs, xe) in [(x0, x0 + mx), (x0 + mx, x1 - mx), (x1 - mx, x1)] {+            if xs >= xe {+                continue;             }-            continue;-        }-        for x in (x0..x0 + mx).chain(x1 - mx..x1) {-            if let Some(v) = general(x, y) {-                dst[(y - y0) * w + x] = v;+            let (xa, xb) = (xs as isize + hp[0], xs as isize + hp[1]);+            let n = xe - xs;+            if !avail[dya][side(xa, x0, x1)] || !avail[dyb][side(xb, x0, x1)] {+                continue;             }+            let cur = &plane[y * w + xs..][..n];+            let na = &plane[ya as usize * w + xa as usize..][..n];+            let nb = &plane[yb as usize * w + xb as usize..][..n];+            let out = &mut dst[(y - y0) * w + xs..][..n];+            sao_edge_run(cur, na, nb, out, &by_sum, max);         }-        let (ya, yb) = ((y as i32 + vp[0]) as usize, (y as i32 + vp[1]) as usize);-        let (xa, xb) = (x0 as i32 + mx as i32 + hp[0], x0 as i32 + mx as i32 + hp[1]);-        let n = x1 - x0 - 2 * mx;-        let cur = &plane[y * w + x0 + mx..][..n];-        let na = &plane[ya * w + xa as usize..][..n];-        let nb = &plane[yb * w + xb as usize..][..n];-        let out = &mut dst[(y - y0) * w + x0 + mx..][..n];-        sao_edge_run(cur, na, nb, out, &by_sum, max);     } }