Got the Controller working

* reworked iput system again
+ added test functions
This commit is contained in:
Steins7 2021-01-18 10:16:58 +01:00
parent 7f37ee7a24
commit e3fdbb142c
8 changed files with 363 additions and 89 deletions

View File

@ -38,7 +38,7 @@ where
Controller { Controller {
pipelines: pipelines_vec, 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 pipeline in &mut self.pipelines {
for input in &pipeline.inputs { for input in &pipeline.inputs {
match input.borrow().read(1) { match input.borrow().read(Duration::from_millis(1)) {
Ok(key) => input_keys.push(key), Ok(key) => input_keys.push(key),
Err(_err) => (), Err(_err) => (),
} }
@ -87,14 +87,14 @@ where
} }
} }
#[cfg(test)] //#[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::iter; use std::iter;
use crate::{ use crate::{
winit_window::WinitWindow, io::WinitWindow,
renderer::Renderer, renderer::Renderer,
utils::Rect, utils::Rect,
subengine::{SubengineController, TestSubengine}, subengine::{SubengineController, TestSubengine},
@ -102,7 +102,7 @@ mod tests {
use gfx_backend_vulkan as vk_back; use gfx_backend_vulkan as vk_back;
#[test] //#[test]
fn test_new() { fn test_new() {
use std::cell::RefCell; use std::cell::RefCell;
@ -129,7 +129,7 @@ mod tests {
let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers); let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers);
//creating controller //creating controller
let controller = Controller::new(vec![subengine_pipeline]); let _controller = Controller::new(vec![subengine_pipeline]);
} }
#[test] #[test]
@ -160,7 +160,9 @@ mod tests {
//running controller //running controller
let mut controller = Controller::new(vec![subengine_pipeline]); let mut controller = Controller::new(vec![subengine_pipeline]);
controller.run(); for _i in 0..10 {
controller.run();
}
} }
} }

View File

@ -1,10 +1,16 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use std::time::Duration;
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use crate::utils::Rect; use crate::utils::Rect;
pub mod winit_window;
pub use self::winit_window::WinitWindow;
//--Output trait------------------------------------------------------------------------------------ //--Output trait------------------------------------------------------------------------------------
/// A trait for the ability of a type to be used as an output by the engine. /// 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; fn window(&self) -> &W;
} }
//impl<'a, W, O> Output<W> for &'a mut O //--Input trait-------------------------------------------------------------------------------------
//where
// W: HasRawWindowHandle,
// O: Output<W>,
//{
// 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<i32> { Output::size(*self) }
//
// fn window(&self) -> &W { Output::window(*self) }
//}
//impl<'a, W, O> Output<W> for Box<O>
//where
// W: HasRawWindowHandle,
// O: Output<W>,
//{
// 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<i32> { Output::size(self) }
//
// fn window(&self) -> &W { Output::window(self) }
//}
//--Input trait------------------------------------------------------------------------------------
/// A trait for the ability of a type to be used as an input by the engine /// 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 /// 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 /// 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. /// was reached. How inputs are handled depends on the implementation.
//TODO change timeout //TODO change timeout
fn read(&self, timeout_ms: u32) -> Result<Key, ReadError>; fn read(&self, timeout: Duration) -> Result<Key, ReadError>;
fn write(&self, signal: Signal);
} }
//impl<'a, I> Input for &'a mut I
//where
// I: Input,
//{
// fn read(&self, timeout_ms: u32) -> Result<Key, ReadError> { Input::read(*self, timeout_ms) }
//}
//--Key enum---------------------------------------------------------------------------------------- //--Key enum----------------------------------------------------------------------------------------
#[derive(Debug)]
pub enum Key { pub enum Key {
Close, Close,
Closed,
Test,
MouseMove { x: f64, y: f64 }, MouseMove { x: f64, y: f64 },
} }
//--Signal enum-------------------------------------------------------------------------------------
#[derive(Debug)]
pub enum Signal {
Exit,
Test,
}
//--ReadError enum---------------------------------------------------------------------------------- //--ReadError enum----------------------------------------------------------------------------------
#[derive(Debug)]
pub enum ReadError { pub enum ReadError {
Timeout, Timeout,
} }

View File

