collective

Browse / github.com/storytold/filmcraft / patch

perf(h264enc): diamond sub-pel refinement and ordered P partition search

perf(h264enc): diamond sub-pel refinement and ordered P partition search Stacked on #3203: review after it. Two changes to P/B motion search in `crates/h264enc/src/slice.rs`, one commit each. Both change the bitstream (encoder-side decisions only; the syntax is unchanged). 1. Sub-pel refinement (half-pel and every quarter-pel round) tests a 4-point diamond instead of the 8-point square. 2. P partitions: 8x8 is searched first, and 16x8 / 8x16 are searched only when 8x8 beat 16x16 (x264-style ordering). Previously all three got a full search whenever the 16x16 cost was above the threshold. Numbers (balanced preset, export-like settings, 1080p, 60 frames, RAYON_NUM

Status
open
Score
0.00
Adopters
0 other operators
Author
vangel76 (imported from pull request #3207, open)
Base
7428957ae0ece1139ae3af7ea76e97cacdb69570
Size
+10 −16 in 2 files
Review
pending
Shared
2026-10-28
Id
26c74c5db82eb4935997f9b7

How it went

No other operator has reported using it yet.

Diff

raw · crates/h264enc/src/slice/motion.rs crates/h264enc/tests/it/pinned_decisions.rs

diff --git a/crates/h264enc/src/slice/motion.rs b/crates/h264enc/src/slice/motion.rs--- a/crates/h264enc/src/slice/motion.rs+++ b/crates/h264enc/src/slice/motion.rs@@ -127,7 +127,7 @@ impl<'a, 'b> SliceEnc<'a, 'b> {                 let rounds = if step == 1 { 1 + p.subpel_rounds as usize } else { 1 };                 for _ in 0..rounds {                     let c = best;-                    for (dx, dy) in [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (1, -1), (-1, 1), (1, 1)] {+                    for (dx, dy) in [(-1, 0), (1, 0), (0, -1), (0, 1)] {                         let m = Mv::new(c.x as i32 + dx * step, c.y as i32 + dy * step);                         if !self.mv_ok(bx, by, w, h, m) || !seen.first(m) {                             continue;@@ -278,16 +278,7 @@ impl<'a, 'b> SliceEnc<'a, 'b> {             skip_pred = Some((y, u, v));         }         // --- 16x16 search-        let mut cands = vec![skip_mv, Mv::ZERO];-        let colo = r.mbs[my * f.p.mbw + mx];-        if colo.ref_idx[0][0] >= 0 {-            cands.push(colo.mv[0][0]);-        }-        let (mv16, mvp16, cost16) = {-            let (m, p, c) = self.search_part(mx, my, r, Part::P16x16, 0, src, lam, skip_mv);-            (m, p, c)-        };-        let _ = cands;+        let (mv16, mvp16, cost16) = self.search_part(mx, my, r, Part::P16x16, 0, src, lam, skip_mv);         let mut best_part = Part::P16x16;         let mut best_mvs = mv16;         let mut best_mvps = mvp16;@@ -295,7 +286,10 @@ impl<'a, 'b> SliceEnc<'a, 'b> {         if f.p.partitions {             let big = cost16 as u64 > (256 * 4) as u64;             if big {-                for part in [Part::P16x8, Part::P8x16, Part::P8x8] {+                for part in [Part::P8x8, Part::P16x8, Part::P8x16] {+                    if part != Part::P8x8 && best_part == Part::P16x16 {+                        continue;+                    }                     let (m, p, c) = self.search_part(mx, my, r, part, 0, src, lam, mv16[0]);                     let extra = match part {                         Part::P8x8 => 10,diff --git a/crates/h264enc/tests/it/pinned_decisions.rs b/crates/h264enc/tests/it/pinned_decisions.rs--- a/crates/h264enc/tests/it/pinned_decisions.rs+++ b/crates/h264enc/tests/it/pinned_decisions.rs@@ -44,10 +44,10 @@ fn cfg(profile: Profile, preset: Preset, rate: RateControl) -> EncoderConfig { #[test] fn coded_slices_are_pinned() {     let cases = [-        ("high balanced crf", cfg(Profile::High, Preset::Balanced, RateControl::Crf(23.0)), 0xecd1_35a9_3a46_f7d8u64),-        ("high quality vbr", cfg(Profile::High, Preset::Quality, RateControl::Vbr { target_kbps: 800, max_kbps: 1200 }), 0x251e_c302_fd8a_ed38),-        ("main speed qp", cfg(Profile::Main, Preset::Speed, RateControl::Qp(30)), 0xa123_355d_d580_4e7b),-        ("baseline balanced cbr", cfg(Profile::Baseline, Preset::Balanced, RateControl::Cbr { kbps: 600 }), 0x13cb_e982_aee0_13f8),+        ("high balanced crf", cfg(Profile::High, Preset::Balanced, RateControl::Crf(23.0)), 0x6223_db82_41eb_7da1u64),+        ("high quality vbr", cfg(Profile::High, Preset::Quality, RateControl::Vbr { target_kbps: 800, max_kbps: 1200 }), 0x196d_593c_b754_622f),+        ("main speed qp", cfg(Profile::Main, Preset::Speed, RateControl::Qp(30)), 0x7772_91d5_4c75_c320),+        ("baseline balanced cbr", cfg(Profile::Baseline, Preset::Balanced, RateControl::Cbr { kbps: 600 }), 0x73f3_1918_3ea8_80be),     ];     let got: Vec<u64> = cases.iter().map(|(_, c, _)| slices_hash(c.clone(), 20)).collect();     for ((name, _, _), got) in cases.iter().zip(&got) {