mirror of
https://github.com/LegitCamper/picocalc-os-rs.git
synced 2025-12-27 07:45:28 +00:00
remove unsafe fb
This commit is contained in:
@@ -46,7 +46,6 @@ pub fn get_key() -> KeyEvent {
|
|||||||
|
|
||||||
pub mod display {
|
pub mod display {
|
||||||
use abi_sys::CPixel;
|
use abi_sys::CPixel;
|
||||||
use alloc::{vec, vec::Vec};
|
|
||||||
use embedded_graphics::{
|
use embedded_graphics::{
|
||||||
Pixel,
|
Pixel,
|
||||||
geometry::{Dimensions, Point},
|
geometry::{Dimensions, Point},
|
||||||
@@ -54,7 +53,6 @@ pub mod display {
|
|||||||
prelude::{DrawTarget, Size},
|
prelude::{DrawTarget, Size},
|
||||||
primitives::Rectangle,
|
primitives::Rectangle,
|
||||||
};
|
};
|
||||||
use once_cell::unsync::Lazy;
|
|
||||||
|
|
||||||
pub const SCREEN_WIDTH: usize = 320;
|
pub const SCREEN_WIDTH: usize = 320;
|
||||||
pub const SCREEN_HEIGHT: usize = 320;
|
pub const SCREEN_HEIGHT: usize = 320;
|
||||||
|
|||||||
@@ -14,32 +14,29 @@ use embedded_hal_2::digital::OutputPin;
|
|||||||
use embedded_hal_async::{delay::DelayNs, spi::SpiDevice};
|
use embedded_hal_async::{delay::DelayNs, spi::SpiDevice};
|
||||||
use st7365p_lcd::ST7365P;
|
use st7365p_lcd::ST7365P;
|
||||||
|
|
||||||
pub const TILE_SIZE: usize = 16; // 16x16 tile
|
const TILE_SIZE: usize = 16; // 16x16 tile
|
||||||
pub const TILE_COUNT: usize = (SCREEN_WIDTH / TILE_SIZE) * (SCREEN_HEIGHT / TILE_SIZE); // 400 tiles
|
const TILE_COUNT: usize = (SCREEN_WIDTH / TILE_SIZE) * (SCREEN_HEIGHT / TILE_SIZE); // 400 tiles
|
||||||
|
|
||||||
// Group of tiles for batching
|
const MAX_BATCH_TILES: usize = (SCREEN_WIDTH / TILE_SIZE) * 2;
|
||||||
pub const MAX_META_TILES: usize = (SCREEN_WIDTH / TILE_SIZE) * 2; // max number of meta tiles in buffer
|
type BatchTileBuf = [u16; MAX_BATCH_TILES];
|
||||||
type MetaTileVec = heapless::Vec<Rectangle, { TILE_COUNT / MAX_META_TILES }>;
|
|
||||||
|
|
||||||
pub const SIZE: usize = SCREEN_HEIGHT * SCREEN_WIDTH;
|
pub const SIZE: usize = SCREEN_HEIGHT * SCREEN_WIDTH;
|
||||||
|
|
||||||
pub static FB_PAUSED: AtomicBool = AtomicBool::new(false);
|
pub static FB_PAUSED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
static mut DIRTY_TILES: LazyLock<heapless::Vec<AtomicBool, TILE_COUNT>> = LazyLock::new(|| {
|
|
||||||
let mut tiles = heapless::Vec::new();
|
|
||||||
for _ in 0..TILE_COUNT {
|
|
||||||
tiles.push(AtomicBool::new(true)).unwrap();
|
|
||||||
}
|
|
||||||
tiles
|
|
||||||
});
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct AtomicFrameBuffer<'a>(&'a mut [u16]);
|
pub struct AtomicFrameBuffer<'a> {
|
||||||
|
fb: &'a mut [u16],
|
||||||
|
dirty_tiles: LazyLock<[AtomicBool; TILE_COUNT]>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> AtomicFrameBuffer<'a> {
|
impl<'a> AtomicFrameBuffer<'a> {
|
||||||
pub fn new(buffer: &'a mut [u16]) -> Self {
|
pub fn new(buffer: &'a mut [u16]) -> Self {
|
||||||
assert!(buffer.len() == SIZE);
|
assert!(buffer.len() == SIZE);
|
||||||
Self(buffer)
|
Self {
|
||||||
|
fb: buffer,
|
||||||
|
dirty_tiles: LazyLock::new(|| [const { AtomicBool::new(true) }; TILE_COUNT]),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_tiles_dirty(&mut self, rect: Rectangle) {
|
fn mark_tiles_dirty(&mut self, rect: Rectangle) {
|
||||||
@@ -52,7 +49,7 @@ impl<'a> AtomicFrameBuffer<'a> {
|
|||||||
for ty in start_ty..=end_ty {
|
for ty in start_ty..=end_ty {
|
||||||
for tx in start_tx..=end_tx {
|
for tx in start_tx..=end_tx {
|
||||||
let tile_idx = ty * tiles_x + tx;
|
let tile_idx = ty * tiles_x + tx;
|
||||||
unsafe { DIRTY_TILES.get_mut()[tile_idx].store(true, Ordering::Release) };
|
self.dirty_tiles.get_mut()[tile_idx].store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +75,7 @@ impl<'a> AtomicFrameBuffer<'a> {
|
|||||||
for y in sy..=ey {
|
for y in sy..=ey {
|
||||||
for x in sx..=ex {
|
for x in sx..=ex {
|
||||||
if let Some(color) = color_iter.next() {
|
if let Some(color) = color_iter.next() {
|
||||||
self.0[(y as usize * SCREEN_WIDTH) + x as usize] = color;
|
self.fb[(y as usize * SCREEN_WIDTH) + x as usize] = color;
|
||||||
} else {
|
} else {
|
||||||
return Err(()); // Not enough data
|
return Err(()); // Not enough data
|
||||||
}
|
}
|
||||||
@@ -93,62 +90,6 @@ impl<'a> AtomicFrameBuffer<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk the dirty tiles and mark groups of tiles(meta-tiles) for batched updates
|
|
||||||
fn find_meta_tiles(&mut self, tiles_x: usize, tiles_y: usize) -> MetaTileVec {
|
|
||||||
let mut meta_tiles: MetaTileVec = heapless::Vec::new();
|
|
||||||
|
|
||||||
for ty in 0..tiles_y {
|
|
||||||
let mut tx = 0;
|
|
||||||
while tx < tiles_x {
|
|
||||||
let idx = ty * tiles_x + tx;
|
|
||||||
if !unsafe { DIRTY_TILES.get()[idx].load(Ordering::Acquire) } {
|
|
||||||
tx += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start meta-tile at this tile
|
|
||||||
let mut width_tiles = 1;
|
|
||||||
let height_tiles = 1;
|
|
||||||
|
|
||||||
// Grow horizontally, but keep under MAX_TILES_PER_METATILE
|
|
||||||
while tx + width_tiles < tiles_x
|
|
||||||
&& unsafe {
|
|
||||||
DIRTY_TILES.get()[ty * tiles_x + tx + width_tiles].load(Ordering::Acquire)
|
|
||||||
}
|
|
||||||
&& (width_tiles + height_tiles) <= MAX_META_TILES
|
|
||||||
{
|
|
||||||
width_tiles += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: for simplicity, skipped vertical growth
|
|
||||||
|
|
||||||
for x_off in 0..width_tiles {
|
|
||||||
unsafe {
|
|
||||||
DIRTY_TILES.get()[ty * tiles_x + tx + x_off]
|
|
||||||
.store(false, Ordering::Release);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// new meta-tile pos
|
|
||||||
let rect = Rectangle::new(
|
|
||||||
Point::new((tx * TILE_SIZE) as i32, (ty * TILE_SIZE) as i32),
|
|
||||||
Size::new(
|
|
||||||
(width_tiles * TILE_SIZE) as u32,
|
|
||||||
(height_tiles * TILE_SIZE) as u32,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if meta_tiles.push(rect).is_err() {
|
|
||||||
return meta_tiles;
|
|
||||||
};
|
|
||||||
|
|
||||||
tx += width_tiles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_tiles
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends the entire framebuffer to the display
|
/// Sends the entire framebuffer to the display
|
||||||
pub async fn draw<SPI, DC, RST, DELAY: DelayNs>(
|
pub async fn draw<SPI, DC, RST, DELAY: DelayNs>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -165,15 +106,13 @@ impl<'a> AtomicFrameBuffer<'a> {
|
|||||||
0,
|
0,
|
||||||
self.size().width as u16 - 1,
|
self.size().width as u16 - 1,
|
||||||
self.size().height as u16 - 1,
|
self.size().height as u16 - 1,
|
||||||
&self.0[..],
|
&self.fb[..],
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
unsafe {
|
for tile in self.dirty_tiles.get_mut().iter() {
|
||||||
for tile in DIRTY_TILES.get_mut().iter() {
|
|
||||||
tile.store(false, Ordering::Release);
|
tile.store(false, Ordering::Release);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -191,7 +130,7 @@ impl<'a> AtomicFrameBuffer<'a> {
|
|||||||
let tiles_x = SCREEN_WIDTH / TILE_SIZE;
|
let tiles_x = SCREEN_WIDTH / TILE_SIZE;
|
||||||
let _tiles_y = SCREEN_HEIGHT / TILE_SIZE;
|
let _tiles_y = SCREEN_HEIGHT / TILE_SIZE;
|
||||||
|
|
||||||
let tiles = unsafe { DIRTY_TILES.get_mut() };
|
let tiles = self.dirty_tiles.get_mut();
|
||||||
let mut pixel_buffer: heapless::Vec<u16, { TILE_SIZE * TILE_SIZE }> = heapless::Vec::new();
|
let mut pixel_buffer: heapless::Vec<u16, { TILE_SIZE * TILE_SIZE }> = heapless::Vec::new();
|
||||||
|
|
||||||
for tile_idx in 0..TILE_COUNT {
|
for tile_idx in 0..TILE_COUNT {
|
||||||
@@ -210,7 +149,9 @@ impl<'a> AtomicFrameBuffer<'a> {
|
|||||||
for y in y_start..y_end {
|
for y in y_start..y_end {
|
||||||
let start = y * SCREEN_WIDTH + x_start;
|
let start = y * SCREEN_WIDTH + x_start;
|
||||||
let end = y * SCREEN_WIDTH + x_end;
|
let end = y * SCREEN_WIDTH + x_end;
|
||||||
pixel_buffer.extend_from_slice(&self.0[start..end]).unwrap();
|
pixel_buffer
|
||||||
|
.extend_from_slice(&self.fb[start..end])
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
display
|
display
|
||||||
@@ -228,72 +169,6 @@ impl<'a> AtomicFrameBuffer<'a> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends only dirty tiles (16x16px) in batches to the display
|
|
||||||
pub async fn partial_draw<SPI, DC, RST, DELAY>(
|
|
||||||
&mut self,
|
|
||||||
display: &mut ST7365P<SPI, DC, RST, DELAY>,
|
|
||||||
) -> Result<(), ()>
|
|
||||||
where
|
|
||||||
SPI: SpiDevice,
|
|
||||||
DC: OutputPin,
|
|
||||||
RST: OutputPin,
|
|
||||||
DELAY: DelayNs,
|
|
||||||
{
|
|
||||||
if unsafe { DIRTY_TILES.get().iter().any(|p| p.load(Ordering::Acquire)) } {
|
|
||||||
let tiles_x = (SCREEN_WIDTH + TILE_SIZE - 1) / TILE_SIZE;
|
|
||||||
let tiles_y = (SCREEN_HEIGHT + TILE_SIZE - 1) / TILE_SIZE;
|
|
||||||
|
|
||||||
let meta_tiles = self.find_meta_tiles(tiles_x, tiles_y);
|
|
||||||
|
|
||||||
// buffer for copying meta tiles before sending to display
|
|
||||||
let mut pixel_buffer: heapless::Vec<u16, { MAX_META_TILES * TILE_SIZE * TILE_SIZE }> =
|
|
||||||
heapless::Vec::new();
|
|
||||||
|
|
||||||
for rect in meta_tiles {
|
|
||||||
let rect_width = rect.size.width as usize;
|
|
||||||
let rect_height = rect.size.height as usize;
|
|
||||||
let rect_x = rect.top_left.x as usize;
|
|
||||||
let rect_y = rect.top_left.y as usize;
|
|
||||||
|
|
||||||
pixel_buffer.clear();
|
|
||||||
|
|
||||||
for row in 0..rect_height {
|
|
||||||
let y = rect_y + row;
|
|
||||||
let start = y * SCREEN_WIDTH + rect_x;
|
|
||||||
let end = start + rect_width;
|
|
||||||
|
|
||||||
// Safe: we guarantee buffer will not exceed MAX_META_TILE_PIXELS
|
|
||||||
pixel_buffer.extend_from_slice(&self.0[start..end]).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
display
|
|
||||||
.set_pixels_buffered(
|
|
||||||
rect_x as u16,
|
|
||||||
rect_y as u16,
|
|
||||||
(rect_x + rect_width - 1) as u16,
|
|
||||||
(rect_y + rect_height - 1) as u16,
|
|
||||||
&pixel_buffer,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// walk the meta-tile and set as clean
|
|
||||||
let start_tx = rect_x / TILE_SIZE;
|
|
||||||
let start_ty = rect_y / TILE_SIZE;
|
|
||||||
let end_tx = (rect_x + rect_width - 1) / TILE_SIZE;
|
|
||||||
let end_ty = (rect_y + rect_height - 1) / TILE_SIZE;
|
|
||||||
|
|
||||||
for ty in start_ty..=end_ty {
|
|
||||||
for tx in start_tx..=end_tx {
|
|
||||||
let tile_idx = ty * tiles_x + tx;
|
|
||||||
unsafe { DIRTY_TILES.get_mut()[tile_idx].store(false, Ordering::Release) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
|
impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
|
||||||
@@ -315,8 +190,8 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
|
|||||||
if (x as usize) < SCREEN_WIDTH && (y as usize) < SCREEN_HEIGHT {
|
if (x as usize) < SCREEN_WIDTH && (y as usize) < SCREEN_HEIGHT {
|
||||||
let idx = (y as usize) * SCREEN_WIDTH + (x as usize);
|
let idx = (y as usize) * SCREEN_WIDTH + (x as usize);
|
||||||
let raw_color = RawU16::from(color).into_inner();
|
let raw_color = RawU16::from(color).into_inner();
|
||||||
if self.0[idx] != raw_color {
|
if self.fb[idx] != raw_color {
|
||||||
self.0[idx] = raw_color;
|
self.fb[idx] = raw_color;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,8 +240,8 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
|
|||||||
if let Some(color) = colors.next() {
|
if let Some(color) = colors.next() {
|
||||||
let idx = (p.y as usize * SCREEN_WIDTH) + (p.x as usize);
|
let idx = (p.y as usize * SCREEN_WIDTH) + (p.x as usize);
|
||||||
let raw_color = RawU16::from(color).into_inner();
|
let raw_color = RawU16::from(color).into_inner();
|
||||||
if self.0[idx] != raw_color {
|
if self.fb[idx] != raw_color {
|
||||||
self.0[idx] = raw_color;
|
self.fb[idx] = raw_color;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -404,7 +279,7 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
|
|||||||
.take((self.size().width * self.size().height) as usize),
|
.take((self.size().width * self.size().height) as usize),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
for tile in unsafe { DIRTY_TILES.get_mut() }.iter() {
|
for tile in self.dirty_tiles.get_mut().iter() {
|
||||||
tile.store(true, Ordering::Release);
|
tile.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ mod abi;
|
|||||||
mod display;
|
mod display;
|
||||||
mod elf;
|
mod elf;
|
||||||
mod framebuffer;
|
mod framebuffer;
|
||||||
|
#[allow(unused)]
|
||||||
mod heap;
|
mod heap;
|
||||||
mod peripherals;
|
mod peripherals;
|
||||||
|
#[allow(unused)]
|
||||||
mod psram;
|
mod psram;
|
||||||
mod scsi;
|
mod scsi;
|
||||||
mod storage;
|
mod storage;
|
||||||
@@ -225,6 +227,7 @@ struct Sd {
|
|||||||
cs: Peri<'static, PIN_17>,
|
cs: Peri<'static, PIN_17>,
|
||||||
det: Peri<'static, PIN_22>,
|
det: Peri<'static, PIN_22>,
|
||||||
}
|
}
|
||||||
|
#[allow(dead_code)]
|
||||||
struct Psram {
|
struct Psram {
|
||||||
pio: Peri<'static, PIO0>,
|
pio: Peri<'static, PIO0>,
|
||||||
sclk: Peri<'static, PIN_21>,
|
sclk: Peri<'static, PIN_21>,
|
||||||
|
|||||||
Reference in New Issue
Block a user