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
.direnv
*.fd
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"
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",
]

View file

@ -6,6 +6,5 @@ authors = [ "Tim Schubert <dadada@dadada.li>"]
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"] }

View file

@ -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<Vec<bool, WIDTH>, 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<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
}
height: usize,
matrix: Vec<BltPixel>,
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()
}
}
frame.push('\n').unwrap()
}
frame
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
}
}
type FrameBuffer = String<{ (WIDTH * HEIGHT) + HEIGHT }>;
#[derive(Default)]
struct Frame {
buffer: FrameBuffer,
}
impl Deref for Frame {
type Target = FrameBuffer;
fn deref(&self) -> &Self::Target {
&self.buffer
/// 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),
})
}
}
impl DerefMut for Frame {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buffer
}
fn can_replicate(alive: BltPixel, neighbors: usize) -> bool {
neighbors == 3 || (alive.blue != 0 || alive.green != 0 || alive.red != 0) && neighbors == 2
}
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::<GraphicsOutput>().unwrap();
let mut gop = boot::open_protocol_exclusive::<GraphicsOutput>(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!()
}