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
//! `cie_1931_cmf` ULP-budget self-test: [`build_cmf_lambdas`]'s dense wavelength sweep
//! plus adversarial points, dispatched by [`run_cmf`].
use crate::;
use ;
/// ULP budget for `cie_1931_cmf`.
///
/// P4: `cie_1931_cmf` is now a 5nm-table lookup with a single-`t` linear interpolation
/// (`lo + (hi - lo) * t`), not the old six-lobe Gaussian fit this budget was originally
/// calibrated against (each lobe chained its own `exp`/`fma`, so the old budget absorbed
/// accumulated rounding across six transcendental evaluations). The table port has only
/// one subtract-multiply-add chain per component, so it should need a narrower budget
/// than this in practice -- kept unchanged (rather than guessed tighter) as a
/// conservative ceiling pending a fresh measurement on live GPU hardware after this
/// port; see [`build_cmf_lambdas`] for the case set that measurement should exercise.
pub const CMF_ULP_BUDGET: u32 = 32;
/// Absolute-difference floor exempting near-zero comparisons from [`CMF_ULP_BUDGET`]
/// entirely.
///
/// See [`crate::renderer::gpu::ulp::within_tolerance`]'s doc comment for why ULP is the
/// wrong metric there. P4: unlike the old Gaussian fit (whose tails decayed smoothly
/// toward zero, e.g. as small as ~1e-24 far outside the visible range),
/// [`crate::color::cie1931::cie_1931_cmf`]'s table lookup returns EXACTLY zero for any
/// wavelength outside 380-780nm by construction (see that function's own
/// `out_of_range_wavelengths_return_zero` test) -- so an in-range/out-of-range
/// disagreement between CPU and GPU would show up as a large absolute difference, not a
/// near-zero one this floor could mask. This floor now exists only to absorb ordinary
/// near-zero rounding-direction noise at the table's own smallest tabulated entries
/// (e.g. `z_bar` sits at exactly `0.0000` for 630nm and above), several orders of
/// magnitude below anything a renderer could visibly distinguish.
pub const CMF_ABS_FLOOR: f32 = 1e-6;
/// Dense wavelength sweep plus adversarial points.
///
/// P4: `cie_1931_cmf` is now [`crate::color::cie1931::CIE_1931_TABLE`], a 5nm table
/// (380-780nm) with linear interpolation, not the old Gaussian-lobe fit this case set
/// was originally built for -- the old per-lobe-mean adversarial points (`x_bar`:
/// 442.0, 599.8, 501.1; `y_bar`: 530.9, 568.8; `z_bar`: 459.0, 437.0, where the old fit's
/// piecewise sigma switched) test nothing about a table lookup, so they are replaced
/// below with points meaningful to THIS implementation: every table node (exact lookup,
/// `t == 0.0`), every node's interpolation midpoint (worst-case interpolation, `t ==
/// 0.5`), the two in-range boundary wavelengths (380.0/780.0 -- distinct from the
/// epsilon-just-outside probes below), and epsilon-scale probes just inside/outside the
/// tabulated range -- the single most failure-prone points for a from-scratch WGSL port
/// of this function's `>=`/`<=` range test and `floor`-based index computation (mirrors
/// `cie1931.rs`'s own `boundary_wavelengths_return_the_table_edges_exactly` and
/// `out_of_range_wavelengths_return_zero` tests). The pre-existing dense 0.5nm-step
/// sweep from 300nm to 850nm is kept too: `TABLE_STEP_NM` = 5nm is an exact multiple of
/// that 0.5nm step, so the dense sweep already lands on every table node and every
/// midpoint as well, plus it alone covers the far out-of-range tail this function must
/// also return exactly zero for.
/// Runs the `cie_1931_cmf` ULP-budget self-test against a live GPU.
///
/// # Panics
///
/// Panics on `wgpu` API misuse (see [`crate::renderer::gpu::layout_check::run`]'s doc
/// comment for the same rationale).