Optimise screen output and group writer state into a struct
This commit is contained in:
parent
7ec14819c1
commit
12cbeb594f
158
src/fbuffer.rs
158
src/fbuffer.rs
@ -1,22 +1,27 @@
|
|||||||
|
use crate::bootinfo::BOOTINFO;
|
||||||
|
use alloc::{
|
||||||
|
borrow::Cow, string::{String, ToString}, vec::Vec, vec
|
||||||
|
};
|
||||||
use bootloader_api::info::FrameBufferInfo;
|
use bootloader_api::info::FrameBufferInfo;
|
||||||
use fontdue::{layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle}, Font, FontSettings};
|
use core::{fmt, slice};
|
||||||
|
use fontdue::{
|
||||||
|
layout::{CoordinateSystem, GlyphRasterConfig, Layout, LayoutSettings, TextStyle},
|
||||||
|
Font, FontSettings
|
||||||
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use spin::{Lazy, Mutex};
|
use spin::{Lazy, Mutex};
|
||||||
use crate::{bootinfo::BOOTINFO, dbg};
|
|
||||||
use alloc::{string::String, vec::Vec, vec};
|
|
||||||
use core::slice;
|
|
||||||
|
|
||||||
|
|
||||||
struct Framebuffer<'a> {
|
struct Framebuffer<'a> {
|
||||||
info: FrameBufferInfo,
|
info: FrameBufferInfo,
|
||||||
buffer: &'a mut [u8],
|
buffer: &'a mut [u8],
|
||||||
draw_buffer: Vec<u8>,
|
draw_buffer: Vec<u8>,
|
||||||
|
scroll_offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Framebuffer<'a> {
|
impl<'a> Framebuffer<'a> {
|
||||||
pub fn new(info: FrameBufferInfo, buffer: &'a mut [u8]) -> Self {
|
fn new(info: FrameBufferInfo, buffer: &'a mut [u8]) -> Self {
|
||||||
dbg!(&info);
|
let draw_buffer = vec![0; 2 * buffer.len()];
|
||||||
let draw_buffer = vec![0; buffer.len()];
|
Self { info, buffer, draw_buffer, scroll_offset: 0 }
|
||||||
Self { info, buffer, draw_buffer }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_row_buf(&mut self, row: usize) -> &mut [u8] {
|
fn get_row_buf(&mut self, row: usize) -> &mut [u8] {
|
||||||
@ -30,26 +35,36 @@ impl<'a> Framebuffer<'a> {
|
|||||||
&mut self.get_row_buf(row)[pbuf_start..][..pbuf_len]
|
&mut self.get_row_buf(row)[pbuf_start..][..pbuf_len]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_pixel(&mut self, row: usize, col: usize, value: u8) {
|
fn set_pixel(&mut self, row: usize, col: usize, value: u8) {
|
||||||
if row >= self.info.height || col >= self.info.width {
|
if row >= self.info.height || col >= self.info.width {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.get_pixel_buf(row, col).fill(value);
|
self.get_pixel_buf((row + self.scroll_offset) % (self.info.height * 2), col).fill(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
for row in 0..self.info.height {
|
for row in 0..self.info.height * 2 {
|
||||||
self.get_row_buf(row).fill(0);
|
self.get_row_buf(row).fill(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_screen(&mut self) {
|
fn update_screen(&mut self) {
|
||||||
self.buffer.copy_from_slice(&self.draw_buffer);
|
let scroll_top_byte_offset = self.row_byte_offset(self.scroll_offset);
|
||||||
|
let scroll_bot_byte_offset = self.row_byte_offset(self.scroll_offset + self.info.height);
|
||||||
|
self.buffer
|
||||||
|
.copy_from_slice(&self.draw_buffer[scroll_top_byte_offset..scroll_bot_byte_offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn row_byte_offset(&self, row: usize) -> usize {
|
||||||
|
self.info.stride * self.info.bytes_per_pixel * row
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll_by(&mut self, offset: usize) {
|
||||||
|
self.scroll_offset = (self.scroll_offset + offset) % (self.info.height * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static FBUFFER: Lazy<Mutex<Framebuffer<'static>>> = Lazy::new(|| {
|
pub static FRAMEBUFFER_WRITER: Lazy<Mutex<FramebufferWriter>> = Lazy::new(|| {
|
||||||
// Testing the framebuffer
|
|
||||||
let fbuf = BOOTINFO.framebuffer.as_ref().unwrap();
|
let fbuf = BOOTINFO.framebuffer.as_ref().unwrap();
|
||||||
|
|
||||||
// No good way to get mutable buffer, so do this. No, its probably not safe. Too lazy to do it
|
// No good way to get mutable buffer, so do this. No, its probably not safe. Too lazy to do it
|
||||||
@ -61,69 +76,106 @@ static FBUFFER: Lazy<Mutex<Framebuffer<'static>>> = Lazy::new(|| {
|
|||||||
let mut fbuf = Framebuffer::new(fbuf_info, fbuf_buf);
|
let mut fbuf = Framebuffer::new(fbuf_info, fbuf_buf);
|
||||||
fbuf.clear();
|
fbuf.clear();
|
||||||
fbuf.update_screen();
|
fbuf.update_screen();
|
||||||
Mutex::new(fbuf)
|
let font = Font::from_bytes(
|
||||||
|
include_bytes!("../FiraCode-Regular.ttf").as_slice(),
|
||||||
|
FontSettings::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Mutex::new(FramebufferWriter::new(fbuf, font))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static OUT_STRING: Lazy<Mutex<String>> = Lazy::new(|| {
|
pub struct FramebufferWriter {
|
||||||
Mutex::new(String::new())
|
fbuf: Framebuffer<'static>,
|
||||||
});
|
out_string: String,
|
||||||
|
font: Font,
|
||||||
pub static FIRA_CODE: Lazy<Font> = Lazy::new(|| {
|
layout: Layout,
|
||||||
Font::from_bytes(include_bytes!("../FiraCode-Regular.ttf").as_slice(), FontSettings::default()).unwrap()
|
next_line_y: usize,
|
||||||
});
|
glyph_cache: HashMap<GlyphRasterConfig, Vec<u8>>,
|
||||||
|
|
||||||
pub fn write_str(s: &str) {
|
|
||||||
OUT_STRING.lock().push_str(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_char(c: char) {
|
impl FramebufferWriter {
|
||||||
OUT_STRING.lock().push(c);
|
fn new(fbuf: Framebuffer<'static>, font: Font) -> Self {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_screen() {
|
|
||||||
let out_string = OUT_STRING.lock();
|
|
||||||
let mut fbuf = FBUFFER.lock();
|
|
||||||
fbuf.clear();
|
|
||||||
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
||||||
|
|
||||||
layout.reset(&LayoutSettings {
|
layout.reset(&LayoutSettings {
|
||||||
max_width: Some(fbuf.info.width as f32),
|
max_width: Some(fbuf.info.width as f32),
|
||||||
max_height: Some(fbuf.info.height as f32),
|
max_height: Some(fbuf.info.height as f32),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
Self {
|
||||||
|
fbuf,
|
||||||
|
out_string: String::new(),
|
||||||
|
font,
|
||||||
|
layout,
|
||||||
|
next_line_y: 0,
|
||||||
|
glyph_cache: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
layout.append(&[&*FIRA_CODE], &TextStyle::new(&out_string, 12.0, 0));
|
fn update_screen(&mut self) {
|
||||||
|
self.layout.clear();
|
||||||
|
self.layout.append(&[&self.font], &TextStyle::new(&self.out_string, 12.0, 0));
|
||||||
|
|
||||||
let height = layout.height();
|
if (self.next_line_y + self.layout.height() as usize) > self.fbuf.info.height {
|
||||||
let top_row = if height as usize > fbuf.info.height {
|
let excess_y =
|
||||||
height as usize - fbuf.info.height
|
self.layout.height() as usize - (self.fbuf.info.height - self.next_line_y);
|
||||||
} else {
|
self.fbuf.scroll_by(excess_y);
|
||||||
0
|
self.next_line_y = self.fbuf.info.height - self.layout.height() as usize;
|
||||||
};
|
}
|
||||||
|
|
||||||
let glyphs = layout.glyphs();
|
let glyphs = self.layout.glyphs();
|
||||||
|
|
||||||
let mut first_line_top_offset = None;
|
let mut first_line_top_offset = None;
|
||||||
|
|
||||||
for line in layout.lines().unwrap() {
|
for line in self.layout.lines().unwrap() {
|
||||||
if ((line.baseline_y - line.max_ascent) as usize) < top_row {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if first_line_top_offset.is_none() {
|
if first_line_top_offset.is_none() {
|
||||||
first_line_top_offset = Some(((line.baseline_y - line.max_ascent) as usize) - top_row);
|
first_line_top_offset = Some((line.baseline_y - line.max_ascent) as usize);
|
||||||
}
|
}
|
||||||
let first_line_top_offset = first_line_top_offset.unwrap();
|
let first_line_top_offset = first_line_top_offset.unwrap();
|
||||||
for glyph_pos in &glyphs[line.glyph_start..=line.glyph_end] {
|
for glyph_pos in &glyphs[line.glyph_start..=line.glyph_end] {
|
||||||
if glyph_pos.width == 0 || glyph_pos.height == 0 {
|
if glyph_pos.width == 0 || glyph_pos.height == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (_, bitmap) = FIRA_CODE.rasterize_config(glyph_pos.key);
|
let bitmap: Cow<[u8]> = if let Some(bitmap) = self.glyph_cache.get(&glyph_pos.key) {
|
||||||
|
bitmap.into()
|
||||||
|
} else {
|
||||||
|
let bitmap = self.font.rasterize_config(glyph_pos.key).1;
|
||||||
|
self.glyph_cache.insert(glyph_pos.key, bitmap.clone());
|
||||||
|
bitmap.into()
|
||||||
|
};
|
||||||
for (i, &pixel) in bitmap.iter().enumerate() {
|
for (i, &pixel) in bitmap.iter().enumerate() {
|
||||||
let row = i / glyph_pos.width;
|
let row = i / glyph_pos.width;
|
||||||
let col = i % glyph_pos.width;
|
let col = i % glyph_pos.width;
|
||||||
fbuf.set_pixel(row + glyph_pos.y as usize - top_row - first_line_top_offset, col + glyph_pos.x as usize, pixel);
|
self.fbuf.set_pixel(
|
||||||
|
row + glyph_pos.y as usize - first_line_top_offset + self.next_line_y,
|
||||||
|
col + glyph_pos.x as usize,
|
||||||
|
pixel,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fbuf.update_screen();
|
self.fbuf.update_screen();
|
||||||
|
if self.out_string.ends_with('\n') {
|
||||||
|
self.out_string.clear();
|
||||||
|
self.next_line_y += self.layout.height() as usize;
|
||||||
|
} else {
|
||||||
|
self.out_string = self.out_string.split('\n').last().unwrap().to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for FramebufferWriter {
|
||||||
|
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||||
|
self.out_string.push(c);
|
||||||
|
if c == '\n' {
|
||||||
|
self.update_screen();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||||
|
self.out_string.push_str(s);
|
||||||
|
if s.contains('\n') {
|
||||||
|
self.update_screen();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
bootinfo::BOOTINFO,
|
bootinfo::BOOTINFO,
|
||||||
|
fbuffer::FRAMEBUFFER_WRITER,
|
||||||
print, println,
|
print, println,
|
||||||
serial::SECOND_PORT,
|
serial::SECOND_PORT,
|
||||||
tasking::SleepReason,
|
tasking::SleepReason,
|
||||||
@ -9,7 +10,7 @@ use crate::{
|
|||||||
use alloc::{boxed::Box, string::ToString, vec::Vec};
|
use alloc::{boxed::Box, string::ToString, vec::Vec};
|
||||||
use az::WrappingCast;
|
use az::WrappingCast;
|
||||||
use cast::{u64, usize};
|
use cast::{u64, usize};
|
||||||
use core::{arch::asm, ffi::CStr, ptr, slice};
|
use core::{arch::asm, ffi::CStr, fmt::Write, ptr, slice};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use pic8259::ChainedPics;
|
use pic8259::ChainedPics;
|
||||||
use saturating_cast::SaturatingCast;
|
use saturating_cast::SaturatingCast;
|
||||||
@ -251,9 +252,8 @@ extern "C" fn syscall_handler() {
|
|||||||
match regs.rax {
|
match regs.rax {
|
||||||
0 => {
|
0 => {
|
||||||
let rval = if let Some(chr) = char::from_u32(regs.rcx.wrapping_cast()) {
|
let rval = if let Some(chr) = char::from_u32(regs.rcx.wrapping_cast()) {
|
||||||
crate::fbuffer::write_char(chr);
|
let _ = FRAMEBUFFER_WRITER.lock().write_char(chr); // writer never gives an error
|
||||||
if chr == '\n' {
|
if chr == '\n' {
|
||||||
crate::fbuffer::update_screen();
|
|
||||||
print!("\r\n");
|
print!("\r\n");
|
||||||
} else {
|
} else {
|
||||||
print!("{}", chr);
|
print!("{}", chr);
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
mod bootinfo;
|
mod bootinfo;
|
||||||
|
mod fbuffer;
|
||||||
mod gdt;
|
mod gdt;
|
||||||
mod interrupts;
|
mod interrupts;
|
||||||
mod kernel_heap;
|
mod kernel_heap;
|
||||||
@ -84,7 +85,6 @@ mod serial;
|
|||||||
mod start;
|
mod start;
|
||||||
mod tasking;
|
mod tasking;
|
||||||
mod virtual_memory;
|
mod virtual_memory;
|
||||||
mod fbuffer;
|
|
||||||
|
|
||||||
use core::{ptr, slice};
|
use core::{ptr, slice};
|
||||||
|
|
||||||
@ -99,6 +99,7 @@ use elf::{
|
|||||||
endian::AnyEndian,
|
endian::AnyEndian,
|
||||||
ElfBytes,
|
ElfBytes,
|
||||||
};
|
};
|
||||||
|
use fbuffer::FRAMEBUFFER_WRITER;
|
||||||
use physical_memory::PHYSICAL_MEMORY;
|
use physical_memory::PHYSICAL_MEMORY;
|
||||||
use serial::SECOND_PORT;
|
use serial::SECOND_PORT;
|
||||||
use spin::lazy::Lazy;
|
use spin::lazy::Lazy;
|
||||||
@ -129,6 +130,7 @@ pub fn main() {
|
|||||||
Lazy::force(&PHYSICAL_MEMORY);
|
Lazy::force(&PHYSICAL_MEMORY);
|
||||||
Lazy::force(&KERNEL_SPACE);
|
Lazy::force(&KERNEL_SPACE);
|
||||||
Lazy::force(&ACTIVE_SPACE);
|
Lazy::force(&ACTIVE_SPACE);
|
||||||
|
Lazy::force(&FRAMEBUFFER_WRITER);
|
||||||
interrupts::init();
|
interrupts::init();
|
||||||
pit::init(100);
|
pit::init(100);
|
||||||
let initrd = unsafe {
|
let initrd = unsafe {
|
||||||
|
Loading…
Reference in New Issue
Block a user