From dd65827e6b49982b9ae911f1f61c854c83fb83ad Mon Sep 17 00:00:00 2001 From: will <104373134+w-utter@users.noreply.github.com> Date: Sat, 14 Sep 2024 18:51:06 +1000 Subject: [PATCH 1/2] remove Vec> in place of Vec and custom iterators --- src/main.rs | 17 ++---- src/shader.rs | 142 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 141 insertions(+), 18 deletions(-) diff --git a/src/main.rs b/src/main.rs index c99377c..a8073c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,24 +65,15 @@ fn main() { top_right, bottom_left, bottom_right, - width: 190, + width: 50, height: 90, }, light, ); - // let grey_scale = - // r##".'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"##.as_bytes(); - let grey_scale = ".......::::::-----====+++**#%@".as_bytes(); - for line in frame { - for char in line { - let char = char as usize; - if char > 0 { - let i = char * grey_scale.len() / (u8::MAX as usize + 1); - print!("{}", grey_scale[i] as char); - } else { - print!(" "); - } + for line in frame.into_iter() { + for char in line.chars() { + print!("{char}"); } println!(); } diff --git a/src/shader.rs b/src/shader.rs index 197f269..a03dc54 100644 --- a/src/shader.rs +++ b/src/shader.rs @@ -12,6 +12,135 @@ pub struct View { pub height: usize, } +pub struct Frame { + height: usize, + width: usize, + inner: Vec, +} + +pub struct FrameIter<'a> { + iter: &'a Frame, + row: usize, +} + +impl <'a> Iterator for FrameIter<'a> { + type Item = FrameRow<'a>; + fn next(&mut self) -> Option { + //println!("height: {}", self.iter.height); + if self.row >= self.iter.height { + return None; + } + + let out = FrameRow { bytes: self.iter, row: self.row, idx: 0 }; + self.row += 1; + //println!("{}", self.row); + + Some(out) + } +} + +impl <'a> IntoIterator for &'a Frame { + type IntoIter= FrameIter<'a>; + type Item = FrameRow<'a>; + fn into_iter(self) -> Self::IntoIter { + FrameIter { + iter: self, + row: 0 + } + } +} + +pub struct FrameRow<'a> { + bytes: &'a Frame, + row: usize, + idx: usize, +} + +impl <'a> FrameRow<'a> { + pub fn as_bytes(&self) -> &[u8] { + let start = self.row * self.bytes.height; + let end = start + self.bytes.width; + &self.bytes.inner[start..end] + } + + pub fn row(&self) -> usize { + self.row + } + + pub fn chars(self) -> FrameChars<'a> { + let Self { + bytes, + row, + idx + } = self; + FrameChars {bytes, row, idx} + } +} + +pub struct FrameChars<'a> { + bytes: &'a Frame, + row: usize, + idx: usize, +} + +// let grey_scale = +// r##".'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"##.as_bytes(); +const GREY_SCALE: &[u8] = b".......::::::-----====+++**#%@"; + +impl <'a> Iterator for FrameChars<'a> { + type Item = char; + fn next(&mut self) -> Option { + if self.idx >= self.bytes.width { + return None; + } + let start = self.row * self.bytes.width; + let idx = start + self.idx; + self.idx += 1; + + let char = self.bytes.inner[idx] as usize; + + let c = if char > 0 { + let i = char * GREY_SCALE.len() / (u8::MAX as usize + 1); + GREY_SCALE[i] as char + } else { + ' ' + }; + Some(c) + } +} + + + + +impl <'a> Iterator for FrameRow<'a> { + type Item = u8; + + fn next(&mut self) -> Option { + if self.idx >= self.bytes.width { + return None; + } + let start = self.row * self.bytes.width; + let idx = start + self.idx; + self.idx += 1; + Some(self.bytes.inner[idx]) + } +} + +impl Frame { + pub fn with_capacity(width: usize, height: usize) -> Self { + let inner = Vec::with_capacity(width * height); + Self { + inner, + width, + height, + } + } + + pub fn push(&mut self, val: u8) { + self.inner.push(val) + } +} + pub fn get_frame( inner_radius: f64, outer_radius: f64, @@ -25,16 +154,19 @@ pub fn get_frame( height, }: View, light: Vector, -) -> Vec> { - let mut frame = Vec::with_capacity(height); +) -> Frame { + + let mut frame = Frame::with_capacity(width, height); + + //let mut frame = Vec::with_capacity(height); for i in 0..height { - let mut row = Vec::with_capacity(width); + //let mut row = Vec::with_capacity(width); for j in 0..width { let bottom = i as f64 / (height as f64 - 1.0); let top = 1.0 - bottom; let right = j as f64 / (width as f64 - 1.0); let left = 1.0 - right; - row.push(get_pixel( + frame.push(get_pixel( inner_radius, outer_radius, camera, @@ -45,7 +177,7 @@ pub fn get_frame( light, )); } - frame.push(row); + //frame.push(row); } frame } From 12088b54365ece2cc0487b2797f34785ece13277 Mon Sep 17 00:00:00 2001 From: will <104373134+w-utter@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:15:05 +1000 Subject: [PATCH 2/2] remove locking and allocation in hot-loop --- src/main.rs | 18 +++++++---- src/shader.rs | 90 ++++++++++++++++++++++++++------------------------- 2 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/main.rs b/src/main.rs index a8073c7..13f4c78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use shader::{get_frame, View}; mod linear_alg; mod shader; -fn main() { +fn main() -> std::io::Result<()> { let height = 1.2; let camera = Vector { x: 0.0, @@ -50,13 +50,18 @@ fn main() { let mut theta_x = 0.0; let mut next_frame = Instant::now(); + let mut frame = shader::Frame::with_capacity(120, 120); + + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + loop { thread::sleep_until(next_frame); next_frame += Duration::from_secs_f32(1.0 / 30.0); let [camera, top_left, top_right, bottom_left, bottom_right, light] = coords.map(|c| rotate_y(rotate_z(c, theta_z), theta_x)); - let frame = get_frame( + get_frame( 0.8, 1.5, View { @@ -65,18 +70,19 @@ fn main() { top_right, bottom_left, bottom_right, - width: 50, - height: 90, + view: &mut frame, }, light, ); + use std::io::Write; for line in frame.into_iter() { for char in line.chars() { - print!("{char}"); + write!(stdout, "{char}")?; } - println!(); + writeln!(stdout)?; } + stdout.flush()?; theta_x += theta_x_frame; theta_z += theta_z_frame; diff --git a/src/shader.rs b/src/shader.rs index a03dc54..ae53388 100644 --- a/src/shader.rs +++ b/src/shader.rs @@ -2,14 +2,13 @@ use roots::find_roots_quartic; use crate::linear_alg::Vector; -pub struct View { +pub struct View<'a> { pub camera: Vector, pub top_left: Vector, pub top_right: Vector, pub bottom_left: Vector, pub bottom_right: Vector, - pub width: usize, - pub height: usize, + pub view: &'a mut Frame, } pub struct Frame { @@ -23,30 +22,29 @@ pub struct FrameIter<'a> { row: usize, } -impl <'a> Iterator for FrameIter<'a> { +impl<'a> Iterator for FrameIter<'a> { type Item = FrameRow<'a>; fn next(&mut self) -> Option { - //println!("height: {}", self.iter.height); if self.row >= self.iter.height { return None; } - let out = FrameRow { bytes: self.iter, row: self.row, idx: 0 }; + let out = FrameRow { + bytes: self.iter, + row: self.row, + idx: 0, + }; self.row += 1; - //println!("{}", self.row); Some(out) } } -impl <'a> IntoIterator for &'a Frame { - type IntoIter= FrameIter<'a>; +impl<'a> IntoIterator for &'a Frame { + type IntoIter = FrameIter<'a>; type Item = FrameRow<'a>; fn into_iter(self) -> Self::IntoIter { - FrameIter { - iter: self, - row: 0 - } + FrameIter { iter: self, row: 0 } } } @@ -56,7 +54,7 @@ pub struct FrameRow<'a> { idx: usize, } -impl <'a> FrameRow<'a> { +impl<'a> FrameRow<'a> { pub fn as_bytes(&self) -> &[u8] { let start = self.row * self.bytes.height; let end = start + self.bytes.width; @@ -68,12 +66,8 @@ impl <'a> FrameRow<'a> { } pub fn chars(self) -> FrameChars<'a> { - let Self { - bytes, - row, - idx - } = self; - FrameChars {bytes, row, idx} + let Self { bytes, row, idx } = self; + FrameChars { bytes, row, idx } } } @@ -87,7 +81,7 @@ pub struct FrameChars<'a> { // r##".'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"##.as_bytes(); const GREY_SCALE: &[u8] = b".......::::::-----====+++**#%@"; -impl <'a> Iterator for FrameChars<'a> { +impl<'a> Iterator for FrameChars<'a> { type Item = char; fn next(&mut self) -> Option { if self.idx >= self.bytes.width { @@ -109,10 +103,7 @@ impl <'a> Iterator for FrameChars<'a> { } } - - - -impl <'a> Iterator for FrameRow<'a> { +impl<'a> Iterator for FrameRow<'a> { type Item = u8; fn next(&mut self) -> Option { @@ -136,6 +127,18 @@ impl Frame { } } + pub fn fill_char(&mut self, i: usize, j: usize, char: u8) { + let pos = j * self.width + i; + assert!(pos <= self.inner.len()); + + if pos == self.inner.len() { + self.push(char) + } else { + let a: &mut [u8] = self.inner.as_mut(); + a[pos] = char; + } + } + pub fn push(&mut self, val: u8) { self.inner.push(val) } @@ -150,36 +153,35 @@ pub fn get_frame( top_right, bottom_left, bottom_right, - width, - height, + view, }: View, light: Vector, -) -> Frame { - - let mut frame = Frame::with_capacity(width, height); +) { + let height = view.height; + let width = view.width; - //let mut frame = Vec::with_capacity(height); for i in 0..height { - //let mut row = Vec::with_capacity(width); for j in 0..width { let bottom = i as f64 / (height as f64 - 1.0); let top = 1.0 - bottom; let right = j as f64 / (width as f64 - 1.0); let left = 1.0 - right; - frame.push(get_pixel( - inner_radius, - outer_radius, - camera, - top_left * top * left - + top_right * top * right - + bottom_left * bottom * left - + bottom_right * bottom * right, - light, - )); + view.fill_char( + j, + i, + get_pixel( + inner_radius, + outer_radius, + camera, + top_left * top * left + + top_right * top * right + + bottom_left * bottom * left + + bottom_right * bottom * right, + light, + ), + ); } - //frame.push(row); } - frame } /// The donut lay flat on the x-y plane centered around (0, 0, 0)