From e3fdbb142c531354eedec589e56f0fe305297f9b Mon Sep 17 00:00:00 2001 From: Steins7 Date: Mon, 18 Jan 2021 10:16:58 +0100 Subject: [PATCH] Got the Controller working * reworked iput system again + added test functions --- src/controller.rs | 16 +- src/io.rs | 56 +++--- src/{winit_window.rs => io/:} | 89 ++++++++-- src/io/winit_window.rs | 234 ++++++++++++++++++++++++++ src/lib.rs | 7 +- src/renderer/swap_system.rs | 16 +- src/renderer/swap_system/frame.rs | 24 ++- src/subengine/subengine_controller.rs | 10 +- 8 files changed, 363 insertions(+), 89 deletions(-) rename src/{winit_window.rs => io/:} (53%) create mode 100644 src/io/winit_window.rs diff --git a/src/controller.rs b/src/controller.rs index a335776..a86b41f 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -38,7 +38,7 @@ where Controller { pipelines: pipelines_vec, - color: [0.0, 1.0, 0.0, 0.0], + color: [0.0, 1.0, 1.0, 0.0], } } @@ -54,7 +54,7 @@ where for pipeline in &mut self.pipelines { for input in &pipeline.inputs { - match input.borrow().read(1) { + match input.borrow().read(Duration::from_millis(1)) { Ok(key) => input_keys.push(key), Err(_err) => (), } @@ -87,14 +87,14 @@ where } } -#[cfg(test)] +//#[cfg(test)] mod tests { use super::*; use std::iter; use crate::{ - winit_window::WinitWindow, + io::WinitWindow, renderer::Renderer, utils::Rect, subengine::{SubengineController, TestSubengine}, @@ -102,7 +102,7 @@ mod tests { use gfx_backend_vulkan as vk_back; - #[test] + //#[test] fn test_new() { use std::cell::RefCell; @@ -129,7 +129,7 @@ mod tests { let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers); //creating controller - let controller = Controller::new(vec![subengine_pipeline]); + let _controller = Controller::new(vec![subengine_pipeline]); } #[test] @@ -160,7 +160,9 @@ mod tests { //running controller let mut controller = Controller::new(vec![subengine_pipeline]); - controller.run(); + for _i in 0..10 { + controller.run(); + } } } diff --git a/src/io.rs b/src/io.rs index 9dd067a..5423d86 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,10 +1,16 @@ #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; +use std::time::Duration; + use raw_window_handle::HasRawWindowHandle; use crate::utils::Rect; +pub mod winit_window; +pub use self::winit_window::WinitWindow; + + //--Output trait------------------------------------------------------------------------------------ /// A trait for the ability of a type to be used as an output by the engine. /// @@ -36,33 +42,7 @@ where fn window(&self) -> &W; } -//impl<'a, W, O> Output for &'a mut O -//where -// W: HasRawWindowHandle, -// O: Output, -//{ -// fn get_id(&self) -> usize { Output::get_id(*self) } -// fn set_id(&mut self, id: usize) { Output::set_id(*self, id); } -// -// fn size(&mut self) -> &mut Rect { Output::size(*self) } -// -// fn window(&self) -> &W { Output::window(*self) } -//} - -//impl<'a, W, O> Output for Box -//where -// W: HasRawWindowHandle, -// O: Output, -//{ -// fn get_id(&self) -> usize { Output::get_id(self) } -// fn set_id(&mut self, id: usize) { Output::set_id(self, id); } -// -// fn size(&mut self) -> &mut Rect { Output::size(self) } -// -// fn window(&self) -> &W { Output::window(self) } -//} - -//--Input trait------------------------------------------------------------------------------------ +//--Input trait------------------------------------------------------------------------------------- /// A trait for the ability of a type to be used as an input by the engine /// /// The `Input` trait defines functions used by different components of the engine. The allow to @@ -75,23 +55,29 @@ pub trait Input { /// Return the next input available or a ReadError if an error occured of the timeout duration /// was reached. How inputs are handled depends on the implementation. //TODO change timeout - fn read(&self, timeout_ms: u32) -> Result; + fn read(&self, timeout: Duration) -> Result; + + fn write(&self, signal: Signal); } -//impl<'a, I> Input for &'a mut I -//where -// I: Input, -//{ -// fn read(&self, timeout_ms: u32) -> Result { Input::read(*self, timeout_ms) } -//} - //--Key enum---------------------------------------------------------------------------------------- +#[derive(Debug)] pub enum Key { Close, + Closed, + Test, MouseMove { x: f64, y: f64 }, } +//--Signal enum------------------------------------------------------------------------------------- +#[derive(Debug)] +pub enum Signal { + Exit, + Test, +} + //--ReadError enum---------------------------------------------------------------------------------- +#[derive(Debug)] pub enum ReadError { Timeout, } diff --git a/src/winit_window.rs b/src/io/: similarity index 53% rename from src/winit_window.rs rename to src/io/: index ac8b605..d0d72d7 100644 --- a/src/winit_window.rs +++ b/src/io/: @@ -4,17 +4,18 @@ use log::{debug, error, info, trace, warn}; use std::{ thread, sync::mpsc, + time::Duration, }; use winit::{ dpi::PhysicalSize, - event_loop::{EventLoop}, + event_loop::{EventLoop, EventLoopProxy}, window::{WindowBuilder, Window}, }; //TODO fix that use crate::{ - io::{Output, Input, Key, ReadError}, + io::{Output, Input, Key, Signal, ReadError}, utils::Rect, }; @@ -24,9 +25,24 @@ pub struct WinitWindow { size: Rect, id: usize, - handle: thread::JoinHandle<()>, receiver: mpsc::Receiver, window: Window, + event_loop_proxy: EventLoopProxy, +} + +impl Drop for WinitWindow { + fn drop(&mut self) { + use winit::event::Event; + + // kill event_loop + if self.event_loop_proxy.send_event(Signal::Exit).is_err() { + warn!("EventLoop thread is dead before Exit signal"); + } + //while match self.read(Duration::from_millis(1)) { + // Ok(Key::Closed) => false, + // _ => true, + //} {} + } } impl WinitWindow { @@ -42,22 +58,24 @@ impl WinitWindow { let cloned_name = name.clone(); let (tx, rx) = mpsc::channel(); let (tmp_tx, tmp_rx) = mpsc::sync_channel(1); - let handle = thread::spawn(move || { + let builder = thread::Builder::new().name(title); + //the EventLoop hijacks the thread so there is no need to join it later... + thread::spawn(move || { trace!("Creating Window in EventLoop thread"); - //winit doesn't like use creating the EventLoop in another thread either so we have to + //winit doesn't like us creating the EventLoop in another thread either so we have to //drop crossplatform compatibility :/ let event_loop = EventLoop::new_any_thread(); let window = WindowBuilder::new() .with_inner_size(PhysicalSize {width: size.w, height: size.h}) .with_title(cloned_name) .build(&event_loop).unwrap(); + let event_loop_proxy = event_loop.create_proxy(); trace!("Sending Window back to main thread"); - tmp_tx.send(window).unwrap(); + tmp_tx.send((window, event_loop_proxy)).unwrap(); - //TODO clean event type - event_loop.run(move |event: winit::event::Event<'_, ()>, _, control_flow| { + event_loop.run(move |event: winit::event::Event<'_, Signal>, _, control_flow| { use winit::{ event_loop::ControlFlow, event::Event, @@ -66,13 +84,15 @@ impl WinitWindow { *control_flow = ControlFlow::Wait; - //TODO manage errors match event { + Event::LoopDestroyed => { + tx.send(Key::Closed).unwrap(); + debug!("Closed EventLoop"); + }, Event::WindowEvent{window_id: _, event} => match event { event::WindowEvent::CloseRequested => { + debug!("Close requested"); tx.send(Key::Close).unwrap(); - warn!("Stop input thread"); - *control_flow = ControlFlow::Exit; }, event::WindowEvent::CursorMoved{position, ..} => { tx.send(Key::MouseMove{ @@ -81,21 +101,30 @@ impl WinitWindow { }).unwrap(); }, _ => (), - } + }, + Event::UserEvent(signal) => match signal { + Signal::Exit => { + debug!("Stopping input thread..."); + *control_flow = ControlFlow::Exit; + }, + Signal::Test => { + tx.send(Key::Test).unwrap(); + }, + }, _ => (), }}) }); - let window = tmp_rx.recv().unwrap(); + let (window, event_loop_proxy) = tmp_rx.recv().unwrap(); trace!("Received Window in main thread"); Ok(Self { name, size, id, - handle: handle, receiver: rx, window, + event_loop_proxy, }) } } @@ -110,13 +139,39 @@ impl Output for WinitWindow { } impl Input for WinitWindow { - fn read(&self, timeout_ms: u32) -> Result { - use std::time::Duration; + fn read(&self, timeout: Duration) -> Result { - match self.receiver.recv_timeout(Duration::from_millis(timeout_ms.into())) { + match self.receiver.recv_timeout(timeout) { Ok(key) => Ok(key), Err(_) => Err(ReadError::Timeout), } } + + fn write(&self, signal: Signal) { + + self.event_loop_proxy.send_event(signal) + .map_err(|_| "Could not send Signal to EventLoop").unwrap(); + } } +#[cfg(test)] +mod tests { + use super::*; + + use crate::utils::Rect; + + #[test] + fn test_new_drop() { + let _window1 = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + let _window2 = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + panic!("test"); + } + + #[test] + fn test_read_write() { + let window = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + window.write(Signal::Test); + let input = window.read(Duration::from_millis(1)).unwrap(); + assert!(matches!(input, Key::Test)); + } +} diff --git a/src/io/winit_window.rs b/src/io/winit_window.rs new file mode 100644 index 0000000..f45b121 --- /dev/null +++ b/src/io/winit_window.rs @@ -0,0 +1,234 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::{ + thread, + sync::mpsc, + time::Duration, +}; + +use winit::{ + dpi::PhysicalSize, + event_loop::{EventLoop, EventLoopProxy}, + window::{WindowBuilder, Window}, +}; + +//TODO fix that +use crate::{ + io::{Output, Input, Key, Signal, ReadError}, + utils::Rect, +}; + +#[derive(Debug)] +pub struct WinitWindow { + pub name: String, + size: Rect, + id: usize, + + receiver: mpsc::Receiver, + window: Window, + event_loop_proxy: EventLoopProxy, +} +// +//impl Drop for WinitWindow { +// fn drop(&mut self) { +// use winit::event::Event; +// +// // kill event_loop +// if self.event_loop_proxy.send_event(Signal::Exit).is_err() { +// warn!("EventLoop thread is dead before Exit signal"); +// } +// while match self.read(Duration::from_millis(1)) { +// Ok(Key::Closed) => false, +// Err(err) => match err { +// ReadError::Timeout => false, +// _ => true, +// }, +// _ => true, +// } {} +// debug!("Dropped !"); +// } +//} + +impl WinitWindow { + fn drop(&mut self) { + use winit::event::Event; + + // kill event_loop + debug!("Sending kill signal..."); + if self.event_loop_proxy.send_event(Signal::Exit).is_err() { + warn!("EventLoop thread is dead before Exit signal"); + } + debug!("Kill signal sent"); + while match self.read(Duration::from_millis(1)) { + Ok(Key::Closed) => false, + Err(err) => match err { + ReadError::Timeout => false, + _ => true, + }, + _ => true, + } {} + debug!("Dropped !"); + } + + pub fn new(title: &str, size: Rect) -> Result { + use winit::platform::unix::EventLoopExtUnix; + + debug!("Creating window"); + let name = title.to_string(); + let id = 0; + + //Since we can't move the EventLoop from one thread to another, we need to create it in the + //right thread and then move the Window back to the main thread instead + let cloned_name = name.clone(); + let (tx, rx) = mpsc::channel(); + let (tmp_tx, tmp_rx) = mpsc::sync_channel(1); + let builder = thread::Builder::new().name(title.into()); + //the EventLoop hijacks the thread so there is no need to join it later... + builder.spawn(move || { + + trace!("Creating Window in EventLoop thread"); + //winit doesn't like us creating the EventLoop in another thread either so we have to + //drop crossplatform compatibility :/ + let event_loop = EventLoop::new_any_thread(); + let window = WindowBuilder::new() + .with_inner_size(PhysicalSize {width: size.w, height: size.h}) + .with_title(cloned_name) + .build(&event_loop).unwrap(); + let event_loop_proxy = event_loop.create_proxy(); + + trace!("Sending Window back to main thread"); + tmp_tx.send((window, event_loop_proxy)).unwrap(); + + event_loop.run(move |event: winit::event::Event<'_, Signal>, _, control_flow| { + use winit::{ + event_loop::ControlFlow, + event::Event, + event, + }; + + *control_flow = ControlFlow::Wait; + + match event { + Event::LoopDestroyed => { + tx.send(Key::Closed).unwrap(); + debug!("Closed EventLoop"); + return; + }, + Event::WindowEvent{window_id: _, event} => match event { + event::WindowEvent::CloseRequested => { + debug!("Close requested"); + tx.send(Key::Close).unwrap(); + }, + event::WindowEvent::CursorMoved{position, ..} => { + tx.send(Key::MouseMove{ + x: position.x, + y: position.y, + }).unwrap(); + }, + _ => (), + }, + Event::UserEvent(signal) => match signal { + Signal::Exit => { + debug!("Stopping input thread..."); + *control_flow = ControlFlow::Exit; + }, + Signal::Test => { + tx.send(Key::Test).unwrap(); + }, + }, + _ => (), + }}) + }); + + let (window, event_loop_proxy) = tmp_rx.recv().unwrap(); + trace!("Received Window in main thread"); + + Ok(Self { + name, + size, + id, + receiver: rx, + window, + event_loop_proxy, + }) + } +} + +impl Output for WinitWindow { + fn get_id(&self) -> usize { self.id } + fn set_id(&mut self, id: usize) { self.id = id; } + + fn size(&mut self) -> &mut Rect { &mut self.size } + + fn window(&self) -> &Window { &self.window } +} + +impl Input for WinitWindow { + fn read(&self, timeout: Duration) -> Result { + + match self.receiver.recv_timeout(timeout) { + Ok(key) => Ok(key), + Err(_) => Err(ReadError::Timeout), + } + } + + fn write(&self, signal: Signal) { + + self.event_loop_proxy.send_event(signal) + .map_err(|_| "Could not send Signal to EventLoop").unwrap(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::utils::Rect; + + fn setup_logger() -> Result<(), fern::InitError> { + use fern::colors::{Color, ColoredLevelConfig}; + + let colors = ColoredLevelConfig::new() + .info(Color::Green) + .debug(Color::Magenta) + .warn(Color::Yellow) + .error(Color::Red); + + fern::Dispatch::new() + .format(move |out, message, record| { + out.finish(format_args!( + "{}[{}][{}] {}", + chrono::Local::now().format("[%H:%M:%S]"), + colors.color(record.level()), + record.target(), + message + )) + }) + .level(log::LevelFilter::Trace) + .chain(std::io::stdout()) + .chain(fern::log_file("output.log")?) + .apply()?; + Ok(()) + } + + //#[test] + fn test_new_drop() { + let _ = setup_logger(); + let mut window1 = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + let mut window2 = WinitWindow::new("IV 2", Rect {w: 1280, h: 720}).unwrap(); + + window1.drop(); + window2.drop(); + + panic!("test"); + } + + //#[test] + fn test_read_write() { + let window = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + window.write(Signal::Test); + let input = window.read(Duration::from_millis(1)).unwrap(); + assert!(matches!(input, Key::Test)); + } +} diff --git a/src/lib.rs b/src/lib.rs index d97c6b0..52380b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,9 +18,6 @@ use controller::Controller; pub mod utils; //use utils::Rect; -mod winit_window; -//use winit_window::WinitWindow; - mod renderer; //use renderer::Renderer; @@ -63,7 +60,7 @@ pub fn run() -> Result<(), &'static str> { }; use crate::{ - winit_window::WinitWindow, + io::WinitWindow, renderer::Renderer, utils::Rect, subengine::{SubengineController, TestSubengine}, @@ -112,7 +109,7 @@ pub fn run() -> Result<(), &'static str> { //let controller = Controller::new(renderers, &windows, &windows, engine_pipelines); //controller.run(); - Ok(()) + //Ok(()) //let local_state = LocalState::default(); // let color = [0.5, 0.0, 0.0, 1.0]; diff --git a/src/renderer/swap_system.rs b/src/renderer/swap_system.rs index 8a251d2..b2ae707 100644 --- a/src/renderer/swap_system.rs +++ b/src/renderer/swap_system.rs @@ -197,6 +197,8 @@ where }}, Err(err) => return Err(err), }}; + //println!("Frame nb : {}", self.frames.len()); + //frames number diminish sometimes at resize... let mut frame = self.frames.pop_back().unwrap(); trace!("Creating Framebuffer..."); @@ -211,19 +213,16 @@ where .unwrap() //TODO improve that }; - frame.link_swapchain_image(image, framebuffer); + frame.link_swapchain_image(gpu, image, framebuffer); Ok(frame) } pub fn present_frame(&mut self, mut frame: Frame, gpu: &mut Gpu) -> Result<(), &'static str> { - use gfx_hal::{ - queue::CommandQueue, - device::Device, - }; + use gfx_hal::queue::CommandQueue; - let (image, framebuffer) = frame + let image = frame .unlink_swapchain_image() .unwrap(); //TODO improve that @@ -236,11 +235,6 @@ where self.frames.push_front(frame); - trace!("Destroying Framebuffer..."); - unsafe { - gpu.device().destroy_framebuffer(framebuffer); - } - Ok(()) } } diff --git a/src/renderer/swap_system/frame.rs b/src/renderer/swap_system/frame.rs index 24ad72d..e582dcb 100644 --- a/src/renderer/swap_system/frame.rs +++ b/src/renderer/swap_system/frame.rs @@ -98,22 +98,28 @@ where } pub fn link_swapchain_image(&mut self, + gpu: &Gpu, image: >::SwapchainImage, framebuffer: B::Framebuffer) { - + use gfx_hal::device::Device; + + match self.framebuffer.replace(framebuffer) { + Some(old_framebuffer) => { + trace!("Destroying Framebuffer..."); + unsafe { gpu.device().destroy_framebuffer(old_framebuffer) }; + }, + None => (), + } + self.image_view = Some(image); - self.framebuffer = Some(framebuffer); } pub fn unlink_swapchain_image(&mut self) - -> Result<(>::SwapchainImage, - B::Framebuffer), &'static str> { + -> Result<>::SwapchainImage, &'static str> { - match (self.image_view.take(), self.framebuffer.take()) { - (Some(image_view), Some(framebuffer)) => Some((image_view, framebuffer)), - _ => None, - } - .ok_or("Can not unlink non-linked Frame !") + self.image_view + .take() + .ok_or("Can not unlink non-linked Frame !") } } diff --git a/src/subengine/subengine_controller.rs b/src/subengine/subengine_controller.rs index 94cef97..a2d4469 100644 --- a/src/subengine/subengine_controller.rs +++ b/src/subengine/subengine_controller.rs @@ -107,18 +107,18 @@ enum SubengineResponse { } //--Tests------------------------------------------------------------------------------------------- -#[cfg(test)] +//#[cfg(test)] mod tests { use super::*; use crate::subengine::TestSubengine; - #[test] + //#[test] fn test_new_drop() { let (test_subengine, _test_rx) = TestSubengine::new("run"); - let subengine_controller = SubengineController::new(test_subengine); + let _subengine_controller = SubengineController::new(test_subengine); } - #[test] + //#[test] fn test_exec() { let (test_subengine, test_rx) = TestSubengine::new("run"); let subengine_controller = SubengineController::new(test_subengine); @@ -128,7 +128,7 @@ mod tests { assert_eq!(response, "run"); } - #[test] + //#[test] fn test_wait_for_exec() { let (test_subengine, _test_rx) = TestSubengine::new("run"); let subengine_controller = SubengineController::new(test_subengine);