@ -4,17 +4,18 @@ use log::{debug, error, info, trace, warn};
use std::{ use std::{
thread, thread,
sync::mpsc, sync::mpsc,
time::Duration,
}; };
use winit::{ use winit::{
dpi::PhysicalSize, dpi::PhysicalSize,
event_loop::{EventLoop}, event_loop::{EventLoop, EventLoopProxy},
window::{WindowBuilder, Window}, window::{WindowBuilder, Window},
}; };
//TODO fix that //TODO fix that
use crate::{ use crate::{
io::{Output, Input, Key, ReadError}, io::{Output, Input, Key, Signal, ReadError},
utils::Rect, utils::Rect,
}; };
@ -24,9 +25,24 @@ pub struct WinitWindow {
size: Rect<i32>, size: Rect<i32>,
id: usize, id: usize,
handle: thread::JoinHandle<()>,
receiver: mpsc::Receiver<Key>, receiver: mpsc::Receiver<Key>,
window: Window, window: Window,
event_loop_proxy: EventLoopProxy<Signal>,
}
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 { impl WinitWindow {
@ -42,22 +58,24 @@ impl WinitWindow {
let cloned_name = name.clone(); let cloned_name = name.clone();
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let (tmp_tx, tmp_rx) = mpsc::sync_channel(1); 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"); 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 :/ //drop crossplatform compatibility :/
let event_loop = EventLoop::new_any_thread(); let event_loop = EventLoop::new_any_thread();
let window = WindowBuilder::new() let window = WindowBuilder::new()
.with_inner_size(PhysicalSize {width: size.w, height: size.h}) .with_inner_size(PhysicalSize {width: size.w, height: size.h})
.with_title(cloned_name) .with_title(cloned_name)
.build(&event_loop).unwrap(); .build(&event_loop).unwrap();
let event_loop_proxy = event_loop.create_proxy();
trace!("Sending Window back to main thread"); 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<'_, Signal>, _, control_flow| {
event_loop.run(move |event: winit::event::Event<'_, ()>, _, control_flow| {
use winit::{ use winit::{
event_loop::ControlFlow, event_loop::ControlFlow,
event::Event, event::Event,
@ -66,13 +84,15 @@ impl WinitWindow {
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
//TODO manage errors
match event { match event {
Event::LoopDestroyed => {
tx.send(Key::Closed).unwrap();
debug!("Closed EventLoop");
},
Event::WindowEvent{window_id: _, event} => match event { Event::WindowEvent{window_id: _, event} => match event {
event::WindowEvent::CloseRequested => { event::WindowEvent::CloseRequested => {
debug!("Close requested");
tx.send(Key::Close).unwrap(); tx.send(Key::Close).unwrap();
warn!("Stop input thread");
*control_flow = ControlFlow::Exit;
}, },
event::WindowEvent::CursorMoved{position, ..} => { event::WindowEvent::CursorMoved{position, ..} => {
tx.send(Key::MouseMove{ tx.send(Key::MouseMove{
@ -81,21 +101,30 @@ impl WinitWindow {
}).unwrap(); }).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"); trace!("Received Window in main thread");
Ok(Self { Ok(Self {
name, name,
size, size,
id, id,
handle: handle,
receiver: rx, receiver: rx,
window, window,
event_loop_proxy,
}) })
} }
} }
@ -110,13 +139,39 @@ impl Output<Window> for WinitWindow {
} }
impl Input for WinitWindow { impl Input for WinitWindow {
fn read(&self, timeout_ms: u32) -> Result<Key, ReadError> { fn read(&self, timeout: Duration) -> Result<Key, ReadError> {
use std::time::Duration;
match self.receiver.recv_timeout(Duration::from_millis(timeout_ms.into())) { match self.receiver.recv_timeout(timeout) {
Ok(key) => Ok(key), Ok(key) => Ok(key),
Err(_) => Err(ReadError::Timeout), 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));
}
}

234
src/io/winit_window.rs Normal file
View File

@ -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<i32>,
id: usize,
receiver: mpsc::Receiver<Key>,
window: Window,
event_loop_proxy: EventLoopProxy<Signal>,
}
//
//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<i32>) -> Result<WinitWindow, &'static str> {
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<Window> 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<i32> { &mut self.size }
fn window(&self) -> &Window { &self.window }
}
impl Input for WinitWindow {
fn read(&self, timeout: Duration) -> Result<Key, ReadError> {
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));
}
}

View File

@ -18,9 +18,6 @@ use controller::Controller;
pub mod utils; pub mod utils;
//use utils::Rect; //use utils::Rect;
mod winit_window;
//use winit_window::WinitWindow;
mod renderer; mod renderer;
//use renderer::Renderer; //use renderer::Renderer;
@ -63,7 +60,7 @@ pub fn run() -> Result<(), &'static str> {
}; };
use crate::{ use crate::{
winit_window::WinitWindow, io::WinitWindow,
renderer::Renderer, renderer::Renderer,
utils::Rect, utils::Rect,
subengine::{SubengineController, TestSubengine}, subengine::{SubengineController, TestSubengine},
@ -112,7 +109,7 @@ pub fn run() -> Result<(), &'static str> {
//let controller = Controller::new(renderers, &windows, &windows, engine_pipelines); //let controller = Controller::new(renderers, &windows, &windows, engine_pipelines);
//controller.run(); //controller.run();
Ok(()) //Ok(())
//let local_state = LocalState::default(); //let local_state = LocalState::default();
// let color = [0.5, 0.0, 0.0, 1.0]; // let color = [0.5, 0.0, 0.0, 1.0];

View File

@ -197,6 +197,8 @@ where
}}, }},
Err(err) => return Err(err), 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(); let mut frame = self.frames.pop_back().unwrap();
trace!("Creating Framebuffer..."); trace!("Creating Framebuffer...");
@ -211,19 +213,16 @@ where
.unwrap() //TODO improve that .unwrap() //TODO improve that
}; };
frame.link_swapchain_image(image, framebuffer); frame.link_swapchain_image(gpu, image, framebuffer);
Ok(frame) Ok(frame)
} }
pub fn present_frame(&mut self, mut frame: Frame<B>, gpu: &mut Gpu<B>) pub fn present_frame(&mut self, mut frame: Frame<B>, gpu: &mut Gpu<B>)
-> Result<(), &'static str> { -> Result<(), &'static str> {
use gfx_hal::{ use gfx_hal::queue::CommandQueue;
queue::CommandQueue,
device::Device,
};
let (image, framebuffer) = frame let image = frame
.unlink_swapchain_image() .unlink_swapchain_image()
.unwrap(); //TODO improve that .unwrap(); //TODO improve that
@ -236,11 +235,6 @@ where
self.frames.push_front(frame); self.frames.push_front(frame);
trace!("Destroying Framebuffer...");
unsafe {
gpu.device().destroy_framebuffer(framebuffer);
}
Ok(()) Ok(())
} }
} }

