mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-25 18:33:24 +00:00
Refactor framebuffer
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
ef898e572c
commit
88f08de3af
147
kernel/comps/framebuffer/src/framebuffer.rs
Normal file
147
kernel/comps/framebuffer/src/framebuffer.rs
Normal file
@ -0,0 +1,147 @@
|
||||
// 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 _
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user