Add interval interpreter
This commit is contained in:
+412
-2
@@ -6,6 +6,7 @@ use std::simd::{
|
||||
|
||||
use crate::{
|
||||
BYTECODE_IMITATES_GLSL, CSG,
|
||||
interval::Interval,
|
||||
ssa::{SSAInput, SSAInstruction, SSAOpcode},
|
||||
};
|
||||
|
||||
@@ -36,6 +37,22 @@ fn glfract(f: Value) -> Value {
|
||||
}
|
||||
}
|
||||
|
||||
fn interval_glsign(f: Interval) -> Interval {
|
||||
if BYTECODE_IMITATES_GLSL {
|
||||
f.simd_eq(Interval::ZERO).select(f, f.signum())
|
||||
} else {
|
||||
f.signum()
|
||||
}
|
||||
}
|
||||
|
||||
fn interval_glfract(f: Interval) -> Interval {
|
||||
if BYTECODE_IMITATES_GLSL {
|
||||
f - f.floor()
|
||||
} else {
|
||||
f.fract()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct PointInterpreter<'csg> {
|
||||
value_map: Vec<Value>,
|
||||
@@ -109,8 +126,8 @@ impl<'csg> PointInterpreter<'csg> {
|
||||
}
|
||||
}
|
||||
|
||||
// cargo asm "erroccfisuvsc::interpreter::Interpreter::scene" --no-color --rust
|
||||
// > scene.asm
|
||||
// cargo asm "erroccfisuvsc::interpreter::PointInterpreter::scene" --no-color
|
||||
// --rust > scene.asm
|
||||
|
||||
pub(crate) fn scene(&mut self, px: Value, py: Value, pz: Value) -> Value {
|
||||
self.clear_stacks();
|
||||
@@ -441,3 +458,396 @@ impl<'csg> PointInterpreter<'csg> {
|
||||
return VALUE_NAN;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct IntervalInterpreter<'csg> {
|
||||
value_map: Vec<Interval>,
|
||||
csg: &'csg CSG,
|
||||
}
|
||||
|
||||
impl IntervalInterpreter<'_> {
|
||||
fn load(&self, consider: SSAInput) -> Interval {
|
||||
match consider {
|
||||
SSAInput::Register(r) => self.value_map[r as usize],
|
||||
SSAInput::Constant(c) => Interval::splat(c),
|
||||
}
|
||||
}
|
||||
|
||||
fn store(&mut self, location: u32, value: Interval) {
|
||||
self.value_map[location as usize] = value;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'csg> IntervalInterpreter<'csg> {
|
||||
pub(crate) fn new(csg: &'csg CSG) -> Self {
|
||||
IntervalInterpreter {
|
||||
value_map: vec![Interval::ZERO; csg.parts.last_output as usize],
|
||||
csg,
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_stacks(&mut self) {
|
||||
self.value_map = vec![Interval::ZERO; self.csg.parts.last_output as usize];
|
||||
}
|
||||
|
||||
fn param_one(&mut self, instruction: &SSAInstruction, func: impl Fn(Interval) -> Interval) {
|
||||
for i in 0..instruction.opcode.size as usize {
|
||||
let val_a = self.load(instruction.inputs[i]);
|
||||
self.store(instruction.outputs[i], func(val_a));
|
||||
}
|
||||
}
|
||||
|
||||
fn param_two(
|
||||
&mut self,
|
||||
instruction: &SSAInstruction,
|
||||
func: impl Fn(Interval, Interval) -> Interval,
|
||||
) {
|
||||
for i in 0..instruction.opcode.size as usize {
|
||||
let val_a = self.load(instruction.inputs[i]);
|
||||
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
|
||||
self.store(instruction.outputs[i], func(val_a, val_b));
|
||||
}
|
||||
}
|
||||
|
||||
fn param_three(
|
||||
&mut self,
|
||||
instruction: &SSAInstruction,
|
||||
func: impl Fn(Interval, Interval, Interval) -> Interval,
|
||||
) {
|
||||
for i in 0..instruction.opcode.size as usize {
|
||||
let val_a = self.load(instruction.inputs[i]);
|
||||
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
|
||||
let val_c = self.load(instruction.inputs[i + (instruction.opcode.size * 2) as usize]);
|
||||
self.store(instruction.outputs[i], func(val_a, val_b, val_c));
|
||||
}
|
||||
}
|
||||
|
||||
fn param_four(
|
||||
&mut self,
|
||||
instruction: &SSAInstruction,
|
||||
func: impl Fn(Interval, Interval, Interval, Interval) -> Interval,
|
||||
) {
|
||||
for i in 0..instruction.opcode.size as usize {
|
||||
let val_a = self.load(instruction.inputs[i]);
|
||||
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
|
||||
let val_c = self.load(instruction.inputs[i + (instruction.opcode.size * 2) as usize]);
|
||||
let val_d = self.load(instruction.inputs[i + (instruction.opcode.size * 3) as usize]);
|
||||
self.store(instruction.outputs[i], func(val_a, val_b, val_c, val_d));
|
||||
}
|
||||
}
|
||||
|
||||
// cargo asm "erroccfisuvsc::interpreter::IntervalInterpreter::scene" --no-color
|
||||
// --rust > scene.asm
|
||||
|
||||
pub(crate) fn scene(&mut self, px: Interval, py: Interval, pz: Interval) -> Interval {
|
||||
self.clear_stacks();
|
||||
|
||||
for instruction in &self.csg.parts.tape {
|
||||
use SSAOpcode::*;
|
||||
match instruction.opcode.opcode {
|
||||
SSAReturn => {
|
||||
return self.load(instruction.inputs[0]);
|
||||
},
|
||||
SSAPosition => {
|
||||
self.store(instruction.outputs[0], px);
|
||||
self.store(instruction.outputs[1], py);
|
||||
self.store(instruction.outputs[2], pz);
|
||||
},
|
||||
SSAAdd => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a + val_b);
|
||||
},
|
||||
SSASub => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a - val_b);
|
||||
},
|
||||
SSAMul => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a * val_b);
|
||||
},
|
||||
SSADiv => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a / val_b);
|
||||
},
|
||||
SSAMod => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a % val_b);
|
||||
},
|
||||
SSAAtan2 => self.param_two(instruction, |val_a, val_b| val_a.atan2(val_b)),
|
||||
SSAMin => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a.min_choice(val_b).0);
|
||||
},
|
||||
SSAMinMaterial => todo!(),
|
||||
SSAMax => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a.max_choice(val_b).0);
|
||||
},
|
||||
SSAMaxMaterial => todo!(),
|
||||
SSACross => {
|
||||
let a_x = self.load(instruction.inputs[0]);
|
||||
let a_y = self.load(instruction.inputs[1]);
|
||||
let a_z = self.load(instruction.inputs[2]);
|
||||
let b_x = self.load(instruction.inputs[3]);
|
||||
let b_y = self.load(instruction.inputs[4]);
|
||||
let b_z = self.load(instruction.inputs[5]);
|
||||
self.store(instruction.outputs[0], (a_y * b_z) + (a_z * b_y));
|
||||
self.store(instruction.outputs[1], (a_z * b_x) + (a_x * b_z));
|
||||
self.store(instruction.outputs[2], (a_x * b_y) + (a_y * b_x));
|
||||
},
|
||||
SSADot => {
|
||||
let val_a = (0..instruction.opcode.size)
|
||||
.map(|i| self.load(instruction.inputs[i as usize]))
|
||||
.collect::<Vec<_>>();
|
||||
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
|
||||
.map(|i| self.load(instruction.inputs[i as usize]))
|
||||
.collect::<Vec<_>>();
|
||||
self.store(
|
||||
instruction.outputs[0],
|
||||
match instruction.opcode.size {
|
||||
1 => val_a[0] * val_b[0],
|
||||
2 => (val_a[0] * val_b[0]) + (val_a[1] * val_b[1]),
|
||||
3 => {
|
||||
(val_a[0] * val_b[0])
|
||||
+ (val_a[1] * val_b[1])
|
||||
+ (val_a[2] * val_b[2])
|
||||
},
|
||||
4 => {
|
||||
(val_a[0] * val_b[0])
|
||||
+ (val_a[1] * val_b[1])
|
||||
+ (val_a[2] * val_b[2])
|
||||
+ (val_a[3] * val_b[3])
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
);
|
||||
},
|
||||
SSALength => {
|
||||
let val_a = (0..instruction.opcode.size)
|
||||
.map(|i| self.load(instruction.inputs[i as usize]))
|
||||
.collect::<Vec<_>>();
|
||||
self.store(
|
||||
instruction.outputs[0],
|
||||
match instruction.opcode.size {
|
||||
1 => val_a[0],
|
||||
2 => ((val_a[0].square()) + (val_a[1].square())).sqrt(),
|
||||
3 => ((val_a[0].square()) + (val_a[1].square()) + (val_a[2].square()))
|
||||
.sqrt(),
|
||||
4 => ((val_a[0].square())
|
||||
+ (val_a[1].square())
|
||||
+ (val_a[2].square())
|
||||
+ (val_a[3].square()))
|
||||
.sqrt(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
);
|
||||
},
|
||||
SSADistance => {
|
||||
let val_a = (0..instruction.opcode.size)
|
||||
.map(|i| self.load(instruction.inputs[i as usize]))
|
||||
.collect::<Vec<_>>();
|
||||
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
|
||||
.map(|i| self.load(instruction.inputs[i as usize]))
|
||||
.collect::<Vec<_>>();
|
||||
self.store(
|
||||
instruction.outputs[0],
|
||||
match instruction.opcode.size {
|
||||
1 => val_b[0] - val_a[0],
|
||||
2 => (((val_a[0] - val_b[0]).square())
|
||||
+ ((val_a[1] - val_b[1]).square()))
|
||||
.sqrt(),
|
||||
3 => (((val_a[0] - val_b[0]).square())
|
||||
+ ((val_a[1] - val_b[1]).square())
|
||||
+ ((val_a[2] - val_b[2]).square())
|
||||
+ ((val_a[3] - val_b[3]).square()))
|
||||
.sqrt(),
|
||||
4 => (((val_a[0] - val_b[0]).square())
|
||||
+ ((val_a[1] - val_b[1]).square())
|
||||
+ ((val_a[2] - val_b[2]).square())
|
||||
+ ((val_a[3] - val_b[3]).square())
|
||||
+ ((val_a[4] - val_b[4]).square()))
|
||||
.sqrt(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
);
|
||||
},
|
||||
SSANormalize => {
|
||||
let val_a = (0..instruction.opcode.size)
|
||||
.map(|i| self.load(instruction.inputs[i as usize]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match instruction.opcode.size {
|
||||
1 => {
|
||||
self.store(instruction.outputs[0], Interval::ONE);
|
||||
},
|
||||
2 => {
|
||||
let max = val_a[0].max_choice(val_a[1]).0;
|
||||
self.store(instruction.outputs[0], val_a[0] / max);
|
||||
self.store(instruction.outputs[1], val_a[1] / max);
|
||||
},
|
||||
3 => {
|
||||
let max = val_a[0].max_choice(val_a[1]).0.max_choice(val_a[2]).0;
|
||||
self.store(instruction.outputs[0], val_a[0] / max);
|
||||
self.store(instruction.outputs[1], val_a[1] / max);
|
||||
self.store(instruction.outputs[2], val_a[2] / max);
|
||||
},
|
||||
4 => {
|
||||
let max = val_a[0]
|
||||
.max_choice(val_a[1])
|
||||
.0
|
||||
.max_choice(val_a[2])
|
||||
.0
|
||||
.max_choice(val_a[3])
|
||||
.0;
|
||||
self.store(instruction.outputs[0], val_a[0] / max);
|
||||
self.store(instruction.outputs[1], val_a[1] / max);
|
||||
self.store(instruction.outputs[2], val_a[2] / max);
|
||||
self.store(instruction.outputs[3], val_a[3] / max);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
},
|
||||
SSANegate => {
|
||||
self.param_one(instruction, |val_a| -val_a);
|
||||
},
|
||||
SSARound => {
|
||||
self.param_one(instruction, |val_a| val_a.round());
|
||||
},
|
||||
SSAAbs => {
|
||||
self.param_one(instruction, |val_a| val_a.abs());
|
||||
},
|
||||
SSAFloor => {
|
||||
self.param_one(instruction, |val_a| val_a.floor());
|
||||
},
|
||||
SSACeil => {
|
||||
self.param_one(instruction, |val_a| val_a.ceil());
|
||||
},
|
||||
SSAFract => {
|
||||
self.param_one(instruction, |val_a| interval_glfract(val_a));
|
||||
},
|
||||
SSASin => {
|
||||
self.param_one(instruction, |val_a| val_a.sin());
|
||||
},
|
||||
SSACos => {
|
||||
self.param_one(instruction, |val_a| val_a.cos());
|
||||
},
|
||||
SSATan => {
|
||||
self.param_one(instruction, |val_a| val_a.tan());
|
||||
},
|
||||
SSAAsin => {
|
||||
self.param_one(instruction, |val_a| val_a.asin());
|
||||
},
|
||||
SSAAcos => {
|
||||
self.param_one(instruction, |val_a| val_a.acos());
|
||||
},
|
||||
SSAAtan => {
|
||||
self.param_one(instruction, |val_a| val_a.atan());
|
||||
},
|
||||
SSAExp => {
|
||||
self.param_one(instruction, |val_a| val_a.exp());
|
||||
},
|
||||
SSALog => {
|
||||
self.param_one(instruction, |val_a| val_a.ln());
|
||||
},
|
||||
SSASqrt => {
|
||||
self.param_one(instruction, |val_a| val_a.sqrt());
|
||||
},
|
||||
SSASquare => {
|
||||
self.param_one(instruction, |val_a| val_a.square());
|
||||
},
|
||||
SSACube => {
|
||||
self.param_one(instruction, |val_a| val_a.cube());
|
||||
},
|
||||
SSASmoothMin => {
|
||||
self.param_three(instruction, |d1, d2, k| {
|
||||
let h = (Interval::HALF + (Interval::HALF * (d2 - d1) / k))
|
||||
.simd_clamp(Interval::ZERO, Interval::ONE);
|
||||
return ((d2 * (Interval::ONE - h)) + (d1 * h))
|
||||
- k * h * (Interval::ONE - h);
|
||||
});
|
||||
},
|
||||
SSASmoothMax => {
|
||||
self.param_three(instruction, |d1, d2, k| {
|
||||
let h = (Interval::HALF - (Interval::HALF * (d2 + d1) / k))
|
||||
.simd_clamp(Interval::ZERO, Interval::ONE);
|
||||
return ((d2 * (Interval::ONE - h)) + (-d1 * h))
|
||||
+ k * h * (Interval::ONE - h);
|
||||
});
|
||||
},
|
||||
SSASmoothMinMaterial => todo!(),
|
||||
SSASmoothMaxMaterial => todo!(),
|
||||
SSAClamp => {
|
||||
self.param_three(instruction, |val_a, val_b, val_c| {
|
||||
val_a.simd_clamp(val_b, val_c)
|
||||
});
|
||||
},
|
||||
SSAMix => {
|
||||
self.param_three(instruction, |val_a, val_b, val_c| {
|
||||
(val_a * (Interval::ONE - val_c)) + (val_b * val_c)
|
||||
});
|
||||
},
|
||||
SSAFMA => {
|
||||
self.param_three(instruction, |val_a, val_b, val_c| (val_a * val_b) + val_c);
|
||||
},
|
||||
SSASDFSphere => {
|
||||
let pos_x = self.load(instruction.inputs[0]);
|
||||
let pos_y = self.load(instruction.inputs[1]);
|
||||
let pos_z = self.load(instruction.inputs[2]);
|
||||
let radius = self.load(instruction.inputs[3]);
|
||||
self.store(
|
||||
instruction.outputs[0],
|
||||
((pos_x.square()) + (pos_y.square()) + (pos_z.square())).sqrt() - radius,
|
||||
);
|
||||
},
|
||||
SSASDFBox => {
|
||||
let pos_x = self.load(instruction.inputs[0]);
|
||||
let pos_y = self.load(instruction.inputs[1]);
|
||||
let pos_z = self.load(instruction.inputs[2]);
|
||||
let rad_x = self.load(instruction.inputs[3]);
|
||||
let rad_y = self.load(instruction.inputs[4]);
|
||||
let rad_z = self.load(instruction.inputs[5]);
|
||||
let qx = pos_x.abs() - rad_x;
|
||||
let qy = pos_y.abs() - rad_y;
|
||||
let qz = pos_z.abs() - rad_z;
|
||||
let qxmax = qx.max_choice(Interval::ZERO).0;
|
||||
let qymax = qy.max_choice(Interval::ZERO).0;
|
||||
let qzmax = qz.max_choice(Interval::ZERO).0;
|
||||
self.store(
|
||||
instruction.outputs[0],
|
||||
((qxmax.square()) + (qymax.square()) + (qzmax.square())).sqrt()
|
||||
+ qx.max_choice(qy.max_choice(qz).0)
|
||||
.0
|
||||
.min_choice(Interval::ZERO)
|
||||
.0,
|
||||
);
|
||||
},
|
||||
SSASDFTorus => {
|
||||
let pos_x = self.load(instruction.inputs[0]);
|
||||
let pos_y = self.load(instruction.inputs[1]);
|
||||
let pos_z = self.load(instruction.inputs[2]);
|
||||
let radius1 = self.load(instruction.inputs[3]);
|
||||
let radius2 = self.load(instruction.inputs[4]);
|
||||
let q = ((pos_x.square()) + (pos_z.square())).sqrt() - radius1;
|
||||
self.store(
|
||||
instruction.outputs[0],
|
||||
((q.square()) + (pos_y.square())).sqrt() - radius2,
|
||||
);
|
||||
},
|
||||
SSACompare => {
|
||||
self.param_two(instruction, |val_a, val_b| {
|
||||
val_a
|
||||
.simd_gt(val_b)
|
||||
.select(VALUE_1, val_a.simd_lt(val_b).select(VALUE_M1, VALUE_0))
|
||||
});
|
||||
},
|
||||
SSAAnd => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a.and_choice(val_b).0);
|
||||
},
|
||||
SSAOr => {
|
||||
self.param_two(instruction, |val_a, val_b| val_a.or_choice(val_b).0);
|
||||
},
|
||||
SSARecip => {
|
||||
self.param_one(instruction, |val_a| val_a.recip());
|
||||
},
|
||||
SSANot => {
|
||||
self.param_one(instruction, |val_a| !val_a);
|
||||
},
|
||||
SSAStop => return Interval::ZERO,
|
||||
}
|
||||
}
|
||||
return Interval::NAN;
|
||||
}
|
||||
}
|
||||
|
||||
+25
-22
@@ -574,28 +574,6 @@ impl Interval {
|
||||
}
|
||||
}
|
||||
|
||||
/// Least non-negative remainder
|
||||
#[inline]
|
||||
pub fn rem_euclid(&self, other: Interval) -> Self {
|
||||
// TODO optimize this more?
|
||||
let has_nan = self.has_nan() | other.has_nan() | other.contains(VALUE_0);
|
||||
let other_constant = other.lower.simd_eq(other.upper) & other.lower.simd_gt(VALUE_0);
|
||||
let a = self.lower / other.lower;
|
||||
let b = self.upper / other.lower;
|
||||
let floors = a.simd_ne(a.floor()) & a.floor().simd_eq(b.floor());
|
||||
|
||||
let lower = has_nan.select(
|
||||
VALUE_NAN,
|
||||
(other_constant & floors).select(self.lower % other.lower, VALUE_0),
|
||||
);
|
||||
let upper = has_nan.select(
|
||||
VALUE_NAN,
|
||||
(other_constant & floors).select(self.upper % other.lower, other.upper.abs()),
|
||||
);
|
||||
|
||||
Interval::new(lower, upper)
|
||||
}
|
||||
|
||||
/// Largest value that is less-than-or-equal to this value
|
||||
#[inline]
|
||||
pub fn floor(&self) -> Self {
|
||||
@@ -646,6 +624,31 @@ impl From<Value> for Interval {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Rem<Interval> for Interval {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn rem(self, rhs: Interval) -> Self::Output {
|
||||
// TODO optimize this more?
|
||||
let has_nan = self.has_nan() | rhs.has_nan() | rhs.contains(VALUE_0);
|
||||
let other_constant = rhs.lower.simd_eq(rhs.upper) & rhs.lower.simd_gt(VALUE_0);
|
||||
let a = self.lower / rhs.lower;
|
||||
let b = self.upper / rhs.lower;
|
||||
let floors = a.simd_ne(a.floor()) & a.floor().simd_eq(b.floor());
|
||||
|
||||
let lower = has_nan.select(
|
||||
VALUE_NAN,
|
||||
(other_constant & floors).select(self.lower % rhs.lower, VALUE_0),
|
||||
);
|
||||
let upper = has_nan.select(
|
||||
VALUE_NAN,
|
||||
(other_constant & floors).select(self.upper % rhs.lower, rhs.upper.abs()),
|
||||
);
|
||||
|
||||
Interval::new(lower, upper)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<Interval> for Interval {
|
||||
type Output = Self;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user