engine: move winit ApplicationHandler to winit plugin

This commit is contained in:
SeanOMik 2024-09-19 17:30:30 -04:00
parent 8b1077cab7
commit 2107b8f7b0
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
3 changed files with 115 additions and 158 deletions

View File

@ -1,10 +1,8 @@
use std::{cell::OnceCell, collections::VecDeque, ptr::NonNull, sync::Arc}; use std::{cell::OnceCell, collections::VecDeque, ptr::NonNull};
use async_std::task::block_on;
use lyra_ecs::{system::{IntoSystem, System}, ResourceObject, World}; use lyra_ecs::{system::{IntoSystem, System}, ResourceObject, World};
use lyra_math::{IVec2, Vec2}; use lyra_math::IVec2;
use rustc_hash::FxHashMap; use tracing::{info, error, Level};
use tracing::{debug, debug_span, info, warn, error, Level};
use tracing_appender::non_blocking; use tracing_appender::non_blocking;
use tracing_subscriber::{ use tracing_subscriber::{
layer::SubscriberExt, layer::SubscriberExt,
@ -12,9 +10,7 @@ use tracing_subscriber::{
util::SubscriberInitExt, fmt, util::SubscriberInitExt, fmt,
}; };
use winit::{application::ApplicationHandler, event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, NamedKey}, window::{Window, WindowId}}; use crate::{plugin::Plugin, render::renderer::Renderer, Stage, StagedExecutor};
use crate::{change_tracker::Ct, input::InputEvent, plugin::Plugin, render::{renderer::{BasicRenderer, Renderer}, window::{PrimaryWindow, WindowOptions}}, winit::WinitWindows, EventQueue, Stage, StagedExecutor};
#[derive(Clone, Copy, Hash, Debug)] #[derive(Clone, Copy, Hash, Debug)]
pub enum GameStages { pub enum GameStages {
@ -54,8 +50,7 @@ impl WindowState {
} }
pub struct App { pub struct App {
windows: FxHashMap<WindowId, Arc<Window>>, pub(crate) renderer: OnceCell<Box<dyn Renderer>>,
renderer: OnceCell<Box<dyn Renderer>>,
pub world: World, pub world: World,
plugins: VecDeque<Box<dyn Plugin>>, plugins: VecDeque<Box<dyn Plugin>>,
startup_systems: VecDeque<Box<dyn System>>, startup_systems: VecDeque<Box<dyn System>>,
@ -97,7 +92,6 @@ impl App {
staged.add_stage_after(GameStages::PostUpdate, GameStages::Last); staged.add_stage_after(GameStages::PostUpdate, GameStages::Last);
Self { Self {
windows: FxHashMap::default(),
renderer: OnceCell::new(), renderer: OnceCell::new(),
world, world,
plugins: Default::default(), plugins: Default::default(),
@ -115,13 +109,13 @@ impl App {
} }
} }
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { pub(crate) fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.renderer.get_mut() self.renderer.get_mut()
.expect("renderer was not initialized") .expect("renderer was not initialized")
.on_resize(&mut self.world, new_size); .on_resize(&mut self.world, new_size);
} }
fn on_exit(&mut self) { pub(crate) fn on_exit(&mut self) {
info!("On exit!"); info!("On exit!");
} }
@ -221,109 +215,3 @@ impl App {
f(self); f(self);
} }
} }
impl ApplicationHandler for App {
fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
debug!("update now");
self.update();
let renderer = self.renderer.get_mut().expect("renderer was not initialized");
renderer.prepare(&mut self.world);
if let Some(mut event_queue) = self.world.try_get_resource_mut::<EventQueue>() {
event_queue.update_events();
}
match renderer.render() {
Ok(_) => {}
// Reconfigure the surface if lost
//Err(wgpu::SurfaceError::Lost) => self.on_resize(.surface_size()),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
error!("OOM");
event_loop.exit();
}
// All other errors (Outdated, Timeout) should be resolved by the next frame
Err(e) => eprintln!("{:?}", e),
}
}
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
let world = &mut self.world;
let en = world.spawn((WindowOptions::default(), PrimaryWindow));
let attr = Window::default_attributes();
let mut windows = world.get_resource_mut::<WinitWindows>();
let wid = windows.create_window(event_loop, en, attr).unwrap();
let window = windows.windows.get(&wid).unwrap().clone();
drop(windows);
let renderer = block_on(BasicRenderer::create_with_window(world, window));
if self.renderer.set(Box::new(renderer)).is_err() {
warn!("renderer was re-initialized");
}
}
fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
window_id: winit::window::WindowId,
event: WindowEvent,
) {
//let _e = debug_span!("window_event", window=window_id).entered();
let windows = self.world.get_resource::<WinitWindows>();
let window = match windows.windows.get(&window_id) {
Some(w) => w.clone(),
None => return,
};
drop(windows);
// If try_from failed, that means that the WindowEvent is not an
// input related event.
if let Some(input_ev) = InputEvent::from_window_event(&event) {
// Its possible to receive multiple input events before the update event for
// the InputSystem is called, so we must use a queue for the events.
if let Some(mut event_queue) = self.world.try_get_resource_mut::<EventQueue>() {
event_queue.trigger_event(input_ev.clone());
}
} else {
match event {
WindowEvent::ActivationTokenDone { serial, token } => todo!(),
WindowEvent::Resized(physical_size) => {
self.on_resize(physical_size);
},
WindowEvent::Moved(physical_position) => {
let mut state = self.world.get_resource_or_else(WindowState::new);
state.position = IVec2::new(physical_position.x, physical_position.y);
},
WindowEvent::CloseRequested => {
self.on_exit();
event_loop.exit();
},
WindowEvent::Destroyed => todo!(),
WindowEvent::DroppedFile(path_buf) => todo!(),
WindowEvent::HoveredFile(path_buf) => todo!(),
WindowEvent::HoveredFileCancelled => todo!(),
WindowEvent::Focused(focused) => {
let mut state = self.world.get_resource_or_else(WindowState::new);
state.focused = focused;
},
WindowEvent::ModifiersChanged(modifiers) => debug!("modifiers changed: {:?}", modifiers),
WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer } => {
info!("changed scale to {scale_factor}");
},
WindowEvent::ThemeChanged(theme) => todo!(),
WindowEvent::Occluded(occ) => {
let mut state = self.world.get_resource_or_else(WindowState::new);
state.occluded = occ;
},
WindowEvent::RedrawRequested => {
debug!("should redraw");
},
_ => {}
}
}
}
}

