From f7a455997d037ff99991fcbb2af5c483a8945163 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 30 Jun 2023 01:17:06 -0400 Subject: [PATCH] use the graph system dispatcher to execute systems, make it easier to add systems --- src/ecs/mod.rs | 90 +++++++++++++++++++++++++------------------------- src/game.rs | 43 +++++++++++++++--------- src/main.rs | 28 +--------------- 3 files changed, 74 insertions(+), 87 deletions(-) diff --git a/src/ecs/mod.rs b/src/ecs/mod.rs index 4d7de4d..5547243 100755 --- a/src/ecs/mod.rs +++ b/src/ecs/mod.rs @@ -9,48 +9,40 @@ pub mod components; /// A trait that represents a simple system pub trait SimpleSystem { + fn setup(&mut self, world: &mut World) -> anyhow::Result<()> { + Ok(()) + } + // todo: make async? fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()>; } -pub type SystemFn = dyn Fn(&mut World) -> anyhow::Result<()>; - -pub struct SystemFnExecutor { - systems: HashMap>, -} - -impl SystemFnExecutor { - pub fn new() -> Self { - Self { - systems: HashMap::new(), - } - } - - pub fn with_systems(systems: HashMap>) -> Self { - Self { - systems, - } - } - - pub fn add_system(&mut self, system_label: &str, system: Box) { - self.systems.insert(system_label.to_string(), system); - } - - pub fn remove_system(&mut self, system_label: &str) { - self.systems.remove(&system_label.to_string()); - } -} - -impl SimpleSystem for SystemFnExecutor { +impl SimpleSystem for S + where S: FnMut(&mut World) -> anyhow::Result<()> +{ fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { - for system in self.systems.iter() { - let system_fn = system.1.as_ref(); - let res = system_fn(world); + self(world) + } +} - // log an error returned by the system - if let Err(e) = res { - warn!("System execution of {} resulted in an error! '{}'", system.0, e); - } +/// A system that executes a batch of systems in order that they were given. +pub struct BatchedSystem { + systems: Vec>, +} + +impl BatchedSystem { + /// Create a new BatchedSystems. The systems will be executed in the order given. + pub fn new(systems: Vec>) -> Self { + Self { + systems + } + } +} + +impl SimpleSystem for BatchedSystem { + fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { + for system in self.systems.iter_mut() { + system.execute_mut(world)?; } Ok(()) @@ -67,12 +59,16 @@ struct SystemGraphEdge { } -pub struct SystemGraphExecutor { +/// Dispatcher for multiple systems. +/// +/// This struct uses a graph for finding executing systems with dependencies. +/// At some point this will be multithreaded. +pub struct SystemDispatcher { graph: StableDiGraph, node_refs: HashMap, } -impl Default for SystemGraphExecutor { +impl Default for SystemDispatcher { fn default() -> Self { Self { graph: StableDiGraph::new(), @@ -81,7 +77,7 @@ impl Default for SystemGraphExecutor { } } -impl SystemGraphExecutor { +impl SystemDispatcher { pub fn new() -> Self { Self::default() } @@ -92,11 +88,11 @@ impl SystemGraphExecutor { /// and will cause a panic later on in the engine. /// /// TODO: Return false if a cycle was created, making it impossible to know when to dispatch them. This will mean that the System would not of had been added. - pub fn add_system(&mut self, name: String, system: Box, dependencies: &[String]) -> bool { + pub fn add_system(&mut self, name: String, system: Box, dependencies: Vec) -> bool { let added_index = self.graph.add_node(SystemGraphNode { name: name.clone(), - dependent_on: dependencies.clone().to_vec(), + dependent_on: dependencies.clone(), system, }); @@ -106,7 +102,7 @@ impl SystemGraphExecutor { // find the dependencies in node_refs and add edges directed towards the new node for depend in dependencies.into_iter() { let idx = self.node_refs - .get(depend) + .get(&depend) .expect("Dependency not found!"); self.graph.add_edge(idx.clone(), added_index, SystemGraphEdge { }); @@ -115,7 +111,7 @@ impl SystemGraphExecutor { true } - pub(crate) fn execute_mut(&mut self, world: &mut World) { + pub(crate) fn execute_systems(&mut self, world: &mut World) { let mut topo = Topo::new(&self.graph); while let Some(nx) = topo.next(&self.graph) { @@ -125,6 +121,10 @@ impl SystemGraphExecutor { Ok(()) => {}, Err(e) => { warn!("System execution of {} resulted in an error! '{}'.", node.name, e); + + // TODO: Find some way to stop traversing down a topopath in the graph executor and continue down a different one. + // This might require a custom topopath implementation (preferably iterative). It would have to ensure that it + // doesn't run other systems that are dependent on that failed system. return; } } @@ -132,9 +132,9 @@ impl SystemGraphExecutor { } } -impl SimpleSystem for SystemGraphExecutor { +impl SimpleSystem for SystemDispatcher { fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { - SystemGraphExecutor::execute_mut(self, world); + self.execute_systems(world); Ok(()) } diff --git a/src/game.rs b/src/game.rs index 323e077..4c56970 100755 --- a/src/game.rs +++ b/src/game.rs @@ -4,7 +4,7 @@ use async_std::{task::block_on, sync::Mutex}; use hecs::World; use instant::Instant; -use tracing::{metadata::LevelFilter, info, debug, warn}; +use tracing::{metadata::LevelFilter, info, debug, warn, error}; use tracing_subscriber::{ layer::{Layer, SubscriberExt}, filter::FilterFn, @@ -13,7 +13,7 @@ use tracing_subscriber::{ use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}}; -use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SystemFnExecutor, SimpleSystem, SystemGraphExecutor}}; +use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SimpleSystem, SystemDispatcher}}; struct TickCounter { counter: u32, @@ -76,20 +76,20 @@ struct GameLoop { world: Arc>, /// higher priority systems - engine_systems: Vec>, - systems: Vec>, + engine_sys_dispatcher: SystemDispatcher, + user_sys_dispatcher: SystemDispatcher, fps_counter: TickCounter, } impl GameLoop { - pub async fn new(window: Arc, world: Arc>, user_systems: Vec>) -> GameLoop { + pub async fn new(window: Arc, world: Arc>, user_systems: SystemDispatcher) -> GameLoop { Self { window: Arc::clone(&window), renderer: Box::new(BasicRenderer::create_with_window(window).await), world, - engine_systems: vec![], - systems: user_systems, + engine_sys_dispatcher: SystemDispatcher::new(), + user_sys_dispatcher: user_systems, fps_counter: TickCounter::new(), } } @@ -105,13 +105,20 @@ impl GameLoop { async fn update(&mut self) { let mut world = self.world.lock().await; - for esystem in self.engine_systems.iter_mut() { + if let Err(e) = self.engine_sys_dispatcher.execute_mut(&mut world) { + error!("Error when executing engine ecs systems: '{}'", e); + } + + if let Err(e) = self.user_sys_dispatcher.execute_mut(&mut world) { + error!("Error when executing user ecs systems: '{}'", e); + } + /* for esystem in self.engine_systems.iter_mut() { esystem.execute_mut(&mut world).unwrap(); } for system in self.systems.iter_mut() { system.execute_mut(&mut world).unwrap(); - } + } */ } async fn input_update(&mut self, event: &InputEvent) -> Option { @@ -218,14 +225,14 @@ impl GameLoop { pub struct Game { world: Option>>, - systems: Option>> + system_dispatcher: Option } impl Default for Game { fn default() -> Self { Self { world: None, - systems: Some(vec![]), + system_dispatcher: Some(SystemDispatcher::new()), } } } @@ -259,12 +266,18 @@ impl Game { self } - pub fn with_system(&mut self, system: S) -> &mut Self + pub fn with_system(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self where S: SimpleSystem + 'static { - let systems = self.systems.as_mut().unwrap(); - systems.push(Box::new(system)); + + let depends: Vec = depends + .iter() + .map(|s| s.to_string()) + .collect(); + + let dispatcher = self.system_dispatcher.as_mut().unwrap(); + dispatcher.add_system(name.to_string(), Box::new(system), depends); self } @@ -275,7 +288,7 @@ impl Game { let event_loop = EventLoop::new(); let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let systems = self.systems.take().unwrap(); + let systems = self.system_dispatcher.take().unwrap(); let mut g_loop = GameLoop::new(Arc::clone(&window), world, systems).await; diff --git a/src/main.rs b/src/main.rs index 3ffceb4..754013c 100755 --- a/src/main.rs +++ b/src/main.rs @@ -12,12 +12,10 @@ use game::Game; use hecs::World; use tracing::debug; -use crate::ecs::SystemFnExecutor; use crate::render::material::Material; use crate::render::texture::Texture; use crate::ecs::components::camera::CameraComponent; use crate::math::{Angle, Transform}; -//use specs::*; #[derive(Debug, Default)] struct Point2d { @@ -100,33 +98,9 @@ async fn main() { Ok(()) }; - - let mut fn_exec = SystemFnExecutor::new(); - fn_exec.add_system("jiggle", Box::new(jiggle_system)); Game::initialize().await .with_world(world) - .with_system(fn_exec) + .with_system("jiggle", jiggle_system, &[]) .run().await; - - /* world.register::(); - world.register::(); - - world.create_entity() - .with(Point2d::new(10, 10)) - .with(Point3d::new(50, 50, 50)) - .build(); - - let mut dispatcher = DispatcherBuilder::new().with(TestingSystem, "testing_system", &[]).build(); - dispatcher.setup(&mut world); - - //dispatcher.dispatch(&mut world); - - Game::initialize().await - .with_world(world) - .with_dispatcher(dispatcher) - .run().await; */ - - /* let mut game = Game::initialize().await; - game.run().await; */ }