Browse / github.com/storytold/filmcraft / patch
hevc: intra planar/angular with const size and hoisted branches
hevc: intra angular/planar with const size and hoisted branches 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. **Intra angular and planar: runtime n, per-sample branches, strided stores** (`intra.rs:104-192`; lines 164-176 ~22% of `intra_predic
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- vangel76 (imported from pull request #3189, open)
- Base
bfc23840b603e69c782302c6ed04d4a9f6ec26a8- Size
- +175 −71 in 1 file
- Review
- pending
- Shared
- 2026-10-28
- Id
a1071294bb55d76f2b4b7a7a
How it went
No other operator has reported using it yet.
Diff
raw · crates/hevc/src/intra.rs
@@ -102,91 +102,121 @@ pub fn filter(r: &mut Refs, mode: u32, n: usize, strong_enabled: bool, is_luma: /// Predict an n x n block with `mode` into `out` (stride `os`). `edge_filters`: DC / horizontal / /// vertical boundary smoothing (luma blocks smaller than 32x32). pub fn predict<S: Sample>(r: &Refs, mode: u32, n: usize, edge_filters: bool, bit_depth: u32, out: &mut [S], os: usize) {- let max = (1i32 << bit_depth) - 1;+ match n {+ 4 => predict_n::<S, 4>(r, mode, edge_filters, bit_depth, out, os),+ 8 => predict_n::<S, 8>(r, mode, edge_filters, bit_depth, out, os),+ 16 => predict_n::<S, 16>(r, mode, edge_filters, bit_depth, out, os),+ _ => predict_n::<S, 32>(r, mode, edge_filters, bit_depth, out, os),+ }+}++#[inline(always)]+fn predict_n<S: Sample, const N: usize>(r: &Refs, mode: u32, edge_filters: bool, bit_depth: u32, out: &mut [S], os: usize) { match mode {- 0 => {- // planar- let log2 = n.trailing_zeros();- let tr = r.top[1 + n] as i32;- let bl = r.left[1 + n] as i32;- for y in 0..n {- let l = r.left[1 + y] as i32;- for x in 0..n {- let t = r.top[1 + x] as i32;- let v = ((n - 1 - x) as i32 * l + (x as i32 + 1) * tr + (n - 1 - y) as i32 * t + (y as i32 + 1) * bl + n as i32) >> (log2 + 1);- out[y * os + x] = S::from_i32(v);- }- }- }+ 0 => planar::<S, N>(r, out, os), 1 => {- let log2 = n.trailing_zeros();- let sum: u32 = r.top[1..=n].iter().map(|&v| v as u32).sum::<u32>() + r.left[1..=n].iter().map(|&v| v as u32).sum::<u32>();- let dc = ((sum + n as u32) >> (log2 + 1)) as u16;+ let log2 = N.trailing_zeros();+ let sum: u32 = r.top[1..=N].iter().map(|&v| v as u32).sum::<u32>() + r.left[1..=N].iter().map(|&v| v as u32).sum::<u32>();+ let dc = ((sum + N as u32) >> (log2 + 1)) as u16; let dcs = S::from_u16(dc);- for y in 0..n {- out[y * os..y * os + n].fill(dcs);+ for y in 0..N {+ out[y * os..y * os + N].fill(dcs); }- if edge_filters && n < 32 {+ if edge_filters && N < 32 { let d = dc as u32; out[0] = S::from_i32(((r.left[1] as u32 + 2 * d + r.top[1] as u32 + 2) >> 2) as i32);- for x in 1..n {+ for x in 1..N { out[x] = S::from_i32(((r.top[1 + x] as u32 + 3 * d + 2) >> 2) as i32); }- for y in 1..n {+ for y in 1..N { out[y * os] = S::from_i32(((r.left[1 + y] as u32 + 3 * d + 2) >> 2) as i32); } } }- _ => {- let angle = INTRA_PRED_ANGLE[mode as usize] as i32;- // reference array ref[-n..=2n] stored at offset n- let mut refa = [0i32; 3 * 64 + 1];- let base = n as i32;- let (main, side) = if mode >= 18 { (&r.top, &r.left) } else { (&r.left, &r.top) };- for x in 0..=n {- refa[(base + x as i32) as usize] = main[x] as i32; // ref[x] = p[-1+x][-1]- }- if angle < 0 {- let lim = (n as i32 * angle) >> 5;- if lim < -1 {- let inv = INV_ANGLE[(mode - 11) as usize] as i32;- for x in lim..=-1 {- let idx = -1 + ((x * inv + 128) >> 8);- refa[(base + x) as usize] = side[(idx + 1) as usize] as i32;- }- }- } else {- for x in n + 1..=2 * n {- refa[(base + x as i32) as usize] = main[x] as i32;- }+ _ => angular::<S, N>(r, mode, edge_filters, bit_depth, out, os),+ }+}++fn planar<S: Sample, const N: usize>(r: &Refs, out: &mut [S], os: usize) {+ let log2 = N.trailing_zeros();+ let tr = r.top[1 + N] as i32;+ let bl = r.left[1 + N] as i32;+ let top = &r.top[1..1 + N];+ for y in 0..N {+ let l = r.left[1 + y] as i32;+ let (wt, wb) = ((N - 1 - y) as i32, (y + 1) as i32);+ for (x, (o, &t)) in out[y * os..y * os + N].iter_mut().zip(top).enumerate() {+ let v = (N - 1 - x) as i32 * l + (x as i32 + 1) * tr + wt * t as i32 + wb * bl + N as i32;+ *o = S::from_i32(v >> (log2 + 1));+ }+ }+}++/// Angular modes 2..34 (8.4.4.2.6). Horizontal modes (< 18) are predicted as the vertical mode+/// on the transposed references into a tile, then transposed into `out`.+/// Row `j` of a vertical angular prediction from `ref[x] = refs[base + x]`.+#[inline(always)]+fn angular_row<const N: usize>(refs: &[u16], base: i32, angle: i32, j: usize) -> [u16; N] {+ let pos = (j as i32 + 1) * angle;+ let fact = (pos & 31) as u32;+ let p = &refs[(base + (pos >> 5) + 1) as usize..][..N + 1];+ let mut v = [0u16; N];+ if fact == 0 {+ v.copy_from_slice(&p[..N]);+ } else {+ for (v, (&a, &b)) in v.iter_mut().zip(p.iter().zip(&p[1..])) {+ *v = (((32 - fact) * a as u32 + fact * b as u32 + 16) >> 5) as u16;+ }+ }+ v+}++fn angular<S: Sample, const N: usize>(r: &Refs, mode: u32, edge_filters: bool, bit_depth: u32, out: &mut [S], os: usize) {+ let angle = INTRA_PRED_ANGLE[mode as usize] as i32;+ let (main, side) = if mode >= 18 { (&r.top, &r.left) } else { (&r.left, &r.top) };+ // ref[x] = refs[base + x] for x = -N..=2N; ref[x] = main[x] for x >= 0+ let mut ext = [0u16; 3 * 32 + 1];+ let (refs, base): (&[u16], i32) = if angle < 0 {+ ext[N..=2 * N].copy_from_slice(&main[..=N]);+ let lim = (N as i32 * angle) >> 5;+ if lim < -1 {+ let inv = INV_ANGLE[(mode - 11) as usize] as i32;+ for x in lim..=-1 {+ ext[(N as i32 + x) as usize] = side[((x * inv + 128) >> 8) as usize]; }- let horizontal = mode < 18;- for j in 0..n {- let pos = (j as i32 + 1) * angle;- let idx = pos >> 5;- let fact = pos & 31;- for i in 0..n {- let k = (base + i as i32 + idx + 1) as usize;- let v = if fact != 0 { ((32 - fact) * refa[k] + fact * refa[k + 1] + 16) >> 5 } else { refa[k] };- if horizontal {- out[i * os + j] = S::from_i32(v);- } else {- out[j * os + i] = S::from_i32(v);- }- }+ }+ (&ext, N as i32)+ } else {+ (main, 0)+ };+ if mode >= 18 {+ for j in 0..N {+ for (o, v) in out[j * os..j * os + N].iter_mut().zip(angular_row::<N>(refs, base, angle, j)) {+ *o = S::from_u16(v); }- if edge_filters && n < 32 {- if mode == 26 {- for y in 0..n {- let v = r.top[1] as i32 + ((r.left[1 + y] as i32 - r.left[0] as i32) >> 1);- out[y * os] = S::from_i32(v.clamp(0, max));- }- } else if mode == 10 {- for x in 0..n {- let v = r.left[1] as i32 + ((r.top[1 + x] as i32 - r.top[0] as i32) >> 1);- out[x] = S::from_i32(v.clamp(0, max));- }- }+ }+ } else {+ let mut tile = [[0u16; N]; N];+ for (j, t) in tile.iter_mut().enumerate() {+ *t = angular_row::<N>(refs, base, angle, j);+ }+ for y in 0..N {+ for (o, t) in out[y * os..y * os + N].iter_mut().zip(&tile) {+ *o = S::from_u16(t[y]);+ }+ }+ }+ if edge_filters && N < 32 {+ let max = (1i32 << bit_depth) - 1;+ if mode == 26 {+ for y in 0..N {+ let v = r.top[1] as i32 + ((r.left[1 + y] as i32 - r.left[0] as i32) >> 1);+ out[y * os] = S::from_i32(v.clamp(0, max));+ }+ } else if mode == 10 {+ for x in 0..N {+ let v = r.left[1] as i32 + ((r.top[1 + x] as i32 - r.top[0] as i32) >> 1);+ out[x] = S::from_i32(v.clamp(0, max)); } } }@@ -238,6 +268,80 @@ mod tests { assert_eq!(out[4 + 1], r.top[1 + 1 + 1 + 1]); } + /// The previous per-sample implementation of planar and angular prediction (8.4.4.2.4, 8.4.4.2.6).+ fn reference(r: &Refs, mode: u32, n: usize, out: &mut [u16]) {+ if mode == 0 {+ let log2 = n.trailing_zeros();+ let (tr, bl) = (r.top[1 + n] as i32, r.left[1 + n] as i32);+ for y in 0..n {+ for x in 0..n {+ let (l, t) = (r.left[1 + y] as i32, r.top[1 + x] as i32);+ out[y * n + x] =+ (((n - 1 - x) as i32 * l + (x as i32 + 1) * tr + (n - 1 - y) as i32 * t + (y as i32 + 1) * bl + n as i32) >> (log2 + 1)) as u16;+ }+ }+ return;+ }+ let angle = INTRA_PRED_ANGLE[mode as usize] as i32;+ let mut refa = [0i32; 3 * 64 + 1];+ let base = n as i32;+ let (main, side) = if mode >= 18 { (&r.top, &r.left) } else { (&r.left, &r.top) };+ for x in 0..=n {+ refa[(base + x as i32) as usize] = main[x] as i32;+ }+ if angle < 0 {+ let lim = (n as i32 * angle) >> 5;+ if lim < -1 {+ let inv = INV_ANGLE[(mode - 11) as usize] as i32;+ for x in lim..=-1 {+ refa[(base + x) as usize] = side[((x * inv + 128) >> 8) as usize] as i32;+ }+ }+ } else {+ for x in n + 1..=2 * n {+ refa[(base + x as i32) as usize] = main[x] as i32;+ }+ }+ for j in 0..n {+ let pos = (j as i32 + 1) * angle;+ let (idx, fact) = (pos >> 5, pos & 31);+ for i in 0..n {+ let k = (base + i as i32 + idx + 1) as usize;+ let v = if fact != 0 { ((32 - fact) * refa[k] + fact * refa[k + 1] + 16) >> 5 } else { refa[k] };+ if mode < 18 {+ out[i * n + j] = v as u16;+ } else {+ out[j * n + i] = v as u16;+ }+ }+ }+ }++ #[test]+ fn planar_and_angular_match_reference() {+ let mut rng = 0x9e37_79b9_7f4a_7c15u64;+ let mut next = move || {+ rng ^= rng << 13;+ rng ^= rng >> 7;+ rng ^= rng << 17;+ rng+ };+ for bit_depth in [8u32, 10, 12] {+ for n in [4usize, 8, 16, 32] {+ for _ in 0..4 {+ let v: Vec<u16> = (0..257).map(|_| (next() % (1 << bit_depth)) as u16).collect();+ let r = refs(|i| v[(i + 128) as usize]);+ for mode in (0..35).filter(|&m| m != 1) {+ let (mut a, mut b) = (vec![0u16; n * n], vec![0u16; n * n]);+ predict(&r, mode, n, false, bit_depth, &mut a, n);+ reference(&r, mode, n, &mut b);+ assert_eq!(a, b, "n {n} mode {mode} bit depth {bit_depth}");+ }+ }+ }+ }+ }+ #[test] fn substitution() { let mut r = Refs::default();