feat: use frame buffer for rendering

This commit is contained in:
Tim Schubert 2025-04-27 17:44:43 +02:00
parent 432191b718
commit a265b857d0
No known key found for this signature in database
5 changed files with 85 additions and 154 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target /target
.direnv
*.fd *.fd
esp esp

68
Cargo.lock generated
View file

@ -14,37 +14,12 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 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]] [[package]]
name = "log" name = "log"
version = "0.4.22" version = "0.4.22"
@ -62,22 +37,22 @@ dependencies = [
[[package]] [[package]]
name = "ptr_meta" name = "ptr_meta"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607" checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90"
dependencies = [ dependencies = [
"ptr_meta_derive", "ptr_meta_derive",
] ]
[[package]] [[package]]
name = "ptr_meta_derive" name = "ptr_meta_derive"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn",
] ]
[[package]] [[package]]
@ -89,23 +64,6 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "syn" name = "syn"
version = "2.0.92" version = "2.0.92"
@ -128,9 +86,9 @@ dependencies = [
[[package]] [[package]]
name = "uefi" name = "uefi"
version = "0.33.0" version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6679b7fc2f6d6d2ea2f67555ef3ed9d71d30c5021faf9193091a5192db7dc468" checksum = "c25038e420a68d30a0e8002a3b51c075ad2342f5ae7a2383f042bd41fef73272"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cfg-if", "cfg-if",
@ -146,30 +104,28 @@ dependencies = [
name = "uefi-gol" name = "uefi-gol"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"heapless",
"log", "log",
"uefi", "uefi",
] ]
[[package]] [[package]]
name = "uefi-macros" name = "uefi-macros"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b24e77d3fc1e617051e630f99da24bcae6328abab37b8f9216bb68d06804f9a" checksum = "72cb3027736dad1b6f23437c63249025d960377fe7f9f769a111dfbc0dd2bdda"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.92", "syn",
] ]
[[package]] [[package]]
name = "uefi-raw" name = "uefi-raw"
version = "0.9.0" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d465de2c918779dafb769a5a4fe8d6e4fb7cc4cc6cb1a735f2f6ec68beea4" checksum = "d246ed5d6fd71c0331f0ac774a6aefb6cb9170a46f83148cacc70b09f82c87bb"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"ptr_meta",
"uguid", "uguid",
] ]

View file

@ -6,6 +6,5 @@ authors = [ "Tim Schubert <dadada@dadada.li>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
heapless = "0.8.0"
log = "0.4.22" log = "0.4.22"
uefi = { version = "0.33.0", features = ["logger", "panic_handler"] } uefi = { version = "0.34.0", features = ["logger", "global_allocator", "panic_handler"] }

View file

@ -1,55 +1,45 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
use core::{ extern crate alloc;
fmt::Write,
iter::repeat, use alloc::vec::Vec;
ops::{Deref, DerefMut},
use core::iter::repeat;
use uefi::{
prelude::*,
proto::console::gop::{BltOp::BufferToVideo, BltPixel, BltRegion, GraphicsOutput},
Result,
}; };
use heapless::{String, Vec}; const ALIVE: BltPixel = BltPixel::new(0xff, 0xff, 0xff);
use system::with_stdout; const DEAD: BltPixel = BltPixel::new(0, 0, 0);
use uefi::prelude::*;
const WIDTH: usize = 30;
const HEIGHT: usize = 30;
type Matrix = Vec<Vec<bool, WIDTH>, HEIGHT>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Life { struct Life {
matrix: Matrix, height: usize,
} matrix: Vec<BltPixel>,
width: usize,
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<Vec<bool, WIDTH>, 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
}
} }
impl Life { impl Life {
fn with_starter(starter: &[(usize, usize)]) -> Self { fn new(width: usize, height: usize) -> Self {
let mut life = Self::default(); 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() { for (i, j) in starter.iter() {
life[*i][*j] = true; life.matrix[i * width + j] = ALIVE;
} }
life life
} }
@ -65,85 +55,69 @@ impl Life {
(1, 0), (1, 0),
(1, 1), (1, 1),
]; ];
let mut future = Life::default(); let mut future = Self::new(self.width, self.height);
self.iter().enumerate().for_each(|(row, line)| { for h in 0..self.height {
line.iter().enumerate().for_each(|(column, alive)| { for w in 0..self.width {
let alive = self.matrix[h * self.width + w];
let neighbors = OFFSETS let neighbors = OFFSETS
.iter() .iter()
.filter(|offset| { .filter(|offset| {
let height = (row as i64) + offset.0; let height = (h as i64) + offset.0;
let width = (column as i64) + offset.1; let width = (w as i64) + offset.1;
height >= 0 self.is_neighbor_alive(height, width)
&& width >= 0
&& (height as usize) < HEIGHT
&& (width as usize) < WIDTH
&& self[height as usize][width as usize]
}) })
.count(); .count();
future[row][column] = neighbors == 3 || *alive && neighbors == 2; future.matrix[h * self.width + w] = if can_replicate(alive, neighbors) {
}) ALIVE
}); } else {
DEAD
};
}
}
future future
} }
/// Buffers output into one string per frame since println does not buffer output in uefi-rs. fn is_neighbor_alive(&self, height: i64, width: i64) -> bool {
fn render(&self) -> Frame { height >= 0
let mut frame = Frame::default(); && width >= 0
for l in self.iter() { && (height as usize) < self.height
for cell in l.iter() { && (width as usize) < self.width
if *cell { && {
frame.push('X').unwrap() let neighbor = self.matrix[height as usize * self.width + width as usize];
} else { neighbor.blue != 0 || neighbor.green != 0 || neighbor.red != 0
frame.push(' ').unwrap()
}
} }
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 }>; fn can_replicate(alive: BltPixel, neighbors: usize) -> bool {
neighbors == 3 || (alive.blue != 0 || alive.green != 0 || alive.red != 0) && neighbors == 2
#[derive(Default)]
struct Frame {
buffer: FrameBuffer,
} }
impl Deref for Frame { const BREEDER: &[(usize, usize)] = &[(415, 416), (416, 415), (416, 416), (417, 416), (417, 417)];
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;
#[entry] #[entry]
fn main() -> Status { fn main() -> Status {
uefi::helpers::init().unwrap(); 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::<GraphicsOutput>().unwrap();
let mut gop = boot::open_protocol_exclusive::<GraphicsOutput>(gop_handle).unwrap();
for _ in repeat(()) { for _ in repeat(()) {
life = life.play(); life = life.play();
let frame = life.render(); life.render(&mut gop).unwrap();
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);
} }
Status::SUCCESS unreachable!()
} }