use crate::{optics::raytracer::blackbody_spectrum, renderer::gpu::compute};
use super::{SHADER_SRC, UlpAccumulator, UlpCheckResult};
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct BlackbodyCase {
lambda_nm: f32,
temp_k: f32,
}
pub const BLACKBODY_ULP_BUDGET: u32 = 48;
pub const BLACKBODY_ABS_FLOOR: f32 = 1e-5;
#[must_use]
pub fn build_blackbody_cases() -> Vec<BlackbodyCase> {
let mut cases = Vec::new();
let temps = [
1000.0f32, 1500.0, 2000.0, 3200.0, 5000.0, 6500.0, 6600.0, 8000.0, 10_000.0,
];
let lambda_steps = ((780.0 - 380.0) / 15.0) as u32;
for step in 0..=lambda_steps {
let lambda = (step as f32).mul_add(15.0, 380.0);
for &temp_k in &temps {
cases.push(BlackbodyCase {
lambda_nm: lambda,
temp_k,
});
}
}
for &temp_k in &[100.0f32, 500.0, 999.0, 1000.0, 1000.1] {
cases.push(BlackbodyCase {
lambda_nm: 560.0,
temp_k,
});
cases.push(BlackbodyCase {
lambda_nm: 380.0,
temp_k,
});
cases.push(BlackbodyCase {
lambda_nm: 780.0,
temp_k,
});
}
cases
}
#[must_use]
pub fn run_blackbody(ctx: &crate::renderer::gpu::GpuContext) -> UlpCheckResult<BlackbodyCase> {
let cases = build_blackbody_cases();
let total = cases.len();
let in_buf = compute::upload(
&ctx.device,
"blackbody cases",
&cases,
wgpu::BufferUsages::STORAGE,
);
let out_buf = compute::zeroed_buffer::<f32>(
&ctx.device,
"blackbody out",
total,
wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
);
let pipeline = compute::create_compute_pipeline(
&ctx.device,
"blackbody_main",
SHADER_SRC,
"blackbody_main",
);
let bind_group = compute::bind_buffers(
&ctx.device,
"blackbody bind group",
&pipeline,
&[(2, &in_buf), (3, &out_buf)],
);
let workgroups = (total as u32).div_ceil(64);
compute::dispatch_and_wait(
&ctx.device,
&ctx.queue,
&pipeline,
&bind_group,
(workgroups, 1, 1),
);
let gpu_out: Vec<f32> = compute::readback(&ctx.device, &ctx.queue, &out_buf, total);
let mut acc = UlpAccumulator::new(
"blackbody_spectrum",
BLACKBODY_ULP_BUDGET,
BLACKBODY_ABS_FLOOR,
);
for (idx, case) in cases.iter().enumerate() {
let cpu = blackbody_spectrum(case.lambda_nm, case.temp_k);
acc.record(case, "value", cpu, gpu_out[idx]);
}
acc.finish()
}