Resolve (most) internal system ambiguities (#1606)

* Adds labels and orderings to systems that need them (uses the new many-to-many labels for InputSystem)
* Removes the Event, PreEvent, Scene, and Ui stages in favor of First, PreUpdate, and PostUpdate (there is more collapsing potential, such as the Asset stages and _maybe_ removing First, but those have more nuance so they should be handled separately)
* Ambiguity detection now prints component conflicts
* Removed broken change filters from flex calculation (which implicitly relied on the z-update system always modifying translation.z). This will require more work to make it behave as expected so i just removed it (and it was already doing this work every frame).
This commit is contained in:
Carter Anderson 2021-03-10 22:37:02 +00:00
parent 1e42de64af
commit be1c317d4e
23 changed files with 242 additions and 131 deletions

View File

@ -211,21 +211,19 @@ impl AppBuilder {
} }
pub fn add_default_stages(&mut self) -> &mut Self { pub fn add_default_stages(&mut self) -> &mut Self {
self.add_stage( self.add_stage(CoreStage::First, SystemStage::parallel())
CoreStage::Startup, .add_stage(
Schedule::default() CoreStage::Startup,
.with_run_criteria(RunOnce::default()) Schedule::default()
.with_stage(StartupStage::PreStartup, SystemStage::parallel()) .with_run_criteria(RunOnce::default())
.with_stage(StartupStage::Startup, SystemStage::parallel()) .with_stage(StartupStage::PreStartup, SystemStage::parallel())
.with_stage(StartupStage::PostStartup, SystemStage::parallel()), .with_stage(StartupStage::Startup, SystemStage::parallel())
) .with_stage(StartupStage::PostStartup, SystemStage::parallel()),
.add_stage(CoreStage::First, SystemStage::parallel()) )
.add_stage(CoreStage::PreEvent, SystemStage::parallel()) .add_stage(CoreStage::PreUpdate, SystemStage::parallel())
.add_stage(CoreStage::Event, SystemStage::parallel()) .add_stage(CoreStage::Update, SystemStage::parallel())
.add_stage(CoreStage::PreUpdate, SystemStage::parallel()) .add_stage(CoreStage::PostUpdate, SystemStage::parallel())
.add_stage(CoreStage::Update, SystemStage::parallel()) .add_stage(CoreStage::Last, SystemStage::parallel())
.add_stage(CoreStage::PostUpdate, SystemStage::parallel())
.add_stage(CoreStage::Last, SystemStage::parallel())
} }
pub fn add_event<T>(&mut self) -> &mut Self pub fn add_event<T>(&mut self) -> &mut Self
@ -233,7 +231,7 @@ impl AppBuilder {
T: Component, T: Component,
{ {
self.insert_resource(Events::<T>::default()) self.insert_resource(Events::<T>::default())
.add_system_to_stage(CoreStage::Event, Events::<T>::update_system.system()) .add_system_to_stage(CoreStage::First, Events::<T>::update_system.system())
} }
/// Inserts a resource to the current [App] and overwrites any resource previously added of the same type. /// Inserts a resource to the current [App] and overwrites any resource previously added of the same type.

View File

@ -3,7 +3,11 @@ use bevy_ecs::{
system::{Local, Res, ResMut, SystemParam}, system::{Local, Res, ResMut, SystemParam},
}; };
use bevy_utils::tracing::trace; use bevy_utils::tracing::trace;
use std::{fmt, marker::PhantomData}; use std::{
fmt::{self},
hash::Hash,
marker::PhantomData,
};
/// An `EventId` uniquely identifies an event. /// An `EventId` uniquely identifies an event.
/// ///

View File

