Replace scrolling with wraparound in graphical debug

Inspired by how Haiku does printing for its kernel debugger, this commit
gets rid of the scrolling when the bootlog reaches the end of the screen
and instead wraps around to the start of the screen. Between the last
written line and the first visible written line there is always a blank
line to provide visual separation.

Getting rid of the scrolling significantly simplifies the implementation
of graphical debug and removes the need for double buffering for
performance as we no longer need to read back the framebuffer when
scrolling which is very expensive on write-combining memory like the
framebuffer.
This commit is contained in:
bjorn3
2025-09-20 17:15:02 +02:00
parent 84ac36ca2f
commit b36deffa61
5 changed files with 23 additions and 111 deletions
-3
View File
@@ -179,9 +179,6 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! {
// Setup kernel heap
allocator::init();
// Set up double buffer for graphical debug now that heap is available
graphical_debug::init_heap();
// Activate memory logging
crate::log::init();
-3
View File
@@ -170,9 +170,6 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! {
#[cfg(all(target_arch = "x86_64", feature = "profiling"))]
crate::profiling::init();
// Set up double buffer for graphical debug now that heap is available
graphical_debug::init_heap();
// Activate memory logging
crate::log::init();
+21 -37
View File
@@ -1,5 +1,3 @@
use core::{cmp, ptr};
use super::Display;
static FONT: &[u8] = include_bytes!("../../../res/unifont.font");
@@ -29,30 +27,32 @@ impl DebugDisplay {
for &b in buf {
if self.x >= self.w || b == b'\n' {
self.x = 0;
self.y += 1;
self.y = (self.y + 1) % self.h;
}
if self.y >= self.h {
let new_y = self.h - 1;
let d_y = self.y - new_y;
self.scroll(d_y * 16);
unsafe {
self.display.sync_screen();
}
self.y = new_y;
if b == b'\n' {
continue;
}
if b != b'\n' {
self.char(self.x * 8, self.y * 16, b as char, 0xFFFFFF);
if self.x == 0 {
self.clear_row(self.y);
self.clear_row((self.y + 1) % self.h);
}
unsafe {
self.display.sync(self.x * 8, self.y * 16, 8, 16);
}
self.char(self.x * 8, self.y * 16, b as char, 0xFFFFFF);
self.x += 1;
self.x += 1;
}
}
fn clear_row(&mut self, y: usize) {
for row in y * 16..(y + 1) * 16 {
unsafe {
core::ptr::write_bytes(
self.display.data_mut().add(row * self.display.stride),
0,
self.display.width,
);
}
}
}
@@ -60,7 +60,7 @@ impl DebugDisplay {
/// Draw a character
fn char(&mut self, x: usize, y: usize, character: char, color: u32) {
if x + 8 <= self.display.width && y + 16 <= self.display.height {
let phys_y = (self.display.offset_y + y) % self.display.height;
let phys_y = y % self.display.height;
let mut dst = unsafe {
self.display
.data_mut()
@@ -89,20 +89,4 @@ impl DebugDisplay {
}
}
}
/// Scroll the screen
fn scroll(&mut self, lines: usize) {
let lines = cmp::min(self.h * 16, lines); // clamp
self.display.offset_y = (self.display.offset_y + lines) % self.display.height;
// clear the new lines
let start_y = (self.display.offset_y + self.h * 16 - lines) % self.display.height;
for row in 0..lines {
let y = (start_y + row) % self.display.height;
unsafe {
let ptr = self.display.data_mut().add(y * self.display.stride);
ptr::write_bytes(ptr, 0, self.display.stride);
}
}
}
}
+2 -61
View File
@@ -1,5 +1,4 @@
use alloc::boxed::Box;
use core::{ptr, slice};
use core::ptr;
/// A display
pub(super) struct Display {
@@ -7,8 +6,6 @@ pub(super) struct Display {
pub(super) height: usize,
pub(super) stride: usize,
onscreen_ptr: *mut u32,
offscreen: Option<Box<[u32]>>,
pub(super) offset_y: usize,
}
unsafe impl Send for Display {}
@@ -28,66 +25,10 @@ impl Display {
height,
stride,
onscreen_ptr,
offscreen: None,
offset_y: 0,
}
}
pub(super) fn heap_init(&mut self) {
let onscreen =
unsafe { slice::from_raw_parts(self.onscreen_ptr, self.stride * self.height) };
self.offscreen = Some(onscreen.to_vec().into_boxed_slice());
}
pub(super) fn data_mut(&mut self) -> *mut u32 {
match &mut self.offscreen {
Some(offscreen) => offscreen.as_mut_ptr(),
None => self.onscreen_ptr,
}
}
/// Sync from offscreen to onscreen, unsafe because it trusts provided x, y, w, h
pub(super) unsafe fn sync(&mut self, x: usize, y: usize, w: usize, mut h: usize) {
unsafe {
if let Some(offscreen) = &self.offscreen {
let mut y = y;
while h > 0 {
let src_y = (self.offset_y + y) % self.height;
let src_offset = src_y * self.stride + x;
let dst_offset = y * self.stride + x;
ptr::copy(
offscreen.as_ptr().add(src_offset),
self.onscreen_ptr.add(dst_offset),
w,
);
y += 1;
h -= 1;
}
}
}
}
// sync the whole screen (faster)
pub(super) unsafe fn sync_screen(&mut self) {
unsafe {
if let Some(offscreen) = &self.offscreen {
let stride_bytes = self.stride;
let first_part_len = (self.height - self.offset_y) * stride_bytes;
let second_part_len = self.offset_y * stride_bytes;
ptr::copy(
offscreen.as_ptr().add(self.offset_y * stride_bytes),
self.onscreen_ptr,
first_part_len,
);
ptr::copy(
offscreen.as_ptr(),
self.onscreen_ptr.add(first_part_len),
second_part_len,
);
}
}
self.onscreen_ptr
}
}
-7
View File
@@ -67,13 +67,6 @@ pub fn init(env: &[u8]) {
}
}
#[allow(unused)]
pub fn init_heap() {
if let Some(debug_display) = &mut *DEBUG_DISPLAY.lock() {
debug_display.display.heap_init();
}
}
#[allow(unused)]
pub fn fini() {
DEBUG_DISPLAY.lock().take();