1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
//! Golden-image pixel-diff comparison for the headless render pipeline (P0-J).
//!
//! GPU rasterization is not bit-exact across drivers and backends, so a golden test
//! cannot demand byte equality. This module compares a rendered frame to a reference
//! under an explicit [`Tolerance`]: a per-channel difference threshold and a cap on
//! the fraction of pixels allowed to exceed it. The comparison is a pure function
//! over 8-bit, 4-channel byte buffers (the format the off-screen readback produces),
//! so it runs identically on every platform.
use anyhow::{Result, bail};
/// How much a rendered frame may differ from its golden reference and still pass.
///
/// A pixel "fails" when its largest absolute per-channel difference exceeds
/// [`Tolerance::per_channel`]; the frame passes when the fraction of failing pixels
/// does not exceed [`Tolerance::max_failing_fraction`]. The two knobs separate
/// *how far off* a pixel may be from *how many* pixels may be off — anti-aliased
/// edges produce a few large-diff pixels even on a correct render.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Tolerance {
/// Maximum allowed absolute per-channel difference before a pixel is "failing".
pub per_channel: u8,
/// Maximum fraction (`0.0..=1.0`) of failing pixels the frame may contain.
pub max_failing_fraction: f64,
}
impl Tolerance {
/// Demand an exact, byte-identical match (used for CPU reference oracles).
pub const fn exact() -> Self {
Self {
per_channel: 0,
max_failing_fraction: 0.0,
}
}
/// A lenient policy suited to GPU rasterization differences: small per-channel
/// drift on up to 1% of pixels (typically anti-aliased edges).
pub const fn gpu() -> Self {
Self {
per_channel: 2,
max_failing_fraction: 0.01,
}
}
}
/// The outcome of comparing a rendered frame to a golden reference.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DiffReport {
/// Frame width in pixels.
pub width: u32,
/// Frame height in pixels.
pub height: u32,
/// Total pixels compared (`width * height`).
pub total_pixels: u64,
/// Pixels whose largest per-channel difference exceeded the tolerance threshold.
pub failing_pixels: u64,
/// Largest single per-channel difference seen anywhere in the frame.
pub max_channel_diff: u8,
/// Mean per-channel difference across every channel of every pixel.
pub mean_channel_diff: f64,
}
impl DiffReport {
/// The fraction of pixels that exceeded the per-channel threshold.
pub fn failing_fraction(&self) -> f64 {
if self.total_pixels == 0 {
return 0.0;
}
self.failing_pixels as f64 / self.total_pixels as f64
}
/// Whether the rendered frame is within tolerance of the golden reference.
pub fn passes(&self, tolerance: &Tolerance) -> bool {
self.failing_fraction() <= tolerance.max_failing_fraction
}
}
/// Compare a rendered frame to a golden reference under `tolerance`.
///
/// Both buffers must be 8-bit, 4-channel, tightly packed at `width * height * 4`
/// bytes; the channel order is irrelevant as long as both buffers share it. A pixel
/// is counted as failing when the maximum absolute difference across its four
/// channels exceeds [`Tolerance::per_channel`].
///
/// Returns an error if the dimensions are zero or either buffer length is wrong.
pub fn compare(
actual: &[u8],
expected: &[u8],
width: u32,
height: u32,
tolerance: &Tolerance,
) -> Result<DiffReport> {
if width == 0 || height == 0 {
bail!("golden comparison requires non-zero dimensions");
}
let expected_len = width as usize * height as usize * 4;
if actual.len() != expected_len {
bail!(
"actual buffer is {} bytes, expected {} ({}x{}x4)",
actual.len(),
expected_len,
width,
height
);
}
if expected.len() != expected_len {
bail!(
"reference buffer is {} bytes, expected {} ({}x{}x4)",
expected.len(),
expected_len,
width,
height
);
}
let total_pixels = width as u64 * height as u64;
let mut failing_pixels = 0u64;
let mut max_channel_diff = 0u8;
let mut diff_sum = 0u64;
for (actual_pixel, expected_pixel) in actual.chunks_exact(4).zip(expected.chunks_exact(4)) {
let mut pixel_max = 0u8;
for channel in 0..4 {
let diff = actual_pixel[channel].abs_diff(expected_pixel[channel]);
diff_sum += diff as u64;
if diff > pixel_max {
pixel_max = diff;
}
}
if pixel_max > max_channel_diff {
max_channel_diff = pixel_max;
}
if pixel_max > tolerance.per_channel {
failing_pixels += 1;
}
}
let mean_channel_diff = diff_sum as f64 / (total_pixels as f64 * 4.0);
Ok(DiffReport {
width,
height,
total_pixels,
failing_pixels,
max_channel_diff,
mean_channel_diff,
})
}
/// Build a solid-color reference buffer for tests that render a known fill.
///
/// `channels` is written verbatim for every pixel, so the caller chooses the byte
/// order (e.g. `[b, g, r, a]` to match a BGRA readback).
pub fn solid_reference(width: u32, height: u32, channels: [u8; 4]) -> Vec<u8> {
let mut buffer = Vec::with_capacity(width as usize * height as usize * 4);
for _ in 0..(width as u64 * height as u64) {
buffer.extend_from_slice(&channels);
}
buffer
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identical_buffers_have_zero_diff_and_pass_exact() {
let frame = solid_reference(4, 4, [10, 20, 30, 255]);
let report = compare(&frame, &frame, 4, 4, &Tolerance::exact()).unwrap();
assert_eq!(report.failing_pixels, 0);
assert_eq!(report.max_channel_diff, 0);
assert_eq!(report.mean_channel_diff, 0.0);
assert!(report.passes(&Tolerance::exact()));
}
#[test]
fn single_off_pixel_is_counted_and_gated_by_fraction() {
let reference = solid_reference(2, 2, [0, 0, 0, 255]);
let mut actual = reference.clone();
// Nudge the first pixel's green channel by 5.
actual[1] = 5;
let report = compare(&actual, &reference, 2, 2, &Tolerance::exact()).unwrap();
assert_eq!(report.failing_pixels, 1);
assert_eq!(report.total_pixels, 4);
assert_eq!(report.max_channel_diff, 5);
assert_eq!(report.failing_fraction(), 0.25);
// Exact tolerance rejects any failing pixel.
assert!(!report.passes(&Tolerance::exact()));
// Allowing up to 25% failing pixels passes; allowing the diff per-channel
// would not even count it as failing.
assert!(report.passes(&Tolerance {
per_channel: 0,
max_failing_fraction: 0.25
}));
let lenient = compare(
&actual,
&reference,
2,
2,
&Tolerance {
per_channel: 5,
max_failing_fraction: 0.0,
},
)
.unwrap();
assert_eq!(lenient.failing_pixels, 0);
assert!(lenient.passes(&Tolerance {
per_channel: 5,
max_failing_fraction: 0.0
}));
}
#[test]
fn mean_diff_averages_over_all_channels() {
let reference = solid_reference(1, 1, [0, 0, 0, 0]);
let actual = vec![4u8, 0, 0, 0];
let report = compare(&actual, &reference, 1, 1, &Tolerance::exact()).unwrap();
// One channel off by 4 across four channels => mean 1.0.
assert_eq!(report.mean_channel_diff, 1.0);
assert_eq!(report.max_channel_diff, 4);
}
#[test]
fn gpu_tolerance_absorbs_small_sparse_drift() {
// 100 pixels, two of them off by 1 — within the 1% / per-channel-2 GPU policy.
let reference = solid_reference(10, 10, [128, 128, 128, 255]);
let mut actual = reference.clone();
actual[0] = 129;
let report = compare(&actual, &reference, 10, 10, &Tolerance::gpu()).unwrap();
assert_eq!(
report.failing_pixels, 0,
"diff of 1 is within per_channel=2"
);
assert!(report.passes(&Tolerance::gpu()));
}
#[test]
fn wrong_buffer_length_is_an_error() {
let reference = solid_reference(4, 4, [0, 0, 0, 0]);
let short = vec![0u8; 8];
assert!(compare(&short, &reference, 4, 4, &Tolerance::exact()).is_err());
assert!(compare(&reference, &reference, 0, 4, &Tolerance::exact()).is_err());
}
}