diff --git a/doc/architecture b/doc/architecture
index 242354c..4990374 100644
--- a/doc/architecture
+++ b/doc/architecture
@@ -1 +1 @@
-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
\ No newline at end of file
+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
\ No newline at end of file
diff --git a/src/controller.rs b/src/controller.rs
new file mode 100644
index 0000000..a335776
--- /dev/null
+++ b/src/controller.rs
@@ -0,0 +1,166 @@
+#[allow(unused_imports)]
+use log::{debug, error, info, trace, warn};
+
+use crate::{
+ io::{Input, Output},
+ subengine::SubenginePipeline,
+};
+
+//--Controller Implementation-----------------------------------------------------------------------
+pub struct Controller<'a, I, W, O>
+where
+ I: Input,
+ W: raw_window_handle::HasRawWindowHandle,
+ O: Output,
+{
+ pipelines: Vec>,
+ color: [f32; 4],
+}
+
+impl Controller<'_, I, W, O>
+where
+ I: Input,
+ W: raw_window_handle::HasRawWindowHandle,
+ O: Output,
+{
+
+ pub fn new<'a, Ip>(pipelines: Ip) -> Controller<'a, I, W, O>
+ where
+ I: 'a + Input,
+ W: raw_window_handle::HasRawWindowHandle,
+ O: 'a + Output,
+ Ip: IntoIterator- >,
+ {
+ let pipelines_vec = pipelines
+ .into_iter()
+ .map(|pipeline| pipeline)
+ .collect();
+
+ Controller {
+ pipelines: pipelines_vec,
+ color: [0.0, 1.0, 0.0, 0.0],
+ }
+ }
+
+ pub fn run(&mut self) {
+ use std::time::Duration;
+
+ use crate::{
+ subengine::subengine_controller::SubengineCommand,
+ io::Key,
+ };
+
+ let mut input_keys: Vec = Vec::new();
+
+ for pipeline in &mut self.pipelines {
+ for input in &pipeline.inputs {
+ match input.borrow().read(1) {
+ Ok(key) => input_keys.push(key),
+ Err(_err) => (),
+ }
+ }
+ for input_key in &input_keys {
+ match input_key {
+ Key::MouseMove{x,y} => self.color = [
+ (x/1280.0) as f32,
+ (y/720.0) as f32,
+ ((x/1280.0 + y/720.0)/2.0) as f32,
+ 1.0],
+ _ => (),
+ };
+ }
+ for subengines in &pipeline.subengines {
+ for subengine in subengines {
+ subengine.exec(SubengineCommand::Run);
+ }
+ for subengine in subengines {
+ subengine.wait_for_exec(Duration::from_millis(1)).unwrap();
+ }
+ }
+ for (renderer, output) in &mut pipeline.renderers {
+ match renderer.draw_clear_frame(output, self.color) {
+ Err(err) => warn!("{}", err),
+ _ => (),
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use std::iter;
+
+ use crate::{
+ winit_window::WinitWindow,
+ renderer::Renderer,
+ utils::Rect,
+ subengine::{SubengineController, TestSubengine},
+ };
+
+ use gfx_backend_vulkan as vk_back;
+
+ #[test]
+ fn test_new() {
+ use std::cell::RefCell;
+
+ //creating windows
+ let window1 = RefCell::new(WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap());
+ let window2 = RefCell::new(WinitWindow::new("IV 2", Rect {w: 720, h: 480}).unwrap());
+
+ //creating renderers
+ let renderer1 = Renderer::::new(&mut iter::once(&window1)).unwrap();
+ let renderer2 = Renderer::::new(&mut iter::once(&window2)).unwrap();
+
+ //creating subengines
+ let (test_subengine1, _test_rx1) = TestSubengine::new("run1");
+ let test_controller1 = SubengineController::new(test_subengine1);
+ let (test_subengine2, _test_rx2) = TestSubengine::new("run2");
+ let test_controller2 = SubengineController::new(test_subengine2);
+
+ //preparing data
+ let inputs = vec![&window1, &window2];
+ let subengines = vec![vec![test_controller1, test_controller2]];
+ let renderers = vec![(renderer1, &window1), (renderer2, &window2)];
+
+ //creating pipeline
+ let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers);
+
+ //creating controller
+ let controller = Controller::new(vec![subengine_pipeline]);
+ }
+
+ #[test]
+ fn test_run() {
+ use std::cell::RefCell;
+
+ //creating windows
+ let window1 = RefCell::new(WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap());
+ let window2 = RefCell::new(WinitWindow::new("IV 2", Rect {w: 720, h: 480}).unwrap());
+
+ //creating renderers
+ let renderer1 = Renderer::::new(&mut iter::once(&window1)).unwrap();
+ let renderer2 = Renderer::::new(&mut iter::once(&window2)).unwrap();
+
+ //creating subengines
+ let (test_subengine1, _test_rx1) = TestSubengine::new("run1");
+ let test_controller1 = SubengineController::new(test_subengine1);
+ let (test_subengine2, _test_rx2) = TestSubengine::new("run2");
+ let test_controller2 = SubengineController::new(test_subengine2);
+
+ //preparing data
+ let inputs = vec![&window1, &window2];
+ let subengines = vec![vec![test_controller1, test_controller2]];
+ let renderers = vec![(renderer1, &window1), (renderer2, &window2)];
+
+ //creating pipeline
+ let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers);
+
+ //running controller
+ let mut controller = Controller::new(vec![subengine_pipeline]);
+ controller.run();
+ }
+}
+
diff --git a/src/input.rs b/src/input.rs
deleted file mode 100644
index bdf7450..0000000
--- a/src/input.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-#[allow(unused_imports)]
-use log::{debug, error, info, trace, warn};
-
-use winit::{
- event::{Event, WindowEvent},
- event_loop::{ControlFlow, EventLoop},
-};
-
-#[derive(Debug)]
-pub struct Input {
- pub close_request: bool,
- pub new_frame_size: Option<(f64, f64)>,
- pub new_mouse_pos: Option<(f64, f64)>,
-}
-
-impl Input {
-
- pub fn poll_events_loop(event_loop: &mut EventLoop<()>) -> Self {
- let mut input = Input::default();
- event_loop.run(|event, _, control_flow| {
- *control_flow = ControlFlow::Wait;
-
- match event {
- Event::WindowEvent{window_id: _, event} => match event {
- WindowEvent::CloseRequested => (),
- _ => (),
- }
- _ => (),
- }
- });
- input
- }
-
- pub fn default() -> Self {
- Input {
- close_request: false,
- new_frame_size: None,
- new_mouse_pos: None,
- }
- }
-}
-
diff --git a/src/io.rs b/src/io.rs
new file mode 100644
index 0000000..9dd067a
--- /dev/null
+++ b/src/io.rs
@@ -0,0 +1,98 @@
+#[allow(unused_imports)]
+use log::{debug, error, info, trace, warn};
+
+use raw_window_handle::HasRawWindowHandle;
+
+use crate::utils::Rect;
+
+//--Output trait------------------------------------------------------------------------------------
+/// A trait for the ability of a type to be used as an output by the engine.
+///
+/// The `Output` trait defines functions to be used by different components of the engine. This
+/// allows to display with any window manager as long as the trait is defined for it.
+///
+/// Types that implement the `Output` trait should manage the underlying window and settings.
+///
+/// Implementation : the ID and size stored by the type implementing this trait don't need to be
+/// initialized to anything specific as the engine will take care of it.
+pub trait Output
+where
+ W: HasRawWindowHandle,
+{
+ /// Return the ID stored internally. Used to select the right swapchain by the render engine.
+ /// For internal use only.
+ fn get_id(&self) -> usize;
+
+ /// Store the given ID internally. For internal use only.
+ fn set_id(&mut self, id: usize);
+
+ //TODO clean that
+ /// Give mutable acces to the size of the output. The size is a simple rectangle containing the
+ /// width and height of the `Output`.
+ fn size(&mut self) -> &mut Rect;
+
+ /// Give reference acces to the underlying window. This is used by the engine during setup to
+ /// create the revelant ressources.
+ 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------------------------------------------------------------------------------------
+/// 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
+/// read inputs from any window manzger as long as the is defined for it.
+///
+/// Types that implement the `Input` trait should manage the EventLoop. This can be done in any way
+/// (polling, interrupt, synchronous, asynchronous, ...) depending on how the input should be
+/// handled.
+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;
+}
+
+//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----------------------------------------------------------------------------------------
+pub enum Key {
+ Close,
+ MouseMove { x: f64, y: f64 },
+}
+
+//--ReadError enum----------------------------------------------------------------------------------
+pub enum ReadError {
+ Timeout,
+}
+
diff --git a/src/lib.rs b/src/lib.rs
index 3dbb978..d97c6b0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,31 +5,32 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
-//use std::{
-// sync::mpsc,
-// thread,
-// collections::HashMap,
-// cell::RefCell,
-//};
-
use gfx_backend_vulkan as vk_back;
+pub mod io;
+
+pub mod subengine;
+use subengine::SubenginePipeline;
+
+pub mod controller;
+use controller::Controller;
+
pub mod utils;
-use crate::utils::Rect;
+//use utils::Rect;
mod winit_window;
-use winit_window::WinitWindow;
-
-//use winit::{
-// event::{Event, WindowEvent},
-// event_loop::ControlFlow,
-//};
+//use winit_window::WinitWindow;
mod renderer;
-use renderer::Renderer;
+//use renderer::Renderer;
-//mod local_state;
-//use local_state::LocalState;
+//mod controller;
+//use controller::Controller;
+
+//use crate::engine::{
+// EngineController,
+// TestEngine,
+//};
pub enum Command {
NoCommand,
@@ -56,24 +57,73 @@ pub enum Input {
/// The main function of the library
pub fn run() -> Result<(), &'static str> {
-
- let mut windows = vec![
- WinitWindow::new("IV", Rect {w: 1280, h: 720})?,
- // WinitWindow::new("IV 2", Rect {w: 720, h: 480})?,
- ];
+ use std::{
+ iter,
+ cell::RefCell,
+ };
- let mut renderer: Renderer = Renderer::new(&mut windows)?;
+ use crate::{
+ winit_window::WinitWindow,
+ renderer::Renderer,
+ utils::Rect,
+ subengine::{SubengineController, TestSubengine},
+ };
+
+ //creating windows
+ let window1 = RefCell::new(WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap());
+ //let window2 = RefCell::new(WinitWindow::new("IV 2", Rect {w: 720, h: 480}).unwrap());
+
+ //creating renderers
+ let renderer1 = Renderer::::new(&mut iter::once(&window1)).unwrap();
+ //let renderer2 = Renderer::::new(&mut iter::once(&window2)).unwrap();
+
+ //creating subengines
+ let (test_subengine1, _test_rx1) = TestSubengine::new("run1");
+ let test_controller1 = SubengineController::new(test_subengine1);
+ //let (test_subengine2, _test_rx2) = TestSubengine::new("run2");
+ //let test_controller2 = SubengineController::new(test_subengine2);
+
+ //preparing data
+ let inputs = vec![&window1];
+ let subengines = vec![vec![test_controller1/*, test_controller2*/]];
+ let renderers = vec![(renderer1, &window1)/*, (renderer2, &window2)*/];
+
+ //creating pipeline
+ let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers);
+
+ //running controller
+ let mut controller = Controller::new(vec![subengine_pipeline]);
+ loop {
+ controller.run();
+ }
+
+ //let engine_pipelines = vec![
+ // EnginePipeline {
+ // Inputs: vec![0,1],
+ // Engines: vec![&color_engine],
+ // Renderers: vec![(1,0)],
+ // },
+ // EnginePipeline {
+ // Inputs: vec![3],
+ // Engines: vec![&color_engine],
+ // Renderers: vec![(1,1),(1,2),(1,3)],
+ //}];
+
+ //let controller = Controller::new(renderers, &windows, &windows, engine_pipelines);
+ //controller.run();
+
+ Ok(())
//let local_state = LocalState::default();
- let color = [0.5, 0.0, 0.0, 1.0];
-
- loop {
- for window in &mut windows {
- match renderer.draw_clear_frame(window, color) {
- Err(err) => println!("{}", err),
- _ => (),
- }}}
-
+// let color = [0.5, 0.0, 0.0, 1.0];
+//
+// loop {
+// for window in &mut windows {
+// match renderer.draw_clear_frame(window, color) {
+// Err(err) => println!("{}", err),
+// _ => (),
+// }}}
+//
// Ok(())
}
@@ -92,7 +142,7 @@ pub fn run() -> Result<(), &'static str> {
//
// let mut color = [0.0, 0.0, 0.0, 0.0];
//
-// loop {
+// loop
//
// //TODO manage errors
// for window in windows {
diff --git a/src/renderer.rs b/src/renderer.rs
index dff28a5..2fc712e 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -4,6 +4,11 @@ use log::{debug, error, info, trace, warn};
use std::{
mem::ManuallyDrop,
iter,
+ cell::RefCell,
+};
+
+use crate::{
+ io::Output,
};
mod gpu;
@@ -12,18 +17,11 @@ use gpu::Gpu;
mod swap_system;
use swap_system::SwapSystem;
-pub mod output;
-use output::Output;
-
-//mod pipeline;
-//use pipeline::Pipeline;
-
//--Renderer implementation-------------------------------------------------------------------------
#[derive(Debug)]
pub struct Renderer {
instance: ManuallyDrop,
gpu: ManuallyDrop>,
- //pipelines: Vec>,
swap_systems: Vec>,
}
@@ -62,12 +60,11 @@ impl Renderer
where
B: gfx_hal::Backend,
{
- pub fn new<'a, H, T: 'a, I>(outputs: I) -> Result, &'static str>
+ pub fn new<'a, W: 'a, O: 'a, I>(outputs: &mut I) -> Result, &'static str>
where
- H: raw_window_handle::HasRawWindowHandle,
- T: Output,
- I: IntoIterator
- ,
-
+ W: raw_window_handle::HasRawWindowHandle,
+ O: Output,
+ I: Iterator
- >,
{
use gfx_hal::Instance;
@@ -91,11 +88,11 @@ where
})
}
- pub fn draw_clear_frame(&mut self, output: &mut O, color: [f32; 4])
+ pub fn draw_clear_frame(&mut self, output: &RefCell, color: [f32; 4])
-> Result<(), &'static str>
where
- T: raw_window_handle::HasRawWindowHandle,
- O: Output
+ W: raw_window_handle::HasRawWindowHandle,
+ O: Output,
{
use gfx_hal::{
window::AcquireError,
@@ -103,7 +100,7 @@ where
queue::Submission,
};
- let swap_system = &mut self.swap_systems[output.get_id()];
+ let swap_system = &mut self.swap_systems[output.borrow_mut().get_id()];
let mut frame = match swap_system.acquire_frame(&self.gpu) {
Ok(frame) => frame,
diff --git a/src/renderer/gpu.rs b/src/renderer/gpu.rs
index e88b197..31fcc7f 100644
--- a/src/renderer/gpu.rs
+++ b/src/renderer/gpu.rs
@@ -4,6 +4,7 @@ use log::{debug, error, info, trace, warn};
use std::{
mem::ManuallyDrop,
ptr::read,
+ cell::RefCell,
};
use gfx_hal::{
@@ -13,9 +14,14 @@ use gfx_hal::{
use gfx_hal::adapter::Gpu as GfxGpu;
-use super::output::Output;
+use crate::io::Output;
//--Gpu implementation------------------------------------------------------------------------------
+/// A struct managing all things related to a specific GPU
+///
+/// The `GPU` struct manages the structs from the gfx_hal[gfx_hal] related to the physical device.
+/// It implements constructor and destructors as well as acces functions. This is done to make the
+/// usage of the HAL easier and make the higher level code less dependant on changes in it.
#[derive(Debug)]
pub struct Gpu {
adapter: ManuallyDrop>,
@@ -47,12 +53,13 @@ impl Gpu
where
B: gfx_hal::Backend,
{
- pub fn new<'a, H, T: 'a, I>(instance: &B::Instance, outputs: I)
+ /// Create a new `GPU` based on an `instance` of the gfx_hal[gfx_hal].
+ pub fn new<'a, W, O: 'a, I>(instance: &B::Instance, outputs: &mut I)
-> Result<(Gpu, Vec), &'static str>
where
- H: raw_window_handle::HasRawWindowHandle,
- T: Output,
- I: IntoIterator
- ,
+ W: raw_window_handle::HasRawWindowHandle,
+ O: Output,
+ I: Iterator
- >,
{
use gfx_hal::{
queue::family::QueueFamily,
@@ -65,13 +72,13 @@ where
let mut id = 0;
outputs
- .into_iter()
+ .by_ref()
.map(|output| unsafe {
- output.set_id(id);
+ output.borrow_mut().set_id(id);
id += 1;
instance
- .create_surface(output.window())
+ .create_surface(output.borrow().window())
.map_err(|_| "Could not create surface")
})
.collect::, &str>>()?
diff --git a/src/renderer/output.rs b/src/renderer/output.rs
deleted file mode 100644
index 49de5f2..0000000
--- a/src/renderer/output.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-#[allow(unused_imports)]
-use log::{debug, error, info, trace, warn};
-
-use raw_window_handle::HasRawWindowHandle;
-
-use crate::utils::Rect;
-
-//--Output trait------------------------------------------------------------------------------------
-pub trait Output
-where
- T: HasRawWindowHandle,
-{
- fn get_id(&self) -> usize;
- fn set_id(&mut self, id: usize);
-
- fn size(&mut self) -> &mut Rect;
-
- fn window(&self) -> &T;
-}
-
-//impl<'a, T, O> Output for &'a O
-//where
-// T: HasRawWindowHandle,
-// O: Output {
-// fn id(&mut self) -> &mut i32 { Output::id(*self) }
-//
-// fn size(&mut self) -> &mut Rect { Output::size(*self) }
-//
-// fn window(&self) -> &T { Output::window(*self) }
-//}
-
-impl<'a, T, O> Output for &'a mut O
-where
- T: 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) -> &T { Output::window(*self) }
-}
-
diff --git a/src/subengine.rs b/src/subengine.rs
new file mode 100644
index 0000000..12fc610
--- /dev/null
+++ b/src/subengine.rs
@@ -0,0 +1,22 @@
+#[allow(unused_imports)]
+use log::{debug, error, info, trace, warn};
+
+pub mod subengine_controller;
+pub use self::subengine_controller::SubengineController;
+
+pub mod subengine_pipeline;
+pub use self::subengine_pipeline::SubenginePipeline;
+
+pub mod test_subengine;
+pub use self::test_subengine::TestSubengine;
+
+
+//--SubEngine trait------------------------------------------------------------------------------------
+/// A trait that should be implemented by all subengines running it the game engine.
+///
+/// Allow the [`Controller`] to run custom subengines.
+pub trait Subengine {
+ /// Main function of the subengine. Called byt the [`Controller`] for each frame.
+ fn run(&self);
+}
+
diff --git a/src/subengine/subengine_controller.rs b/src/subengine/subengine_controller.rs
new file mode 100644
index 0000000..94cef97
--- /dev/null
+++ b/src/subengine/subengine_controller.rs
@@ -0,0 +1,140 @@
+#[allow(unused_imports)]
+use log::{debug, error, info, trace, warn};
+
+use std::{
+ sync::mpsc::{Sender, Receiver},
+ thread::JoinHandle,
+ mem::ManuallyDrop,
+ ptr::read,
+ time::Duration,
+};
+
+use crate::subengine::Subengine;
+
+//--SubengineController Implementation--------------------------------------------------------------
+/// Handle to manage an [`Subengine`].
+///
+/// The `SubengineController` runs the [`Engine`] in a separated thread with some control code to
+/// allow control from the main thread. SubengineControllers are used by the [`Controller`] to
+/// interact with Subengines.
+#[derive(Debug)]
+pub struct SubengineController {
+ tx: Sender,
+ rx: Receiver,
+ handle: ManuallyDrop>,
+}
+
+impl Drop for SubengineController {
+ fn drop(&mut self) {
+ self.exec(SubengineCommand::Stop);
+ unsafe { let _ = ManuallyDrop::into_inner(read(&mut self.handle)).join(); }
+ debug!("SubengineController dropped !");
+ }
+}
+
+impl SubengineController {
+ /// Creates a new `SubengineController` from a given [`Engine`]. Since the latter will be moved
+ /// to a separated thread, it must implement the [`Send`] trait.
+ pub fn new(engine: E) -> SubengineController
+ where
+ E: Subengine + std::marker::Send,
+ {
+ use std::{
+ thread,
+ sync::mpsc::channel,
+ };
+
+ debug!("Creating SubengineController...");
+ let (tx, e_rx) = channel();
+ let (e_tx, rx) = channel();
+
+ let handle = thread::spawn(move || {
+ trace!("Subengine thread started !");
+ loop {
+ //TODO manage errors
+ match e_rx.recv().unwrap() {
+ SubengineCommand::Run => engine.run(),
+ SubengineCommand::Stop => return,
+ };
+ e_tx.send(SubengineResponse::Done).unwrap();
+ }
+ });
+
+ SubengineController {
+ tx,
+ rx,
+ handle: ManuallyDrop::new(handle),
+ }
+ }
+
+ /// Sends a command to the [`Subengine`]. This is function is not blocking and WILL NOT check if
+ /// the command was received.
+ pub fn exec(&self, cmd: SubengineCommand) {
+ self.tx.send(cmd).unwrap();
+ }
+
+ /// Blocking function, waits for the [`Subengine`] to finish executing the last command given.
+ pub fn wait_for_exec(&self, timeout: Duration) -> Result<(),SubengineWaitError> {
+ match self.rx.recv_timeout(timeout) {
+ Err(_) => Err(SubengineWaitError::NoResponse),
+ Ok(val) => match val {
+ SubengineResponse::Done => Ok(()),
+ //_ => Err(SubengineWaitError::BadResponse),
+ }}
+ }
+}
+
+//--SubengineCommand enum---------------------------------------------------------------------------
+/// Commands that can be sent to an [`Subengine`] via an `EngineController`.
+#[derive(Debug)]
+pub enum SubengineCommand {
+ Run,
+ Stop,
+}
+
+//--SubengineWaitError enum-------------------------------------------------------------------------
+/// Errors that can be returned by the wait_for_exec function.
+#[derive(Debug)]
+pub enum SubengineWaitError {
+ NoResponse,
+ BadResponse,
+}
+
+//--SubengineResponse enum--------------------------------------------------------------------------
+#[derive(Debug)]
+enum SubengineResponse {
+ Done,
+}
+
+//--Tests-------------------------------------------------------------------------------------------
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::subengine::TestSubengine;
+
+ #[test]
+ fn test_new_drop() {
+ let (test_subengine, _test_rx) = TestSubengine::new("run");
+ let subengine_controller = SubengineController::new(test_subengine);
+ }
+
+ #[test]
+ fn test_exec() {
+ let (test_subengine, test_rx) = TestSubengine::new("run");
+ let subengine_controller = SubengineController::new(test_subengine);
+
+ subengine_controller.exec(SubengineCommand::Run);
+ let response = test_rx.recv_timeout(Duration::from_millis(10)).unwrap();
+ assert_eq!(response, "run");
+ }
+
+ #[test]
+ fn test_wait_for_exec() {
+ let (test_subengine, _test_rx) = TestSubengine::new("run");
+ let subengine_controller = SubengineController::new(test_subengine);
+
+ subengine_controller.exec(SubengineCommand::Run);
+ subengine_controller.wait_for_exec(Duration::from_millis(10)).unwrap();
+ }
+
+}
diff --git a/src/subengine/subengine_pipeline.rs b/src/subengine/subengine_pipeline.rs
new file mode 100644
index 0000000..dc5ad08
--- /dev/null
+++ b/src/subengine/subengine_pipeline.rs
@@ -0,0 +1,72 @@
+#[allow(unused_imports)]
+use log::{debug, error, info, trace, warn};
+
+use std::{
+ cell::RefCell,
+ marker::PhantomData,
+};
+
+use gfx_backend_vulkan as vk_back;
+
+use crate::{
+ io::{Input, Output},
+ subengine::SubengineController,
+ renderer::Renderer,
+};
+
+//--SubenginePipeline Implementation----------------------------------------------------------------
+#[derive(Debug)]
+pub struct SubenginePipeline<'a, I, W, O>
+where
+ I: Input,
+ W: raw_window_handle::HasRawWindowHandle,
+ O: Output,
+{
+ pub inputs: Vec<&'a RefCell>,
+ pub subengines: Vec>,
+ pub renderers: Vec<(Renderer, &'a RefCell)>,
+ pub phantom: PhantomData, //needed because of compiler limitations
+}
+
+impl<'a, I, W, O> SubenginePipeline<'a, I, W, O>
+where
+ I: Input,
+ W: raw_window_handle::HasRawWindowHandle,
+ O: Output,
+{
+
+ pub fn new(inputs: Ii, subengines: Is, renderers: Ir)
+ -> SubenginePipeline<'a, I, W, O>
+ where
+ Ii: IntoIterator
- >,
+ Iss: IntoIterator
- ,
+ Is: IntoIterator
- ,
+ Ir: IntoIterator
- , &'a RefCell)>,
+ {
+ let inputs_vec = inputs
+ .into_iter()
+ .map(|input| input)
+ .collect();
+
+ let subengines_vecs = subengines
+ .into_iter()
+ .map(|subengines_vec| subengines_vec
+ .into_iter()
+ .map(|subengine| subengine)
+ .collect())
+ .collect();
+
+ let renderers_vec = renderers
+ .into_iter()
+ .map(|renderer| renderer)
+ .collect();
+
+ SubenginePipeline{
+ inputs: inputs_vec,
+ subengines: subengines_vecs,
+ renderers: renderers_vec,
+ phantom: PhantomData,
+ }
+ }
+}
+
diff --git a/src/subengine/test_subengine.rs b/src/subengine/test_subengine.rs
new file mode 100644
index 0000000..ac32da6
--- /dev/null
+++ b/src/subengine/test_subengine.rs
@@ -0,0 +1,36 @@
+#[allow(unused_imports)]
+use log::{debug, error, info, trace, warn};
+
+use std::sync::mpsc::{Sender, Receiver, channel};
+
+use super::*;
+
+//--TestSubengine Implementation--------------------------------------------------------------------
+/// [`Subengine`] implementation used for tests.
+///
+/// Simply sends back the given [`str`] depending on the command.
+#[derive(Debug)]
+pub struct TestSubengine {
+ tx: Sender<&'static str>,
+ run: &'static str,
+}
+
+impl TestSubengine {
+ /// Creates a `TestSubengine` with specific [`str`]s to use.
+ pub fn new(run: &'static str)
+ -> (TestSubengine, Receiver<&'static str>) {
+
+ let (tx, rx) = channel();
+ (
+ TestSubengine { tx, run },
+ rx
+ )
+ }
+}
+
+impl Subengine for TestSubengine {
+ fn run(&self) {
+ self.tx.send(self.run).unwrap();
+ }
+}
+
diff --git a/src/winit_window.rs b/src/winit_window.rs
index c08320a..ac8b605 100644
--- a/src/winit_window.rs
+++ b/src/winit_window.rs
@@ -1,6 +1,11 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
+use std::{
+ thread,
+ sync::mpsc,
+};
+
use winit::{
dpi::PhysicalSize,
event_loop::{EventLoop},
@@ -8,8 +13,10 @@ use winit::{
};
//TODO fix that
-use super::renderer::output::Output;
-use crate::utils::Rect;
+use crate::{
+ io::{Output, Input, Key, ReadError},
+ utils::Rect,
+};
#[derive(Debug)]
pub struct WinitWindow {
@@ -17,27 +24,77 @@ pub struct WinitWindow {
size: Rect,
id: usize,
- pub event_loop: EventLoop<()>,
+ handle: thread::JoinHandle<()>,
+ receiver: mpsc::Receiver,
window: Window,
}
impl WinitWindow {
pub fn new(title: &str, size: Rect) -> Result {
+ use winit::platform::unix::EventLoopExtUnix;
+ debug!("Creating window");
let name = title.to_string();
let id = 0;
- let event_loop = EventLoop::new();
- let window = WindowBuilder::new()
- .with_inner_size(PhysicalSize {width: size.w, height: size.h})
- .with_title(title)
- .build(&event_loop)
- .map_err(|_| "Could not create window")?;
+
+ //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 handle = 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
+ //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();
+
+ trace!("Sending Window back to main thread");
+ tmp_tx.send(window).unwrap();
+
+ //TODO clean event type
+ event_loop.run(move |event: winit::event::Event<'_, ()>, _, control_flow| {
+ use winit::{
+ event_loop::ControlFlow,
+ event::Event,
+ event,
+ };
+
+ *control_flow = ControlFlow::Wait;
+
+ //TODO manage errors
+ match event {
+ Event::WindowEvent{window_id: _, event} => match event {
+ event::WindowEvent::CloseRequested => {
+ tx.send(Key::Close).unwrap();
+ warn!("Stop input thread");
+ *control_flow = ControlFlow::Exit;
+ },
+ event::WindowEvent::CursorMoved{position, ..} => {
+ tx.send(Key::MouseMove{
+ x: position.x,
+ y: position.y,
+ }).unwrap();
+ },
+ _ => (),
+ }
+ _ => (),
+ }})
+ });
+
+ let window = tmp_rx.recv().unwrap();
+ trace!("Received Window in main thread");
Ok(Self {
name,
size,
id,
- event_loop,
+ handle: handle,
+ receiver: rx,
window,
})
}
@@ -52,3 +109,14 @@ impl Output for WinitWindow {
fn window(&self) -> &Window { &self.window }
}
+impl Input for WinitWindow {
+ fn read(&self, timeout_ms: u32) -> Result {
+ use std::time::Duration;
+
+ match self.receiver.recv_timeout(Duration::from_millis(timeout_ms.into())) {
+ Ok(key) => Ok(key),
+ Err(_) => Err(ReadError::Timeout),
+ }
+ }
+}
+