Skip to content

Commit

Permalink
Add ShadowBuffer to improve drawing performance (osbook_day09c)
Browse files Browse the repository at this point in the history
  • Loading branch information
gifnksm committed May 18, 2021
1 parent 00f958d commit 262083d
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 30 deletions.
6 changes: 3 additions & 3 deletions src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,10 @@ enum Drawer<'a> {
}

impl Draw for Drawer<'_> {
fn area(&self) -> Rectangle<i32> {
fn size(&self) -> Size<i32> {
match self {
Self::FrameBuffer(drawer) => drawer.area(),
Self::Window(drawer) => drawer.area(),
Self::FrameBuffer(drawer) => drawer.size(),
Self::Window(drawer) => drawer.size(),
}
}

Expand Down
41 changes: 34 additions & 7 deletions src/framebuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
desktop,
graphics::{Color, Draw, Point, Rectangle, Size},
prelude::*,
shadow_buffer::ShadowBuffer,
sync::{
mutex::{Mutex, MutexGuard},
once_cell::OnceCell,
Expand Down Expand Up @@ -57,6 +58,7 @@ pub(crate) struct ScreenInfo {
pub(crate) height: i32,
pub(crate) stride: i32,
pub(crate) bytes_per_pixel: i32,
pub(crate) pixel_format: PixelFormat,
}

impl ScreenInfo {
Expand All @@ -70,6 +72,7 @@ impl ScreenInfo {
height: usize_to_i32("vertical_resolution", info.vertical_resolution)?,
stride: usize_to_i32("stride", info.stride)?,
bytes_per_pixel: usize_to_i32("byte_per_pixel", info.bytes_per_pixel)?,
pixel_format: info.pixel_format,
})
}

Expand All @@ -89,11 +92,8 @@ pub(crate) struct Drawer {
}

impl Draw for Drawer {
fn area(&self) -> Rectangle<i32> {
Rectangle {
pos: Point::new(0, 0),
size: Point::new(self.info.width, self.info.height),
}
fn size(&self) -> Size<i32> {
Size::new(self.info.width, self.info.height)
}

fn draw(&mut self, p: Point<i32>, c: Color) {
Expand All @@ -105,6 +105,33 @@ impl Draw for Drawer {
}

impl Drawer {
pub(crate) fn copy(&mut self, pos: Point<i32>, src: &ShadowBuffer) {
let dst_size = self.size();
let src_size = src.size();

let copy_start_dst_x = i32::max(pos.x, 0);
let copy_start_dst_y = i32::max(pos.y, 0);
let copy_end_dst_x = i32::min(pos.x + src_size.x, dst_size.x);
let copy_end_dst_y = i32::min(pos.y + src_size.y, dst_size.y);

let stride = self.info.stride;
let bytes_per_pixel = self.info.bytes_per_pixel;
let bytes_per_copy_line = (bytes_per_pixel * (copy_end_dst_x - copy_start_dst_x)) as usize;

let dst_start_idx =
(bytes_per_pixel * (stride * copy_start_dst_y + copy_start_dst_x)) as usize;
let dst_buf = &mut self.framebuffer.buffer_mut()[dst_start_idx..];
let src_buf = src.data();

for dy in 0..(copy_end_dst_y - copy_start_dst_y) {
let dst =
&mut dst_buf[(bytes_per_pixel * dy * stride) as usize..][..bytes_per_copy_line];
let src =
&src_buf[(bytes_per_pixel * dy * src_size.x) as usize..][..bytes_per_copy_line];
dst.copy_from_slice(src);
}
}

fn pixel_index(&self, p: Point<i32>) -> Option<usize> {
if !self.area().contains(&p) {
return None;
Expand All @@ -113,11 +140,11 @@ impl Drawer {
}
}

trait PixelDraw {
pub(crate) trait PixelDraw {
fn pixel_draw(&self, buffer: &mut [u8], pixel_index: usize, c: Color);
}

fn select_pixel_drawer(
pub(crate) fn select_pixel_drawer(
pixel_format: PixelFormat,
) -> Option<&'static (dyn PixelDraw + Send + Sync)> {
match pixel_format {
Expand Down
6 changes: 3 additions & 3 deletions src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ where
}

pub(crate) trait Draw {
fn area(&self) -> Rectangle<i32>;
fn size(&self) -> Size<i32>;
fn draw(&mut self, p: Point<i32>, c: Color);

fn size(&self) -> Size<i32> {
self.area().size
fn area(&self) -> Rectangle<i32> {
Rectangle::new(Point::new(0, 0), self.size())
}

fn fill_rect(&mut self, rect: Rectangle<i32>, c: Color) {
Expand Down
6 changes: 3 additions & 3 deletions src/layer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
framebuffer,
graphics::{Draw, Point, Vector2d},
graphics::{Point, Vector2d},
prelude::*,
sync::{mpsc, mutex::Mutex, once_cell::OnceCell},
window::Window,
Expand Down Expand Up @@ -62,7 +62,7 @@ impl Layer {
self.pos += diff;
}

fn draw(&self, drawer: &mut dyn Draw) {
fn draw(&self, drawer: &mut framebuffer::Drawer) {
if let Some(window) = self.window.as_ref() {
window.lock().draw_to(drawer, self.pos)
}
Expand All @@ -88,7 +88,7 @@ impl LayerManager {
self.layers.insert(id, layer);
}

fn draw(&self, drawer: &mut dyn Draw) {
fn draw(&self, drawer: &mut framebuffer::Drawer) {
for id in &self.layer_stack {
if let Some(layer) = self.layers.get(&id) {
layer.draw(drawer);
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ mod mouse;
mod paging;
mod pci;
mod prelude;
mod shadow_buffer;
mod sync;
mod timer;
mod window;
Expand Down
55 changes: 55 additions & 0 deletions src/shadow_buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use crate::{
framebuffer::{self, PixelDraw},
graphics::{Color, Draw, Point, Size},
};
use alloc::{vec, vec::Vec};
use core::convert::TryFrom;

pub(crate) struct ShadowBuffer {
size: Size<i32>,
bytes_per_pixel: i32,
data: Vec<u8>,
pixel_drawer: &'static (dyn PixelDraw + Send + Sync),
}

impl Draw for ShadowBuffer {
fn size(&self) -> Size<i32> {
self.size
}

fn draw(&mut self, p: Point<i32>, c: Color) {
if let Some(pixel_index) = self.pixel_index(p) {
self.pixel_drawer.pixel_draw(&mut self.data, pixel_index, c)
}
}
}

impl ShadowBuffer {
pub(crate) fn new(size: Size<i32>) -> Self {
let screen_info = *framebuffer::info();

#[allow(clippy::unwrap_used)]
let data = vec![0; usize::try_from(size.x * size.y * screen_info.bytes_per_pixel).unwrap()];
let bytes_per_pixel = screen_info.bytes_per_pixel;
#[allow(clippy::unwrap_used)]
let pixel_drawer = framebuffer::select_pixel_drawer(screen_info.pixel_format).unwrap();

Self {
size,
bytes_per_pixel,
data,
pixel_drawer,
}
}

pub(crate) fn data(&self) -> &[u8] {
&self.data
}

fn pixel_index(&self, p: Point<i32>) -> Option<usize> {
if !self.area().contains(&p) {
return None;
}
usize::try_from((p.y * self.size.x + p.x) * self.bytes_per_pixel).ok()
}
}
36 changes: 22 additions & 14 deletions src/window.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::{
graphics::{Color, Draw, Point, Rectangle, Size},
framebuffer,
graphics::{Color, Draw, Point, Size},
shadow_buffer::ShadowBuffer,
sync::mutex::Mutex,
};
use alloc::{
Expand All @@ -8,26 +10,29 @@ use alloc::{
vec::Vec,
};
use core::convert::TryFrom;
use custom_debug_derive::Debug as CustomDebug;

#[derive(Debug)]
#[derive(CustomDebug)]
pub(crate) struct Window {
area: Rectangle<i32>,
size: Size<i32>,
data: Vec<Vec<Color>>,
drawer: Arc<Mutex<WindowDrawer>>,
transparent_color: Option<Color>,
#[debug(skip)]
shadow_buffer: ShadowBuffer,
}

impl Window {
pub(crate) fn new(size: Size<i32>) -> Arc<Mutex<Self>> {
let area = Rectangle::new(Point::new(0, 0), size);
let window = Arc::new(Mutex::new(Self {
area,
size,
data: vec![vec![Color::BLACK; size.x as usize]; size.y as usize],
drawer: Arc::new(Mutex::new(WindowDrawer {
area,
size,
window: Weak::new(),
})),
transparent_color: None,
shadow_buffer: ShadowBuffer::new(size),
}));
window.lock().drawer.lock().window = Arc::downgrade(&window);
window
Expand Down Expand Up @@ -59,11 +64,12 @@ impl Window {
let y = usize::try_from(at.y).ok()?;
let r = self.data.get_mut(y)?.get_mut(x)?;
*r = c;
self.shadow_buffer.draw(at, c);
Some(())
});
}

pub(crate) fn draw_to(&self, drawer: &mut dyn Draw, pos: Point<i32>) {
pub(crate) fn draw_to(&self, drawer: &mut framebuffer::Drawer, pos: Point<i32>) {
match self.transparent_color {
Some(tc) => {
for (c, wp) in self.colors() {
Expand All @@ -73,28 +79,30 @@ impl Window {
}
}
None => {
for (c, wp) in self.colors() {
drawer.draw(pos + wp, c)
}
drawer.copy(pos, &self.shadow_buffer);
// for (c, wp) in self.colors() {
// drawer.draw(pos + wp, c)
// }
}
}
}
}

#[derive(Debug)]
pub(crate) struct WindowDrawer {
area: Rectangle<i32>,
size: Size<i32>,
window: Weak<Mutex<Window>>,
}

impl Draw for WindowDrawer {
fn area(&self) -> Rectangle<i32> {
self.area
fn size(&self) -> Size<i32> {
self.size
}

fn draw(&mut self, p: Point<i32>, c: Color) {
if let Some(window) = self.window.upgrade() {
window.lock().set(p, c);
let mut window = window.lock();
window.set(p, c);
}
}
}

0 comments on commit 262083d

Please sign in to comment.