Skip to content

Commit

Permalink
Add an iterator over XFB rows
Browse files Browse the repository at this point in the history
This emits slices of u16, and allows users to not use unsafe for
drawing.
  • Loading branch information
linkmauve committed Apr 23, 2023
1 parent a6b266c commit acbc539
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 34 deletions.
17 changes: 10 additions & 7 deletions luma_core/src/allocate.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
use alloc::alloc::{alloc, Layout};
use alloc::boxed::Box;
use core::pin::Pin;
use core::slice;

const CACHELINE: usize = 32;

/// Allocate a slice aligned to a cacheline, and return it pinned.
pub fn alloc_aligned(size: usize) -> Pin<Box<[u8]>> {
let layout = Layout::from_size_align(size, CACHELINE).unwrap();
let ptr = unsafe { alloc(layout) };
let boxed = unsafe { Box::from_raw(ptr) };
let slice = Box::into_boxed_slice(boxed);
Pin::from(slice)
/// Allocate a slice aligned to a cacheline, and return it boxed.
pub fn alloc_aligned<T: Copy>(size: usize) -> Box<[T]> {
let layout = Layout::array::<T>(size)
.unwrap()
.align_to(CACHELINE)
.unwrap();
let ptr = unsafe { alloc(layout) } as *mut T;
let slice = unsafe { slice::from_raw_parts(ptr, size) };
Box::from(slice)
}

/// Allocate an array aligned to a cacheline, and return it pinned.
Expand Down
53 changes: 45 additions & 8 deletions luma_core/src/vi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@
use crate::allocate::alloc_aligned;
use crate::io::{read16, write16, write32};
use alloc::boxed::Box;
use core::pin::Pin;

/// A struct representing the eXternal FrameBuffer, or XFB. It represents the image that will be
/// sent to the screen, in YUYV format. It must be allocated as contiguous physical memory.
pub struct Xfb {
data: Pin<Box<[u8]>>,
data: Box<[u16]>,
width: usize,
height: usize,
}

impl Xfb {
/// Allocate an XFB with the given width and height.
pub fn allocate(width: usize, height: usize) -> Xfb {
let stride = width * 2;
let stride = width;
let data = alloc_aligned(stride * height);
Xfb {
data,
Expand All @@ -37,23 +36,61 @@ impl Xfb {
self.height
}

/// Get the stride of this XFB, given the YUYV format this is always width × 2.
/// Get the stride of this XFB, in u16. Given the YUYV format this is always equal to width
/// for now.
pub fn stride(&self) -> usize {
// YUYV always takes two bytes per pixel.
self.width * 2
self.width
}

/// Get a mutable iterator over the rows of this XFB.
pub fn iter_mut(&mut self) -> XfbIterMut {
XfbIterMut {
xfb: self,
cur_row: 0,
}
}

/// Return the raw pointer to this XFB.
pub fn as_ptr(&self) -> *const u8 {
pub fn as_ptr(&self) -> *const u16 {
self.data.as_ptr()
}

/// Return the raw mutable pointer to this XFB.
pub fn as_mut_ptr(&mut self) -> *mut u8 {
pub fn as_mut_ptr(&mut self) -> *mut u16 {
self.data.as_mut_ptr()
}
}

pub struct XfbIterMut<'a> {
xfb: &'a mut Xfb,
cur_row: usize,
}

impl<'a> Iterator for XfbIterMut<'a> {
type Item = &'a mut [u16];

fn next(&mut self) -> Option<&'a mut [u16]> {
if self.cur_row < self.xfb.height {
let stride = self.xfb.stride();
let offset = self.cur_row * stride;
let slice = &mut self.xfb.data[offset..offset + stride];
self.cur_row += 1;

// TODO: Figure out how to unify the two lifetimes without transmute.
Some(unsafe { core::mem::transmute(slice) })
} else {
None
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.xfb.height - self.cur_row;
(remaining, Some(remaining))
}
}

impl<'a> ExactSizeIterator for XfbIterMut<'a> {}

const BASE: u32 = 0xcc00_2000;

bitflags::bitflags! {
Expand Down
36 changes: 17 additions & 19 deletions src/bin/vi-draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ const fn rgba2yuyv(pixel: i32, odd: bool) -> u16 {
}

/// Ported from Weston’s clients/simple-shm.c
fn paint_pixels(mut image: *mut u16, padding: i32, width: i32, height: i32, time: i32) {
fn paint_pixels(xfb: &mut Xfb, padding: i32, time: i32) {
let width = xfb.width() as i32;
let height = xfb.height() as i32;
let mut rows = xfb.iter_mut().skip(padding as usize);

let halfh = padding + (height - padding * 2) / 2;
let halfw = padding + (width - padding * 2) / 2;

Expand All @@ -50,30 +54,24 @@ fn paint_pixels(mut image: *mut u16, padding: i32, width: i32, height: i32, time
or *= or;
ir *= ir;

image = unsafe { image.offset((padding * width) as isize) };
for y in padding..(height - padding) {
let y2 = (y - halfh) * (y - halfh);
let row = rows.next().unwrap();

image = unsafe { image.offset(padding as isize) };
let y2 = (y - halfh) * (y - halfh);
for x in padding..(width - padding) {
let v;

/* squared distance from center */
let r2 = (x - halfw) * (x - halfw) + y2;

if r2 < ir {
v = (r2 / 32 + time / 4) * 0x0080401;
let v = if r2 < ir {
(r2 / 32 + time / 4) * 0x0080401
} else if r2 < or {
v = (y + time / 2) * 0x0080401;
(y + time / 2) * 0x0080401
} else {
v = (x + time) * 0x0080401;
}
(x + time) * 0x0080401
};

unsafe { *image = rgba2yuyv(v, (x & 1) != 0) };
image = unsafe { image.offset(1) };
row[x as usize] = rgba2yuyv(v, (x & 1) != 0);
}

image = unsafe { image.offset(padding as isize) };
}
}

Expand All @@ -83,15 +81,15 @@ fn main() {
let mut vi = Vi::setup(xfb);

// First fill the XFB with white.
let xfb = vi.xfb().as_mut_ptr() as *mut u16;
for i in 0..(640 * 480) {
unsafe { xfb.offset(i).write(0xff80) };
let xfb = vi.xfb();
for row in xfb.iter_mut() {
row.fill(0xff80);
}

// Then draw to it as fast as we can.
let mut i = 0;
loop {
paint_pixels(xfb, 20, 640, 480, i);
paint_pixels(xfb, 20, i);
i += 1;
}
}

0 comments on commit acbc539

Please sign in to comment.