diff --git a/src/bitstack.rs b/src/bitstack.rs new file mode 100644 index 0000000..2e96788 --- /dev/null +++ b/src/bitstack.rs @@ -0,0 +1,73 @@ +#![allow(dead_code)] + +use std::mem::size_of; + +#[derive(Copy, Clone, Debug)] +pub struct BitStack128 { + data: (u64, u64), +} + +impl BitStack128 { + pub fn new() -> BitStack128 { + BitStack128 { data: (0, 0) } + } + + /// Push a bit onto the top of the stack. + pub fn push(&mut self, value: bool) { + debug_assert!((self.data.1 >> (size_of::() - 1)) == 0); // Verify no stack overflow + self.data.1 = (self.data.1 << 1) | (self.data.0 >> (size_of::() - 1)); + self.data.0 <<= 1; + self.data.0 |= value as u64; + } + + /// Push 3 bits onto the top of the stack. The input + /// bits are passed as an integer, with the bit that + /// will be on top in the least significant digit, and + /// the rest following in order from there. + /// + /// Note that unless you are running a debug build, no + /// effort is made to verify that only the first three + /// bits of the passed value are used. So if other + /// bits are non-zero this will produce incorrect results. + pub fn push_3(&mut self, value: u8) { + debug_assert!((self.data.1 >> (size_of::() - 3)) == 0); // Verify no stack overflow + debug_assert!(value & (!((1 << 3) - 1)) == 0); // Verify no bits outside of the 3-bit range + self.data.1 = (self.data.1 << 3) | (self.data.0 >> (size_of::() - 3)); + self.data.0 <<= 3; + self.data.0 |= value as u64; + } + + /// Pop the top bit off the stack. + pub fn pop(&mut self) -> bool { + let b = (self.data.0 & 1) != 0; + self.data.0 = (self.data.0 >> 1) | (self.data.1 << (size_of::() - 1)); + self.data.1 >>= 1; + return b; + } + + /// Pop the top n bits off the stack. The bits are returned as + /// an integer, with the top bit in the least significant digit, + /// and the rest following in order from there. + pub fn pop_n(&mut self, n: usize) -> u64 { + debug_assert!(n < size_of::()); // Can't pop more than we have + debug_assert!(n < size_of::()); // Can't pop more than the return type can hold + let b = self.data.0 & ((1 << n) - 1); + self.data.0 = (self.data.0 >> n) | (self.data.1 << (size_of::() - n)); + self.data.1 >>= n; + return b; + } + + /// Read the top bit of the stack without popping it. + pub fn peek(&self) -> bool { + (self.data.0 & 1) != 0 + } + + /// Read the top n bits of the stack without popping them. The bits + /// are returned as an integer, with the top bit in the least + /// significant digit, and the rest following in order from there. + pub fn peek_n(&self, n: usize) -> u64 { + debug_assert!(n < size_of::()); // Can't return more than we have + debug_assert!(n < size_of::()); // Can't return more than the return type can hold + self.data.0 & ((1 << n) - 1) + } +} diff --git a/src/main.rs b/src/main.rs index 3d4c7e0..14a8a1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ extern crate simd; mod accel; mod algorithm; mod bbox; +mod bitstack; mod boundable; mod camera; mod color; diff --git a/src/ray.rs b/src/ray.rs index d75e1d6..d3717cb 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -2,6 +2,7 @@ use std; +use bitstack::BitStack128; use float4::Float4; use math::{Vector, Point, Matrix4x4}; @@ -16,6 +17,7 @@ pub struct Ray { pub max_t: f32, pub time: f32, pub flags: u32, + pub trav_stack: BitStack128, } impl Ray { @@ -27,6 +29,7 @@ impl Ray { max_t: std::f32::INFINITY, time: time, flags: 0, + trav_stack: BitStack128::new(), } } else { Ray { @@ -35,6 +38,7 @@ impl Ray { max_t: 1.0, time: time, flags: OCCLUSION_FLAG, + trav_stack: BitStack128::new(), } } }