add SchedulePlans

This commit is contained in:
Carter Anderson 2020-04-06 01:57:00 -07:00
parent 1471cc84c5
commit 683a70d8e7
9 changed files with 268 additions and 110 deletions

View file

@ -26,7 +26,7 @@ impl Default for App {
impl App {
pub fn build() -> AppBuilder {
AppBuilder::new()
AppBuilder::default()
}
pub fn update(&mut self) {

View file

@ -1,33 +1,40 @@
use crate::{
default_stage,
plugin::{load_plugin, AppPlugin},
system_stage, App, Events,
schedule_plan::SchedulePlan,
App, Events,
};
use legion::prelude::{Resources, Runnable, Schedulable, Schedule, Universe, World};
use std::collections::HashMap;
use legion::prelude::{Resources, Runnable, Schedulable, Universe, World};
static APP_MISSING_MESSAGE: &str = "This AppBuilder no longer has an App. Check to see if you already called run(). A call to app_builder.run() consumes the AppBuilder's App.";
pub struct AppBuilder {
app: Option<App>,
pub setup_systems: Vec<Box<dyn Schedulable>>,
// TODO: these separate lists will produce incorrect ordering
pub system_stages: HashMap<String, Vec<Box<dyn Schedulable>>>,
pub runnable_stages: HashMap<String, Vec<Box<dyn Runnable>>>,
pub thread_local_stages: HashMap<String, Vec<Box<dyn FnMut(&mut World, &mut Resources)>>>,
pub stage_order: Vec<String>,
schedule_plan: SchedulePlan,
startup_schedule_plan: SchedulePlan,
}
impl Default for AppBuilder {
fn default() -> Self {
let mut app_builder = AppBuilder {
app: Some(App::default()),
schedule_plan: SchedulePlan::default(),
startup_schedule_plan: SchedulePlan::default(),
};
app_builder.add_default_stages();
app_builder
}
}
impl AppBuilder {
pub fn new() -> Self {
pub fn empty() -> AppBuilder {
AppBuilder {
app: Some(App::default()),
setup_systems: Vec::new(),
system_stages: HashMap::new(),
runnable_stages: HashMap::new(),
thread_local_stages: HashMap::new(),
stage_order: Vec::new(),
}
schedule_plan: SchedulePlan::default(),
startup_schedule_plan: SchedulePlan::default(),
}
}
pub fn app(&self) -> &App {
@ -62,53 +69,21 @@ impl AppBuilder {
&mut self.app_mut().resources
}
pub fn build_and_run_startup_schedule(&mut self) -> &mut Self {
let mut startup_schedule = self.startup_schedule_plan.build();
let app = self.app_mut();
startup_schedule.execute(&mut app.world, &mut app.resources);
self
}
pub fn build_schedule(&mut self) -> &mut Self {
let mut setup_schedule_builder = Schedule::builder();
for setup_system in self.setup_systems.drain(..) {
setup_schedule_builder = setup_schedule_builder.add_system(setup_system);
}
let mut setup_schedule = setup_schedule_builder.build();
let app = self.app_mut();
setup_schedule.execute(&mut app.world, &mut app.resources);
let mut schedule_builder = Schedule::builder();
for stage_name in self.stage_order.iter() {
if let Some((_name, stage_systems)) = self.system_stages.remove_entry(stage_name) {
for system in stage_systems {
schedule_builder = schedule_builder.add_system(system);
}
schedule_builder = schedule_builder.flush();
}
if let Some((_name, stage_runnables)) = self.runnable_stages.remove_entry(stage_name) {
for system in stage_runnables {
schedule_builder = schedule_builder.add_thread_local(system);
}
schedule_builder = schedule_builder.flush();
}
if let Some((_name, stage_thread_locals)) =
self.thread_local_stages.remove_entry(stage_name)
{
for system in stage_thread_locals {
schedule_builder = schedule_builder.add_thread_local_fn(system);
}
schedule_builder = schedule_builder.flush();
}
}
let app = self.app_mut();
app.schedule = Some(schedule_builder.build());
self.app_mut().schedule = Some(self.schedule_plan.build());
self
}
pub fn run(&mut self) {
self.build_schedule();
self.build_and_run_startup_schedule();
self.app.take().unwrap().run();
}
@ -123,15 +98,54 @@ impl AppBuilder {
self
}
pub fn add_system(&mut self, system: Box<dyn Schedulable>) -> &mut Self {
self.add_system_to_stage(system_stage::UPDATE, system)
pub fn add_stage(&mut self, stage_name: &str) -> &mut Self {
self.schedule_plan.add_stage(stage_name);
self
}
pub fn add_setup_system(&mut self, system: Box<dyn Schedulable>) -> &mut Self {
self.setup_systems.push(system);
pub fn add_stage_after(&mut self, target: &str, stage_name: &str) -> &mut Self {
self.schedule_plan.add_stage_after(target, stage_name);
self
}
pub fn add_stage_before(&mut self, target: &str, stage_name: &str) -> &mut Self {
self.schedule_plan.add_stage_before(target, stage_name);
self
}
pub fn add_startup_stage(&mut self, stage_name: &str) -> &mut Self {
self.startup_schedule_plan.add_stage(stage_name);
self
}
pub fn add_system(&mut self, system: Box<dyn Schedulable>) -> &mut Self {
self.add_system_to_stage(default_stage::UPDATE, system)
}
pub fn add_startup_system_to_stage(
&mut self,
stage_name: &str,
system: Box<dyn Schedulable>,
) -> &mut Self {
self.startup_schedule_plan
.add_system_to_stage(stage_name, system);
self
}
pub fn add_startup_system(&mut self, system: Box<dyn Schedulable>) -> &mut Self {
self.startup_schedule_plan
.add_system_to_stage(default_stage::STARTUP, system);
self
}
pub fn add_default_stages(&mut self) -> &mut Self {
self.add_startup_stage(default_stage::STARTUP)
.add_stage(default_stage::FIRST)
.add_stage(default_stage::EVENT_UPDATE)
.add_stage(default_stage::UPDATE)
.add_stage(default_stage::LAST)
}
pub fn build_system<F>(&mut self, build: F) -> &mut Self
where
F: Fn(&mut Resources) -> Box<dyn Schedulable>,
@ -145,49 +159,27 @@ impl AppBuilder {
stage_name: &str,
system: Box<dyn Schedulable>,
) -> &mut Self {
if let None = self.system_stages.get(stage_name) {
self.system_stages
.insert(stage_name.to_string(), Vec::new());
self.stage_order.push(stage_name.to_string());
}
let stages = self.system_stages.get_mut(stage_name).unwrap();
stages.push(system);
self
}
pub fn add_runnable_to_stage(
&mut self,
stage_name: &str,
system: Box<dyn Runnable>,
) -> &mut Self {
if let None = self.runnable_stages.get(stage_name) {
self.runnable_stages
.insert(stage_name.to_string(), Vec::new());
self.stage_order.push(stage_name.to_string());
}
let stages = self.runnable_stages.get_mut(stage_name).unwrap();
stages.push(system);
self.schedule_plan.add_system_to_stage(stage_name, system);
self
}
pub fn add_thread_local_to_stage(
&mut self,
stage_name: &str,
system: Box<dyn Runnable>,
) -> &mut Self {
self.schedule_plan
.add_thread_local_to_stage(stage_name, system);
self
}
pub fn add_thread_local_fn_to_stage(
&mut self,
stage_name: &str,
f: impl FnMut(&mut World, &mut Resources) + 'static,
) -> &mut Self {
if let None = self.thread_local_stages.get(stage_name) {
self.thread_local_stages
.insert(stage_name.to_string(), Vec::new());
// TODO: this is so broken
self.stage_order.push(stage_name.to_string());
}
let thread_local_stages = self.thread_local_stages.get_mut(stage_name).unwrap();
thread_local_stages.push(Box::new(f));
self.schedule_plan
.add_thread_local_fn_to_stage(stage_name, f);
self
}
@ -197,7 +189,7 @@ impl AppBuilder {
{
self.add_resource(Events::<T>::default())
.add_system_to_stage(
system_stage::EVENT_UPDATE,
default_stage::EVENT_UPDATE,
Events::<T>::build_update_system(),
)
}

View file

@ -1,7 +1,6 @@
// TODO: move me
pub const STARTUP: &str = "startup";
pub const FIRST: &str = "first";
pub const EVENT_UPDATE: &str = "event_update";
pub const UPDATE: &str = "update";
pub const RENDER: &str = "render";
pub const LAST: &str = "last";

View file

@ -2,8 +2,9 @@ mod app;
mod app_builder;
mod event;
mod plugin;
pub mod schedule_plan;
pub mod schedule_runner;
pub mod system_stage;
pub mod default_stage;
pub use app::*;
pub use app_builder::*;

View file

@ -0,0 +1,163 @@
use legion::prelude::*;
use std::{cmp::Ordering, collections::HashMap};
enum System {
Schedulable(Box<dyn Schedulable>),
ThreadLocal(Box<dyn Runnable>),
ThreadLocalFn(Box<dyn FnMut(&mut World, &mut Resources)>),
}
#[derive(Default)]
pub struct SchedulePlan {
stages: HashMap<String, Vec<System>>,
stage_order: Vec<String>,
}
impl SchedulePlan {
pub fn build(&mut self) -> Schedule {
let mut schedule_builder = Schedule::builder();
for stage in self.stage_order.drain(..) {
if let Some((_, mut systems)) = self.stages.remove_entry(&stage) {
let system_count = systems.len();
for system in systems.drain(..) {
match system {
System::Schedulable(schedulable) => {
schedule_builder = schedule_builder.add_system(schedulable);
}
System::ThreadLocal(runnable) => {
schedule_builder = schedule_builder.add_thread_local(runnable);
}
System::ThreadLocalFn(thread_local) => {
schedule_builder = schedule_builder.add_thread_local_fn(thread_local);
}
}
}
if system_count > 0 {
schedule_builder = schedule_builder.flush();
}
}
}
schedule_builder.build()
}
pub fn add_stage(&mut self, stage: &str) {
if let Some(_) = self.stages.get(stage) {
panic!("Stage already exists: {}", stage);
} else {
self.stages.insert(stage.to_string(), Vec::new());
self.stage_order.push(stage.to_string());
}
}
pub fn add_stage_after(&mut self, target: &str, stage: &str) {
if let Some(_) = self.stages.get(stage) {
panic!("Stage already exists: {}", stage);
}
let target_index = self
.stage_order
.iter()
.enumerate()
.find(|(_i, stage)| stage.as_str() == target)
.map(|(i, _)| i)
.unwrap_or_else(|| panic!("Target stage does not exist: {}", target));
self.stages.insert(stage.to_string(), Vec::new());
self.stage_order.insert(target_index + 1, stage.to_string());
}
pub fn add_stage_before(&mut self, target: &str, stage: &str) {
if let Some(_) = self.stages.get(stage) {
panic!("Stage already exists: {}", stage);
}
let target_index = self
.stage_order
.iter()
.enumerate()
.find(|(_i, stage)| stage.as_str() == target)
.map(|(i, _)| i)
.unwrap_or_else(|| panic!("Target stage does not exist: {}", target));
self.stages.insert(stage.to_string(), Vec::new());
self.stage_order.insert(target_index, stage.to_string());
}
pub fn add_system_to_stage(
&mut self,
stage_name: &str,
system: Box<dyn Schedulable>,
) -> &mut Self {
let system = System::Schedulable(system);
let systems = self
.stages
.get_mut(stage_name)
.unwrap_or_else(|| panic!("Stage does not exist: {}", stage_name));
systems.push(system);
self
}
pub fn add_thread_local_to_stage(
&mut self,
stage_name: &str,
runnable: Box<dyn Runnable>,
) -> &mut Self {
let system = System::ThreadLocal(runnable);
let systems = self
.stages
.get_mut(stage_name)
.unwrap_or_else(|| panic!("Stage does not exist: {}", stage_name));
systems.push(system);
self
}
pub fn add_thread_local_fn_to_stage(
&mut self,
stage_name: &str,
f: impl FnMut(&mut World, &mut Resources) + 'static,
) -> &mut Self {
let system = System::ThreadLocalFn(Box::new(f));
let systems = self
.stages
.get_mut(stage_name)
.unwrap_or_else(|| panic!("Stage does not exist: {}", stage_name));
systems.push(system);
self
}
}
// working around the famous "rust float ordering" problem
#[derive(PartialOrd)]
struct FloatOrd(f32);
impl Ord for FloatOrd {
fn cmp(&self, other: &Self) -> Ordering {
self.0.partial_cmp(&other.0).unwrap_or_else(|| {
if self.0.is_nan() && !other.0.is_nan() {
Ordering::Less
} else if !self.0.is_nan() && other.0.is_nan() {
Ordering::Greater
} else {
Ordering::Equal
}
})
}
}
impl PartialEq for FloatOrd {
fn eq(&self, other: &Self) -> bool {
if self.0.is_nan() && other.0.is_nan() {
true
} else {
self.0 == other.0
}
}
}
impl Eq for FloatOrd {}

View file

@ -1,7 +1,7 @@
pub mod bytes;
pub mod time;
use bevy_app::{system_stage, AppBuilder, AppPlugin};
use bevy_app::{default_stage, AppBuilder, AppPlugin};
use bevy_transform::transform_system_bundle;
use time::{start_timer_system, stop_timer_system};
@ -15,7 +15,7 @@ impl AppPlugin for CorePlugin {
}
app.add_resource(time::Time::new())
.add_system_to_stage(system_stage::FIRST, start_timer_system())
.add_system_to_stage(system_stage::LAST, stop_timer_system());
.add_system_to_stage(default_stage::FIRST, start_timer_system())
.add_system_to_stage(default_stage::LAST, stop_timer_system());
}
}

View file

@ -48,11 +48,13 @@ use self::{
},
};
use bevy_app::{AppBuilder, AppPlugin, GetEventReader};
use bevy_app::{AppBuilder, AppPlugin, GetEventReader, default_stage};
use bevy_transform::prelude::LocalToWorld;
use bevy_asset::AssetStorage;
use bevy_window::WindowResized;
pub static RENDER_STAGE: &str = "render";
#[derive(Default)]
pub struct RenderPlugin;
@ -94,6 +96,7 @@ impl AppPlugin for RenderPlugin {
asset_batchers.batch_types2::<Mesh, StandardMaterial>();
app
.add_system(build_entity_render_resource_assignments_system())
.add_stage_after(default_stage::UPDATE, RENDER_STAGE)
.add_resource(RenderGraph::default())
.add_resource(AssetStorage::<Mesh>::new())
.add_resource(AssetStorage::<Texture>::new())

View file

@ -7,8 +7,8 @@ pub use wgpu_render_pass::*;
pub use wgpu_renderer::*;
pub use wgpu_resources::*;
use bevy_app::{AppPlugin, system_stage, AppBuilder, Events};
use bevy_render::renderer::Renderer;
use bevy_app::{AppPlugin, AppBuilder, Events};
use bevy_render::{renderer::Renderer, RENDER_STAGE};
use bevy_window::{WindowCreated, WindowResized};
use legion::prelude::*;
@ -18,7 +18,7 @@ pub struct WgpuRendererPlugin;
impl AppPlugin for WgpuRendererPlugin {
fn build(&self, app: &mut AppBuilder) {
let render_system = wgpu_render_system(app.resources());
app.add_thread_local_to_stage(system_stage::RENDER, render_system);
app.add_thread_local_fn_to_stage(RENDER_STAGE, render_system);
}
}

View file

@ -3,11 +3,11 @@ use bevy::prelude::*;
fn main() {
App::build()
.add_default_plugins()
.add_setup_system(setup_system())
.add_startup_system(startup_system())
.run();
}
pub fn setup_system() -> Box<dyn Schedulable> {
pub fn startup_system() -> Box<dyn Schedulable> {
SystemBuilder::new("setup")
.write_resource::<AssetStorage<Mesh>>()
.write_resource::<AssetStorage<StandardMaterial>>()