@ -31,10 +31,6 @@ pub enum CoreStage {
Startup, Startup,
/// Name of app stage that runs before all other app stages /// Name of app stage that runs before all other app stages
First, First,
/// Name of app stage that runs before EVENT
PreEvent,
/// Name of app stage that updates events. Runs before UPDATE
Event,
/// Name of app stage responsible for performing setup before an update. Runs before UPDATE. /// Name of app stage responsible for performing setup before an update. Runs before UPDATE.
PreUpdate, PreUpdate,
/// Name of app stage responsible for doing most app logic. Systems should be registered here by default. /// Name of app stage responsible for doing most app logic. Systems should be registered here by default.

View File

@ -17,7 +17,11 @@ pub mod prelude {
} }
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_ecs::{entity::Entity, system::IntoSystem}; use bevy_ecs::{
entity::Entity,
schedule::{ExclusiveSystemDescriptorCoercion, SystemLabel},
system::{IntoExclusiveSystem, IntoSystem},
};
use bevy_utils::HashSet; use bevy_utils::HashSet;
use std::ops::Range; use std::ops::Range;
@ -25,6 +29,12 @@ use std::ops::Range;
#[derive(Default)] #[derive(Default)]
pub struct CorePlugin; pub struct CorePlugin;
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemLabel)]
pub enum CoreSystem {
/// Updates the elapsed time. Any system that interacts with [Time] component should run after this.
Time,
}
impl Plugin for CorePlugin { impl Plugin for CorePlugin {
fn build(&self, app: &mut AppBuilder) { fn build(&self, app: &mut AppBuilder) {
// Setup the default bevy task pools // Setup the default bevy task pools
@ -44,7 +54,11 @@ impl Plugin for CorePlugin {
.register_type::<Labels>() .register_type::<Labels>()
.register_type::<Range<f32>>() .register_type::<Range<f32>>()
.register_type::<Timer>() .register_type::<Timer>()
.add_system_to_stage(CoreStage::First, time_system.system()) // time system is added as an "exclusive system" to ensure it runs before other systems in CoreStage::First
.add_system_to_stage(
CoreStage::First,
time_system.exclusive_system().label(CoreSystem::Time),
)
.add_startup_system_to_stage(StartupStage::PostStartup, entity_labels_system.system()) .add_startup_system_to_stage(StartupStage::PostStartup, entity_labels_system.system())
.add_system_to_stage(CoreStage::PostUpdate, entity_labels_system.system()); .add_system_to_stage(CoreStage::PostUpdate, entity_labels_system.system());

View File

@ -198,3 +198,35 @@ impl<T: SparseSetIndex> Default for FilteredAccessSet<T> {
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::query::Access;
#[test]
fn access_get_conflicts() {
let mut access_a = Access::<usize>::default();
access_a.add_read(0);
access_a.add_read(1);
let mut access_b = Access::<usize>::default();
access_b.add_read(0);
access_b.add_write(1);
assert_eq!(access_a.get_conflicts(&access_b), vec![1]);
let mut access_c = Access::<usize>::default();
access_c.add_write(0);
access_c.add_write(1);
assert_eq!(access_a.get_conflicts(&access_c), vec![0, 1]);
assert_eq!(access_b.get_conflicts(&access_c), vec![0, 1]);
let mut access_d = Access::<usize>::default();
access_d.add_read(0);
assert_eq!(access_d.get_conflicts(&access_a), vec![]);
assert_eq!(access_d.get_conflicts(&access_b), vec![]);
assert_eq!(access_d.get_conflicts(&access_c), vec![0]);
}
}

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
component::ComponentId,
schedule::{ schedule::{
BoxedSystemLabel, ExclusiveSystemContainer, InsertionPoint, ParallelExecutor, BoxedSystemLabel, ExclusiveSystemContainer, InsertionPoint, ParallelExecutor,
ParallelSystemContainer, ParallelSystemExecutor, RunCriteria, ShouldRun, ParallelSystemContainer, ParallelSystemExecutor, RunCriteria, ShouldRun,
@ -263,15 +264,16 @@ impl SystemStage {
} }
/// Logs execution order ambiguities between systems. System orders must be fresh. /// Logs execution order ambiguities between systems. System orders must be fresh.
fn report_ambiguities(&self) { fn report_ambiguities(&self, world: &World) {
debug_assert!(!self.systems_modified); debug_assert!(!self.systems_modified);
use std::fmt::Write; use std::fmt::Write;
fn write_display_names_of_pairs( fn write_display_names_of_pairs(
string: &mut String, string: &mut String,
systems: &[impl SystemContainer], systems: &[impl SystemContainer],
mut ambiguities: Vec<(usize, usize)>, mut ambiguities: Vec<(usize, usize, Vec<ComponentId>)>,
world: &World,
) { ) {
for (index_a, index_b) in ambiguities.drain(..) { for (index_a, index_b, conflicts) in ambiguities.drain(..) {
writeln!( writeln!(
string, string,
" -- {:?} and {:?}", " -- {:?} and {:?}",
@ -279,6 +281,13 @@ impl SystemStage {
systems[index_b].name() systems[index_b].name()
) )
.unwrap(); .unwrap();
if !conflicts.is_empty() {
let names = conflicts
.iter()
.map(|id| world.components().get_info(*id).unwrap().name())
.collect::<Vec<_>>();
writeln!(string, " conflicts: {:?}", names).unwrap();
}
} }
} }
let parallel = find_ambiguities(&self.parallel); let parallel = find_ambiguities(&self.parallel);
@ -295,11 +304,16 @@ impl SystemStage {
.to_owned(); .to_owned();
if !parallel.is_empty() { if !parallel.is_empty() {
writeln!(string, " * Parallel systems:").unwrap(); writeln!(string, " * Parallel systems:").unwrap();
write_display_names_of_pairs(&mut string, &self.parallel, parallel); write_display_names_of_pairs(&mut string, &self.parallel, parallel, world);
} }
if !at_start.is_empty() { if !at_start.is_empty() {
writeln!(string, " * Exclusive systems at start of stage:").unwrap(); writeln!(string, " * Exclusive systems at start of stage:").unwrap();
write_display_names_of_pairs(&mut string, &self.exclusive_at_start, at_start); write_display_names_of_pairs(
&mut string,
&self.exclusive_at_start,
at_start,
world,
);
} }
if !before_commands.is_empty() { if !before_commands.is_empty() {
writeln!(string, " * Exclusive systems before commands of stage:").unwrap(); writeln!(string, " * Exclusive systems before commands of stage:").unwrap();
@ -307,11 +321,12 @@ impl SystemStage {
&mut string, &mut string,
&self.exclusive_before_commands, &self.exclusive_before_commands,
before_commands, before_commands,
world,
); );
} }
if !at_end.is_empty() { if !at_end.is_empty() {
writeln!(string, " * Exclusive systems at end of stage:").unwrap(); writeln!(string, " * Exclusive systems at end of stage:").unwrap();
write_display_names_of_pairs(&mut string, &self.exclusive_at_end, at_end); write_display_names_of_pairs(&mut string, &self.exclusive_at_end, at_end, world);
} }
info!("{}", string); info!("{}", string);
} }
@ -454,9 +469,10 @@ fn topological_order(
Ok(sorted) Ok(sorted)
} }
/// Returns vector containing all pairs of indices of systems with ambiguous execution order. /// Returns vector containing all pairs of indices of systems with ambiguous execution order,
/// along with specific components that have triggered the warning.
/// Systems must be topologically sorted beforehand. /// Systems must be topologically sorted beforehand.
fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize)> { fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize, Vec<ComponentId>)> {
let mut ambiguity_set_labels = HashMap::default(); let mut ambiguity_set_labels = HashMap::default();
for set in systems.iter().flat_map(|c| c.ambiguity_sets()) { for set in systems.iter().flat_map(|c| c.ambiguity_sets()) {
let len = ambiguity_set_labels.len(); let len = ambiguity_set_labels.len();
@ -511,9 +527,17 @@ fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize)> {
{ {
if !processed.contains(index_b) if !processed.contains(index_b)
&& all_ambiguity_sets[index_a].is_disjoint(&all_ambiguity_sets[index_b]) && all_ambiguity_sets[index_a].is_disjoint(&all_ambiguity_sets[index_b])
&& !systems[index_a].is_compatible(&systems[index_b])
{ {
ambiguities.push((index_a, index_b)); let a_access = systems[index_a].component_access();
let b_access = systems[index_b].component_access();
if let (Some(a), Some(b)) = (a_access, b_access) {
let conflicts = a.get_conflicts(b);
if !conflicts.is_empty() {
ambiguities.push((index_a, index_b, conflicts))
}
} else {
ambiguities.push((index_a, index_b, Vec::new()));
}
} }
} }
processed.insert(index_a); processed.insert(index_a);
@ -549,7 +573,7 @@ impl Stage for SystemStage {
self.executor.rebuild_cached_data(&self.parallel); self.executor.rebuild_cached_data(&self.parallel);
self.executor_modified = false; self.executor_modified = false;
if world.contains_resource::<ReportExecutionOrderAmbiguities>() { if world.contains_resource::<ReportExecutionOrderAmbiguities>() {
self.report_ambiguities(); self.report_ambiguities(world);
} }
} else if self.executor_modified { } else if self.executor_modified {
self.executor.rebuild_cached_data(&self.parallel); self.executor.rebuild_cached_data(&self.parallel);
@ -1184,7 +1208,7 @@ mod tests {
) -> Vec<(BoxedSystemLabel, BoxedSystemLabel)> { ) -> Vec<(BoxedSystemLabel, BoxedSystemLabel)> {
find_ambiguities(systems) find_ambiguities(systems)
.drain(..) .drain(..)
.map(|(index_a, index_b)| { .map(|(index_a, index_b, _conflicts)| {
( (
systems[index_a].labels()[0].clone(), systems[index_a].labels()[0].clone(),
systems[index_b].labels()[0].clone(), systems[index_b].labels()[0].clone(),

View File

@ -1,4 +1,6 @@
use crate::{ use crate::{
component::ComponentId,
query::Access,
schedule::{ schedule::{
BoxedAmbiguitySetLabel, BoxedSystemLabel, ExclusiveSystemDescriptor, BoxedAmbiguitySetLabel, BoxedSystemLabel, ExclusiveSystemDescriptor,
ParallelSystemDescriptor, ParallelSystemDescriptor,
@ -16,7 +18,7 @@ pub(super) trait SystemContainer {
fn before(&self) -> &[BoxedSystemLabel]; fn before(&self) -> &[BoxedSystemLabel];
fn after(&self) -> &[BoxedSystemLabel]; fn after(&self) -> &[BoxedSystemLabel];
fn ambiguity_sets(&self) -> &[BoxedAmbiguitySetLabel]; fn ambiguity_sets(&self) -> &[BoxedAmbiguitySetLabel];
fn is_compatible(&self, other: &Self) -> bool; fn component_access(&self) -> Option<&Access<ComponentId>>;
} }
pub(super) struct ExclusiveSystemContainer { pub(super) struct ExclusiveSystemContainer {
@ -81,8 +83,8 @@ impl SystemContainer for ExclusiveSystemContainer {
&self.ambiguity_sets &self.ambiguity_sets
} }
fn is_compatible(&self, _: &Self) -> bool { fn component_access(&self) -> Option<&Access<ComponentId>> {
false None
} }
} }
@ -178,9 +180,7 @@ impl SystemContainer for ParallelSystemContainer {
&self.ambiguity_sets &self.ambiguity_sets
} }
fn is_compatible(&self, other: &Self) -> bool { fn component_access(&self) -> Option<&Access<ComponentId>> {
self.system() Some(self.system().component_access())
.component_access()
.is_compatible(other.system().component_access())
} }
} }

View File

@ -17,6 +17,8 @@ pub use system_param::*;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::any::TypeId;
use crate::{ use crate::{
archetype::Archetypes, archetype::Archetypes,
bundle::Bundles, bundle::Bundles,
@ -415,4 +417,25 @@ mod tests {
// ensure the system actually ran // ensure the system actually ran
assert_eq!(*world.get_resource::<bool>().unwrap(), true); assert_eq!(*world.get_resource::<bool>().unwrap(), true);
} }
#[test]
fn get_system_conflicts() {
fn sys_x(_: Res<A>, _: Res<B>, _: Query<(&C, &D)>) {}
fn sys_y(_: Res<A>, _: ResMut<B>, _: Query<(&C, &mut D)>) {}
let mut world = World::default();
let mut x = sys_x.system();
let mut y = sys_y.system();
x.initialize(&mut world);
y.initialize(&mut world);
let conflicts = x.component_access().get_conflicts(y.component_access());
let b_id = world
.components()
.get_resource_id(TypeId::of::<B>())
.unwrap();
let d_id = world.components().get_id(TypeId::of::<D>()).unwrap();
assert_eq!(conflicts, vec![b_id, d_id]);
}
} }

View File

@ -248,6 +248,7 @@ impl<'a, T: Component> SystemParam for Option<Res<'a, T>> {
unsafe impl<T: Component> SystemParamState for OptionResState<T> { unsafe impl<T: Component> SystemParamState for OptionResState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self {
Self(ResState::init(world, system_state, ())) Self(ResState::init(world, system_state, ()))
} }
@ -383,6 +384,7 @@ impl<'a, T: Component> SystemParam for Option<ResMut<'a, T>> {
unsafe impl<T: Component> SystemParamState for OptionResMutState<T> { unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self {
Self(ResMutState::init(world, system_state, ())) Self(ResMutState::init(world, system_state, ()))
} }

View File

@ -24,7 +24,7 @@ impl Plugin for GilrsPlugin {
gilrs_event_startup_system.exclusive_system(), gilrs_event_startup_system.exclusive_system(),
) )
.add_system_to_stage( .add_system_to_stage(
CoreStage::PreEvent, CoreStage::PreUpdate,
gilrs_event_system.exclusive_system(), gilrs_event_system.exclusive_system(),
); );
} }

View File

@ -7,7 +7,10 @@ pub mod system;
pub mod touch; pub mod touch;
pub use axis::*; pub use axis::*;
use bevy_ecs::system::IntoSystem; use bevy_ecs::{
schedule::{ParallelSystemDescriptorCoercion, SystemLabel},
system::IntoSystem,
};
pub use input::*; pub use input::*;
pub mod prelude { pub mod prelude {
@ -37,27 +40,46 @@ use gamepad::{
#[derive(Default)] #[derive(Default)]
pub struct InputPlugin; pub struct InputPlugin;
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemLabel)]
pub struct InputSystem;
impl Plugin for InputPlugin { impl Plugin for InputPlugin {
fn build(&self, app: &mut AppBuilder) { fn build(&self, app: &mut AppBuilder) {
app.add_event::<KeyboardInput>() app
// keyboard
.add_event::<KeyboardInput>()
.init_resource::<Input<KeyCode>>()
.add_system_to_stage(
CoreStage::PreUpdate,
keyboard_input_system.system().label(InputSystem),
)
// mouse
.add_event::<MouseButtonInput>() .add_event::<MouseButtonInput>()
.add_event::<MouseMotion>() .add_event::<MouseMotion>()
.add_event::<MouseWheel>() .add_event::<MouseWheel>()
.init_resource::<Input<KeyCode>>()
.add_system_to_stage(CoreStage::Event, keyboard_input_system.system())
.init_resource::<Input<MouseButton>>() .init_resource::<Input<MouseButton>>()
.add_system_to_stage(CoreStage::Event, mouse_button_input_system.system()) .add_system_to_stage(
CoreStage::PreUpdate,
mouse_button_input_system.system().label(InputSystem),
)
// gamepad
.add_event::<GamepadEvent>() .add_event::<GamepadEvent>()
.add_event::<GamepadEventRaw>() .add_event::<GamepadEventRaw>()
.init_resource::<GamepadSettings>() .init_resource::<GamepadSettings>()
.init_resource::<Input<GamepadButton>>() .init_resource::<Input<GamepadButton>>()
.init_resource::<Axis<GamepadAxis>>() .init_resource::<Axis<GamepadAxis>>()
.init_resource::<Axis<GamepadButton>>() .init_resource::<Axis<GamepadButton>>()
.add_system_to_stage(CoreStage::Event, gamepad_event_system.system()) .add_system_to_stage(
.add_startup_system_to_stage(StartupStage::Startup, gamepad_event_system.system()) CoreStage::PreUpdate,
gamepad_event_system.system().label(InputSystem),
)
// touch
.add_event::<TouchInput>() .add_event::<TouchInput>()
.init_resource::<Touches>() .init_resource::<Touches>()
.add_system_to_stage(CoreStage::Event, touch_screen_input_system.system()); .add_system_to_stage(
CoreStage::PreUpdate,
touch_screen_input_system.system().label(InputSystem),
);
} }
} }

View File

@ -13,9 +13,10 @@ pub mod texture;
pub mod wireframe; pub mod wireframe;
use bevy_ecs::{ use bevy_ecs::{
schedule::SystemStage, schedule::{ParallelSystemDescriptorCoercion, SystemStage},
system::{IntoExclusiveSystem, IntoSystem}, system::{IntoExclusiveSystem, IntoSystem},
}; };
use bevy_transform::TransformSystem;
use draw::Visible; use draw::Visible;
pub use once_cell; pub use once_cell;
@ -37,7 +38,7 @@ use crate::prelude::*;
use base::Msaa; use base::Msaa;
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{AddAsset, AssetStage}; use bevy_asset::{AddAsset, AssetStage};
use bevy_ecs::schedule::StageLabel; use bevy_ecs::schedule::{StageLabel, SystemLabel};
use camera::{ use camera::{
ActiveCameras, Camera, DepthCalculation, OrthographicProjection, PerspectiveProjection, ActiveCameras, Camera, DepthCalculation, OrthographicProjection, PerspectiveProjection,
RenderLayers, ScalingMode, VisibleEntities, WindowOrigin, RenderLayers, ScalingMode, VisibleEntities, WindowOrigin,
@ -57,6 +58,11 @@ use texture::HdrTextureLoader;
#[cfg(feature = "png")] #[cfg(feature = "png")]
use texture::ImageTextureLoader; use texture::ImageTextureLoader;
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
pub enum RenderSystem {
VisibleEntities,
}
/// The names of "render" App stages /// The names of "render" App stages
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)] #[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
pub enum RenderStage { pub enum RenderStage {
@ -157,16 +163,22 @@ impl Plugin for RenderPlugin {
) )
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
camera::camera_system::<OrthographicProjection>.system(), camera::camera_system::<OrthographicProjection>
.system()
.before(RenderSystem::VisibleEntities),
) )
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
camera::camera_system::<PerspectiveProjection>.system(), camera::camera_system::<PerspectiveProjection>
.system()
.before(RenderSystem::VisibleEntities),
) )
// registration order matters here. this must come after all camera_system::<T> systems
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
camera::visible_entities_system.system(), camera::visible_entities_system
.system()
.label(RenderSystem::VisibleEntities)
.after(TransformSystem::TransformPropagate),
) )
.add_system_to_stage( .add_system_to_stage(
RenderStage::RenderResource, RenderStage::RenderResource,

View File

@ -233,13 +233,10 @@ pub struct Mesh {
impl Mesh { impl Mesh {
/// Per vertex coloring. Use in conjunction with [`Mesh::set_attribute`] /// Per vertex coloring. Use in conjunction with [`Mesh::set_attribute`]
pub const ATTRIBUTE_COLOR: &'static str = "Vertex_Color"; pub const ATTRIBUTE_COLOR: &'static str = "Vertex_Color";
/// The direction the vertex normal is facing in. Use in conjunction with [`Mesh::set_attribute`] /// The direction the vertex normal is facing in. Use in conjunction with [`Mesh::set_attribute`]
pub const ATTRIBUTE_NORMAL: &'static str = "Vertex_Normal"; pub const ATTRIBUTE_NORMAL: &'static str = "Vertex_Normal";
/// Where the vertex is located in space. Use in conjunction with [`Mesh::set_attribute`] /// Where the vertex is located in space. Use in conjunction with [`Mesh::set_attribute`]
pub const ATTRIBUTE_POSITION: &'static str = "Vertex_Position"; pub const ATTRIBUTE_POSITION: &'static str = "Vertex_Position";
/// Texture coordinates for the vertex. Use in conjunction with [`Mesh::set_attribute`] /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::set_attribute`]
pub const ATTRIBUTE_UV_0: &'static str = "Vertex_Uv"; pub const ATTRIBUTE_UV_0: &'static str = "Vertex_Uv";

View File

@ -19,33 +19,20 @@ pub mod prelude {
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::AddAsset; use bevy_asset::AddAsset;
use bevy_ecs::{ use bevy_ecs::{schedule::ExclusiveSystemDescriptorCoercion, system::IntoExclusiveSystem};
schedule::{StageLabel, SystemStage},
system::IntoExclusiveSystem,
};
#[derive(Default)] #[derive(Default)]
pub struct ScenePlugin; pub struct ScenePlugin;
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
enum SceneStage {
SceneStage,
}
impl Plugin for ScenePlugin { impl Plugin for ScenePlugin {
fn build(&self, app: &mut AppBuilder) { fn build(&self, app: &mut AppBuilder) {
app.add_asset::<DynamicScene>() app.add_asset::<DynamicScene>()
.add_asset::<Scene>() .add_asset::<Scene>()
.init_asset_loader::<SceneLoader>() .init_asset_loader::<SceneLoader>()
.init_resource::<SceneSpawner>() .init_resource::<SceneSpawner>()
.add_stage_after(
CoreStage::Event,
SceneStage::SceneStage,
SystemStage::parallel(),
)
.add_system_to_stage( .add_system_to_stage(
SceneStage::SceneStage, CoreStage::PreUpdate,
scene_spawner_system.exclusive_system(), scene_spawner_system.exclusive_system().at_end(),
); );
} }
} }

View File

@ -102,6 +102,6 @@ impl Default for TextStyle {
} }
#[derive(Default, Copy, Clone, Debug)] #[derive(Default, Copy, Clone, Debug)]
pub struct CalculatedSize { pub struct Text2dSize {
pub size: Size, pub size: Size,
} }

View File

@ -18,9 +18,7 @@ use bevy_transform::prelude::{GlobalTransform, Transform};
use bevy_window::Windows; use bevy_window::Windows;
use glyph_brush_layout::{HorizontalAlign, VerticalAlign}; use glyph_brush_layout::{HorizontalAlign, VerticalAlign};
use crate::{ use crate::{DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, Text2dSize, TextError};
CalculatedSize, DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, TextError,
};
/// The bundle of components needed to draw text in a 2D scene via the Camera2dBundle. /// The bundle of components needed to draw text in a 2D scene via the Camera2dBundle.
#[derive(Bundle, Clone, Debug)] #[derive(Bundle, Clone, Debug)]
@ -31,7 +29,7 @@ pub struct Text2dBundle {
pub transform: Transform, pub transform: Transform,
pub global_transform: GlobalTransform, pub global_transform: GlobalTransform,
pub main_pass: MainPass, pub main_pass: MainPass,
pub calculated_size: CalculatedSize, pub text_2d_size: Text2dSize,
} }
impl Default for Text2dBundle { impl Default for Text2dBundle {
@ -48,7 +46,7 @@ impl Default for Text2dBundle {
transform: Default::default(), transform: Default::default(),
global_transform: Default::default(), global_transform: Default::default(),
main_pass: MainPass {}, main_pass: MainPass {},
calculated_size: CalculatedSize { text_2d_size: Text2dSize {
size: Size::default(), size: Size::default(),
}, },
} }
@ -72,7 +70,7 @@ pub fn draw_text2d_system(
&Visible, &Visible,
&Text, &Text,
&GlobalTransform, &GlobalTransform,
&CalculatedSize, &Text2dSize,
), ),
With<MainPass>, With<MainPass>,
>, >,
@ -138,7 +136,7 @@ pub fn text2d_system(
mut text_pipeline: ResMut<DefaultTextPipeline>, mut text_pipeline: ResMut<DefaultTextPipeline>,
mut text_queries: QuerySet<( mut text_queries: QuerySet<(
Query<Entity, (With<MainPass>, Changed<Text>)>, Query<Entity, (With<MainPass>, Changed<Text>)>,
Query<(&Text, &mut CalculatedSize), With<MainPass>>, Query<(&Text, &mut Text2dSize), With<MainPass>>,
)>, )>,
) { ) {
// Adds all entities where the text or the style has changed to the local queue // Adds all entities where the text or the style has changed to the local queue

View File

@ -2,7 +2,7 @@ use super::Node;
use crate::{ use crate::{
render::UI_PIPELINE_HANDLE, render::UI_PIPELINE_HANDLE,
widget::{Button, Image}, widget::{Button, Image},
FocusPolicy, Interaction, Style, CalculatedSize, FocusPolicy, Interaction, Style,
}; };
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_ecs::bundle::Bundle; use bevy_ecs::bundle::Bundle;
@ -14,7 +14,7 @@ use bevy_render::{
prelude::Visible, prelude::Visible,
}; };
use bevy_sprite::{ColorMaterial, QUAD_HANDLE}; use bevy_sprite::{ColorMaterial, QUAD_HANDLE};
use bevy_text::{CalculatedSize, Text}; use bevy_text::Text;
use bevy_transform::prelude::{GlobalTransform, Transform}; use bevy_transform::prelude::{GlobalTransform, Transform};
#[derive(Bundle, Clone, Debug)] #[derive(Bundle, Clone, Debug)]

View File

@ -1,15 +1,14 @@
mod convert; mod convert;
use crate::{Node, Style}; use crate::{CalculatedSize, Node, Style};
use bevy_app::EventReader; use bevy_app::EventReader;
use bevy_ecs::{ use bevy_ecs::{
entity::Entity, entity::Entity,
query::{Changed, FilterFetch, Flags, With, Without, WorldQuery}, query::{Changed, FilterFetch, With, Without, WorldQuery},
system::{Query, Res, ResMut}, system::{Query, Res, ResMut},
}; };
use bevy_log::warn; use bevy_log::warn;
use bevy_math::Vec2; use bevy_math::Vec2;
use bevy_text::CalculatedSize;
use bevy_transform::prelude::{Children, Parent, Transform}; use bevy_transform::prelude::{Children, Parent, Transform};
use bevy_utils::HashMap; use bevy_utils::HashMap;
use bevy_window::{Window, WindowId, WindowScaleFactorChanged, Windows}; use bevy_window::{Window, WindowId, WindowScaleFactorChanged, Windows};
@ -201,13 +200,7 @@ pub fn flex_node_system(
(With<Node>, Changed<CalculatedSize>), (With<Node>, Changed<CalculatedSize>),
>, >,
children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>, children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>,
mut node_transform_query: Query<( mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>,
Entity,
&mut Node,
&mut Transform,
Option<(&Parent, Flags<Parent>)>,
Flags<Transform>,
)>,
) { ) {
// update window root nodes // update window root nodes
for window in windows.iter() { for window in windows.iter() {
@ -272,9 +265,8 @@ pub fn flex_node_system(
let to_logical = |v| (physical_to_logical_factor * v as f64) as f32; let to_logical = |v| (physical_to_logical_factor * v as f64) as f32;
for (entity, mut node, mut transform, parent, transform_flags) in // PERF: try doing this incrementally
node_transform_query.iter_mut() for (entity, mut node, mut transform, parent) in node_transform_query.iter_mut() {
{
let layout = flex_surface.get_layout(entity).unwrap(); let layout = flex_surface.get_layout(entity).unwrap();
node.size = Vec2::new( node.size = Vec2::new(
to_logical(layout.size.width), to_logical(layout.size.width),
@ -283,12 +275,10 @@ pub fn flex_node_system(
let position = &mut transform.translation; let position = &mut transform.translation;
position.x = to_logical(layout.location.x + layout.size.width / 2.0); position.x = to_logical(layout.location.x + layout.size.width / 2.0);
position.y = to_logical(layout.location.y + layout.size.height / 2.0); position.y = to_logical(layout.location.y + layout.size.height / 2.0);
if let Some((parent, parent_flags)) = parent { if let Some(parent) = parent {
if parent_flags.changed() || transform_flags.changed() { if let Ok(parent_layout) = flex_surface.get_layout(parent.0) {
if let Ok(parent_layout) = flex_surface.get_layout(parent.0) { position.x -= to_logical(parent_layout.size.width / 2.0);
position.x -= to_logical(parent_layout.size.width / 2.0); position.y -= to_logical(parent_layout.size.height / 2.0);
position.y -= to_logical(parent_layout.size.height / 2.0);
}
} }
} }
} }

View File

@ -1,16 +1,15 @@
mod anchors; mod anchors;
pub mod entity;
mod flex; mod flex;
mod focus; mod focus;
mod margins; mod margins;
mod render; mod render;
mod ui_node; mod ui_node;
pub mod entity;
pub mod update; pub mod update;
pub mod widget; pub mod widget;
pub use anchors::*; pub use anchors::*;
use bevy_math::{Rect, Size};
use bevy_render::RenderStage;
pub use flex::*; pub use flex::*;
pub use focus::*; pub use focus::*;
pub use margins::*; pub use margins::*;
@ -23,28 +22,24 @@ pub mod prelude {
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_ecs::{ use bevy_ecs::{
schedule::{ParallelSystemDescriptorCoercion, StageLabel, SystemLabel, SystemStage}, schedule::{ParallelSystemDescriptorCoercion, SystemLabel},
system::IntoSystem, system::IntoSystem,
}; };
use bevy_input::InputSystem;
use bevy_math::{Rect, Size};
use bevy_render::RenderStage;
use bevy_transform::TransformSystem;
use update::ui_z_system; use update::ui_z_system;
#[derive(Default)] #[derive(Default)]
pub struct UiPlugin; pub struct UiPlugin;
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
pub enum UiStage {
Ui,
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)] #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
pub enum UiSystem { pub enum UiSystem {
/// After this label, the ui flex state has been updated
Flex, Flex,
} }
pub mod system {
pub const FLEX: &str = "flex";
}
impl Plugin for UiPlugin { impl Plugin for UiPlugin {
fn build(&self, app: &mut AppBuilder) { fn build(&self, app: &mut AppBuilder) {
app.init_resource::<FlexSurface>() app.init_resource::<FlexSurface>()
@ -63,19 +58,33 @@ impl Plugin for UiPlugin {
.register_type::<Rect<Val>>() .register_type::<Rect<Val>>()
.register_type::<Style>() .register_type::<Style>()
.register_type::<Val>() .register_type::<Val>()
.add_stage_before(CoreStage::PostUpdate, UiStage::Ui, SystemStage::parallel()) .add_system_to_stage(
.add_system_to_stage(CoreStage::PreUpdate, ui_focus_system.system()) CoreStage::PreUpdate,
ui_focus_system.system().after(InputSystem),
)
// add these stages to front because these must run before transform update systems // add these stages to front because these must run before transform update systems
.add_system_to_stage( .add_system_to_stage(
UiStage::Ui, CoreStage::PostUpdate,
widget::text_system.system().before(UiSystem::Flex), widget::text_system.system().before(UiSystem::Flex),
) )
.add_system_to_stage( .add_system_to_stage(
UiStage::Ui, CoreStage::PostUpdate,
widget::image_node_system.system().before(UiSystem::Flex), widget::image_node_system.system().before(UiSystem::Flex),
) )
.add_system_to_stage(UiStage::Ui, flex_node_system.system().label(UiSystem::Flex)) .add_system_to_stage(
.add_system_to_stage(UiStage::Ui, ui_z_system.system()) CoreStage::PostUpdate,
flex_node_system
.system()
.label(UiSystem::Flex)
.before(TransformSystem::TransformPropagate),
)
.add_system_to_stage(
CoreStage::PostUpdate,
ui_z_system
.system()
.after(UiSystem::Flex)
.before(TransformSystem::TransformPropagate),
)
.add_system_to_stage(RenderStage::Draw, widget::draw_text_system.system()); .add_system_to_stage(RenderStage::Draw, widget::draw_text_system.system());
crate::render::add_ui_graph(app.world_mut()); crate::render::add_ui_graph(app.world_mut());

View File

@ -250,3 +250,8 @@ impl Default for FlexWrap {
FlexWrap::NoWrap FlexWrap::NoWrap
} }
} }
#[derive(Default, Copy, Clone, Debug)]
pub struct CalculatedSize {
pub size: Size,
}

View File

@ -1,3 +1,4 @@
use crate::CalculatedSize;
use bevy_asset::{Assets, Handle}; use bevy_asset::{Assets, Handle};
use bevy_ecs::{ use bevy_ecs::{
query::With, query::With,
@ -6,7 +7,6 @@ use bevy_ecs::{
use bevy_math::Size; use bevy_math::Size;
use bevy_render::texture::Texture; use bevy_render::texture::Texture;
use bevy_sprite::ColorMaterial; use bevy_sprite::ColorMaterial;
use bevy_text::CalculatedSize;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Image { pub enum Image {

View File

@ -1,4 +1,4 @@
use crate::{Node, Style, Val}; use crate::{CalculatedSize, Node, Style, Val};
use bevy_asset::Assets; use bevy_asset::Assets;
use bevy_ecs::{ use bevy_ecs::{
entity::Entity, entity::Entity,
@ -14,9 +14,7 @@ use bevy_render::{
texture::Texture, texture::Texture,
}; };
use bevy_sprite::{TextureAtlas, QUAD_HANDLE}; use bevy_sprite::{TextureAtlas, QUAD_HANDLE};
use bevy_text::{ use bevy_text::{DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, TextError};
CalculatedSize, DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, TextError,
};
use bevy_transform::prelude::GlobalTransform; use bevy_transform::prelude::GlobalTransform;
use bevy_window::Windows; use bevy_window::Windows;

View File

@ -10,7 +10,7 @@ use bevy_input::{
pub use winit_config::*; pub use winit_config::*;
pub use winit_windows::*; pub use winit_windows::*;
use bevy_app::{App, AppBuilder, AppExit, Events, ManualEventReader, Plugin}; use bevy_app::{App, AppBuilder, AppExit, CoreStage, Events, ManualEventReader, Plugin};
use bevy_ecs::{system::IntoExclusiveSystem, world::World}; use bevy_ecs::{system::IntoExclusiveSystem, world::World};
use bevy_math::{ivec2, Vec2}; use bevy_math::{ivec2, Vec2};
use bevy_utils::tracing::{error, trace, warn}; use bevy_utils::tracing::{error, trace, warn};
@ -42,7 +42,7 @@ impl Plugin for WinitPlugin {
fn build(&self, app: &mut AppBuilder) { fn build(&self, app: &mut AppBuilder) {
app.init_resource::<WinitWindows>() app.init_resource::<WinitWindows>()
.set_runner(winit_runner) .set_runner(winit_runner)
.add_system(change_window.exclusive_system()); .add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system());
} }
} }