aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2025-07-15 18:57:03 +0200
committerAstatin <[email protected]>2025-07-15 18:57:03 +0200
commitc6972abff6c81565a41df8731509435274a80c1f (patch)
tree5d73ee5e68132df3465ad1b2b260940b6b40f1b7
parent81f8a04c38671a82c756450bbe13803e1701ada0 (diff)
Make render function not block when window is suspended
-rw-r--r--src/desktop/input.rs59
-rw-r--r--src/desktop/serial.rs3
-rw-r--r--src/desktop/window.rs157
-rw-r--r--src/display.rs43
-rw-r--r--src/io.rs6
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
+ }
}
diff --git a/src/io.rs b/src/io.rs
index fae191a..92e5984 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -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;
}
}