optimize for speed
authorJacob Lifshay <programmerjake@gmail.com>
Thu, 5 Oct 2023 23:38:16 +0000 (16:38 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Thu, 5 Oct 2023 23:38:16 +0000 (16:38 -0700)
rust_voxels_game/Cargo.toml
rust_voxels_game/Makefile
rust_voxels_game/src/console.rs
rust_voxels_game/src/fixed.rs
rust_voxels_game/src/lib.rs
rust_voxels_game/src/world.rs

index 53f75fcf65bfde4bf9f277216fe018816f7df5c6..4c35063014c48f07126035b20660fd1aa794a48e 100644 (file)
@@ -19,6 +19,7 @@ minetest-schematic = { path = "minetest-schematic" }
 embedded = []
 hosted = ["dep:termios", "dep:libc"]
 default = ["hosted"]
+hosted_full_speed = ["hosted"]
 
 [profile.release]
 panic = "abort"
index b01ac3cd6eb0dcadc71a2772b51e0a5020aea54f..c8f17ff774eea186481d0e82e5338d62198423f2 100644 (file)
@@ -1,4 +1,4 @@
-.PHONY: all run size dump emu clean
+.PHONY: all run size dump emu emu_full_speed clean
 
 ARCH = $(shell uname -m)
 ifneq ("$(ARCH)", "ppc64")
@@ -47,6 +47,9 @@ rust_voxels_game.hex: rust_voxels_game.bin
 emu:
        cargo run --release
 
+emu_full_speed:
+       cargo run --release --features=hosted_full_speed
+
 clean:
        cargo clean
        @rm -f *.o rust_voxels_game.elf rust_voxels_game.bin rust_voxels_game.hex
index 18f443cb68ea04ef729ee9609ad81a7bb97aa6f1..64444d879008b3780965d0a2b82b2bfbf031d53d 100644 (file)
@@ -78,7 +78,7 @@ fn console_write(b: u8) {
     }
 }
 
