use bytemuck::{Pod, Zeroable};
use std::fmt;
use std::mem::size_of;
use std::ops::Range;
use std::sync::Arc;
use crate::{ExecutionContext, MemoryBudgetError, MemoryReservation};
const LANE: usize = 16;
#[repr(C, align(64))]
#[derive(Clone, Copy, Pod, Zeroable)]
struct CoordLane([[f32; 3]; LANE]);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
#[repr(transparent)]
pub struct CoordinateGeneration(u64);
impl CoordinateGeneration {
pub const INITIAL: Self = Self(0);
#[must_use]
pub const fn from_raw(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn next(self) -> Option<Self> {
match self.0.checked_add(1) {
Some(next) => Some(Self(next)),
None => None,
}
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Aabb {
pub min: [f32; 3],
pub max: [f32; 3],
}
impl Aabb {
pub const EMPTY: Self = Self {
min: [f32::INFINITY; 3],
max: [f32::NEG_INFINITY; 3],
};
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.min[0] > self.max[0] || self.min[1] > self.max[1] || self.min[2] > self.max[2]
}
#[inline]
pub fn extend(&mut self, point: [f32; 3]) {
extend_axis(&mut self.min[0], &mut self.max[0], point[0]);
extend_axis(&mut self.min[1], &mut self.max[1], point[1]);
extend_axis(&mut self.min[2], &mut self.max[2], point[2]);
}
#[inline]
pub fn union(&mut self, other: &Self) {
if other.is_empty() {
return;
}
self.extend(other.min);
self.extend(other.max);
}
#[must_use]
#[inline]
pub fn within(&self, other: &Self, distance: f32) -> bool {
if self.is_empty() || other.is_empty() {
return false;
}
let maximum_gap_squared = distance * distance;
let x = axis_gap(self.min[0], self.max[0], other.min[0], other.max[0]);
let mut gap_squared = x * x;
if gap_squared > maximum_gap_squared {
return false;
}
let y = axis_gap(self.min[1], self.max[1], other.min[1], other.max[1]);
gap_squared += y * y;
if gap_squared > maximum_gap_squared {
return false;
}
let z = axis_gap(self.min[2], self.max[2], other.min[2], other.max[2]);
gap_squared += z * z;
gap_squared <= maximum_gap_squared
}
}
impl Default for Aabb {
fn default() -> Self {
Self::EMPTY
}
}
#[derive(Clone, Default)]
pub struct CoordinateBlock {
lanes: Arc<Vec<CoordLane>>,
len: u32,
reservation: Option<Arc<MemoryReservation>>,
}
impl CoordinateBlock {
#[must_use]
pub fn new() -> Self {
Self {
lanes: Arc::new(Vec::new()),
len: 0,
reservation: None,
}
}
#[must_use]
pub fn with_capacity(positions: usize) -> Self {
Self {
lanes: Arc::new(Vec::with_capacity(lanes_for_positions(positions))),
len: 0,
reservation: None,
}
}
#[must_use]
pub const fn len(&self) -> u32 {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub fn push(&mut self, position: [f32; 3]) {
let Some((index, next_len)) = append_indices(self.len) else {
return;
};
let lanes = Arc::make_mut(&mut self.lanes);
if write_position(lanes, index, position) {
self.len = next_len;
}
}
#[must_use]
#[inline]
pub fn as_slice(&self) -> &[[f32; 3]] {
let all: &[[f32; 3]] = bytemuck::cast_slice(self.lanes.as_slice());
visible_slice(all, self.len)
}
#[inline]
pub(crate) fn as_mut_slice(&mut self) -> &mut [[f32; 3]] {
let lanes = Arc::make_mut(&mut self.lanes);
let all: &mut [[f32; 3]] = bytemuck::cast_slice_mut(lanes.as_mut_slice());
visible_mut_slice(all, self.len)
}
pub(crate) fn make_unique_in(
&mut self,
context: &ExecutionContext,
) -> Result<(), MemoryBudgetError> {
if Arc::strong_count(&self.lanes) == 1 {
return Ok(());
}
let bytes = self.allocated_bytes();
let mut reservation = context.try_reserve(bytes)?;
let mut lanes = Vec::with_capacity(self.lanes.capacity());
lanes.extend_from_slice(&self.lanes);
let retained = lanes.capacity().saturating_mul(size_of::<CoordLane>());
if retained > bytes {
reservation.try_grow(retained - bytes)?;
} else {
reservation.shrink_to(retained);
}
self.lanes = Arc::new(lanes);
self.reservation = Some(Arc::new(reservation));
Ok(())
}
#[must_use]
#[inline]
pub fn range(&self, range: Range<u32>) -> Option<&[[f32; 3]]> {
let range = usize_range(range)?;
self.as_slice().get(range)
}
#[must_use]
pub fn bounds(&self, range: Range<u32>) -> Aabb {
let Some(positions) = self.range(range) else {
return Aabb::EMPTY;
};
let mut bounds = Aabb::EMPTY;
for &position in positions {
bounds.extend(position);
}
bounds
}
#[must_use]
pub fn allocated_bytes(&self) -> usize {
self.lanes.capacity() * size_of::<CoordLane>()
}
}
impl fmt::Debug for CoordinateBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CoordinateBlock")
.field("positions", &self.len)
.finish_non_exhaustive()
}
}
impl FromIterator<[f32; 3]> for CoordinateBlock {
fn from_iter<T: IntoIterator<Item = [f32; 3]>>(iter: T) -> Self {
let mut iter = iter.into_iter();
let hinted_positions = iter.size_hint().0;
let mut lanes = Vec::new();
let hinted_lanes = lanes_for_positions(hinted_positions);
if hinted_lanes != 0 {
let _reserve_failed = lanes.try_reserve_exact(hinted_lanes).is_err();
}
let mut len = 0u32;
for position in &mut iter {
let Some((index, next_len)) = append_indices(len) else {
break;
};
if !write_position(&mut lanes, index, position) {
break;
}
len = next_len;
}
Self {
lanes: Arc::new(lanes),
len,
reservation: None,
}
}
}
#[inline]
fn extend_axis(minimum: &mut f32, maximum: &mut f32, value: f32) {
if !value.is_finite() {
return;
}
*minimum = minimum.min(value);
*maximum = maximum.max(value);
}
#[inline]
fn axis_gap(own_min: f32, own_max: f32, other_min: f32, other_max: f32) -> f32 {
(other_min - own_max).max(own_min - other_max).max(0.0)
}
#[inline]
fn lanes_for_positions(positions: usize) -> usize {
positions.div_ceil(LANE)
}
#[inline]
fn append_indices(len: u32) -> Option<(usize, u32)> {
let next_len = len.checked_add(1)?;
let index = usize::try_from(len).ok()?;
Some((index, next_len))
}
#[inline]
fn write_position(lanes: &mut Vec<CoordLane>, index: usize, position: [f32; 3]) -> bool {
let (lane_index, slot) = lane_position(index);
if lane_index > lanes.len() {
return false;
}
if lane_index == lanes.len() {
let mut lane = CoordLane([[0.0; 3]; LANE]);
let Some(target) = lane.0.get_mut(slot) else {
return false;
};
*target = position;
lanes.push(lane);
return true;
}
let Some(target) = lanes
.get_mut(lane_index)
.and_then(|lane| lane.0.get_mut(slot))
else {
return false;
};
*target = position;
true
}
#[inline]
const fn lane_position(position: usize) -> (usize, usize) {
(position / LANE, position % LANE)
}
#[inline]
fn visible_slice<T>(values: &[T], len: u32) -> &[T] {
let Ok(len) = usize::try_from(len) else {
return &[];
};
match values.get(..len) {
Some(values) => values,
None => &[],
}
}
#[inline]
fn visible_mut_slice<T>(values: &mut [T], len: u32) -> &mut [T] {
let Ok(len) = usize::try_from(len) else {
return &mut [];
};
match values.get_mut(..len) {
Some(values) => values,
None => &mut [],
}
}
#[inline]
fn usize_range(range: Range<u32>) -> Option<Range<usize>> {
let start = usize::try_from(range.start).ok()?;
let end = usize::try_from(range.end).ok()?;
Some(start..end)
}
#[cfg(test)]
#[path = "storage_tests.rs"]
mod tests;