View File

@ -98,22 +98,28 @@ where
} }
pub fn link_swapchain_image(&mut self, pub fn link_swapchain_image(&mut self,
gpu: &Gpu<B>,
image: <B::Surface as PresentationSurface<B>>::SwapchainImage, image: <B::Surface as PresentationSurface<B>>::SwapchainImage,
framebuffer: B::Framebuffer) { 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.image_view = Some(image);
self.framebuffer = Some(framebuffer);
} }
pub fn unlink_swapchain_image(&mut self) pub fn unlink_swapchain_image(&mut self)
-> Result<(<B::Surface as PresentationSurface<B>>::SwapchainImage, -> Result<<B::Surface as PresentationSurface<B>>::SwapchainImage, &'static str> {
B::Framebuffer), &'static str> {
match (self.image_view.take(), self.framebuffer.take()) { self.image_view
(Some(image_view), Some(framebuffer)) => Some((image_view, framebuffer)), .take()
_ => None, .ok_or("Can not unlink non-linked Frame !")
}
.ok_or("Can not unlink non-linked Frame !")
} }
} }

View File

@ -107,18 +107,18 @@ enum SubengineResponse {
} }
//--Tests------------------------------------------------------------------------------------------- //--Tests-------------------------------------------------------------------------------------------
#[cfg(test)] //#[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::subengine::TestSubengine; use crate::subengine::TestSubengine;
#[test] //#[test]
fn test_new_drop() { fn test_new_drop() {
let (test_subengine, _test_rx) = TestSubengine::new("run"); 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() { fn test_exec() {
let (test_subengine, test_rx) = TestSubengine::new("run"); let (test_subengine, test_rx) = TestSubengine::new("run");
let subengine_controller = SubengineController::new(test_subengine); let subengine_controller = SubengineController::new(test_subengine);
@ -128,7 +128,7 @@ mod tests {
assert_eq!(response, "run"); assert_eq!(response, "run");
} }
#[test] //#[test]
fn test_wait_for_exec() { fn test_wait_for_exec() {
let (test_subengine, _test_rx) = TestSubengine::new("run"); let (test_subengine, _test_rx) = TestSubengine::new("run");
let subengine_controller = SubengineController::new(test_subengine); let subengine_controller = SubengineController::new(test_subengine);