get fps better

This commit is contained in:
2025-10-28 12:20:02 -06:00
parent bbc9613f62
commit a7daa4c5fc
6 changed files with 72 additions and 21 deletions

View File

@@ -65,10 +65,10 @@ pub mod display {
abi_sys::lock_display(lock); abi_sys::lock_display(lock);
} }
// const BUF_SIZE: usize = 15 * 1024; // tune this for performance const BUF_SIZE: usize = 15 * 1024; // tune this for performance
// static mut BUF: [CPixel; BUF_SIZE] = [CPixel::new(); BUF_SIZE]; static mut BUF: [CPixel; BUF_SIZE] = [CPixel::new(); BUF_SIZE];
const BUF_SIZE: usize = 250 * 1024; // tune this for performance // const BUF_SIZE: usize = 250 * 1024; // tune this for performance
static mut BUF: Lazy<Vec<CPixel>> = Lazy::new(|| vec![const { CPixel::new() }; BUF_SIZE]); // static mut BUF: Lazy<Vec<CPixel>> = Lazy::new(|| vec![const { CPixel::new() }; BUF_SIZE]);
pub struct Display; pub struct Display;
@@ -97,7 +97,7 @@ pub mod display {
unsafe { BUF[count] = p.into() }; unsafe { BUF[count] = p.into() };
count += 1; count += 1;
if count == BUF_SIZE { if count == BUF_SIZE - 1 {
abi_sys::draw_iter(unsafe { BUF.as_ptr() }, count); abi_sys::draw_iter(unsafe { BUF.as_ptr() }, count);
count = 0; count = 0;
} }

View File

@@ -11,7 +11,8 @@ use embedded_sdmmc::{DirEntry, LfnBuffer};
use heapless::spsc::Queue; use heapless::spsc::Queue;
use crate::{ use crate::{
display::{FB_PAUSED, FRAMEBUFFER}, display::FRAMEBUFFER,
framebuffer::FB_PAUSED,
storage::{Dir, File, SDCARD}, storage::{Dir, File, SDCARD},
}; };
@@ -74,7 +75,7 @@ pub extern "C" fn get_ms() -> u64 {
const _: LockDisplay = lock_display; const _: LockDisplay = lock_display;
pub extern "C" fn lock_display(lock: bool) { pub extern "C" fn lock_display(lock: bool) {
FB_PAUSED.store(lock, Ordering::Relaxed); FB_PAUSED.store(lock, Ordering::Release);
} }
const _: DrawIterAbi = draw_iter; const _: DrawIterAbi = draw_iter;
@@ -83,7 +84,10 @@ pub extern "C" fn draw_iter(cpixels: *const CPixel, len: usize) {
let cpixels = unsafe { core::slice::from_raw_parts(cpixels, len) }; let cpixels = unsafe { core::slice::from_raw_parts(cpixels, len) };
let iter = cpixels.iter().copied().map(|c: CPixel| c.into()); let iter = cpixels.iter().copied().map(|c: CPixel| c.into());
FB_PAUSED.store(true, Ordering::Release);
unsafe { FRAMEBUFFER.draw_iter(iter).unwrap() } unsafe { FRAMEBUFFER.draw_iter(iter).unwrap() }
FB_PAUSED.store(false, Ordering::Release);
} }
pub static mut KEY_CACHE: Queue<KeyEvent, 32> = Queue::new(); pub static mut KEY_CACHE: Queue<KeyEvent, 32> = Queue::new();

View File

@@ -26,7 +26,6 @@ pub static mut FRAMEBUFFER: Lazy<AtomicFrameBuffer> = Lazy::new(|| {
static mut BUF: [u16; framebuffer::SIZE] = [0; framebuffer::SIZE]; static mut BUF: [u16; framebuffer::SIZE] = [0; framebuffer::SIZE];
AtomicFrameBuffer::new(unsafe { &mut BUF }) AtomicFrameBuffer::new(unsafe { &mut BUF })
}); });
pub static FB_PAUSED: AtomicBool = AtomicBool::new(false);
pub async fn init_display( pub async fn init_display(
spi: Spi<'static, SPI1, Async>, spi: Spi<'static, SPI1, Async>,
@@ -54,9 +53,7 @@ pub async fn init_display(
#[embassy_executor::task] #[embassy_executor::task]
pub async fn display_handler(mut display: DISPLAY) { pub async fn display_handler(mut display: DISPLAY) {
loop { loop {
if !FB_PAUSED.load(Ordering::Acquire) { unsafe { FRAMEBUFFER.safe_draw(&mut display).await.unwrap() };
unsafe { FRAMEBUFFER.partial_draw(&mut display).await.unwrap() }
}
// small yield to allow other tasks to run // small yield to allow other tasks to run
Timer::after_nanos(100).await; Timer::after_nanos(100).await;

View File

@@ -18,11 +18,13 @@ pub const TILE_SIZE: usize = 16; // 16x16 tile
pub const TILE_COUNT: usize = (SCREEN_WIDTH / TILE_SIZE) * (SCREEN_HEIGHT / TILE_SIZE); // 400 tiles pub const TILE_COUNT: usize = (SCREEN_WIDTH / TILE_SIZE) * (SCREEN_HEIGHT / TILE_SIZE); // 400 tiles
// Group of tiles for batching // Group of tiles for batching
pub const MAX_META_TILES: usize = SCREEN_WIDTH / TILE_SIZE; // max number of meta tiles in buffer pub const MAX_META_TILES: usize = (SCREEN_WIDTH / TILE_SIZE) * 2; // max number of meta tiles in buffer
type MetaTileVec = heapless::Vec<Rectangle, { TILE_COUNT / MAX_META_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);
static mut DIRTY_TILES: LazyLock<heapless::Vec<AtomicBool, TILE_COUNT>> = LazyLock::new(|| { static mut DIRTY_TILES: LazyLock<heapless::Vec<AtomicBool, TILE_COUNT>> = LazyLock::new(|| {
let mut tiles = heapless::Vec::new(); let mut tiles = heapless::Vec::new();
for _ in 0..TILE_COUNT { for _ in 0..TILE_COUNT {
@@ -50,7 +52,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::Relaxed) }; unsafe { DIRTY_TILES.get_mut()[tile_idx].store(true, Ordering::Release) };
} }
} }
} }
@@ -111,7 +113,7 @@ impl<'a> AtomicFrameBuffer<'a> {
// Grow horizontally, but keep under MAX_TILES_PER_METATILE // Grow horizontally, but keep under MAX_TILES_PER_METATILE
while tx + width_tiles < tiles_x while tx + width_tiles < tiles_x
&& unsafe { && unsafe {
DIRTY_TILES.get()[ty * tiles_x + tx + width_tiles].load(Ordering::Relaxed) DIRTY_TILES.get()[ty * tiles_x + tx + width_tiles].load(Ordering::Acquire)
} }
&& (width_tiles + height_tiles) <= MAX_META_TILES && (width_tiles + height_tiles) <= MAX_META_TILES
{ {
@@ -176,6 +178,57 @@ impl<'a> AtomicFrameBuffer<'a> {
Ok(()) Ok(())
} }
pub async fn safe_draw<SPI, DC, RST, DELAY>(
&mut self,
display: &mut ST7365P<SPI, DC, RST, DELAY>,
) -> Result<(), ()>
where
SPI: SpiDevice,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
let tiles_x = SCREEN_WIDTH / TILE_SIZE;
let _tiles_y = SCREEN_HEIGHT / TILE_SIZE;
let tiles = unsafe { DIRTY_TILES.get_mut() };
let mut pixel_buffer: heapless::Vec<u16, { TILE_SIZE * TILE_SIZE }> = heapless::Vec::new();
for tile_idx in 0..TILE_COUNT {
if tiles[tile_idx].swap(false, Ordering::AcqRel) {
let tx = tile_idx % tiles_x;
let ty = tile_idx / tiles_x;
let x_start = tx * TILE_SIZE;
let y_start = ty * TILE_SIZE;
let x_end = (x_start + TILE_SIZE).min(SCREEN_WIDTH);
let y_end = (y_start + TILE_SIZE).min(SCREEN_HEIGHT);
pixel_buffer.clear();
for y in y_start..y_end {
let start = y * SCREEN_WIDTH + x_start;
let end = y * SCREEN_WIDTH + x_end;
pixel_buffer.extend_from_slice(&self.0[start..end]).unwrap();
}
display
.set_pixels_buffered(
x_start as u16,
y_start as u16,
(x_end - 1) as u16,
(y_end - 1) as u16,
&pixel_buffer,
)
.await
.unwrap();
}
}
Ok(())
}
/// Sends only dirty tiles (16x16px) in batches to the display /// Sends only dirty tiles (16x16px) in batches to the display
pub async fn partial_draw<SPI, DC, RST, DELAY>( pub async fn partial_draw<SPI, DC, RST, DELAY>(
&mut self, &mut self,

View File

@@ -1,9 +1,6 @@
use crate::{ use crate::{
BINARY_CH, BINARY_CH, display::FRAMEBUFFER, elf::load_binary, framebuffer::FB_PAUSED,
display::{FB_PAUSED, FRAMEBUFFER}, peripherals::keyboard, storage::FileName,
elf::load_binary,
peripherals::keyboard,
storage::FileName,
}; };
use abi_sys::keyboard::{KeyCode, KeyState}; use abi_sys::keyboard::{KeyCode, KeyState};
use alloc::{str::FromStr, string::String, vec::Vec}; use alloc::{str::FromStr, string::String, vec::Vec};

View File

@@ -42,9 +42,9 @@ pub fn main() {
for frame in gif.frames() { for frame in gif.frames() {
let start = get_ms(); let start = get_ms();
lock_display(true); // lock_display(true);
frame.draw(&mut display).unwrap(); frame.draw(&mut display).unwrap();
lock_display(false); // lock_display(false);
// frame_num += 1; // frame_num += 1;