use glam::Vec3;
use crate::{
geometry::cuts::StandardGemCuts,
optics::{
materials::GemMaterial,
raytracer::{BACKDROP_GREY, Camera, FacetFinish, LightingPreset, build_plane_soa},
},
renderer::cpu_frame::{scatter_interleaved, trace_pixels_interleaved},
};
use super::{
estimator_check::bruted_girdle_finishes, frame::GpuFrameScene, hybrid::cpu_accumulate,
};
const WIDTH: u32 = 24;
const HEIGHT: u32 = 16;
fn fnv1a_hash_xyz(buf: &[Vec3]) -> u64 {
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
let mut hash = FNV_OFFSET;
for pixel in buf {
for component in [pixel.x, pixel.y, pixel.z] {
for byte in component.to_bits().to_le_bytes() {
hash ^= u64::from(byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
}
}
hash
}
fn hash_cpu_accumulate(scene: &GpuFrameScene<'_>, sample_offset: u32, spp: u32) -> u64 {
let num_pixels = scene.width as usize * scene.height as usize;
let mut accum = vec![Vec3::ZERO; num_pixels];
cpu_accumulate(scene, sample_offset, spp, &mut accum);
fnv1a_hash_xyz(&accum)
}
const fn diamond_scene<'a>(
camera: &'a Camera,
planes: &'a [crate::geometry::GpuFacetPlane],
material: &'a GemMaterial,
) -> GpuFrameScene<'a> {
GpuFrameScene {
camera,
width: WIDTH,
height: HEIGHT,
planes,
facet_finishes: &[],
material,
max_bounces: 8,
environment: LightingPreset::LightTent
.studio(1.0, 0.4, 0.35)
.with_backdrop(BACKDROP_GREY),
}
}
const fn zircon_frosted_scene<'a>(
camera: &'a Camera,
planes: &'a [crate::geometry::GpuFacetPlane],
finishes: &'a [FacetFinish],
material: &'a GemMaterial,
) -> GpuFrameScene<'a> {
GpuFrameScene {
camera,
width: WIDTH,
height: HEIGHT,
planes,
facet_finishes: finishes,
material,
max_bounces: 8,
environment: LightingPreset::Daylight
.studio(1.0, 0.5, 0.6)
.with_backdrop(BACKDROP_GREY),
}
}
#[test]
#[cfg_attr(
not(windows),
ignore = "pinned to the Windows math library's rounding; run with --ignored to read \
this platform's hashes"
)]
fn cpu_accumulate_diamond_light_tent_pin() {
let camera = Camera::new(0.35, 0.28, 5.0, 18.0);
let planes = StandardGemCuts::standard_round_brilliant();
let material = GemMaterial::by_name("Diamond").expect("Diamond is a built-in material");
let scene = diamond_scene(&camera, &planes, &material);
assert_pinned(
"diamond, light tent",
&scene,
[0xc464_d298_6d52_47d5, 0x9ebb_4ae1_6688_88ea],
);
}
#[test]
#[cfg_attr(
not(windows),
ignore = "pinned to the Windows math library's rounding; run with --ignored to read \
this platform's hashes"
)]
fn cpu_accumulate_zircon_frosted_daylight_pin() {
let camera = Camera::new(0.35, 0.28, 5.0, 18.0);
let planes = StandardGemCuts::standard_round_brilliant();
let finishes = bruted_girdle_finishes(planes.len());
let material = GemMaterial::by_name("Zircon").expect("Zircon is a built-in material");
let scene = zircon_frosted_scene(&camera, &planes, &finishes, &material);
assert_pinned(
"zircon, frosted girdle, daylight",
&scene,
[0x232a_34f3_590e_09ef, 0xeded_a667_de32_1145],
);
}
fn assert_pinned(label: &str, scene: &GpuFrameScene<'_>, expected: [u64; 2]) {
let mut changed = Vec::new();
for (offset, want) in [0u32, 5].into_iter().zip(expected) {
let got = hash_cpu_accumulate(scene, offset, 3);
if got != want {
changed.push(format!("sample_offset={offset}: {got:#018x}"));
}
}
assert!(
changed.is_empty(),
"cpu_accumulate({label}, spp=3) changed bit-for-bit; actual hashes: {}",
changed.join(", ")
);
}
fn xyz_bits(buf: &[Vec3]) -> Vec<(u32, u32, u32)> {
buf.iter()
.map(|v| (v.x.to_bits(), v.y.to_bits(), v.z.to_bits()))
.collect()
}
fn trace_whole_frame_via_stride(
scene: &GpuFrameScene<'_>,
stride: u32,
sample_offset: u32,
spp: u32,
) -> Vec<Vec3> {
let num_pixels = scene.width as usize * scene.height as usize;
let mut out = vec![Vec3::ZERO; num_pixels];
let plane_soa = build_plane_soa(scene.planes);
for first_pixel in 0..stride {
let sums =
trace_pixels_interleaved(scene, &plane_soa, first_pixel, stride, sample_offset, spp);
scatter_interleaved(&mut out, first_pixel, stride, &sums);
}
out
}
#[test]
fn trace_pixels_interleaved_recombines_to_cpu_accumulate_for_several_strides() {
let camera = Camera::new(0.35, 0.28, 5.0, 18.0);
let planes = StandardGemCuts::standard_round_brilliant();
let material = GemMaterial::by_name("Diamond").expect("Diamond is a built-in material");
let scene = diamond_scene(&camera, &planes, &material);
let num_pixels = (WIDTH * HEIGHT) as usize;
let mut reference = vec![Vec3::ZERO; num_pixels];
cpu_accumulate(&scene, 0, 3, &mut reference);
let reference_bits = xyz_bits(&reference);
for stride in [1, 3, 7] {
let recombined = trace_whole_frame_via_stride(&scene, stride, 0, 3);
assert_eq!(
xyz_bits(&recombined),
reference_bits,
"stride {stride} did not recombine to cpu_accumulate's output bit-for-bit"
);
}
}