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
@@ -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); } }