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 Dec 14, 2024
1 parent c107b6a commit 0706834
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 34 deletions.
16 changes: 10 additions & 6 deletions luma_core/src/allocate.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
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)
pub fn alloc_aligned<T: Copy>(size: usize) -> Pin<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) };
let boxed = Box::from(slice);
Pin::from(boxed)
}

/// Allocate an array aligned to a cacheline, and return it pinned.
Expand Down
61 changes: 52 additions & 9 deletions luma_core/src/vi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ 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: Pin<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 +37,66 @@ impl Xfb {
self.height
}

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

/// Get the stride of this XFB in bytes. Given the YUYV format this is always equal to
/// stride_in_u16() × 2.
pub fn stride_in_u8(&self) -> usize {
self.stride_in_u16() * 2
}

/// 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_in_u16();
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) })

Check warning on line 86 in luma_core/src/vi.rs

View workflow job for this annotation

GitHub Actions / Lints

transmute used without annotations
} 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> {}

Check warning on line 98 in luma_core/src/vi.rs

View workflow job for this annotation

GitHub Actions / Lints

the following explicit lifetimes could be elided: 'a

const BASE: u32 = 0xcc00_2000;

bitflags::bitflags! {
Expand Down Expand Up @@ -119,7 +162,7 @@ unsafe fn set_burst_blanking_interval_2(be2: u32, bs2: u32, be4: u32, bs4: u32)
}

unsafe fn set_xfb(addr: u32, xfb: &Xfb, bottom: bool) {
let stride = xfb.stride() as u32;
let stride = xfb.stride_in_u8() as u32;
let xfb = xfb.as_ptr();
let mut xfb = xfb as u32;
let shift;

Check warning on line 168 in luma_core/src/vi.rs

View workflow job for this annotation

GitHub Actions / Lints

unneeded late initialization
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 0706834

Please sign in to comment.