embree3 0.4.0

Safe Rust bindings to Embree 3.13.5, Intel's high-performance ray-tracing kernels.
Documentation
//! Shared helpers for the FFI soundness proof tests.
#![allow(dead_code)]

use embree3::{
    Bounds, BufferUsage, Device, Format, GeometryBuilder, GeometryKind, IntersectContext, Ray,
    RayHit, Scene,
};

pub fn device() -> Device {
    Device::new().expect("failed to create embree device (is EMBREE_DIR/LD_LIBRARY_PATH set?)")
}

/// Returns an uncommitted [`GeometryBuilder`] for a unit triangle. Callers add
/// any further configuration (e.g. a filter function) and then `commit()`
/// before attaching to a scene.
pub fn unit_triangle(device: &Device) -> GeometryBuilder<'static> {
    let mut tri = device.create_geometry(GeometryKind::TRIANGLE).unwrap();
    tri.set_new_buffer::<[f32; 3]>(BufferUsage::VERTEX, 0, Format::FLOAT3, 3 * 4, 3)
        .unwrap()
        .copy_from_slice(&[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]);
    tri.set_new_buffer::<[u32; 3]>(BufferUsage::INDEX, 0, Format::UINT3, 3 * 4, 1)
        .unwrap()
        .copy_from_slice(&[[0, 1, 2]]);
    tri
}

/// Provokes embree into reporting an error through the device's error function.
/// Binding a vertex buffer to an out-of-range slot makes embree report
/// `RTC_ERROR_INVALID_ARGUMENT` via the error callback (verified empirically;
/// it does not abort, unlike e.g. committing a user geometry with no bounds
/// function). Used by the default-error-reporter tests.
pub fn trigger_embree_error(device: &Device) {
    let mut geom = device.create_geometry(GeometryKind::TRIANGLE).unwrap();
    let _ = geom.set_new_buffer::<[f32; 3]>(BufferUsage::VERTEX, 100, Format::FLOAT3, 3 * 4, 3);
}

/// Casts one ray straight down +z at (0.25, 0.25), which hits `unit_triangle`.
/// Returns the committed `RayHit` so callers can inspect `hit.geomID` etc.
pub fn cast_center_ray(scene: &Scene) -> RayHit {
    let ray = Ray::segment([0.25, 0.25, -1.0], [0.0, 0.0, 1.0], 0.0, f32::INFINITY);
    let mut ctx = IntersectContext::coherent();
    let mut ray_hit = RayHit::from(ray);
    scene.intersect(&mut ctx, &mut ray_hit);
    ray_hit
}

/// Overwrites ~64 KiB of fresh stack so that any pointer left dangling into a
/// previously-returned stack frame now reads clobbered bytes. Call this between
/// registering a callback and tracing, to make the use-after-free deterministic
/// instead of "works by luck because the bytes are still there".
#[inline(never)]
pub fn clobber_stack() {
    let mut buf = [0xA5u8; 64 * 1024];
    // Touch every page so the writes are not optimised away.
    for (i, b) in buf.iter_mut().enumerate() {
        *b = (i as u8) ^ 0x5A;
    }
    std::hint::black_box(&buf);
}

pub fn user_sphere(deviec: &Device) -> GeometryBuilder<'static> {
    let mut geom = deviec.create_geometry(GeometryKind::USER).unwrap();
    geom.set_primitive_count(1);
    geom.set_bounds_function::<_, ()>(|bounds: &mut Bounds, _prim, _time, _user: Option<&()>| {
        bounds.lower_x = -0.5;
        bounds.lower_y = -0.5;
        bounds.lower_z = -0.5;
        bounds.upper_x = 0.5;
        bounds.upper_y = 0.5;
        bounds.upper_z = 0.5;
    });
    geom
}