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:
116
crates/spirv_backend/src/lib.rs
Normal file
116
crates/spirv_backend/src/lib.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
#![cfg_attr(
|
||||
target_arch = "spirv",
|
||||
feature(register_attr),
|
||||
register_attr(spirv),
|
||||
no_std
|
||||
)]
|
||||
#![feature(const_fn_floating_point_arithmetic)]
|
||||
|
||||
extern crate spirv_std;
|
||||
|
||||
pub use glam::{UVec3, Vec3};
|
||||
use spirv_std::glam;
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
use spirv_std::macros::spirv;
|
||||
|
||||
pub mod mat;
|
||||
pub mod sim;
|
||||
pub mod support;
|
||||
|
||||
pub use sim::{SerializedSimMeta, SerializedStepE, SerializedStepH};
|
||||
pub use support::{Optional, UnsizedArray, UVec3Std, Vec3Std};
|
||||
|
||||
use mat::{IsoConductorOr, Ferroxcube3R1MH, FullyGenericMaterial, Material};
|
||||
|
||||
type Iso3R1 = IsoConductorOr<Ferroxcube3R1MH>;
|
||||
|
||||
fn step_h<M: Material>(
|
||||
id: UVec3,
|
||||
meta: &SerializedSimMeta,
|
||||
stimulus_h: &UnsizedArray<Vec3Std>,
|
||||
material: &UnsizedArray<M>,
|
||||
e: &UnsizedArray<Vec3Std>,
|
||||
h: &mut UnsizedArray<Vec3Std>,
|
||||
m: &mut UnsizedArray<Vec3Std>,
|
||||
) {
|
||||
if id.x < meta.dim.x() && id.y < meta.dim.y() && id.z < meta.dim.z() {
|
||||
let sim_state = SerializedStepH::new(meta, stimulus_h, material, e, h, m);
|
||||
let update_state = sim_state.index(id);
|
||||
update_state.step_h();
|
||||
}
|
||||
}
|
||||
|
||||
fn step_e<M: Material>(
|
||||
id: UVec3,
|
||||
meta: &SerializedSimMeta,
|
||||
stimulus_e: &UnsizedArray<Vec3Std>,
|
||||
material: &UnsizedArray<M>,
|
||||
e: &mut UnsizedArray<Vec3Std>,
|
||||
h: &UnsizedArray<Vec3Std>,
|
||||
) {
|
||||
if id.x < meta.dim.x() && id.y < meta.dim.y() && id.z < meta.dim.z() {
|
||||
let sim_state = SerializedStepE::new(meta, stimulus_e, material, e, h);
|
||||
|
||||
let update_state = sim_state.index(id);
|
||||
update_state.step_e();
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the step_h/step_e entry point names for the provided material
|
||||
pub fn entry_points<M: 'static>() -> Optional<(&'static str, &'static str)> {
|
||||
use core::any::TypeId;
|
||||
let mappings = [
|
||||
(TypeId::of::<FullyGenericMaterial>(),
|
||||
("step_h_generic_material", "step_e_generic_material")
|
||||
),
|
||||
(TypeId::of::<Iso3R1>(),
|
||||
("step_h_iso_3r1", "step_e_iso_3r1")
|
||||
),
|
||||
];
|
||||
|
||||
for (id, names) in mappings {
|
||||
if id == TypeId::of::<M>() {
|
||||
return Optional::some(names);
|
||||
}
|
||||
}
|
||||
Optional::none()
|
||||
}
|
||||
|
||||
macro_rules! steps {
|
||||
($mat:ty, $step_h:ident, $step_e:ident) => {
|
||||
// LocalSize/numthreads
|
||||
#[spirv(compute(threads(4, 4, 4)))]
|
||||
pub fn $step_h(
|
||||
#[spirv(global_invocation_id)] id: UVec3,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] meta: &SerializedSimMeta,
|
||||
// XXX: delete this input?
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] _unused_stimulus_e: &UnsizedArray<Vec3Std>,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 2)] stimulus_h: &UnsizedArray<Vec3Std>,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 3)] material: &UnsizedArray<$mat>,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 4)] e: &UnsizedArray<Vec3Std>,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 5)] h: &mut UnsizedArray<Vec3Std>,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 6)] m: &mut UnsizedArray<Vec3Std>,
|
||||
) {
|
||||
step_h(id, meta, stimulus_h, material, e, h, m)
|
||||
}
|
||||
|
||||
#[spirv(compute(threads(4, 4, 4)))]
|
||||
pub fn $step_e(
|
||||
#[spirv(global_invocation_id)] id: UVec3,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] meta: &SerializedSimMeta,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] stimulus_e: &UnsizedArray<Vec3Std>,
|
||||
// XXX: delete this input?
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 2)] _unused_stimulus_h: &UnsizedArray<Vec3Std>,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 3)] material: &UnsizedArray<$mat>,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 4)] e: &mut UnsizedArray<Vec3Std>,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 5)] h: &UnsizedArray<Vec3Std>,
|
||||
// XXX: can/should this m input be deleted?
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 6)] _unused_m: &UnsizedArray<Vec3Std>,
|
||||
) {
|
||||
step_e(id, meta, stimulus_e, material, e, h)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
steps!(FullyGenericMaterial, step_h_generic_material, step_e_generic_material);
|
||||
steps!(Iso3R1, step_h_iso_3r1, step_e_iso_3r1);
|
3
crates/spirv_backend/src/main.rs
Normal file
3
crates/spirv_backend/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
393
crates/spirv_backend/src/mat.rs
Normal file
393
crates/spirv_backend/src/mat.rs
Normal file
@@ -0,0 +1,393 @@
|
||||
use crate::support::{Optional, Vec3Std};
|
||||
|
||||
pub trait Material: Sized {
|
||||
fn conductivity(&self) -> Vec3Std {
|
||||
Default::default()
|
||||
}
|
||||
/// returns the new M vector for this material
|
||||
fn move_b_vec(&self, m: Vec3Std, _target_b: Vec3Std) -> Vec3Std {
|
||||
// XXX could return either 0, or `m`. they should be the same, but one might be more
|
||||
// optimizable than the other (untested).
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Conductor<T>(pub T);
|
||||
pub type AnisomorphicConductor = Conductor<Vec3Std>;
|
||||
pub type IsomorphicConductor = Conductor<f32>;
|
||||
|
||||
impl Material for AnisomorphicConductor {
|
||||
fn conductivity(&self) -> Vec3Std {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for IsomorphicConductor {
|
||||
fn conductivity(&self) -> Vec3Std {
|
||||
Vec3Std::uniform(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// M(B) parallelogram
|
||||
///
|
||||
///```ignore
|
||||
/// ____________
|
||||
/// / /
|
||||
/// / . /
|
||||
/// / /
|
||||
/// /___________/
|
||||
/// ```
|
||||
///
|
||||
/// The `.` depicts (0, 0). X axis is B; y axis is M.
|
||||
/// As B increases, M remains constant until it hits an edge.
|
||||
/// Then M rises up to its max.
|
||||
/// Same thing happens on the left edge, as B decreases and M falls to its min.
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct MBPgram {
|
||||
/// X coordinate at which the upward slope starts
|
||||
pub b_start: f32,
|
||||
/// X coordinate at which the upward slope ends
|
||||
pub b_end: f32,
|
||||
/// Vertical range of the graph
|
||||
pub max_m: f32,
|
||||
}
|
||||
|
||||
/// f(x0) = -ymax
|
||||
/// f(x1) = ymax
|
||||
fn eval_bounded_line(x: f32, x0: f32, x1: f32, ymax: f32) -> f32 {
|
||||
let x = x - x0;
|
||||
let x1 = x1 - x0;
|
||||
// Now, f(0) = -ymax, f(x1) = ymax
|
||||
let x = x.max(0.0).min(x1);
|
||||
ymax * (2.0*x/x1 - 1.0)
|
||||
}
|
||||
|
||||
impl MBPgram {
|
||||
pub fn new(b_start: f32, b_end: f32, max_m: f32) -> Self {
|
||||
Self { b_start, b_end, max_m }
|
||||
}
|
||||
|
||||
/// Return the new `M`
|
||||
pub fn move_b(self, m: f32, target_b: f32) -> f32 {
|
||||
// Evaluate the curve on the right:
|
||||
let y_right = eval_bounded_line(target_b, self.b_start, self.b_end, self.max_m);
|
||||
let y_left = eval_bounded_line(target_b, -self.b_end, -self.b_start, self.max_m);
|
||||
m.max(y_right).min(y_left)
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for MBPgram {
|
||||
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
|
||||
Vec3Std::new(
|
||||
self.move_b(m.x(), target_b.x()),
|
||||
self.move_b(m.y(), target_b.y()),
|
||||
self.move_b(m.z(), target_b.z()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct MHPgram {
|
||||
/// optimized form of mu_0^-1 * (1-mu_r^-1)
|
||||
b_mult: f32,
|
||||
/// optimized form of h_intercept * (1-mu_r^-1)
|
||||
m_offset: f32,
|
||||
/// Vertical range of the graph
|
||||
pub max_m: f32,
|
||||
}
|
||||
|
||||
impl MHPgram {
|
||||
/// h_intercept: X coordinate at which M is always zero.
|
||||
/// mu_r: relative mu value along the non-flat edges of the parallelogram.
|
||||
pub const fn new(h_intercept: f32, mu_r: f32, max_m: f32) -> Self {
|
||||
const MU0_INV: f32 = 795774.715025073;
|
||||
let one_minus_mu_r_inv = 1.0 - 1.0/mu_r;
|
||||
Self {
|
||||
b_mult: MU0_INV * one_minus_mu_r_inv,
|
||||
m_offset: h_intercept * one_minus_mu_r_inv,
|
||||
max_m,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn h_intercept(&self) -> f32 {
|
||||
const MU0_INV: f32 = 795774.715025073;
|
||||
MU0_INV * self.m_offset / self.b_mult
|
||||
}
|
||||
pub fn mu_r(&self) -> f32 {
|
||||
const MU0: f32 = 1.2566370621219e-06;
|
||||
let mu_r_inv = 1.0 - MU0*self.b_mult;
|
||||
1.0 / mu_r_inv
|
||||
}
|
||||
|
||||
/// Return the new `M`
|
||||
pub fn move_b(self, m: f32, target_b: f32) -> f32 {
|
||||
// The point may exist outside the parallelogram.
|
||||
// The right slope is defined by:
|
||||
// B(H)/mu0 = h_intercept + (h-h_intercept)*mu_r
|
||||
// "unoptimized" solution:
|
||||
// let target_mh = target_b*MU0_INV;
|
||||
// let h_on_right = (target_mh-self.h_intercept)/self.mu_r + self.h_intercept;
|
||||
// let m_on_right = target_mh - h_on_right;
|
||||
// Left:
|
||||
// B(H)/mu0 = -h_intercept + (h+h_intercept)*mu_r
|
||||
// "unoptimized" solution:
|
||||
// let h_on_left = (target_mh+self.h_intercept)/self.mu_r - self.h_intercept;
|
||||
// let m_on_left = target_mh - h_on_left;
|
||||
|
||||
let m_on_right = target_b*self.b_mult - self.m_offset;
|
||||
let m_on_left = target_b*self.b_mult + self.m_offset;
|
||||
|
||||
if m_on_right >= m {
|
||||
// if m_on_right <= self.max_m {
|
||||
// // rightward edge movement
|
||||
// m_on_right
|
||||
// } else {
|
||||
// // right of saturation
|
||||
// self.max_m
|
||||
// }
|
||||
m_on_right.min(self.max_m)
|
||||
} else if m_on_left <= m {
|
||||
// if m_on_left >= -self.max_m {
|
||||
// // leftward edge movement
|
||||
// m_on_left
|
||||
// } else {
|
||||
// // left of saturation
|
||||
// -self.max_m
|
||||
// }
|
||||
m_on_left.max(-self.max_m)
|
||||
} else {
|
||||
// interior movement
|
||||
m
|
||||
}
|
||||
// this boils down to:
|
||||
// m.clamp(m_on_left, m_on_right).clamp(-max_m, max_m)
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for MHPgram {
|
||||
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
|
||||
Vec3Std::new(
|
||||
self.move_b(m.x(), target_b.x()),
|
||||
self.move_b(m.y(), target_b.y()),
|
||||
self.move_b(m.z(), target_b.z()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct FullyGenericMaterial {
|
||||
pub conductivity: Vec3Std,
|
||||
pub m_b_curve: Optional<MBPgram>,
|
||||
pub m_h_curve: Optional<MHPgram>,
|
||||
}
|
||||
|
||||
impl Material for FullyGenericMaterial {
|
||||
fn conductivity(&self) -> Vec3Std {
|
||||
self.conductivity
|
||||
}
|
||||
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
|
||||
if self.m_b_curve.is_some() {
|
||||
self.m_b_curve.unwrap().move_b_vec(m, target_b)
|
||||
} else if self.m_h_curve.is_some() {
|
||||
self.m_h_curve.unwrap().move_b_vec(m, target_b)
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// MHPgram that's vaguely similar to Ferroxcube's 3R1 material
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Ferroxcube3R1MH;
|
||||
|
||||
impl Into<MHPgram> for Ferroxcube3R1MH {
|
||||
fn into(self) -> MHPgram {
|
||||
const CURVE: MHPgram = MHPgram::new(25.0, 881.33, 44000.0);
|
||||
CURVE
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Ferroxcube3R1MH {
|
||||
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
|
||||
let curve: MHPgram = (*self).into();
|
||||
curve.move_b_vec(m, target_b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Optimized material aimed at saving space for the common case of a simulation that contains
|
||||
/// isomorphic conductors plus one exception material.
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct IsoConductorOr<M> {
|
||||
/// conductivity, if >= 0, else ignored & used to signal the other material
|
||||
pub value: f32,
|
||||
pub mat: M,
|
||||
}
|
||||
|
||||
impl<M: Default> IsoConductorOr<M> {
|
||||
pub fn new_conductor(conductivity: f32) -> Self {
|
||||
Self {
|
||||
value: conductivity,
|
||||
mat: Default::default(),
|
||||
}
|
||||
}
|
||||
pub fn new_other() -> Self {
|
||||
Self {
|
||||
value: -1.0,
|
||||
mat: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Material + Copy> Material for IsoConductorOr<M> {
|
||||
fn conductivity(&self) -> Vec3Std {
|
||||
Vec3Std::uniform(self.value.max(0.0))
|
||||
}
|
||||
fn move_b_vec(&self, m: Vec3Std, target_b: Vec3Std) -> Vec3Std {
|
||||
if self.value < 0.0 {
|
||||
let mat = self.mat; //< XXX hack for ZST
|
||||
mat.move_b_vec(m, target_b)
|
||||
} else {
|
||||
m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn assert_eq_approx(actual: f32, expected: f32, max_error: f32) {
|
||||
assert!((actual - expected).abs() < max_error, "{} != {}", actual, expected);
|
||||
}
|
||||
|
||||
fn assert_eq_approx_tight(actual: f32, expected: f32) {
|
||||
assert_eq_approx(actual, expected, 1e-5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mb_curve_interior() {
|
||||
let curve = MBPgram::new(4.0, 6.0, 20.0f32);
|
||||
|
||||
assert_eq_approx_tight(curve.move_b(0.0, 2.0), 0.0);
|
||||
assert_eq_approx_tight(curve.move_b(0.0, 5.0), 0.0);
|
||||
assert_eq_approx_tight(curve.move_b(1.0, 5.0), 1.0);
|
||||
assert_eq_approx_tight(curve.move_b(20.0, 5.0), 20.0);
|
||||
assert_eq_approx_tight(curve.move_b(-20.0, 4.0), -20.0);
|
||||
assert_eq_approx_tight(curve.move_b(-20.0, -6.0), -20.0);
|
||||
assert_eq_approx_tight(curve.move_b(20.0, -4.0), 20.0);
|
||||
assert_eq_approx_tight(curve.move_b(10.0, -2.0), 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mb_curve_exterior() {
|
||||
let curve = MBPgram::new(4.0, 6.0, 20.0f32);
|
||||
assert_eq_approx_tight(curve.move_b(0.0, 6.0), 20.0);
|
||||
assert_eq_approx_tight(curve.move_b(0.0, 7.0), 20.0);
|
||||
assert_eq_approx_tight(curve.move_b(0.0, -6.0), -20.0);
|
||||
assert_eq_approx_tight(curve.move_b(0.0, -7.0), -20.0);
|
||||
|
||||
assert_eq_approx_tight(curve.move_b(2.0, -6.0), -20.0);
|
||||
assert_eq_approx_tight(curve.move_b(20.0, -6.0), -20.0);
|
||||
assert_eq_approx_tight(curve.move_b(20.0, -5.0), 0.0);
|
||||
assert_eq_approx_tight(curve.move_b(20.0, -4.5), 10.0);
|
||||
assert_eq_approx_tight(curve.move_b(-15.0, 4.5), -10.0);
|
||||
assert_eq_approx_tight(curve.move_b(-15.0, 5.0), 0.0);
|
||||
assert_eq_approx_tight(curve.move_b(-15.0, 5.5), 10.0);
|
||||
assert_eq_approx_tight(curve.move_b(-15.0, 7.5), 20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mb_curve_3r1() {
|
||||
// slope of 3r1 is about M=793210*B
|
||||
// This is almost identical to iron (795615)!
|
||||
let curve = MBPgram::new(-0.3899, 0.3900, 310000f32);
|
||||
|
||||
// magnetizing:
|
||||
// v.s. 198893 in B(H) curve
|
||||
assert_eq_approx(curve.move_b(0.0, 0.250), 198703.0, 1.0);
|
||||
// v.s. 278321 in B(H) curve
|
||||
assert_eq_approx(curve.move_b(198703.0, 0.350), 278201.0, 1.0);
|
||||
assert_eq_approx(curve.move_b(278201.0, 0.390), 310000.0, 1.0);
|
||||
|
||||
// de-magnetizing:
|
||||
// From saturation, decreasing B causes NO decrease in M: instead, it causes a decrease in
|
||||
// H. This is probably BAD: in the B(H) curve, a large change in H always causes a large
|
||||
// change in B. The movement of H here is likely to induce current, whereas it SHOULDN'T.
|
||||
assert_eq_approx(curve.move_b(310000.0, 0.38995), 310000.0, 1.0);
|
||||
// v.s. 258626 in B(H); H = 220
|
||||
assert_eq_approx(curve.move_b(310000.0, 0.325), 258406.0, 1.0);
|
||||
// here's where H crosses 0 (v.s. B=0.325 in the B(H) curve... quite a difference)
|
||||
assert_eq_approx(curve.move_b(310000.0, 0.050), 39788.438, 1.0);
|
||||
// v.s. 35.0 in B(H)
|
||||
assert_eq_approx(curve.move_b(310000.0, 0.0), 39.75, 1.0);
|
||||
|
||||
// negative magnetization:
|
||||
// erase the magnetization: H = -40
|
||||
assert_eq_approx(curve.move_b(310000.0, -0.00005), 0.0, 1.0);
|
||||
// the magnetization has been completely erased:
|
||||
assert_eq_approx(curve.move_b(310000.0, -0.25), -198703.0, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mh_curve_parameters() {
|
||||
let curve = MHPgram::new(50.0, 101.0, 500.0);
|
||||
assert_eq_approx(curve.h_intercept(), 50.0, 1e-3);
|
||||
assert_eq_approx(curve.mu_r(), 101.0, 1e-3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mh_curve_edge_travel() {
|
||||
const MU0: f32 = 1.2566370621219e-06;
|
||||
let curve = MHPgram::new(50.0, 101.0, 500.0);
|
||||
|
||||
assert_eq_approx(curve.move_b(0.0, 151.0*MU0), 100.0, 0.1); // rightward travel along edge
|
||||
assert_eq_approx(curve.move_b(100.0, 252.0*MU0), 200.0, 0.1);
|
||||
assert_eq_approx(curve.move_b(200.0, 555.0*MU0), 500.0, 0.1);
|
||||
assert_eq_approx(curve.move_b(500.0, 500.0*MU0), 500.0, 0.1); // back (leftward) travel to H=0
|
||||
assert_eq_approx(curve.move_b(500.0, 455.0*MU0), 500.0, 0.1); // back (leftward) travel to H=-45
|
||||
assert_eq_approx(curve.move_b(500.0, 354.0*MU0), 400.0, 0.1); // back (leftward) travel to H=-46
|
||||
assert_eq_approx(curve.move_b(400.0, 253.0*MU0), 300.0, 0.1); // back (leftward) travel to H=-47
|
||||
assert_eq_approx(curve.move_b(300.0, -50.0*MU0), 0.0, 0.1); // back (leftward) travel to H=-50
|
||||
assert_eq_approx(curve.move_b(0.0, -151.0*MU0), -100.0, 0.1); // back (leftward) travel to H=-51
|
||||
assert_eq_approx(curve.move_b(-100.0, -555.0*MU0), -500.0, 0.1); // back (leftward) travel to H=-55
|
||||
assert_eq_approx(curve.move_b(-500.0, -456.0*MU0), -500.0, 0.1); // interior (rightward) travel to H=44
|
||||
assert_eq_approx(curve.move_b(-500.0, -354.0*MU0), -400.0, 0.1); // interior (rightward) travel to H=46
|
||||
assert_eq_approx(curve.move_b(-400.0, -253.0*MU0), -300.0, 0.1); // interior (rightward) travel to H=47
|
||||
assert_eq_approx(curve.move_b(-300.0, 50.0*MU0), 0.0, 0.1); // interior (rightward) travel to H=50
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mh_curve_interior() {
|
||||
const MU0: f32 = 1.2566370621219e-06;
|
||||
let curve = MHPgram::new(50.0, 101.0, 500.0);
|
||||
// max M (right) happens at H=55; B = 555*MU0;
|
||||
// min M (right) happens at H=45; B = -455*MU0;
|
||||
// max M (left) happens at H=-45; B = 455*MU0;
|
||||
// min M (left) happens at H=-55; B = -555*MU0;
|
||||
|
||||
assert_eq_approx(curve.move_b(0.0, 40.0*MU0), 0.0, 0.1);
|
||||
assert_eq_approx(curve.move_b(0.0, 50.0*MU0), 0.0, 0.1);
|
||||
assert_eq_approx(curve.move_b(0.0, -40.0*MU0), 0.0, 0.1);
|
||||
assert_eq_approx(curve.move_b(0.0, -50.0*MU0), 0.0, 0.1);
|
||||
|
||||
assert_eq_approx(curve.move_b(-400.0, -400.0*MU0), -400.0, 0.1); // rightward travel from H=-54 to NOT H=-53.5
|
||||
assert_eq_approx(curve.move_b(-400.0, -355.0*MU0), -400.0, 0.1); // rightward travel from H=-54 to H=45
|
||||
assert_eq_approx(curve.move_b(-400.0, -354.0*MU0), -400.0, 0.1); // rightward travel from H=-54 to H=46
|
||||
assert_eq_approx(curve.move_b(-400.0, 5000.0*MU0), 500.0, 0.1); // rightward travel from H=-54 to H>>55
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mh_curve_exterior() {
|
||||
const MU0: f32 = 1.2566370621219e-06;
|
||||
let curve = MHPgram::new(50.0, 101.0, 500.0);
|
||||
|
||||
assert_eq_approx(curve.move_b(500.0, 556.0*MU0), 500.0, 0.1); // exterior travel to H=56
|
||||
assert_eq_approx(curve.move_b(500.0, 5000.0*MU0), 500.0, 0.1); // exterior travel to H>>55
|
||||
|
||||
assert_eq_approx(curve.move_b(500.0, -5000.0*MU0), -500.0, 0.1); // exterior travel from H>>55 to H << -55
|
||||
assert_eq_approx(curve.move_b(-501.0, 454.0*MU0), 400.0, 0.1); // exterior travel from H<<-55 (rounding error) to H=54
|
||||
|
||||
}
|
||||
}
|
343
crates/spirv_backend/src/sim.rs
Normal file
343
crates/spirv_backend/src/sim.rs
Normal file
@@ -0,0 +1,343 @@
|
||||
// use spirv_std::RuntimeArray;
|
||||
use spirv_std::glam::UVec3;
|
||||
use crate::mat::Material;
|
||||
use crate::support::{Array3, Array3Mut, ArrayHandle, ArrayHandleMut, Optional, UnsizedArray, UVec3Std, Vec3Std};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SerializedSimMeta {
|
||||
// XXX this HAS to be the tuple version instead of the glam dim, else:
|
||||
// OpLoad Pointer <id> '138[%138]' is not a logical pointer.
|
||||
pub dim: UVec3Std,
|
||||
pub inv_feature_size: f32,
|
||||
pub time_step: f32,
|
||||
pub feature_size: f32,
|
||||
}
|
||||
|
||||
impl SerializedSimMeta {
|
||||
fn dim(&self) -> UVec3 {
|
||||
self.dim.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Whatever data we received from the host in their call to step_h
|
||||
pub struct SerializedStepH<'a, M> {
|
||||
meta: &'a SerializedSimMeta,
|
||||
stimulus_h: &'a UnsizedArray<Vec3Std>,
|
||||
material: &'a UnsizedArray<M>,
|
||||
e: &'a UnsizedArray<Vec3Std>,
|
||||
h: &'a mut UnsizedArray<Vec3Std>,
|
||||
m: &'a mut UnsizedArray<Vec3Std>,
|
||||
}
|
||||
|
||||
impl<'a, M> SerializedStepH<'a, M> {
|
||||
pub fn new(
|
||||
meta: &'a SerializedSimMeta,
|
||||
stimulus_h: &'a UnsizedArray<Vec3Std>,
|
||||
material: &'a UnsizedArray<M>,
|
||||
e: &'a UnsizedArray<Vec3Std>,
|
||||
h: &'a mut UnsizedArray<Vec3Std>,
|
||||
m: &'a mut UnsizedArray<Vec3Std>,
|
||||
) -> Self {
|
||||
Self {
|
||||
meta, stimulus_h, material, e, h, m
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(self, idx: UVec3) -> StepHContext<'a, M> {
|
||||
let dim = self.meta.dim();
|
||||
let stim_h_matrix = Array3::new(self.stimulus_h, dim);
|
||||
let mat_matrix = Array3::new(self.material, dim);
|
||||
let e = Array3::new(self.e, dim);
|
||||
let h = Array3Mut::new(self.h, dim);
|
||||
let m = Array3Mut::new(self.m, dim);
|
||||
|
||||
let in_e = VolumeSamplePos {
|
||||
mid: e.get(idx).unwrap(),
|
||||
xp1: e.get(idx + UVec3::X),
|
||||
yp1: e.get(idx + UVec3::Y),
|
||||
zp1: e.get(idx + UVec3::Z),
|
||||
};
|
||||
let out_h = h.into_mut_handle(idx);
|
||||
let out_m = m.into_mut_handle(idx);
|
||||
|
||||
let mat = mat_matrix.into_handle(idx);
|
||||
|
||||
StepHContext {
|
||||
inv_feature_size: self.meta.inv_feature_size,
|
||||
time_step: self.meta.time_step,
|
||||
stim_h: stim_h_matrix.get(idx).unwrap(),
|
||||
mat,
|
||||
in_e,
|
||||
out_h,
|
||||
out_m,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whatever data we received from the host in their call to step_e
|
||||
pub struct SerializedStepE<'a, M> {
|
||||
meta: &'a SerializedSimMeta,
|
||||
stimulus_e: &'a UnsizedArray<Vec3Std>,
|
||||
material: &'a UnsizedArray<M>,
|
||||
e: &'a mut UnsizedArray<Vec3Std>,
|
||||
h: &'a UnsizedArray<Vec3Std>,
|
||||
}
|
||||
|
||||
impl<'a, M> SerializedStepE<'a, M> {
|
||||
pub fn new(
|
||||
meta: &'a SerializedSimMeta,
|
||||
stimulus_e: &'a UnsizedArray<Vec3Std>,
|
||||
material: &'a UnsizedArray<M>,
|
||||
e: &'a mut UnsizedArray<Vec3Std>,
|
||||
h: &'a UnsizedArray<Vec3Std>,
|
||||
) -> Self {
|
||||
Self {
|
||||
meta, stimulus_e, material, e, h
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(self, idx: UVec3) -> StepEContext<'a, M> {
|
||||
let dim = self.meta.dim();
|
||||
let stim_e_matrix = Array3::new(self.stimulus_e, dim);
|
||||
let mat_matrix = Array3::new(self.material, dim);
|
||||
let e = Array3Mut::new(self.e, dim);
|
||||
let h = Array3::new(self.h, dim);
|
||||
|
||||
let xm1 = if idx.x == 0 {
|
||||
Optional::none()
|
||||
} else {
|
||||
h.get(idx - UVec3::X)
|
||||
};
|
||||
let ym1 = if idx.y == 0 {
|
||||
Optional::none()
|
||||
} else {
|
||||
h.get(idx - UVec3::Y)
|
||||
};
|
||||
let zm1 = if idx.z == 0 {
|
||||
Optional::none()
|
||||
} else {
|
||||
h.get(idx - UVec3::Z)
|
||||
};
|
||||
|
||||
let in_h = VolumeSampleNeg {
|
||||
mid: h.get(idx).unwrap(),
|
||||
xm1,
|
||||
ym1,
|
||||
zm1,
|
||||
};
|
||||
let out_e = e.into_mut_handle(idx);
|
||||
|
||||
let mat = mat_matrix.into_handle(idx);
|
||||
|
||||
StepEContext {
|
||||
inv_feature_size: self.meta.inv_feature_size,
|
||||
time_step: self.meta.time_step,
|
||||
stim_e: stim_e_matrix.get(idx).unwrap(),
|
||||
mat,
|
||||
in_h,
|
||||
out_e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Package the field vectors adjacent to some particular location.
|
||||
/// Particular those at negative offsets from the midpoint.
|
||||
/// This is used in step_e when looking at the H field deltas.
|
||||
#[derive(Copy, Clone)]
|
||||
struct VolumeSampleNeg {
|
||||
mid: Vec3Std,
|
||||
xm1: Optional<Vec3Std>,
|
||||
ym1: Optional<Vec3Std>,
|
||||
zm1: Optional<Vec3Std>,
|
||||
}
|
||||
|
||||
impl VolumeSampleNeg {
|
||||
/// Calculate the delta in H values amongst this cell and its neighbors (left/up/out)
|
||||
fn delta_h(self) -> FieldDeltas {
|
||||
let mid = self.mid;
|
||||
// let (dfy_dx, dfz_dx) = self.xm1.map(|xm1| {
|
||||
// (mid.y() - xm1.y(), mid.z() - xm1.z())
|
||||
// }).unwrap_or_default();
|
||||
|
||||
// let (dfx_dy, dfz_dy) = self.ym1.map(|ym1| {
|
||||
// (mid.x() - ym1.x(), mid.z() - ym1.z())
|
||||
// }).unwrap_or_default();
|
||||
|
||||
// let (dfx_dz, dfy_dz) = self.zm1.map(|zm1| {
|
||||
// (mid.x() - zm1.x(), mid.y() - zm1.y())
|
||||
// }).unwrap_or_default();
|
||||
|
||||
let (dfy_dx, dfz_dx) = if self.xm1.is_some() {
|
||||
(mid.y() - self.xm1.unwrap().y(), mid.z() - self.xm1.unwrap().z())
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
};
|
||||
|
||||
let (dfx_dy, dfz_dy) = if self.ym1.is_some() {
|
||||
(mid.x() - self.ym1.unwrap().x(), mid.z() - self.ym1.unwrap().z())
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
};
|
||||
|
||||
let (dfx_dz, dfy_dz) = if self.zm1.is_some() {
|
||||
(mid.x() - self.zm1.unwrap().x(), mid.y() - self.zm1.unwrap().y())
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
};
|
||||
|
||||
FieldDeltas {
|
||||
dfy_dx,
|
||||
dfz_dx,
|
||||
dfx_dy,
|
||||
dfz_dy,
|
||||
dfx_dz,
|
||||
dfy_dz,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Package the field vectors adjacent to some particular location.
|
||||
/// Particular those at positive offsets from the midpoint.
|
||||
/// This is used in step_h when looking at the E field deltas.
|
||||
#[derive(Copy, Clone)]
|
||||
struct VolumeSamplePos {
|
||||
mid: Vec3Std,
|
||||
xp1: Optional<Vec3Std>,
|
||||
yp1: Optional<Vec3Std>,
|
||||
zp1: Optional<Vec3Std>
|
||||
}
|
||||
|
||||
impl VolumeSamplePos {
|
||||
/// Calculate the delta in E values amongst this cell and its neighbors (right/down/in)
|
||||
fn delta_e(self) -> FieldDeltas {
|
||||
let mid = self.mid;
|
||||
// let (dfy_dx, dfz_dx) = self.xp1.map(|xp1| {
|
||||
// (xp1.y() - mid.y(), xp1.z() - mid.z())
|
||||
// }).unwrap_or_default();
|
||||
|
||||
// let (dfx_dy, dfz_dy) = self.yp1.map(|yp1| {
|
||||
// (yp1.x() - mid.x(), yp1.z() - mid.z())
|
||||
// }).unwrap_or_default();
|
||||
|
||||
// let (dfx_dz, dfy_dz) = self.zp1.map(|zp1| {
|
||||
// (zp1.x() - mid.x(), zp1.y() - mid.y())
|
||||
// }).unwrap_or_default();
|
||||
|
||||
let (dfy_dx, dfz_dx) = if self.xp1.is_some() {
|
||||
(self.xp1.unwrap().y() - mid.y(), self.xp1.unwrap().z() - mid.z())
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
};
|
||||
|
||||
let (dfx_dy, dfz_dy) = if self.yp1.is_some() {
|
||||
(self.yp1.unwrap().x() - mid.x(), self.yp1.unwrap().z() - mid.z())
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
};
|
||||
|
||||
let (dfx_dz, dfy_dz) = if self.zp1.is_some() {
|
||||
(self.zp1.unwrap().x() - mid.x(), self.zp1.unwrap().y() - mid.y())
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
};
|
||||
|
||||
FieldDeltas {
|
||||
dfy_dx,
|
||||
dfz_dx,
|
||||
dfx_dy,
|
||||
dfz_dy,
|
||||
dfx_dz,
|
||||
dfy_dz,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FieldDeltas {
|
||||
dfy_dx: f32,
|
||||
dfz_dx: f32,
|
||||
dfx_dy: f32,
|
||||
dfz_dy: f32,
|
||||
dfx_dz: f32,
|
||||
dfy_dz: f32,
|
||||
}
|
||||
|
||||
impl FieldDeltas {
|
||||
fn nabla(self) -> Vec3Std {
|
||||
Vec3Std::new(
|
||||
self.dfz_dy - self.dfy_dz,
|
||||
self.dfx_dz - self.dfz_dx,
|
||||
self.dfy_dx - self.dfx_dy,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StepEContext<'a, M> {
|
||||
inv_feature_size: f32,
|
||||
time_step: f32,
|
||||
stim_e: Vec3Std,
|
||||
mat: ArrayHandle<'a, M>,
|
||||
/// Input field sampled near this location
|
||||
in_h: VolumeSampleNeg,
|
||||
/// Handle to the output field at one specific index.
|
||||
out_e: ArrayHandleMut<'a, Vec3Std>,
|
||||
}
|
||||
|
||||
impl<'a, M: Material> StepEContext<'a, M> {
|
||||
pub fn step_e(mut self) {
|
||||
// const EPS0_INV: f32 = 112940906737.1361;
|
||||
const TWICE_EPS0: f32 = 1.7708375625626e-11;
|
||||
let deltas = self.in_h.delta_h();
|
||||
// \nabla x H
|
||||
let nabla_h = deltas.nabla() * self.inv_feature_size;
|
||||
// $\nabla x H = \epsilon_0 dE/dt + \sigma E$
|
||||
// no-conductivity version:
|
||||
// let delta_e = nabla_h * (self.time_step * EPS0_INV);
|
||||
let sigma = self.mat.get_ref().conductivity();
|
||||
let e_prev = self.out_e.get();
|
||||
let delta_e = (nabla_h - e_prev.elem_mul(sigma)).elem_div(
|
||||
sigma*self.time_step + Vec3Std::uniform(TWICE_EPS0)
|
||||
)*(2.0*self.time_step);
|
||||
// println!("spirv-step_e delta_e: {:?}", delta_e);
|
||||
self.out_e.write(e_prev + delta_e + self.stim_e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct StepHContext<'a, M> {
|
||||
inv_feature_size: f32,
|
||||
time_step: f32,
|
||||
stim_h: Vec3Std,
|
||||
mat: ArrayHandle<'a, M>,
|
||||
/// Input field sampled near this location
|
||||
in_e: VolumeSamplePos,
|
||||
/// Handle to the output field at one specific index.
|
||||
out_h: ArrayHandleMut<'a, Vec3Std>,
|
||||
out_m: ArrayHandleMut<'a, Vec3Std>,
|
||||
}
|
||||
|
||||
impl<'a, M: Material> StepHContext<'a, M> {
|
||||
pub fn step_h(mut self) {
|
||||
const MU0: f32 = 1.2566370621219e-06;
|
||||
const MU0_INV: f32 = 795774.715025073;
|
||||
let deltas = self.in_e.delta_e();
|
||||
// println!("spirv-step_h delta_e_struct: {:?}", deltas);
|
||||
// \nabla x E
|
||||
let nabla_e = deltas.nabla() * self.inv_feature_size;
|
||||
// println!("spirv-step_h nabla_e: {:?}", nabla_e);
|
||||
let delta_b = nabla_e * (-self.time_step);
|
||||
|
||||
// Relation between these is: B = mu0*(H + M)
|
||||
let old_h = self.out_h.get();
|
||||
let old_m = self.out_m.get();
|
||||
let old_b = (old_h + old_m) * MU0;
|
||||
|
||||
let new_b = old_b + delta_b + self.stim_h * MU0;
|
||||
let mat = self.mat.get_ref();
|
||||
let new_m = mat.move_b_vec(old_m, new_b);
|
||||
let new_h = new_b * MU0_INV - new_m;
|
||||
// println!("spirv-step_h delta_h: {:?}", delta_h);
|
||||
self.out_h.write(new_h);
|
||||
self.out_m.write(new_m);
|
||||
}
|
||||
}
|
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