mod common;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
};
use embree3::{
Hit, IntersectContext, IntersectFunctionNArgs, OccludedFunctionNArgs, Ray4, RayHit4, SoAHit,
SoARay, ValidMask, ValidMaskN, INVALID_ID,
};
fn trace_sphere_with_filter(reject: bool) -> (bool, Vec<u32>, usize) {
let device = common::device();
let mut scene = device.create_scene().unwrap();
let probe: Arc<Mutex<Vec<u32>>> = Arc::new(Mutex::new(Vec::new()));
let calls = Arc::new(AtomicUsize::new(0));
let probe_cb = probe.clone();
let calls_cb = calls.clone();
let mut sphere = common::user_sphere(&device);
sphere.set_intersect_function::<_, ()>(move |args: &mut IntersectFunctionNArgs<'_, ()>| {
for i in 0..args.len() {
if args.valid_n()[i] == 0 {
continue; }
let mut ray = args.ray(i);
let t = 1.5_f32;
if t > ray.tnear && t < ray.tfar {
let mut hit = Hit {
Ng_x: 0.0,
Ng_y: 0.0,
Ng_z: -1.0,
u: 0.0,
v: 0.0,
primID: args.prim_id(),
geomID: args.geom_id(),
instID: [INVALID_ID],
};
ray.tfar = t; if args.filter_intersection(&mut ray, &mut hit) {
args.commit_hit(i, &ray, &hit);
}
}
}
});
sphere.set_intersect_filter_function::<_, ()>(
move |_ray, hit, mut valid, _ctx, _user: Option<&()>| {
calls_cb.fetch_add(1, Ordering::SeqCst);
probe_cb.lock().unwrap().push(0xF11A ^ hit.prim_id(0));
if reject {
valid[0] = 0; }
},
);
let sphere = sphere.commit();
scene.attach_geometry(&sphere);
scene.commit();
common::clobber_stack();
let ray = embree3::Ray::segment([0.0, 0.0, -2.0], [0.0, 0.0, 1.0], 0.0, f32::INFINITY);
let mut ctx = IntersectContext::coherent();
let mut ray_hit = embree3::RayHit::from(ray);
scene.intersect(&mut ctx, &mut ray_hit);
let hit_valid = ray_hit.hit.is_valid();
let recorded = probe.lock().unwrap().clone();
let call_count = calls.load(Ordering::SeqCst);
(hit_valid, recorded, call_count)
}
#[test]
fn filter_runs_for_user_geometry_and_accepts() {
let (hit_valid, probe, calls) = trace_sphere_with_filter(false);
assert!(
calls >= 1,
"the intersect filter must run via the user geometry"
);
assert_eq!(
probe,
vec![0xF11A ^ 0],
"filter saw live captured state + primID 0"
);
assert!(hit_valid, "accepted hit must be committed");
}
#[test]
fn filter_runs_for_user_geometry_and_rejects() {
let (hit_valid, _probe, calls) = trace_sphere_with_filter(true);
assert!(
calls >= 1,
"the intersect filter must run via the user geometry"
);
assert!(!hit_valid, "rejected hit must NOT be committed");
}
fn trace_sphere_occlusion_with_filter(reject: bool) -> (bool, usize) {
let device = common::device();
let mut scene = device.create_scene().unwrap();
let calls = Arc::new(AtomicUsize::new(0));
let calls_cb = calls.clone();
let mut sphere = common::user_sphere(&device);
sphere.set_occluded_function::<_, ()>(move |args: &mut OccludedFunctionNArgs<'_, ()>| {
for i in 0..args.len() {
if args.valid_n()[i] == 0 {
continue; }
let mut ray = args.ray(i);
let t = 1.5_f32;
if t > ray.tnear && t < ray.tfar {
let mut hit = Hit {
Ng_x: 0.0,
Ng_y: 0.0,
Ng_z: -1.0,
u: 0.0,
v: 0.0,
primID: args.prim_id(),
geomID: args.geom_id(),
instID: [INVALID_ID],
};
ray.tfar = t; if args.filter_occlusion(&mut ray, &mut hit) {
args.set_occluded(i);
}
}
}
});
sphere.set_occluded_filter_function::<_, ()>(
move |_ray, _hit, mut valid, _ctx, _user: Option<&()>| {
calls_cb.fetch_add(1, Ordering::SeqCst);
if reject {
valid[0] = 0;
}
},
);
let sphere = sphere.commit();
scene.attach_geometry(&sphere);
scene.commit();
common::clobber_stack();
let ray = embree3::Ray::segment([0.0, 0.0, -2.0], [0.0, 0.0, 1.0], 0.0, f32::INFINITY);
let mut ctx = IntersectContext::coherent();
let mut probe = ray;
let occluded = scene.occluded(&mut ctx, &mut probe);
(occluded, calls.load(Ordering::SeqCst))
}
#[test]
fn occlusion_filter_runs_and_occludes() {
let (occluded, calls) = trace_sphere_occlusion_with_filter(false);
assert!(
calls >= 1,
"the occlusion filter must run via the user geometry"
);
assert!(occluded, "surviving occluder must mark the ray occluded");
}
#[test]
fn occlusion_filter_runs_and_passes_through() {
let (occluded, calls) = trace_sphere_occlusion_with_filter(true);
assert!(
calls >= 1,
"the occlusion filter must run via the user geometry"
);
assert!(
!occluded,
"rejected occluder must NOT mark the ray occluded"
);
}
#[test]
fn filter_rejecting_nearest_returns_farther_hit() {
let device = common::device();
let mut scene = device.create_scene().unwrap();
let seen = Arc::new(AtomicUsize::new(0));
let seen_cb = seen.clone();
let mut sphere = common::user_sphere(&device);
sphere.set_intersect_function::<_, ()>(move |args: &mut IntersectFunctionNArgs<'_, ()>| {
for i in 0..args.len() {
if args.valid_n()[i] == 0 {
continue;
}
for &t in &[1.5_f32, 2.5_f32] {
let mut ray = args.ray(i);
if t > ray.tnear && t < ray.tfar {
let mut hit = Hit {
Ng_x: 0.0,
Ng_y: 0.0,
Ng_z: -1.0,
u: 0.0,
v: 0.0,
primID: args.prim_id(),
geomID: args.geom_id(),
instID: [INVALID_ID],
};
ray.tfar = t;
if args.filter_intersection(&mut ray, &mut hit) {
args.commit_hit(i, &ray, &hit);
}
}
}
}
});
sphere.set_intersect_filter_function::<_, ()>(
move |_ray, _hit, mut valid, _ctx, _user: Option<&()>| {
if seen_cb.fetch_add(1, Ordering::SeqCst) == 0 {
valid[0] = 0; }
},
);
let sphere = sphere.commit();
scene.attach_geometry(&sphere);
scene.commit();
common::clobber_stack();
let ray = embree3::Ray::segment([0.0, 0.0, -2.0], [0.0, 0.0, 1.0], 0.0, f32::INFINITY);
let mut ctx = IntersectContext::coherent();
let mut ray_hit = embree3::RayHit::from(ray);
scene.intersect(&mut ctx, &mut ray_hit);
assert!(
ray_hit.hit.is_valid(),
"the farther hit should be committed"
);
assert!(
(ray_hit.ray.tfar - 2.5).abs() < 1e-4,
"committed hit must be the farther accepted one (t=2.5), got {}",
ray_hit.ray.tfar
);
}
#[test]
fn packet_filter_intersection_n_rejects_per_lane() {
let device = common::device();
let mut scene = device.create_scene().unwrap();
let calls = Arc::new(AtomicUsize::new(0));
let calls_cb = calls.clone();
let mut sphere = common::user_sphere(&device);
sphere.set_intersect_function::<_, ()>(move |args: &mut IntersectFunctionNArgs<'_, ()>| {
let n = args.len();
let t = 1.5f32;
let mut hits = vec![
Hit {
primID: args.prim_id(),
geomID: args.geom_id(),
..Default::default()
};
n
];
let mut valid = vec![ValidMask::Valid as i32; n];
let mut cached_tfar = vec![0.0; n];
for i in 0..n {
let ray = args.ray(i);
cached_tfar[i] = ray.tfar;
if args.valid_n()[i] != ValidMask::Invalid && t > ray.tnear && t < ray.tfar {
args.set_tfar(i, t); } else {
valid[i] = ValidMask::Invalid as i32;
}
}
args.filter_intersection_n(&mut hits, &mut valid);
for i in 0..n {
if valid[i] != ValidMask::Invalid as i32 {
let r = args.ray(i); args.commit_hit(i, &r, &hits[i]);
} else {
args.set_tfar(i, cached_tfar[i]); }
}
});
sphere.set_intersect_filter_function::<_, ()>(
move |ray, _hit, mut valid, _ctx, _user: Option<&()>| {
calls_cb.fetch_add(1, Ordering::SeqCst);
for i in 0..ray.len() {
if ray.id(i) % 2 == 1 {
valid[i] = 0; }
}
},
);
let sphere = sphere.commit();
scene.attach_geometry(&sphere);
scene.commit();
common::clobber_stack();
let origins = [
[-0.3, 0.0, -2.0],
[-0.1, 0.0, -2.0],
[0.1, 0.0, -2.0],
[0.3, 0.0, -2.0],
];
let mut ray4 = Ray4::new(origins, [[0.0, 0.0, 1.0]; 4]);
for i in 0..4 {
ray4.set_id(i, i as u32);
}
let mut rh = RayHit4::new(ray4);
let valid = ValidMaskN::all_active();
let mut ctx = IntersectContext::coherent();
scene.intersect4(&mut ctx, &mut rh, &valid);
assert!(
calls.load(Ordering::SeqCst) >= 1,
"the filter must run for the packet"
);
for i in 0..4 {
let hit = rh.hit.is_valid(i);
if i % 2 == 0 {
assert!(hit, "even-id lane {i} should hit (filter accepts)");
} else {
assert!(!hit, "odd-id lane {i} should be rejected by the filter");
}
}
}