rusty_h264_encoder/
bitacct.rs1use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
23
24#[derive(Clone, Copy)]
26pub enum B {
27 SkipFlag = 0,
28 MbType = 1,
29 RefIdx = 2,
30 Mvd = 3,
31 IntraBody = 4,
32 Cbp = 5,
33 QpDelta = 6,
34 ResidLuma = 7,
35 ResidChroma = 8,
36 Terminate = 9,
37 MvdBypass = 10,
41 IntraResid = 11,
43 MvdSign = 12,
45}
46
47pub const N: usize = 13;
48const NAMES: [&str; N] = [
49 "mb_skip_flag",
50 "mb_type/sub_type",
51 "ref_idx",
52 "mvd (MOTION)",
53 "intra MB body (I+P)",
54 "cbp",
55 "mb_qp_delta",
56 "residual luma (TEX)",
57 "residual chroma (TEX)",
58 "end_of_slice",
59 " └ of which mvd bypass",
60 "intra residual (TEX)",
61 " └ of which mvd SIGNS",
62];
63
64static ON: AtomicBool = AtomicBool::new(false);
65static BITS: [AtomicU64; N] = [const { AtomicU64::new(0) }; N];
66static COUNT: [AtomicU64; N] = [const { AtomicU64::new(0) }; N];
67static ACTUAL: AtomicU64 = AtomicU64::new(0);
69
70#[inline]
71pub fn enabled() -> bool {
72 ON.load(Ordering::Relaxed)
73}
74
75pub fn set_enabled(on: bool) {
77 ON.store(on, Ordering::Relaxed);
78}
79
80pub fn init_from_env() {
81 if std::env::var("RFF_BITACCT").map(|v| v != "0").unwrap_or(false) {
82 set_enabled(true);
83 }
84}
85
86#[inline]
87pub fn add(b: B, bits: u64) {
88 BITS[b as usize].fetch_add(bits, Ordering::Relaxed);
89 COUNT[b as usize].fetch_add(1, Ordering::Relaxed);
90}
91
92pub fn add_actual_bytes(n: usize) {
94 ACTUAL.fetch_add(n as u64 * 8, Ordering::Relaxed);
95}
96
97pub fn reset() {
98 for i in 0..N {
99 BITS[i].store(0, Ordering::Relaxed);
100 COUNT[i].store(0, Ordering::Relaxed);
101 }
102 ACTUAL.store(0, Ordering::Relaxed);
103}
104
105pub fn dump(label: &str, mbs: u64) {
108 let vals: Vec<u64> = (0..N).map(|i| BITS[i].load(Ordering::Relaxed)).collect();
109 let cnts: Vec<u64> = (0..N).map(|i| COUNT[i].load(Ordering::Relaxed)).collect();
110 let accounted: u64 = vals.iter().sum();
111 let actual = ACTUAL.load(Ordering::Relaxed);
112 println!("\n=== BIT ACCOUNTANT — {label} ===");
113 println!(
114 "{:<24}{:>12}{:>9}{:>12}{:>12}",
115 "syntax element", "bits", "share", "bits/MB", "elements"
116 );
117 println!("{}", "-".repeat(69));
118 for i in 0..N {
119 if cnts[i] == 0 && vals[i] == 0 {
120 continue;
121 }
122 println!(
123 "{:<24}{:>12}{:>8.1}%{:>12.1}{:>12}",
124 NAMES[i],
125 vals[i],
126 100.0 * vals[i] as f64 / (accounted - vals[B::MvdBypass as usize] - vals[B::IntraResid as usize] - vals[B::MvdSign as usize]).max(1) as f64,
127 vals[i] as f64 / mbs.max(1) as f64,
128 cnts[i]
129 );
130 }
131 println!("{}", "-".repeat(69));
132 let sub = vals[B::MvdBypass as usize] + vals[B::IntraResid as usize] + vals[B::MvdSign as usize];
136 let accounted = accounted - sub;
137 let mv = vals[B::Mvd as usize] + vals[B::RefIdx as usize];
138 let tex = vals[B::ResidLuma as usize] + vals[B::ResidChroma as usize] + vals[B::IntraResid as usize];
139 let x264_syntax = accounted - tex - vals[B::Terminate as usize];
141 let misc = accounted - mv - tex;
142 let _ = misc;
143 let pc = |v: u64| 100.0 * v as f64 / accounted.max(1) as f64;
144 println!(
145 "x264-comparable: NON-RESIDUAL SYNTAX {:.1}% (of which mvd {:.1}%) TEXTURE {:.1}% hdr/term {:.1}%",
146 pc(x264_syntax),
147 pc(mv),
148 pc(tex),
149 pc(vals[B::Terminate as usize])
150 );
151 println!(
153 "reconciliation: accounted {accounted} / actual {actual} = {:.1}% (residue {} bits = slice headers + NAL + flush)",
154 100.0 * accounted as f64 / actual.max(1) as f64,
155 actual as i64 - accounted as i64
156 );
157}
158
159pub const MVD_K: usize = 65; static MVD_BITS: [AtomicU64; MVD_K] = [const { AtomicU64::new(0) }; MVD_K];
166static MVD_CNT: [AtomicU64; MVD_K] = [const { AtomicU64::new(0) }; MVD_K];
167
168#[inline]
169pub fn add_mvd_sample(abs_d: u32, bits: u64) {
170 let k = (abs_d as usize).min(MVD_K - 1);
171 MVD_BITS[k].fetch_add(bits, Ordering::Relaxed);
172 MVD_CNT[k].fetch_add(1, Ordering::Relaxed);
173}
174
175pub fn dump_mvd_table() {
176 println!("|d|,count,avg_bits");
177 for k in 0..MVD_K {
178 let (b, c) = (MVD_BITS[k].load(Ordering::Relaxed), MVD_CNT[k].load(Ordering::Relaxed));
179 if c > 0 {
180 println!("{k},{c},{:.3}", b as f64 / c as f64);
181 }
182 }
183}