-#[cfg(feature = "hosted")]
+#[cfg(all(feature = "hosted", not(feature = "hosted_full_speed")))]
 fn console_write(b: u8) {
     use core::{
         sync::atomic::{AtomicU32, Ordering},
@@ -113,6 +113,13 @@ fn console_write(b: u8) {
     let _ = std::io::stdout().write_all(&[b]);
 }
 
+#[cfg(feature = "hosted_full_speed")]
+fn console_write(b: u8) {
+    use std::io::Write;
+
+    let _ = std::io::stdout().write_all(&[b]);
+}
+
 pub struct Console(());
 
 impl Console {
index fc20794980538cf85cae6d962941df803d8935d7..9b32c9890350ec65b8d1619acde415049ace68ea 100644 (file)
@@ -1,8 +1,9 @@
-#[cfg(feature = "hosted")]
-use core::fmt;
-use core::ops::{
-    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr,
-    ShrAssign, Sub, SubAssign,
+use core::{
+    fmt,
+    ops::{
+        Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr,
+        ShrAssign, Sub, SubAssign,
+    },
 };
 
 macro_rules! impl_assign_op {
@@ -21,7 +22,6 @@ macro_rules! impl_assign_op {
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
 pub struct Fix64(i64);
 
-#[cfg(feature = "hosted")]
 impl fmt::Display for Fix64 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let frac_digits = (Fix64::FRAC_BITS + 3) / 4;
@@ -40,7 +40,6 @@ impl fmt::Display for Fix64 {
     }
 }
 
-#[cfg(feature = "hosted")]
 impl fmt::Debug for Fix64 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         fmt::Display::fmt(self, f)
@@ -184,6 +183,7 @@ impl_assign_op!(MulAssign::mul_assign => Mul::mul);
 impl Div for Fix64 {
     type Output = Self;
 
+    #[inline(always)]
     fn div(self, rhs: Fix64) -> Self::Output {
         Fix64((((self.0 as i128) << Self::FRAC_BITS) / rhs.0 as i128) as i64)
     }
index 0f59e9fa887f2d0b87a38c0afea3c1937a8383b5..524772267baf8deee870fded91a16943257e7af4 100644 (file)
@@ -114,8 +114,16 @@ pub extern "C" fn main() -> ! {
         world.render(screen, pos, forward, right, down);
         restore_cursor.map(|f| f(world));
         screen.display(console);
-        writeln!(console, "Press WASD to move, IJKL to change look dir, F to move down, R to move up").unwrap();
-        writeln!(console, "0-9 to place a block, - to delete a block, ESC to exit.").unwrap();
+        writeln!(
+            console,
+            "Press WASD to move, IJKL to change look dir, F to move down, R to move up"
+        )
+        .unwrap();
+        writeln!(
+            console,
+            "0-9 to place a block, - to delete a block, ESC to exit."
+        )
+        .unwrap();
         loop {
             let (prev_pos, hit_pos) = world.get_hit_pos(pos, forward);
             let mut new_pos = pos;
index 812e77aeae48ac7ef74768c871382e47a87815df..a07eb1b0b48bda8988e00a2c100d48399b458b71 100644 (file)
@@ -24,6 +24,7 @@ pub struct World {
     pub blocks: [[[Block; Self::SIZE]; Self::SIZE]; Self::SIZE],
 }
 
+#[derive(Copy, Clone, Debug)]
 struct RayCastDimension {
     next_pos: i64,
     next_t: Fix64,
@@ -32,6 +33,18 @@ struct RayCastDimension {
 }
 
 impl RayCastDimension {
+    const fn at_inf(pos: i64) -> Self {
+        Self {
+            next_pos: pos,
+            next_t: Fix64::from_bits(i64::MAX),
+            t_step: Fix64::from_int(0),
+            pos_step: 0,
+        }
+    }
+    const fn is_at_inf(self) -> bool {
+        self.pos_step == 0
+    }
+    #[inline(always)]
     fn new(start: Fix64, dir: Fix64) -> Option<Self> {
         let pos_step = dir.signum();
         if pos_step == 0 {
@@ -259,6 +272,7 @@ impl World {
         let array_pos = Self::array_pos(pos);
         self.get_array_mut(array_pos)
     }
+    #[inline(always)]
     pub fn get(&self, pos: Vec3D<i64>) -> Option<&Block> {
         let array_pos = Self::array_pos(pos);
         self.get_array(array_pos)
@@ -271,6 +285,7 @@ impl World {
     pub fn positions() -> impl Iterator<Item = Vec3D<i64>> {
         Self::array_positions().map(Self::from_array_pos)
     }
+    #[inline(never)]
     fn cast_ray_impl(
         &self,
         start: Vec3D<Fix64>,
@@ -283,30 +298,37 @@ impl World {
             };
             f(pos, block)
         };
-        let mut pos = start.map(Fix64::floor).into_array();
+        let pos = start.map(Fix64::floor);
         let mut ray_casters = start
             .zip(dir)
-            .map(|(start, dir)| RayCastDimension::new(start, dir))
+            .zip(pos)
+            .map(
+                #[inline(always)]
+                |((start, dir), pos)| {
+                    RayCastDimension::new(start, dir).unwrap_or(RayCastDimension::at_inf(pos))
+                },
+            )
             .into_array();
+        let mut pos = pos.into_array();
+        if ray_casters[0].is_at_inf() && ray_casters[1].is_at_inf() && ray_casters[2].is_at_inf() {
+            return ControlFlow::Break(());
+        }
         loop {
             f(Vec3D::from_array(pos))?;
-            let mut min_index = None;
-            let mut min_t = Fix64::from_bits(i64::MAX);
-            for (index, ray_caster) in ray_casters.iter().enumerate() {
-                let Some(ray_caster) = ray_caster else {
-                    continue;
+            macro_rules! do_step_at_min {
+                ($ray_casters:ident, $pos:ident, [$index:literal]) => {
+                    $pos[$index] = $ray_casters[$index].next_pos;
+                    $ray_casters[$index].step();
+                };
+                ($ray_casters:ident, $pos:ident, [$index0:literal, $index1:literal $(, $rest:literal)*]) => {
+                    if $ray_casters[$index1].next_t < $ray_casters[$index0].next_t {
+                        do_step_at_min!($ray_casters, $pos, [$index1 $(, $rest)*]);
+                    } else {
+                        do_step_at_min!($ray_casters, $pos, [$index0 $(, $rest)*]);
+                    }
                 };
-                if ray_caster.next_t < min_t {
-                    min_t = ray_caster.next_t;
-                    min_index = Some(index);
-                }
             }
-            let Some(min_index) = min_index else {
-                return ControlFlow::Break(());
-            };
-            let ray_caster = ray_casters[min_index].as_mut().unwrap();
-            pos[min_index] = ray_caster.next_pos;
-            ray_caster.step();
+            do_step_at_min!(ray_casters, pos, [0, 1, 2]);
         }
     }
     pub fn cast_ray(