mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 01:43:22 +00:00
148 lines
4.3 KiB
Rust
148 lines
4.3 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use alloc::sync::Arc;
|
|
|
|
use ostd::{boot::boot_info, io::IoMem, mm::VmIo, Result};
|
|
use spin::Once;
|
|
|
|
use crate::{Pixel, PixelFormat, RenderedPixel};
|
|
|
|
/// The framebuffer used for text or graphical output.
|
|
///
|
|
/// # Notes
|
|
///
|
|
/// It is highly recommended to use a synchronization primitive, such as a `SpinLock`, to
|
|
/// lock the framebuffer before performing any operation on it.
|
|
/// Failing to properly synchronize access can result in corrupted framebuffer content
|
|
/// or unspecified behavior during rendering.
|
|
#[derive(Debug)]
|
|
pub struct FrameBuffer {
|
|
io_mem: IoMem,
|
|
width: usize,
|
|
height: usize,
|
|
pixel_format: PixelFormat,
|
|
}
|
|
|
|
pub static FRAMEBUFFER: Once<Arc<FrameBuffer>> = Once::new();
|
|
|
|
pub(crate) fn init() {
|
|
let Some(framebuffer_arg) = boot_info().framebuffer_arg else {
|
|
log::warn!("Framebuffer not found");
|
|
return;
|
|
};
|
|
|
|
if framebuffer_arg.address == 0 {
|
|
log::error!("Framebuffer address is zero");
|
|
return;
|
|
}
|
|
|
|
// FIXME: There are several pixel formats that have the same BPP. We lost the information
|
|
// during the boot phase, so here we guess the pixel format on a best effort basis.
|
|
let pixel_format = match framebuffer_arg.bpp {
|
|
8 => PixelFormat::Grayscale8,
|
|
16 => PixelFormat::Rgb565,
|
|
24 => PixelFormat::Rgb888,
|
|
32 => PixelFormat::BgrReserved,
|
|
_ => {
|
|
log::error!(
|
|
"Unsupported framebuffer pixel format: {} bpp",
|
|
framebuffer_arg.bpp
|
|
);
|
|
return;
|
|
}
|
|
};
|
|
|
|
let framebuffer = {
|
|
let fb_base = framebuffer_arg.address;
|
|
let fb_size = framebuffer_arg.width
|
|
* framebuffer_arg.height
|
|
* (framebuffer_arg.bpp / u8::BITS as usize);
|
|
let io_mem = IoMem::acquire(fb_base..fb_base + fb_size).unwrap();
|
|
FrameBuffer {
|
|
io_mem,
|
|
width: framebuffer_arg.width,
|
|
height: framebuffer_arg.height,
|
|
pixel_format,
|
|
}
|
|
};
|
|
|
|
framebuffer.clear();
|
|
FRAMEBUFFER.call_once(|| Arc::new(framebuffer));
|
|
}
|
|
|
|
impl FrameBuffer {
|
|
/// Returns the size of the framebuffer in bytes.
|
|
pub fn size(&self) -> usize {
|
|
self.io_mem.length()
|
|
}
|
|
|
|
/// Returns the width of the framebuffer in pixels.
|
|
pub fn width(&self) -> usize {
|
|
self.width
|
|
}
|
|
|
|
/// Returns the height of the framebuffer in pixels.
|
|
pub fn height(&self) -> usize {
|
|
self.height
|
|
}
|
|
|
|
/// Returns the pixel format of the framebuffer.
|
|
pub fn pixel_format(&self) -> PixelFormat {
|
|
self.pixel_format
|
|
}
|
|
|
|
/// Renders the pixel according to the pixel format of the framebuffer.
|
|
pub fn render_pixel(&self, pixel: Pixel) -> RenderedPixel {
|
|
pixel.render(self.pixel_format)
|
|
}
|
|
|
|
/// Calculates the offset of a pixel at the specified position.
|
|
pub fn calc_offset(&self, x: usize, y: usize) -> PixelOffset {
|
|
PixelOffset {
|
|
fb: self,
|
|
offset: ((y * self.width + x) * self.pixel_format.nbytes()) as isize,
|
|
}
|
|
}
|
|
|
|
/// Writes a pixel at the specified position.
|
|
pub fn write_pixel_at(&self, offset: PixelOffset, pixel: RenderedPixel) -> Result<()> {
|
|
self.io_mem.write_bytes(offset.as_usize(), pixel.as_slice())
|
|
}
|
|
|
|
/// Writes raw bytes at the specified offset.
|
|
pub fn write_bytes_at(&self, offset: usize, bytes: &[u8]) -> Result<()> {
|
|
self.io_mem.write_bytes(offset, bytes)
|
|
}
|
|
|
|
/// Clears the framebuffer with default color (black).
|
|
pub fn clear(&self) {
|
|
let frame = alloc::vec![0u8; self.size()];
|
|
self.write_bytes_at(0, &frame).unwrap();
|
|
}
|
|
}
|
|
|
|
/// The offset of a pixel in the framebuffer.
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct PixelOffset<'a> {
|
|
fb: &'a FrameBuffer,
|
|
offset: isize,
|
|
}
|
|
|
|
impl PixelOffset<'_> {
|
|
/// Adds the specified delta to the x coordinate.
|
|
pub fn x_add(&mut self, x_delta: isize) {
|
|
let delta = x_delta * self.fb.pixel_format.nbytes() as isize;
|
|
self.offset += delta;
|
|
}
|
|
|
|
/// Adds the specified delta to the y coordinate.
|
|
pub fn y_add(&mut self, y_delta: isize) {
|
|
let delta = y_delta * (self.fb.width * self.fb.pixel_format.nbytes()) as isize;
|
|
self.offset += delta;
|
|
}
|
|
|
|
pub fn as_usize(&self) -> usize {
|
|
self.offset as _
|
|
}
|
|
}
|