restructure this multi-crate project to use Cargo's "workspace" feature
this solves an issue in the Nix build, where managing multiple Cargo.lock files is otherwise tricky. it causes (or fails to fix?) an adjacent issue where the spirv builder doesn't seem to have everything it needs vendored.
This commit is contained in:
506
crates/spirv_backend/src/support.rs
Normal file
506
crates/spirv_backend/src/support.rs
Normal file
@@ -0,0 +1,506 @@
|
||||
use spirv_std::glam::{self, UVec3};
|
||||
|
||||
// pub trait Nullable {
|
||||
// fn null() -> *const Self;
|
||||
// fn null_mut() -> *mut Self;
|
||||
// }
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialEq)]
|
||||
pub struct XYZStd<T>(T, T, T);
|
||||
|
||||
pub type Vec3Std = XYZStd<f32>;
|
||||
pub type UVec3Std = XYZStd<u32>;
|
||||
|
||||
impl<T> XYZStd<T> {
|
||||
pub fn new(x: T, y: T, z: T) -> Self {
|
||||
Self(x, y, z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> XYZStd<T> {
|
||||
pub fn uniform(u: T) -> Self {
|
||||
Self(u, u, u)
|
||||
}
|
||||
|
||||
pub fn x(self) -> T {
|
||||
self.0
|
||||
}
|
||||
pub fn y(self) -> T {
|
||||
self.1
|
||||
}
|
||||
pub fn z(self) -> T {
|
||||
self.2
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: core::ops::Add<T, Output=T>> core::ops::Add<XYZStd<T>> for XYZStd<T> {
|
||||
type Output = Self;
|
||||
fn add(self, s: Self) -> Self {
|
||||
Self::new(self.0 + s.0, self.1 + s.1, self.2 + s.2)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: core::ops::Sub<T, Output=T>> core::ops::Sub<XYZStd<T>> for XYZStd<T> {
|
||||
type Output = Self;
|
||||
fn sub(self, s: Self) -> Self {
|
||||
Self::new(self.0 - s.0, self.1 - s.1, self.2 - s.2)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + core::ops::Mul<T, Output=T>> core::ops::Mul<T> for XYZStd<T> {
|
||||
type Output = Self;
|
||||
fn mul(self, s: T) -> Self {
|
||||
Self::new(self.0 * s, self.1 * s, self.2 * s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<glam::Vec3> for Vec3Std {
|
||||
fn into(self) -> glam::Vec3 {
|
||||
glam::Vec3::new(self.x(), self.y(), self.z())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<glam::UVec3> for UVec3Std {
|
||||
fn into(self) -> glam::UVec3 {
|
||||
glam::UVec3::new(self.x(), self.y(), self.z())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: core::ops::Mul<T, Output=T>> XYZStd<T> {
|
||||
pub fn elem_mul(self, other: Self) -> Self {
|
||||
Self::new(self.0 * other.0, self.1 * other.1, self.2 * other.2)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: core::ops::Div<T, Output=T>> XYZStd<T> {
|
||||
pub fn elem_div(self, other: Self) -> Self {
|
||||
Self::new(self.0 / other.0, self.1 / other.1, self.2 / other.2)
|
||||
}
|
||||
}
|
||||
|
||||
// const NULL_VEC3STD: *const Vec3Std = core::ptr::null();
|
||||
// const NULL_VEC3STD_MUT: *mut Vec3Std = core::ptr::null_mut();
|
||||
// impl Nullable for Vec3Std {
|
||||
// fn null() -> *const Self {
|
||||
// NULL_VEC3STD
|
||||
// }
|
||||
// fn null_mut() -> *mut Self {
|
||||
// NULL_VEC3STD_MUT
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// pub struct OptionalRef<'a, T> {
|
||||
// ptr: *const T, // NULL for None
|
||||
// _marker: PhantomData<&'a T>,
|
||||
// }
|
||||
//
|
||||
// impl<'a, T> OptionalRef<'a, T> {
|
||||
// fn from_ptr(ptr: *const T) -> Self {
|
||||
// Self {
|
||||
// ptr,
|
||||
// _marker: PhantomData,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn some(data: &'a T) -> Self {
|
||||
// Self::from_ptr(data as *const T)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a, T: Nullable> OptionalRef<'a, T> {
|
||||
// pub fn none() -> Self {
|
||||
// Self::from_ptr(T::null())
|
||||
// }
|
||||
//
|
||||
// pub fn is_some(&self) -> bool {
|
||||
// self.ptr != T::null()
|
||||
// }
|
||||
//
|
||||
// pub fn unwrap(self) -> &'a T {
|
||||
// assert!(self.is_some());
|
||||
// unsafe {
|
||||
// &*self.ptr
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// This is a spirv-compatible option type.
|
||||
/// The native rust Option type produces invalid spirv due to its enum nature; this custom option
|
||||
/// type creates code which will actually compile.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct Optional<T> {
|
||||
// XXX: not a bool, because: "entrypoint parameter cannot contain a boolean"
|
||||
present: u8,
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T> Optional<T> {
|
||||
pub fn some(data: T) -> Self {
|
||||
Self {
|
||||
present: 1,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn explicit_none(data: T) -> Self {
|
||||
Self {
|
||||
present: 0,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_some(self) -> bool {
|
||||
self.present != 0
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> T {
|
||||
assert!(self.present != 0);
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn map<U: Default, F: FnOnce(T) -> U>(self, f: F) -> Optional<U> {
|
||||
self.and_then(|inner| Optional::some(f(inner)))
|
||||
}
|
||||
|
||||
pub fn and_then<U: Default, F: FnOnce(T) -> Optional<U>>(self, f: F) -> Optional<U> {
|
||||
if self.present != 0 {
|
||||
f(self.unwrap())
|
||||
} else {
|
||||
Optional::none()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_or(self, default: T) -> T {
|
||||
if self.present != 0 {
|
||||
self.unwrap()
|
||||
} else {
|
||||
default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Optional<T> {
|
||||
pub fn none() -> Self {
|
||||
Self::explicit_none(Default::default())
|
||||
}
|
||||
|
||||
pub fn unwrap_or_default(self) -> T {
|
||||
self.unwrap_or(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Optional<T> {
|
||||
fn default() -> Self {
|
||||
Self::none()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Default, T1: Default> Optional<(T0, T1)> {
|
||||
pub fn flatten((f0, f1): (Optional<T0>, Optional<T1>)) -> Self {
|
||||
if f0.present != 0 && f1.present != 0 {
|
||||
Optional::some((f0.unwrap(), f1.unwrap()))
|
||||
} else {
|
||||
Optional::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #[derive(Copy, Clone, Default, PartialEq)]
|
||||
// pub struct Optional<T>(Option<T>);
|
||||
//
|
||||
// impl<T> Optional<T> {
|
||||
// pub fn some(data: T) -> Self {
|
||||
// Self(Some(data))
|
||||
// }
|
||||
//
|
||||
// pub fn explicit_none(_data: T) -> Self {
|
||||
// Self(None)
|
||||
// }
|
||||
//
|
||||
// pub fn is_some(self) -> bool {
|
||||
// self.0.is_some()
|
||||
// }
|
||||
//
|
||||
// pub fn unwrap(self) -> T {
|
||||
// self.0.unwrap()
|
||||
// }
|
||||
//
|
||||
// pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Optional<U> {
|
||||
// Optional(self.0.map(f))
|
||||
// }
|
||||
//
|
||||
// pub fn unwrap_or(self, default: T) -> T {
|
||||
// self.0.unwrap_or(default)
|
||||
// }
|
||||
//
|
||||
// pub fn none() -> Self {
|
||||
// Self(None)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<T: Default> Optional<T> {
|
||||
// pub fn unwrap_or_default(self) -> T {
|
||||
// self.0.unwrap_or_default()
|
||||
// }
|
||||
// }
|
||||
|
||||
/// This struct allows doing things like *(ptr + offset) = value.
|
||||
/// Such code wouldn't ordinarily compile with the spirv target because of
|
||||
/// unsupported pointer math. RuntimeArray exists to overcome this, however
|
||||
/// it emits invalid code for non-primitive types. Hence, this hack.
|
||||
// XXX: maximum bytes an array may occupy is 0x7fff_ffff
|
||||
// We don't know the element size, so assume it to be <= 44 bytes
|
||||
// pub const MAX_UNSIZED_ARRAY: usize = 0x2e7_ffff;
|
||||
pub const MAX_UNSIZED_ARRAY: usize = 0x1ff_ffff;
|
||||
pub struct UnsizedArray<T>(pub [T; MAX_UNSIZED_ARRAY]);
|
||||
|
||||
impl<T: Copy> UnsizedArray<T> {
|
||||
pub unsafe fn index(&self, index: usize) -> T {
|
||||
// *self.0.index(index)
|
||||
self.0[index]
|
||||
// *self.0.get_unchecked(index)
|
||||
// &self.0
|
||||
//asm! {
|
||||
// "%result = OpAccessChain _ {arr} {index}",
|
||||
// "OpReturnValue %result",
|
||||
// "%unused = OpLabel",
|
||||
// arr = in(reg) self,
|
||||
// index = in(reg) index,
|
||||
//}
|
||||
//loop {}
|
||||
}
|
||||
|
||||
pub unsafe fn write(&mut self, index: usize, value: T) {
|
||||
self.0[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UnsizedArray<T> {
|
||||
pub unsafe fn index_ref(&self, index: usize) -> &T {
|
||||
&self.0[index]
|
||||
}
|
||||
|
||||
pub unsafe fn get_handle<'a>(&'a self, index: usize) -> ArrayHandle<'a, T> {
|
||||
ArrayHandle {
|
||||
base: self,
|
||||
index,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_handle_mut<'a>(&'a mut self, index: usize) -> ArrayHandleMut<'a, T> {
|
||||
ArrayHandleMut {
|
||||
base: self,
|
||||
index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrayHandle<'a, T> {
|
||||
base: &'a UnsizedArray<T>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a, T: Copy> ArrayHandle<'a, T> {
|
||||
pub fn get(&self) -> T {
|
||||
unsafe {
|
||||
self.base.index(self.index)
|
||||
}
|
||||
}
|
||||
pub fn get_into(&self, out: &mut T) {
|
||||
core::clone::Clone::clone_from(out, self.get_ref());
|
||||
}
|
||||
}
|
||||
impl<'a, T> ArrayHandle<'a, T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
unsafe {
|
||||
self.base.index_ref(self.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct ArrayHandleMut<'a, T> {
|
||||
base: &'a mut UnsizedArray<T>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a, T: Copy> ArrayHandleMut<'a, T> {
|
||||
pub fn write(&mut self, value: T) {
|
||||
unsafe {
|
||||
self.base.write(self.index, value);
|
||||
}
|
||||
}
|
||||
pub fn get(&self) -> T {
|
||||
unsafe {
|
||||
self.base.index(self.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 3d dynamically-sized array backed by a borrowed buffer.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Array3<'a, T> {
|
||||
data: &'a UnsizedArray<T>,
|
||||
dim: UVec3,
|
||||
}
|
||||
|
||||
fn index(loc: UVec3, dim: UVec3) -> usize {
|
||||
((loc.z*dim.y + loc.y)*dim.x + loc.x) as usize
|
||||
}
|
||||
|
||||
fn checked_index(idx: UVec3, dim: UVec3) -> Optional<usize> {
|
||||
if idx.x < dim.x && idx.y < dim.y && idx.z < dim.z {
|
||||
let flat_idx = index(idx, dim);
|
||||
Optional::some(flat_idx)
|
||||
} else {
|
||||
Optional::none()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Array3<'a, T> {
|
||||
pub fn new(data: &'a UnsizedArray<T>, dim: UVec3) -> Self {
|
||||
Self {
|
||||
data,
|
||||
dim,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(self, idx: UVec3) -> Optional<usize> {
|
||||
checked_index(idx, self.dim)
|
||||
}
|
||||
|
||||
pub fn into_handle(self, idx: UVec3) -> ArrayHandle<'a, T> {
|
||||
let idx = checked_index(idx, self.dim).unwrap();
|
||||
unsafe {
|
||||
self.data.get_handle(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Copy + Default> Array3<'a, T> {
|
||||
pub fn get(&self, idx: UVec3) -> Optional<T> {
|
||||
let idx = self.index(idx);
|
||||
if idx.is_some() {
|
||||
Optional::some(unsafe {
|
||||
self.data.index(idx.unwrap())
|
||||
})
|
||||
} else {
|
||||
Optional::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl<'a, T> Array3<'a, T> {
|
||||
// pub fn get_ref(&self, idx: UVec3) -> OptionalRef<'a, T> {
|
||||
// OptionalRef::some(unsafe {
|
||||
// self.data.index(0)
|
||||
// })
|
||||
// // OptionalRef::none()
|
||||
// // let idx = self.index(idx);
|
||||
// // if idx.is_some() {
|
||||
// // OptionalRef::some(unsafe {
|
||||
// // self.data.index(idx.unwrap())
|
||||
// // })
|
||||
// // } else {
|
||||
// // OptionalRef::none()
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a> Array3<'a, Vec3Std> {
|
||||
// pub fn get(&self, idx: UVec3) -> Vec3Std {
|
||||
// let flat_idx = self.index(idx);
|
||||
// (self.data[0].0, 0.0, 0.0)
|
||||
// // let idx = self.index(idx);
|
||||
// // if idx.is_some() {
|
||||
// // Optional::some((0.0, 0.0, self.data[idx.unwrap()].0))
|
||||
// // } else {
|
||||
// // Optional::none()
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
/// 3d dynamically-sized array backed by a mutably borrowed buffer.
|
||||
pub struct Array3Mut<'a, T> {
|
||||
data: &'a mut UnsizedArray<T>,
|
||||
dim: UVec3,
|
||||
}
|
||||
|
||||
impl<'a, T> Array3Mut<'a, T> {
|
||||
pub fn new(data: &'a mut UnsizedArray<T>, dim: UVec3) -> Self {
|
||||
Self {
|
||||
data,
|
||||
dim,
|
||||
}
|
||||
}
|
||||
|
||||
// fn as_array3<'b>(&'b self) -> Array3<'b, T> {
|
||||
// Array3::new(self.data, self.dim)
|
||||
// }
|
||||
|
||||
pub fn index(&self, idx: UVec3) -> Optional<usize> {
|
||||
if idx.x < self.dim.x && idx.y < self.dim.y && idx.z < self.dim.z {
|
||||
let flat_idx = index(idx, self.dim);
|
||||
Optional::some(flat_idx)
|
||||
} else {
|
||||
Optional::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Copy> Array3Mut<'a, T> {
|
||||
pub fn into_mut_handle(self, idx: UVec3) -> ArrayHandleMut<'a, T> {
|
||||
let idx = self.index(idx).unwrap();
|
||||
unsafe {
|
||||
self.data.get_handle_mut(idx)
|
||||
}
|
||||
}
|
||||
// fn index(&self, idx: UVec3) -> Optional<usize> {
|
||||
// self.as_array3().index(idx)
|
||||
// }
|
||||
// // pub fn get(&self, idx: UVec3) -> Option<T> {
|
||||
// // self.get_ref(idx).cloned()
|
||||
// // }
|
||||
// pub fn get_ref<'b>(&'b self, idx: UVec3) -> OptionalRef<'b, T> {
|
||||
// let idx = self.index(idx);
|
||||
// if idx.is_some() {
|
||||
// OptionalRef::some(&self.data[idx.unwrap()])
|
||||
// } else {
|
||||
// OptionalRef::none()
|
||||
// }
|
||||
// }
|
||||
// pub fn get_mut(&mut self, idx: UVec3) -> Option<T> {
|
||||
// self.get_ref_mut(idx).cloned()
|
||||
// }
|
||||
// pub fn get_ref_mut<'b>(&'b mut self, idx: UVec3) -> Option<&'b mut T> {
|
||||
// self.data.get_mut(index(idx, self.dim))
|
||||
// }
|
||||
|
||||
// /// Like `get_ref_mut`, but imposes fewer lifetime requirements by consuming `self`.
|
||||
// pub fn into_ref_mut(self, idx: UVec3) -> Option<&'a mut T> {
|
||||
// self.data.get_mut(index(idx, self.dim))
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_index() {
|
||||
let dim = UVec3::new(2, 3, 7);
|
||||
assert_eq!(index(UVec3::new(0, 0, 0), dim), 0);
|
||||
assert_eq!(index(UVec3::new(1, 0, 0), dim), 1);
|
||||
assert_eq!(index(UVec3::new(0, 1, 0), dim), 2);
|
||||
assert_eq!(index(UVec3::new(1, 1, 0), dim), 3);
|
||||
assert_eq!(index(UVec3::new(0, 2, 0), dim), 4);
|
||||
assert_eq!(index(UVec3::new(0, 0, 1), dim), 6);
|
||||
assert_eq!(index(UVec3::new(1, 0, 1), dim), 7);
|
||||
assert_eq!(index(UVec3::new(0, 1, 1), dim), 8);
|
||||
assert_eq!(index(UVec3::new(1, 2, 1), dim), 11);
|
||||
assert_eq!(index(UVec3::new(1, 2, 2), dim), 17);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user