View File

@ -1,9 +1,3 @@
use std::cell::Cell;
use std::cell::OnceCell;
use std::cell::RefCell;
use std::ptr::NonNull;
use lyra_ecs::system::System;
use lyra_ecs::CommandQueue; use lyra_ecs::CommandQueue;
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
@ -41,18 +35,6 @@ impl<P> Plugin for P
} }
} }
/// An ECS system converted to a plugin.
///
/// The system is executed in plugin setup phase.
pub(crate) struct SystemPlugin<S: System>(pub RefCell<S>);
impl<S: System> Plugin for SystemPlugin<S> {
fn setup(&self, app: &mut App) {
let mut s = self.0.borrow_mut();
s.execute(NonNull::from(&app.world));
}
}
/// Represents a set of plugins that will be executed in order they are supplied. /// Represents a set of plugins that will be executed in order they are supplied.
#[derive(Default)] #[derive(Default)]
pub struct PluginSet { pub struct PluginSet {

View File

@ -1,11 +1,13 @@
use std::sync::Arc; use std::sync::Arc;
use async_std::task::block_on;
use glam::IVec2;
use lyra_ecs::Entity; use lyra_ecs::Entity;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use tracing::debug; use tracing::{debug, error, info, warn};
use winit::{application::ApplicationHandler, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowAttributes, WindowId}}; use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowAttributes, WindowId}};
use crate::{game::App, plugin::Plugin, render::window::WindowOptions}; use crate::{game::{App, WindowState}, input::InputEvent, plugin::Plugin, render::{renderer::BasicRenderer, window::{PrimaryWindow, WindowOptions}}, EventQueue};
#[derive(Default)] #[derive(Default)]
pub struct WinitPlugin; pub struct WinitPlugin;
@ -16,15 +18,15 @@ impl Plugin for WinitPlugin {
app.add_resource(WinitWindows::default()); app.add_resource(WinitWindows::default());
} }
fn is_ready(&self, app: &mut crate::game::App) -> bool { fn is_ready(&self, _app: &mut crate::game::App) -> bool {
true true
} }
fn complete(&self, app: &mut crate::game::App) { fn complete(&self, _app: &mut crate::game::App) {
} }
fn cleanup(&self, app: &mut crate::game::App) { fn cleanup(&self, _app: &mut crate::game::App) {
} }
} }
@ -52,37 +54,122 @@ impl WinitWindows {
} }
} }
pub fn winit_app_runner(mut app: App) { pub fn winit_app_runner(app: App) {
let evloop = EventLoop::new() let evloop = EventLoop::new()
.expect("failed to create winit EventLoop"); .expect("failed to create winit EventLoop");
evloop.run_app(&mut app) let mut winit_runner = WinitRunner {
app,
};
evloop.run_app(&mut winit_runner)
.expect("loop error"); .expect("loop error");
} }
/* struct WinitRunner { struct WinitRunner {
app: App app: App
} }
impl ApplicationHandler for WinitRunner { impl ApplicationHandler for WinitRunner {
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
debug!("resumed") debug!("update now");
self.app.update();
let renderer = self.app.renderer.get_mut().expect("renderer was not initialized");
renderer.prepare(&mut self.app.world);
if let Some(mut event_queue) = self.app.world.try_get_resource_mut::<EventQueue>() {
event_queue.update_events();
}
match renderer.render() {
Ok(_) => {}
// Reconfigure the surface if lost
//Err(wgpu::SurfaceError::Lost) => self.on_resize(.surface_size()),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
error!("OOM");
event_loop.exit();
}
// All other errors (Outdated, Timeout) should be resolved by the next frame
Err(e) => eprintln!("{:?}", e),
}
}
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
let world = &mut self.app.world;
let en = world.spawn((WindowOptions::default(), PrimaryWindow));
let attr = Window::default_attributes();
let mut windows = world.get_resource_mut::<WinitWindows>();
let wid = windows.create_window(event_loop, en, attr).unwrap();
let window = windows.windows.get(&wid).unwrap().clone();
drop(windows);
let renderer = block_on(BasicRenderer::create_with_window(world, window));
if self.app.renderer.set(Box::new(renderer)).is_err() {
warn!("renderer was re-initialized");
}
} }
fn window_event( fn window_event(
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &winit::event_loop::ActiveEventLoop,
window_id: WindowId, window_id: winit::window::WindowId,
event: winit::event::WindowEvent, event: WindowEvent,
) { ) {
let world = &mut self.app.world; let windows = self.app.world.get_resource::<WinitWindows>();
let mut windows = world.get_resource_mut::<WinitWindows>(); let window = match windows.windows.get(&window_id) {
Some(w) => w.clone(),
let window = match windows.windows.get_mut(&window_id) {
Some(w) => w,
None => return, None => return,
}; };
drop(windows);
// If try_from failed, that means that the WindowEvent is not an
// input related event.
if let Some(input_ev) = InputEvent::from_window_event(&event) {
// Its possible to receive multiple input events before the update event for
// the InputSystem is called, so we must use a queue for the events.
if let Some(mut event_queue) = self.app.world.try_get_resource_mut::<EventQueue>() {
event_queue.trigger_event(input_ev.clone());
}
} else {
match event {
WindowEvent::ActivationTokenDone { serial, token } => todo!(),
WindowEvent::Resized(physical_size) => {
self.app.on_resize(physical_size);
},
WindowEvent::Moved(physical_position) => {
let mut state = self.app.world.get_resource_or_else(WindowState::new);
state.position = IVec2::new(physical_position.x, physical_position.y);
},
WindowEvent::CloseRequested => {
self.app.on_exit();
event_loop.exit();
},
WindowEvent::Destroyed => todo!(),
WindowEvent::DroppedFile(path_buf) => todo!(),
WindowEvent::HoveredFile(path_buf) => todo!(),
WindowEvent::HoveredFileCancelled => todo!(),
WindowEvent::Focused(focused) => {
let mut state = self.app.world.get_resource_or_else(WindowState::new);
state.focused = focused;
},
WindowEvent::ModifiersChanged(modifiers) => debug!("modifiers changed: {:?}", modifiers),
WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer } => {
info!("changed scale to {scale_factor}");
},
WindowEvent::ThemeChanged(theme) => todo!(),
WindowEvent::Occluded(occ) => {
let mut state = self.app.world.get_resource_or_else(WindowState::new);
state.occluded = occ;
},
WindowEvent::RedrawRequested => {
debug!("should redraw");
},
_ => {}
}
}
} }
} */ }