From a265b857d072dd0f0b6c9c133fa0926c012418fc Mon Sep 17 00:00:00 2001 From: Tim Schubert Date: Sun, 27 Apr 2025 17:44:43 +0200 Subject: [PATCH] feat: use frame buffer for rendering --- .envrc | 1 + .gitignore | 1 + Cargo.lock | 68 ++++----------------- Cargo.toml | 3 +- src/main.rs | 166 ++++++++++++++++++++++------------------------------ 5 files changed, 85 insertions(+), 154 deletions(-) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index cb23289..cb13758 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target +.direnv *.fd esp diff --git a/Cargo.lock b/Cargo.lock index 0b0fa8b..4c80c92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,37 +14,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - [[package]] name = "log" version = "0.4.22" @@ -62,22 +37,22 @@ dependencies = [ [[package]] name = "ptr_meta" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" dependencies = [ "ptr_meta_derive", ] [[package]] name = "ptr_meta_derive" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -89,23 +64,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.92" @@ -128,9 +86,9 @@ dependencies = [ [[package]] name = "uefi" -version = "0.33.0" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6679b7fc2f6d6d2ea2f67555ef3ed9d71d30c5021faf9193091a5192db7dc468" +checksum = "c25038e420a68d30a0e8002a3b51c075ad2342f5ae7a2383f042bd41fef73272" dependencies = [ "bitflags", "cfg-if", @@ -146,30 +104,28 @@ dependencies = [ name = "uefi-gol" version = "0.1.0" dependencies = [ - "heapless", "log", "uefi", ] [[package]] name = "uefi-macros" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b24e77d3fc1e617051e630f99da24bcae6328abab37b8f9216bb68d06804f9a" +checksum = "72cb3027736dad1b6f23437c63249025d960377fe7f9f769a111dfbc0dd2bdda" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn", ] [[package]] name = "uefi-raw" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d465de2c918779dafb769a5a4fe8d6e4fb7cc4cc6cb1a735f2f6ec68beea4" +checksum = "d246ed5d6fd71c0331f0ac774a6aefb6cb9170a46f83148cacc70b09f82c87bb" dependencies = [ "bitflags", - "ptr_meta", "uguid", ] diff --git a/Cargo.toml b/Cargo.toml index e83db9a..2352a18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,5 @@ authors = [ "Tim Schubert "] license = "MIT OR Apache-2.0" [dependencies] -heapless = "0.8.0" log = "0.4.22" -uefi = { version = "0.33.0", features = ["logger", "panic_handler"] } +uefi = { version = "0.34.0", features = ["logger", "global_allocator", "panic_handler"] } diff --git a/src/main.rs b/src/main.rs index 4b7c0f6..560adb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,55 +1,45 @@ #![no_main] #![no_std] -use core::{ - fmt::Write, - iter::repeat, - ops::{Deref, DerefMut}, +extern crate alloc; + +use alloc::vec::Vec; + +use core::iter::repeat; + +use uefi::{ + prelude::*, + proto::console::gop::{BltOp::BufferToVideo, BltPixel, BltRegion, GraphicsOutput}, + Result, }; -use heapless::{String, Vec}; -use system::with_stdout; -use uefi::prelude::*; - -const WIDTH: usize = 30; -const HEIGHT: usize = 30; - -type Matrix = Vec, HEIGHT>; +const ALIVE: BltPixel = BltPixel::new(0xff, 0xff, 0xff); +const DEAD: BltPixel = BltPixel::new(0, 0, 0); #[derive(Debug, Clone)] struct Life { - matrix: Matrix, -} - -impl Deref for Life { - type Target = Matrix; - - fn deref(&self) -> &Self::Target { - &self.matrix - } -} - -impl Default for Life { - fn default() -> Self { - let mut matrix: Vec, HEIGHT> = Vec::default(); - for _ in 0..HEIGHT { - matrix.push(repeat(false).take(WIDTH).collect()).unwrap(); - } - Life { matrix } - } -} - -impl DerefMut for Life { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.matrix - } + height: usize, + matrix: Vec, + width: usize, } impl Life { - fn with_starter(starter: &[(usize, usize)]) -> Self { - let mut life = Self::default(); + fn new(width: usize, height: usize) -> Self { + let mut matrix = Vec::default(); + for _ in 0..height { + matrix.extend(repeat(DEAD).take(width)); + } + Life { + matrix, + width, + height, + } + } + + fn with_starter(width: usize, height: usize, starter: &[(usize, usize)]) -> Self { + let mut life = Self::new(width, height); for (i, j) in starter.iter() { - life[*i][*j] = true; + life.matrix[i * width + j] = ALIVE; } life } @@ -65,85 +55,69 @@ impl Life { (1, 0), (1, 1), ]; - let mut future = Life::default(); - self.iter().enumerate().for_each(|(row, line)| { - line.iter().enumerate().for_each(|(column, alive)| { + let mut future = Self::new(self.width, self.height); + for h in 0..self.height { + for w in 0..self.width { + let alive = self.matrix[h * self.width + w]; let neighbors = OFFSETS .iter() .filter(|offset| { - let height = (row as i64) + offset.0; - let width = (column as i64) + offset.1; - height >= 0 - && width >= 0 - && (height as usize) < HEIGHT - && (width as usize) < WIDTH - && self[height as usize][width as usize] + let height = (h as i64) + offset.0; + let width = (w as i64) + offset.1; + self.is_neighbor_alive(height, width) }) .count(); - future[row][column] = neighbors == 3 || *alive && neighbors == 2; - }) - }); + future.matrix[h * self.width + w] = if can_replicate(alive, neighbors) { + ALIVE + } else { + DEAD + }; + } + } future } - /// Buffers output into one string per frame since println does not buffer output in uefi-rs. - fn render(&self) -> Frame { - let mut frame = Frame::default(); - for l in self.iter() { - for cell in l.iter() { - if *cell { - frame.push('X').unwrap() - } else { - frame.push(' ').unwrap() - } + fn is_neighbor_alive(&self, height: i64, width: i64) -> bool { + height >= 0 + && width >= 0 + && (height as usize) < self.height + && (width as usize) < self.width + && { + let neighbor = self.matrix[height as usize * self.width + width as usize]; + neighbor.blue != 0 || neighbor.green != 0 || neighbor.red != 0 } - frame.push('\n').unwrap() - } - frame + } + + /// Blits entire display + fn render(&self, gop: &mut GraphicsOutput) -> Result { + gop.blt(BufferToVideo { + buffer: &self.matrix, + src: BltRegion::Full, + dest: (0, 0), + dims: (self.width, self.height), + }) } } -type FrameBuffer = String<{ (WIDTH * HEIGHT) + HEIGHT }>; - -#[derive(Default)] -struct Frame { - buffer: FrameBuffer, +fn can_replicate(alive: BltPixel, neighbors: usize) -> bool { + neighbors == 3 || (alive.blue != 0 || alive.green != 0 || alive.red != 0) && neighbors == 2 } -impl Deref for Frame { - type Target = FrameBuffer; - - fn deref(&self) -> &Self::Target { - &self.buffer - } -} - -impl DerefMut for Frame { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.buffer - } -} - -const BREEDER: &[(usize, usize)] = &[(15, 16), (16, 15), (16, 16), (17, 16), (17, 17)]; -const RENDER_DELAY: usize = 100_000; +const BREEDER: &[(usize, usize)] = &[(415, 416), (416, 415), (416, 416), (417, 416), (417, 417)]; #[entry] fn main() -> Status { uefi::helpers::init().unwrap(); - let mut life = Life::with_starter(BREEDER); + let mut life = Life::with_starter(800, 600, BREEDER); + let gop_handle = boot::get_handle_for_protocol::().unwrap(); + let mut gop = boot::open_protocol_exclusive::(gop_handle).unwrap(); for _ in repeat(()) { life = life.play(); - let frame = life.render(); - with_stdout(|stdout| { - stdout.reset(true).unwrap(); - stdout.write_str(&frame).unwrap() - }); - // Stall the loop a bit so it is easier to watch - boot::stall(RENDER_DELAY); + life.render(&mut gop).unwrap(); } - Status::SUCCESS + unreachable!() }