moonlight-triangulation-1.3.0.0: bench/delaunay-compare/rust/src/lib.rs
//! Rust referent boundary for Moonlight's Delaunay construction comparison.
//!
//! Fixture choice, benchmark grouping, agreement, timing, and reporting belong
//! to Haskell. This library retains only the operations that cannot move there:
//! Spade's exact seeded generator and direct calls into the Rust crates under
//! comparison.
use core::ffi::c_void;
use core::hint::black_box;
use core::ptr;
use rand::distr::{Distribution, Uniform};
use rand::rngs::StdRng;
use rand::SeedableRng;
use spade::{HierarchyHintGenerator, Point2, Triangulation};
use std::panic::{catch_unwind, AssertUnwindSafe};
const UPSTREAM_SEED: [u8; 32] = [
0xfb, 0xdc, 0x4e, 0xa0, 0x30, 0xde, 0x82, 0xba, 0x69, 0x97, 0x3c, 0x52, 0x49, 0x4d, 0x00, 0xca,
0x0a, 0x5c, 0x21, 0xa3, 0x8d, 0x5c, 0xf2, 0x34, 0x4e, 0x58, 0x7d, 0x80, 0x16, 0x66, 0x23, 0x30,
];
const UNIFORM_RANGE: f64 = 1.0e9;
const LOCAL_STEP_RANGE: f64 = 1.0;
type PlainSpade = spade::DelaunayTriangulation<Point2<f64>>;
type HierarchySpade =
spade::DelaunayTriangulation<Point2<f64>, (), (), (), HierarchyHintGenerator<f64>>;
#[repr(i32)]
#[derive(Clone, Copy, Debug)]
enum BoundaryStatus {
Success = 0,
NullPointer = 1,
UnknownTag = 2,
CoordinateCountMismatch = 3,
DistributionConstructionFailed = 4,
TriangulationFailed = 5,
Panicked = 6,
}
impl BoundaryStatus {
const fn code(self) -> i32 {
self as i32
}
}
#[derive(Clone, Copy)]
enum PointDistribution {
LocalInsertion,
Uniform,
}
impl TryFrom<i32> for PointDistribution {
type Error = BoundaryStatus;
fn try_from(tag: i32) -> Result<Self, Self::Error> {
match tag {
0 => Ok(Self::LocalInsertion),
1 => Ok(Self::Uniform),
_ => Err(BoundaryStatus::UnknownTag),
}
}
}
#[derive(Clone, Copy)]
enum NativeImplementation {
Spade,
SpadeHierarchy,
Cdt,
Delaunator,
}
impl TryFrom<i32> for NativeImplementation {
type Error = BoundaryStatus;
fn try_from(tag: i32) -> Result<Self, Self::Error> {
match tag {
0 => Ok(Self::Spade),
1 => Ok(Self::SpadeHierarchy),
2 => Ok(Self::Cdt),
3 => Ok(Self::Delaunator),
_ => Err(BoundaryStatus::UnknownTag),
}
}
}
enum PreparedImplementation {
Spade(Vec<Point2<f64>>),
SpadeHierarchy(Vec<Point2<f64>>),
Cdt(Vec<(f64, f64)>),
Delaunator(Vec<delaunator::Point>),
}
#[derive(Clone, Copy)]
struct ConstructionSummary {
vertices: usize,
triangles: usize,
}
impl PreparedImplementation {
fn from_coordinates(implementation: NativeImplementation, coordinates: &[[f64; 2]]) -> Self {
match implementation {
NativeImplementation::Spade => {
Self::Spade(coordinates.iter().copied().map(Point2::from).collect())
}
NativeImplementation::SpadeHierarchy => {
Self::SpadeHierarchy(coordinates.iter().copied().map(Point2::from).collect())
}
NativeImplementation::Cdt => {
Self::Cdt(coordinates.iter().map(|[x, y]| (*x, *y)).collect())
}
NativeImplementation::Delaunator => Self::Delaunator(
coordinates
.iter()
.map(|[x, y]| delaunator::Point { x: *x, y: *y })
.collect(),
),
}
}
fn run(&self) -> Result<ConstructionSummary, BoundaryStatus> {
match self {
Self::Spade(vertices) => {
let triangulation = PlainSpade::bulk_load(vertices.clone())
.map_err(|_| BoundaryStatus::TriangulationFailed)?;
let summary = ConstructionSummary {
vertices: triangulation.num_vertices(),
triangles: triangulation.num_inner_faces(),
};
black_box(triangulation);
Ok(summary)
}
Self::SpadeHierarchy(vertices) => {
let triangulation = HierarchySpade::bulk_load(vertices.clone())
.map_err(|_| BoundaryStatus::TriangulationFailed)?;
let summary = ConstructionSummary {
vertices: triangulation.num_vertices(),
triangles: triangulation.num_inner_faces(),
};
black_box(triangulation);
Ok(summary)
}
Self::Cdt(vertices) => {
let triangles = cdt::triangulate_points(vertices)
.map_err(|_| BoundaryStatus::TriangulationFailed)?;
let summary = ConstructionSummary {
vertices: vertices.len(),
triangles: triangles.len(),
};
black_box(triangles);
Ok(summary)
}
Self::Delaunator(vertices) => {
let triangulation = delaunator::triangulate(vertices);
let summary = ConstructionSummary {
vertices: vertices.len(),
triangles: triangulation.triangles.len() / 3,
};
black_box(triangulation);
Ok(summary)
}
}
}
}
fn boundary_status(action: impl FnOnce() -> Result<(), BoundaryStatus>) -> i32 {
match catch_unwind(AssertUnwindSafe(action)) {
Ok(Ok(())) => BoundaryStatus::Success.code(),
Ok(Err(status)) => status.code(),
Err(_) => BoundaryStatus::Panicked.code(),
}
}
fn coordinate_count(point_count: usize) -> Result<usize, BoundaryStatus> {
point_count
.checked_mul(2)
.ok_or(BoundaryStatus::CoordinateCountMismatch)
}
fn uniform_samples(point_count: usize) -> Result<impl Iterator<Item = [f64; 2]>, BoundaryStatus> {
let distribution = Uniform::new_inclusive(-UNIFORM_RANGE, UNIFORM_RANGE)
.map_err(|_| BoundaryStatus::DistributionConstructionFailed)?;
let mut generator = StdRng::from_seed(UPSTREAM_SEED);
Ok(core::iter::from_fn(move || {
Some([
distribution.sample(&mut generator),
distribution.sample(&mut generator),
])
})
.take(point_count))
}
fn local_insertion_samples(
point_count: usize,
) -> Result<impl Iterator<Item = [f64; 2]>, BoundaryStatus> {
let distribution = Uniform::new_inclusive(-LOCAL_STEP_RANGE, LOCAL_STEP_RANGE)
.map_err(|_| BoundaryStatus::DistributionConstructionFailed)?;
let mut generator = StdRng::from_seed(UPSTREAM_SEED);
let mut previous = [0.0, 1.0];
Ok(core::iter::from_fn(move || {
previous = [
previous[0] + distribution.sample(&mut generator),
previous[1] + distribution.sample(&mut generator),
];
Some(previous)
})
.take(point_count))
}
fn write_coordinates(
distribution: PointDistribution,
point_count: usize,
output: &mut [f64],
) -> Result<(), BoundaryStatus> {
let expected_count = coordinate_count(point_count)?;
if output.len() != expected_count {
return Err(BoundaryStatus::CoordinateCountMismatch);
}
let samples: Box<dyn Iterator<Item = [f64; 2]>> = match distribution {
PointDistribution::LocalInsertion => Box::new(local_insertion_samples(point_count)?),
PointDistribution::Uniform => Box::new(uniform_samples(point_count)?),
};
output
.chunks_exact_mut(2)
.zip(samples)
.for_each(|(coordinate_pair, [x, y])| {
coordinate_pair.copy_from_slice(&[x, y]);
});
Ok(())
}
/// Fill `coordinates` with Spade's exact seeded benchmark distribution.
///
/// # Safety
///
/// A non-null `coordinates` pointer must be writable for `coordinate_count`
/// consecutive `f64` values.
#[no_mangle]
pub unsafe extern "C" fn delaunay_compare_generate(
distribution_tag: i32,
point_count: usize,
coordinates: *mut f64,
supplied_coordinate_count: usize,
) -> i32 {
boundary_status(|| {
let distribution = PointDistribution::try_from(distribution_tag)?;
let expected_count = coordinate_count(point_count)?;
if supplied_coordinate_count != expected_count {
return Err(BoundaryStatus::CoordinateCountMismatch);
}
if coordinates.is_null() && supplied_coordinate_count != 0 {
return Err(BoundaryStatus::NullPointer);
}
let output = if supplied_coordinate_count == 0 {
&mut []
} else {
// SAFETY: the caller contract and checks above establish a non-null
// writable region of exactly `supplied_coordinate_count` values.
unsafe { core::slice::from_raw_parts_mut(coordinates, supplied_coordinate_count) }
};
write_coordinates(distribution, point_count, output)
})
}
/// Prepare the implementation-specific native input outside the timed region.
///
/// # Safety
///
/// A non-null `coordinates` pointer must be readable for `2 * point_count`
/// consecutive `f64` values. `prepared_output` must be writable for one pointer.
#[no_mangle]
pub unsafe extern "C" fn delaunay_compare_prepare(
implementation_tag: i32,
coordinates: *const f64,
point_count: usize,
prepared_output: *mut *mut c_void,
) -> i32 {
boundary_status(|| {
let implementation = NativeImplementation::try_from(implementation_tag)?;
if prepared_output.is_null() {
return Err(BoundaryStatus::NullPointer);
}
let supplied_coordinate_count = coordinate_count(point_count)?;
if coordinates.is_null() && supplied_coordinate_count != 0 {
return Err(BoundaryStatus::NullPointer);
}
let flat_coordinates = if supplied_coordinate_count == 0 {
&[]
} else {
// SAFETY: the caller contract and checks above establish a non-null
// readable region of exactly `supplied_coordinate_count` values.
unsafe { core::slice::from_raw_parts(coordinates, supplied_coordinate_count) }
};
let coordinate_pairs = flat_coordinates.as_chunks::<2>().0;
let prepared = PreparedImplementation::from_coordinates(implementation, coordinate_pairs);
// SAFETY: `prepared_output` was checked non-null and points to writable
// storage for one opaque pointer by the caller contract.
unsafe {
ptr::write(prepared_output, Box::into_raw(Box::new(prepared)).cast());
}
Ok(())
})
}
/// Construct one triangulation from a prepared native input.
///
/// # Safety
///
/// `prepared` must be a live pointer returned by `delaunay_compare_prepare`.
/// Both output pointers must be writable for one `usize`.
#[no_mangle]
pub unsafe extern "C" fn delaunay_compare_run(
prepared: *const c_void,
vertex_count_output: *mut usize,
triangle_count_output: *mut usize,
) -> i32 {
boundary_status(|| {
if prepared.is_null() || vertex_count_output.is_null() || triangle_count_output.is_null() {
return Err(BoundaryStatus::NullPointer);
}
// SAFETY: the caller contract requires a live pointer produced by the
// matching prepare function and keeps it alive for this call.
let prepared_implementation = unsafe { &*prepared.cast::<PreparedImplementation>() };
let summary = prepared_implementation.run()?;
// SAFETY: both output pointers were checked non-null and the caller
// contract gives writable storage for one value at each address.
unsafe {
ptr::write(vertex_count_output, summary.vertices);
ptr::write(triangle_count_output, summary.triangles);
}
Ok(())
})
}
/// Release one prepared native input. A null pointer is a no-op.
///
/// # Safety
///
/// A non-null pointer must have been returned by `delaunay_compare_prepare`
/// and must not have been released before.
#[no_mangle]
pub unsafe extern "C" fn delaunay_compare_release(prepared: *mut c_void) {
if !prepared.is_null() {
// SAFETY: the caller contract transfers the one remaining ownership of
// the allocation produced by `Box::into_raw` in the prepare function.
unsafe {
drop(Box::from_raw(prepared.cast::<PreparedImplementation>()));
}
}
}
#[cfg(test)]
mod tests {
use super::{local_insertion_samples, uniform_samples};
#[test]
fn local_insertion_prefix_matches_upstream_fixture_bits() {
let observed = local_insertion_samples(3)
.expect("the fixed local-insertion distribution is valid")
.flatten()
.map(f64::to_bits)
.collect::<Vec<_>>();
assert_eq!(
observed,
[
0xbfe28b04856ac6db,
0x3ff8c718b563a19a,
0x3fc014ab09d22a14,
0x4003a1ee45dc4712,
0xbfd097c41d82adc4,
0x4008d420538d5c3d,
]
);
}
#[test]
fn uniform_prefix_matches_upstream_fixture_bits() {
let observed = uniform_samples(3)
.expect("the fixed uniform distribution is valid")
.flatten()
.map(f64::to_bits)
.collect::<Vec<_>>();
assert_eq!(
observed,
[
0xc1c1450134a5bd5a,
0x41c0598b1e249a72,
0x41c5037dbf1c0b38,
0x41cafc1cf53845c8,
0xc1b6f1036f0b1386,
0x41c35b5d4df8b356,
]
);
}
}