diff --git a/crates/riddle-audio/src/audio_system.rs b/crates/riddle-audio/src/audio_system.rs index ac965a3..77194d6 100644 --- a/crates/riddle-audio/src/audio_system.rs +++ b/crates/riddle-audio/src/audio_system.rs @@ -1,28 +1,29 @@ use crate::*; -use riddle_common::clone_handle::CloneHandle; use rodio::{Device, Sink}; use std::{ - cell::RefCell, collections::HashMap, - rc::{Rc, Weak}, + sync::Arc, + sync::Mutex, time::{Duration, Instant}, }; pub struct AudioSystem { - weak_self: Weak, + weak_self: AudioSystemWeak, pub(super) device: Device, - fades: RefCell>, + fades: Mutex>, } +define_handles!(::weak_self, pub AudioSystemHandle, pub AudioSystemWeak); + impl AudioSystem { - pub fn new() -> Result, AudioError> { + pub fn new() -> Result { let device = rodio::default_output_device().ok_or(AudioError::UnknownError)?; - Ok(Rc::new_cyclic(|weak_self| AudioSystem { - weak_self: weak_self.clone(), + Ok(AudioSystemHandle::new(|weak_self| AudioSystem { + weak_self, device, - fades: RefCell::new(HashMap::new()), + fades: Mutex::new(HashMap::new()), })) } @@ -32,7 +33,7 @@ impl AudioSystem { } pub(crate) fn register_fade(&self, fade: Fade) { - let mut fades = self.fades.borrow_mut(); + let mut fades = self.fades.lock().unwrap(); let existing = fades.remove(&fade.key()); match existing { Some(old) => fades.insert(fade.key(), Fade::merge_pair(old, fade)), @@ -41,13 +42,13 @@ impl AudioSystem { } pub fn tick_fades(&self, now: Instant) { - let mut fades = self.fades.borrow_mut(); + let mut fades = self.fades.lock().unwrap(); fades.retain(|_, f| f.update(now)); } } struct FadeKey { - sink: Rc, + sink: Arc, } impl std::hash::Hash for FadeKey { @@ -58,7 +59,7 @@ impl std::hash::Hash for FadeKey { impl std::cmp::PartialEq for FadeKey { fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.sink, &other.sink) + Arc::ptr_eq(&self.sink, &other.sink) } } @@ -71,7 +72,7 @@ pub(crate) enum FadeType { } pub(crate) struct Fade { - sink: Rc, + sink: Arc, start_volume: f32, dest_volume: f32, start_time: Instant, @@ -81,7 +82,7 @@ pub(crate) struct Fade { impl Fade { pub(crate) fn new( - sink: Rc, + sink: Arc, dest_volume: f32, duration: Duration, fade_type: FadeType, @@ -132,9 +133,3 @@ impl Fade { } } } - -impl CloneHandle for AudioSystem { - fn clone_weak_handle(&self) -> Weak { - self.weak_self.clone() - } -} diff --git a/crates/riddle-audio/src/clip_player.rs b/crates/riddle-audio/src/clip_player.rs index e7e153d..a794a81 100644 --- a/crates/riddle-audio/src/clip_player.rs +++ b/crates/riddle-audio/src/clip_player.rs @@ -1,15 +1,14 @@ use crate::*; -use riddle_common::clone_handle::CloneHandle; use rodio::{decoder::Decoder, source::Source, Sink}; -use std::{io::Cursor, rc::Rc, time::Duration}; +use std::{io::Cursor, sync::Arc, time::Duration}; const QUICK_FADE_DURATION_SECONDS: f32 = 0.2; pub struct ClipPlayer { - audio: Rc, + audio: AudioSystemHandle, clip: Clip, - sink: Option>, + sink: Option>, volume: f32, } @@ -25,7 +24,7 @@ impl ClipPlayer { } fn play(&mut self, mode: PlayMode) -> Result<(), AudioError> { - let sink: Rc = Sink::new(&self.audio.device).into(); + let sink: Arc = Sink::new(&self.audio.device).into(); sink.set_volume(self.volume); let source = Decoder::new(Cursor::new(self.clip.data.clone())) .map_err(|_| AudioError::UnknownError)?; diff --git a/crates/riddle-audio/src/lib.rs b/crates/riddle-audio/src/lib.rs index a89dab5..85f47d8 100644 --- a/crates/riddle-audio/src/lib.rs +++ b/crates/riddle-audio/src/lib.rs @@ -5,6 +5,8 @@ mod clip; mod clip_player; mod error; +use riddle_common::*; + pub use audio_system::*; pub use clip::*; pub use clip_player::*; diff --git a/crates/riddle-common/src/clone_handle.rs b/crates/riddle-common/src/clone_handle.rs index 8ce9a75..00912aa 100644 --- a/crates/riddle-common/src/clone_handle.rs +++ b/crates/riddle-common/src/clone_handle.rs @@ -1,10 +1,77 @@ -use std::rc::{Rc, Weak}; - pub trait CloneHandle { - #[inline] - fn clone_handle(&self) -> Option> { - std::rc::Weak::upgrade(&self.clone_weak_handle()) - } + type Handle; + type WeakHandle; + + fn clone_handle(&self) -> Option; + fn clone_weak_handle(&self) -> Self::WeakHandle; +} + +#[macro_export] +macro_rules! define_handles { + (< $t:ty > :: $i:ident , $sv:vis $s:ident , $wv:vis $w:ident) => { + impl riddle_common::CloneHandle for $t { + type Handle = $s; + type WeakHandle = $w; + + #[inline] + fn clone_handle(&self) -> Option<$s> { + <$w>::upgrade(&self.$i) + } + + #[inline] + fn clone_weak_handle(&self) -> $w { + self.$i.clone() + } + } + + #[derive(Clone)] + $sv struct $s { + handle: std::sync::Arc<$t>, + } + + impl $s { + #[inline] + pub fn downgrade(this: &$s) -> $w { + $w { + handle: std::sync::Arc::downgrade(&this.handle) + } + } + + #[inline] + pub fn new $t>(f: F) -> $s { + $s { + handle: std::sync::Arc::new_cyclic(|weak_sync| { + let weak_self = $w { handle: weak_sync.clone() }; + f(weak_self) + }) + } + } + + #[inline] + pub fn eq(a: &$s, b: &$s) -> bool { + std::sync::Arc::ptr_eq(&a.handle, &b.handle) + } + } + + impl std::ops::Deref for $s { + type Target = $t; + + #[inline] + fn deref(&self) -> &$t { + std::ops::Deref::deref(&self.handle) + } + } + + #[derive(Clone)] + $wv struct $w { + handle: std::sync::Weak<$t>, + } - fn clone_weak_handle(&self) -> Weak; + impl $w { + #[inline] + pub fn upgrade(this: &$w) -> Option<$s> { + std::sync::Weak::upgrade(&this.handle).map(|s| $s { handle: s.clone() }) + } + } + }; } diff --git a/crates/riddle-common/src/eventpub.rs b/crates/riddle-common/src/eventpub.rs index 3318248..8b3db58 100644 --- a/crates/riddle-common/src/eventpub.rs +++ b/crates/riddle-common/src/eventpub.rs @@ -1,35 +1,34 @@ -use std::{ - cell::RefCell, - rc::{Rc, Weak}, -}; +use std::sync::{Arc, Mutex, RwLock, Weak}; struct EventQueue { - queue: RefCell>, - filter: Box bool>, + queue: Mutex>, + filter: Box bool + Sync + Send>, } pub struct EventPub { - subs: RefCell>>>, + subs: RwLock>>>, } pub struct EventSub { - events: Rc>, + events: Arc>, } impl EventPub { pub fn new() -> Self { Self { - subs: RefCell::new(vec![]), + subs: RwLock::new(vec![]), } } pub fn attach(&self, sub: &EventSub) { - self.subs.borrow_mut().push(Rc::downgrade(&sub.events)); + let mut subs = self.subs.write().unwrap(); + subs.push(Arc::downgrade(&sub.events)); } pub fn dispatch(&self, event: &T) { let mut dirty = false; - for sub in self.subs.borrow().iter() { + + for sub in self.subs.read().unwrap().iter() { match Weak::upgrade(sub) { Some(strong_sub) => strong_sub.deliver(event.clone()), None => dirty = true, @@ -42,9 +41,8 @@ impl EventPub { } fn clean(&self) { - self.subs - .borrow_mut() - .retain(|w| Weak::upgrade(w).is_some()) + let mut subs = self.subs.write().unwrap(); + subs.retain(|w| Weak::upgrade(w).is_some()) } } @@ -57,7 +55,7 @@ impl EventSub { pub fn new_with_filter(filter: F) -> Self where - F: Fn(&T) -> bool + 'static, + F: Fn(&T) -> bool + Send + Sync + 'static, { Self { events: EventQueue::new_with_filter(filter).into(), @@ -76,21 +74,25 @@ impl EventQueue { fn new_with_filter(filter: F) -> Self where - F: Fn(&T) -> bool + 'static, + F: Fn(&T) -> bool + Send + Sync + 'static, { Self { - queue: RefCell::new(vec![]), + queue: Mutex::new(vec![]), filter: Box::new(filter), } } fn deliver(&self, event: T) { if (*self.filter)(&event) { - self.queue.borrow_mut().push(event); + let mut queue = self.queue.lock().unwrap(); + queue.push(event); } } fn collect(&self) -> Vec { - self.queue.replace(vec![]) + let mut queue = self.queue.lock().unwrap(); + let mut res = vec![]; + res.append(&mut queue); + res } } diff --git a/crates/riddle-common/src/lib.rs b/crates/riddle-common/src/lib.rs index 3fa0fa8..c018e6d 100644 --- a/crates/riddle-common/src/lib.rs +++ b/crates/riddle-common/src/lib.rs @@ -1,10 +1,11 @@ use thiserror::Error; +mod clone_handle; mod color; -pub mod clone_handle; pub mod eventpub; +pub use clone_handle::*; pub use color::*; #[derive(Debug, Error)] diff --git a/crates/riddle-input/src/gamepad_state.rs b/crates/riddle-input/src/gamepad_state.rs new file mode 100644 index 0000000..6560a7e --- /dev/null +++ b/crates/riddle-input/src/gamepad_state.rs @@ -0,0 +1,62 @@ +use crate::*; + +use std::collections::{HashMap, HashSet}; + +pub(crate) struct GamePadState { + buttons: HashSet, +} + +impl GamePadState { + pub fn new() -> Self { + Self { + buttons: HashSet::new(), + } + } + + fn is_button_down(&self, button: GamePadButton) -> bool { + self.buttons.contains(&button) + } + + fn button_down(&mut self, button: GamePadButton) { + self.buttons.insert(button); + } + + fn button_up(&mut self, button: GamePadButton) { + self.buttons.remove(&button); + } +} + +pub(crate) struct GamePadStateMap { + gamepads: HashMap, +} + +impl GamePadStateMap { + pub fn new() -> Self { + Self { + gamepads: HashMap::new(), + } + } + + pub fn is_button_down(&self, pad: GamePadId, button: GamePadButton) -> bool { + if let Some(pad_state) = self.gamepads.get(&pad) { + pad_state.is_button_down(button) + } else { + false + } + } + + pub fn button_down(&mut self, pad: GamePadId, button: GamePadButton) { + self.get_pad_mut(pad).button_down(button); + } + + pub fn button_up(&mut self, pad: GamePadId, button: GamePadButton) { + self.get_pad_mut(pad).button_up(button); + } + + fn get_pad_mut(&mut self, pad: GamePadId) -> &mut GamePadState { + if !self.gamepads.contains_key(&pad) { + self.gamepads.insert(pad.clone(), GamePadState::new()); + } + self.gamepads.get_mut(&pad).unwrap() + } +} diff --git a/crates/riddle-input/src/input_system.rs b/crates/riddle-input/src/input_system.rs index b65c414..17ba0ee 100644 --- a/crates/riddle-input/src/input_system.rs +++ b/crates/riddle-input/src/input_system.rs @@ -1,12 +1,12 @@ use crate::*; -use riddle_common::eventpub::{EventPub, EventSub}; +use riddle_common::{ + define_handles, + eventpub::{EventPub, EventSub}, +}; use riddle_platform_common::{LogicalPosition, PlatformEvent, WindowId}; -use std::{ - cell::{Ref, RefCell, RefMut}, - collections::HashMap, -}; +use std::{collections::HashMap, sync::Mutex}; struct WindowInputState { mouse: MouseState, @@ -14,89 +14,90 @@ struct WindowInputState { } pub struct InputSystem { - window_states: RefCell>, - event_sub: EventSub, + weak_self: InputSystemWeak, - gilrs: RefCell, + window_states: Mutex>, + gamepad_states: Mutex, - outgoing_input_events: RefCell>, + event_sub: EventSub, + + outgoing_input_events: Mutex>, } +define_handles!(::weak_self, pub InputSystemHandle, pub InputSystemWeak); + impl InputSystem { - pub fn new(sys_events: &EventPub) -> Result { + pub fn new( + sys_events: &EventPub, + ) -> Result<(InputSystemHandle, InputMainThreadState), InputError> { let event_sub = EventSub::new_with_filter(Self::event_filter); sys_events.attach(&event_sub); let gilrs = gilrs::Gilrs::new().map_err(|_| InputError::Unknown)?; - Ok(InputSystem { - window_states: RefCell::new(HashMap::new()), + let system = InputSystemHandle::new(|weak_self| InputSystem { + weak_self, + window_states: Mutex::new(HashMap::new()), + gamepad_states: Mutex::new(GamePadStateMap::new()), event_sub, - gilrs: RefCell::new(gilrs), - outgoing_input_events: RefCell::new(vec![]), - }) + outgoing_input_events: Mutex::new(vec![]), + }); + + let main_thread = InputMainThreadState { + system: system.clone(), + gilrs, + }; + + Ok((system, main_thread)) } pub fn update(&self) { self.process_platform_events(); - self.process_gilrs_events(); } pub fn take_input_events(&self) -> Vec { - std::mem::replace(&mut self.outgoing_input_events.borrow_mut(), vec![]) + std::mem::replace(&mut self.outgoing_input_events.lock().unwrap(), vec![]) } pub fn mouse_pos(&self, window: WindowId) -> LogicalPosition { - let state = self.get_mouse_state(window); - state.position() + self.with_window_state(window, |w| w.mouse.position()) } pub fn keyboard_modifiers(&self, window: WindowId) -> KeyboardModifiers { - let state = self.get_keyboard_state(window); - state.modifiers() + self.with_window_state(window, |w| w.keyboard.modifiers()) } pub fn is_gamepad_button_down(&self, gamepad: GamePadId, button: GamePadButton) -> bool { - self.gilrs - .borrow() - .gamepad(gamepad.into()) - .is_pressed(button.into()) + self.gamepad_states + .lock() + .unwrap() + .is_button_down(gamepad, button) } - fn get_window_state<'a>(&'a self, window: WindowId) -> Ref<'a, WindowInputState> { - let mut ms = self.window_states.borrow_mut(); + fn with_window_state<'a, R, F>(&'a self, window: WindowId, f: F) -> R + where + F: FnOnce(&WindowInputState) -> R, + { + let mut ms = self.window_states.lock().unwrap(); if !ms.contains_key(&window) { ms.insert(window, Default::default()); } - let ms = self.window_states.borrow(); - Ref::map(ms, |ms| ms.get(&window).unwrap()) + f(ms.get(&window).unwrap()) } - fn get_window_state_mut<'a>(&'a self, window: WindowId) -> RefMut<'a, WindowInputState> { - let mut ms = self.window_states.borrow_mut(); + fn with_window_state_mut<'a, R, F>(&'a self, window: WindowId, f: F) -> R + where + F: FnOnce(&mut WindowInputState) -> R, + { + let mut ms = self.window_states.lock().unwrap(); if !ms.contains_key(&window) { ms.insert(window, Default::default()); } - RefMut::map(ms, |ms| ms.get_mut(&window).unwrap()) - } - - fn get_mouse_state<'a>(&'a self, window: WindowId) -> RefMut<'a, MouseState> { - RefMut::map(self.get_window_state_mut(window), |state| &mut state.mouse) - } - - fn get_keyboard_state_mut<'a>(&'a self, window: WindowId) -> RefMut<'a, KeyboardState> { - RefMut::map(self.get_window_state_mut(window), |state| { - &mut state.keyboard - }) - } - - fn get_keyboard_state<'a>(&'a self, window: WindowId) -> Ref<'a, KeyboardState> { - Ref::map(self.get_window_state(window), |state| &state.keyboard) + f(ms.get_mut(&window).unwrap()) } fn cursor_moved(&self, window: WindowId, logical_position: LogicalPosition) { - let mut state = self.get_mouse_state(window); - state.set_position(logical_position); + self.with_window_state_mut(window, |w| w.mouse.set_position(logical_position)); self.send_input_event(InputEvent::CursorMove { window, position: logical_position, @@ -104,36 +105,38 @@ impl InputSystem { } fn mouse_down(&self, window: WindowId, button: MouseButton) { - let mut state = self.get_mouse_state(window); - state.button_down(button); + self.with_window_state_mut(window, |w| w.mouse.button_down(button)); self.send_input_event(InputEvent::MouseButtonDown { window, button }); } fn mouse_up(&self, window: WindowId, button: MouseButton) { - let mut state = self.get_mouse_state(window); - state.button_up(button); + self.with_window_state_mut(window, |w| w.mouse.button_up(button)); self.send_input_event(InputEvent::MouseButtonUp { window, button }); } fn key_down(&self, window: WindowId, scancode: Scancode, vkey: Option) { - let mut state = self.get_keyboard_state_mut(window); - state.key_down(scancode, vkey); + let modifiers = self.with_window_state_mut(window, |w| { + w.keyboard.key_down(scancode, vkey); + w.keyboard.modifiers() + }); self.send_input_event(InputEvent::KeyDown { window, scancode, vkey, - modifiers: state.modifiers(), + modifiers, }) } fn key_up(&self, window: WindowId, scancode: Scancode, vkey: Option) { - let mut state = self.get_keyboard_state_mut(window); - state.key_up(scancode, vkey); + let modifiers = self.with_window_state_mut(window, |w| { + w.keyboard.key_up(scancode, vkey); + w.keyboard.modifiers() + }); self.send_input_event(InputEvent::KeyUp { window, scancode, vkey, - modifiers: state.modifiers(), + modifiers, }) } @@ -181,13 +184,38 @@ impl InputSystem { } } - fn process_gilrs_events(&self) { - while let Some(gilrs::Event { event, id, .. }) = self.gilrs.borrow_mut().next_event() { + fn send_input_event(&self, event: InputEvent) { + self.outgoing_input_events.lock().unwrap().push(event); + } +} + +impl Default for WindowInputState { + fn default() -> Self { + Self { + mouse: Default::default(), + keyboard: Default::default(), + } + } +} + +pub struct InputMainThreadState { + system: InputSystemHandle, + gilrs: gilrs::Gilrs, +} + +impl InputMainThreadState { + pub fn update(&mut self) { + while let Some(gilrs::Event { event, id, .. }) = self.gilrs.next_event() { use std::convert::TryFrom; match event { gilrs::EventType::ButtonPressed(button, _) => { if let Ok(button) = GamePadButton::try_from(button) { - self.send_input_event(InputEvent::GamePadButtonDown { + self.system + .gamepad_states + .lock() + .unwrap() + .button_down(id.into(), button.clone()); + self.system.send_input_event(InputEvent::GamePadButtonDown { gamepad: id.into(), button, }); @@ -196,7 +224,12 @@ impl InputSystem { gilrs::EventType::ButtonRepeated(_, _) => {} gilrs::EventType::ButtonReleased(button, _) => { if let Ok(button) = GamePadButton::try_from(button) { - self.send_input_event(InputEvent::GamePadButtonUp { + self.system + .gamepad_states + .lock() + .unwrap() + .button_up(id.into(), button.clone()); + self.system.send_input_event(InputEvent::GamePadButtonUp { gamepad: id.into(), button, }); @@ -205,34 +238,26 @@ impl InputSystem { gilrs::EventType::ButtonChanged(_, _, _) => {} gilrs::EventType::AxisChanged(axis, value, _) => { if let Ok(axis) = GamePadAxis::try_from(axis) { - self.send_input_event(InputEvent::GamePadAxisChanged { - gamepad: id.into(), - axis, - value, - }); + self.system + .send_input_event(InputEvent::GamePadAxisChanged { + gamepad: id.into(), + axis, + value, + }); } } gilrs::EventType::Connected => { - self.send_input_event(InputEvent::GamePadConnected(id.into())); + self.system + .send_input_event(InputEvent::GamePadConnected(id.into())); } gilrs::EventType::Disconnected => { - self.send_input_event(InputEvent::GamePadDisconnected(id.into())); + self.system + .send_input_event(InputEvent::GamePadDisconnected(id.into())); } _ => {} } } - } - - fn send_input_event(&self, event: InputEvent) { - self.outgoing_input_events.borrow_mut().push(event); - } -} -impl Default for WindowInputState { - fn default() -> Self { - Self { - mouse: Default::default(), - keyboard: Default::default(), - } + self.system.update(); } } diff --git a/crates/riddle-input/src/lib.rs b/crates/riddle-input/src/lib.rs index a0ddffe..927aef1 100644 --- a/crates/riddle-input/src/lib.rs +++ b/crates/riddle-input/src/lib.rs @@ -1,10 +1,15 @@ +#![feature(arc_new_cyclic)] + mod error; mod event; mod gamepad; +mod gamepad_state; mod input_system; mod keyboard_state; mod mouse_state; +use gamepad_state::*; + pub use error::*; pub use event::*; pub use gamepad::*; diff --git a/crates/riddle-macros/Cargo.toml b/crates/riddle-macros/Cargo.toml index c3fb2c7..7d970bc 100644 --- a/crates/riddle-macros/Cargo.toml +++ b/crates/riddle-macros/Cargo.toml @@ -5,8 +5,11 @@ authors = ["vickles "] edition = "2018" [lib] -proc-macros = true +proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = "1.0" \ No newline at end of file diff --git a/crates/riddle-platform-common/src/dimensions.rs b/crates/riddle-platform-common/src/dimensions.rs index 293c6b0..6352e70 100644 --- a/crates/riddle-platform-common/src/dimensions.rs +++ b/crates/riddle-platform-common/src/dimensions.rs @@ -77,6 +77,12 @@ impl From> for LogicalPosition { } } +impl Default for LogicalPosition { + fn default() -> Self { + Self { x: 0, y: 0 } + } +} + impl From for Vector2 { fn from(pos: LogicalPosition) -> Self { Self { diff --git a/crates/riddle-platform-winit/Cargo.toml b/crates/riddle-platform-winit/Cargo.toml index 456873f..1ee3a08 100644 --- a/crates/riddle-platform-winit/Cargo.toml +++ b/crates/riddle-platform-winit/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] riddle-common = {path = "../riddle-common"} +riddle-macros = {path = "../riddle-macros"} riddle-platform-common = {path = "../riddle-platform-common"} raw-window-handle = "0.3" diff --git a/crates/riddle-platform-winit/src/event.rs b/crates/riddle-platform-winit/src/event.rs index 16c6071..17fcf0e 100644 --- a/crates/riddle-platform-winit/src/event.rs +++ b/crates/riddle-platform-winit/src/event.rs @@ -1,7 +1,5 @@ use crate::*; -use std::rc::Rc; - #[derive(Debug)] pub(crate) enum InternalEvent { QuitRequested, @@ -13,8 +11,8 @@ pub(crate) fn convert_winit_event( ) -> Option { match event { winit::event::Event::WindowEvent { window_id, event } => { - let window = system.borrow_window_map().lookup_winit_window(window_id); - window.and_then(|window| convert_winit_window_event(window, event)) + let window = system.with_window_map(|wmap| wmap.lookup_winit_window(window_id)); + window.and_then(|window| convert_winit_window_event(&window, event)) } winit::event::Event::MainEventsCleared => PlatformEvent::EventQueueEmpty.into(), _ => None, @@ -22,7 +20,7 @@ pub(crate) fn convert_winit_event( } fn convert_winit_window_event( - window: Rc, + window: &Window, event: winit::event::WindowEvent, ) -> Option { match event { @@ -34,7 +32,7 @@ fn convert_winit_window_event( } winit::event::WindowEvent::CursorMoved { position, .. } => { Some(PlatformEvent::CursorMove { - window: window.clone().window_id(), + window: window.window_id(), position: dimensions::logical_pos_from_winit( position.to_logical(window.scale_factor()), ), diff --git a/crates/riddle-platform-winit/src/lib.rs b/crates/riddle-platform-winit/src/lib.rs index f4870db..9c9526e 100644 --- a/crates/riddle-platform-winit/src/lib.rs +++ b/crates/riddle-platform-winit/src/lib.rs @@ -3,13 +3,16 @@ mod dimensions; mod error; mod event; +mod platform_context; mod platform_system; mod window; mod window_map; pub use error::WindowError; +pub use platform_context::*; pub use platform_system::*; pub use riddle_platform_common::*; -pub use window::{Window, WindowBuilder}; +pub use window::*; +use riddle_common::*; use window_map::*; diff --git a/crates/riddle-platform-winit/src/platform_context.rs b/crates/riddle-platform-winit/src/platform_context.rs new file mode 100644 index 0000000..a40f108 --- /dev/null +++ b/crates/riddle-platform-winit/src/platform_context.rs @@ -0,0 +1,48 @@ +use crate::{event::InternalEvent, *}; + +use std::ops::Deref; + +pub struct PlatformContext<'a> { + pub(crate) main_thread_state: &'a PlatformMainThreadState, + pub(crate) event_loop: Option<&'a winit::event_loop::EventLoopWindowTarget>, + pub(crate) triggering_event: PlatformEvent, +} + +impl<'a> PlatformContext<'a> { + pub(crate) fn with_event_loop(&self, f: F) -> Result + where + F: FnOnce( + &winit::event_loop::EventLoopWindowTarget, + ) -> Result, + { + match self.event_loop { + Some(el) => f(el), + None => { + let el_ref = self.main_thread_state.event_loop.borrow(); + let el = el_ref.deref(); + match el { + Some(el) => f(el), + None => Err(WindowError::Unknown), + } + } + } + } + + pub fn quit(&self) -> Result<(), WindowError> { + self.main_thread_state + .system + .event_proxy + .lock() + .unwrap() + .send_event(InternalEvent::QuitRequested) + .map_err(|_| WindowError::Unknown) + } + + pub fn event(&self) -> &PlatformEvent { + &self.triggering_event + } + + pub fn system(&self) -> &PlatformSystem { + &self.main_thread_state.system + } +} diff --git a/crates/riddle-platform-winit/src/platform_system.rs b/crates/riddle-platform-winit/src/platform_system.rs index a787426..3f77a82 100644 --- a/crates/riddle-platform-winit/src/platform_system.rs +++ b/crates/riddle-platform-winit/src/platform_system.rs @@ -1,107 +1,59 @@ use crate::{event::InternalEvent, *}; -use riddle_common::{clone_handle::CloneHandle, eventpub::EventPub}; +use riddle_common::eventpub::EventPub; -use std::{ - cell::{Ref, RefCell, RefMut}, - ops::Deref, - rc::{Rc, Weak}, -}; +use std::{cell::RefCell, sync::Mutex}; pub struct PlatformSystem { - weak_self: Weak, - event_loop: RefCell>>, - event_proxy: winit::event_loop::EventLoopProxy, + weak_self: PlatformSystemWeak, + pub(crate) event_proxy: Mutex>, - window_map: RefCell, + window_map: Mutex, event_pub: EventPub, } -pub struct PlatformContext<'a> { - pub(crate) system: Rc, - event_loop: Option<&'a winit::event_loop::EventLoopWindowTarget>, - triggering_event: PlatformEvent, -} +define_handles!(::weak_self, pub PlatformSystemHandle, pub PlatformSystemWeak); impl PlatformSystem { - pub fn borrow_context(this: &Rc) -> Result { - Ok(PlatformContext { - system: this.clone(), - event_loop: None, - triggering_event: PlatformEvent::Unknown, - }) - } - - /// Starts the main even loop for this window system. - /// - /// # Panics - /// - /// If run has already been invoked, then this function will panic. - pub fn run(this: Rc, main_loop: F) -> ! - where - F: FnMut(PlatformContext) + 'static, - { - let el = std::mem::replace(&mut *this.event_loop.borrow_mut(), None).unwrap(); - let mut main_loop = main_loop; - el.run(move |event, el, cf| { - match &event { - winit::event::Event::UserEvent(InternalEvent::QuitRequested) => { - *cf = winit::event_loop::ControlFlow::Exit - } - _ => *cf = winit::event_loop::ControlFlow::Poll, - } - - match event::convert_winit_event(&this, event) { - Some(system_event) => { - let ctx = PlatformContext { - system: this.clone(), - event_loop: Some(el), - triggering_event: system_event.clone(), - }; - - this.event_pub.dispatch(&system_event); - this.update_windows(); - - main_loop(ctx); - } - None => (), - } - }) - } - - pub fn new() -> Rc { + pub fn new() -> (PlatformSystemHandle, PlatformMainThreadState) { let event_loop = winit::event_loop::EventLoop::with_user_event(); let event_proxy = event_loop.create_proxy(); - Rc::new_cyclic(|weak_self| PlatformSystem { - weak_self: weak_self.clone(), - event_loop: RefCell::new(event_loop.into()), - event_proxy, + let system = PlatformSystemHandle::new(|weak_self| PlatformSystem { + weak_self, + event_proxy: Mutex::new(event_proxy), window_map: WindowMap::new().into(), event_pub: EventPub::new(), - }) + }); + let main_thread_state = PlatformMainThreadState { + system: system.clone(), + event_loop: RefCell::new(Some(event_loop)), + }; + (system, main_thread_state) } pub fn event_pub(&self) -> &EventPub { &self.event_pub } - pub(crate) fn borrow_window_map(&self) -> Ref { - self.window_map.borrow() + #[inline] + pub(crate) fn with_window_map R>(&self, f: F) -> R { + f(&self.window_map.lock().unwrap()) } - pub(crate) fn borrow_window_map_mut(&self) -> RefMut { - self.window_map.borrow_mut() + #[inline] + pub(crate) fn with_window_map_mut R>(&self, f: F) -> R { + f(&mut self.window_map.lock().unwrap()) } - pub fn lookup_window(&self, window_id: WindowId) -> Option> { - self.window_map.borrow().lookup_window(window_id) + pub fn lookup_window(&self, window_id: WindowId) -> Option { + self.window_map.lock().unwrap().lookup_window(window_id) } fn update_windows(&self) { - let windows = self.window_map.borrow().windows(); + let windows = self.window_map.lock().unwrap().windows(); for window in windows { window.update() } @@ -114,40 +66,55 @@ impl riddle_platform_common::traits::WindowSystem for PlatformSystem { } } -impl<'a> PlatformContext<'a> { - pub(crate) fn with_event_loop(&self, f: F) -> Result +pub struct PlatformMainThreadState { + pub(crate) system: PlatformSystemHandle, + pub(crate) event_loop: RefCell>>, +} + +impl PlatformMainThreadState { + /// Starts the main even loop for this window system. + /// + /// # Panics + /// + /// If run has already been invoked, then this function will panic. + pub fn run(self, main_loop: F) -> ! where - F: FnOnce( - &winit::event_loop::EventLoopWindowTarget, - ) -> Result, + F: FnMut(PlatformContext) + 'static, { - match self.event_loop { - Some(el) => f(el), - None => { - let el_ref = self.system.event_loop.borrow(); - let el = el_ref.deref(); - match el { - Some(el) => f(el), - None => Err(WindowError::Unknown), + let el = std::mem::replace(&mut *self.event_loop.borrow_mut(), None).unwrap(); + let mut main_loop = main_loop; + let this = self.system.clone_handle().unwrap(); + el.run(move |event, el, cf| { + match &event { + winit::event::Event::UserEvent(InternalEvent::QuitRequested) => { + *cf = winit::event_loop::ControlFlow::Exit } + _ => *cf = winit::event_loop::ControlFlow::Poll, } - } - } - pub fn quit(&self) -> Result<(), WindowError> { - self.system - .event_proxy - .send_event(InternalEvent::QuitRequested) - .map_err(|_| WindowError::Unknown) - } + match event::convert_winit_event(&this, event) { + Some(system_event) => { + let ctx = PlatformContext { + main_thread_state: &self, + event_loop: Some(el), + triggering_event: system_event.clone(), + }; - pub fn event(&self) -> &PlatformEvent { - &self.triggering_event + this.event_pub.dispatch(&system_event); + this.update_windows(); + + main_loop(ctx); + } + None => (), + } + }) } -} -impl CloneHandle for PlatformSystem { - fn clone_weak_handle(&self) -> Weak { - self.weak_self.clone() + pub fn borrow_context(&self) -> Result { + Ok(PlatformContext { + main_thread_state: &self, + event_loop: None, + triggering_event: PlatformEvent::Unknown, + }) } } diff --git a/crates/riddle-platform-winit/src/window.rs b/crates/riddle-platform-winit/src/window.rs index 9bb333d..6ef04e0 100644 --- a/crates/riddle-platform-winit/src/window.rs +++ b/crates/riddle-platform-winit/src/window.rs @@ -1,17 +1,14 @@ use crate::*; -use riddle_common::{clone_handle::CloneHandle, eventpub::*}; +use riddle_common::eventpub::*; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use riddle_platform_common::traits::WindowExt; -use std::{ - borrow::Borrow, - rc::{Rc, Weak}, -}; +use std::borrow::Borrow; pub struct Window { - weak_self: Weak, - window_system: Rc, + weak_self: WindowWeak, + window_system: PlatformSystemHandle, winit_window: winit::window::Window, event_sub: EventSub, event_pub: EventPub, @@ -19,9 +16,14 @@ pub struct Window { id: WindowId, } +define_handles!(::weak_self, pub WindowHandle, pub WindowWeak); + impl Window { - fn new_shared(ctx: &PlatformContext, args: &WindowBuilder) -> Result, WindowError> { - let system = ctx.system.clone(); + fn new_shared( + ctx: &PlatformContext, + args: &WindowBuilder, + ) -> Result { + let system = ctx.system().clone_handle().unwrap(); #[cfg(target_os = "windows")] let winit_builder = { @@ -51,20 +53,19 @@ impl Window { args.configure_window(&winit_window); let event_sub = EventSub::new_with_filter(Self::event_filter); - system.event_pub().attach(&event_sub); + ctx.system().event_pub().attach(&event_sub); - let window = Rc::new_cyclic(|weak_self| Self { - weak_self: weak_self.clone(), + let window = WindowHandle::new(|weak_self| Self { + weak_self, window_system: system.clone(), winit_window: winit_window, event_sub, event_pub: EventPub::new(), - id: system.borrow_window_map_mut().take_next_window_id(), + id: system.with_window_map_mut(|wmap| wmap.take_next_window_id()), }); - ctx.system - .borrow_window_map_mut() - .register_window(window.clone()); + ctx.system() + .with_window_map_mut(|wmap| wmap.register_window(window.clone())); Ok(window) } @@ -134,8 +135,7 @@ impl std::cmp::Eq for Window {} impl Drop for Window { fn drop(&mut self) { self.window_system - .borrow_window_map_mut() - .unregister_window(&self); + .with_window_map_mut(|wmap| wmap.unregister_window(&self)); } } @@ -145,12 +145,6 @@ unsafe impl HasRawWindowHandle for Window { } } -impl CloneHandle for Window { - fn clone_weak_handle(&self) -> Weak { - self.weak_self.clone() - } -} - pub struct WindowBuilder { width: u32, height: u32, @@ -197,7 +191,7 @@ impl WindowBuilder { self } - pub fn build<'a, C>(&self, ctx: C) -> Result, WindowError> + pub fn build<'a, C>(&self, ctx: C) -> Result where C: Borrow>, { diff --git a/crates/riddle-platform-winit/src/window_map.rs b/crates/riddle-platform-winit/src/window_map.rs index 7039f6f..00cfcfe 100644 --- a/crates/riddle-platform-winit/src/window_map.rs +++ b/crates/riddle-platform-winit/src/window_map.rs @@ -1,15 +1,12 @@ use crate::*; -use std::{ - collections::HashMap, - rc::{Rc, Weak}, -}; +use std::collections::HashMap; pub(crate) struct WindowMap { next_window_id: u32, - winit_windows: HashMap>, - windows: HashMap>, + winit_windows: HashMap, + windows: HashMap, } impl WindowMap { @@ -22,11 +19,11 @@ impl WindowMap { } } - pub fn register_window(&mut self, window: Rc) { + pub fn register_window(&mut self, window: WindowHandle) { self.windows - .insert(window.window_id(), Rc::downgrade(&window)); + .insert(window.window_id(), WindowHandle::downgrade(&window)); self.winit_windows - .insert(window.winit_window_id(), Rc::downgrade(&window)); + .insert(window.winit_window_id(), WindowHandle::downgrade(&window)); } pub fn unregister_window(&mut self, window: &Window) { @@ -34,16 +31,16 @@ impl WindowMap { self.winit_windows.remove(&window.winit_window_id()); } - pub fn lookup_window(&self, window_id: WindowId) -> Option> { + pub fn lookup_window(&self, window_id: WindowId) -> Option { self.windows .get(&window_id) - .and_then(|weak| Weak::upgrade(weak)) + .and_then(|weak| WindowWeak::upgrade(weak)) } - pub fn lookup_winit_window(&self, winit_id: winit::window::WindowId) -> Option> { + pub fn lookup_winit_window(&self, winit_id: winit::window::WindowId) -> Option { self.winit_windows .get(&winit_id) - .and_then(|weak| Weak::upgrade(weak)) + .and_then(|weak| WindowWeak::upgrade(weak)) } pub fn take_next_window_id(&mut self) -> WindowId { @@ -53,11 +50,11 @@ impl WindowMap { WindowId::new(id) } - pub fn windows(&self) -> Vec> { + pub fn windows(&self) -> Vec { self.windows .values() .into_iter() - .filter_map(|w| Weak::upgrade(w)) + .filter_map(|w| WindowWeak::upgrade(w)) .collect() } } diff --git a/crates/riddle-renderer-wgpu/Cargo.toml b/crates/riddle-renderer-wgpu/Cargo.toml index 6f165c4..be9e523 100644 --- a/crates/riddle-renderer-wgpu/Cargo.toml +++ b/crates/riddle-renderer-wgpu/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [dependencies] riddle-common = {path = "../riddle-common"} riddle-image = {path = "../riddle-image"} +riddle-macros = {path = "../riddle-macros"} riddle-math = {path = "../riddle-math"} riddle-platform-winit = {path = "../riddle-platform-winit"} diff --git a/crates/riddle-renderer-wgpu/src/frame_renderer.rs b/crates/riddle-renderer-wgpu/src/frame_renderer.rs new file mode 100644 index 0000000..38d4005 --- /dev/null +++ b/crates/riddle-renderer-wgpu/src/frame_renderer.rs @@ -0,0 +1,68 @@ +use math::Rect; + +use crate::*; + +pub struct FrameRenderer { + renderer: RendererHandle, + + pub(crate) stream_renderer: StreamRenderer, +} + +impl FrameRenderer { + pub(crate) fn new( + renderer: &Renderer, + frame: wgpu::SwapChainFrame, + encoder: wgpu::CommandEncoder, + ) -> Self { + FrameRenderer { + renderer: renderer.clone_handle().unwrap(), + stream_renderer: StreamRenderer::new(renderer, encoder, frame), + } + } + + pub fn renderer(&self) -> &Renderer { + &self.renderer + } + + /// Set the clear color and mark the frame buffer for clearing. The actual clear operation + /// will be performed when the next batched render happens, or when `present` is called, + /// whichever comes first. + pub fn clear(&mut self, color: Color) -> Result<(), RendererError> { + self.stream_renderer.clear(color) + } + + pub fn set_transform( + &mut self, + transform: mint::ColumnMatrix4, + ) -> Result<(), RendererError> { + self.stream_renderer.set_transform(transform) + } + + pub fn push_transform( + &mut self, + transform: mint::ColumnMatrix4, + ) -> Result<(), RendererError> { + self.stream_renderer.push_transform(transform) + } + + pub fn pop_transform(&mut self) -> Result<(), RendererError> { + self.stream_renderer.pop_transform() + } + + pub fn fill_rect(&mut self, rect: &Rect, color: [f32; 4]) -> Result<(), RendererError> { + self.stream_renderer.fill_rect(rect, color) + } + + pub fn present(self) -> Result<(), RendererError> { + self.stream_renderer.present() + } + + pub(crate) fn render( + &mut self, + args: &StreamRenderArgs, + verts: &[Vertex], + indices: &[u16], + ) -> Result<(), RendererError> { + self.stream_renderer.stream_render(args, verts, indices) + } +} diff --git a/crates/riddle-renderer-wgpu/src/lib.rs b/crates/riddle-renderer-wgpu/src/lib.rs index 56a262f..37ab599 100644 --- a/crates/riddle-renderer-wgpu/src/lib.rs +++ b/crates/riddle-renderer-wgpu/src/lib.rs @@ -1,22 +1,25 @@ #![feature(arc_new_cyclic)] mod error; +mod frame_renderer; mod renderer; mod shader; mod sprite; mod sprite_atlas; -mod stream_render_buffer; +mod stream_renderer; mod texture; mod vertex; pub use error::*; +pub use frame_renderer::*; pub use renderer::*; pub use sprite::*; pub use sprite_atlas::*; pub use texture::*; +use riddle_common::*; use shader::*; -use stream_render_buffer::*; +use stream_renderer::*; use vertex::*; use riddle_image as image; diff --git a/crates/riddle-renderer-wgpu/src/renderer.rs b/crates/riddle-renderer-wgpu/src/renderer.rs index 2936fe8..40c71f6 100644 --- a/crates/riddle-renderer-wgpu/src/renderer.rs +++ b/crates/riddle-renderer-wgpu/src/renderer.rs @@ -1,40 +1,32 @@ use crate::{math::*, platform::*, *}; -use riddle_common::{clone_handle::CloneHandle, eventpub::EventSub, Color}; - -use std::{cell::RefCell, rc::Rc}; +use riddle_common::{define_handles, eventpub::EventSub}; +use std::sync::Mutex; /// A simple 2D sprite based renderer. /// /// A renderer is created for a Window and holds a reference to the window, which will keep /// the window alive as long as the renderer is alive pub struct Renderer { - weak_self: std::rc::Weak, - pub(super) window: Rc, - pub(super) default_shader: Rc, - pub(super) white_tex: Rc, - pub(super) frame_state: RefCell>, + weak_self: RendererWeak, + + pub(super) window: WindowHandle, + pub(super) default_shader: ShaderHandle, + pub(super) white_tex: TextureHandle, pub(super) device: wgpu::Device, surface: wgpu::Surface, pub(super) queue: wgpu::Queue, - swap_chain: RefCell, - camera_size: RefCell>, + swap_chain: Mutex, + camera_size: Mutex>, window_event_sub: EventSub, - - pub(crate) stream_buffer: RefCell, } -pub(super) struct FrameRenderState { - pub encoder: wgpu::CommandEncoder, - pub frame: wgpu::SwapChainFrame, - pub pending_clear_color: Option<[f32; 4]>, - pub view_matrix_stack: Vec>, -} +define_handles!(::weak_self, pub RendererHandle, pub RendererWeak); impl Renderer { - pub fn new_shared(window: &Window) -> Result, RendererError> { + pub fn new_shared(window: &Window) -> Result { let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let surface = unsafe { instance.create_surface(window) }; @@ -92,183 +84,45 @@ impl Renderer { let window_handle = window.clone_handle().ok_or(RendererError::Unknown)?; - Ok(std::rc::Rc::new_cyclic(|weak_self| Self { - weak_self: weak_self.clone(), + Ok(RendererHandle::new(|weak_self| Self { + weak_self, window: window_handle, surface: surface, device, queue, - swap_chain: RefCell::new(swap_chain), - frame_state: RefCell::new(None), + swap_chain: Mutex::new(swap_chain), default_shader: sprite_shader, white_tex, - camera_size: RefCell::new(Vector2 { + camera_size: Mutex::new(Vector2 { x: camera_size.width as f32, y: camera_size.height as f32, }), window_event_sub, - stream_buffer: RefCell::new(StreamRenderBuffer::new()), })) } - pub fn set_transform(&self, transform: mint::ColumnMatrix4) -> Result<(), RendererError> { - self.stream_buffer.borrow_mut().flush(self)?; - - let FrameRenderState { - view_matrix_stack, .. - } = &mut *(self.get_frame_state()?); - - match view_matrix_stack.last_mut() { - Some(last) => *last = transform, - _ => (), - } - - Ok(()) - } - - pub fn push_transform(&self, transform: mint::ColumnMatrix4) -> Result<(), RendererError> { - self.stream_buffer.borrow_mut().flush(self)?; - - let FrameRenderState { - view_matrix_stack, .. - } = &mut *(self.get_frame_state()?); - - view_matrix_stack.push(transform); - Ok(()) - } - - pub fn pop_transform(&self) -> Result<(), RendererError> { - self.stream_buffer.borrow_mut().flush(self)?; - - let FrameRenderState { - view_matrix_stack, .. - } = &mut *(self.get_frame_state()?); + pub fn begin_render_frame(&self) -> Result { + self.handle_window_events()?; - view_matrix_stack.pop(); + let frame = self + .swap_chain + .lock() + .unwrap() + .get_current_frame() + .expect("Timeout when acquiring next swap chain texture"); + let encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - Ok(()) - } - - pub(super) fn get_frame_state( - &self, - ) -> Result, RendererError> { - if self.frame_state.borrow().is_none() { - self.handle_window_events()?; - - let frame = self - .swap_chain - .borrow_mut() - .get_current_frame() - .expect("Timeout when acquiring next swap chain texture"); - let encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - let identity: mint::ColumnMatrix4 = glam::Mat4::identity().into(); - - let mut fs = self - .frame_state - .try_borrow_mut() - .map_err(|_| RendererError::Unknown)?; - *fs = Some(FrameRenderState { - encoder, - frame, - pending_clear_color: None, - view_matrix_stack: vec![identity], - }); - } - - Ok(std::cell::RefMut::map(self.frame_state.borrow_mut(), |s| { - s.as_mut().unwrap() - })) - } - - /// Set the clear color and mark the frame buffer for clearing. The actual clear operation - /// will be performed when the next batched render happens, or when `present` is called, - /// whichever comes first. - pub fn clear(&self, color: Color) -> Result<(), RendererError> { - self.stream_buffer.borrow_mut().flush(self)?; - - let FrameRenderState { - pending_clear_color, - .. - } = &mut *(self.get_frame_state()?); - - *pending_clear_color = Some(color.into()); - Ok(()) - } - - fn clear_immediate(&self, color: Color) -> Result<(), RendererError> { - self.stream_buffer.borrow_mut().flush(self)?; - - let FrameRenderState { encoder, frame, .. } = &mut *(self.get_frame_state()?); - - let _rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { - attachment: &frame.output.view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: color.r as f64, - g: color.g as f64, - b: color.b as f64, - a: color.a as f64, - }), - store: true, - }, - }], - depth_stencil_attachment: None, - }); - Ok(()) - } - - pub fn fill_rect(&self, rect: &Rect, color: [f32; 4]) -> Result<(), RendererError> { - let pos_topleft = glam::Vec2::from(rect.location); - let pos_topright = pos_topleft + glam::vec2(rect.dimensions.x, 0.0); - let pos_bottomleft = pos_topleft + glam::vec2(0.0, rect.dimensions.y); - let pos_bottomright = pos_bottomleft + glam::vec2(rect.dimensions.x, 0.0); - - let vertex_data = [ - Vertex::ptc(pos_topleft, [0.0, 0.0], &color), - Vertex::ptc(pos_bottomleft, [0.0, 0.0], &color), - Vertex::ptc(pos_bottomright, [0.0, 0.0], &color), - Vertex::ptc(pos_topright, [0.0, 0.0], &color), - ]; - let index_data: &[u16] = &[1, 2, 0, 2, 0, 3]; - - self.stream_buffer.borrow_mut().stream_render( - self, - &StreamRenderArgs { - texture: self.white_tex.clone(), - shader: self.default_shader.clone(), - }, - &vertex_data[..], - index_data, - ) - } - - pub fn present(&self) -> Result<(), RendererError> { - self.stream_buffer.borrow_mut().flush(self)?; - - let pending_clear_color = self.get_frame_state()?.pending_clear_color; - - if pending_clear_color.is_some() { - self.clear_immediate(pending_clear_color.unwrap().into())?; - } - - let fs = self - .frame_state - .replace(None) - .ok_or(RendererError::Unknown)?; - self.queue.submit(Some(fs.encoder.finish())); - Ok(()) + Ok(FrameRenderer::new(self, frame, encoder)) } pub fn camera_size(&self) -> Vector2 { - self.camera_size.borrow().clone() + self.camera_size.lock().unwrap().clone() } - pub fn window(&self) -> Rc { - self.window.clone() + pub fn window(&self) -> &Window { + &self.window } pub fn handle_window_events(&self) -> Result<(), RendererError> { @@ -291,9 +145,9 @@ impl Renderer { }; let swap_chain = self.device.create_swap_chain(&self.surface, &sc_desc); - *self.swap_chain.borrow_mut() = swap_chain; + *self.swap_chain.lock().unwrap() = swap_chain; let camera_size = self.window.logical_size(); - *self.camera_size.borrow_mut() = Vector2 { + *self.camera_size.lock().unwrap() = Vector2 { x: camera_size.width as f32, y: camera_size.height as f32, }; @@ -302,10 +156,3 @@ impl Renderer { Ok(()) } } - -impl CloneHandle for Renderer { - #[inline] - fn clone_weak_handle(&self) -> std::rc::Weak { - self.weak_self.clone() - } -} diff --git a/crates/riddle-renderer-wgpu/src/shader.rs b/crates/riddle-renderer-wgpu/src/shader.rs index 201e7b6..2f22111 100644 --- a/crates/riddle-renderer-wgpu/src/shader.rs +++ b/crates/riddle-renderer-wgpu/src/shader.rs @@ -1,21 +1,23 @@ use crate::{math::*, *}; -use std::rc::Rc; use wgpu::util::DeviceExt; use wgpu::{CommandEncoder, RenderPass, TextureView}; pub(super) struct Shader { + weak_self: ShaderWeak, pub bind_group_layout: wgpu::BindGroupLayout, pipeline: wgpu::RenderPipeline, } +define_handles!(::weak_self, pub(super) ShaderHandle, pub(super) ShaderWeak); + impl Shader { pub(crate) fn from_readers( device: &wgpu::Device, mut vs: VR, mut fs: FR, primitive_type: wgpu::PrimitiveTopology, - ) -> Result, RendererError> + ) -> Result<::Handle, RendererError> where VR: std::io::Read + std::io::Seek, FR: std::io::Read + std::io::Seek, @@ -133,11 +135,11 @@ impl Shader { alpha_to_coverage_enabled: false, }); - let shader = Shader { + Ok(ShaderHandle::new(|weak_self| Shader { + weak_self, bind_group_layout, pipeline: render_pipeline, - }; - Ok(Rc::new(shader)) + })) } pub(crate) fn bind_params( diff --git a/crates/riddle-renderer-wgpu/src/sprite.rs b/crates/riddle-renderer-wgpu/src/sprite.rs index 1331f71..acf1a7f 100644 --- a/crates/riddle-renderer-wgpu/src/sprite.rs +++ b/crates/riddle-renderer-wgpu/src/sprite.rs @@ -1,16 +1,14 @@ use crate::{math::*, *}; -use riddle_common::clone_handle::CloneHandle; use riddle_common::Color; -use std::rc::Rc; /// A sprite represents an image texture, along with an axis aligned rect to select which /// part of the texture the sprite represents. /// /// Sprites store a reference to the Renderer that constructed it. pub struct Sprite { - renderer: Rc, - texture: Rc, + renderer: RendererHandle, + texture: TextureHandle, source_rect: Rect, } @@ -30,12 +28,12 @@ impl Sprite { mag_filter, min_filter, )?; - Self::from_texture(renderer, texture.into()) + Self::from_texture(renderer, &texture) } pub(super) fn from_texture( renderer: &Renderer, - texture: Rc, + texture: &Texture, ) -> Result { let dimensions = texture.dimensions.convert(); Self::from_texture_with_bounds( @@ -50,12 +48,12 @@ impl Sprite { pub(super) fn from_texture_with_bounds( renderer: &Renderer, - texture: Rc, + texture: &Texture, source_rect: Rect, ) -> Result { Ok(Sprite { renderer: renderer.clone_handle().ok_or(RendererError::Unknown)?, - texture: texture, + texture: texture.clone_handle().unwrap(), source_rect, }) } @@ -74,7 +72,11 @@ impl Sprite { } } - pub fn render(&self, args: &SpriteRenderCommand) -> Result<(), RendererError> { + pub fn render( + &self, + frame: &mut FrameRenderer, + args: &SpriteRenderCommand, + ) -> Result<(), RendererError> { let rot: glam::Mat2 = glam::Mat2::from_angle((args.angle / 180.0) * std::f32::consts::PI); let Vector2 { x: tex_width, @@ -121,8 +123,7 @@ impl Sprite { let index_data: &[u16] = &[1, 2, 0, 2, 0, 3]; - self.renderer.stream_buffer.borrow_mut().stream_render( - &self.renderer, + frame.render( &StreamRenderArgs { texture: self.texture.clone(), shader: self.renderer.default_shader.clone(), @@ -132,11 +133,18 @@ impl Sprite { ) } - pub fn render_at>>(&self, location: P) -> Result<(), RendererError> { - self.render(&SpriteRenderCommand { - location: location.into(), - ..Default::default() - }) + pub fn render_at>>( + &self, + frame: &mut FrameRenderer, + location: P, + ) -> Result<(), RendererError> { + self.render( + frame, + &SpriteRenderCommand { + location: location.into(), + ..Default::default() + }, + ) } pub fn dimensions(&self) -> Vector2 { @@ -193,8 +201,8 @@ impl SpriteRenderCommand { self } - pub fn render(&self, sprite: &Sprite) -> Result<(), RendererError> { - sprite.render(self) + pub fn render(&self, frame: &mut FrameRenderer, sprite: &Sprite) -> Result<(), RendererError> { + sprite.render(frame, self) } } diff --git a/crates/riddle-renderer-wgpu/src/sprite_atlas.rs b/crates/riddle-renderer-wgpu/src/sprite_atlas.rs index b4584eb..4e247f5 100644 --- a/crates/riddle-renderer-wgpu/src/sprite_atlas.rs +++ b/crates/riddle-renderer-wgpu/src/sprite_atlas.rs @@ -63,7 +63,7 @@ impl<'a> SpriteAtlasBuilder<'a> { for (bounds, sprite) in sprite_bounds { *sprite = Some(Sprite::from_texture_with_bounds( renderer, - texture.clone(), + &texture, bounds.convert(), )?); } diff --git a/crates/riddle-renderer-wgpu/src/stream_render_buffer.rs b/crates/riddle-renderer-wgpu/src/stream_render_buffer.rs deleted file mode 100644 index 9248705..0000000 --- a/crates/riddle-renderer-wgpu/src/stream_render_buffer.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::*; - -use std::rc::Rc; -use wgpu::util::DeviceExt; - -#[derive(Clone)] -pub(crate) struct StreamRenderArgs { - pub texture: Rc, - pub shader: Rc, -} - -impl PartialEq for StreamRenderArgs { - fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.texture, &other.texture) && Rc::ptr_eq(&self.shader, &other.shader) - } -} - -impl Eq for StreamRenderArgs {} - -pub(crate) struct StreamRenderBuffer { - current_args: Option, - - verts: Vec, - indices: Vec, -} - -impl StreamRenderBuffer { - pub fn new() -> Self { - Self { - current_args: None, - verts: vec![], - indices: vec![], - } - } - - pub fn stream_render( - &mut self, - renderer: &Renderer, - args: &StreamRenderArgs, - verts: &[Vertex], - indices: &[u16], - ) -> Result<(), RendererError> { - if Some(args) != self.current_args.as_ref() { - self.flush(renderer)?; - self.current_args = Some(args.clone()); - } - - let indices: Vec = indices - .iter() - .map(|idx| idx + self.verts.len() as u16) - .collect(); - - self.verts.extend_from_slice(verts); - self.indices.extend_from_slice(&indices[..]); - - Ok(()) - } - - pub fn flush(&mut self, renderer: &Renderer) -> Result<(), RendererError> { - match &self.current_args { - Some(args) => { - let args = args.clone(); - self.do_flush(renderer, &args) - } - _ => Ok(()), - } - } - - fn do_flush( - &mut self, - renderer: &Renderer, - args: &StreamRenderArgs, - ) -> Result<(), RendererError> { - let device = &renderer.device; - let FrameRenderState { - encoder, - frame, - pending_clear_color, - view_matrix_stack, - } = &mut *(renderer.get_frame_state()?); - - let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: bytemuck::cast_slice(&self.verts), - usage: wgpu::BufferUsage::VERTEX, - }); - - let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: bytemuck::cast_slice(&self.indices), - usage: wgpu::BufferUsage::INDEX, - }); - - let view_matrix = view_matrix_stack - .last() - .map(|m| m.clone()) - .unwrap_or_else(|| glam::Mat4::identity().into()); - - let bind_group = - args.shader - .bind_params(device, renderer.camera_size(), view_matrix, &args.texture); - - let load_op = match pending_clear_color { - Some(c) => wgpu::LoadOp::Clear(wgpu::Color { - r: c[0] as f64, - g: c[1] as f64, - b: c[2] as f64, - a: c[3] as f64, - }), - None => wgpu::LoadOp::Load, - }; - *pending_clear_color = None; - - // Scope render pass so that the render is encoded before the buffers are cleared. - { - let mut rpass = args - .shader - .begin_render_pass(&frame.output.view, encoder, load_op); - - rpass.set_bind_group(0, &bind_group, &[]); - - rpass.set_vertex_buffer(0, vertex_buf.slice(..)); - rpass.set_index_buffer(index_buf.slice(..)); - rpass.draw_indexed(0..self.indices.len() as u32, 0, 0..1); - } - - self.verts.clear(); - self.indices.clear(); - self.current_args = None; - Ok(()) - } -} diff --git a/crates/riddle-renderer-wgpu/src/stream_renderer.rs b/crates/riddle-renderer-wgpu/src/stream_renderer.rs new file mode 100644 index 0000000..9d131a0 --- /dev/null +++ b/crates/riddle-renderer-wgpu/src/stream_renderer.rs @@ -0,0 +1,236 @@ +use crate::*; + +use math::Rect; +use wgpu::util::DeviceExt; + +#[derive(Clone)] +pub(crate) struct StreamRenderArgs { + pub texture: TextureHandle, + pub shader: ShaderHandle, +} + +impl PartialEq for StreamRenderArgs { + fn eq(&self, other: &Self) -> bool { + TextureHandle::eq(&self.texture, &other.texture) + && ShaderHandle::eq(&self.shader, &other.shader) + } +} + +impl Eq for StreamRenderArgs {} + +pub(crate) struct StreamRenderer { + renderer: RendererHandle, + current_args: Option, + + verts: Vec, + indices: Vec, + + pending_clear_color: Option<[f32; 4]>, + view_matrix_stack: Vec>, + + encoder: wgpu::CommandEncoder, + frame: wgpu::SwapChainFrame, +} + +impl StreamRenderer { + pub fn new( + renderer: &Renderer, + encoder: wgpu::CommandEncoder, + frame: wgpu::SwapChainFrame, + ) -> Self { + let identity: mint::ColumnMatrix4 = glam::Mat4::identity().into(); + Self { + renderer: renderer.clone_handle().unwrap(), + current_args: None, + verts: vec![], + indices: vec![], + pending_clear_color: None, + view_matrix_stack: vec![identity], + encoder, + frame, + } + } + + pub fn set_transform( + &mut self, + transform: mint::ColumnMatrix4, + ) -> Result<(), RendererError> { + self.flush()?; + match self.view_matrix_stack.last_mut() { + Some(last) => *last = transform, + _ => (), + } + Ok(()) + } + + pub fn push_transform( + &mut self, + transform: mint::ColumnMatrix4, + ) -> Result<(), RendererError> { + self.flush()?; + self.view_matrix_stack.push(transform); + Ok(()) + } + + pub fn pop_transform(&mut self) -> Result<(), RendererError> { + self.flush()?; + self.view_matrix_stack.pop(); + Ok(()) + } + + /// Set the clear color and mark the frame buffer for clearing. The actual clear operation + /// will be performed when the next batched render happens, or when `present` is called, + /// whichever comes first. + pub fn clear(&mut self, color: Color) -> Result<(), RendererError> { + self.flush()?; + self.pending_clear_color = Some(color.into()); + Ok(()) + } + + pub fn fill_rect(&mut self, rect: &Rect, color: [f32; 4]) -> Result<(), RendererError> { + let pos_topleft = glam::Vec2::from(rect.location); + let pos_topright = pos_topleft + glam::vec2(rect.dimensions.x, 0.0); + let pos_bottomleft = pos_topleft + glam::vec2(0.0, rect.dimensions.y); + let pos_bottomright = pos_bottomleft + glam::vec2(rect.dimensions.x, 0.0); + + let vertex_data = [ + Vertex::ptc(pos_topleft, [0.0, 0.0], &color), + Vertex::ptc(pos_bottomleft, [0.0, 0.0], &color), + Vertex::ptc(pos_bottomright, [0.0, 0.0], &color), + Vertex::ptc(pos_topright, [0.0, 0.0], &color), + ]; + let index_data: &[u16] = &[1, 2, 0, 2, 0, 3]; + + self.stream_render( + &StreamRenderArgs { + texture: self.renderer.white_tex.clone(), + shader: self.renderer.default_shader.clone(), + }, + &vertex_data[..], + index_data, + ) + } + + fn clear_immediate(&mut self, color: Color) -> Result<(), RendererError> { + self.flush()?; + let _rpass = self.encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &self.frame.output.view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: color.r as f64, + g: color.g as f64, + b: color.b as f64, + a: color.a as f64, + }), + store: true, + }, + }], + depth_stencil_attachment: None, + }); + Ok(()) + } + + pub fn stream_render( + &mut self, + args: &StreamRenderArgs, + verts: &[Vertex], + indices: &[u16], + ) -> Result<(), RendererError> { + if Some(args) != self.current_args.as_ref() { + self.flush()?; + self.current_args = Some(args.clone()); + } + + let indices: Vec = indices + .iter() + .map(|idx| idx + self.verts.len() as u16) + .collect(); + + self.verts.extend_from_slice(verts); + self.indices.extend_from_slice(&indices[..]); + + Ok(()) + } + + pub fn flush(&mut self) -> Result<(), RendererError> { + match &self.current_args { + Some(args) => { + let args = args.clone(); + self.do_flush(&args) + } + _ => Ok(()), + } + } + + pub fn present(mut self) -> Result<(), RendererError> { + self.flush()?; + if let Some(clear_color) = self.pending_clear_color { + self.clear_immediate(clear_color.into())?; + } + + let cmd = self.encoder.finish(); + self.renderer.queue.submit(Some(cmd)); + + Ok(()) + } + + fn do_flush(&mut self, args: &StreamRenderArgs) -> Result<(), RendererError> { + let device = &self.renderer.device; + + let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&self.verts), + usage: wgpu::BufferUsage::VERTEX, + }); + + let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&self.indices), + usage: wgpu::BufferUsage::INDEX, + }); + + let view_matrix = self + .view_matrix_stack + .last() + .map(|m| m.clone()) + .unwrap_or_else(|| glam::Mat4::identity().into()); + + let bind_group = args.shader.bind_params( + device, + self.renderer.camera_size(), + view_matrix, + &args.texture, + ); + + let load_op = match self.pending_clear_color { + Some(c) => wgpu::LoadOp::Clear(wgpu::Color { + r: c[0] as f64, + g: c[1] as f64, + b: c[2] as f64, + a: c[3] as f64, + }), + None => wgpu::LoadOp::Load, + }; + self.pending_clear_color = None; + + // Scope render pass so that the render is encoded before the buffers are cleared. + { + let mut rpass = + args.shader + .begin_render_pass(&self.frame.output.view, &mut self.encoder, load_op); + + rpass.set_bind_group(0, &bind_group, &[]); + + rpass.set_vertex_buffer(0, vertex_buf.slice(..)); + rpass.set_index_buffer(index_buf.slice(..)); + rpass.draw_indexed(0..self.indices.len() as u32, 0, 0..1); + } + + self.verts.clear(); + self.indices.clear(); + self.current_args = None; + Ok(()) + } +} diff --git a/crates/riddle-renderer-wgpu/src/texture.rs b/crates/riddle-renderer-wgpu/src/texture.rs index 93b23dd..d028ab3 100644 --- a/crates/riddle-renderer-wgpu/src/texture.rs +++ b/crates/riddle-renderer-wgpu/src/texture.rs @@ -3,11 +3,14 @@ use crate::*; use riddle_math::Vector2; pub(super) struct Texture { + weak_self: TextureWeak, pub(crate) texture: wgpu::Texture, pub(crate) sampler: wgpu::Sampler, pub dimensions: Vector2, } +define_handles!(::weak_self, pub(crate) TextureHandle, pub(crate) TextureWeak); + impl Texture { pub fn from_image( device: &wgpu::Device, @@ -15,7 +18,7 @@ impl Texture { image: image::Image, mag_filter: FilterMode, min_filter: FilterMode, - ) -> Result { + ) -> Result { let texture_extent = wgpu::Extent3d { width: image.width(), height: image.height(), @@ -57,12 +60,12 @@ impl Texture { ..Default::default() }); - Ok(Texture { + Ok(TextureHandle::new(|weak_self| Texture { + weak_self, texture, sampler, dimensions: image.dimensions(), - } - .into()) + })) } } diff --git a/crates/riddle-time/src/lib.rs b/crates/riddle-time/src/lib.rs index ba4dd8f..c0606a1 100644 --- a/crates/riddle-time/src/lib.rs +++ b/crates/riddle-time/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(arc_new_cyclic)] + mod time_system; mod timer; diff --git a/crates/riddle-time/src/time_system.rs b/crates/riddle-time/src/time_system.rs index 9487b82..a7a3ac3 100644 --- a/crates/riddle-time/src/time_system.rs +++ b/crates/riddle-time/src/time_system.rs @@ -1,36 +1,46 @@ use crate::*; -use std::cell::RefCell; +use riddle_common::define_handles; + +use std::sync::Mutex; pub struct TimeSystem { - frame_time: RefCell, + weak_self: TimeSystemWeak, + frame_time: Mutex, timers: TimerSet, } +define_handles!(::weak_self, pub TimeSystemHandle, pub TimeSystemWeak); + impl TimeSystem { - pub fn new() -> Self { - Self { - frame_time: RefCell::new(FrameTime::new()), + pub fn new() -> TimeSystemHandle { + TimeSystemHandle::new(|weak_self| Self { + weak_self, + frame_time: Mutex::new(FrameTime::new()), timers: TimerSet::new(), - } + }) } pub fn process_frame(&self) { - self.frame_time.borrow_mut().update(); - self.timers.update(self.frame_time.borrow().frame_delta); + let mut locked_time = self.frame_time.lock().unwrap(); + locked_time.update(); + let delta = locked_time.frame_delta; + drop(locked_time); + + self.timers.update(delta); } pub fn fps(&self) -> f32 { - self.frame_time.borrow().fps + self.frame_time.lock().unwrap().fps } pub fn frame_instant(&self) -> std::time::Instant { - self.frame_time.borrow().frame_instant + self.frame_time.lock().unwrap().frame_instant } pub fn register_timer(&self, duration: std::time::Duration, callback: F) -> TimerHandle where - F: FnOnce() + 'static, + F: FnOnce() + Send + 'static, { self.timers.register_timer(duration, Box::new(callback)) } diff --git a/crates/riddle-time/src/timer.rs b/crates/riddle-time/src/timer.rs index 3622ac4..c092364 100644 --- a/crates/riddle-time/src/timer.rs +++ b/crates/riddle-time/src/timer.rs @@ -1,54 +1,54 @@ -use std::{ - cell::RefCell, - rc::{Rc, Weak}, +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, Weak, }; pub(crate) struct TimerSet { - active_timers: RefCell>, + active_timers: Mutex>, } impl TimerSet { pub fn new() -> Self { Self { - active_timers: RefCell::new(vec![]), + active_timers: Mutex::new(vec![]), } } pub fn update(&self, dt: std::time::Duration) { - let mut timers = std::mem::take(&mut *self.active_timers.borrow_mut()); + let mut timers = std::mem::take(&mut *self.active_timers.lock().unwrap()); for timer in timers.iter_mut() { timer.update(dt); } timers.retain(|t| t.pending()); - self.active_timers.borrow_mut().append(&mut timers); + self.active_timers.lock().unwrap().append(&mut timers); } pub fn register_timer( &self, duration: std::time::Duration, - callback: Box, + callback: Box, ) -> TimerHandle { let timer = Timer::new(duration, callback); let handle = TimerHandle { - shared_state: Rc::downgrade(&timer.shared_state), + shared_state: Arc::downgrade(&timer.shared_state), }; - self.active_timers.borrow_mut().push(timer); + self.active_timers.lock().unwrap().push(timer); handle } } struct Timer { time_remaining: std::time::Duration, - callback: Option>, - shared_state: Rc, + callback: Option>, + shared_state: Arc, } impl Timer { - pub fn new(duration: std::time::Duration, callback: Box) -> Self { + pub fn new(duration: std::time::Duration, callback: Box) -> Self { Self { time_remaining: duration, callback: Some(callback), - shared_state: Rc::new(SharedTimerState::default()), + shared_state: Arc::new(SharedTimerState::default()), } } @@ -74,31 +74,31 @@ impl Timer { callback(); } self.time_remaining = std::time::Duration::default(); - *self.shared_state.pending.borrow_mut() = false; + self.shared_state.pending.store(false, Ordering::Relaxed); } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug)] struct SharedTimerState { - pending: RefCell, - cancelled: RefCell, + pending: AtomicBool, + cancelled: AtomicBool, } impl SharedTimerState { fn cancelled(&self) -> bool { - *self.cancelled.borrow() + self.cancelled.load(Ordering::Relaxed) } fn pending(&self) -> bool { - *self.pending.borrow() + self.pending.load(Ordering::Relaxed) } } impl Default for SharedTimerState { fn default() -> Self { Self { - pending: RefCell::new(true), - cancelled: RefCell::new(false), + pending: AtomicBool::new(true), + cancelled: AtomicBool::new(false), } } } @@ -110,7 +110,7 @@ pub struct TimerHandle { impl TimerHandle { pub fn cancel(&self) { if let Some(state) = Weak::upgrade(&self.shared_state) { - *state.cancelled.borrow_mut() = true; + state.cancelled.store(true, Ordering::Relaxed); } } diff --git a/examples/sandbox/main.rs b/examples/sandbox/main.rs index 3e8a37b..c29753f 100644 --- a/examples/sandbox/main.rs +++ b/examples/sandbox/main.rs @@ -8,16 +8,12 @@ use riddle::{ }; use input::{KeyboardModifiers, MouseButton}; -use std::rc::Rc; +use std::sync::{Arc, Mutex}; struct DemoState { - window: Rc, state: RiddleState, - renderer: Rc, - sprite: renderer::Sprite, - subsprite: renderer::Sprite, - label_sprite: renderer::Sprite, + mouse_location: Arc>, clip: audio::Clip, music_player: audio::ClipPlayer, @@ -27,6 +23,7 @@ struct DemoState { impl DemoState { fn new(rdl: &RiddleApp) -> Result { let window = WindowBuilder::new().build(rdl.context())?; + let renderer = renderer::Renderer::new_shared(&window)?; let img = { @@ -65,19 +62,47 @@ impl DemoState { location: [3.0, 3.0].into(), dimensions: [39.0, 39.0].into(), }); + let label_sprite = label_sprite.unwrap(); let music_player = audio::ClipPlayerBuilder::new() .with_mode(audio::PlayMode::Loop) .play(&rdl.state().audio(), music)?; - Ok(Self { - window, - state: rdl.state().clone(), + let mouse_location = Arc::new(Mutex::new(input::LogicalPosition::default())); - renderer, + let renderer_state = RendererState { + renderer: renderer.clone(), sprite, subsprite, - label_sprite: label_sprite.unwrap(), + label_sprite, + mouse_location: mouse_location.clone(), + }; + + { + let window = window.clone(); + std::thread::spawn(move || loop { + println!("Window Size: {:?}", window.logical_size()); + std::thread::sleep(std::time::Duration::from_secs(5)); + }); + } + + { + let rdlstate = rdl.state().clone(); + let clip = clip.clone(); + std::thread::spawn(move || loop { + let _player = audio::ClipPlayerBuilder::new() + .play(&rdlstate.audio(), clip.clone()) + .unwrap(); + std::thread::sleep(std::time::Duration::from_secs(10)); + }); + } + + std::thread::spawn(move || renderer_state.run()); + + Ok(Self { + state: rdl.state().clone(), + + mouse_location: mouse_location, clip, music_player, @@ -85,20 +110,46 @@ impl DemoState { }) } + pub fn on_mouse_down(&mut self) -> Result<(), RiddleError> { + self.blip_player = Some( + audio::ClipPlayerBuilder::new() + .with_mode(audio::PlayMode::OneShot) + .play(&self.state.audio(), self.clip.clone())?, + ); + Ok(()) + } +} + +struct RendererState { + renderer: renderer::RendererHandle, + sprite: renderer::Sprite, + subsprite: renderer::Sprite, + label_sprite: renderer::Sprite, + + mouse_location: Arc>, +} + +impl RendererState { + fn run(&self) { + loop { + self.render_frame().unwrap(); + } + } + pub fn render_frame(&self) -> Result<(), RiddleError> { - self.renderer.clear(Color::rgb(0.0, 1.0, 0.0))?; + let mut frame = self.renderer.begin_render_frame()?; + frame.clear(Color::rgb(0.0, 1.0, 0.0))?; - self.renderer - .push_transform(glam::Mat4::from_scale(glam::vec3(2.0, 2.0, 1.0)).into())?; + frame.push_transform(glam::Mat4::from_scale(glam::vec3(2.0, 2.0, 1.0)).into())?; - self.renderer.fill_rect( + frame.fill_rect( &Rect { location: [100.0, 100.0].into(), dimensions: [50.0, 50.0].into(), }, [1.0, 0.0, 0.0, 1.0], )?; - self.renderer.fill_rect( + frame.fill_rect( &Rect { location: [102.0, 102.0].into(), dimensions: [46.0, 46.0].into(), @@ -106,28 +157,23 @@ impl DemoState { [1.0, 1.0, 1.0, 1.0], )?; - self.subsprite.render_at([60.0, 60.0])?; - self.label_sprite.render(&SpriteRenderCommand { - location: [10.0, 100.0].into(), - diffuse_color: [0.0, 0.0, 1.0, 1.0], - ..Default::default() - })?; + self.subsprite.render_at(&mut frame, [60.0, 60.0])?; + self.label_sprite.render( + &mut frame, + &SpriteRenderCommand { + location: [10.0, 100.0].into(), + diffuse_color: [0.0, 0.0, 1.0, 1.0], + ..Default::default() + }, + )?; - self.renderer.pop_transform()?; + frame.pop_transform()?; - self.sprite - .render_at(self.state.input().mouse_pos(self.window.window_id()))?; + let pos: input::LogicalPosition = self.mouse_location.lock().unwrap().clone(); - self.renderer.present()?; - Ok(()) - } + self.sprite.render_at(&mut frame, pos)?; - pub fn on_mouse_down(&mut self) -> Result<(), RiddleError> { - self.blip_player = Some( - audio::ClipPlayerBuilder::new() - .with_mode(audio::PlayMode::OneShot) - .play(&self.state.audio(), self.clip.clone())?, - ); + frame.present()?; Ok(()) } } @@ -187,8 +233,8 @@ fn main() -> Result<(), RiddleError> { Event::Input(InputEvent::GamePadAxisChanged { axis, value, .. }) => { println!("Gamepad Up {:?} {:?}", axis, value); } - Event::ProcessFrame => { - state.render_frame().unwrap(); + Event::Input(InputEvent::CursorMove { position, .. }) => { + *state.mouse_location.lock().unwrap() = position.clone(); } _ => (), }) diff --git a/src/app.rs b/src/app.rs index e22cf7b..26f13ef 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,58 +2,33 @@ use crate::*; pub struct RiddleApp { pub(crate) state: RiddleState, + main_thread_state: MainThreadState, } impl RiddleApp { pub fn new() -> Result { + let (state, main_thread_state) = RiddleState::new()?; Ok(Self { - state: RiddleState::new()?.into(), + state, + main_thread_state, }) } - pub fn run(self, mut update: F) -> ! + pub fn run(self, update: F) -> ! where F: FnMut(&RiddleContext) -> () + 'static, { - let window_system = self.state.window.clone(); - platform::PlatformSystem::run(window_system, move |platform_ctx| { - match platform_ctx.event() { - platform::PlatformEvent::EventQueueEmpty => { - self.state.time.process_frame(); - - #[cfg(feature = "riddle-audio")] - self.state.audio.process_frame(); - } - _ => (), - }; - - self.state.input.update(); - - let event = match platform_ctx.event() { - platform::PlatformEvent::EventQueueEmpty => Event::ProcessFrame, - _ => Event::Platform(platform_ctx.event().clone()), - }; - - let mut ctx = RiddleContext { - window_ctx: platform_ctx, - state: &self.state, - event, - }; - update(&ctx); - - let input_events = self.state.input.take_input_events(); - for input_event in input_events { - ctx.event = Event::Input(input_event); - update(&ctx); - } - }) + let RiddleApp { + state, + main_thread_state, + } = self; + main_thread_state.run(state, update); } pub fn context(&self) -> RiddleContext { - let state = &self.state; - let platform_ctx = platform::PlatformSystem::borrow_context(&self.state.window).unwrap(); + let platform_ctx = self.main_thread_state.platform.borrow_context().unwrap(); RiddleContext { - state, + state: &self.state, window_ctx: platform_ctx, event: Event::PreRunPlaceholder, } diff --git a/src/context.rs b/src/context.rs index e548eae..5285504 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,6 +1,6 @@ use crate::*; -use std::{borrow::Borrow, rc::Rc}; +use std::borrow::Borrow; /// An riddle execution context. A context is always associated with the event that caused /// the context to be created. @@ -33,8 +33,8 @@ impl<'a> RiddleContext<'a> { } #[cfg(feature = "riddle-audio")] - pub fn audio(&self) -> Rc { - self.state.audio.clone() + pub fn audio(&self) -> &audio::AudioSystem { + &self.state.audio } pub fn input(&self) -> &input::InputSystem { @@ -46,13 +46,6 @@ impl<'a> RiddleContext<'a> { } } -#[cfg(feature = "riddle-audio")] -impl<'a> Borrow> for RiddleContext<'a> { - fn borrow(&self) -> &Rc { - &self.state.audio - } -} - impl<'a> Borrow> for RiddleContext<'a> { fn borrow(&self) -> &platform::PlatformContext<'a> { &self.window_ctx diff --git a/src/state.rs b/src/state.rs index d906371..0fab579 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,51 +1,112 @@ -#[cfg(feature = "riddle-audio")] -use crate::audio::AudioSystem; -use crate::{input::InputSystem, platform::PlatformSystem, time::TimeSystem, *}; +use time::TimeSystemHandle; -use std::rc::Rc; +#[cfg(feature = "riddle-audio")] +use crate::audio::{AudioSystem, AudioSystemHandle}; +use crate::{ + input::{InputMainThreadState, InputSystem, InputSystemHandle}, + platform::{PlatformMainThreadState, PlatformSystem, PlatformSystemHandle}, + time::TimeSystem, + *, +}; #[derive(Clone)] pub struct RiddleState { - pub window: Rc, - pub input: Rc, - pub time: Rc, + pub platform: PlatformSystemHandle, + pub input: InputSystemHandle, + pub time: TimeSystemHandle, #[cfg(feature = "riddle-audio")] - pub audio: Rc, + pub audio: AudioSystemHandle, } impl RiddleState { - pub fn new() -> Result { - let window = PlatformSystem::new(); - let input = InputSystem::new(window.event_pub())?; + pub(crate) fn new() -> Result<(Self, MainThreadState), RiddleError> { + let (platform_system, platform_main_thread) = PlatformSystem::new(); + let (input_system, input_main_thread) = InputSystem::new(platform_system.event_pub())?; let time = TimeSystem::new(); #[cfg(feature = "riddle-audio")] let audio = AudioSystem::new()?; - Ok(RiddleState { - window: window.into(), - input: input.into(), - time: time.into(), + let riddle_state = RiddleState { + platform: platform_system, + input: input_system, + time: time, #[cfg(feature = "riddle-audio")] - audio: audio.into(), - }) + audio: audio, + }; + + let main_thread_state = MainThreadState { + platform: platform_main_thread, + input: input_main_thread, + }; + + Ok((riddle_state, main_thread_state)) + } + + pub fn platform(&self) -> &PlatformSystem { + &self.platform } - pub fn window(&self) -> Rc { - self.window.clone() + pub fn input(&self) -> &InputSystem { + &self.input } - pub fn input(&self) -> Rc { - self.input.clone() + pub fn time(&self) -> &TimeSystem { + &self.time } - pub fn time(&self) -> Rc { - self.time.clone() + pub fn audio(&self) -> &AudioSystem { + &self.audio } +} - pub fn audio(&self) -> Rc { - self.audio.clone() +pub(crate) struct MainThreadState { + pub(crate) platform: PlatformMainThreadState, + input: InputMainThreadState, +} + +impl MainThreadState { + #[inline] + pub fn run(self, state: RiddleState, mut update: F) -> ! + where + F: FnMut(&RiddleContext) -> () + 'static, + { + let MainThreadState { + platform, + mut input, + } = self; + platform.run(move |platform_ctx| { + match platform_ctx.event() { + platform::PlatformEvent::EventQueueEmpty => { + state.time.process_frame(); + + #[cfg(feature = "riddle-audio")] + state.audio.process_frame(); + } + _ => (), + }; + + input.update(); + + let event = match platform_ctx.event() { + platform::PlatformEvent::EventQueueEmpty => Event::ProcessFrame, + _ => Event::Platform(platform_ctx.event().clone()), + }; + + let mut ctx = RiddleContext { + window_ctx: platform_ctx, + state: &state, + event, + }; + update(&ctx); + + let input_events = state.input.take_input_events(); + for input_event in input_events { + ctx.event = Event::Input(input_event); + update(&ctx); + } + }) } }