Add interval interpreter

This commit is contained in:
2025-06-24 23:42:08 +01:00
parent 3ca29db4c8
commit 42d47f57c4
2 changed files with 437 additions and 24 deletions
+412 -2
View File
@@ -6,6 +6,7 @@ use std::simd::{
use crate::{ use crate::{
BYTECODE_IMITATES_GLSL, CSG, BYTECODE_IMITATES_GLSL, CSG,
interval::Interval,
ssa::{SSAInput, SSAInstruction, SSAOpcode}, 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)] #[derive(Clone, Debug)]
pub(crate) struct PointInterpreter<'csg> { pub(crate) struct PointInterpreter<'csg> {
value_map: Vec<Value>, value_map: Vec<Value>,
@@ -109,8 +126,8 @@ impl<'csg> PointInterpreter<'csg> {
} }
} }
// cargo asm "erroccfisuvsc::interpreter::Interpreter::scene" --no-color --rust // cargo asm "erroccfisuvsc::interpreter::PointInterpreter::scene" --no-color
// > scene.asm // --rust > scene.asm
pub(crate) fn scene(&mut self, px: Value, py: Value, pz: Value) -> Value { pub(crate) fn scene(&mut self, px: Value, py: Value, pz: Value) -> Value {
self.clear_stacks(); self.clear_stacks();
@@ -441,3 +458,396 @@ impl<'csg> PointInterpreter<'csg> {
return VALUE_NAN; 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
View File
@@ -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 /// Largest value that is less-than-or-equal to this value
#[inline] #[inline]
pub fn floor(&self) -> Self { 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 { impl std::ops::Add<Interval> for Interval {
type Output = Self; type Output = Self;