diff options
author | Astatin <[email protected]> | 2025-07-15 18:57:03 +0200 |
---|---|---|
committer | Astatin <[email protected]> | 2025-07-15 18:57:03 +0200 |
commit | c6972abff6c81565a41df8731509435274a80c1f (patch) | |
tree | 5d73ee5e68132df3465ad1b2b260940b6b40f1b7 | |
parent | 81f8a04c38671a82c756450bbe13803e1701ada0 (diff) |
Make render function not block when window is suspended
-rw-r--r-- | src/desktop/input.rs | 59 | ||||
-rw-r--r-- | src/desktop/serial.rs | 3 | ||||
-rw-r--r-- | src/desktop/window.rs | 157 | ||||
-rw-r--r-- | src/display.rs | 43 | ||||
-rw-r--r-- | src/io.rs | 6 |
5 files changed, 149 insertions, 119 deletions
diff --git a/src/desktop/input.rs b/src/desktop/input.rs index d7c10c8..55bbfb3 100644 --- a/src/desktop/input.rs +++ b/src/desktop/input.rs @@ -130,46 +130,47 @@ impl Keyboard { impl Input for Keyboard { fn update_events(&mut self, _cycles: u128) -> Option<u128> { - let mut res = 0xf; - let keys = self.keys.borrow(); + if let Ok(keys) = self.keys.lock() { + let mut res = 0xf; - if keys.contains(&KeyCode::KeyA) { - res &= 0b1110; - } + if (*keys).contains(&KeyCode::KeyA) { + res &= 0b1110; + } - if keys.contains(&KeyCode::KeyB) { - res &= 0b1101; - } + if (*keys).contains(&KeyCode::KeyB) { + res &= 0b1101; + } - if keys.contains(&KeyCode::Backspace) { - res &= 0b1011; - } + if (*keys).contains(&KeyCode::Backspace) { + res &= 0b1011; + } - if keys.contains(&KeyCode::Enter) { - res &= 0b0111; - } + if (*keys).contains(&KeyCode::Enter) { + res &= 0b0111; + } - self.action_reg = res; + self.action_reg = res; - let mut res = 0xf; + let mut res = 0xf; - if keys.contains(&KeyCode::ArrowRight) { - res &= 0b1110; - } + if (*keys).contains(&KeyCode::ArrowRight) { + res &= 0b1110; + } - if keys.contains(&KeyCode::ArrowLeft) { - res &= 0b1101; - } + if (*keys).contains(&KeyCode::ArrowLeft) { + res &= 0b1101; + } - if keys.contains(&KeyCode::ArrowUp) { - res &= 0b1011; - } + if (*keys).contains(&KeyCode::ArrowUp) { + res &= 0b1011; + } - if keys.contains(&KeyCode::ArrowDown) { - res &= 0b0111; - } + if (*keys).contains(&KeyCode::ArrowDown) { + res &= 0b0111; + } - self.direction_reg = res; + self.direction_reg = res; + } None } diff --git a/src/desktop/serial.rs b/src/desktop/serial.rs index 3eac454..d8fc2e3 100644 --- a/src/desktop/serial.rs +++ b/src/desktop/serial.rs @@ -138,7 +138,6 @@ pub struct TcpSerial { external_clock: bool, next_byte_transfer_cycle: u128, - next_byte_receive_cycle: u128, no_response: bool, @@ -188,7 +187,6 @@ impl TcpSerial { external_clock: false, next_byte_transfer_cycle: 0, - next_byte_receive_cycle: 0, input, output, @@ -214,7 +212,6 @@ impl TcpSerial { external_clock: false, next_byte_transfer_cycle: 0, - next_byte_receive_cycle: 0, input, output, diff --git a/src/desktop/window.rs b/src/desktop/window.rs index 2fea3e5..06b672d 100644 --- a/src/desktop/window.rs +++ b/src/desktop/window.rs @@ -1,83 +1,76 @@ -use std::cell::RefCell; use std::collections::HashSet; -use std::rc::Rc; -use std::sync::Arc; -use std::time::Duration; +use std::sync::{Arc, Mutex}; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::thread; use crate::io::{Window, WindowSignal}; use pixels::{Error, Pixels, SurfaceTexture}; use winit::dpi::LogicalSize; use winit::event::{Event, WindowEvent}; -use winit::event_loop::EventLoop; +use winit::event_loop::EventLoopBuilder; use winit::keyboard::{KeyCode, PhysicalKey}; -use winit::platform::pump_events::EventLoopExtPumpEvents; -use winit::window::{Window as WinitWindow, WindowBuilder}; +use winit::platform::wayland::EventLoopBuilderExtWayland; +use winit::window::{WindowBuilder}; use winit_input_helper::WinitInputHelper; const WIDTH: u32 = 160; const HEIGHT: u32 = 144; -pub type Keys = Rc<RefCell<HashSet<KeyCode>>>; +pub type Keys = Arc<Mutex<HashSet<KeyCode>>>; -pub struct DesktopWindow<'a> { - event_loop: EventLoop<()>, - input: WinitInputHelper, - window: Arc<WinitWindow>, - pixels: Pixels<'a>, +pub struct DesktopWindow { + fb_send: Sender<Box<[u32; 160 * 144]>>, + signal_recv: Receiver<WindowSignal>, pub keys: Keys, } -fn draw(frame: &mut [u8], fb: &[u32; 160 * 144]) { - for (i, pixel) in frame.chunks_exact_mut(4).enumerate() { - pixel.copy_from_slice(&((fb[i] << 8) | 0xff).to_be_bytes()) - } -} - -impl<'a> DesktopWindow<'a> { +impl DesktopWindow { pub fn new(title: impl Into<String>) -> Result<Self, Error> { - let event_loop = EventLoop::new().unwrap(); - let input = WinitInputHelper::new(); - let window = Arc::new({ - let size = LogicalSize::new((WIDTH * 4) as f64, (HEIGHT * 4) as f64); - WindowBuilder::new() - .with_title(title.into()) - .with_inner_size(size) - .with_min_inner_size(size) - .build(&event_loop) - .unwrap() - }); + let title: String = title.into(); + let (fb_send, fb_recv) = channel(); + let (signal_send, signal_recv) = channel(); - let pixels = { - let window_size = window.inner_size(); - let surface_texture = - SurfaceTexture::new(window_size.width, window_size.height, window.clone()); - Pixels::new(WIDTH, HEIGHT, surface_texture)? - }; + let keys = Arc::new(Mutex::new(HashSet::new())); - Ok(Self { - event_loop, - input, - window, - pixels, - keys: Rc::new(HashSet::new().into()), - }) - } -} + let key_eventloop = keys.clone(); + thread::spawn(move || { + let keys = key_eventloop; + let event_loop = EventLoopBuilder::new().with_any_thread(true).build().unwrap(); + let mut input = WinitInputHelper::new(); + let window = Arc::new({ + let size = LogicalSize::new((WIDTH * 4) as f64, (HEIGHT * 4) as f64); + WindowBuilder::new() + .with_title(title) + .with_inner_size(size) + .with_min_inner_size(size) + .build(&event_loop) + .unwrap() + }); -impl<'a> Window for DesktopWindow<'a> { - fn update(&mut self, fb: &[u32; 160 * 144]) -> Option<WindowSignal> { - let mut res = None; - let mut keys = (*self.keys).borrow_mut(); - self.event_loop - .pump_events(Some(Duration::ZERO), |event, elwt| { + let mut pixels = { + let window_size = window.inner_size(); + let surface_texture = + SurfaceTexture::new(window_size.width, window_size.height, window.clone()); + Pixels::new(WIDTH, HEIGHT, surface_texture).unwrap() + }; + let mut fb = Box::new([0; 160 * 144]); + event_loop + .run(|event, elwt| { if let Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } = event { - draw(self.pixels.frame_mut(), fb); - if let Err(err) = self.pixels.render() { + loop { + if let Ok(new_fb) = fb_recv.try_recv() { + fb = new_fb; + } else { + break; + } + } + draw(pixels.frame_mut(), &fb); + if let Err(err) = pixels.render() { eprintln!("Error during render: {}", err); return; } @@ -93,33 +86,63 @@ impl<'a> Window for DesktopWindow<'a> { }, } = event { - if let PhysicalKey::Code(keycode) = keyboard_event.physical_key { - if keyboard_event.state.is_pressed() { - keys.insert(keycode); - } else { - keys.remove(&keycode); + if let Ok(mut keys) = keys.lock() { + if let PhysicalKey::Code(keycode) = keyboard_event.physical_key { + if keyboard_event.state.is_pressed() { + (*keys).insert(keycode); + } else { + (*keys).remove(&keycode); + } } } } - if self.input.update(&event) { - if self.input.close_requested() { + if input.update(&event) { + if input.close_requested() { elwt.exit(); - res = Some(WindowSignal::Exit); + if let Err(err) = signal_send.send(WindowSignal::Exit) { + eprintln!("window signal send failed with error {}", err); + } return; } - if let Some(size) = self.input.window_resized() { - if let Err(err) = self.pixels.resize_surface(size.width, size.height) { + if let Some(size) = input.window_resized() { + if let Err(err) = pixels.resize_surface(size.width, size.height) { eprintln!("Error during resize: {}", err); return; } } - self.window.request_redraw(); + window.request_redraw(); } - }); + }).unwrap(); + }); + + + Ok(Self { + fb_send, + signal_recv, + keys, + }) + } +} + +impl Window for DesktopWindow { + fn update(&mut self, fb: Box<[u32; 160 * 144]>) -> Option<WindowSignal> { + if let Err(err) = self.fb_send.send(fb) { + eprintln!("Framebuffer channel send failed with error: {}", err); + } + + if let Ok(signal) = self.signal_recv.try_recv() { + Some(signal) + } else { + None + } + } +} - res +fn draw(frame: &mut [u8], fb: &[u32; 160 * 144]) { + for (i, pixel) in frame.chunks_exact_mut(4).enumerate() { + pixel.copy_from_slice(&((fb[i] << 8) | 0xff).to_be_bytes()) } } diff --git a/src/display.rs b/src/display.rs index 0417a00..7ef95fe 100644 --- a/src/display.rs +++ b/src/display.rs @@ -2,6 +2,7 @@ use crate::consts::DISPLAY_UPDATE_SLEEP_TIME_MICROS; use std::time::SystemTime; +use std::mem; const COLORS: [u32; 4] = [0x00e0f8d0, 0x0088c070, 0x346856, 0x00081820]; @@ -27,15 +28,15 @@ pub enum DisplayInterrupt { #[derive(Debug)] pub struct Display { - framebuffer: [u32; 160 * 144], - bg_buffer: [u8; 160 * 144], + framebuffer: Box<[u32; 160 * 144]>, + bg_buffer: Box<[u8; 160 * 144]>, - tiledata: [u8; 0x3000], - bg_map_attr: [u8; 0x400], - tilemaps: [u8; 0x800], - oam: [u8; 0xa0], + tiledata: Box<[u8; 0x3000]>, + bg_map_attr: Box<[u8; 0x400]>, + tilemaps: Box<[u8; 0x800]>, + oam: Box<[u8; 0xa0]>, - pub cram: [u8; 0x80], + pub cram: Box<[u8; 0x80]>, pub bg_palette: u8, pub obj_palettes: [u8; 2], pub viewport_y: u8, @@ -56,19 +57,19 @@ pub struct Display { pub stat: u64, - pub redraw_request: Option<[u32; 160 * 144]>, + pub redraw_request: Option<Box<[u32; 160 * 144]>>, } impl Display { pub fn new() -> Self { Self { - framebuffer: [0; 160 * 144], - bg_buffer: [0; 160 * 144], - tiledata: [0; 0x3000], - bg_map_attr: [0; 0x400], - cram: [0; 0x80], - tilemaps: [0; 0x800], - oam: [0; 0xa0], + framebuffer: Box::new([0; 160 * 144]), + bg_buffer: Box::new([0; 160 * 144]), + tiledata: Box::new([0; 0x3000]), + bg_map_attr: Box::new([0; 0x400]), + cram: Box::new([0; 0x80]), + tilemaps: Box::new([0; 0x800]), + oam: Box::new([0; 0xa0]), bg_palette: 0, vram_bank: 0, obj_palettes: [0; 2], @@ -89,7 +90,7 @@ impl Display { } pub fn cls(&mut self) { - self.framebuffer = [COLORS[0]; 160 * 144]; + self.framebuffer = Box::new([COLORS[0]; 160 * 144]); } pub fn color_palette(&self, color_byte: u8, palette: u8, cgb_mode: bool) -> u32 { @@ -332,7 +333,7 @@ impl Display { .as_micros() > DISPLAY_UPDATE_SLEEP_TIME_MICROS as u128 { - self.redraw_request = Some(self.framebuffer); + self.redraw_request = Some(self.framebuffer.clone()); self.last_dt = SystemTime::now(); } @@ -352,4 +353,12 @@ impl Display { return ret_interrupt; } + + pub fn get_redraw_request(&mut self) -> Option<Box<[u32; 160 * 144]>> { + let mut result = None; + + mem::swap(&mut result, &mut self.redraw_request); + + result + } } @@ -31,7 +31,7 @@ pub enum WindowSignal { } pub trait Window { - fn update(&mut self, fb: &[u32; 160 * 144]) -> Option<WindowSignal>; + fn update(&mut self, fb: Box<[u32; 160 * 144]>) -> Option<WindowSignal>; } pub trait Serial { @@ -208,8 +208,8 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, } if nanos_sleep > 0.0 { - if let Some(fb) = state.mem.display.redraw_request { - if let Some(WindowSignal::Exit) = window.update(&fb) { + if let Some(fb) = state.mem.display.get_redraw_request() { + if let Some(WindowSignal::Exit) = window.update(fb) { break; } } |