#![warn(dead_code)] use std::collections::HashMap; use crate::{ core::{ events::Events, module::{Module, ModulesStack, RenderModule as ThreadLocalModule, RenderModulesStack}, }, modules::graphics::egui::{Gui, GuiConfig}, }; use anyhow::Result; use flax::{Schedule, World}; use vulkano::device::DeviceFeatures; use vulkano_util::{ context::{VulkanoConfig, VulkanoContext}, window::VulkanoWindows, }; use winit::{event::{Event, WindowEvent}, window::WindowId}; #[allow(dead_code)] pub struct App { name: String, modules: ModulesStack, thread_local_modules: RenderModulesStack, world: World, schedule: Schedule, events: Events, rx: flume::Receiver, running: bool, event_cleanup_time: std::time::Duration, vk_context: VulkanoContext, vk_windows: VulkanoWindows, guis: HashMap, } impl App { pub fn new() -> Self { let mut events = Events::new(); let (tx, rx) = flume::unbounded(); events.subscribe_custom(tx); let schedule = Schedule::builder().build(); let vk_config = VulkanoConfig { device_features: DeviceFeatures { dynamic_rendering: true, ..Default::default() }, ..Default::default() }; let vk_context = VulkanoContext::new(vk_config); let vk_windows = VulkanoWindows::default(); Self { name: "Khors".into(), modules: ModulesStack::new(), thread_local_modules: RenderModulesStack::new(), world: World::new(), schedule, events, rx, running: false, event_cleanup_time: std::time::Duration::from_secs(60), vk_context, vk_windows, guis: HashMap::new(), } } pub fn run(&mut self) -> Result<()> { self.running = true; self.schedule.execute_par(&mut self.world).unwrap(); let vk_context = &mut self.vk_context; let vk_windows = &mut self.vk_windows; let world = &mut self.world; let events = &mut self.events; let frame_time = std::time::Duration::from_millis(16); let guis = &mut self.guis; for module in self.modules.iter_mut() { module.on_update(world, events, frame_time)?; } for module in self.thread_local_modules.iter_mut() { module.on_update(guis, vk_context, vk_windows, world, events, frame_time)?; } self.handle_events(); Ok(()) } pub fn create_window(&mut self, event_loop: &winit::event_loop::EventLoopWindowTarget) where T: Clone + Send + Sync, { let window = self.vk_windows.create_window( event_loop, &self.vk_context, &vulkano_util::window::WindowDescriptor { title: self.name.clone(), present_mode: vulkano::swapchain::PresentMode::Fifo, ..Default::default() }, |_| {}, ); let renderer = self.vk_windows.get_renderer(window).unwrap(); let gui = Gui::new( event_loop, renderer.surface().clone(), renderer.graphics_queue().clone(), renderer.swapchain_format(), GuiConfig { is_overlay: true, allow_srgb_render_target: false, ..Default::default() }, ); self.guis.insert(window, gui); } pub fn process_event_loop( &mut self, event: winit::event::Event, _elwt: &winit::event_loop::EventLoopWindowTarget, ) -> Result where T: Clone + Send + Sync, { match &event { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { return Ok(true); } Event::WindowEvent { event: WindowEvent::Focused(_), .. } => self.events().send(event.clone()), Event::WindowEvent { event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. }, window_id, } => self .vk_windows .get_renderer_mut(*window_id) .unwrap() .resize(), Event::WindowEvent { event: WindowEvent::RedrawRequested, window_id, } => 'redraw: { // Tasks for redrawing: // 1. Update state based on events // 2. Compute & Render // 3. Reset input state // 4. Update time & title // The rendering part goes here: match self .vk_windows .get_renderer(*window_id) .unwrap() .window_size() { [w, h] => { // Skip this frame when minimized. if w == 0.0 || h == 0.0 { break 'redraw; } } } self.run()?; } Event::WindowEvent { window_id, event } => { let window = self.vk_windows.get_window(*window_id).unwrap(); let gui = self.guis.get_mut(window_id).unwrap(); gui.update(window, event); } Event::AboutToWait => { for (window_id, _) in self.vk_windows.iter() { self.vk_windows .get_window(*window_id) .unwrap() .request_redraw() } } _ => (), } Ok(false) } pub fn handle_events(&mut self) { for event in self.rx.try_iter() { match event { AppEvent::Exit => self.running = false, } } } #[allow(dead_code)] pub fn set_schedule(&mut self, schedule: Schedule) { self.schedule = schedule; } #[allow(dead_code)] pub fn world(&self) -> &World { &self.world } #[allow(dead_code)] pub fn world_mut(&mut self) -> &mut World { &mut self.world } pub fn events(&self) -> &Events { &self.events } #[allow(dead_code)] pub fn events_mut(&mut self) -> &mut Events { &mut self.events } /// Pushes a module from the provided init closure to to the top of the layer stack. The provided /// closure to construct the layer takes in the world and events. pub fn push_render_module(&mut self, func: F) where F: FnOnce( &mut VulkanoContext, &mut VulkanoWindows, &mut Schedule, &mut World, &mut Events, ) -> T, T: 'static + ThreadLocalModule, { let module = func( &mut self.vk_context, &mut self.vk_windows, &mut self.schedule, &mut self.world, &mut self.events, ); self.thread_local_modules.push(module); } /// Pushes a layer from the provided init closure to to the top of the layer stack. The provided /// closure to construct the layer takes in the world and events. pub fn push_module(&mut self, func: F) where F: FnOnce(&mut Schedule, &mut World, &mut Events) -> T, T: 'static + Module, { let module = func(&mut self.schedule, &mut self.world, &mut self.events); self.modules.push(module); } /// Pushes a module from the provided init closure to to the top of the module stack. The provided /// closure to construct the module takes in the world and events, and may return an error which /// is propagated to the callee. #[allow(dead_code)] pub fn try_push_module(&mut self, func: F) -> Result<(), E> where F: FnOnce(&mut World, &mut Events) -> Result, T: 'static + Module, { let module = func(&mut self.world, &mut self.events)?; self.modules.push(module); Ok(()) } /// Inserts a module from the provided init closure to to the top of the module stack. The provided /// closure to construct the module takes in the world and events. #[allow(dead_code)] pub fn insert_module(&mut self, index: usize, func: F) where F: FnOnce(&mut World, &mut Events) -> T, T: 'static + Module, { let module = func(&mut self.world, &mut self.events); self.modules.insert(index, module); } /// Pushes a module from the provided init closure to to the top of the module stack. The provided /// closure to construct the module takes in the world and events, and may return an error which /// is propagated to the callee. #[allow(dead_code)] pub fn try_insert_module(&mut self, index: usize, func: F) -> Result<(), E> where F: FnOnce(&mut World, &mut Events) -> Result, T: 'static + Module, { let module = func(&mut self.world, &mut self.events)?; self.modules.insert(index, module); Ok(()) } } #[derive(Debug, Clone, Copy, PartialEq)] #[allow(dead_code)] pub enum AppEvent { Exit, } impl Default for App { fn default() -> Self { Self::new() } }