use the graph system dispatcher to execute systems, make it easier to add systems
This commit is contained in:
parent
4c6edff639
commit
f7a455997d
|
@ -9,50 +9,42 @@ pub mod components;
|
||||||
|
|
||||||
/// A trait that represents a simple system
|
/// A trait that represents a simple system
|
||||||
pub trait SimpleSystem {
|
pub trait SimpleSystem {
|
||||||
|
fn setup(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// todo: make async?
|
// todo: make async?
|
||||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()>;
|
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SystemFn = dyn Fn(&mut World) -> anyhow::Result<()>;
|
impl<S> SimpleSystem for S
|
||||||
|
where S: FnMut(&mut World) -> anyhow::Result<()>
|
||||||
pub struct SystemFnExecutor {
|
{
|
||||||
systems: HashMap<String, Box<SystemFn>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SystemFnExecutor {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
systems: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_systems(systems: HashMap<String, Box<SystemFn>>) -> Self {
|
|
||||||
Self {
|
|
||||||
systems,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_system(&mut self, system_label: &str, system: Box<SystemFn>) {
|
|
||||||
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 {
|
|
||||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||||
for system in self.systems.iter() {
|
self(world)
|
||||||
let system_fn = system.1.as_ref();
|
|
||||||
let res = system_fn(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<Box<dyn SimpleSystem>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BatchedSystem {
|
||||||
|
/// Create a new BatchedSystems. The systems will be executed in the order given.
|
||||||
|
pub fn new(systems: Vec<Box<dyn SimpleSystem>>) -> 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(())
|
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<SystemGraphNode, SystemGraphEdge>,
|
graph: StableDiGraph<SystemGraphNode, SystemGraphEdge>,
|
||||||
node_refs: HashMap<String, NodeIndex>,
|
node_refs: HashMap<String, NodeIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SystemGraphExecutor {
|
impl Default for SystemDispatcher {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
graph: StableDiGraph::new(),
|
graph: StableDiGraph::new(),
|
||||||
|
@ -81,7 +77,7 @@ impl Default for SystemGraphExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemGraphExecutor {
|
impl SystemDispatcher {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
@ -92,11 +88,11 @@ impl SystemGraphExecutor {
|
||||||
/// and will cause a panic later on in the engine.
|
/// 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.
|
/// 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<dyn SimpleSystem>, dependencies: &[String]) -> bool {
|
pub fn add_system(&mut self, name: String, system: Box<dyn SimpleSystem>, dependencies: Vec<String>) -> bool {
|
||||||
let added_index =
|
let added_index =
|
||||||
self.graph.add_node(SystemGraphNode {
|
self.graph.add_node(SystemGraphNode {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
dependent_on: dependencies.clone().to_vec(),
|
dependent_on: dependencies.clone(),
|
||||||
system,
|
system,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -106,7 +102,7 @@ impl SystemGraphExecutor {
|
||||||
// find the dependencies in node_refs and add edges directed towards the new node
|
// find the dependencies in node_refs and add edges directed towards the new node
|
||||||
for depend in dependencies.into_iter() {
|
for depend in dependencies.into_iter() {
|
||||||
let idx = self.node_refs
|
let idx = self.node_refs
|
||||||
.get(depend)
|
.get(&depend)
|
||||||
.expect("Dependency not found!");
|
.expect("Dependency not found!");
|
||||||
|
|
||||||
self.graph.add_edge(idx.clone(), added_index, SystemGraphEdge { });
|
self.graph.add_edge(idx.clone(), added_index, SystemGraphEdge { });
|
||||||
|
@ -115,7 +111,7 @@ impl SystemGraphExecutor {
|
||||||
true
|
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);
|
let mut topo = Topo::new(&self.graph);
|
||||||
|
|
||||||
while let Some(nx) = topo.next(&self.graph) {
|
while let Some(nx) = topo.next(&self.graph) {
|
||||||
|
@ -125,6 +121,10 @@ impl SystemGraphExecutor {
|
||||||
Ok(()) => {},
|
Ok(()) => {},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("System execution of {} resulted in an error! '{}'.", node.name, 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;
|
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<()> {
|
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||||
SystemGraphExecutor::execute_mut(self, world);
|
self.execute_systems(world);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
43
src/game.rs
43
src/game.rs
|
@ -4,7 +4,7 @@ use async_std::{task::block_on, sync::Mutex};
|
||||||
|
|
||||||
use hecs::World;
|
use hecs::World;
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
use tracing::{metadata::LevelFilter, info, debug, warn};
|
use tracing::{metadata::LevelFilter, info, debug, warn, error};
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
layer::{Layer, SubscriberExt},
|
layer::{Layer, SubscriberExt},
|
||||||
filter::FilterFn,
|
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 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 {
|
struct TickCounter {
|
||||||
counter: u32,
|
counter: u32,
|
||||||
|
@ -76,20 +76,20 @@ struct GameLoop {
|
||||||
|
|
||||||
world: Arc<Mutex<World>>,
|
world: Arc<Mutex<World>>,
|
||||||
/// higher priority systems
|
/// higher priority systems
|
||||||
engine_systems: Vec<Box<dyn SimpleSystem>>,
|
engine_sys_dispatcher: SystemDispatcher,
|
||||||
systems: Vec<Box<dyn SimpleSystem>>,
|
user_sys_dispatcher: SystemDispatcher,
|
||||||
fps_counter: TickCounter,
|
fps_counter: TickCounter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameLoop {
|
impl GameLoop {
|
||||||
pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>, user_systems: Vec<Box<dyn SimpleSystem>>) -> GameLoop {
|
pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>, user_systems: SystemDispatcher) -> GameLoop {
|
||||||
Self {
|
Self {
|
||||||
window: Arc::clone(&window),
|
window: Arc::clone(&window),
|
||||||
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
||||||
|
|
||||||
world,
|
world,
|
||||||
engine_systems: vec![],
|
engine_sys_dispatcher: SystemDispatcher::new(),
|
||||||
systems: user_systems,
|
user_sys_dispatcher: user_systems,
|
||||||
fps_counter: TickCounter::new(),
|
fps_counter: TickCounter::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,13 +105,20 @@ impl GameLoop {
|
||||||
async fn update(&mut self) {
|
async fn update(&mut self) {
|
||||||
let mut world = self.world.lock().await;
|
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();
|
esystem.execute_mut(&mut world).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for system in self.systems.iter_mut() {
|
for system in self.systems.iter_mut() {
|
||||||
system.execute_mut(&mut world).unwrap();
|
system.execute_mut(&mut world).unwrap();
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
|
async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
|
||||||
|
@ -218,14 +225,14 @@ impl GameLoop {
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
world: Option<Arc<Mutex<World>>>,
|
world: Option<Arc<Mutex<World>>>,
|
||||||
systems: Option<Vec<Box<dyn SimpleSystem>>>
|
system_dispatcher: Option<SystemDispatcher>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Game {
|
impl Default for Game {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
world: None,
|
world: None,
|
||||||
systems: Some(vec![]),
|
system_dispatcher: Some(SystemDispatcher::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,12 +266,18 @@ impl Game {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_system<S>(&mut self, system: S) -> &mut Self
|
pub fn with_system<S>(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self
|
||||||
where
|
where
|
||||||
S: SimpleSystem + 'static
|
S: SimpleSystem + 'static
|
||||||
{
|
{
|
||||||
let systems = self.systems.as_mut().unwrap();
|
|
||||||
systems.push(Box::new(system));
|
let depends: Vec<String> = 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
|
self
|
||||||
}
|
}
|
||||||
|
@ -275,7 +288,7 @@ impl Game {
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
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;
|
let mut g_loop = GameLoop::new(Arc::clone(&window), world, systems).await;
|
||||||
|
|
||||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -12,12 +12,10 @@ use game::Game;
|
||||||
use hecs::World;
|
use hecs::World;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::ecs::SystemFnExecutor;
|
|
||||||
use crate::render::material::Material;
|
use crate::render::material::Material;
|
||||||
use crate::render::texture::Texture;
|
use crate::render::texture::Texture;
|
||||||
use crate::ecs::components::camera::CameraComponent;
|
use crate::ecs::components::camera::CameraComponent;
|
||||||
use crate::math::{Angle, Transform};
|
use crate::math::{Angle, Transform};
|
||||||
//use specs::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct Point2d {
|
struct Point2d {
|
||||||
|
@ -101,32 +99,8 @@ async fn main() {
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut fn_exec = SystemFnExecutor::new();
|
|
||||||
fn_exec.add_system("jiggle", Box::new(jiggle_system));
|
|
||||||
|
|
||||||
Game::initialize().await
|
Game::initialize().await
|
||||||
.with_world(world)
|
.with_world(world)
|
||||||
.with_system(fn_exec)
|
.with_system("jiggle", jiggle_system, &[])
|
||||||
.run().await;
|
.run().await;
|
||||||
|
|
||||||
/* world.register::<Point2d>();
|
|
||||||
world.register::<Point3d>();
|
|
||||||
|
|
||||||
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; */
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue