use glam::Vec3;
mod blackbody;
mod cmf;
mod hdr_env;
mod studio_env;
mod white_balance;
pub use blackbody::{BLACKBODY_ABS_FLOOR, BLACKBODY_ULP_BUDGET, BlackbodyCase, run_blackbody};
pub use cmf::{CMF_ABS_FLOOR, CMF_ULP_BUDGET, build_cmf_lambdas, run_cmf};
pub(crate) use hdr_env::build_hdr_test_map;
pub use hdr_env::{
HDR_ENV_ABS_FLOOR, HDR_ENV_ULP_BUDGET, HdrEnvCase, build_hdr_env_cases, run_hdr_env_radiance,
};
pub use studio_env::{
STUDIO_ENV_ABS_FLOOR, STUDIO_ENV_ULP_BUDGET, StudioEnvCase, build_studio_env_cases,
run_studio_env,
};
pub use white_balance::{WHITE_BALANCE_ABS_FLOOR, WHITE_BALANCE_ULP_BUDGET, run_white_balance};
const SHADER_SRC: &str = include_str!("../../shaders/environment.wgsl");
#[derive(Debug, Clone)]
pub struct UlpCheckResult<Case: Clone> {
pub label: &'static str,
pub total_comparisons: usize,
pub budget: u32,
pub abs_floor: f32,
pub max_ulp: u32,
pub max_raw_ulp: u32,
pub over_budget_count: usize,
pub exempted_count: usize,
pub argmax: Option<UlpArgmax<Case>>,
}
#[derive(Debug, Clone)]
pub struct UlpArgmax<Case: Clone> {
pub case: Case,
pub component: &'static str,
pub cpu: f32,
pub gpu: f32,
pub ulp: u32,
}
impl<Case: Clone> UlpCheckResult<Case> {
#[must_use]
pub const fn passed(&self) -> bool {
self.over_budget_count == 0
}
}
struct UlpAccumulator<Case: Clone> {
label: &'static str,
budget: u32,
abs_floor: f32,
total: usize,
max_ulp: u32,
max_raw_ulp: u32,
over_budget: usize,
exempted: usize,
argmax: Option<UlpArgmax<Case>>,
}
impl<Case: Clone> UlpAccumulator<Case> {
const fn new(label: &'static str, budget: u32, abs_floor: f32) -> Self {
Self {
label,
budget,
abs_floor,
total: 0,
max_ulp: 0,
max_raw_ulp: 0,
over_budget: 0,
exempted: 0,
argmax: None,
}
}
fn record(&mut self, case: &Case, component: &'static str, cpu: f32, gpu: f32) {
self.total += 1;
let ulp = crate::renderer::gpu::ulp::ulp_distance(cpu, gpu);
if ulp > self.max_raw_ulp {
self.max_raw_ulp = ulp;
}
let within =
crate::renderer::gpu::ulp::within_tolerance(cpu, gpu, self.budget, self.abs_floor);
if !within {
self.over_budget += 1;
if ulp > self.max_ulp {
self.max_ulp = ulp;
self.argmax = Some(UlpArgmax {
case: case.clone(),
component,
cpu,
gpu,
ulp,
});
}
} else if ulp > self.budget {
self.exempted += 1;
}
}
fn finish(self) -> UlpCheckResult<Case> {
UlpCheckResult {
label: self.label,
total_comparisons: self.total,
budget: self.budget,
abs_floor: self.abs_floor,
max_ulp: self.max_ulp,
max_raw_ulp: self.max_raw_ulp,
over_budget_count: self.over_budget,
exempted_count: self.exempted,
argmax: self.argmax,
}
}
}
fn fibonacci_sphere(n: usize) -> Vec<Vec3> {
let golden_angle = std::f32::consts::PI * (3.0 - 5.0_f32.sqrt());
(0..n)
.map(|i| {
let y = 1.0 - 2.0 * (i as f32) / ((n - 1).max(1) as f32);
let radius = y.mul_add(-y, 1.0).max(0.0).sqrt();
let theta = golden_angle * i as f32;
Vec3::new(theta.cos() * radius, y, theta.sin() * radius)
})
.collect()
}