perf: fast display (row runs, bulk fills, pixel scroll), terminal draws changed cells only + scrolls pixels, print! batched (was IPC per fmt fragment), snake sleeps on timer
This commit is contained in:
@@ -47,6 +47,13 @@ pub fn draw_text(ep: CapSlot, x: ScreenCoord, y: ScreenCoord, fg: Color, bg: Col
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves the rows from `top`, `height` tall, up by `by`, filling the rows
|
||||
/// left at the bottom with `fill`.
|
||||
pub fn scroll(ep: CapSlot, top: ScreenCoord, height: Dimension, by: Dimension, fill: Color) {
|
||||
let band = (top.raw() as usize) | ((height.raw() as usize) << 16);
|
||||
let _ = syscall::request(ep, [types::display::SCROLL, band, by.raw() as usize, fill.raw() as usize]);
|
||||
}
|
||||
|
||||
/// The display server's character cell, in pixels.
|
||||
pub const CELL_WIDTH: usize = 8;
|
||||
pub const CELL_HEIGHT: usize = 16;
|
||||
|
||||
@@ -1,10 +1,32 @@
|
||||
use core::fmt;
|
||||
|
||||
struct Writer;
|
||||
/// Holds what one `print!` formats, so it goes out in full messages:
|
||||
/// formatting writes in fragments (padding a character at a time), and a
|
||||
/// call per fragment is a call (and a redraw, on a terminal) per character.
|
||||
struct Writer {
|
||||
buf: [u8; BATCH],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
/// A few whole text messages' worth.
|
||||
const BATCH: usize = 5 * types::log::TEXT_BYTES;
|
||||
|
||||
impl Writer {
|
||||
fn flush(&mut self) {
|
||||
write_bytes(&self.buf[..self.len]);
|
||||
self.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
write_bytes(s.as_bytes());
|
||||
for &b in s.as_bytes() {
|
||||
if self.len == BATCH {
|
||||
self.flush();
|
||||
}
|
||||
self.buf[self.len] = b;
|
||||
self.len += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -26,7 +48,9 @@ pub fn write_bytes(bytes: &[u8]) {
|
||||
|
||||
pub fn _print(args: fmt::Arguments) {
|
||||
use fmt::Write;
|
||||
Writer.write_fmt(args).ok();
|
||||
let mut w = Writer { buf: [0; BATCH], len: 0 };
|
||||
w.write_fmt(args).ok();
|
||||
w.flush();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
||||
@@ -118,15 +118,36 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
/// `count` u32s from `offset` set to `value`.
|
||||
/// `count` u32s from `offset` set to `value`: one check, then a run of
|
||||
/// stores (drawing fills thousands at a time).
|
||||
pub fn fill_u32(&self, offset: ByteOffset, count: usize, value: u32) {
|
||||
let bytes = count.checked_mul(4).expect("window fill too large");
|
||||
self.at(offset, bytes, 4);
|
||||
let addr = self.at(offset, bytes, 4) as *mut u32;
|
||||
for i in 0..count {
|
||||
self.write(offset + i * 4, value);
|
||||
// SAFETY: the whole run was checked in range and aligned.
|
||||
unsafe { core::ptr::write_volatile(addr.add(i), value) }
|
||||
}
|
||||
}
|
||||
|
||||
/// `values` stored from `offset` on, in one checked run.
|
||||
pub fn write_u32s(&self, offset: ByteOffset, values: &[u32]) {
|
||||
let addr = self.at(offset, values.len() * 4, 4) as *mut u32;
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
// SAFETY: the whole run was checked in range and aligned.
|
||||
unsafe { core::ptr::write_volatile(addr.add(i), *v) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves `len` bytes from `from` to `to` inside the window; the two may
|
||||
/// overlap.
|
||||
pub fn copy_within(&self, from: ByteOffset, to: ByteOffset, len: usize) {
|
||||
let src = self.at(from, len, 1) as *const u8;
|
||||
let dst = self.at(to, len, 1) as *mut u8;
|
||||
// SAFETY: both ranges are inside the mapped window; `copy` allows
|
||||
// overlap. Plain memory moves, not device registers.
|
||||
unsafe { core::ptr::copy(src, dst, len) }
|
||||
}
|
||||
|
||||
/// `count` words from `offset` as atomics, for memory shared with
|
||||
/// threads or processes that only ever touch it atomically.
|
||||
pub fn atomics(&self, offset: ByteOffset, count: usize) -> &'static [AtomicUsize] {
|
||||
|
||||
Reference in New Issue
Block a user